ttnn-visualizer 0.40.3__py3-none-any.whl → 0.42.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.
Files changed (23) hide show
  1. ttnn_visualizer/app.py +21 -6
  2. ttnn_visualizer/csv_queries.py +135 -32
  3. ttnn_visualizer/decorators.py +7 -4
  4. ttnn_visualizer/exceptions.py +16 -0
  5. ttnn_visualizer/queries.py +3 -109
  6. ttnn_visualizer/remote_sqlite_setup.py +97 -19
  7. ttnn_visualizer/requirements.txt +1 -3
  8. ttnn_visualizer/sftp_operations.py +637 -219
  9. ttnn_visualizer/static/assets/{allPaths-DSFl5HNA.js → allPaths-wwXsGKJ2.js} +1 -1
  10. ttnn_visualizer/static/assets/{allPathsLoader-BwkPDbOI.js → allPathsLoader-BK9jqlVe.js} +2 -2
  11. ttnn_visualizer/static/assets/{index-CWerbNbe.js → index-Ybr1HJxx.js} +202 -202
  12. ttnn_visualizer/static/assets/{splitPathsBySizeLoader-DoKJAUb8.js → splitPathsBySizeLoader-CauQGZHk.js} +1 -1
  13. ttnn_visualizer/static/index.html +5 -5
  14. ttnn_visualizer/tests/test_queries.py +0 -68
  15. ttnn_visualizer/views.py +115 -9
  16. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/LICENSE +0 -1
  17. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/METADATA +2 -3
  18. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/RECORD +22 -23
  19. ttnn_visualizer/ssh_client.py +0 -85
  20. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/LICENSE_understanding.txt +0 -0
  21. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/WHEEL +0 -0
  22. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/entry_points.txt +0 -0
  23. {ttnn_visualizer-0.40.3.dist-info → ttnn_visualizer-0.42.0.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- import{_ as o,a as _,b as i,p as c,I as u}from"./index-CWerbNbe.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-BKzgFDAn.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BvSuWPlB.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
1
+ import{_ as o,a as _,b as i,p as c,I as u}from"./index-Ybr1HJxx.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-BKzgFDAn.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BvSuWPlB.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
@@ -13,17 +13,17 @@
13
13
  <link
14
14
  rel="icon"
15
15
  type="image/svg+xml"
16
- href="/favicon/favicon.svg"
16
+ href="/static/favicon/favicon.svg"
17
17
  />
18
18
  <link
19
19
  rel="icon"
20
20
  type="image/png"
21
21
  sizes="32x32"
22
- href="/favicon/favicon-32x32.png"
22
+ href="/static/favicon/favicon-32x32.png"
23
23
  />
24
24
  <link
25
25
  rel="manifest"
26
- href="/assets/site-BTBrvHC5.webmanifest"
26
+ href="/static/assets/site-BTBrvHC5.webmanifest"
27
27
  />
28
28
  <meta
29
29
  name="theme-color"
@@ -34,8 +34,8 @@
34
34
  /* SERVER_CONFIG */
35
35
  </script>
36
36
 
37
- <script type="module" crossorigin src="/assets/index-CWerbNbe.js"></script>
38
- <link rel="stylesheet" crossorigin href="/assets/index-C1rJBrMl.css">
37
+ <script type="module" crossorigin src="/static/assets/index-Ybr1HJxx.js"></script>
38
+ <link rel="stylesheet" crossorigin href="/static/assets/index-C1rJBrMl.css">
39
39
  </head>
40
40
  <body>
41
41
 
@@ -16,7 +16,6 @@ from ttnn_visualizer.models import (
16
16
  )
17
17
  from ttnn_visualizer.queries import DatabaseQueries
18
18
  from ttnn_visualizer.queries import LocalQueryRunner
19
- from ttnn_visualizer.queries import RemoteQueryRunner
20
19
 
21
20
 
22
21
  class TestQueryTable(unittest.TestCase):
@@ -222,14 +221,6 @@ class TestDatabaseQueries(unittest.TestCase):
222
221
  str(context.exception),
223
222
  )
224
223
 
225
- @patch("ttnn_visualizer.queries.get_client")
226
- def test_init_with_valid_remote_instance(self, _mock_client):
227
- mock_instance = Mock()
228
- mock_instance.remote_connection = Mock(useRemoteQuerying=True)
229
- mock_instance.remote_connection.sqliteBinaryPath = "/usr/bin/sqlite3"
230
- mock_instance.remote_profiler_folder = Mock(remotePath="/remote/path")
231
- db_queries = DatabaseQueries(instance=mock_instance)
232
- self.assertIsInstance(db_queries.query_runner, RemoteQueryRunner)
233
224
 
234
225
  def test_init_with_valid_local_instance(self):
235
226
  with tempfile.NamedTemporaryFile(suffix=".sqlite") as temp_db_file:
@@ -381,64 +372,5 @@ class TestDatabaseQueries(unittest.TestCase):
381
372
  self.assertEqual(result.operation_id, 2)
382
373
 
383
374
 
384
- class TestRemoteQueryRunner(unittest.TestCase):
385
-
386
- def setUp(self):
387
- self.mock_instance = Mock()
388
- self.mock_instance.remote_connection.sqliteBinaryPath = "/usr/bin/sqlite3"
389
- self.mock_instance.remote_connection.host = "mockhost"
390
- self.mock_instance.remote_connection.user = "mockuser"
391
- self.mock_instance.remote_profiler_folder.remotePath = "/remote/db"
392
-
393
- @patch("ttnn_visualizer.queries.get_client")
394
- def test_init_with_mock_get_client(self, mock_get_client):
395
- # Mock the SSHClient returned by get_client
396
- mock_ssh_client = Mock()
397
- mock_get_client.return_value = mock_ssh_client
398
-
399
- runner = RemoteQueryRunner(instance=self.mock_instance)
400
- self.assertEqual(runner.ssh_client, mock_ssh_client)
401
- mock_get_client.assert_called_once_with(
402
- remote_connection=self.mock_instance.remote_connection
403
- )
404
-
405
- @patch("ttnn_visualizer.queries.get_client")
406
- def test_execute_query(self, mock_get_client):
407
- # Mock the SSH client
408
- mock_ssh_client = Mock()
409
- mock_get_client.return_value = mock_ssh_client
410
-
411
- mock_stdout = Mock()
412
- mock_stdout.read.return_value = b'[{"col1": "value1", "col2": "value2"}]'
413
- mock_stderr = Mock()
414
- mock_stderr.read.return_value = b""
415
- mock_ssh_client.exec_command.return_value = (None, mock_stdout, mock_stderr)
416
-
417
- runner = RemoteQueryRunner(instance=self.mock_instance)
418
-
419
- query = "SELECT * FROM table WHERE id = ?"
420
- params = [1]
421
- results = runner.execute_query(query, params)
422
-
423
- # Validate results
424
- self.assertEqual(results, [("value1", "value2")])
425
- mock_get_client.assert_called_once()
426
- mock_ssh_client.exec_command.assert_called_once()
427
-
428
- @patch("ttnn_visualizer.queries.get_client")
429
- def test_close(self, mock_get_client):
430
- # Mock the SSH client
431
- mock_ssh_client = Mock()
432
- mock_get_client.return_value = mock_ssh_client
433
-
434
- runner = RemoteQueryRunner(instance=self.mock_instance)
435
-
436
- runner.close()
437
- mock_ssh_client.close.assert_called_once()
438
-
439
- def tearDown(self):
440
- pass
441
-
442
-
443
375
  if __name__ == "__main__":
444
376
  unittest.main()
ttnn_visualizer/views.py CHANGED
@@ -6,13 +6,13 @@ import dataclasses
6
6
  import json
7
7
  import logging
8
8
  import re
9
+ import shutil
9
10
  import time
10
11
  from http import HTTPStatus
11
12
  from pathlib import Path
12
13
  from typing import List
13
- import shutil
14
- from wsgiref.validate import bad_header_value_re
15
14
 
15
+ import yaml
16
16
  import zstd
17
17
  from flask import (
18
18
  Blueprint,
@@ -23,7 +23,8 @@ from flask import (
23
23
  request,
24
24
  )
25
25
 
26
- from ttnn_visualizer.csv_queries import DeviceLogProfilerQueries, OpsPerformanceQueries, OpsPerformanceReportQueries
26
+ from ttnn_visualizer.csv_queries import DeviceLogProfilerQueries, OpsPerformanceQueries, OpsPerformanceReportQueries, \
27
+ NPEQueries
27
28
  from ttnn_visualizer.decorators import with_instance, local_only
28
29
  from ttnn_visualizer.enums import ConnectionTestStates
29
30
  from ttnn_visualizer.exceptions import DataFormatError
@@ -34,6 +35,9 @@ from ttnn_visualizer.file_uploads import (
34
35
  save_uploaded_files,
35
36
  validate_files,
36
37
  )
38
+ from ttnn_visualizer.instances import (
39
+ get_instances, update_instance,
40
+ )
37
41
  from ttnn_visualizer.models import (
38
42
  RemoteReportFolder,
39
43
  RemoteConnection,
@@ -51,9 +55,6 @@ from ttnn_visualizer.serializers import (
51
55
  serialize_operations_buffers,
52
56
  serialize_devices, serialize_buffer,
53
57
  )
54
- from ttnn_visualizer.instances import (
55
- get_instances, update_instance,
56
- )
57
58
  from ttnn_visualizer.sftp_operations import (
58
59
  sync_remote_profiler_folders,
59
60
  read_remote_file,
@@ -64,14 +65,91 @@ from ttnn_visualizer.sftp_operations import (
64
65
  sync_remote_performance_folders,
65
66
  get_cluster_desc,
66
67
  )
67
- from ttnn_visualizer.ssh_client import get_client
68
+ from ttnn_visualizer.exceptions import SSHException, AuthenticationException, NoValidConnectionsError
69
+ import subprocess
68
70
  from ttnn_visualizer.utils import (
69
71
  get_cluster_descriptor_path,
70
72
  read_last_synced_file,
71
73
  timer,
72
74
  )
73
75
 
74
- import yaml
76
+
77
+ def handle_ssh_subprocess_error(e: subprocess.CalledProcessError, remote_connection):
78
+ """
79
+ Convert subprocess SSH errors to appropriate SSH exceptions.
80
+
81
+ :param e: The subprocess.CalledProcessError
82
+ :param remote_connection: The RemoteConnection object for context
83
+ :raises: SSHException, AuthenticationException, or NoValidConnectionsError
84
+ """
85
+ stderr = e.stderr.lower() if e.stderr else ""
86
+
87
+ # Check for authentication failures
88
+ if any(auth_err in stderr for auth_err in [
89
+ "permission denied",
90
+ "authentication failed",
91
+ "publickey",
92
+ "password",
93
+ "host key verification failed"
94
+ ]):
95
+ raise AuthenticationException(f"SSH authentication failed: {e.stderr}")
96
+
97
+ # Check for connection failures
98
+ elif any(conn_err in stderr for conn_err in [
99
+ "connection refused",
100
+ "network is unreachable",
101
+ "no route to host",
102
+ "name or service not known",
103
+ "connection timed out"
104
+ ]):
105
+ raise NoValidConnectionsError(f"SSH connection failed: {e.stderr}")
106
+
107
+ # Check for general SSH protocol errors
108
+ elif "ssh:" in stderr or "protocol" in stderr:
109
+ raise SSHException(f"SSH protocol error: {e.stderr}")
110
+
111
+ # Default to generic SSH exception
112
+ else:
113
+ raise SSHException(f"SSH command failed: {e.stderr}")
114
+
115
+
116
+ def test_ssh_connection(connection) -> bool:
117
+ """Test SSH connection by running a simple command."""
118
+ ssh_cmd = ["ssh"]
119
+
120
+ # Handle non-standard SSH port
121
+ if connection.port != 22:
122
+ ssh_cmd.extend(["-p", str(connection.port)])
123
+
124
+ ssh_cmd.extend([
125
+ f"{connection.username}@{connection.host}",
126
+ "echo 'SSH connection test'"
127
+ ])
128
+
129
+ try:
130
+ result = subprocess.run(
131
+ ssh_cmd,
132
+ capture_output=True,
133
+ text=True,
134
+ check=True,
135
+ timeout=10
136
+ )
137
+ return True
138
+ except subprocess.CalledProcessError as e:
139
+ if e.returncode == 255: # SSH protocol errors
140
+ handle_ssh_subprocess_error(e, connection)
141
+ else:
142
+ raise RemoteConnectionException(
143
+ message=f"SSH connection test failed: {e.stderr}",
144
+ status=ConnectionTestStates.FAILED
145
+ )
146
+ return False
147
+ except subprocess.TimeoutExpired:
148
+ raise RemoteConnectionException(
149
+ message="SSH connection test timed out",
150
+ status=ConnectionTestStates.FAILED
151
+ )
152
+ return False
75
153
 
76
154
  logger = logging.getLogger(__name__)
77
155
 
@@ -636,6 +714,34 @@ def get_performance_data_raw(instance: Instance):
636
714
  headers={"Content-Disposition": "attachment; filename=profile_log_device.csv"},
637
715
  )
638
716
 
717
+ @api.route("/performance/npe/manifest", methods=["GET"])
718
+ @with_instance
719
+ def get_npe_manifest(instance: Instance):
720
+ if not instance.performance_path:
721
+ return Response(status=HTTPStatus.NOT_FOUND)
722
+ try:
723
+ content = NPEQueries.get_npe_manifest(instance)
724
+ except FileNotFoundError:
725
+ return jsonify([])
726
+
727
+ return jsonify(content)
728
+
729
+
730
+ @api.route("/performance/npe/timeline", methods=["GET"])
731
+ @with_instance
732
+ def get_npe_timeline(instance: Instance):
733
+ if not instance.performance_path:
734
+ return Response(status=HTTPStatus.NOT_FOUND)
735
+
736
+ filename = request.args.get("filename", default=None)
737
+
738
+ try:
739
+ content = NPEQueries.get_npe_timeline(instance, filename=filename)
740
+ except FileNotFoundError:
741
+ return jsonify({})
742
+
743
+ return jsonify(content)
744
+
639
745
 
640
746
  @api.route("/performance/device-log/zone/<zone>", methods=["GET"])
641
747
  @with_instance
@@ -901,7 +1007,7 @@ def test_remote_folder():
901
1007
 
902
1008
  # Test SSH Connection
903
1009
  try:
904
- get_client(connection)
1010
+ test_ssh_connection(connection)
905
1011
  add_status(ConnectionTestStates.OK.value, "SSH connection established")
906
1012
  except RemoteConnectionException as e:
907
1013
  add_status(ConnectionTestStates.FAILED.value, e.message)
@@ -109,7 +109,6 @@ The following separate and independent dependencies are utilized by this project
109
109
  - Flask – BSD‑3‑Clause – https://github.com/pallets/flask/blob/main/LICENSE.txt
110
110
  - gunicorn – MIT – https://github.com/benoitc/gunicorn/blob/master/LICENSE
111
111
  - uvicorn – BSD‑3‑Clause – https://github.com/encode/uvicorn/blob/master/LICENSE.md
112
- - paramiko~=3.4.0 – LGPL‑2.1+ – https://github.com/paramiko/paramiko/blob/main/LICENSE
113
112
  - flask_cors – MIT – https://github.com/corydolphin/flask-cors/blob/main/LICENSE
114
113
  - pydantic – MIT – https://github.com/pydantic/pydantic-settings/blob/main/LICENSE
115
114
  - setuptools – MIT – https://github.com/pypa/setuptools/blob/main/LICENSE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ttnn_visualizer
3
- Version: 0.40.3
3
+ Version: 0.42.0
4
4
  Summary: TT-NN Visualizer
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -16,7 +16,6 @@ Requires-Dist: Flask-SocketIO==5.4.1
16
16
  Requires-Dist: Flask-SQLAlchemy==3.1.1
17
17
  Requires-Dist: pandas==2.2.3
18
18
  Requires-Dist: gunicorn~=22.0.0
19
- Requires-Dist: paramiko~=3.4.0
20
19
  Requires-Dist: pydantic==2.7.3
21
20
  Requires-Dist: pydantic-core==2.18.4
22
21
  Requires-Dist: setuptools==65.5.0
@@ -24,7 +23,7 @@ Requires-Dist: gevent===24.10.2
24
23
  Requires-Dist: python-dotenv==1.0.1
25
24
  Requires-Dist: sqlalchemy==2.0.34
26
25
  Requires-Dist: PyYAML==6.0.2
27
- Requires-Dist: tt-perf-report==1.0.6
26
+ Requires-Dist: tt-perf-report==1.0.7
28
27
  Requires-Dist: zstd==1.5.7.0
29
28
 
30
29
 
@@ -1,46 +1,45 @@
1
1
  ttnn_visualizer/__init__.py,sha256=tVr9rTN1gXorz5XQQYmcRkzeEYjDGAKez1gP0oOIYeY,94
2
- ttnn_visualizer/app.py,sha256=bBVl93IhAncu5AOSeym2U7t8VIfe5TPQHUgkI9g5BTc,6770
3
- ttnn_visualizer/csv_queries.py,sha256=yFh3pUex6l9elGtSKhBVIZKEBOVmKyFpwTWozy1XxoY,22074
4
- ttnn_visualizer/decorators.py,sha256=YthndmmOfNlCMyjhChA_Lztcx1npFk7g0u4-7XT7Gnc,4571
2
+ ttnn_visualizer/app.py,sha256=QCgd3nyc_I6iwJEAXGmNCvIXvR6oLSGKmLyV_7ECsH8,7222
3
+ ttnn_visualizer/csv_queries.py,sha256=WzY1D_2fzrRzgdLnXf5w_aBwsW695Mkq5h2GXGeshyM,25676
4
+ ttnn_visualizer/decorators.py,sha256=kveD2h6cgXD-rt03uAYWmZ9RKYphgvoGk53g2Pgn_RM,4799
5
5
  ttnn_visualizer/enums.py,sha256=SEIqp1tlc_zw2vQ8nHH9YTaV0m3Cb8fjn_goqz5wurE,203
6
- ttnn_visualizer/exceptions.py,sha256=iwDejaYUkRwHhpv8bc16X_2ax-2QmFqgaajbHvQImN0,1055
6
+ ttnn_visualizer/exceptions.py,sha256=wzD_RuW1L6E2WdfM1pf34e25gisIe9jIMatf3HgyhIA,1375
7
7
  ttnn_visualizer/extensions.py,sha256=AW2aujcvayePfrwovVcsXIUkCFe_k2T4Vk3OblZd4Bk,380
8
8
  ttnn_visualizer/file_uploads.py,sha256=aXolaGeisY3H89y_GxfciPvkSU1XmcvWqpf8vDqKrgM,5011
9
9
  ttnn_visualizer/instances.py,sha256=99L80yT0BXyhEkBbGFC_tNAg8LlGWUqWQrhkK-4-Yxc,11261
10
10
  ttnn_visualizer/models.py,sha256=ut9V9fTzbbjk_WNKKP8ILrVXYnIv7qQaDvNOEEX3BT8,7778
11
- ttnn_visualizer/queries.py,sha256=KC4WtI6Pf3LL_LqHXQTC5zVd-HvjJLWY4Stndd0z0qs,13681
12
- ttnn_visualizer/remote_sqlite_setup.py,sha256=FXZTT4_-7zPG6rooXJ7o3a12r86hRzKt87qfUiKdMcs,3269
13
- ttnn_visualizer/requirements.txt,sha256=AoRskxD-FUPk1j_xruPxweDgKb3RjFv8lCQNqpf6Rrs,370
11
+ ttnn_visualizer/queries.py,sha256=MB57Ci3gyXn9jdXdR6KaMBHoHYt5jgdk3wP7jSDGWFA,9891
12
+ ttnn_visualizer/remote_sqlite_setup.py,sha256=0JfSQLA_ST6gh7lyHhxUdsEsmw8moremC37myES6Al8,5892
13
+ ttnn_visualizer/requirements.txt,sha256=RuHRxxkHKSAAMF7JbvifDMOOFTfQH19AW3qJFL3I69M,339
14
14
  ttnn_visualizer/serializers.py,sha256=7kjtxp3NEpTlBKUNsxdtUcOM23pAyIt5MR-XTl85bVw,8028
15
15
  ttnn_visualizer/settings.py,sha256=r7UdqFuXEVg0qw1F2_zRFM_687PctqpgoUEKjvTVhwU,4578
16
- ttnn_visualizer/sftp_operations.py,sha256=Sn13vkkQoRifCO3SCNGo6ORlwaHB4MO4uSxxo_7m8Dk,17906
16
+ ttnn_visualizer/sftp_operations.py,sha256=p06_dYHoM-qbAZuUUR9CN7fWy4U_QpIaXBxPsjxJfgI,32074
17
17
  ttnn_visualizer/sockets.py,sha256=GAvReqKaT0YQyeb0kf7ndtL1odLldHCpbbKkr0INXRM,3624
18
- ttnn_visualizer/ssh_client.py,sha256=dTkxzAFCGKyiZL1_LHx23Gn4y0oW4yKfGKjTxuqcx8I,2679
19
18
  ttnn_visualizer/utils.py,sha256=9Ier5XBD4vHzvtIxfbIRnsP1Is03J2usUmhfB5gSqqc,6186
20
- ttnn_visualizer/views.py,sha256=MlbN19BYaVgsVxYwN0pqvqqRmOsDPy9gFGjTZosyUUI,41465
19
+ ttnn_visualizer/views.py,sha256=WywffIzF5ahslw6qBDHyfL5kIzDKxbUf_yQXxeMUVgA,44784
21
20
  ttnn_visualizer/bin/docker-entrypoint-web,sha256=uuv6aubpMCfOcuvDBxwBDITE8PN39teuwyJ2zA5KWuw,413
22
21
  ttnn_visualizer/bin/pip3-install,sha256=nbSRT4GfJQIQ9KTNO3j-6b5WM4lrx9XA4GBlAURRMws,502
23
- ttnn_visualizer/static/index.html,sha256=kFTLNN94zVVgKgkuZ6NKCQnVNraDQ6Hrjsyru1JwHT0,1100
24
- ttnn_visualizer/static/assets/allPaths-DSFl5HNA.js,sha256=4rwRYkqQBalBP6PUs5qtVWI0BsBTuTcc7r8_Y7SoP30,309
25
- ttnn_visualizer/static/assets/allPathsLoader-BwkPDbOI.js,sha256=zS7B8U-iYYt6SiEhejfFcjjgWT-fm8OL13TOy9VnFSs,550
22
+ ttnn_visualizer/static/index.html,sha256=gM7cQnEuPT29ZMKqJcEqjW9POf4AigZftNS8uw68ViY,1135
23
+ ttnn_visualizer/static/assets/allPaths-wwXsGKJ2.js,sha256=CxiByiF9vD2BvhrONDu-MQVoLKj-KhOcYm1XBZdSXAw,309
24
+ ttnn_visualizer/static/assets/allPathsLoader-BK9jqlVe.js,sha256=Bc4W_X5zso_Yf-8g1aZBA9wg0NfKXGjD64lDJWqAyzE,550
26
25
  ttnn_visualizer/static/assets/index-BKzgFDAn.js,sha256=Pu504I3PJMoop2k0KQxrd1qKuS3zR5KtvTTdz9IhfeY,283793
27
26
  ttnn_visualizer/static/assets/index-BvSuWPlB.js,sha256=43E6se9tLaZkX7kCG4waDG_vyL4yED6SEaDKLt3Nw_s,292382
28
27
  ttnn_visualizer/static/assets/index-C1rJBrMl.css,sha256=f1PX54ZLwJIs0PqePOu-B3YP3joUxhrkkZ1Butqil9M,623666
29
- ttnn_visualizer/static/assets/index-CWerbNbe.js,sha256=yI0Gdggx7pxJ9T4eGZfTnSnFTwjThYqHKIBEhu6pCJU,7784873
28
+ ttnn_visualizer/static/assets/index-Ybr1HJxx.js,sha256=fSByVEefEo8y-kGOi8sS1n6_thxMXcapSWptHAd3z3Y,7785792
30
29
  ttnn_visualizer/static/assets/site-BTBrvHC5.webmanifest,sha256=Uy_XmnGuYFVf-OZuma2NvgEPdrCrevb3HZvaxSIHoA0,456
31
- ttnn_visualizer/static/assets/splitPathsBySizeLoader-DoKJAUb8.js,sha256=O8apTiCHTzNdudN5X__rXR7br9w6mlPREJWAcp7Hl-g,472
30
+ ttnn_visualizer/static/assets/splitPathsBySizeLoader-CauQGZHk.js,sha256=431X0PrDog4lr5A_hi5ObsXMZe6KpjgIDgTc65IwZWM,472
32
31
  ttnn_visualizer/static/favicon/android-chrome-192x192.png,sha256=BZWA09Zxaa3fXbaeS6nhWo2e-DUSjm9ElzNQ_xTB5XU,6220
33
32
  ttnn_visualizer/static/favicon/android-chrome-512x512.png,sha256=HBiJSZyguB3o8fMJuqIGcpeBy_9JOdImme3wD02UYCw,62626
34
33
  ttnn_visualizer/static/favicon/favicon-32x32.png,sha256=Zw201qUsczQv1UvoQvJf5smQ2ss10xaTeWxmQNYCGtY,480
35
34
  ttnn_visualizer/static/favicon/favicon.svg,sha256=wDPY3VrekJ_DE1TnJ2vUy602K3S4Xe9TgrdZ7jXK9c8,633
36
35
  ttnn_visualizer/static/sample-data/cluster-desc.yaml,sha256=LMxOmsRUXtVVU5ogzYkXUozB3dg2IzqIRJQpV_O5qMU,29618
37
36
  ttnn_visualizer/tests/__init__.py,sha256=tVr9rTN1gXorz5XQQYmcRkzeEYjDGAKez1gP0oOIYeY,94
38
- ttnn_visualizer/tests/test_queries.py,sha256=74JgzuA3Kj1DPjLexI-YCOl8sgMi9L1I1wkNdzeHih8,16602
37
+ ttnn_visualizer/tests/test_queries.py,sha256=QLfV_MWnM68WOWKKa1TPzn5aKQJbysQrGPIJ5aoxl7k,13882
39
38
  ttnn_visualizer/tests/test_serializers.py,sha256=XyS5f8JK234CtizslrQ49hhxaz7RmhkEbVTaxOvdSd4,18490
40
- ttnn_visualizer-0.40.3.dist-info/LICENSE,sha256=hcHiC16In8fLrs56SK2KzaBvs1OwBfVMJBxi7Rc2w0g,14899
41
- ttnn_visualizer-0.40.3.dist-info/LICENSE_understanding.txt,sha256=pymi-yb_RvYM9p2ZA4iSNsImcvhDBBxlGuJCY9dTq7M,233
42
- ttnn_visualizer-0.40.3.dist-info/METADATA,sha256=T70sbFH3NeezG2q6DmaRVX0zUn-myPAy7HIBhY0IwGY,7393
43
- ttnn_visualizer-0.40.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
44
- ttnn_visualizer-0.40.3.dist-info/entry_points.txt,sha256=QpuUpkmQ_mEHJTMqOBdU0MH2Z4WF_9iFsGACeyyAO1E,61
45
- ttnn_visualizer-0.40.3.dist-info/top_level.txt,sha256=M1EGkvDOuIfbhDbcUdz2-TSdmCtDoQ2Uyag9k5JLDSY,16
46
- ttnn_visualizer-0.40.3.dist-info/RECORD,,
39
+ ttnn_visualizer-0.42.0.dist-info/LICENSE,sha256=-Y7CZK1-MxZZ7l0Rb414S0SZ9tdCby-bMGl_LmFFicM,14806
40
+ ttnn_visualizer-0.42.0.dist-info/LICENSE_understanding.txt,sha256=pymi-yb_RvYM9p2ZA4iSNsImcvhDBBxlGuJCY9dTq7M,233
41
+ ttnn_visualizer-0.42.0.dist-info/METADATA,sha256=PpiZyDS_Vxcen6rGwhwgx1YFrfmCb35x7Jn_mxijYgU,7362
42
+ ttnn_visualizer-0.42.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
43
+ ttnn_visualizer-0.42.0.dist-info/entry_points.txt,sha256=QpuUpkmQ_mEHJTMqOBdU0MH2Z4WF_9iFsGACeyyAO1E,61
44
+ ttnn_visualizer-0.42.0.dist-info/top_level.txt,sha256=M1EGkvDOuIfbhDbcUdz2-TSdmCtDoQ2Uyag9k5JLDSY,16
45
+ ttnn_visualizer-0.42.0.dist-info/RECORD,,
@@ -1,85 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- #
3
- # SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
4
-
5
- import paramiko
6
- import os
7
- from pathlib import Path
8
- from paramiko.agent import Agent
9
- from paramiko.ssh_exception import SSHException
10
-
11
- from ttnn_visualizer.decorators import remote_exception_handler
12
- from ttnn_visualizer.models import RemoteConnection
13
- import logging
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- def initialize_ssh_client():
19
- ssh = paramiko.SSHClient()
20
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
21
- ssh.load_system_host_keys()
22
- return ssh
23
-
24
-
25
- def get_connection_args(remote_connection: RemoteConnection) -> dict:
26
- use_agent = os.getenv("USE_SSH_AGENT", "true").lower() == "true"
27
- ssh_config_path = Path(os.getenv("SSH_CONFIG_PATH", "~/.ssh/config")).expanduser()
28
-
29
- if use_agent:
30
- agent = Agent()
31
- keys = agent.get_keys()
32
- if not keys:
33
- logger.error("No keys found in agent")
34
- raise SSHException("No keys found")
35
- return {"look_for_keys": True}
36
-
37
- config = paramiko.SSHConfig.from_path(ssh_config_path).lookup(
38
- remote_connection.host
39
- )
40
- if not config:
41
- raise SSHException(f"Host not found in SSH config {remote_connection.host}")
42
-
43
- return {"key_filename": config["identityfile"].pop(), "look_for_keys": False} # type: ignore
44
-
45
-
46
- @remote_exception_handler
47
- def get_client(remote_connection: RemoteConnection) -> paramiko.SSHClient:
48
- ssh = initialize_ssh_client()
49
- connection_args = get_connection_args(remote_connection)
50
-
51
- ssh.connect(
52
- remote_connection.host,
53
- port=remote_connection.port,
54
- username=remote_connection.username,
55
- **connection_args,
56
- )
57
- return ssh
58
-
59
-
60
- def check_permissions(client, directory):
61
- """Check if write and delete permissions are available in the remote directory."""
62
- test_file = Path(directory) / "test_permission_file.txt"
63
- touch_command = f"touch {test_file}"
64
- remove_command = f"rm {test_file}"
65
-
66
- stdin, stdout, stderr = client.exec_command(touch_command)
67
- error = stderr.read().decode().strip()
68
- if error:
69
- raise Exception(f"No permission to create files in {directory}: {error}")
70
-
71
- stdin, stdout, stderr = client.exec_command(remove_command)
72
- error = stderr.read().decode().strip()
73
- if error:
74
- raise Exception(f"No permission to delete files in {directory}: {error}")
75
-
76
- return True
77
-
78
-
79
- def check_gzip_exists(client):
80
- """Check if gzip and tar exist on the remote server."""
81
- stdin, stdout, stderr = client.exec_command("which gzip && which tar")
82
- result = stdout.read().decode().strip()
83
- if not result:
84
- return False
85
- return True