nexforge-cli 0.2.17__tar.gz → 0.3.0__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.
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/.gitignore +38 -30
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/PKG-INFO +9 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/README.md +8 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/config.example.toml +30 -42
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-fails-loud/345/210/206/347/272/247.md +41 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/344/274/232/350/257/235/344/270/215/345/217/230/351/207/217.md +40 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/344/276/235/350/265/226/344/270/200/350/207/264/346/200/247.md +46 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/345/233/236/346/224/276/350/214/203/345/233/264/351/207/215/345/256/232/344/271/211.md +46 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/345/256/241/350/256/241/344/273/245/350/260/203/347/224/250/351/223/276/344/270/272/345/207/206.md +38 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/350/203/275/345/212/233/345/217/257/347/224/250/346/200/247/344/270/212/346/212/245.md +43 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-10-/350/264/250/351/207/217gate/344/275/223/347/263/273.md +33 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-11-deepseek/346/241/243/344/275/215/347/256/200/345/214/226.md +42 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-11-/346/241/243/344/275/215/347/256/200/345/214/226/344/270/272/345/215/225/346/241/243flash.md +90 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-11-/347/273/210/347/253/257/347/212/266/346/200/201/345/205/234/345/272/225/346/201/242/345/244/215.md +91 -0
- nexforge_cli-0.3.0/docs/decisions/2026-09-11-/351/207/215/350/257/225/345/275/222/344/270/200/345/261/202.md +62 -0
- nexforge_cli-0.3.0/docs/decisions/README.md +67 -0
- nexforge_cli-0.3.0/docs/evidence/D10_acceptance.log +17 -0
- nexforge_cli-0.3.0/docs/evidence/D10_full_test.log +44 -0
- nexforge_cli-0.3.0/docs/evidence/D11_a4.log +36 -0
- nexforge_cli-0.3.0/docs/evidence/D11_full_test.log +45 -0
- nexforge_cli-0.3.0/docs/evidence/D12_audit.md +60 -0
- nexforge_cli-0.3.0/docs/evidence/D12_gap.log +12 -0
- nexforge_cli-0.3.0/docs/evidence/D13_break.log +6 -0
- nexforge_cli-0.3.0/docs/evidence/D13_e2e.log +13 -0
- nexforge_cli-0.3.0/docs/evidence/D13_full_test.log +36 -0
- nexforge_cli-0.3.0/docs/evidence/D13_gaps.log +9 -0
- nexforge_cli-0.3.0/docs/evidence/D14_audit.md +64 -0
- nexforge_cli-0.3.0/docs/evidence/D14_full_test.log +24 -0
- nexforge_cli-0.3.0/docs/evidence/D14_recheck.log +29 -0
- nexforge_cli-0.3.0/docs/evidence/D15_audit.md +105 -0
- nexforge_cli-0.3.0/docs/evidence/D4_fast.log +10 -0
- nexforge_cli-0.3.0/docs/evidence/D4_full_test.log +43 -0
- nexforge_cli-0.3.0/docs/evidence/D4_hook.log +21 -0
- nexforge_cli-0.3.0/docs/evidence/D4_scene_c.log +22 -0
- nexforge_cli-0.3.0/docs/evidence/D4_scene_d.log +24 -0
- nexforge_cli-0.3.0/docs/evidence/D4_scene_e.log +10 -0
- nexforge_cli-0.3.0/docs/evidence/D5_audit.md +83 -0
- nexforge_cli-0.3.0/docs/evidence/D5_b2_plugin.log +11 -0
- nexforge_cli-0.3.0/docs/evidence/D5_full_test.log +44 -0
- nexforge_cli-0.3.0/docs/evidence/D6_b3_a.log +10 -0
- nexforge_cli-0.3.0/docs/evidence/D6_b3_b.log +8 -0
- nexforge_cli-0.3.0/docs/evidence/D6_b3_c.log +4 -0
- nexforge_cli-0.3.0/docs/evidence/D6_b4_provider.log +10 -0
- nexforge_cli-0.3.0/docs/evidence/D6_full_test.log +45 -0
- nexforge_cli-0.3.0/docs/evidence/D7_dedup.log +24 -0
- nexforge_cli-0.3.0/docs/evidence/D7_full_test.log +45 -0
- nexforge_cli-0.3.0/docs/evidence/D8_audit.md +93 -0
- nexforge_cli-0.3.0/docs/evidence/D8_invariant.log +22 -0
- nexforge_cli-0.3.0/docs/evidence/D9_after.log +3 -0
- nexforge_cli-0.3.0/docs/evidence/D9_before.log +22 -0
- nexforge_cli-0.3.0/docs/evidence/D9_decision.md +62 -0
- nexforge_cli-0.3.0/docs/evidence/D9_full_test.log +45 -0
- nexforge_cli-0.3.0/docs/evidence/D9_rebuild.log +16 -0
- nexforge_cli-0.3.0/docs/evidence/E_cap_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/E_capabilities.log +12 -0
- nexforge_cli-0.3.0/docs/evidence/E_engines.log +19 -0
- nexforge_cli-0.3.0/docs/evidence/E_engines_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/E_tts_availability.log +12 -0
- nexforge_cli-0.3.0/docs/evidence/E_tts_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/E_tts_installer.log +9 -0
- nexforge_cli-0.3.0/docs/evidence/E_vision_channels.log +14 -0
- nexforge_cli-0.3.0/docs/evidence/E_vision_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/F_decisions_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/deps_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/deps_reverse.log +13 -0
- nexforge_cli-0.3.0/docs/evidence/exit-cost.log +9 -0
- nexforge_cli-0.3.0/docs/evidence/final_check_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/final_check_pypi.log +11 -0
- nexforge_cli-0.3.0/docs/evidence/final_check_report.log +22 -0
- nexforge_cli-0.3.0/docs/evidence/fix_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/health_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/health_report.md +93 -0
- nexforge_cli-0.3.0/docs/evidence/health_scan.log +113 -0
- nexforge_cli-0.3.0/docs/evidence/model-sidebar-dot.log +54 -0
- nexforge_cli-0.3.0/docs/evidence/model_rename_check.log +52 -0
- nexforge_cli-0.3.0/docs/evidence/provider-family-check.log +42 -0
- nexforge_cli-0.3.0/docs/evidence/release_0218_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/release_0218_pypi.log +9 -0
- nexforge_cli-0.3.0/docs/evidence/simplify_tier_fulltest.log +7 -0
- nexforge_cli-0.3.0/docs/evidence/terminal-leak-conditions.log +11 -0
- nexforge_cli-0.3.0/docs/session-invariant.md +103 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/01-/345/256/211/350/243/205/344/270/216/351/205/215/347/275/256.md +1 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/02-/347/225/214/351/235/242/344/270/216/345/277/253/346/215/267/351/224/256.md +3 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/03-/346/250/241/345/236/213/351/205/215/347/275/256.md +43 -24
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/05-/346/225/260/346/215/256/344/270/216/346/220/234/347/264/242.md +2 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/07-/344/274/232/350/257/235/344/270/216/350/256/260/345/277/206.md +4 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/08-/350/207/252/345/256/232/344/271/211/351/205/215/347/275/256.md +1 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/09-/345/270/270/350/247/201/351/227/256/351/242/230.md +33 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/10-/346/217/222/344/273/266/345/274/200/345/217/221.md +6 -1
- nexforge_cli-0.3.0/manual/11-/345/274/200/345/217/221/346/240/241/351/252/214.md +112 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/README.md +3 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual//351/231/204/345/275/225-/345/257/271/346/257/224/345/210/206/346/236/220.md +1 -1
- nexforge_cli-0.3.0/plugin_speech_summary/README.md +68 -0
- nexforge_cli-0.3.0/plugin_speech_summary/install_plugin.py +109 -0
- nexforge_cli-0.3.0/plugin_speech_summary/speech_summary.py +374 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/pyproject.toml +1 -1
- nexforge_cli-0.3.0/scripts/__init__.py +4 -0
- nexforge_cli-0.3.0/scripts/check.py +139 -0
- nexforge_cli-0.3.0/scripts/exit_cost_probe.py +76 -0
- nexforge_cli-0.3.0/scripts/exit_smoke.py +113 -0
- nexforge_cli-0.3.0/scripts/gates/__init__.py +35 -0
- nexforge_cli-0.3.0/scripts/gates/base.py +49 -0
- nexforge_cli-0.3.0/scripts/gates/hygiene.py +235 -0
- nexforge_cli-0.3.0/scripts/gates/manual_consistency.py +107 -0
- nexforge_cli-0.3.0/scripts/gates/registry_consistency.py +184 -0
- nexforge_cli-0.3.0/scripts/gates/version_sync.py +100 -0
- nexforge_cli-0.3.0/scripts/install_hooks.py +159 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/scripts/publish.sh +41 -36
- nexforge_cli-0.3.0/scripts/smoke_tui.py +102 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/__init__.py +1 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/app.py +41 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/cli.py +38 -5
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/config.py +239 -76
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/agent.py +24 -6
- nexforge_cli-0.3.0/src/nexforge/core/capability.py +90 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/federation.py +11 -6
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/plugin.py +60 -7
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/session.py +8 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/auto_skills.py +6 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/federation_feature.py +11 -3
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/session_feature.py +26 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/i18n.py +4 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/anthropic.py +3 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/deepseek.py +32 -6
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/openai.py +9 -3
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/qianwen.py +7 -2
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/store/session_store.py +209 -19
- nexforge_cli-0.3.0/src/nexforge/terminal_guard.py +281 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/image_tools.py +155 -52
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/screenshot_tools.py +4 -1
- nexforge_cli-0.3.0/src/nexforge/tools/search.py +325 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/screens/chat.py +42 -26
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/slash_handler.py +24 -3
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/file_browser.py +27 -13
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/model_sidebar.py +87 -25
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/plan_sidebar.py +2 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/sidebar.py +2 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/statusbar.py +1 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/web/server.py +1 -1
- nexforge_cli-0.3.0/tests/test_archive_integrity.py +176 -0
- nexforge_cli-0.3.0/tests/test_capability.py +223 -0
- nexforge_cli-0.3.0/tests/test_check_gates.py +361 -0
- nexforge_cli-0.3.0/tests/test_config.py +353 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_deepseek_provider.py +4 -5
- nexforge_cli-0.3.0/tests/test_deps.py +101 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_director_loop.py +173 -2
- nexforge_cli-0.3.0/tests/test_exit_smoke.py +121 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_federation.py +47 -1
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_file_browser.py +57 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_goal_confirm.py +77 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_manual_consistency.py +36 -2
- nexforge_cli-0.3.0/tests/test_model_sidebar_dot.py +254 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_plugin.py +69 -0
- nexforge_cli-0.3.0/tests/test_plugin_example_tts.py +144 -0
- nexforge_cli-0.3.0/tests/test_provider_errors.py +90 -0
- nexforge_cli-0.3.0/tests/test_quit_paths.py +109 -0
- nexforge_cli-0.3.0/tests/test_retry_policy.py +180 -0
- nexforge_cli-0.3.0/tests/test_session_invariant.py +158 -0
- nexforge_cli-0.3.0/tests/test_session_save_safety.py +146 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_session_store.py +172 -0
- nexforge_cli-0.3.0/tests/test_sidebar_align.py +39 -0
- nexforge_cli-0.3.0/tests/test_terminal_guard.py +326 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_tui.py +56 -50
- nexforge_cli-0.3.0/tests/test_vision_capability.py +86 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_vision_model.py +40 -11
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_vision_tool_retention.py +37 -2
- nexforge_cli-0.2.17/scripts/smoke_tui.py +0 -45
- nexforge_cli-0.2.17/src/nexforge/core/router.py +0 -62
- nexforge_cli-0.2.17/src/nexforge/tools/search.py +0 -210
- nexforge_cli-0.2.17/tests/test_config.py +0 -117
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/INSTALL.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/STATS_API.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/agent.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/cost.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/edit.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/err.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/file.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/model.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/ok.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/read.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/running.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/search.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/shell.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/thinking.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/web.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/assets/icons/write.svg +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/devlog/2026-07-07.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/doc//344/273/243/347/240/201/344/274/230/345/214/226/350/256/241/345/210/222.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/doc//347/211/271/346/200/247/346/250/241/345/235/227/346/263/250/345/206/214/345/210/266-/345/256/236/346/226/275/350/256/241/345/210/222.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/doc//351/241/271/347/233/256/346/200/273/347/233/221/345/276/252/347/216/257-/345/274/200/345/217/221/350/256/241/345/210/222.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/docs/submission/README-/346/212/225/347/250/277/350/257/264/346/230/216.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/docs/submission/nexforge.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/docs/submission/nexforge.zh-CN.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/04-/345/267/245/345/205/267/345/217/202/350/200/203.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/manual/06-/346/235/203/351/231/220/344/270/216/346/250/241/345/274/217.md" +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/README.md +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/action.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/base.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/vision.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/guiauto/window.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/install_plugin.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/requirements.txt +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/smoke_safe.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/verify_final.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/verify_host.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/plugin_gui_auto/verify_plugin.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/scripts/smoke_provider.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/__main__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/context.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/goal_manager.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/jobs.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/knowledge_base.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/memory_engine.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/model_catalog.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/permissions.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/project_mapper.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/provider.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/summarizer.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/system_inject.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/tool_manifest.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/types.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/core/update_check.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/base.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/kb_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/map_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/memory_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/mode_features.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/plan_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/registry.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/slash_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/stats_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/features/tool_manifest_feature.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/icons.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/mcp/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/_retry.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/provider/balance.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/skills/loader.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/stats/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/stats/heartbeat.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/store/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/store/director_store.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/store/plan_store.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/theme.tcss +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/_paths.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/background.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/base.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/data_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/edit_file.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/federation.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/fs_ops.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/git.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/kb_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/list_dir.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/multi_edit.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/ops_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/read_file.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/registry.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/search_content.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/search_files.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/shell.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/web.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/tools/write_file.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/clipboard_image.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/screen_api.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/screens/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/screens/chat_actions.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/screens/splash.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/apikey_modal.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/banner.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/confirm_modal.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/diff_modal.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/file_menu.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/image_sidebar.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/messages.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/mode_warning.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/multiline_modal.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/ui/widgets/multiline_prompt.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/utils/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/utils/logging.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/web/__init__.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/src/nexforge/web/static/index.html +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/conftest.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_app_mouse.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_approval.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_cross_platform.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_director_store.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_dual_panel.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_i18n.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_icons.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_lang_cmd.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_m2_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_model_catalog.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_p2_core.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_project_map_prune.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_sanitize.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_screenshot_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_session_repair.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_sidebar.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_sidebar_actions.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_splash_modal.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_statusbar.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_system_inject.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_tool_iters.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_tools.py +0 -0
- {nexforge_cli-0.2.17 → nexforge_cli-0.3.0}/tests/test_update_check.py +0 -0
|
@@ -1,30 +1,38 @@
|
|
|
1
|
-
# Python
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.py[cod]
|
|
4
|
-
*.egg-info/
|
|
5
|
-
build/
|
|
6
|
-
dist/
|
|
7
|
-
.eggs/
|
|
8
|
-
|
|
9
|
-
# venv
|
|
10
|
-
.venv/
|
|
11
|
-
venv/
|
|
12
|
-
env/
|
|
13
|
-
|
|
14
|
-
# 测试 / 缓存
|
|
15
|
-
.pytest_cache/
|
|
16
|
-
.mypy_cache/
|
|
17
|
-
.ruff_cache/
|
|
18
|
-
|
|
19
|
-
# 会话与本地配置(不入库)
|
|
20
|
-
.nexforge/
|
|
21
|
-
*.jsonl
|
|
22
|
-
|
|
23
|
-
# 生成的图标位图
|
|
24
|
-
assets/icons/*.png
|
|
25
|
-
|
|
26
|
-
# IDE / OS
|
|
27
|
-
.idea/
|
|
28
|
-
.vscode/
|
|
29
|
-
.DS_Store
|
|
30
|
-
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# venv
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# 测试 / 缓存
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
|
|
19
|
+
# 会话与本地配置(不入库)
|
|
20
|
+
.nexforge/
|
|
21
|
+
*.jsonl
|
|
22
|
+
|
|
23
|
+
# 生成的图标位图
|
|
24
|
+
assets/icons/*.png
|
|
25
|
+
|
|
26
|
+
# IDE / OS
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
29
|
+
.DS_Store
|
|
30
|
+
|
|
31
|
+
# 凭据 / 恢复码(永不入库)
|
|
32
|
+
PyPI-*.txt
|
|
33
|
+
*recovery-code*
|
|
34
|
+
*recovery_code*
|
|
35
|
+
credentials*.env
|
|
36
|
+
*.pem
|
|
37
|
+
id_rsa*
|
|
38
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nexforge-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: NexForgeCLI — 一个面向 DeepSeek V4 的终端智能体(TUI + CLI 双模式)
|
|
5
5
|
Project-URL: Homepage, https://gitee.com/Lighthorn/nexforgecli
|
|
6
6
|
Author: NexForge
|
|
@@ -51,7 +51,14 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
|
|
52
52
|
NexForgeCLI 是一个运行在终端里的智能体助手,既支持 DeepSeek/千问等多模型,也是**插件宿主**(plugins 目录即插即用)。既能作为全屏 TUI 交互式对话/编码,也能作为 one-shot 命令行工具嵌进脚本和管道。
|
|
53
53
|
|
|
54
|
-
> 当前版本:**v0.
|
|
54
|
+
> 当前版本:**v0.3.0** · PyPI: `pip install nexforge-cli`
|
|
55
|
+
|
|
56
|
+
**v0.3.0 要点**(**含破坏性变更**):档位简化为单档 `flash`——`/model pro`、`/model auto`、
|
|
57
|
+
`/model vision` 不再可用(`pro` 档与 `vision` 档模型均已下线;`deepseek-flash` 原生多模态,
|
|
58
|
+
文本与读图一档搞定);旧配置里的档位值会自动迁移为 `flash` 并给出可见提醒。
|
|
59
|
+
另含:终端状态兜底恢复 + 退出看门狗(进程被硬中断时不再留下被鼠标序列刷屏的终端)、
|
|
60
|
+
模型侧边栏绿点恒为一个(只标当前在用的模型)、历史归档不再静默丢批次、
|
|
61
|
+
两层重试放大修复(429 时由最多 12 次请求降为 `1 + max_retries` 次)。
|
|
55
62
|
|
|
56
63
|
---
|
|
57
64
|
|
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
NexForgeCLI 是一个运行在终端里的智能体助手,既支持 DeepSeek/千问等多模型,也是**插件宿主**(plugins 目录即插即用)。既能作为全屏 TUI 交互式对话/编码,也能作为 one-shot 命令行工具嵌进脚本和管道。
|
|
10
10
|
|
|
11
|
-
> 当前版本:**v0.
|
|
11
|
+
> 当前版本:**v0.3.0** · PyPI: `pip install nexforge-cli`
|
|
12
|
+
|
|
13
|
+
**v0.3.0 要点**(**含破坏性变更**):档位简化为单档 `flash`——`/model pro`、`/model auto`、
|
|
14
|
+
`/model vision` 不再可用(`pro` 档与 `vision` 档模型均已下线;`deepseek-flash` 原生多模态,
|
|
15
|
+
文本与读图一档搞定);旧配置里的档位值会自动迁移为 `flash` 并给出可见提醒。
|
|
16
|
+
另含:终端状态兜底恢复 + 退出看门狗(进程被硬中断时不再留下被鼠标序列刷屏的终端)、
|
|
17
|
+
模型侧边栏绿点恒为一个(只标当前在用的模型)、历史归档不再静默丢批次、
|
|
18
|
+
两层重试放大修复(429 时由最多 12 次请求降为 `1 + max_retries` 次)。
|
|
12
19
|
|
|
13
20
|
---
|
|
14
21
|
|
|
@@ -1,42 +1,30 @@
|
|
|
1
|
-
# NexForgeCLI 配置示例。
|
|
2
|
-
# 复制到 ~/.nexforge/config.toml 后按需修改;所有字段都有内置默认值,可只写要覆盖的项。
|
|
3
|
-
# DeepSeek V4 参数已核实自 api-docs.deepseek.com(2026-04)。
|
|
4
|
-
|
|
5
|
-
[provider]
|
|
6
|
-
base_url = "https://api.deepseek.com" # OpenAI 格式;Anthropic 格式加 /anthropic
|
|
7
|
-
api_key_env = "DEEPSEEK_API_KEY" # 优先从此环境变量读 key
|
|
8
|
-
# api_key = "sk-..." # 次选:直接写这里(不建议入库)
|
|
9
|
-
timeout = 120.0
|
|
10
|
-
max_retries = 2
|
|
11
|
-
|
|
12
|
-
[models]
|
|
13
|
-
flash = "deepseek-v4-flash
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
[router] # auto 档位的客户端路由
|
|
32
|
-
entry = "flash" # auto 默认从 flash 起步
|
|
33
|
-
escalate_to = "pro"
|
|
34
|
-
escalate_on_tool_failures = 2
|
|
35
|
-
escalate_context_tokens = 200000
|
|
36
|
-
|
|
37
|
-
[ui]
|
|
38
|
-
theme = "dark" # dark | light
|
|
39
|
-
show_thinking = true
|
|
40
|
-
icons = "auto" # auto | image | glyph | ascii
|
|
41
|
-
accent = "#6D5EF5"
|
|
42
|
-
accent2 = "#22D3EE"
|
|
1
|
+
# NexForgeCLI 配置示例。
|
|
2
|
+
# 复制到 ~/.nexforge/config.toml 后按需修改;所有字段都有内置默认值,可只写要覆盖的项。
|
|
3
|
+
# DeepSeek V4 参数已核实自 api-docs.deepseek.com(2026-04)。
|
|
4
|
+
|
|
5
|
+
[provider]
|
|
6
|
+
base_url = "https://api.deepseek.com" # OpenAI 格式;Anthropic 格式加 /anthropic
|
|
7
|
+
api_key_env = "DEEPSEEK_API_KEY" # 优先从此环境变量读 key
|
|
8
|
+
# api_key = "sk-..." # 次选:直接写这里(不建议入库)
|
|
9
|
+
timeout = 120.0
|
|
10
|
+
max_retries = 2
|
|
11
|
+
|
|
12
|
+
[models]
|
|
13
|
+
flash = "deepseek-flash" # 原生多模态(文本+读图), 取代原 v4-flash + vision 双档位
|
|
14
|
+
default_tier = "flash" # 档位只剩 flash(pro/auto/vision 均已移除)
|
|
15
|
+
thinking = true # 思考模式,默认开
|
|
16
|
+
reasoning_effort = "high" # high | max
|
|
17
|
+
max_output_tokens = 8192 # 上限 384K
|
|
18
|
+
|
|
19
|
+
# 定价(每百万 token,USD)—— 已核实
|
|
20
|
+
[models.pricing.flash]
|
|
21
|
+
input_cache_hit = 0.0028
|
|
22
|
+
input_cache_miss = 0.14
|
|
23
|
+
output = 0.28
|
|
24
|
+
|
|
25
|
+
[ui]
|
|
26
|
+
theme = "dark" # dark | light
|
|
27
|
+
show_thinking = true
|
|
28
|
+
icons = "auto" # auto | image | glyph | ascii
|
|
29
|
+
accent = "#6D5EF5"
|
|
30
|
+
accent2 = "#22D3EE"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 决策:Fails Loud 分级(危险不得降级为警告)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
项目的病根是"静默失败 / 危险降级":
|
|
8
|
+
|
|
9
|
+
- 插件目录消失 → **一声不吭**(用户遇到"语音播报凭空没了",完全无从排查)
|
|
10
|
+
- 配置写错字段名 → Pydantic 默认忽略 → **静默无效果**
|
|
11
|
+
- `config.py` 有 4 处 `except Exception: pass` → **吞掉校验错误**,静默回退默认值
|
|
12
|
+
|
|
13
|
+
更具讽刺意味的是:修复过程中,`hygiene` gate 自己把"敏感凭据躺在工作区"降级成**不拦人的提示**——
|
|
14
|
+
gate 自己干了它要根治的事。
|
|
15
|
+
|
|
16
|
+
## 决策
|
|
17
|
+
|
|
18
|
+
1. **危险项默认失败**:敏感文件、残留目录、配置校验失败 → `[FAIL]` + 非 0 退出。
|
|
19
|
+
2. **例外只能走显式 allowlist**(`.hygiene-allow`),且**每次放行必须在输出中可见**。
|
|
20
|
+
3. **禁止"默认静默降级为警告"的路径**。
|
|
21
|
+
4. **能力缺失时**:要么报错(含修复指引),要么显式声明"降级/跳过"并可见。
|
|
22
|
+
5. **容错可以保留,但必须留痕**:如配置读失败 → 保留原文件 + 明确警告(而非覆盖)。
|
|
23
|
+
|
|
24
|
+
## 理由
|
|
25
|
+
|
|
26
|
+
- 危险与错误**必须可见**,且**不允许被降级为提示**——降级等于没检查。
|
|
27
|
+
- **否决"敏感文件只警告"**:那正是要根治的"看见了风险却放行"。
|
|
28
|
+
- **否决"用空 dict 覆盖损坏的配置"**:会丢失用户手写的注释与字段(数据丢失 > 启动失败)。
|
|
29
|
+
- **否决"能力缺失静默跳过"**:用户无法区分"没配"与"坏了"。
|
|
30
|
+
|
|
31
|
+
## 代价与边界
|
|
32
|
+
|
|
33
|
+
- 失败必须**可定位**(文件:行、期望 vs 实际),否则等于没报。
|
|
34
|
+
- 分级要克制:只有"用户期待的功能失败"才必须可见;清理/关闭类失败保持静默是合理的
|
|
35
|
+
(源码中仍有约 101 处 `except: pass`,经分类后判定多为合理防御)。
|
|
36
|
+
- 单点探测异常不得中断整体(如能力列举、插件加载)。
|
|
37
|
+
|
|
38
|
+
## 关联
|
|
39
|
+
|
|
40
|
+
`scripts/gates/hygiene.py`、`config.py`(ConfigIssue)、`core/plugin.py`、`core/capability.py`、
|
|
41
|
+
`plugin_speech_summary/`、`docs/session-invariant.md`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 决策:会话不变量(model-visible ⟺ logged)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
> 详规见 [`docs/session-invariant.md`](../session-invariant.md)
|
|
5
|
+
|
|
6
|
+
## 背景
|
|
7
|
+
|
|
8
|
+
"恢复/切换会话后行为异常""输入丢失/错位"是历史高频抱怨,但一直说不清根因。
|
|
9
|
+
D8 审计用真实探针把它钉成了铁证:**构造含工具链的会话,`save → load` 后 5 条只剩 2 条**——
|
|
10
|
+
工具调用、工具结果、思考内容、注入的上下文**全部丢失**。
|
|
11
|
+
|
|
12
|
+
即:**模型看到的历史 ≠ 磁盘记录的历史**。
|
|
13
|
+
|
|
14
|
+
## 决策
|
|
15
|
+
|
|
16
|
+
1. 确立不变量:**凡是进入模型请求的内容,必须能由落盘数据重建**;
|
|
17
|
+
不能重建的,必须显式声明为"运行时派生"并给出补偿方式。**不允许无声地丢。**
|
|
18
|
+
2. 内容分三类:**P1 原始事实**(必须可逐项重建)、**P2 外部状态快照**(必须留痕)、
|
|
19
|
+
**P3 运行时派生**(不落盘,但须显式标记 + 恢复时补偿)。
|
|
20
|
+
判据核心:**事后能否重算**。
|
|
21
|
+
3. 落盘引入**结构化标记** `<!-- nf-msg <base64 JSON> -->` 作为**重建的唯一权威**;
|
|
22
|
+
人读 Markdown 保持不变(截断只影响展示,不影响重建)。
|
|
23
|
+
4. 注入的上下文(记忆/地图/知识库/工具清单)另存 `<id>.ctx.md` 快照留痕。
|
|
24
|
+
|
|
25
|
+
## 理由
|
|
26
|
+
|
|
27
|
+
- **否决"增强人读 Markdown 的解析"**:内容里出现 `### ❯ 用户`、`## ` 等会破坏解析
|
|
28
|
+
(实测已踩:注入内容里的 `## 标题` 被误判为轮次边界)→ 标记法无此歧义。
|
|
29
|
+
- **否决"抽独立 JSONL 存储"**:两套文件需同步,且旧会话迁移复杂;内嵌标记向后兼容(无标记即回退人读解析)。
|
|
30
|
+
- **否决"把注入内容混进会话消息"**:它们源自会变的项目文件,混入会让会话文件成倍膨胀且语义不清。
|
|
31
|
+
|
|
32
|
+
## 代价与边界
|
|
33
|
+
|
|
34
|
+
- 文件体积约 2 倍(人读部分 + 标记)。
|
|
35
|
+
- 标记是"机器真相",人读部分是"展示"——**二者不一致时以标记为准**(规则写死)。
|
|
36
|
+
- 派生项(如 `DEFAULT_SYSTEM`)**不进会话文件**,靠恢复时重放补偿;其定义处必须有显式标记说明。
|
|
37
|
+
|
|
38
|
+
## 关联
|
|
39
|
+
|
|
40
|
+
`store/session_store.py`、`core/session.py`、`ui/screens/chat.py`、`tests/test_session_invariant.py`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 决策:依赖一致性(只用已声明的依赖)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
健康体检时发现一个**真实可用性缺陷**:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
tools/image_tools.py: import requests ← 3 处
|
|
11
|
+
pyproject.toml 依赖: httpx, openai, pydantic, ...
|
|
12
|
+
↑ 没有 requests
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**本机恰好装了 `requests`**(所以测试全绿),但**全新用户 `pip install nexforge-cli` 后**,
|
|
16
|
+
识图/生图会因 `ImportError` 被 `except` 吞掉 → **静默失败**。
|
|
17
|
+
|
|
18
|
+
这既是一个可用性缺陷,也是"静默失败"病根的又一个实例。
|
|
19
|
+
|
|
20
|
+
## 决策
|
|
21
|
+
|
|
22
|
+
1. HTTP 客户端**统一为 httpx**(`pyproject` 已声明),全仓库不再使用 `requests`。
|
|
23
|
+
2. 新增 `tests/test_deps.py` 三条防线:
|
|
24
|
+
- **未声明的第三方 import**(AST 扫描全仓库,与 pyproject 声明比对)
|
|
25
|
+
- `image_tools` 具体回归(不得用 requests)
|
|
26
|
+
- HTTP 客户端一致性(全仓库不得 `import requests`)
|
|
27
|
+
3. 新增第三方依赖时,必须**同时**更新 `pyproject.toml`,否则测试会失败。
|
|
28
|
+
|
|
29
|
+
## 理由
|
|
30
|
+
|
|
31
|
+
- **本机环境 ≠ 用户环境**:依赖缺失类缺陷在本机不可见,只有"声明一致性检查"能提前发现。
|
|
32
|
+
- **否决"把 requests 加进依赖"**:项目已有 httpx,再加一个等价 HTTP 客户端徒增体积与维护面;
|
|
33
|
+
统一为 httpx 还能让超时/重试策略一致。
|
|
34
|
+
- **否决"只在代码里 try/except ImportError 静默降级"**:那会把缺陷藏得更深。
|
|
35
|
+
|
|
36
|
+
## 代价与边界
|
|
37
|
+
|
|
38
|
+
- httpx 与 requests 的 API 相近但不完全相同(本项目用到的 `post/get/.json()/.text/.status_code` 均一致)。
|
|
39
|
+
- 检查基于**顶层 import 的模块名**;通过 `importlib.import_module("字符串")` 的动态导入不在覆盖范围。
|
|
40
|
+
- 插件(`~/.nexforge/plugins/`)不受此检查约束——它们的额外依赖由插件的安装器负责(如
|
|
41
|
+
`plugin_gui_auto/requirements.txt`)。
|
|
42
|
+
|
|
43
|
+
## 关联
|
|
44
|
+
|
|
45
|
+
`tests/test_deps.py`、`src/nexforge/tools/image_tools.py`、`pyproject.toml`、
|
|
46
|
+
`docs/evidence/health_report.md`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 决策:D(回放测试)范围重定义
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:**生效**
|
|
4
|
+
> 关联:D12 侦察(`docs/evidence/D12_audit.md`)、C 工程(`docs/session-invariant.md`)
|
|
5
|
+
> 说明:本文同时作为 **F(决策留痕)的首条记录**,确立 `docs/decisions/` 作为决策留痕位置。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 背景
|
|
10
|
+
|
|
11
|
+
D 工程原定目标:**"把目标模式四轮审查暴露的 12 个 bug 固化成回归网"**。
|
|
12
|
+
|
|
13
|
+
## 侦察结论(D12)
|
|
14
|
+
|
|
15
|
+
从 `.nexforge/kb/` 四份审查记录重建出 **11 条** bug(总监所述 12 条,差 1 条来源无法确认,未凑数):
|
|
16
|
+
|
|
17
|
+
| 覆盖状态 | 条数 | 说明 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| 早已有既有测试 | 7 | 远早于 C 工程 |
|
|
20
|
+
| 由 C4 吸收 | 1 | 会话序列完整性(`test_session_invariant.py`)|
|
|
21
|
+
| 代码已删 | 1 | 无需回归 |
|
|
22
|
+
| **未覆盖** | 1 | #9 Esc 忙态判断 |
|
|
23
|
+
| **部分覆盖** | 1 | #10 停止后迟到回复 |
|
|
24
|
+
|
|
25
|
+
→ **原目标实际已完成**;再建回放网属于重复劳动(收敛原则点名的"反复磨同一模块、无新增价值")。
|
|
26
|
+
|
|
27
|
+
## 决策
|
|
28
|
+
|
|
29
|
+
1. **原 D 目标记为"已由既有测试 + C 工程吸收",不再单独建网。**
|
|
30
|
+
2. **D 重定义为"最小端到端回放"**:仅一条链路(提目标 → 出方案 → 确认 → 分解 → 执行 → 验收 → 停止),
|
|
31
|
+
在**每一步之后**校验会话不变量。独立价值在于覆盖 **D9/D10 才引入的重建链**,而非重测单点。
|
|
32
|
+
3. **刻意不建"框架"**:一条链路 + 复用既有夹具,避免为测试而测试。
|
|
33
|
+
4. **补齐仅存真缺口**:#9 / #10(D13 任务 1 已完成,含对照用例)。
|
|
34
|
+
|
|
35
|
+
## 理由
|
|
36
|
+
|
|
37
|
+
- **收敛原则**:反复磨同一模块、无新增价值即停。
|
|
38
|
+
- **侦察前置的收益**:避免约 2 天的重复劳动。这与 D8→D10 的教训同源——**先查证,再动工**。
|
|
39
|
+
|
|
40
|
+
## 影响
|
|
41
|
+
|
|
42
|
+
| 项 | 结果 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| D 工程 | **完成(重定义后)** |
|
|
45
|
+
| F 工程 | 本文为首条决策记录,确立 `docs/decisions/` 位置 |
|
|
46
|
+
| 会话不变量 | 端到端回放成为其**跨组件验证入口**(`docs/session-invariant.md` §7)|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 决策:审计以调用链为准(覆盖性结论须逐点附证)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
C1 审计(D8)产出了"模型可见输入点 14 处、类型 A 共 7 处未落盘"的结论。后续实践中发现**其中两处是错的**:
|
|
8
|
+
|
|
9
|
+
- 判"总监会话不落盘" → 实际 `chat.py:908` 早已每轮落盘(我只顺着内存对象查,没搜落盘调用链)
|
|
10
|
+
- 判"插话完全不记录" → 实际已随总监会话落盘(`chat.py:897` `add_user`)
|
|
11
|
+
|
|
12
|
+
同期还出现一次**方法错误**:用 `grep` 统计"裸 `except: pass`"得到 **0 处**(跨行匹配失效),
|
|
13
|
+
改用 AST 精确扫描后是 **101 处**。
|
|
14
|
+
|
|
15
|
+
## 决策
|
|
16
|
+
|
|
17
|
+
1. **结论以调用链为准**:判断"某内容是否落盘/是否执行"必须搜到实际调用点(`file:line`),
|
|
18
|
+
**不得只看对象形态或凭记忆**。
|
|
19
|
+
2. **凡下"共 N 处"这类覆盖性结论,每一点都须附证据**(调用链或工具输出)。
|
|
20
|
+
3. **工具给出的结论,必须先验证工具本身是否可靠**(grep 会说谎,AST 更可信)。
|
|
21
|
+
4. 清单**无法完整重建时如实声明缺项**,不得凑数(如"12 个 bug"实查为 11 条,差额来源无法确认)。
|
|
22
|
+
|
|
23
|
+
## 理由
|
|
24
|
+
|
|
25
|
+
- 覆盖性结论比单点结论**更容易出错**,却更常被当作事实引用——错一处会误导整条决策链
|
|
26
|
+
(D12 的"D 记已吸收"决定,正是建立在"7 条已覆盖"之上;若其中有假绿,决定就不成立)。
|
|
27
|
+
- **否决"凭记忆/凭印象下结论"**:本项目的记忆已多次被证伪。
|
|
28
|
+
|
|
29
|
+
## 代价与边界
|
|
30
|
+
|
|
31
|
+
- 审计更慢(要搜调用链、要写脚本实证)——但与"带着错误结论改错方向"相比,这个成本极低。
|
|
32
|
+
- 不要求每个细节都附证;**要求的是"覆盖性/否定性结论"必须有证据**。
|
|
33
|
+
- 实证脚本应落盘(`docs/evidence/`),便于复核。
|
|
34
|
+
|
|
35
|
+
## 关联
|
|
36
|
+
|
|
37
|
+
`docs/evidence/D8_audit.md`(含被后续修正的结论)、`D10`/`D14` 报告、
|
|
38
|
+
`scripts/gates/*`(gate 本身也遵守"失败必须可定位")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 决策:能力可用性上报(Capability 协议 + 注册制)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
E 工程审计发现:**多实现/可选能力**(识图、语音、搜索)在缺失时表现不一——
|
|
8
|
+
|
|
9
|
+
- 识图:**通道级静默**(单通道不可用不吭声,要等两路全废才报错)
|
|
10
|
+
- 语音:**全程静默**(引擎不可用/缺 SDK/非 Windows 一律无声)
|
|
11
|
+
- provider:**已达标**(注册制 + 缺什么报什么),可直接当模板
|
|
12
|
+
|
|
13
|
+
用户无法回答"这个功能现在能不能用、为什么不能用"。
|
|
14
|
+
|
|
15
|
+
## 决策
|
|
16
|
+
|
|
17
|
+
1. 定义统一契约 `Capability`:`name` / `title` / `available()` / `unavailable_reason()`。
|
|
18
|
+
2. **注册制**:`register_capability(impl)`,与 `provider.register_provider` 同构。
|
|
19
|
+
3. 提供 `get_capability(name)`(未登记返回 `None`,调用方决定报错还是降级)、
|
|
20
|
+
`describe_capabilities()` / `format_capabilities()`。
|
|
21
|
+
4. 新增 `/capabilities` 命令展示各能力状态与原因。
|
|
22
|
+
5. **只登记真正多实现/可替换的能力**(search / vision / tts);单实现无替换需求者
|
|
23
|
+
(kb / map / memory)**不登记**。
|
|
24
|
+
|
|
25
|
+
## 理由
|
|
26
|
+
|
|
27
|
+
- **三性**:可插拔(新增实现不改调用方分支)、可降级(先问 `available()` 再决定)、
|
|
28
|
+
可诊断(一条命令看清"哪个能力为何不可用")。
|
|
29
|
+
- **否决"给所有工具都套一层能力"**:单实现者套上去纯属过度抽象,徒增维护面。
|
|
30
|
+
- **否决"能力探测发真实请求"**:探测必须**只读、无副作用**(不发网络、不写文件),否则启动变慢且会打扰服务。
|
|
31
|
+
- **搜索引擎改为注册制**(原 if/elif 硬编码):新增引擎只需 `register_engine()`;
|
|
32
|
+
各引擎自带"配置就绪检查",被能力上报**复用**(单一真相,不重复实现判据)。
|
|
33
|
+
|
|
34
|
+
## 代价与边界
|
|
35
|
+
|
|
36
|
+
- 能力探测是**配置级**判断("有没有路"),不等于"请求一定成功"——文档已注明。
|
|
37
|
+
- 插件(如语音)登记能力时,需容忍旧版主程序无该模块(`except ImportError`)。
|
|
38
|
+
- 单个能力探测抛异常**不得**中断整体列举(记为该能力不可用 + 异常说明)。
|
|
39
|
+
|
|
40
|
+
## 关联
|
|
41
|
+
|
|
42
|
+
`core/capability.py`、`tools/search.py`、`tools/image_tools.py`、
|
|
43
|
+
`plugin_speech_summary/speech_summary.py`、`ui/slash_handler.py`、`tests/test_capability.py`
|
nexforge_cli-0.3.0/docs/decisions/2026-09-10-/350/264/250/351/207/217gate/344/275/223/347/263/273.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 决策:质量 Gate 体系(机器强制取代人肉提醒)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-10 · 状态:生效
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
此前所有质量约定都靠人记:改完记得跑 pytest、记得同步手册版本号、记得别提交凭据。
|
|
8
|
+
**人一忘,问题就漏**——曾出现版本号漂移、手册与代码脱节、敏感文件躺在工作区无人察觉。
|
|
9
|
+
对比 dsh(DeepSeek Harness)的做法:每条约定都配一个可执行 gate 并在 push 前强制运行。
|
|
10
|
+
|
|
11
|
+
## 决策
|
|
12
|
+
|
|
13
|
+
1. 建立聚合入口 `scripts/check.py`(`--fast` 秒级 / `--full` 含全量测试 / `--only` / `--list`)。
|
|
14
|
+
2. 每个校验项一个模块(`scripts/gates/`),**自动发现,新增无需注册**。
|
|
15
|
+
3. 提供 `scripts/install_hooks.py` 安装 **git 原生 pre-push 钩子**,失败即拦截;保留 `--no-verify` 逃生舱。
|
|
16
|
+
|
|
17
|
+
## 理由
|
|
18
|
+
|
|
19
|
+
- **把"我记得"变成"机器强制"**:纪律只有被执行才叫纪律。
|
|
20
|
+
- **否决了"为 gate 化而重构既有测试"**:`tests/test_manual_consistency.py` 是 212 行已验证的 pytest 用例,
|
|
21
|
+
重构成独立函数风险大于收益 → 改为**子进程包装 + 解析结果**(单一真相,不复制断言逻辑)。
|
|
22
|
+
- **否决引入 lefthook/ruff 等第三方**:项目约定"优先标准库";git 原生 hook + sh 脚本已足够。
|
|
23
|
+
- **否决"改错就静默通过"**:未发现任何 gate 本身也判失败(静默通过是最危险的失败模式)。
|
|
24
|
+
|
|
25
|
+
## 代价与边界
|
|
26
|
+
|
|
27
|
+
- pre-push 会拖慢推送 → fast gate 严格控制在秒级,全量测试只在 `--full` 或 CI。
|
|
28
|
+
- hook 装在本机(`.git/hooks/` 不入库)→ 换机器需重跑安装脚本;README/手册已说明。
|
|
29
|
+
- gate 只覆盖**已知**约定;新增约定需同步新增 gate。
|
|
30
|
+
|
|
31
|
+
## 关联
|
|
32
|
+
|
|
33
|
+
`scripts/check.py`、`scripts/gates/`、`scripts/install_hooks.py`、`manual/11-开发校验.md`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 决策:DeepSeek 档位简化为 flash / vision(移除 pro 与 auto)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-11 · 状态:生效 · **破坏性变更**
|
|
4
|
+
|
|
5
|
+
## 背景
|
|
6
|
+
|
|
7
|
+
DeepSeek 调整模型阵容后,`pro` 档基本无用;而 `auto` 档的唯一作用是"命中升级信号时自动升到 pro"。
|
|
8
|
+
用户要求简化 DS 的模型列表("只保留 flash,包括 pro 在内全部删掉")。
|
|
9
|
+
|
|
10
|
+
同期实测(`docs/evidence/model_rename_check.log`):官方 `GET /models` 只剩 `deepseek-flash` 与 `deepseek-v4-pro`。
|
|
11
|
+
|
|
12
|
+
## 决策
|
|
13
|
+
|
|
14
|
+
1. `Tier` 从 `flash / pro / auto / vision` 简化为 **`flash / vision`**。
|
|
15
|
+
2. 删 `ModelsConfig.pro` 与其计价项。
|
|
16
|
+
3. **删除 `RouterConfig` 与整个 `core/router.py`**(`Router` 类)——
|
|
17
|
+
`agent.py` 改为直接按用户选定档位构造 `RouteDecision`。
|
|
18
|
+
4. `vision` **保留**:识图与贴图直达模型的必需档位。
|
|
19
|
+
|
|
20
|
+
## 理由
|
|
21
|
+
|
|
22
|
+
- **pro 已无用**(用户判断 + 实测支持),保留只会增加配置面与认知负担。
|
|
23
|
+
- **auto 是死逻辑**:它存在的意义是"升级到 pro",没有 pro 就无从升级。
|
|
24
|
+
- **否决"保留 auto 但降级为 flash"**:那等于给一个不存在的选择留按钮,纯误导。
|
|
25
|
+
- **否决"保留 pro 但改指向别的模型"**:用户诉求就是删掉它。
|
|
26
|
+
|
|
27
|
+
## 代价与边界
|
|
28
|
+
|
|
29
|
+
- **破坏性**:`/model pro` 与 `/model auto` 不再可用(会提示未知档位);
|
|
30
|
+
配置文件里残留的 `[router]` 段与 `models.pro` 将被"未知字段"检查提示(见 B 工程)。
|
|
31
|
+
已写入的 `models.default_tier = "pro"/"auto"` 会自动迁移为 `flash` 并告警(见下一条 ADR)。
|
|
32
|
+
- **恢复成本**:若 DeepSeek 未来重启 pro/推理档,需要重新加档位 + 计价,
|
|
33
|
+
并视情况恢复路由机制。
|
|
34
|
+
(数据载体 `RouteDecision` 仍在,但已内联到 `core/agent.py` 并退化为"固定档位"记录;
|
|
35
|
+
`Router` 与 `core/router.py` 已随单档化删除 —— 恢复路由需重写,不是改开关。)
|
|
36
|
+
- `reasoning_effort` / `thinking` 仍由 `[models]` 控制,效果不变——**只是不再自动切换**。
|
|
37
|
+
|
|
38
|
+
## 关联
|
|
39
|
+
|
|
40
|
+
`src/nexforge/config.py`、`src/nexforge/core/agent.py`(`router.py` 已删)、
|
|
41
|
+
`ui/widgets/model_sidebar.py`、`ui/widgets/statusbar.py`、`ui/slash_handler.py`、`cli.py`、`web/server.py`、
|
|
42
|
+
`manual/02`、`manual/03`、`manual/08`、`config.example.toml`
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ADR: 档位简化为单档 flash
|
|
2
|
+
|
|
3
|
+
- 日期: 2026-09-11
|
|
4
|
+
- 状态: 已采纳
|
|
5
|
+
- 类型: **破坏性变更**
|
|
6
|
+
|
|
7
|
+
## 背景
|
|
8
|
+
|
|
9
|
+
DeepSeek 于 2026-09 调整模型阵容:
|
|
10
|
+
|
|
11
|
+
1. `deepseek-v4-pro` 处于更新前的尴尬期,实际可用性差 → 此前(2026-09)已删除 `pro` 与 `auto` 档位。
|
|
12
|
+
2. 当时保留 `vision` 档位(模型 `deepseek-v4-flash-vision-exp`)用于识图。
|
|
13
|
+
3. 现在官方新模型 **`deepseek-flash`** 已**原生多模态**(实测可准确读图),而原 flash 档位模型 `deepseek-v4-flash` **不支持读图**。
|
|
14
|
+
|
|
15
|
+
实测证据(`docs/evidence/model_rename_check.log`):
|
|
16
|
+
|
|
17
|
+
| 模型 | 文本 | 读图 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `deepseek-v4-flash-vision-exp` | OK | 蓝圆 + 红三角 |
|
|
20
|
+
| `deepseek-flash` | OK | 蓝圆、红 |
|
|
21
|
+
| `deepseek-v4-flash` | OK | 空(不支持) |
|
|
22
|
+
|
|
23
|
+
## 决策
|
|
24
|
+
|
|
25
|
+
**档位从 `flash / vision` 简化为单一 `flash`**,且 `flash` 档位模型由 `deepseek-v4-flash` 改为 `deepseek-flash`。
|
|
26
|
+
|
|
27
|
+
具体改动:
|
|
28
|
+
|
|
29
|
+
1. `Tier = Literal["flash"]`;删除 `ModelsConfig.vision` 与 `pricing.vision`。
|
|
30
|
+
2. `models.flash` 默认值 → `deepseek-flash`(原生多模态)。
|
|
31
|
+
3. 删除 F6 侧边栏的档位子按钮渲染与 `ms-tier-*` 分支(只剩单档,子选项失去意义)。
|
|
32
|
+
4. 识图能力探测(`VisionCapability`)与 `_provider_vision_channel` 不再回退 `cfg.models.vision`,改为当前档位模型。
|
|
33
|
+
5. CLI / 斜杠命令 / web 的档位白名单只保留 `flash`。
|
|
34
|
+
|
|
35
|
+
## 理由
|
|
36
|
+
|
|
37
|
+
- **避免静默失败**(核心):若只删 vision 档位而不改 flash 模型名,用户切到 flash 后贴图会因 `deepseek-v4-flash` 不支持读图而**静默失败**——正是本项目持续根治的病(见 ADR-2026-09-10-fails-loud分级)。
|
|
38
|
+
- **消除冗余**:`deepseek-flash` 一档同时覆盖文本与读图,独立视觉档位不再必要。
|
|
39
|
+
- **代价可控**:旧档位名(`pro`/`vision`)写错会被明确拒绝,而非静默降级到错误模型。
|
|
40
|
+
|
|
41
|
+
## 复盘:改名漏改一处判定,贴图静默退化(2026-09-11 第五轮复核)
|
|
42
|
+
|
|
43
|
+
**症状**:默认档位下贴图只发出 `[图片附件: 路径]` **文本占位符**,模型根本看不到图;
|
|
44
|
+
识图工具通道报"模型 deepseek-flash 不支持图片输入"(与实测相反);而 `/capabilities`
|
|
45
|
+
**又报"可用"** —— 三者互相矛盾。
|
|
46
|
+
|
|
47
|
+
**根因**:多模态判定只有两条规则 —— ①模型名含 `"vision"`;②`models.toml` 里该
|
|
48
|
+
provider 显式 `multimodal = true`。而内置 deepseek **没有 pdef**(不在 `providers` 表里),
|
|
49
|
+
新档位模型 `deepseek-flash` 名字里**也没有 "vision"**(旧档位模型
|
|
50
|
+
`deepseek-v4-flash-vision-exp` 靠名字就能过)→ 判定为纯文本。
|
|
51
|
+
`/capabilities` 之所以说"可用",是因为它的判据里有个 `or cfg.model_id()`,而
|
|
52
|
+
`model_id()` 恒非空 —— 于是"上报与实际"分叉。
|
|
53
|
+
|
|
54
|
+
**修法**:
|
|
55
|
+
1. `provider/deepseek.py` 增加显式名单 `NATIVE_MULTIMODAL_MODELS = {"deepseek-flash"}`
|
|
56
|
+
(与既有 `"vision"` 名字规则并列);
|
|
57
|
+
2. 抽出 `image_tools.vision_channel(cfg)` 作为**唯一**判定,供贴图通道、识图工具
|
|
58
|
+
与 `VisionCapability` 三处共用(消除"上报说可用、实际跑不通")。
|
|
59
|
+
|
|
60
|
+
**为什么不用"配置开关"代替名单**:若让 `models.flash` 一律视为多模态,用户把它改成
|
|
61
|
+
纯文本模型(如 `deepseek-v4-flash`)时会被错误放行 → 又把图片发给看不懂的模型。
|
|
62
|
+
名单式规则与既有 `"vision"` 规则同构,代价小。
|
|
63
|
+
|
|
64
|
+
**维护纪律**:**DeepSeek 再次改名时,`NATIVE_MULTIMODAL_MODELS` 必须与本文同步更新。**
|
|
65
|
+
这也解释了为什么"档位模型名"不是一个纯配置项 —— 它同时是多模态判定的输入。
|
|
66
|
+
|
|
67
|
+
## 代价与边界
|
|
68
|
+
|
|
69
|
+
- **破坏性**:`/model pro`、`/model auto`、`/model vision` 不再可用。
|
|
70
|
+
- 用户 `config.toml` 若残留 `models.vision` 或 `[models.pricing.vision]`,启动时会有"未知字段"提示(这是 B 工程配置校验在提醒),删掉即可。
|
|
71
|
+
- **升级残留的档位值会自动迁移**(2026-09-11 三轮复核补充):旧版本的 `/model pro`
|
|
72
|
+
会把 `default_tier = "pro"` **写进 config.toml**(`slash_handler` 的持久化),
|
|
73
|
+
若不处理,升级后会在校验阶段直接中止启动(exit 2)——用户被挡在门外、只能手改 TOML。
|
|
74
|
+
现按既有 `mode: review → supervised` 的迁移先例处理:`pro`/`auto`/`vision` 三个**已知被移除**
|
|
75
|
+
的档位名 → 自动改为 `flash` 并**发出可见告警**;真正的拼写错误(如 `flsh`)仍照常报错中止。
|
|
76
|
+
实现在 `config.py:_migrate_removed_tier()`(用户级与项目级两条路径都覆盖)。
|
|
77
|
+
- `supports_model_multimodal` 仍保留 `"vision" in 模型名` 判定——用于**兼容外部显式传入的 vision 系模型名**(如用户自挂的 exp 模型),不属档位概念。
|
|
78
|
+
|
|
79
|
+
## 备选方案(未采纳)
|
|
80
|
+
|
|
81
|
+
| 方案 | 未采纳原因 |
|
|
82
|
+
|---|---|
|
|
83
|
+
| 保留 vision 档位,仅改 flash 模型名 | 两档模型实际相同(都是多模态),用户需无谓切换 |
|
|
84
|
+
| flash 仍用 `deepseek-v4-flash` | 该模型不支持读图,会引入静默失败 |
|
|
85
|
+
| 用 `deepseek-v4-flash-vision-exp` 作 flash | exp 为实验版、名字带过期语义,非长期选择 |
|
|
86
|
+
|
|
87
|
+
## 相关
|
|
88
|
+
|
|
89
|
+
- ADR-2026-09-11-deepseek档位简化(本轮,pro/auto 删除)
|
|
90
|
+
- ADR-2026-09-10-能力可用性上报(vision 能力探测同源)
|