ttnn-visualizer 0.62.0__py3-none-any.whl → 0.63.1__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 +86 -50
- ttnn_visualizer/csv_queries.py +7 -2
- ttnn_visualizer/settings.py +11 -6
- ttnn_visualizer/static/assets/{allPaths-BCkFOWKv.js → allPaths-dy9nsQF7.js} +1 -1
- ttnn_visualizer/static/assets/allPathsLoader-CQM4eIEo.js +2 -0
- ttnn_visualizer/static/assets/{index-Bixx4Hi-.js → index-B_nutJ_M.js} +153 -153
- ttnn_visualizer/static/assets/{index-Cz8iCC8X.css → index-BzMUwfVf.css} +1 -1
- ttnn_visualizer/static/assets/{splitPathsBySizeLoader-e7ZU8yKQ.js → splitPathsBySizeLoader-357aoIrt.js} +1 -1
- ttnn_visualizer/static/index.html +2 -2
- ttnn_visualizer/tests/test_utils.py +106 -0
- ttnn_visualizer/utils.py +79 -0
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/METADATA +51 -22
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/RECORD +18 -17
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/licenses/LICENSE +6 -0
- ttnn_visualizer/static/assets/allPathsLoader-83spAXtq.js +0 -2
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/WHEEL +0 -0
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/entry_points.txt +0 -0
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/licenses/LICENSE_understanding.txt +0 -0
- {ttnn_visualizer-0.62.0.dist-info → ttnn_visualizer-0.63.1.dist-info}/top_level.txt +0 -0
ttnn_visualizer/app.py
CHANGED
|
@@ -25,7 +25,7 @@ from ttnn_visualizer.exceptions import (
|
|
|
25
25
|
)
|
|
26
26
|
from ttnn_visualizer.instances import create_instance_from_local_paths
|
|
27
27
|
from ttnn_visualizer.settings import Config, DefaultConfig
|
|
28
|
-
from ttnn_visualizer.utils import
|
|
28
|
+
from ttnn_visualizer.utils import find_gunicorn_path
|
|
29
29
|
from werkzeug.debug import DebuggedApplication
|
|
30
30
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
31
31
|
|
|
@@ -115,7 +115,6 @@ def extensions(app: flask.Flask):
|
|
|
115
115
|
:param app: Flask application instance
|
|
116
116
|
:return: None
|
|
117
117
|
"""
|
|
118
|
-
|
|
119
118
|
flask_static_digest.init_app(app)
|
|
120
119
|
if app.config["USE_WEBSOCKETS"]:
|
|
121
120
|
socketio.init_app(app)
|
|
@@ -124,14 +123,9 @@ def extensions(app: flask.Flask):
|
|
|
124
123
|
if app.config["USE_WEBSOCKETS"]:
|
|
125
124
|
register_handlers(socketio)
|
|
126
125
|
|
|
127
|
-
# Create the tables within the application context
|
|
128
126
|
with app.app_context():
|
|
129
127
|
db.create_all()
|
|
130
128
|
|
|
131
|
-
# For automatically reflecting table data
|
|
132
|
-
# with app.app_context():
|
|
133
|
-
# db.reflect()
|
|
134
|
-
|
|
135
129
|
return None
|
|
136
130
|
|
|
137
131
|
|
|
@@ -198,63 +192,91 @@ def parse_args():
|
|
|
198
192
|
parser.add_argument(
|
|
199
193
|
"--tt-metal-home", help="Specify a TT-Metal home path", default=None
|
|
200
194
|
)
|
|
195
|
+
parser.add_argument(
|
|
196
|
+
"-d",
|
|
197
|
+
"--daemon",
|
|
198
|
+
action="store_true",
|
|
199
|
+
help="Run the server as a daemon process",
|
|
200
|
+
)
|
|
201
201
|
return parser.parse_args()
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
def display_mode_info_without_db(config):
|
|
205
|
+
"""Display mode information using only config, without initializing database."""
|
|
206
|
+
# Determine if we're in TT-Metal mode
|
|
207
|
+
tt_metal_home = config.TT_METAL_HOME
|
|
208
|
+
is_tt_metal_mode = tt_metal_home is not None
|
|
209
|
+
|
|
210
|
+
if is_tt_metal_mode:
|
|
211
|
+
print("🚀 TT-METAL MODE: Working directly with tt-metal generated directory")
|
|
212
|
+
print(f" TT_METAL_HOME: {tt_metal_home}")
|
|
213
|
+
|
|
214
|
+
profiler_base = Path(tt_metal_home) / "generated" / "ttnn" / "reports"
|
|
215
|
+
performance_base = Path(tt_metal_home) / "generated" / "profiler" / "reports"
|
|
216
|
+
|
|
217
|
+
print(f" Profiler reports: {profiler_base}")
|
|
218
|
+
print(f" Performance reports: {performance_base}")
|
|
219
|
+
|
|
220
|
+
# Validate setup
|
|
221
|
+
if not Path(tt_metal_home).exists():
|
|
222
|
+
print(
|
|
223
|
+
f" ⚠️ Warning: TT_METAL_HOME directory does not exist: {tt_metal_home}"
|
|
224
|
+
)
|
|
225
|
+
elif not (Path(tt_metal_home) / "generated").exists():
|
|
226
|
+
print(f" ⚠️ Warning: TT-Metal generated directory not found")
|
|
227
|
+
elif not profiler_base.exists():
|
|
228
|
+
print(
|
|
229
|
+
f" ⚠️ Warning: Profiler reports directory not found: {profiler_base}"
|
|
230
|
+
)
|
|
231
|
+
elif not performance_base.exists():
|
|
232
|
+
print(
|
|
233
|
+
f" ⚠️ Warning: Performance reports directory not found: {performance_base}"
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
print(f" ✓ TT-Metal setup is valid")
|
|
237
|
+
else:
|
|
238
|
+
print(
|
|
239
|
+
"📁 UPLOAD/SYNC MODE: Using local data directory for uploaded/synced reports"
|
|
240
|
+
)
|
|
241
|
+
print(f" Local directory: {config.LOCAL_DATA_DIRECTORY}")
|
|
242
|
+
print(f" Remote directory: {config.REMOTE_DATA_DIRECTORY}")
|
|
243
|
+
|
|
244
|
+
|
|
204
245
|
def main():
|
|
205
246
|
|
|
206
247
|
run_command = sys.argv[0].split("/")
|
|
207
248
|
if run_command[-1] == "ttnn-visualizer":
|
|
208
249
|
os.environ.setdefault("FLASK_ENV", "production")
|
|
209
250
|
|
|
210
|
-
config = cast(DefaultConfig, Config())
|
|
211
251
|
args = parse_args()
|
|
212
|
-
instance_id = None
|
|
213
252
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
app.app_context().push()
|
|
217
|
-
try:
|
|
218
|
-
session = create_instance_from_local_paths(
|
|
219
|
-
profiler_path=args.profiler_path,
|
|
220
|
-
performance_path=args.performance_path,
|
|
221
|
-
)
|
|
222
|
-
except InvalidReportPath:
|
|
223
|
-
sys.exit("Invalid report path")
|
|
224
|
-
except InvalidProfilerPath:
|
|
225
|
-
sys.exit("Invalid profiler path")
|
|
226
|
-
|
|
227
|
-
instance_id = session.instance_id
|
|
253
|
+
config = cast(DefaultConfig, Config())
|
|
254
|
+
instance_id = None
|
|
228
255
|
|
|
256
|
+
# Display mode information first (using config only, no DB needed)
|
|
229
257
|
if args.tt_metal_home:
|
|
230
258
|
config.TT_METAL_HOME = args.tt_metal_home
|
|
231
259
|
|
|
232
|
-
|
|
233
|
-
app = create_app()
|
|
234
|
-
with app.app_context():
|
|
235
|
-
resolver = create_path_resolver(app)
|
|
236
|
-
mode_info = resolver.get_mode_info()
|
|
260
|
+
display_mode_info_without_db(config)
|
|
237
261
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
print(f" Local directory: {mode_info['local_dir']}")
|
|
257
|
-
print(f" Remote directory: {mode_info['remote_dir']}")
|
|
262
|
+
# If profiler/performance paths are provided, create an instance
|
|
263
|
+
# This requires DB access, so we create the app temporarily
|
|
264
|
+
if args.profiler_path or args.performance_path:
|
|
265
|
+
app = create_app()
|
|
266
|
+
with app.app_context():
|
|
267
|
+
try:
|
|
268
|
+
session = create_instance_from_local_paths(
|
|
269
|
+
profiler_path=args.profiler_path,
|
|
270
|
+
performance_path=args.performance_path,
|
|
271
|
+
)
|
|
272
|
+
instance_id = session.instance_id
|
|
273
|
+
except InvalidReportPath:
|
|
274
|
+
sys.exit("Invalid report path")
|
|
275
|
+
except InvalidProfilerPath:
|
|
276
|
+
sys.exit("Invalid profiler path")
|
|
277
|
+
|
|
278
|
+
# Clean up this temporary app - workers will create their own
|
|
279
|
+
del app
|
|
258
280
|
|
|
259
281
|
# Check if DEBUG environment variable is set
|
|
260
282
|
debug_mode = os.environ.get("DEBUG", "false").lower() == "true"
|
|
@@ -263,8 +285,19 @@ def main():
|
|
|
263
285
|
for key, value in config.to_dict().items():
|
|
264
286
|
print(f"{key}={value}")
|
|
265
287
|
|
|
288
|
+
# Warn if there's a gunicorn config file in current directory
|
|
289
|
+
if Path("gunicorn.conf.py").exists():
|
|
290
|
+
logger.warning(
|
|
291
|
+
"Found gunicorn.conf.py in current directory - this may override environment settings"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
gunicorn_cmd, gunicorn_warning = find_gunicorn_path()
|
|
295
|
+
|
|
296
|
+
if gunicorn_warning:
|
|
297
|
+
print(gunicorn_warning)
|
|
298
|
+
|
|
266
299
|
gunicorn_args = [
|
|
267
|
-
|
|
300
|
+
gunicorn_cmd,
|
|
268
301
|
"-t",
|
|
269
302
|
config.GUNICORN_TIMEOUT,
|
|
270
303
|
"-k",
|
|
@@ -279,7 +312,10 @@ def main():
|
|
|
279
312
|
if debug_mode:
|
|
280
313
|
gunicorn_args.insert(1, "--reload")
|
|
281
314
|
|
|
282
|
-
if
|
|
315
|
+
if args.daemon:
|
|
316
|
+
gunicorn_args.insert(1, "--daemon")
|
|
317
|
+
|
|
318
|
+
if config.LAUNCH_BROWSER_ON_START and not args.daemon:
|
|
283
319
|
flask_env = os.getenv("FLASK_ENV", "development")
|
|
284
320
|
port = config.PORT if flask_env == "production" else config.DEV_SERVER_PORT
|
|
285
321
|
host = config.HOST if flask_env == "production" else config.DEV_SERVER_HOST
|
ttnn_visualizer/csv_queries.py
CHANGED
|
@@ -463,7 +463,7 @@ class OpsPerformanceReportQueries:
|
|
|
463
463
|
|
|
464
464
|
try:
|
|
465
465
|
perf_report.generate_perf_report(
|
|
466
|
-
csv_file,
|
|
466
|
+
[csv_file],
|
|
467
467
|
signpost,
|
|
468
468
|
ignore_signposts,
|
|
469
469
|
cls.DEFAULT_MIN_PERCENTAGE,
|
|
@@ -497,7 +497,12 @@ class OpsPerformanceReportQueries:
|
|
|
497
497
|
op_id = index + 2 # Match IDs with row numbers in ops perf results csv
|
|
498
498
|
if not any(s["op_code"] == op_code for s in signposts):
|
|
499
499
|
captured_signposts.add(op_code)
|
|
500
|
-
signposts.append(
|
|
500
|
+
signposts.append(
|
|
501
|
+
{
|
|
502
|
+
"id": op_id,
|
|
503
|
+
"op_code": op_code,
|
|
504
|
+
}
|
|
505
|
+
)
|
|
501
506
|
|
|
502
507
|
report = []
|
|
503
508
|
|
ttnn_visualizer/settings.py
CHANGED
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from dotenv import load_dotenv
|
|
9
|
+
from sqlalchemy.pool import NullPool
|
|
9
10
|
from ttnn_visualizer.utils import str_to_bool
|
|
10
11
|
|
|
11
12
|
load_dotenv()
|
|
@@ -55,13 +56,17 @@ class DefaultConfig(object):
|
|
|
55
56
|
USE_WEBSOCKETS = str_to_bool(os.getenv("USE_WEBSOCKETS", "true"))
|
|
56
57
|
|
|
57
58
|
# SQL Alchemy Settings
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
# Build database path - use absolute path to avoid any ambiguity
|
|
60
|
+
_db_file_path = str(Path(APP_DATA_DIRECTORY) / f"ttnn_{DB_VERSION}.db")
|
|
61
|
+
SQLALCHEMY_DATABASE_URI = f"sqlite:///{_db_file_path}"
|
|
61
62
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
# SQLite-specific settings for multi-process/worker environments
|
|
64
|
+
# NullPool: Each worker gets its own connection, avoiding file locking issues
|
|
65
|
+
# This is critical for gunicorn's multi-worker mode with SQLite
|
|
66
|
+
"poolclass": NullPool,
|
|
67
|
+
"connect_args": {
|
|
68
|
+
"check_same_thread": False, # Allow SQLite to be used across threads in gevent
|
|
69
|
+
},
|
|
65
70
|
}
|
|
66
71
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
67
72
|
|
|
@@ -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-
|
|
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-B_nutJ_M.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-dy9nsQF7.js","assets/index-voJy5fZe.js","assets/index-BZITDwoa.js","assets/index-B_nutJ_M.js","assets/index-BzMUwfVf.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as e}from"./index-B_nutJ_M.js";const s=async(t,a)=>{const{getIconPaths:o}=await e(async()=>{const{getIconPaths:r}=await import("./allPaths-dy9nsQF7.js");return{getIconPaths:r}},__vite__mapDeps([0,1,2,3,4]));return o(t,a)};export{s as allPathsLoader};
|