diff --git a/examples/studio_demo/tensorrt_jetson_25w_result.json b/examples/studio_demo/tensorrt_jetson_25w_result.json new file mode 100644 index 0000000..1540bf3 --- /dev/null +++ b/examples/studio_demo/tensorrt_jetson_25w_result.json @@ -0,0 +1,146 @@ +{ + "schema_version": "inferedge-runtime-result-v1", + "compare_key": "yolov8n__b1__h640w640__fp16", + "backend_key": "tensorrt__jetson", + "runtime_role": "runtime-result", + "manifest_path": "/home/risenano01/InferEdgeForge/builds/yolov8n__jetson__tensorrt__jetson_fp16/metadata.json", + "manifest_applied": true, + "model_name": "model.engine", + "model_path": "/home/risenano01/InferEdgeForge/builds/yolov8n__jetson__tensorrt__jetson_fp16/model.engine", + "engine_name": "tensorrt", + "engine_backend": "tensorrt", + "device_name": "jetson", + "batch": 1, + "height": 640, + "width": 640, + "warmup": 10, + "runs": 50, + "mean_ms": 10.066401, + "p50_ms": 9.908564, + "p95_ms": 15.476641, + "p99_ms": 15.548438, + "fps_value": 99.340373, + "success": true, + "status": "success", + "model": { + "path": "/home/risenano01/InferEdgeForge/builds/yolov8n__jetson__tensorrt__jetson_fp16/model.engine", + "name": "model.engine" + }, + "engine": { + "name": "tensorrt", + "backend": "tensorrt", + "available": true, + "status_message": "TensorRT backend is linked. Engine metadata, one-shot inference, and benchmark execution are available." + }, + "device": { + "name": "jetson" + }, + "precision": "fp16", + "run_config": { + "batch": 1, + "height": 640, + "width": 640, + "warmup": 10, + "runs": 50, + "power_mode": "25W", + "jetson_clocks": "unknown", + "tegrastats_log_path": "results/jetson_evidence/tegrastats_yolov8n_trt_fp16_25w_20260504T170039Z.log", + "manifest_path": "/home/risenano01/InferEdgeForge/builds/yolov8n__jetson__tensorrt__jetson_fp16/metadata.json", + "manifest_applied": true + }, + "latency_ms": { + "mean": 10.066401, + "min": 7.241229, + "max": 15.548438, + "std": 2.279389, + "p50": 9.908564, + "p90": 12.284969, + "p95": 15.476641, + "p99": 15.548438 + }, + "fps": 99.340373, + "benchmark": { + "success": true, + "status": "success", + "message": "benchmark completed" + }, + "timestamp": "2026-05-04T17:00:41Z", + "system": { + "os": "linux", + "compiler": "GCC", + "cpp_standard": "17", + "jetson": { + "power_mode": "25W", + "jetson_clocks": "unknown", + "tegrastats_log_path": "results/jetson_evidence/tegrastats_yolov8n_trt_fp16_25w_20260504T170039Z.log" + } + }, + "jetson_evidence": { + "power_mode": "25W", + "jetson_clocks": "unknown", + "tegrastats_log_path": "results/jetson_evidence/tegrastats_yolov8n_trt_fp16_25w_20260504T170039Z.log", + "tegrastats_summary": { + "status": "parsed", + "sample_count": 3, + "ram_used_mb_avg": 947.666667, + "ram_used_mb_max": 1072.0, + "ram_total_mb": 7620.0, + "max_temp_c": 40.656, + "max_temp_name": "gpu", + "vdd_in_mw_avg": 4863.0, + "vdd_in_mw_max": 5827.0 + } + }, + "model_metadata": { + "inputs": [ + { + "name": "images", + "element_type": "float32", + "shape": [ + 1, + 3, + 640, + 640 + ] + } + ], + "outputs": [ + { + "name": "output0", + "element_type": "float32", + "shape": [ + 1, + 84, + 8400 + ] + } + ] + }, + "extra": { + "runtime": "inferedge-runtime", + "json_export": "enabled", + "output_mode": "explicit", + "latest_path": "results/latest.json", + "manifest_recorded": true, + "manifest_precision": "fp16", + "manifest_format": "engine", + "manifest_preset_name": "tensorrt/jetson_fp16", + "manifest_build_id": "yolov8n-tensorrt-jetson_fp16-20260424T133518Z", + "source_model_path": "models/onnx/yolov8n.onnx", + "source_model_sha256": "4b31ebf8213f2971b8136f7ccca475e27f40559a14bc27e0d8a531a933273eb7", + "runtime_artifact_sha256": "29484d824f5be2dfd3e1e801e927298f15f8e77af785711ac6fd429a7445ea22", + "runtime_artifact_path": "/home/risenano01/InferEdgeForge/builds/yolov8n__jetson__tensorrt__jetson_fp16/model.engine", + "input_mode": "dummy", + "input_path": "", + "input_preprocess": "dummy_zero_float32", + "power_mode": "25W", + "jetson_clocks": "unknown", + "tegrastats_log_path": "results/jetson_evidence/tegrastats_yolov8n_trt_fp16_25w_20260504T170039Z.log", + "tegrastats_status": "parsed", + "compare_ready": true, + "compare_key": "yolov8n__b1__h640w640__fp16", + "backend_key": "tensorrt__jetson", + "compare_model_source": "manifest_source_model", + "compare_model_name": "yolov8n" + } +} diff --git a/inferedgelab/studio/routes.py b/inferedgelab/studio/routes.py index 8897ada..f06aabc 100644 --- a/inferedgelab/studio/routes.py +++ b/inferedgelab/studio/routes.py @@ -25,7 +25,7 @@ VALIDATION_PROBLEM_DIR = Path(__file__).resolve().parents[2] / "examples" / "validation_demo" / "problem_cases" DEMO_EVIDENCE_FILES = ( "onnxruntime_cpu_result.json", - "tensorrt_jetson_result.json", + "tensorrt_jetson_25w_result.json", ) DEMO_EVALUATION_REPORT = "yolov8_coco_subset_evaluation.json" DEMO_PROBLEM_REPORTS = ( @@ -174,6 +174,7 @@ def studio_demo_evidence(request: Request) -> dict[str, Any]: evaluation_report = _load_demo_evaluation_report() problem_cases = _load_demo_problem_cases() guard_demo_cases = _load_aiguard_portfolio_cases() + jetson_evidence_track = _build_jetson_evidence_track(results) imported_results = _get_imported_results(request) imported_results.extend(results) guard_analysis = _build_demo_guard_analysis(results, evaluation_report) @@ -183,7 +184,14 @@ def studio_demo_evidence(request: Request) -> dict[str, Any]: results[1], guard_analysis=guard_analysis, ) - demo_job = _build_demo_job(results, compare, evaluation_report, problem_cases, guard_demo_cases) + demo_job = _build_demo_job( + results, + compare, + evaluation_report, + problem_cases, + guard_demo_cases, + jetson_evidence_track, + ) _get_demo_jobs(request)[DEMO_JOB_ID] = demo_job return { "status": "loaded", @@ -197,6 +205,7 @@ def studio_demo_evidence(request: Request) -> dict[str, Any]: "evaluation_report": evaluation_report, "problem_cases": problem_cases, "guard_demo_cases": guard_demo_cases, + "jetson_evidence_track": jetson_evidence_track, "guard_analysis": guard_analysis, "deployment_decision": compare["deployment_decision"], } @@ -455,6 +464,7 @@ def _build_demo_job( evaluation_report: dict[str, Any], problem_cases: list[dict[str, Any]], guard_demo_cases: dict[str, Any], + jetson_evidence_track: dict[str, Any], ) -> dict[str, Any]: now = _utc_now_iso() runtime_result = results[-1] if results else {} @@ -477,6 +487,7 @@ def _build_demo_job( "evaluation_report": evaluation_report, "problem_cases": problem_cases, "guard_demo_cases": guard_demo_cases, + "jetson_evidence_track": jetson_evidence_track, "summary": compare["judgement"]["summary"], }, "error": None, @@ -488,6 +499,51 @@ def _build_demo_job( } +def _build_jetson_evidence_track(results: list[dict[str, Any]]) -> dict[str, Any]: + candidate = next( + ( + result + for result in reversed(results) + if str(result.get("backend_key") or "").startswith("tensorrt__") + and str(result.get("device_name") or _display_value(result.get("device"))).lower() == "jetson" + ), + {}, + ) + jetson_evidence = candidate.get("jetson_evidence") if isinstance(candidate, dict) else {} + if not isinstance(jetson_evidence, dict): + jetson_evidence = {} + run_config = candidate.get("run_config") if isinstance(candidate.get("run_config"), dict) else {} + tegrastats_summary = jetson_evidence.get("tegrastats_summary") + if not isinstance(tegrastats_summary, dict): + tegrastats_summary = {} + + return { + "track": "jetson_runtime_evidence", + "scope": "Jetson Orin Nano TensorRT FP16 25W Runtime smoke", + "runtime_result_source": candidate.get("_source_path") or "examples/studio_demo/tensorrt_jetson_25w_result.json", + "backend_key": candidate.get("backend_key"), + "compare_key": candidate.get("compare_key"), + "precision": candidate.get("precision"), + "power_mode": jetson_evidence.get("power_mode") or run_config.get("power_mode"), + "jetson_clocks": jetson_evidence.get("jetson_clocks") or run_config.get("jetson_clocks"), + "mean_ms": candidate.get("mean_ms"), + "p50_ms": candidate.get("p50_ms") or (candidate.get("latency_ms") or {}).get("p50"), + "p95_ms": candidate.get("p95_ms") or (candidate.get("latency_ms") or {}).get("p95"), + "p99_ms": candidate.get("p99_ms"), + "fps_value": candidate.get("fps_value") or candidate.get("fps"), + "tegrastats_status": tegrastats_summary.get("status"), + "tegrastats_samples": tegrastats_summary.get("sample_count"), + "max_temp_c": tegrastats_summary.get("max_temp_c"), + "max_temp_name": tegrastats_summary.get("max_temp_name"), + "vdd_in_mw_avg": tegrastats_summary.get("vdd_in_mw_avg"), + "vdd_in_mw_max": tegrastats_summary.get("vdd_in_mw_max"), + "note": ( + "This is empirical Jetson runtime evidence for the Local Studio demo, " + "not a production inference server or cloud benchmark track." + ), + } + + def _build_demo_guard_analysis( results: list[dict[str, Any]], evaluation_report: dict[str, Any], diff --git a/inferedgelab/studio/static/app.js b/inferedgelab/studio/static/app.js index 576ead9..8502b1e 100644 --- a/inferedgelab/studio/static/app.js +++ b/inferedgelab/studio/static/app.js @@ -32,6 +32,7 @@ let demoEvaluationReport = null; let demoProblemCases = []; let activeGuardAnalysis = null; let guardDemoCases = null; +let demoJetsonEvidence = null; const importedResultsByJobId = {}; function createElement(tagName, className, textContent) { @@ -375,6 +376,7 @@ async function loadDemoEvidence() { demoEvaluationReport = payload.evaluation_report || null; demoProblemCases = Array.isArray(payload.problem_cases) ? payload.problem_cases : []; guardDemoCases = payload.guard_demo_cases || null; + demoJetsonEvidence = payload.jetson_evidence_track || null; compareData = payload.compare || null; updateGuardEvidence(payload.guard_analysis || payload.compare?.guard_analysis || null); selectedJobId = payload.job_id || payload.job?.job_id || selectedJobId; @@ -501,6 +503,15 @@ function renderDemoEvaluation(report) { evidenceItem("contract", contract.status || "-"), evidenceItem("report", report.source || "-"), ); + + if (demoJetsonEvidence) { + target.append( + evidenceItem("jetson_power", demoJetsonEvidence.power_mode || "-"), + evidenceItem("jetson_p95", demoJetsonEvidence.p95_ms === undefined ? "-" : `${formatNumber(demoJetsonEvidence.p95_ms)} ms`), + evidenceItem("jetson_temp", demoJetsonEvidence.max_temp_c === undefined ? "-" : `${formatNumber(demoJetsonEvidence.max_temp_c)} C`), + evidenceItem("tegrastats", demoJetsonEvidence.tegrastats_status || "-"), + ); + } } function renderPipeline() { @@ -1144,7 +1155,22 @@ function findResultByBackend(results, keyword) { } function runtimeModelName(result = {}) { - return firstDisplayValue(result.model_name, result.model?.name, result.model, result.model_path); + return firstDisplayValue( + result.extra?.compare_model_name, + pathBasename(result.extra?.source_model_path), + result.model_name, + result.model?.name, + result.model, + result.model_path, + ); +} + +function pathBasename(path) { + if (!path || typeof path !== "string") { + return ""; + } + const parts = path.split(/[\\/]/).filter(Boolean); + return parts[parts.length - 1] || path; } function runtimeBackendName(result = {}) { diff --git a/tests/test_studio_routes.py b/tests/test_studio_routes.py index 521d33e..ae6199f 100644 --- a/tests/test_studio_routes.py +++ b/tests/test_studio_routes.py @@ -132,6 +132,7 @@ def test_studio_static_assets_include_redesigned_ui_contracts(): assert "request record only" in app_text assert "loadDemoEvidence" in app_text assert "renderDemoEvaluation" in app_text + assert "demoJetsonEvidence" in app_text assert "renderDemoProblemCases" in app_text assert "renderGuardEvidence" in app_text assert "renderGuardDemoCases" in app_text @@ -361,9 +362,19 @@ def test_studio_demo_evidence_loads_compare_ready_pair(): assert response["results"][0]["backend_key"] == "onnxruntime__cpu" assert response["results"][1]["backend_key"] == "tensorrt__jetson" assert response["results"][0]["mean_ms"] == 45.4299 - assert response["results"][1]["mean_ms"] == 9.9375 + assert response["results"][1]["mean_ms"] == 10.066401 + assert response["results"][1]["precision"] == "fp16" + assert response["results"][1]["run_config"]["power_mode"] == "25W" + assert response["results"][1]["jetson_evidence"]["tegrastats_summary"]["status"] == "parsed" + assert response["jetson_evidence_track"]["power_mode"] == "25W" + assert response["jetson_evidence_track"]["mean_ms"] == 10.066401 + assert response["jetson_evidence_track"]["p95_ms"] == 15.476641 + assert response["jetson_evidence_track"]["tegrastats_status"] == "parsed" + assert response["jetson_evidence_track"]["runtime_result_source"] == ( + "examples/studio_demo/tensorrt_jetson_25w_result.json" + ) assert response["compare"]["status"] == "ok" - assert response["compare"]["judgement"]["overall"] == "improvement" + assert response["compare"]["judgement"]["overall"] == "tradeoff_faster" assert response["guard_analysis"]["guard_verdict"] == "review_required" assert response["guard_analysis"]["evidence"][0]["metric_name"] == "map50" assert response["guard_demo_cases"]["schema_version"] == "inferedge-aiguard-portfolio-demo-v1" @@ -417,6 +428,8 @@ def test_studio_demo_evidence_is_listed_and_selectable_as_job(): assert detail["job_id"] == "demo_yolov8n_trt_vs_onnx" assert detail["status"] == "completed" assert detail["result"]["runtime_result"]["backend_key"] == "tensorrt__jetson" + assert detail["result"]["runtime_result"]["run_config"]["power_mode"] == "25W" + assert detail["result"]["jetson_evidence_track"]["power_mode"] == "25W" assert detail["result"]["guard_analysis"]["guard_verdict"] == "review_required" assert detail["result"]["guard_demo_cases"]["case_count"] == 4 assert detail["result"]["comparison"]["base"]["backend_key"] == "onnxruntime__cpu"