ttnn-visualizer 0.67.2__py3-none-any.whl → 0.68.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/app.py CHANGED
@@ -177,19 +177,22 @@ def open_browser(host, port, instance_id=None):
177
177
 
178
178
  max_attempts = 10
179
179
  attempt = 0
180
- server_ready = False
180
+ app_ready = False
181
181
 
182
- print(f"Waiting for server to be ready at {url}...")
183
- while attempt < max_attempts and not server_ready:
182
+ print(f"Waiting for application to be ready at {url}...")
183
+ while attempt < max_attempts and not app_ready:
184
184
  try:
185
185
  urlopen(url, timeout=1)
186
- server_ready = True
186
+ app_ready = True
187
187
  except (URLError, ConnectionError, OSError):
188
188
  attempt += 1
189
+ logger.warning(f"Retrying {url}...")
189
190
  time.sleep(0.5)
190
191
 
191
- if not server_ready:
192
- print(f"❌ Server not ready after {max_attempts} attempts.")
192
+ if not app_ready:
193
+ print(
194
+ f"❌ Application not ready after {max_attempts} attempts - is the front end running?"
195
+ )
193
196
  else:
194
197
  print(f"Launching browser with url: {url}")
195
198
 
@@ -329,7 +329,9 @@ class OpsPerformanceQueries:
329
329
  performance_path.glob(f"{OpsPerformanceQueries.PERF_RESULTS_PREFIX}_*.csv")
330
330
  )
331
331
  if not perf_files:
332
- raise FileNotFoundError("No performance results file found.")
332
+ raise FileNotFoundError(
333
+ f"No performance results file found at {performance_path}"
334
+ )
333
335
 
334
336
  # Use the latest file
335
337
  latest_file = max(perf_files, key=os.path.getctime)
@@ -76,8 +76,8 @@ def remote_exception_handler(func):
76
76
 
77
77
  # Return user-friendly error message about SSH keys
78
78
  user_message = (
79
- "SSH authentication failed. This application requires SSH key-based authentication. "
80
- "Please ensure your SSH public key is added to the authorized_keys file on the remote server. "
79
+ "SSH authentication failed. This application requires SSH key-based authentication."
80
+ " Please ensure your SSH public key is added to the authorized_keys file on the remote server."
81
81
  "Password authentication is not supported."
82
82
  )
83
83
 
@@ -95,7 +95,7 @@ def remote_exception_handler(func):
95
95
  current_app.logger.error(f"No projects: {str(err)}")
96
96
  raise RemoteConnectionException(
97
97
  status=ConnectionTestStates.FAILED,
98
- message=f"No projects found at remote location: {str(err)}",
98
+ message=f"No remote projects found: {str(err)}",
99
99
  )
100
100
  except NoValidConnectionsError as err:
101
101
  current_app.logger.warning(
@@ -132,6 +132,15 @@ def remote_exception_handler(func):
132
132
  raise RemoteConnectionException(
133
133
  status=ConnectionTestStates.FAILED, message=message
134
134
  )
135
+ except Exception as err:
136
+ # Catch any other unhandled exceptions
137
+ current_app.logger.exception(
138
+ f"Unexpected error during remote operation for {connection}: {str(err)}"
139
+ )
140
+ raise RemoteConnectionException(
141
+ status=ConnectionTestStates.FAILED,
142
+ message=f"An unexpected error occurred: {str(err)}",
143
+ )
135
144
 
136
145
  return remote_handler
137
146
 
@@ -19,14 +19,25 @@ logger = getLogger(__name__)
19
19
  from flask import current_app, jsonify
20
20
  from sqlalchemy.exc import IntegrityError, SQLAlchemyError
21
21
 
22
+ # Active report dictionary keys
23
+ KEY_PROFILER_NAME = "profiler_name"
24
+ KEY_PROFILER_LOCATION = "profiler_location"
25
+ KEY_PERFORMANCE_NAME = "performance_name"
26
+ KEY_PERFORMANCE_LOCATION = "performance_location"
27
+ KEY_NPE_NAME = "npe_name"
28
+ KEY_NPE_LOCATION = "npe_location"
29
+
22
30
  _sentinel = object()
23
31
 
24
32
 
25
33
  def update_existing_instance(
26
34
  instance_data,
27
35
  profiler_name,
36
+ profiler_location,
28
37
  performance_name,
38
+ performance_location,
29
39
  npe_name,
40
+ npe_location,
30
41
  remote_connection,
31
42
  remote_profiler_folder,
32
43
  remote_performance_folder,
@@ -39,19 +50,24 @@ def update_existing_instance(
39
50
 
40
51
  # First ifs are explicit deletes and elifs are updates
41
52
  if profiler_name == "":
42
- active_report.pop("profiler_name", None)
43
- elif profiler_name is not None:
44
- active_report["profiler_name"] = profiler_name
53
+ active_report.pop(KEY_PROFILER_NAME, None)
54
+ active_report.pop(KEY_PROFILER_LOCATION, None)
55
+ elif profiler_name is not None and profiler_location is not None:
56
+ active_report[KEY_PROFILER_NAME] = profiler_name
57
+ active_report[KEY_PROFILER_LOCATION] = profiler_location
45
58
 
46
59
  if performance_name == "":
47
- active_report.pop("performance_name", None)
48
- elif performance_name is not None:
49
-
50
- active_report["performance_name"] = performance_name
60
+ active_report.pop(KEY_PERFORMANCE_NAME, None)
61
+ active_report.pop(KEY_PERFORMANCE_LOCATION, None)
62
+ elif performance_name is not None and performance_location is not None:
63
+ active_report[KEY_PERFORMANCE_NAME] = performance_name
64
+ active_report[KEY_PERFORMANCE_LOCATION] = performance_location
51
65
  if npe_name == "":
52
- active_report.pop("npe_name", None)
53
- elif npe_name is not None:
54
- active_report["npe_name"] = npe_name
66
+ active_report.pop(KEY_NPE_NAME, None)
67
+ active_report.pop(KEY_NPE_LOCATION, None)
68
+ elif npe_name is not None and npe_location is not None:
69
+ active_report[KEY_NPE_NAME] = npe_name
70
+ active_report[KEY_NPE_LOCATION] = npe_location
55
71
 
56
72
  instance_data.active_report = active_report
57
73
 
@@ -68,9 +84,9 @@ def update_existing_instance(
68
84
  if profiler_path is not _sentinel:
69
85
  instance_data.profiler_path = profiler_path
70
86
  else:
71
- if active_report.get("profiler_name"):
87
+ if active_report.get(KEY_PROFILER_NAME):
72
88
  instance_data.profiler_path = get_profiler_path(
73
- profiler_name=active_report["profiler_name"],
89
+ profiler_name=active_report[KEY_PROFILER_NAME],
74
90
  current_app=current_app,
75
91
  remote_connection=remote_connection,
76
92
  )
@@ -78,9 +94,9 @@ def update_existing_instance(
78
94
  if performance_path is not _sentinel:
79
95
  instance_data.performance_path = performance_path
80
96
  else:
81
- if active_report.get("performance_name"):
97
+ if active_report.get(KEY_PERFORMANCE_NAME):
82
98
  instance_data.performance_path = get_performance_path(
83
- performance_name=active_report["performance_name"],
99
+ performance_name=active_report[KEY_PERFORMANCE_NAME],
84
100
  current_app=current_app,
85
101
  remote_connection=remote_connection,
86
102
  )
@@ -88,9 +104,9 @@ def update_existing_instance(
88
104
  if npe_path is not _sentinel:
89
105
  instance_data.npe_path = npe_path
90
106
  else:
91
- if active_report.get("npe_name"):
107
+ if active_report.get(KEY_NPE_NAME):
92
108
  instance_data.npe_path = get_npe_path(
93
- npe_name=active_report["npe_name"], current_app=current_app
109
+ npe_name=active_report[KEY_NPE_NAME], current_app=current_app
94
110
  )
95
111
 
96
112
 
@@ -117,8 +133,11 @@ def commit_and_log_session(instance_data, instance_id):
117
133
  def create_new_instance(
118
134
  instance_id,
119
135
  profiler_name,
136
+ profiler_location,
120
137
  performance_name,
138
+ performance_location,
121
139
  npe_name,
140
+ npe_location,
122
141
  remote_connection,
123
142
  remote_profiler_folder,
124
143
  remote_performance_folder,
@@ -128,12 +147,18 @@ def create_new_instance(
128
147
  npe_path=_sentinel,
129
148
  ):
130
149
  active_report = {}
131
- if profiler_name:
132
- active_report["profiler_name"] = profiler_name
133
- if performance_name:
134
- active_report["performance_name"] = performance_name
135
- if npe_name:
136
- active_report["npe_name"] = npe_name
150
+
151
+ if profiler_name and profiler_location is not None:
152
+ active_report[KEY_PROFILER_NAME] = profiler_name
153
+ active_report[KEY_PROFILER_LOCATION] = profiler_location
154
+
155
+ if performance_name and performance_location is not None:
156
+ active_report[KEY_PERFORMANCE_NAME] = performance_name
157
+ active_report[KEY_PERFORMANCE_LOCATION] = performance_location
158
+
159
+ if npe_name and npe_location is not None:
160
+ active_report[KEY_NPE_NAME] = npe_name
161
+ active_report[KEY_NPE_LOCATION] = npe_location
137
162
 
138
163
  if clear_remote:
139
164
  remote_connection = None
@@ -147,7 +172,7 @@ def create_new_instance(
147
172
  profiler_path
148
173
  if profiler_path is not _sentinel
149
174
  else get_profiler_path(
150
- active_report["profiler_name"],
175
+ active_report[KEY_PROFILER_NAME],
151
176
  current_app=current_app,
152
177
  remote_connection=remote_connection,
153
178
  )
@@ -178,8 +203,11 @@ def create_new_instance(
178
203
  def update_instance(
179
204
  instance_id,
180
205
  profiler_name=None,
206
+ profiler_location=None,
181
207
  performance_name=None,
208
+ performance_location=None,
182
209
  npe_name=None,
210
+ npe_location=None,
183
211
  remote_connection=None,
184
212
  remote_profiler_folder=None,
185
213
  remote_performance_folder=None,
@@ -195,8 +223,11 @@ def update_instance(
195
223
  update_existing_instance(
196
224
  instance_data,
197
225
  profiler_name,
226
+ profiler_location,
198
227
  performance_name,
228
+ performance_location,
199
229
  npe_name,
230
+ npe_location,
200
231
  remote_connection,
201
232
  remote_profiler_folder,
202
233
  remote_performance_folder,
@@ -209,8 +240,11 @@ def update_instance(
209
240
  instance_data = create_new_instance(
210
241
  instance_id,
211
242
  profiler_name,
243
+ profiler_location,
212
244
  performance_name,
245
+ performance_location,
213
246
  npe_name,
247
+ npe_location,
214
248
  remote_connection,
215
249
  remote_profiler_folder,
216
250
  remote_performance_folder,
@@ -231,8 +265,11 @@ def update_instance(
231
265
  def get_or_create_instance(
232
266
  instance_id,
233
267
  profiler_name=None,
268
+ profiler_location=None,
234
269
  performance_name=None,
270
+ performance_location=None,
235
271
  npe_name=None,
272
+ npe_location=None,
236
273
  remote_connection=None,
237
274
  remote_profiler_folder=None,
238
275
  ):
@@ -265,16 +302,22 @@ def get_or_create_instance(
265
302
  # Update the instance if any new data is provided
266
303
  if (
267
304
  profiler_name
305
+ or profiler_location is not None
268
306
  or performance_name
307
+ or performance_location is not None
269
308
  or npe_name
309
+ or npe_location is not None
270
310
  or remote_connection
271
311
  or remote_profiler_folder
272
312
  ):
273
313
  update_instance(
274
314
  instance_id=instance_id,
275
315
  profiler_name=profiler_name,
316
+ profiler_location=profiler_location,
276
317
  performance_name=performance_name,
318
+ performance_location=performance_location,
277
319
  npe_name=npe_name,
320
+ npe_location=npe_location,
278
321
  remote_connection=remote_connection,
279
322
  remote_profiler_folder=remote_profiler_folder,
280
323
  )
@@ -359,9 +402,9 @@ def create_instance_from_local_paths(profiler_path, performance_path):
359
402
  instance_data = InstanceTable(
360
403
  instance_id=create_random_instance_id(),
361
404
  active_report={
362
- "profiler_name": profiler_name,
363
- "performance_name": performance_name,
364
- "npe_name": None,
405
+ KEY_PROFILER_NAME: profiler_name,
406
+ KEY_PERFORMANCE_NAME: performance_name,
407
+ KEY_NPE_NAME: None,
365
408
  },
366
409
  profiler_path=f"{_profiler_path}/db.sqlite" if profiler_path else None,
367
410
  performance_path=performance_path if performance_path else None,
ttnn_visualizer/models.py CHANGED
@@ -24,6 +24,11 @@ class BufferType(enum.Enum):
24
24
  TRACE = 4
25
25
 
26
26
 
27
+ class ReportLocation(enum.Enum):
28
+ LOCAL = "local"
29
+ REMOTE = "remote"
30
+
31
+
27
32
  @dataclasses.dataclass
28
33
  class Operation(SerializeableDataclass):
29
34
  operation_id: int
@@ -200,8 +205,11 @@ class StatusMessage(SerializeableModel):
200
205
 
201
206
  class ActiveReports(SerializeableModel):
202
207
  profiler_name: Optional[str] = None
208
+ profiler_location: Optional[ReportLocation] = None
203
209
  performance_name: Optional[str] = None
210
+ performance_location: Optional[ReportLocation] = None
204
211
  npe_name: Optional[str] = None
212
+ npe_location: Optional[ReportLocation] = None
205
213
 
206
214
 
207
215
  class RemoteReportFolder(SerializeableModel):
@@ -626,7 +626,7 @@ def check_remote_path_for_reports(remote_connection):
626
626
  remote_connection, remote_connection.profilerPath, [TEST_CONFIG_FILE]
627
627
  )
628
628
  else:
629
- logger.info("No profiler path configured; skipping check.")
629
+ logger.info("No profiler path configured; skipping check")
630
630
 
631
631
  remote_performance_paths = []
632
632
  if remote_connection.performancePath:
@@ -634,7 +634,7 @@ def check_remote_path_for_reports(remote_connection):
634
634
  remote_connection, remote_connection.performancePath, [TEST_PROFILER_FILE]
635
635
  )
636
636
  else:
637
- logger.info("No performance path configured; skipping check.")
637
+ logger.info("No performance path configured; skipping check")
638
638
 
639
639
  errors = []
640
640
  if not remote_profiler_paths and remote_connection.profilerPath:
@@ -757,8 +757,23 @@ def find_folders_by_files(
757
757
  # This line should never be reached as handle_ssh_subprocess_error raises an exception
758
758
  return []
759
759
  else:
760
- logger.error(f"Error finding folders: {e.stderr}")
761
- return []
760
+ stderr = e.stderr.lower() if e.stderr else ""
761
+ # Check for permission denied errors
762
+ if "permission denied" in stderr:
763
+ error_msg = (
764
+ f"Permission denied accessing '{root_folder}'. "
765
+ f"The user '{remote_connection.username}' does not have read access to this directory. "
766
+ "Please check directory permissions on the remote server or choose a different path."
767
+ )
768
+ logger.error(f"Error finding folders: {e.stderr}")
769
+ raise RemoteConnectionException(
770
+ message=error_msg,
771
+ status=ConnectionTestStates.FAILED,
772
+ detail=e.stderr.strip() if e.stderr else None,
773
+ )
774
+ else:
775
+ logger.error(f"Error finding folders: {e.stderr}")
776
+ return []
762
777
  except subprocess.TimeoutExpired:
763
778
  logger.error(f"Timeout finding folders in: {root_folder}")
764
779
  return []
@@ -784,7 +799,7 @@ def get_remote_performance_folders(
784
799
  raise NoProjectsException(status=ConnectionTestStates.FAILED, message=error)
785
800
 
786
801
  if not performance_paths:
787
- error = f"No performance reports found at {remote_connection.performancePath}"
802
+ error = f"Performance path: {remote_connection.performancePath}"
788
803
  logger.info(error)
789
804
  raise NoProjectsException(status=ConnectionTestStates.FAILED, message=error)
790
805
 
@@ -802,10 +817,10 @@ def get_remote_profiler_folders(
802
817
  remote_connection: RemoteConnection,
803
818
  ) -> List[RemoteReportFolder]:
804
819
  """Return a list of remote folders containing a config.json file."""
805
- remote_config_paths = []
820
+ profiler_paths = []
806
821
 
807
822
  if remote_connection.profilerPath:
808
- remote_config_paths = find_folders_by_files(
823
+ profiler_paths = find_folders_by_files(
809
824
  remote_connection, remote_connection.profilerPath, [TEST_CONFIG_FILE]
810
825
  )
811
826
  else:
@@ -813,10 +828,15 @@ def get_remote_profiler_folders(
813
828
  logger.info(error)
814
829
  raise NoProjectsException(status=ConnectionTestStates.FAILED, message=error)
815
830
 
831
+ if not profiler_paths:
832
+ error = f"Profiler path: {remote_connection.profilerPath}"
833
+ logger.info(error)
834
+ raise NoProjectsException(status=ConnectionTestStates.FAILED, message=error)
835
+
816
836
  remote_folder_data = []
817
- for config_path in remote_config_paths:
837
+ for path in profiler_paths:
818
838
  remote_folder = get_remote_profiler_folder_from_config_path(
819
- remote_connection, str(Path(config_path).joinpath(TEST_CONFIG_FILE))
839
+ remote_connection, str(Path(path).joinpath(TEST_CONFIG_FILE))
820
840
  )
821
841
  remote_folder_data.append(remote_folder)
822
842
 
@@ -1 +1 @@
1
- import{I as s}from"./index-voJy5fZe.js";import{I as r}from"./index-BZITDwoa.js";import{p as n,I as c}from"./index-Uz_q_wTj.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};
1
+ import{I as s}from"./index-voJy5fZe.js";import{I as r}from"./index-BZITDwoa.js";import{p as n,I as c}from"./index-ryWOUYjC.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-BZLVCrxa.js","assets/index-voJy5fZe.js","assets/index-BZITDwoa.js","assets/index-ryWOUYjC.js","assets/index-DS75jGv-.css"])))=>i.map(i=>d[i]);
2
+ import{_ as e}from"./index-ryWOUYjC.js";const s=async(t,a)=>{const{getIconPaths:o}=await e(async()=>{const{getIconPaths:r}=await import("./allPaths-BZLVCrxa.js");return{getIconPaths:r}},__vite__mapDeps([0,1,2,3,4]));return o(t,a)};export{s as allPathsLoader};