dflash-console 0.3.136__tar.gz → 0.3.138__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.
Files changed (177) hide show
  1. {dflash_console-0.3.136 → dflash_console-0.3.138}/PKG-INFO +2 -2
  2. {dflash_console-0.3.136 → dflash_console-0.3.138}/README.md +1 -1
  3. {dflash_console-0.3.136 → dflash_console-0.3.138}/api/app.py +4 -0
  4. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/model_presets.py +20 -5
  5. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/model_stack.py +23 -6
  6. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/server_boot.py +18 -0
  7. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/version.py +1 -1
  8. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/RELEASING.md +37 -7
  9. {dflash_console-0.3.136 → dflash_console-0.3.138}/pyproject.toml +1 -1
  10. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/index.html +2 -2
  11. {dflash_console-0.3.136 → dflash_console-0.3.138}/.gitignore +0 -0
  12. {dflash_console-0.3.136 → dflash_console-0.3.138}/LICENSE +0 -0
  13. {dflash_console-0.3.136 → dflash_console-0.3.138}/NOTICE.md +0 -0
  14. {dflash_console-0.3.136 → dflash_console-0.3.138}/api/__init__.py +0 -0
  15. {dflash_console-0.3.136 → dflash_console-0.3.138}/api/gateway.py +0 -0
  16. {dflash_console-0.3.136 → dflash_console-0.3.138}/assets/dflash_console_logo.png +0 -0
  17. {dflash_console-0.3.136 → dflash_console-0.3.138}/assets/dflash_console_logo_only.png +0 -0
  18. {dflash_console-0.3.136 → dflash_console-0.3.138}/assets/dflash_console_logo_only2.png +0 -0
  19. {dflash_console-0.3.136 → dflash_console-0.3.138}/assets/dflash_console_logo_only_clear.png +0 -0
  20. {dflash_console-0.3.136 → dflash_console-0.3.138}/assets/dflash_txt_logo.png +0 -0
  21. {dflash_console-0.3.136 → dflash_console-0.3.138}/config.example.json +0 -0
  22. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/__init__.py +0 -0
  23. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/api_access_log.py +0 -0
  24. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/api_catalog.py +0 -0
  25. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/api_introspection.py +0 -0
  26. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/auto_register.py +0 -0
  27. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/catalog_load.py +0 -0
  28. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/chat_proxy.py +0 -0
  29. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/chat_ready.py +0 -0
  30. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/chat_vision.py +0 -0
  31. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/components_hub.py +0 -0
  32. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/config.py +0 -0
  33. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/dflash_generation.py +0 -0
  34. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/display_names.py +0 -0
  35. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/embedding_server.py +0 -0
  36. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/engine_state.py +0 -0
  37. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/freetoken_runtime_install.py +0 -0
  38. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/fs_browse.py +0 -0
  39. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/fs_reveal.py +0 -0
  40. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/gateway_routing.py +0 -0
  41. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/gguf_meta.py +0 -0
  42. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/gpu_devices.py +0 -0
  43. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/gpu_processes.py +0 -0
  44. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hardware_apply.py +0 -0
  45. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hardware_info.py +0 -0
  46. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hf_catalog_cache.py +0 -0
  47. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hf_engines.py +0 -0
  48. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hf_install.py +0 -0
  49. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hf_local_match.py +0 -0
  50. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/hf_model_fit.py +0 -0
  51. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/huggingface.py +0 -0
  52. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/inference_stats.py +0 -0
  53. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/library_import.py +0 -0
  54. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/load_progress.py +0 -0
  55. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/local_models.py +0 -0
  56. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/log_utils.py +0 -0
  57. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/memory_guardrails.py +0 -0
  58. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/model_discovery.py +0 -0
  59. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/model_paths.py +0 -0
  60. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/model_runtime_policy.py +0 -0
  61. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/net_listeners.py +0 -0
  62. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/node_connect.py +0 -0
  63. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/ocr_setup.py +0 -0
  64. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/remote_nodes.py +0 -0
  65. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtime.py +0 -0
  66. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtime_install_job.py +0 -0
  67. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtime_recommendations.py +0 -0
  68. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/__init__.py +0 -0
  69. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/base.py +0 -0
  70. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/contention.py +0 -0
  71. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/faster_whisper.py +0 -0
  72. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/freetoken.py +0 -0
  73. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/noop.py +0 -0
  74. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/ollama.py +0 -0
  75. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/piper.py +0 -0
  76. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/registry.py +0 -0
  77. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/stt.py +0 -0
  78. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/transformers_hf.py +0 -0
  79. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/vibevoice.py +0 -0
  80. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/runtimes/vllm.py +0 -0
  81. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/setup.py +0 -0
  82. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/stack_match.py +0 -0
  83. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/status_report.py +0 -0
  84. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/system_stats.py +0 -0
  85. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/transformers_runtime_install.py +0 -0
  86. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/vision_setup.py +0 -0
  87. {dflash_console-0.3.136 → dflash_console-0.3.138}/core/vllm_runtime_install.py +0 -0
  88. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/__init__.py +0 -0
  89. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/__main__.py +0 -0
  90. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/cli.py +0 -0
  91. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/commands.py +0 -0
  92. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/http.py +0 -0
  93. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/render.py +0 -0
  94. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/resolve.py +0 -0
  95. {dflash_console-0.3.136 → dflash_console-0.3.138}/dflash_cli/server_takeover.py +0 -0
  96. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ARCHITECTURE.md +0 -0
  97. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/CLI.md +0 -0
  98. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/GOING-PUBLIC.md +0 -0
  99. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/LICENSING.md +0 -0
  100. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/MULTI-MODAL-AGENT-PROMPT.md +0 -0
  101. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/MULTI-MODAL-RUNTIME-PLAN.md +0 -0
  102. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/PRODUCTION.md +0 -0
  103. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/STT-ENGINE-DECISION.md +0 -0
  104. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/USER-GUIDE.md +0 -0
  105. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/announcements/public-preview-v0.3.103.md +0 -0
  106. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/announcements/public-preview-v0.3.122.md +0 -0
  107. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/announcements/public-preview.md +0 -0
  108. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/automatic-updates.md +0 -0
  109. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/plans/shared-speak-stt-service.md +0 -0
  110. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.103.md +0 -0
  111. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.106.md +0 -0
  112. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.117.md +0 -0
  113. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.118.md +0 -0
  114. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.119.md +0 -0
  115. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.120.md +0 -0
  116. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.121.md +0 -0
  117. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.122.md +0 -0
  118. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.123.md +0 -0
  119. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.128.md +0 -0
  120. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.129.md +0 -0
  121. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.132.md +0 -0
  122. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/release-notes-0.3.133.md +0 -0
  123. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/README.md +0 -0
  124. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/devices-view.md +0 -0
  125. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/model-search.md +0 -0
  126. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/models-view.md +0 -0
  127. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/nodes-v1-plan.md +0 -0
  128. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-appearance.md +0 -0
  129. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-chat.md +0 -0
  130. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-developer.md +0 -0
  131. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-general.md +0 -0
  132. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-hardware.md +0 -0
  133. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-integrations.md +0 -0
  134. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-lm-link.md +0 -0
  135. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-model-defaults.md +0 -0
  136. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-runtime.md +0 -0
  137. {dflash_console-0.3.136 → dflash_console-0.3.138}/docs/ui/settings-runtimes.md +0 -0
  138. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/app-functional.html +0 -0
  139. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/css/dflash-console.css +0 -0
  140. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/css/dflash-shell.css +0 -0
  141. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/css/studio.css +0 -0
  142. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/about-live.js +0 -0
  143. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/app-settings-live.js +0 -0
  144. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/app.js +0 -0
  145. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/chat-live.js +0 -0
  146. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/component-install-live.js +0 -0
  147. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/console-api.js +0 -0
  148. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/dashboard.js +0 -0
  149. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/desktop-shell.js +0 -0
  150. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/dflash-console-ui.js +0 -0
  151. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/docs-live.js +0 -0
  152. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/download-queue.js +0 -0
  153. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/downloads-live.js +0 -0
  154. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/library-browse-live.js +0 -0
  155. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/library-scan-live.js +0 -0
  156. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/library-type-options.js +0 -0
  157. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/log-format.js +0 -0
  158. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/model-card.js +0 -0
  159. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/model-catalog-groups.js +0 -0
  160. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/model-search-live.js +0 -0
  161. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/models-live.js +0 -0
  162. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/nodes-live.js +0 -0
  163. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/runtime-recommendations.js +0 -0
  164. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/runtime-steppers.js +0 -0
  165. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/select-theme.js +0 -0
  166. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/server-live.js +0 -0
  167. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/server-reload-watch.js +0 -0
  168. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/settings-live.js +0 -0
  169. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/setup-wizard-live.js +0 -0
  170. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/speak-live.js +0 -0
  171. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/stack-wizard.js +0 -0
  172. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/status-feed.js +0 -0
  173. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/sysbar-live.js +0 -0
  174. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/table-column-resize.js +0 -0
  175. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/js/ui-layout-prefs.js +0 -0
  176. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/vendor/marked.min.js +0 -0
  177. {dflash_console-0.3.136 → dflash_console-0.3.138}/static/vendor/purify.min.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dflash-console
3
- Version: 0.3.136
3
+ Version: 0.3.138
4
4
  Summary: Local control panel and terminal CLI for DFlash stacks and local model runtimes
5
5
  Project-URL: Homepage, https://github.com/ilan4ever/Dflash-Console
6
6
  Project-URL: Repository, https://github.com/ilan4ever/Dflash-Console
@@ -672,7 +672,7 @@ from one UI, then talk to them through a single OpenAI-compatible port.
672
672
 
673
673
  > **Status:** Public preview for local, single-user Windows use.
674
674
 
675
- **Developer:** ILAN AVIV · **UI:** [http://127.0.0.1:8900/](http://127.0.0.1:8900/) · **Version:** v0.3.136
675
+ **Developer:** ILAN AVIV · **UI:** [http://127.0.0.1:8900/](http://127.0.0.1:8900/) · **Version:** v0.3.138
676
676
 
677
677
  ## Download (Windows)
678
678
 
@@ -6,7 +6,7 @@ from one UI, then talk to them through a single OpenAI-compatible port.
6
6
 
7
7
  > **Status:** Public preview for local, single-user Windows use.
8
8
 
9
- **Developer:** ILAN AVIV · **UI:** [http://127.0.0.1:8900/](http://127.0.0.1:8900/) · **Version:** v0.3.136
9
+ **Developer:** ILAN AVIV · **UI:** [http://127.0.0.1:8900/](http://127.0.0.1:8900/) · **Version:** v0.3.138
10
10
 
11
11
  ## Download (Windows)
12
12
 
@@ -2374,6 +2374,8 @@ def _ensure_server_ready_for_chat(
2374
2374
  from core.runtime import build_server_status
2375
2375
 
2376
2376
  started = ensure_engine_listener_for_chat(server, cfg=cfg)
2377
+ if not started.get('success') and str(started.get('error') or '') == 'dflash_stack_repair_required':
2378
+ raise HTTPException(status_code=409, detail=started)
2377
2379
  cfg = load_config()
2378
2380
  server = normalize_server(get_server(cfg, server_id) or server)
2379
2381
  rebound_port = int(started.get('port') or 0)
@@ -2461,6 +2463,8 @@ def _ensure_server_ready_for_chat(
2461
2463
 
2462
2464
  result = load_server_checkpoint(server, cfg=cfg)
2463
2465
  if not result.get('success'):
2466
+ if str(result.get('error') or '') == 'dflash_stack_repair_required' or result.get('repair'):
2467
+ raise HTTPException(status_code=409, detail=result)
2464
2468
  raise HTTPException(
2465
2469
  status_code=503,
2466
2470
  detail={
@@ -127,11 +127,26 @@ def write_server_preset(
127
127
  }
128
128
  draft = None
129
129
  if use_draft is not False and draft_path_resolved:
130
- draft = {
131
- 'role': 'draft-dflash',
132
- 'label': Path(draft_path_resolved).name,
133
- 'path': draft_path_resolved,
134
- }
130
+ draft_file = Path(draft_path_resolved).expanduser()
131
+ if draft_file.is_file():
132
+ draft = {
133
+ 'role': 'draft-dflash',
134
+ 'label': draft_file.name,
135
+ 'path': str(draft_file),
136
+ }
137
+ if use_draft is not False and profile_requires_draft(preset_profile) and not draft:
138
+ stack = resolve_model_stack({**server, 'target_path': '', 'draft_path': ''}, cfg=cfg)
139
+ draft_row = next(
140
+ (row for row in stack if str(row.get('role') or '').startswith('draft')),
141
+ None,
142
+ )
143
+ draft_path = str(draft_row.get('path') or '').strip() if draft_row else ''
144
+ if draft_path and Path(draft_path).expanduser().is_file():
145
+ draft = {
146
+ 'role': str(draft_row.get('role') or 'draft-dflash'),
147
+ 'label': str(draft_row.get('label') or Path(draft_path).name),
148
+ 'path': draft_path,
149
+ }
135
150
  else:
136
151
  stack = resolve_model_stack(server, cfg=cfg)
137
152
  target = next((row for row in stack if row.get('role') == 'target'), None)
@@ -18,6 +18,25 @@ def _console_models_dir(cfg: dict[str, Any] | None = None) -> Path:
18
18
  return get_models_root(cfg)
19
19
 
20
20
 
21
+ def _resolve_gemma_draft_path(models: Path, root: Path, filename: str) -> Path:
22
+ """Return the first existing Gemma DFlash draft path, else the preferred location."""
23
+ candidates = [
24
+ models / 'gemma-draft' / filename,
25
+ models / 'google' / 'gemma-draft' / filename,
26
+ root / 'models' / 'gemma-draft' / filename,
27
+ root / 'models' / 'google' / 'gemma-draft' / filename,
28
+ ]
29
+ if filename.lower().startswith('gemma-4-12b'):
30
+ candidates.extend([
31
+ models / 'williamliao' / 'gemma-4-12B-it-DFlash-GGUF' / 'gemma-4-12B-it-DFlash-F16.gguf',
32
+ root / 'models' / 'williamliao' / 'gemma-4-12B-it-DFlash-GGUF' / 'gemma-4-12B-it-DFlash-F16.gguf',
33
+ ])
34
+ for candidate in candidates:
35
+ if candidate.is_file():
36
+ return candidate
37
+ return candidates[0]
38
+
39
+
21
40
  def _file_size_gb(path: Path) -> float | None:
22
41
  try:
23
42
  if path.is_file():
@@ -53,7 +72,9 @@ def _find_gemma12_target(*, cfg: dict[str, Any] | None = None) -> Path | None:
53
72
  models = _console_models_dir(cfg)
54
73
  candidates: list[Path] = [
55
74
  models / 'gemma-4-12b-it' / 'gemma-4-12B-it-Q4_K_M.gguf',
75
+ models / 'google' / 'gemma-4-12b-it' / 'gemma-4-12B-it-Q4_K_M.gguf',
56
76
  root / 'models' / 'gemma-4-12b-it' / 'gemma-4-12B-it-Q4_K_M.gguf',
77
+ root / 'models' / 'google' / 'gemma-4-12b-it' / 'gemma-4-12B-it-Q4_K_M.gguf',
57
78
  _lmstudio_models_dir() / 'bartowski' / 'gemma-4-12B-it-GGUF' / 'gemma-4-12B-it-Q4_K_M.gguf',
58
79
  _lmstudio_models_dir() / 'google' / 'gemma-4-12B-it-qat-q4_0-gguf' / 'gemma-4-12B_q4_0-it.gguf',
59
80
  _lmstudio_models_dir() / 'google' / 'gemma-4-12b-it-qat-q4_0-gguf' / 'gemma-4-12b-it-qat-q4_0.gguf',
@@ -144,12 +165,8 @@ def resolve_model_stack(server: dict[str, Any], *, cfg: dict[str, Any] | None =
144
165
  gemma_target = models / 'google' / 'gemma-4-31B-it-qat-q4_0-gguf' / 'gemma-4-31B_q4_0-it.gguf'
145
166
  if not gemma_target.is_file():
146
167
  gemma_target = _lmstudio_models_dir() / 'google' / 'gemma-4-31B-it-qat-q4_0-gguf' / 'gemma-4-31B_q4_0-it.gguf'
147
- gemma_draft = models / 'gemma-draft' / 'gemma-4-31B-it-DFlash-Q4_K_M.gguf'
148
- if not gemma_draft.is_file():
149
- gemma_draft = root / 'models' / 'gemma-draft' / 'gemma-4-31B-it-DFlash-Q4_K_M.gguf'
150
- gemma12_draft = models / 'gemma-draft' / 'gemma-4-12B-it-DFlash-Q4_K_M.gguf'
151
- if not gemma12_draft.is_file():
152
- gemma12_draft = root / 'models' / 'gemma-draft' / 'gemma-4-12B-it-DFlash-Q4_K_M.gguf'
168
+ gemma_draft = _resolve_gemma_draft_path(models, root, 'gemma-4-31B-it-DFlash-Q4_K_M.gguf')
169
+ gemma12_draft = _resolve_gemma_draft_path(models, root, 'gemma-4-12B-it-DFlash-Q4_K_M.gguf')
153
170
  bonsai_root = root / 'bonsai-27b'
154
171
  bonsai_target = bonsai_root / 'models' / 'ternary-gguf' / '27B' / 'Ternary-Bonsai-27B-Q2_0.gguf'
155
172
  bonsai_draft = bonsai_root / 'models' / 'ternary-gguf' / '27B' / 'Ternary-Bonsai-27B-dspark-Q4_1.gguf'
@@ -869,6 +869,24 @@ def validate_dflash_stack(
869
869
  )
870
870
  target = str(model_path or entry.get('target_path') or target_row.get('path') or '').strip()
871
871
  draft = str(entry.get('draft_path') or draft_row.get('path') or '').strip()
872
+ if (target and not Path(target).expanduser().is_file()) or (
873
+ draft and not Path(draft).expanduser().is_file()
874
+ ):
875
+ fallback_stack = resolve_model_stack(
876
+ {**entry, 'target_path': '', 'draft_path': ''},
877
+ cfg=cfg,
878
+ )
879
+ fallback_target = next((row for row in fallback_stack if row.get('role') == 'target'), {})
880
+ fallback_draft = next(
881
+ (row for row in fallback_stack if str(row.get('role') or '').startswith('draft')),
882
+ {},
883
+ )
884
+ if target and not Path(target).expanduser().is_file():
885
+ target = str(fallback_target.get('path') or target).strip()
886
+ if draft and not Path(draft).expanduser().is_file():
887
+ draft = str(fallback_draft.get('path') or draft).strip()
888
+ elif not draft:
889
+ draft = str(fallback_draft.get('path') or '').strip()
872
890
  generation = infer_dflash_generation(draft)
873
891
  if not target:
874
892
  return _dflash_repair_result(
@@ -1,3 +1,3 @@
1
1
  """DFlash Console release label."""
2
2
 
3
- APP_VERSION = "0.3.136"
3
+ APP_VERSION = "0.3.138"
@@ -68,7 +68,37 @@ publish production artifacts:
68
68
  Authenticode signing.
69
69
  - Hostinger SSH secrets are optional and only publish the legacy protected feed.
70
70
 
71
- ## 3. Publish the Windows EXE
71
+ ## 3. Publish a release (GitHub + PyPI)
72
+
73
+ A **release** means both distribution channels stay in sync:
74
+
75
+ | Channel | Artifact | How |
76
+ |---------|----------|-----|
77
+ | GitHub | `DFlash-Console-Setup-<version>-x64.exe`, portable EXE, `latest.json` | Tag push → Actions |
78
+ | PyPI | `dflash-console` (`pip install dflash-console`) | `scripts/publish-pypi.ps1` |
79
+
80
+ ### One-command release (recommended)
81
+
82
+ From a clean checkout, after tests pass:
83
+
84
+ ```powershell
85
+ .\scripts\publish-release.ps1
86
+ ```
87
+
88
+ This bumps the version (patch by default), builds the Windows installer locally,
89
+ commits are **not** automatic — commit and push your changes first or after bump,
90
+ creates and pushes `vX.Y.Z`, publishes to PyPI, and prints verification URLs.
91
+
92
+ Options:
93
+
94
+ - `.\scripts\publish-release.ps1 -Part minor`
95
+ - `.\scripts\publish-release.ps1 -SetVersion 1.0.0`
96
+ - `.\scripts\publish-release.ps1 -SkipBuild` — tag + PyPI only (CI builds EXEs)
97
+ - `.\scripts\publish-release.ps1 -WaitForGitHub` — wait for the Actions workflow
98
+
99
+ PyPI token: set `PYPI_TOKEN` in `.env.admin` (or `TWINE_PASSWORD` in the environment).
100
+
101
+ ### GitHub tag only (manual)
72
102
 
73
103
  Create a version tag that exactly matches `package.json`:
74
104
 
@@ -115,17 +145,17 @@ release assets.
115
145
 
116
146
  ## 5. Publish the pip package
117
147
 
118
- The PyPI name is `dflash-console`. Publish with `scripts/publish-pypi.ps1`
119
- after every version bump so `pip install dflash-console` matches the release.
148
+ The PyPI name is `dflash-console`. **Every release must publish PyPI** so
149
+ `pip install dflash-console` matches the GitHub tag. This is included in
150
+ `scripts/publish-release.ps1` and must be run by the agent whenever the user
151
+ asks to publish a release.
152
+
120
153
  Users run `dflash serve` / `dflash list`. Do not upload as `dflash`; that name
121
154
  is a different project.
122
155
 
123
- After the version bump:
124
-
125
156
  ```powershell
126
- $env:PYPI_TOKEN = '<pypi-api-token>'
127
157
  .\scripts\publish-pypi.ps1
128
158
  ```
129
159
 
130
160
  Use `.\scripts\publish-pypi.ps1 -Test` for TestPyPI. Wheels land in `dist-pypi/`.
131
- The token needs permission to upload `dflash-console`.
161
+ The token needs permission to upload `dflash-console` (`PYPI_TOKEN` in `.env.admin`).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dflash-console"
7
- version = "0.3.136"
7
+ version = "0.3.138"
8
8
  description = "Local control panel and terminal CLI for DFlash stacks and local model runtimes"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -17,7 +17,7 @@
17
17
  <div class="df-brand-text">
18
18
  <div class="df-brand-title-row">
19
19
  <span class="df-brand-title">DFlash Console</span>
20
- <span class="df-app-version" id="dfAppVersion" title="DFlash Console version">v0.3.136</span>
20
+ <span class="df-app-version" id="dfAppVersion" title="DFlash Console version">v0.3.138</span>
21
21
  </div>
22
22
  </div>
23
23
  </div>
@@ -505,7 +505,7 @@ try {
505
505
  </article>
506
506
  <article class="df-about-meta-card">
507
507
  <span class="df-about-meta-label">Version</span>
508
- <strong id="dfAboutVersion">v0.3.136</strong>
508
+ <strong id="dfAboutVersion">v0.3.138</strong>
509
509
  <span>Current Console release</span>
510
510
  </article>
511
511
  <article class="df-about-meta-card">