flashstudio 0.1.3__tar.gz → 0.1.4__tar.gz

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.

Potentially problematic release.


This version of flashstudio might be problematic. Click here for more details.

Files changed (116) hide show
  1. {flashstudio-0.1.3 → flashstudio-0.1.4}/PKG-INFO +87 -19
  2. {flashstudio-0.1.3 → flashstudio-0.1.4}/README.md +84 -17
  3. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/app.py +76 -5
  4. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/project_manager.py +20 -10
  5. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/sidebar.py +7 -6
  6. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/styles.py +40 -3
  7. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/constants.py +35 -1
  8. flashstudio-0.1.4/flashstudio/core/__init__.py +3 -0
  9. flashstudio-0.1.4/flashstudio/core/constants.py +3 -0
  10. flashstudio-0.1.4/flashstudio/pages/__init__.py +8 -0
  11. flashstudio-0.1.4/flashstudio/pages/dashboard/__init__.py +3 -0
  12. flashstudio-0.1.4/flashstudio/pages/dashboard/_common.py +10 -0
  13. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/__init__.py +3 -0
  14. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/metrics.py +66 -0
  15. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/pipeline_status.py +35 -0
  16. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/project_banner.py +23 -0
  17. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/quick_actions.py +28 -0
  18. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/recent_runs.py +99 -0
  19. flashstudio-0.1.4/flashstudio/pages/dashboard/overview/tab.py +35 -0
  20. flashstudio-0.1.4/flashstudio/pages/dashboard/page.py +17 -0
  21. flashstudio-0.1.4/flashstudio/pages/data/__init__.py +3 -0
  22. flashstudio-0.1.4/flashstudio/pages/data/_common.py +107 -0
  23. flashstudio-0.1.4/flashstudio/pages/data/download/__init__.py +3 -0
  24. flashstudio-0.1.4/flashstudio/pages/data/download/tab.py +75 -0
  25. flashstudio-0.1.4/flashstudio/pages/data/helpers.py +290 -0
  26. flashstudio-0.1.4/flashstudio/pages/data/page.py +50 -0
  27. flashstudio-0.1.4/flashstudio/pages/data/preview/__init__.py +3 -0
  28. flashstudio-0.1.4/flashstudio/pages/data/preview/tab.py +139 -0
  29. flashstudio-0.1.4/flashstudio/pages/data/upload/__init__.py +3 -0
  30. flashstudio-0.1.4/flashstudio/pages/data/upload/tab.py +160 -0
  31. flashstudio-0.1.4/flashstudio/pages/data/verify/__init__.py +3 -0
  32. flashstudio-0.1.4/flashstudio/pages/data/verify/tab.py +107 -0
  33. flashstudio-0.1.4/flashstudio/pages/export/__init__.py +3 -0
  34. flashstudio-0.1.4/flashstudio/pages/export/page.py +54 -0
  35. flashstudio-0.1.4/flashstudio/pages/export/weights/__init__.py +4 -0
  36. flashstudio-0.1.4/flashstudio/pages/export/weights/exporter.py +60 -0
  37. flashstudio-0.1.4/flashstudio/pages/export/weights/settings.py +75 -0
  38. flashstudio-0.1.4/flashstudio/pages/inference/__init__.py +3 -0
  39. flashstudio-0.1.4/flashstudio/pages/inference/_common.py +76 -0
  40. flashstudio-0.1.4/flashstudio/pages/inference/data/__init__.py +3 -0
  41. flashstudio-0.1.4/flashstudio/pages/inference/data/tab.py +121 -0
  42. flashstudio-0.1.4/flashstudio/pages/inference/model/__init__.py +3 -0
  43. flashstudio-0.1.4/flashstudio/pages/inference/model/tab.py +60 -0
  44. flashstudio-0.1.4/flashstudio/pages/inference/page.py +42 -0
  45. flashstudio-0.1.4/flashstudio/pages/inference/run/__init__.py +3 -0
  46. flashstudio-0.1.4/flashstudio/pages/inference/run/detection.py +133 -0
  47. flashstudio-0.1.4/flashstudio/pages/inference/run/tab.py +159 -0
  48. flashstudio-0.1.4/flashstudio/pages/inference/run/video.py +195 -0
  49. flashstudio-0.1.4/flashstudio/pages/inference/solution/__init__.py +3 -0
  50. flashstudio-0.1.4/flashstudio/pages/inference/solution/tab.py +121 -0
  51. flashstudio-0.1.4/flashstudio/pages/model/__init__.py +3 -0
  52. flashstudio-0.1.4/flashstudio/pages/model/_common.py +3 -0
  53. flashstudio-0.1.4/flashstudio/pages/model/advanced/__init__.py +3 -0
  54. flashstudio-0.1.4/flashstudio/pages/model/advanced/tab.py +54 -0
  55. flashstudio-0.1.4/flashstudio/pages/model/architecture/__init__.py +3 -0
  56. flashstudio-0.1.4/flashstudio/pages/model/architecture/tab.py +90 -0
  57. flashstudio-0.1.4/flashstudio/pages/model/augment/__init__.py +3 -0
  58. flashstudio-0.1.4/flashstudio/pages/model/augment/tab.py +18 -0
  59. flashstudio-0.1.4/flashstudio/pages/model/hyperparams/__init__.py +4 -0
  60. flashstudio-0.1.4/flashstudio/pages/model/hyperparams/tab.py +89 -0
  61. flashstudio-0.1.4/flashstudio/pages/model/page.py +30 -0
  62. flashstudio-0.1.4/flashstudio/pages/model/utils/__init__.py +3 -0
  63. flashstudio-0.1.4/flashstudio/pages/model/utils/config.py +27 -0
  64. flashstudio-0.1.4/flashstudio/pages/training/__init__.py +3 -0
  65. flashstudio-0.1.4/flashstudio/pages/training/_common.py +24 -0
  66. flashstudio-0.1.4/flashstudio/pages/training/launch/__init__.py +3 -0
  67. flashstudio-0.1.4/flashstudio/pages/training/launch/controls.py +84 -0
  68. flashstudio-0.1.4/flashstudio/pages/training/launch/dialogs.py +247 -0
  69. flashstudio-0.1.4/flashstudio/pages/training/launch/preflight.py +66 -0
  70. flashstudio-0.1.4/flashstudio/pages/training/launch/runner.py +429 -0
  71. flashstudio-0.1.4/flashstudio/pages/training/launch/tab.py +130 -0
  72. flashstudio-0.1.4/flashstudio/pages/training/monitor/__init__.py +3 -0
  73. flashstudio-0.1.4/flashstudio/pages/training/monitor/checkpoints.py +96 -0
  74. flashstudio-0.1.4/flashstudio/pages/training/monitor/curves.py +113 -0
  75. flashstudio-0.1.4/flashstudio/pages/training/monitor/dashboard.py +129 -0
  76. flashstudio-0.1.4/flashstudio/pages/training/monitor/gt_verification.py +122 -0
  77. flashstudio-0.1.4/flashstudio/pages/training/monitor/log_viewer.py +43 -0
  78. flashstudio-0.1.4/flashstudio/pages/training/monitor/parsers.py +253 -0
  79. flashstudio-0.1.4/flashstudio/pages/training/monitor/run_meta.py +165 -0
  80. flashstudio-0.1.4/flashstudio/pages/training/monitor/tab.py +142 -0
  81. flashstudio-0.1.4/flashstudio/pages/training/monitor/visualizations.py +52 -0
  82. flashstudio-0.1.4/flashstudio/pages/training/page.py +128 -0
  83. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/utils/__init__.py +39 -1
  84. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/utils/config.py +11 -4
  85. flashstudio-0.1.4/flashstudio/utils/training_hooks.py +266 -0
  86. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio.egg-info/PKG-INFO +87 -19
  87. flashstudio-0.1.4/flashstudio.egg-info/SOURCES.txt +106 -0
  88. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio.egg-info/requires.txt +1 -0
  89. {flashstudio-0.1.3 → flashstudio-0.1.4}/pyproject.toml +16 -5
  90. flashstudio-0.1.4/tests/test_app.py +120 -0
  91. flashstudio-0.1.4/tests/test_cli.py +37 -0
  92. flashstudio-0.1.4/tests/test_constants.py +172 -0
  93. flashstudio-0.1.4/tests/test_launcher.py +60 -0
  94. flashstudio-0.1.4/tests/test_pipeline_flow.py +771 -0
  95. flashstudio-0.1.4/tests/test_save_dir_compat.py +685 -0
  96. flashstudio-0.1.3/flashstudio/pages/__init__.py +0 -0
  97. flashstudio-0.1.3/flashstudio/pages/dashboard.py +0 -251
  98. flashstudio-0.1.3/flashstudio/pages/data.py +0 -865
  99. flashstudio-0.1.3/flashstudio/pages/export.py +0 -160
  100. flashstudio-0.1.3/flashstudio/pages/inference.py +0 -850
  101. flashstudio-0.1.3/flashstudio/pages/model.py +0 -281
  102. flashstudio-0.1.3/flashstudio/pages/training.py +0 -2079
  103. flashstudio-0.1.3/flashstudio.egg-info/SOURCES.txt +0 -31
  104. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/__init__.py +0 -0
  105. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/cli.py +0 -0
  106. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/__init__.py +0 -0
  107. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/wizard.py +0 -0
  108. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/components/zone_drawer/__init__.py +0 -0
  109. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/launcher.py +0 -0
  110. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/utils/device.py +0 -0
  111. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/utils/filesystem.py +0 -0
  112. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio/utils/jobs.py +0 -0
  113. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio.egg-info/dependency_links.txt +0 -0
  114. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio.egg-info/entry_points.txt +0 -0
  115. {flashstudio-0.1.3 → flashstudio-0.1.4}/flashstudio.egg-info/top_level.txt +0 -0
  116. {flashstudio-0.1.3 → flashstudio-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashstudio
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Interactive Training & Inference UI for FlashDet — runs on Google Colab
5
5
  Author: Gaurav14cs17
6
6
  License: Apache-2.0
@@ -12,7 +12,7 @@ Classifier: Intended Audience :: Science/Research
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
- Requires-Python: >=3.9
15
+ Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  Requires-Dist: streamlit>=1.28.0
18
18
  Requires-Dist: plotly>=5.0
@@ -33,6 +33,7 @@ Requires-Dist: pyngrok>=6.0; extra == "colab"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: ruff; extra == "dev"
35
35
  Requires-Dist: pytest; extra == "dev"
36
+ Requires-Dist: pytest-cov; extra == "dev"
36
37
 
37
38
  <p align="center">
38
39
  <img src="https://raw.githubusercontent.com/FlashVision/FlashStudio/main/assets/logo.png" width="200" alt="FlashStudio Logo">
@@ -237,26 +238,93 @@ Click the **Public URL** to open FlashStudio in a new tab.
237
238
  ```
238
239
  FlashStudio/
239
240
  ├── flashstudio/
240
- │ ├── __init__.py # Package init + launch() export
241
- │ ├── app.py # Main Streamlit app (wizard flow)
242
- │ ├── launcher.py # Colab/local launcher with ngrok
243
- │ ├── cli.py # CLI entrypoint
244
- │ ├── pages/
245
- │ │ ├── dashboard.py # Overview + recent training runs
246
- │ │ ├── data.py # Dataset upload/download
247
- │ │ ├── model.py # Architecture & hyperparameter config
248
- │ │ ├── training.py # Training monitor (reads real workspace)
249
- │ │ ├── export.py # ONNX export
250
- │ │ └── inference.py # 4-step inference pipeline
241
+ │ ├── __init__.py # Package init + launch() export
242
+ │ ├── app.py # Main Streamlit app (wizard flow)
243
+ │ ├── launcher.py # Colab/local launcher with ngrok
244
+ │ ├── cli.py # CLI entrypoint
245
+ │ ├── constants.py # Centralized constants (paths, defaults, models)
246
+ │ │
247
+ │ ├── pages/ # Each page is a sub-package
248
+ │ │ ├── dashboard/
249
+ │ │ │ ├── page.py # Main dashboard render
250
+ │ │ │ ├── pipeline_status.py # Pipeline status cards
251
+ │ │ │ └── recent_runs.py # Recent training runs table
252
+ │ │ ├── data/
253
+ │ │ │ ├── page.py # Main data page render
254
+ │ │ │ ├── upload.py # Upload tab (ZIP/TAR + class config)
255
+ │ │ │ ├── download.py # Download tab (quick start + registries)
256
+ │ │ │ ├── preview.py # Preview tab (image grid + annotations)
257
+ │ │ │ ├── verify.py # Verify tab (dataset validation)
258
+ │ │ │ └── helpers.py # Shared helpers (extract, detect, convert)
259
+ │ │ ├── model/
260
+ │ │ │ ├── page.py # Main model page render
261
+ │ │ │ ├── architecture.py # Architecture tab (FlashDet/YOLO selection)
262
+ │ │ │ ├── hyperparams.py # Hyperparameters tab
263
+ │ │ │ ├── augmentation.py # Augmentation tab
264
+ │ │ │ ├── advanced.py # Advanced tab (memory, distributed)
265
+ │ │ │ └── summary.py # Config summary bar + YAML save/load
266
+ │ │ ├── training/
267
+ │ │ │ ├── page.py # Main training page render
268
+ │ │ │ ├── _common.py # Shared utilities (_get_save_dir)
269
+ │ │ │ ├── launch/ # Launch sub-package
270
+ │ │ │ │ ├── tab.py # Launch tab entry point
271
+ │ │ │ │ ├── preflight.py # Pre-flight checks
272
+ │ │ │ │ ├── runner.py # FlashDet Trainer subprocess
273
+ │ │ │ │ ├── controls.py # Start/stop/pause/resume buttons
274
+ │ │ │ │ └── dialogs.py # Clean/resume/config dialogs
275
+ │ │ │ └── monitor/ # Monitor sub-package
276
+ │ │ │ ├── tab.py # Monitor tab entry point
277
+ │ │ │ ├── run_meta.py # Run metadata extraction
278
+ │ │ │ ├── parsers.py # CSV/log parsing
279
+ │ │ │ ├── dashboard.py # Run dashboard + metrics
280
+ │ │ │ ├── curves.py # Plotly training curves
281
+ │ │ │ ├── visualizations.py # Epoch visualizations
282
+ │ │ │ ├── gt_verification.py # Ground truth verification
283
+ │ │ │ ├── log_viewer.py # Full log viewer
284
+ │ │ │ └── checkpoints.py # File browser + checkpoints
285
+ │ │ ├── export/
286
+ │ │ │ └── page.py # Export page (ONNX/TorchScript)
287
+ │ │ └── inference/
288
+ │ │ ├── page.py # Main inference page render
289
+ │ │ ├── model_tab.py # Model selection tab
290
+ │ │ ├── data_tab.py # Data input tab (images/video/RTSP)
291
+ │ │ ├── solution_tab.py # Solution selection + zone drawing
292
+ │ │ ├── run_tab.py # Run tab + results display
293
+ │ │ ├── detection.py # Detection utilities (real + demo)
294
+ │ │ └── video.py # Video/image inference runners
295
+ │ │
251
296
  │ ├── components/
252
- │ │ ├── sidebar.py # Navigation sidebar
253
- │ │ ├── styles.py # Custom CSS
254
- │ │ └── wizard.py # Step indicator & navigation
297
+ │ │ ├── sidebar.py # Navigation sidebar
298
+ │ │ ├── styles.py # Custom CSS + UI helpers
299
+ │ │ ├── wizard.py # Step indicator + navigation
300
+ │ │ ├── project_manager.py # Project CRUD + state persistence
301
+ │ │ └── zone_drawer/ # Interactive canvas zone drawing
302
+ │ │
255
303
  │ └── utils/
256
- │ └── device.py # GPU/environment detection
304
+ │ ├── __init__.py # Shared helpers (defaults, state, flash)
305
+ │ ├── device.py # GPU/environment detection
306
+ │ ├── jobs.py # Background process tracking
307
+ │ ├── filesystem.py # Directory size/listing utilities
308
+ │ ├── config.py # Training config build/save/load
309
+ │ └── training_hooks.py # FlashDet training callbacks
310
+ │
311
+ ├── tests/ # Pytest test suite
312
+ │ ├── conftest.py # Shared fixtures
313
+ │ ├── test_constants.py # Constants validation
314
+ │ ├── test_cli.py # CLI tests
315
+ │ ├── test_utils/ # Utils sub-package tests
316
+ │ │ ├── test_init.py
317
+ │ │ ├── test_device.py
318
+ │ │ ├── test_filesystem.py
319
+ │ │ ├── test_config.py
320
+ │ │ └── test_jobs.py
321
+ │ ├── test_pages/ # Page logic tests
322
+ │ │ └── test_training_parsers.py
323
+ │ └── test_components/
324
+ │ └── test_project_manager.py
325
+ │
326
+ ├── .github/workflows/ci.yml # GitHub Actions CI
257
327
  ├── notebooks/
258
- │ ├── FlashStudio_Train.ipynb
259
- │ └── FlashStudio_Inference.ipynb
260
328
  ├── .streamlit/config.toml
261
329
  ├── pyproject.toml
262
330
  └── README.md
@@ -201,26 +201,93 @@ Click the **Public URL** to open FlashStudio in a new tab.
201
201
  ```
202
202
  FlashStudio/
203
203
  ├── flashstudio/
204
- │ ├── __init__.py # Package init + launch() export
205
- │ ├── app.py # Main Streamlit app (wizard flow)
206
- │ ├── launcher.py # Colab/local launcher with ngrok
207
- │ ├── cli.py # CLI entrypoint
208
- │ ├── pages/
209
- │ │ ├── dashboard.py # Overview + recent training runs
210
- │ │ ├── data.py # Dataset upload/download
211
- │ │ ├── model.py # Architecture & hyperparameter config
212
- │ │ ├── training.py # Training monitor (reads real workspace)
213
- │ │ ├── export.py # ONNX export
214
- │ │ └── inference.py # 4-step inference pipeline
204
+ │ ├── __init__.py # Package init + launch() export
205
+ │ ├── app.py # Main Streamlit app (wizard flow)
206
+ │ ├── launcher.py # Colab/local launcher with ngrok
207
+ │ ├── cli.py # CLI entrypoint
208
+ │ ├── constants.py # Centralized constants (paths, defaults, models)
209
+ │ │
210
+ │ ├── pages/ # Each page is a sub-package
211
+ │ │ ├── dashboard/
212
+ │ │ │ ├── page.py # Main dashboard render
213
+ │ │ │ ├── pipeline_status.py # Pipeline status cards
214
+ │ │ │ └── recent_runs.py # Recent training runs table
215
+ │ │ ├── data/
216
+ │ │ │ ├── page.py # Main data page render
217
+ │ │ │ ├── upload.py # Upload tab (ZIP/TAR + class config)
218
+ │ │ │ ├── download.py # Download tab (quick start + registries)
219
+ │ │ │ ├── preview.py # Preview tab (image grid + annotations)
220
+ │ │ │ ├── verify.py # Verify tab (dataset validation)
221
+ │ │ │ └── helpers.py # Shared helpers (extract, detect, convert)
222
+ │ │ ├── model/
223
+ │ │ │ ├── page.py # Main model page render
224
+ │ │ │ ├── architecture.py # Architecture tab (FlashDet/YOLO selection)
225
+ │ │ │ ├── hyperparams.py # Hyperparameters tab
226
+ │ │ │ ├── augmentation.py # Augmentation tab
227
+ │ │ │ ├── advanced.py # Advanced tab (memory, distributed)
228
+ │ │ │ └── summary.py # Config summary bar + YAML save/load
229
+ │ │ ├── training/
230
+ │ │ │ ├── page.py # Main training page render
231
+ │ │ │ ├── _common.py # Shared utilities (_get_save_dir)
232
+ │ │ │ ├── launch/ # Launch sub-package
233
+ │ │ │ │ ├── tab.py # Launch tab entry point
234
+ │ │ │ │ ├── preflight.py # Pre-flight checks
235
+ │ │ │ │ ├── runner.py # FlashDet Trainer subprocess
236
+ │ │ │ │ ├── controls.py # Start/stop/pause/resume buttons
237
+ │ │ │ │ └── dialogs.py # Clean/resume/config dialogs
238
+ │ │ │ └── monitor/ # Monitor sub-package
239
+ │ │ │ ├── tab.py # Monitor tab entry point
240
+ │ │ │ ├── run_meta.py # Run metadata extraction
241
+ │ │ │ ├── parsers.py # CSV/log parsing
242
+ │ │ │ ├── dashboard.py # Run dashboard + metrics
243
+ │ │ │ ├── curves.py # Plotly training curves
244
+ │ │ │ ├── visualizations.py # Epoch visualizations
245
+ │ │ │ ├── gt_verification.py # Ground truth verification
246
+ │ │ │ ├── log_viewer.py # Full log viewer
247
+ │ │ │ └── checkpoints.py # File browser + checkpoints
248
+ │ │ ├── export/
249
+ │ │ │ └── page.py # Export page (ONNX/TorchScript)
250
+ │ │ └── inference/
251
+ │ │ ├── page.py # Main inference page render
252
+ │ │ ├── model_tab.py # Model selection tab
253
+ │ │ ├── data_tab.py # Data input tab (images/video/RTSP)
254
+ │ │ ├── solution_tab.py # Solution selection + zone drawing
255
+ │ │ ├── run_tab.py # Run tab + results display
256
+ │ │ ├── detection.py # Detection utilities (real + demo)
257
+ │ │ └── video.py # Video/image inference runners
258
+ │ │
215
259
  │ ├── components/
216
- │ │ ├── sidebar.py # Navigation sidebar
217
- │ │ ├── styles.py # Custom CSS
218
- │ │ └── wizard.py # Step indicator & navigation
260
+ │ │ ├── sidebar.py # Navigation sidebar
261
+ │ │ ├── styles.py # Custom CSS + UI helpers
262
+ │ │ ├── wizard.py # Step indicator + navigation
263
+ │ │ ├── project_manager.py # Project CRUD + state persistence
264
+ │ │ └── zone_drawer/ # Interactive canvas zone drawing
265
+ │ │
219
266
  │ └── utils/
220
- │ └── device.py # GPU/environment detection
267
+ │ ├── __init__.py # Shared helpers (defaults, state, flash)
268
+ │ ├── device.py # GPU/environment detection
269
+ │ ├── jobs.py # Background process tracking
270
+ │ ├── filesystem.py # Directory size/listing utilities
271
+ │ ├── config.py # Training config build/save/load
272
+ │ └── training_hooks.py # FlashDet training callbacks
273
+ │
274
+ ├── tests/ # Pytest test suite
275
+ │ ├── conftest.py # Shared fixtures
276
+ │ ├── test_constants.py # Constants validation
277
+ │ ├── test_cli.py # CLI tests
278
+ │ ├── test_utils/ # Utils sub-package tests
279
+ │ │ ├── test_init.py
280
+ │ │ ├── test_device.py
281
+ │ │ ├── test_filesystem.py
282
+ │ │ ├── test_config.py
283
+ │ │ └── test_jobs.py
284
+ │ ├── test_pages/ # Page logic tests
285
+ │ │ └── test_training_parsers.py
286
+ │ └── test_components/
287
+ │ └── test_project_manager.py
288
+ │
289
+ ├── .github/workflows/ci.yml # GitHub Actions CI
221
290
  ├── notebooks/
222
- │ ├── FlashStudio_Train.ipynb
223
- │ └── FlashStudio_Inference.ipynb
224
291
  ├── .streamlit/config.toml
225
292
  ├── pyproject.toml
226
293
  └── README.md
@@ -43,6 +43,76 @@ PAGE_RENDERERS = {
43
43
  }
44
44
 
45
45
 
46
+ def _ensure_config_in_session(project_id: str):
47
+ """Re-seed any missing model config keys from saved project state.
48
+
49
+ Streamlit may clean up widget-owned session state keys when the widget is
50
+ not rendered (e.g. navigating away from the Model page). This ensures
51
+ critical config values like epochs, batch_size, lr etc. always remain
52
+ available in session state for other pages to read.
53
+ """
54
+ import os
55
+ import json
56
+ from flashstudio.components.project_manager import get_project_dir
57
+
58
+ config_keys = [
59
+ "epochs", "batch_size", "lr", "img_size", "weight_decay",
60
+ "warmup_epochs", "patience", "num_workers", "grad_accum",
61
+ "val_interval", "lr_final_ratio", "optimizer",
62
+ "model_arch", "arch_family", "pico_backbone",
63
+ "amp", "grad_clip", "multiscale",
64
+ "aug_mosaic", "aug_mixup", "aug_copypaste",
65
+ "finetune_strategy", "pretrain_option", "custom_weights",
66
+ "lora_variant", "lora_rank", "lora_alpha", "lora_dropout", "lora_targets",
67
+ "activation_checkpointing", "activation_offloading", "optimizer_in_bwd",
68
+ "compile_model", "use_8bit_optimizer", "ddp", "chunked_loss", "chunk_size",
69
+ "save_dir", "save_best", "resume_training",
70
+ "num_classes", "class_names",
71
+ ]
72
+
73
+ missing = [k for k in config_keys if k not in st.session_state]
74
+ if not missing:
75
+ return
76
+
77
+ proj_dir = get_project_dir(project_id)
78
+ state_file = os.path.join(proj_dir, "session_state.json")
79
+ if not os.path.isfile(state_file):
80
+ return
81
+
82
+ try:
83
+ with open(state_file, "r") as f:
84
+ saved = json.load(f)
85
+ except (json.JSONDecodeError, OSError):
86
+ return
87
+
88
+ for key in missing:
89
+ if key in saved:
90
+ st.session_state[key] = saved[key]
91
+
92
+
93
+ def _init_config_mirror():
94
+ """Build initial _config_mirror from loaded session state."""
95
+ mirror_keys = [
96
+ "epochs", "batch_size", "lr", "img_size", "weight_decay",
97
+ "warmup_epochs", "patience", "num_workers", "grad_accum",
98
+ "val_interval", "lr_final_ratio", "optimizer",
99
+ "model_arch", "arch_family", "pico_backbone",
100
+ "amp", "grad_clip", "multiscale",
101
+ "aug_mosaic", "aug_mixup", "aug_copypaste",
102
+ "finetune_strategy", "pretrain_option", "custom_weights",
103
+ "lora_variant", "lora_rank", "lora_alpha", "lora_dropout", "lora_targets",
104
+ "activation_checkpointing", "activation_offloading", "optimizer_in_bwd",
105
+ "compile_model", "use_8bit_optimizer", "ddp", "chunked_loss", "chunk_size",
106
+ "save_dir", "save_best", "resume_training",
107
+ ]
108
+ mirror = {}
109
+ for k in mirror_keys:
110
+ if k in st.session_state:
111
+ mirror[k] = st.session_state[k]
112
+ if mirror:
113
+ st.session_state["_config_mirror"] = mirror
114
+
115
+
46
116
  def main():
47
117
  import traceback as _tb
48
118
 
@@ -51,11 +121,6 @@ def main():
51
121
  if "current_step" not in st.session_state:
52
122
  st.session_state["current_step"] = 0
53
123
 
54
- # Ensure critical config values are always initialized
55
- from flashstudio.utils import DEFAULTS
56
- if not st.session_state.get("save_dir"):
57
- st.session_state["save_dir"] = DEFAULTS["save_dir"]
58
-
59
124
  # Project system — check if user has an active project
60
125
  active_project = get_active_project()
61
126
 
@@ -68,6 +133,12 @@ def main():
68
133
  if active_project and not st.session_state.get("_project_state_loaded"):
69
134
  load_project_state(active_project["id"])
70
135
  st.session_state["_project_state_loaded"] = True
136
+ # Initialize config mirror from loaded project state
137
+ _init_config_mirror()
138
+
139
+ # Re-seed any config keys that Streamlit's widget lifecycle may have removed
140
+ if active_project:
141
+ _ensure_config_in_session(active_project["id"])
71
142
 
72
143
  if st.session_state.get("show_project_manager"):
73
144
  render_project_manager_page()
@@ -105,10 +105,12 @@ def create_project(name: str, description: str = "") -> dict:
105
105
 
106
106
  def switch_project(project_id: str):
107
107
  """Switch active project and load its state."""
108
+ _clear_session_for_new_project()
108
109
  index = _load_index()
109
110
  index["active"] = project_id
110
111
  _save_index(index)
111
112
  load_project_state(project_id)
113
+ st.session_state["_project_state_loaded"] = True
112
114
 
113
115
 
114
116
  def delete_project(project_id: str):
@@ -203,6 +205,8 @@ def load_project_state(project_id: str):
203
205
 
204
206
  def duplicate_project(project_id: str, new_name: str) -> dict:
205
207
  """Duplicate an existing project with a new name."""
208
+ index = _load_index()
209
+ prev_active = index.get("active")
206
210
  src_dir = get_project_dir(project_id)
207
211
  new_project = create_project(new_name)
208
212
  dst_dir = get_project_dir(new_project["id"])
@@ -220,12 +224,20 @@ def duplicate_project(project_id: str, new_name: str) -> dict:
220
224
  if os.path.isfile(src_state):
221
225
  shutil.copy2(src_state, os.path.join(dst_dir, "session_state.json"))
222
226
 
227
+ # Restore previously active project so duplication doesn't switch context
228
+ if prev_active and prev_active != new_project["id"]:
229
+ idx = _load_index()
230
+ idx["active"] = prev_active
231
+ _save_index(idx)
232
+
223
233
  return new_project
224
234
 
225
235
 
226
236
  def get_project_stats(project_id: str) -> dict:
227
237
  """Get statistics for a project."""
228
- import re, csv, glob
238
+ import re
239
+ import csv
240
+ import glob
229
241
  proj_dir = get_project_dir(project_id)
230
242
  stats = {"runs": 0, "best_map": None, "total_size": "0 KB", "has_export": False}
231
243
 
@@ -257,7 +269,8 @@ def get_project_stats(project_id: str) -> dict:
257
269
  except Exception:
258
270
  pass
259
271
  if best_map == 0:
260
- logs = glob.glob(os.path.join(rd, "train_*.log"))
272
+ from flashstudio.constants import TRAINING_LOG_GLOB
273
+ logs = glob.glob(os.path.join(rd, TRAINING_LOG_GLOB))
261
274
  for lf in logs:
262
275
  try:
263
276
  with open(lf, "r", encoding="utf-8", errors="replace") as f:
@@ -298,7 +311,6 @@ def render_project_selector():
298
311
  return False # No projects exist — show creation UI
299
312
 
300
313
  # Quick selector
301
- project_names = {p["id"]: p["name"] for p in projects}
302
314
  active_idx = 0
303
315
  for i, p in enumerate(projects):
304
316
  if p["id"] == active_id:
@@ -324,8 +336,8 @@ def render_project_selector():
324
336
  def render_project_manager_page():
325
337
  """Full project management page — create, switch, delete, manage projects."""
326
338
  st.markdown(
327
- "<div style='text-align:center; padding:1rem 0;'>"
328
- "<b style='font-size:1.4rem;'>Project Manager</b>"
339
+ "<div style='text-align:center; padding:0.4rem 0;'>"
340
+ "<b style='font-size:1.2rem;'>Project Manager</b>"
329
341
  "</div>",
330
342
  unsafe_allow_html=True,
331
343
  )
@@ -336,7 +348,7 @@ def render_project_manager_page():
336
348
 
337
349
  # ─── Create New Project ───
338
350
  with st.container(border=True):
339
- st.markdown("### Create New Project")
351
+ st.markdown("#### Create New Project")
340
352
  col_name, col_desc, col_btn = st.columns([2, 3, 1])
341
353
  with col_name:
342
354
  new_name = st.text_input("Project Name", placeholder="e.g. PPE Detection v2",
@@ -349,7 +361,7 @@ def render_project_manager_page():
349
361
  st.markdown("<div style='height:28px'></div>", unsafe_allow_html=True)
350
362
  if st.button("Create", type="primary", key="create_project_btn",
351
363
  use_container_width=True, disabled=not new_name):
352
- proj = create_project(new_name, new_desc)
364
+ create_project(new_name, new_desc)
353
365
  st.success(f"Project **{new_name}** created!")
354
366
  _clear_session_for_new_project()
355
367
  st.rerun()
@@ -359,14 +371,12 @@ def render_project_manager_page():
359
371
  st.info("No projects yet. Create your first project above to get started.")
360
372
  return
361
373
 
362
- st.markdown(f"### Your Projects ({len(projects)})")
374
+ st.markdown(f"#### Your Projects ({len(projects)})")
363
375
 
364
376
  for p in sorted(projects, key=lambda x: x.get("last_modified", ""), reverse=True):
365
377
  is_active = p["id"] == active_id
366
- proj_dir = get_project_dir(p["id"])
367
378
  stats = get_project_stats(p["id"])
368
379
 
369
- border_style = "border-left: 3px solid #7C3AED;" if is_active else ""
370
380
  with st.container(border=True):
371
381
  col_info, col_stats, col_actions = st.columns([3, 2, 2])
372
382
 
@@ -39,17 +39,17 @@ def render_sidebar():
39
39
  logo_b64 = _get_logo_b64()
40
40
  if logo_b64:
41
41
  st.markdown(
42
- f'<div style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.2rem 0.4rem;">'
43
- f'<img src="data:image/png;base64,{logo_b64}" style="width:32px;height:32px;">'
42
+ f'<div style="display:flex;flex-direction:column;align-items:center;gap:0.3rem;padding:0.6rem 0.2rem 0.5rem;">'
43
+ f'<img src="data:image/png;base64,{logo_b64}" style="width:56px;height:56px;">'
44
44
  f'<b style="font-size:1.1rem;color:{COLOR_TEXT_PRIMARY};">FlashStudio</b>'
45
45
  f'</div>',
46
46
  unsafe_allow_html=True,
47
47
  )
48
48
  else:
49
49
  st.markdown(
50
- f'<div style="text-align:center;padding:0.4rem 0 0.3rem;">'
50
+ f'<div style="display:flex;flex-direction:column;align-items:center;gap:0.2rem;padding:0.5rem 0 0.3rem;">'
51
51
  f'<b style="font-size:1.5rem;">FS</b>'
52
- f'<b style="font-size:1rem;color:{COLOR_TEXT_PRIMARY};margin-left:0.3rem;">FlashStudio</b>'
52
+ f'<b style="font-size:1rem;color:{COLOR_TEXT_PRIMARY};">FlashStudio</b>'
53
53
  f'</div>',
54
54
  unsafe_allow_html=True,
55
55
  )
@@ -71,14 +71,15 @@ def render_sidebar():
71
71
  st.divider()
72
72
 
73
73
  gpu = get_gpu_info()
74
+ from flashstudio.constants import GPU_NAME_TRUNCATE, DATASET_NAME_TRUNCATE
74
75
  train_status = st.session_state.get("training_status", "Idle")
75
76
  status_color = COLOR_SUCCESS if train_status == "Complete" else (
76
77
  COLOR_WARNING if train_status == "Running" else (
77
78
  COLOR_ERROR if train_status == "Failed" else COLOR_TEXT_SECONDARY))
78
79
  st.markdown(
79
80
  f'<div style="font-size:0.75rem;color:{COLOR_TEXT_BODY};padding:0.1rem 0.5rem;line-height:1.9;">'
80
- f'GPU: {gpu["name"][:15] if gpu["available"] else "CPU"}<br>'
81
- f'Data: {(st.session_state.get("dataset_name") or "—")[:15]}<br>'
81
+ f'GPU: {gpu["name"][:GPU_NAME_TRUNCATE] if gpu["available"] else "CPU"}<br>'
82
+ f'Data: {(st.session_state.get("dataset_name") or "—")[:DATASET_NAME_TRUNCATE]}<br>'
82
83
  f'Model: {st.session_state.get("model_arch", "Pico").replace("FlashDet-", "")}<br>'
83
84
  f'Status: <span style="color:{status_color};font-weight:600;">{train_status[:12]}</span>'
84
85
  f'</div>',
@@ -9,13 +9,33 @@ def inject_custom_css():
9
9
  <style>
10
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
11
11
 
12
- /* ═══════ GLOBAL ═══════ */
13
- .stApp { font-family: 'Inter', -apple-system, sans-serif; }
12
+ /* ═══════ GLOBAL — NO SCROLL ═══════ */
13
+ .stApp {
14
+ font-family: 'Inter', -apple-system, sans-serif;
15
+ overflow: hidden !important;
16
+ height: 100vh !important;
17
+ }
18
+
19
+ .main {
20
+ overflow: hidden !important;
21
+ height: 100vh !important;
22
+ }
14
23
 
15
24
  .main .block-container {
16
25
  padding: 0.8rem 1.5rem 0.5rem !important;
17
26
  max-width: 1500px;
27
+ height: calc(100vh - 2rem) !important;
28
+ overflow: hidden !important;
29
+ }
30
+
31
+ /* Allow internal scroll on specific containers */
32
+ .scrollable-panel {
33
+ overflow-y: auto !important;
34
+ scrollbar-width: thin;
35
+ scrollbar-color: #D1D5DB transparent;
18
36
  }
37
+ .scrollable-panel::-webkit-scrollbar { width: 4px; }
38
+ .scrollable-panel::-webkit-scrollbar-thumb { background: #D1D5DB; border-radius: 2px; }
19
39
 
20
40
  .main .stMarkdown { margin-bottom: 0 !important; }
21
41
  .main .element-container { margin-bottom: 0.15rem !important; }
@@ -152,6 +172,17 @@ def inject_custom_css():
152
172
  border-bottom: 2px solid #7C3AED !important;
153
173
  }
154
174
 
175
+ /* Tab content area: constrain height to prevent page scroll */
176
+ .stTabs [data-baseweb="tab-panel"] {
177
+ max-height: calc(100vh - 12rem) !important;
178
+ overflow-y: auto !important;
179
+ overflow-x: hidden !important;
180
+ scrollbar-width: thin;
181
+ scrollbar-color: #D1D5DB transparent;
182
+ }
183
+ .stTabs [data-baseweb="tab-panel"]::-webkit-scrollbar { width: 4px; }
184
+ .stTabs [data-baseweb="tab-panel"]::-webkit-scrollbar-thumb { background: #D1D5DB; border-radius: 2px; }
185
+
155
186
  /* ═══════ FORM INPUTS ═══════ */
156
187
  .main .stSelectbox > div > div,
157
188
  .main .stTextInput > div > div > input,
@@ -296,9 +327,15 @@ def render_info_bar(items: dict):
296
327
 
297
328
 
298
329
  def render_page_header(icon: str, title: str, subtitle: str = ""):
330
+ icon_html = f'<span style="margin-right:0.4rem;">{icon}</span>' if icon else ""
331
+ subtitle_html = (
332
+ f'<div style="font-size:0.82rem;color:#6B7280;margin-top:0.1rem;">{subtitle}</div>'
333
+ if subtitle else ""
334
+ )
299
335
  st.markdown(
300
336
  f'<div style="margin-bottom:0.4rem;">'
301
- f'<span style="font-size:1.2rem;font-weight:700;color:#1A1A2E;">{title}</span>'
337
+ f'<span style="font-size:1.2rem;font-weight:700;color:#1A1A2E;">{icon_html}{title}</span>'
338
+ f'{subtitle_html}'
302
339
  f'</div>',
303
340
  unsafe_allow_html=True,
304
341
  )
@@ -124,6 +124,13 @@ VIS_QUEUE_SIZE = 3
124
124
  VIS_DIR_NAMES = ("visualizations", "plots", "vis")
125
125
  VIS_SKIP_FILE = "latest_visualization.jpg"
126
126
  GT_VERIFICATION_DIR = "gt_verification"
127
+ GT_REPORT_FILE = "verification_report.json"
128
+ GT_SUMMARY_FILE = "verification_summary.txt"
129
+ TRAINING_LOG_GLOB = "train_*.log"
130
+ ONNX_MODEL_FILE = "model.onnx"
131
+ ONNX_DATA_FILE = "model.onnx.data"
132
+ CONFIG_YAML_FILE = "config.yaml"
133
+ RESULTS_JSON_FILE = "results.json"
127
134
 
128
135
  # ════════════════════════════════════════
129
136
  # MODEL ARCHITECTURE
@@ -135,7 +142,16 @@ MODEL_SIZE_DEFAULT = "n"
135
142
 
136
143
  ARCH_FAMILIES = ["FlashDet (recommended)", "YOLOv8", "YOLOv9", "YOLOv10", "YOLOv11", "YOLOX"]
137
144
  OPTIMIZERS = ["AdamW", "SGD", "MuSGD"]
145
+ DEFAULT_OPTIMIZER = "AdamW"
138
146
  LORA_VARIANTS = ["standard", "dora", "lora_plus", "adalora", "ortho", "lora_fa"]
147
+ LORA_RANK_DEFAULT = 8
148
+ LORA_ALPHA_DEFAULT = 16.0
149
+ LORA_DROPOUT_DEFAULT = 0.05
150
+ LORA_TARGETS_DEFAULT = ["backbone", "fpn"]
151
+ QLORA_DTYPE_DEFAULT = "int8"
152
+ CHUNKED_LOSS_CHUNK_SIZE = 1024
153
+ DEFAULT_FINETUNE_STRATEGY = "Full fine-tune"
154
+ DEFAULT_PRETRAIN_OPTION = "COCO pretrained"
139
155
 
140
156
  FLASHDET_MODELS = {
141
157
  "FlashDet-Pico": {"size": "p", "params": "~298K", "speed": "Ultra-fast", "backbone": "LiteBackbone(0.5x)", "neck": "PicoNeck(64ch)", "head": "E2EDualHead", "for": "Edge/Mobile"},
@@ -159,7 +175,25 @@ ZONE_DEFAULT_POLYGON = "100,100\n500,100\n500,400\n100,400"
159
175
  # FONT
160
176
  # ════════════════════════════════════════
161
177
 
162
- FONT_PATH_LINUX = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
178
+ def _resolve_font_path():
179
+ """Find a usable TrueType font across platforms, with env override."""
180
+ env = os.environ.get("FLASHSTUDIO_FONT_PATH", "")
181
+ if env and os.path.isfile(env):
182
+ return env
183
+ candidates = [
184
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", # Debian/Ubuntu
185
+ "/usr/share/fonts/dejavu-sans-fonts/DejaVuSans-Bold.ttf", # Fedora/RHEL
186
+ "/usr/share/fonts/TTF/DejaVuSans-Bold.ttf", # Arch
187
+ "/System/Library/Fonts/Helvetica.ttc", # macOS
188
+ "C:\\Windows\\Fonts\\arial.ttf", # Windows
189
+ ]
190
+ for c in candidates:
191
+ if os.path.isfile(c):
192
+ return c
193
+ return ""
194
+
195
+ FONT_PATH = _resolve_font_path()
196
+ FONT_PATH_LINUX = FONT_PATH # backward compat alias
163
197
  FONT_SIZE_DEFAULT = 14
164
198
 
165
199
  # ════════════════════════════════════════
@@ -0,0 +1,3 @@
1
+ """FlashStudio core package."""
2
+
3
+ from flashstudio.core.constants import * # noqa: F401,F403
@@ -0,0 +1,3 @@
1
+ """Re-export all constants from the canonical location."""
2
+
3
+ from flashstudio.constants import * # noqa: F401,F403
@@ -0,0 +1,8 @@
1
+ """FlashStudio pages package — each page is a sub-package."""
2
+
3
+ from flashstudio.pages.dashboard.page import render_dashboard # noqa: F401
4
+ from flashstudio.pages.data.page import render_data_page # noqa: F401
5
+ from flashstudio.pages.model.page import render_model_page # noqa: F401
6
+ from flashstudio.pages.training.page import render_training_page # noqa: F401
7
+ from flashstudio.pages.export.page import render_export_page # noqa: F401
8
+ from flashstudio.pages.inference.page import render_inference_page # noqa: F401
@@ -0,0 +1,3 @@
1
+ """FlashStudio — Dashboard page package."""
2
+
3
+ from flashstudio.pages.dashboard.page import render_dashboard # noqa: F401