ttnn-visualizer 0.47.0__py3-none-any.whl → 0.49.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ttnn_visualizer/models.py +1 -0
- ttnn_visualizer/pytest_plugin.py +93 -0
- ttnn_visualizer/queries.py +1 -1
- ttnn_visualizer/sockets.py +31 -0
- ttnn_visualizer/static/assets/allPaths-G_CNx_x1.js +1 -0
- ttnn_visualizer/static/assets/allPathsLoader-s_Yfmxfp.js +2 -0
- ttnn_visualizer/static/assets/index-CnPrfHYh.js +1 -0
- ttnn_visualizer/static/assets/index-Cnc1EkDo.js +1 -0
- ttnn_visualizer/static/assets/{index-CmA3KkTi.js → index-DVrPLQJ7.js} +254 -254
- ttnn_visualizer/static/assets/index-UuXdrHif.css +7 -0
- ttnn_visualizer/static/assets/splitPathsBySizeLoader-ivxxaHxa.js +1 -0
- ttnn_visualizer/static/index.html +2 -2
- ttnn_visualizer/tests/test_serializers.py +6 -0
- ttnn_visualizer/views.py +79 -13
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/METADATA +2 -1
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/RECORD +21 -20
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/licenses/LICENSE +13 -5
- ttnn_visualizer/static/assets/allPaths-OR2-IW-_.js +0 -1
- ttnn_visualizer/static/assets/allPathsLoader-xRXweacG.js +0 -2
- ttnn_visualizer/static/assets/index-03c8d4Gh.js +0 -1
- ttnn_visualizer/static/assets/index-DJA68-a6.css +0 -7
- ttnn_visualizer/static/assets/index-PKNBViIU.js +0 -1
- ttnn_visualizer/static/assets/splitPathsBySizeLoader-P9sdNg6R.js +0 -1
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/WHEEL +0 -0
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/entry_points.txt +0 -0
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/licenses/LICENSE_understanding.txt +0 -0
- {ttnn_visualizer-0.47.0.dist-info → ttnn_visualizer-0.49.0.dist-info}/top_level.txt +0 -0
ttnn_visualizer/models.py
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
#
|
3
|
+
# SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
|
4
|
+
import json
|
5
|
+
import os
|
6
|
+
import urllib
|
7
|
+
|
8
|
+
try:
|
9
|
+
import ttnn
|
10
|
+
from loguru import logger
|
11
|
+
from tt_metal.tools.profiler.common import PROFILER_OUTPUT_DIR
|
12
|
+
except ImportError:
|
13
|
+
raise Exception("TT-Metal environment not found")
|
14
|
+
|
15
|
+
|
16
|
+
def pytest_runtest_makereport(item, call):
|
17
|
+
if call.when != "call":
|
18
|
+
return
|
19
|
+
|
20
|
+
webhook_url = os.environ.get("TTNN_VISUALIZER_WEBHOOK_URL", "").strip()
|
21
|
+
|
22
|
+
if not webhook_url:
|
23
|
+
webhook_url = "http://localhost:8000/api/notify"
|
24
|
+
|
25
|
+
status_str = _get_test_status_from_call(call)
|
26
|
+
_notify_visualizer_webhook(status_str, webhook_url)
|
27
|
+
|
28
|
+
|
29
|
+
def _notify_visualizer_webhook(status_str: str, webhook_url: str) -> None:
|
30
|
+
"""POST JSON to the visualizer webhook endpoint."""
|
31
|
+
payload = {
|
32
|
+
"report_name": _get_ttnn_report_name(),
|
33
|
+
"profiler_path": _get_profiler_path(),
|
34
|
+
"performance_path": _get_performance_path(),
|
35
|
+
"status": status_str,
|
36
|
+
}
|
37
|
+
|
38
|
+
data = json.dumps(payload).encode("utf-8")
|
39
|
+
headers = {"Content-Type": "application/json"}
|
40
|
+
try:
|
41
|
+
request = urllib.request.Request(
|
42
|
+
webhook_url, data=data, headers=headers, method="POST"
|
43
|
+
)
|
44
|
+
logger.info(f"Posting report to TTNN-Visualizer: {payload}")
|
45
|
+
urllib.request.urlopen(request, timeout=2)
|
46
|
+
except urllib.error.HTTPError as error:
|
47
|
+
logger.error(
|
48
|
+
f"Error posting report to TTNN-Visualizer: {error.status} {error.reason}"
|
49
|
+
)
|
50
|
+
except Exception as err:
|
51
|
+
logger.error(err)
|
52
|
+
|
53
|
+
|
54
|
+
def _get_test_status_from_call(call) -> str:
|
55
|
+
"""Determine test status from the call object."""
|
56
|
+
if call.excinfo is None:
|
57
|
+
return "PASS"
|
58
|
+
elif call.excinfo[0] == AssertionError:
|
59
|
+
return "FAIL"
|
60
|
+
else:
|
61
|
+
return "ERROR"
|
62
|
+
|
63
|
+
|
64
|
+
def _get_ttnn_report_name() -> str:
|
65
|
+
name = ttnn.CONFIG.report_name
|
66
|
+
return str(name) if name else "Not Available"
|
67
|
+
|
68
|
+
|
69
|
+
def _get_profiler_path() -> str:
|
70
|
+
tt_metal_home = os.getenv("TT_METAL_HOME", "")
|
71
|
+
return f"{tt_metal_home}/{ttnn.CONFIG.report_path}"
|
72
|
+
|
73
|
+
|
74
|
+
def _get_performance_path() -> str:
|
75
|
+
output_dir = PROFILER_OUTPUT_DIR
|
76
|
+
|
77
|
+
if os.getenv("TT_METAL_DEVICE_PROFILER", None) != "1":
|
78
|
+
return None
|
79
|
+
|
80
|
+
try:
|
81
|
+
directories = [item for item in output_dir.iterdir() if item.is_dir()]
|
82
|
+
|
83
|
+
if not directories:
|
84
|
+
return None
|
85
|
+
|
86
|
+
# Find the newest directory by modification time
|
87
|
+
newest_dir = max(directories, key=lambda d: d.stat().st_mtime)
|
88
|
+
return str(newest_dir)
|
89
|
+
|
90
|
+
except (OSError, ValueError):
|
91
|
+
return None
|
92
|
+
|
93
|
+
return str(output_dir)
|
ttnn_visualizer/queries.py
CHANGED
@@ -146,7 +146,7 @@ class DatabaseQueries:
|
|
146
146
|
) -> Generator[Buffer, None, None]:
|
147
147
|
rows = self._query_table("buffers", filters)
|
148
148
|
for row in rows:
|
149
|
-
yield Buffer(*row)
|
149
|
+
yield Buffer(*row[:6])
|
150
150
|
|
151
151
|
def query_stack_traces(
|
152
152
|
self, filters: Optional[Dict[str, Any]] = None
|
ttnn_visualizer/sockets.py
CHANGED
@@ -17,6 +17,7 @@ logger = getLogger(__name__)
|
|
17
17
|
|
18
18
|
class Messages(object):
|
19
19
|
FILE_TRANSFER_PROGRESS = "fileTransferProgress"
|
20
|
+
REPORT_GENERATED = "reportGenerated"
|
20
21
|
|
21
22
|
|
22
23
|
class FileStatus(Enum):
|
@@ -27,6 +28,12 @@ class FileStatus(Enum):
|
|
27
28
|
STARTED = "STARTED"
|
28
29
|
|
29
30
|
|
31
|
+
class ExitStatus(Enum):
|
32
|
+
PASS = "PASS"
|
33
|
+
FAIL = "FAIL"
|
34
|
+
ERROR = "ERROR"
|
35
|
+
|
36
|
+
|
30
37
|
@dataclass
|
31
38
|
class FileProgress(SerializeableDataclass):
|
32
39
|
current_file_name: str
|
@@ -40,6 +47,16 @@ class FileProgress(SerializeableDataclass):
|
|
40
47
|
self.percent_of_current = round(self.percent_of_current, 2)
|
41
48
|
|
42
49
|
|
50
|
+
@dataclass
|
51
|
+
class ReportGenerated(SerializeableDataclass):
|
52
|
+
report_name: str
|
53
|
+
profiler_path: str | None = None
|
54
|
+
performance_path: str | None = None
|
55
|
+
exit_status: ExitStatus | None = None
|
56
|
+
message_type: str = "report_generated"
|
57
|
+
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
|
58
|
+
|
59
|
+
|
43
60
|
# For tracking connected clients subscriber ID
|
44
61
|
tab_clients = {}
|
45
62
|
|
@@ -77,6 +94,20 @@ def emit_file_status(progress: FileProgress, instance_id=None):
|
|
77
94
|
debounce_timer.start()
|
78
95
|
|
79
96
|
|
97
|
+
def emit_report_generated(report_generated: ReportGenerated):
|
98
|
+
"""Emit a report update notification to all connected clients."""
|
99
|
+
try:
|
100
|
+
if socketio is not None and hasattr(socketio, "emit"):
|
101
|
+
data = report_generated.to_dict()
|
102
|
+
socketio.emit(Messages.REPORT_GENERATED, data)
|
103
|
+
logger.info(
|
104
|
+
f"Report update notification sent: {report_generated.report_name}"
|
105
|
+
)
|
106
|
+
except NameError:
|
107
|
+
logger.warning("SocketIO not available - skipping report update notification")
|
108
|
+
pass # Can silently pass since we know the NameError is from sockets being disabled
|
109
|
+
|
110
|
+
|
80
111
|
def register_handlers(socketio_instance):
|
81
112
|
global socketio
|
82
113
|
socketio = socketio_instance
|
@@ -0,0 +1 @@
|
|
1
|
+
import{I as s}from"./index-CnPrfHYh.js";import{I as r}from"./index-Cnc1EkDo.js";import{p as n,I as c}from"./index-DVrPLQJ7.js";function p(t,a){const o=n(t);return a===c.STANDARD?s[o]:r[o]}export{s as IconSvgPaths16,r as IconSvgPaths20,p as getIconPaths};
|
@@ -0,0 +1,2 @@
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-G_CNx_x1.js","assets/index-CnPrfHYh.js","assets/index-Cnc1EkDo.js","assets/index-DVrPLQJ7.js","assets/index-UuXdrHif.css"])))=>i.map(i=>d[i]);
|
2
|
+
import{_ as e}from"./index-DVrPLQJ7.js";const s=async(t,a)=>{const{getIconPaths:o}=await e(async()=>{const{getIconPaths:r}=await import("./allPaths-G_CNx_x1.js");return{getIconPaths:r}},__vite__mapDeps([0,1,2,3,4]));return o(t,a)};export{s as allPathsLoader};
|