foxclaw 0.1.2 → 0.1.3

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 (344) hide show
  1. package/index.js +1 -8
  2. package/package.json +1 -1
  3. package/python/.env.example +8 -0
  4. package/python/.github/workflows/ci.yml +47 -2
  5. package/python/CHANGELOG.md +57 -0
  6. package/python/CHANGELOG_CN.md +57 -0
  7. package/python/README.md +11 -2
  8. package/python/README_CN.md +32 -20
  9. package/python/bench/bm25_audit.py +1 -14
  10. package/python/bench/bm25_regression.py +0 -1
  11. package/python/bench/bm25_stress.py +0 -4
  12. package/python/bench/cd_audit.py +0 -23
  13. package/python/bench/chaos.py +6 -8
  14. package/python/bench/e2e_real_models.py +0 -1
  15. package/python/bench/llm_retry_test.py +2 -9
  16. package/python/bench/load_test.py +5 -8
  17. package/python/bench/vector_ivf_upgrade.py +1 -7
  18. package/python/docs/RELEASE.md +4 -3
  19. package/python/docs/RUN_CN.md +2 -2
  20. package/python/evals/governance/cases.py +0 -37
  21. package/python/evals/governance/run_evals.py +5 -37
  22. package/python/examples/governance_middleware_demo.py +1 -6
  23. package/python/examples/langgraph_governance_demo.py +7 -11
  24. package/python/foxclaw/__init__.py +34 -152
  25. package/python/foxclaw/agents/base.py +65 -136
  26. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  27. package/python/foxclaw/agents/impl/finance.py +4 -21
  28. package/python/foxclaw/agents/impl/office.py +7 -24
  29. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  30. package/python/foxclaw/agents/mixins/_context.py +0 -23
  31. package/python/foxclaw/agents/mixins/augment.py +10 -16
  32. package/python/foxclaw/agents/mixins/llm.py +7 -6
  33. package/python/foxclaw/agents/mixins/memory.py +7 -1
  34. package/python/foxclaw/agents/mixins/preference.py +58 -8
  35. package/python/foxclaw/agents/mixins/prompt.py +0 -6
  36. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  37. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  38. package/python/foxclaw/api/__init__.py +18 -77
  39. package/python/foxclaw/api/_state.py +43 -0
  40. package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
  41. package/python/foxclaw/api/middleware.py +5 -28
  42. package/python/foxclaw/api/routes/ahs.py +60 -73
  43. package/python/foxclaw/api/routes/attachments.py +5 -6
  44. package/python/foxclaw/api/routes/browser.py +7 -11
  45. package/python/foxclaw/api/routes/domain.py +18 -26
  46. package/python/foxclaw/api/routes/knowledge.py +3 -21
  47. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  48. package/python/foxclaw/api/routes/memory.py +28 -12
  49. package/python/foxclaw/api/routes/orchestration.py +3 -5
  50. package/python/foxclaw/api/routes/recovery.py +4 -3
  51. package/python/foxclaw/api/routes/review.py +2 -11
  52. package/python/foxclaw/api/routes/search.py +3 -7
  53. package/python/foxclaw/api/routes/system.py +33 -49
  54. package/python/foxclaw/api/routes/task.py +31 -49
  55. package/python/foxclaw/api/routes/ws.py +2 -6
  56. package/python/foxclaw/api/spine.py +1 -4
  57. package/python/foxclaw/browser/action/action_executor.py +1 -5
  58. package/python/foxclaw/browser/action/action_types.py +0 -1
  59. package/python/foxclaw/browser/action/url_policy.py +1 -5
  60. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  61. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  62. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  63. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  64. package/python/foxclaw/browser/service.py +7 -11
  65. package/python/foxclaw/capability/embedding_service.py +4 -6
  66. package/python/foxclaw/channels/__init__.py +9 -11
  67. package/python/foxclaw/channels/discord.py +4 -13
  68. package/python/foxclaw/channels/dispatcher.py +2 -27
  69. package/python/foxclaw/channels/feishu.py +10 -20
  70. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  71. package/python/foxclaw/channels/signing.py +9 -9
  72. package/python/foxclaw/channels/telegram.py +3 -4
  73. package/python/foxclaw/channels/weixin.py +7 -17
  74. package/python/foxclaw/cli.py +42 -13
  75. package/python/foxclaw/cognitive/reasoner.py +9 -10
  76. package/python/foxclaw/cognitive/state.py +18 -24
  77. package/python/foxclaw/core/audit_chain.py +1 -3
  78. package/python/foxclaw/core/audit_sink.py +3 -3
  79. package/python/foxclaw/core/compaction.py +8 -26
  80. package/python/foxclaw/core/config.py +2 -2
  81. package/python/foxclaw/core/confirm_nonce.py +6 -12
  82. package/python/foxclaw/core/envfile.py +2 -2
  83. package/python/foxclaw/core/failure_policy.py +17 -26
  84. package/python/foxclaw/core/gateway.py +6 -10
  85. package/python/foxclaw/core/governance_audit.py +3 -7
  86. package/python/foxclaw/core/idempotency.py +15 -22
  87. package/python/foxclaw/core/injection_guard.py +10 -29
  88. package/python/foxclaw/core/memory_adapter.py +30 -57
  89. package/python/foxclaw/core/metrics.py +4 -5
  90. package/python/foxclaw/core/model_router.py +36 -148
  91. package/python/foxclaw/core/paths.py +1 -1
  92. package/python/foxclaw/core/recovery.py +21 -34
  93. package/python/foxclaw/core/scheduler.py +23 -26
  94. package/python/foxclaw/core/secrets.py +0 -3
  95. package/python/foxclaw/core/sessions.py +4 -6
  96. package/python/foxclaw/core/settings.py +16 -32
  97. package/python/foxclaw/core/shared_write.py +11 -23
  98. package/python/foxclaw/core/steering_queue.py +12 -11
  99. package/python/foxclaw/core/system_bus.py +1 -1
  100. package/python/foxclaw/core/thought_chain.py +30 -32
  101. package/python/foxclaw/core/token_meter.py +9 -13
  102. package/python/foxclaw/core/tools.py +4 -12
  103. package/python/foxclaw/core/tracing.py +25 -40
  104. package/python/foxclaw/core/truth_pipeline.py +5 -9
  105. package/python/foxclaw/core/update_check.py +149 -0
  106. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  107. package/python/foxclaw/core/verify.py +6 -14
  108. package/python/foxclaw/core/web_search.py +4 -28
  109. package/python/foxclaw/finance/evidence.py +14 -18
  110. package/python/foxclaw/finance/fx.py +1 -3
  111. package/python/foxclaw/finance/graph.py +9 -18
  112. package/python/foxclaw/finance/models.py +20 -24
  113. package/python/foxclaw/finance/nlparse.py +12 -72
  114. package/python/foxclaw/finance/policy.py +8 -36
  115. package/python/foxclaw/finance/reasoner.py +3 -11
  116. package/python/foxclaw/finance/reasoning.py +13 -34
  117. package/python/foxclaw/finance/reconcile.py +5 -10
  118. package/python/foxclaw/finance/runtime.py +19 -46
  119. package/python/foxclaw/governance/middleware.py +13 -21
  120. package/python/foxclaw/harness/_base.py +2 -8
  121. package/python/foxclaw/harness/_policy.py +19 -38
  122. package/python/foxclaw/harness/drift.py +1 -13
  123. package/python/foxclaw/harness/event_bus.py +2 -5
  124. package/python/foxclaw/harness/harness.py +9 -97
  125. package/python/foxclaw/harness/loop_detection.py +11 -18
  126. package/python/foxclaw/itinerary/amap_client.py +2 -5
  127. package/python/foxclaw/itinerary/models.py +7 -7
  128. package/python/foxclaw/itinerary/reasoner.py +1 -6
  129. package/python/foxclaw/itinerary/reasoning.py +15 -21
  130. package/python/foxclaw/itinerary/runtime.py +8 -12
  131. package/python/foxclaw/kernel/capabilities.py +5 -6
  132. package/python/foxclaw/kernel/policy_gate.py +4 -10
  133. package/python/foxclaw/kernel/process_manager.py +17 -36
  134. package/python/foxclaw/kernel/scheduler.py +3 -9
  135. package/python/foxclaw/kernel/sentinel.py +13 -23
  136. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  137. package/python/foxclaw/kernel/spine.py +9 -9
  138. package/python/foxclaw/knowledge/__init__.py +4 -20
  139. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  140. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  141. package/python/foxclaw/learning/models.py +13 -13
  142. package/python/foxclaw/learning/reasoner.py +1 -4
  143. package/python/foxclaw/learning/runtime.py +19 -31
  144. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  145. package/python/foxclaw/mcp_server.py +2 -3
  146. package/python/foxclaw/orchestration/__init__.py +16 -33
  147. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  148. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  149. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  150. package/python/foxclaw/planning/llm_planner.py +3 -3
  151. package/python/foxclaw/planning/planner.py +6 -11
  152. package/python/foxclaw/search/agent_port.py +7 -11
  153. package/python/foxclaw/search/evidence.py +0 -4
  154. package/python/foxclaw/search/internal_search.py +3 -5
  155. package/python/foxclaw/search/models.py +11 -13
  156. package/python/foxclaw/search/planner.py +3 -3
  157. package/python/foxclaw/search/retriever.py +1 -1
  158. package/python/foxclaw/search/runtime.py +1 -8
  159. package/python/foxclaw/skills/__init__.py +46 -100
  160. package/python/foxclaw/skills/archive.py +111 -0
  161. package/python/foxclaw/skills/fetch.py +226 -0
  162. package/python/foxclaw/skills/hooks_spec.py +21 -25
  163. package/python/foxclaw/skills/install_cmd.py +216 -0
  164. package/python/foxclaw/skills/lifecycle.py +16 -42
  165. package/python/foxclaw/skills/markdown_skill.py +10 -9
  166. package/python/foxclaw/skills/mcp_client.py +5 -11
  167. package/python/foxclaw/skills/mcp_store.py +0 -3
  168. package/python/foxclaw/skills/permission.py +13 -48
  169. package/python/foxclaw/skills/pipeline.py +21 -53
  170. package/python/foxclaw/skills/policy.py +4 -10
  171. package/python/foxclaw/skills/review.py +3 -3
  172. package/python/frontend/deck/auth.js +0 -7
  173. package/python/frontend/deck/chat_model_picker.js +0 -54
  174. package/python/frontend/deck/domain.js +0 -20
  175. package/python/frontend/deck/main.js +0 -144
  176. package/python/frontend/deck/markdown.js +0 -4
  177. package/python/frontend/deck/settings.js +1 -18
  178. package/python/frontend/deck/state.js +0 -1
  179. package/python/frontend/deck/util.js +0 -2
  180. package/python/frontend/deck/visuals.js +0 -22
  181. package/python/frontend/planet/planet.js +0 -201
  182. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  183. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  184. package/python/frontend/v3/index.html +3 -3
  185. package/python/frontend/widget_runtime.js +0 -43
  186. package/python/frontend/widgets.js +0 -57
  187. package/python/frontend_src/index.html +1 -1
  188. package/python/frontend_src/package-lock.json +2 -2
  189. package/python/frontend_src/package.json +1 -1
  190. package/python/frontend_src/src/App.vue +3 -7
  191. package/python/frontend_src/src/api.js +0 -7
  192. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  193. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  194. package/python/frontend_src/src/components/Inspector.vue +0 -6
  195. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  196. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  197. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  198. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  199. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  200. package/python/frontend_src/src/components/Stage.vue +71 -25
  201. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  202. package/python/frontend_src/src/components/TopBar.vue +38 -10
  203. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  204. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  205. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  206. package/python/frontend_src/src/components/panels/MemoryPanel.vue +0 -1
  207. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  208. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  209. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  210. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  211. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  212. package/python/frontend_src/src/lib/fmt.js +0 -4
  213. package/python/frontend_src/src/lib/i18n.js +53 -0
  214. package/python/frontend_src/src/lib/locales/en.js +173 -0
  215. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  216. package/python/frontend_src/src/lib/md.js +0 -14
  217. package/python/frontend_src/src/main.js +2 -0
  218. package/python/frontend_src/src/stores/app.js +17 -32
  219. package/python/frontend_src/src/stores/chat.js +0 -34
  220. package/python/frontend_src/src/stores/model.js +0 -8
  221. package/python/frontend_src/src/stores/panels.js +0 -41
  222. package/python/frontend_src/src/stores/profile.js +31 -0
  223. package/python/frontend_src/src/stores/system.js +25 -0
  224. package/python/frontend_src/vite.config.js +0 -7
  225. package/python/pyproject.toml +6 -3
  226. package/python/requirements.txt +311 -0
  227. package/python/tests/conftest.py +0 -22
  228. package/python/tests/run_all.py +3 -66
  229. package/python/tests/test_agent_lifecycle.py +4 -38
  230. package/python/tests/test_ahs_p2.py +4 -25
  231. package/python/tests/test_anthropic_tools.py +1 -15
  232. package/python/tests/test_api.py +19 -9
  233. package/python/tests/test_api_auth.py +1 -5
  234. package/python/tests/test_attachments.py +9 -25
  235. package/python/tests/test_audit_chain.py +2 -7
  236. package/python/tests/test_audit_sink.py +0 -6
  237. package/python/tests/test_browser.py +2 -6
  238. package/python/tests/test_browser_request_guard.py +0 -4
  239. package/python/tests/test_capabilities.py +0 -6
  240. package/python/tests/test_channel_signing.py +2 -9
  241. package/python/tests/test_channels.py +1 -11
  242. package/python/tests/test_config_layering.py +0 -4
  243. package/python/tests/test_confirm_nonce.py +1 -12
  244. package/python/tests/test_context_compaction_wiring.py +1 -7
  245. package/python/tests/test_dispatch.py +0 -6
  246. package/python/tests/test_domain_evals.py +11 -80
  247. package/python/tests/test_domain_import.py +0 -6
  248. package/python/tests/test_domain_reasoners.py +0 -14
  249. package/python/tests/test_e2e.py +0 -17
  250. package/python/tests/test_emb_cache.py +7 -17
  251. package/python/tests/test_embedding_singleton.py +1 -3
  252. package/python/tests/test_evals_smoke.py +0 -24
  253. package/python/tests/test_event_spine.py +10 -18
  254. package/python/tests/test_evidence_semantic.py +1 -14
  255. package/python/tests/test_evidence_sse.py +3 -6
  256. package/python/tests/test_file_ingest.py +0 -11
  257. package/python/tests/test_finance.py +0 -9
  258. package/python/tests/test_finance_ledger.py +5 -12
  259. package/python/tests/test_finance_persist.py +8 -12
  260. package/python/tests/test_gateway.py +3 -22
  261. package/python/tests/test_governance.py +5 -52
  262. package/python/tests/test_governance_audit.py +4 -39
  263. package/python/tests/test_governance_middleware.py +1 -7
  264. package/python/tests/test_hkc.py +6 -24
  265. package/python/tests/test_hkc_index_build_manager.py +0 -33
  266. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  267. package/python/tests/test_hkc_search_event_sync.py +3 -54
  268. package/python/tests/test_import_path_guard.py +0 -6
  269. package/python/tests/test_injection_guard.py +18 -36
  270. package/python/tests/test_integration.py +5 -29
  271. package/python/tests/test_itinerary.py +0 -3
  272. package/python/tests/test_kee_semantic.py +0 -5
  273. package/python/tests/test_kernel.py +1 -11
  274. package/python/tests/test_learning.py +3 -12
  275. package/python/tests/test_llm_admin.py +4 -19
  276. package/python/tests/test_llm_planner.py +1 -23
  277. package/python/tests/test_markdown_skills.py +3 -14
  278. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  279. package/python/tests/test_mcp_connectors.py +3 -15
  280. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  281. package/python/tests/test_mcp_no_memory.py +3 -3
  282. package/python/tests/test_mcp_policy_gate.py +0 -1
  283. package/python/tests/test_mcp_server.py +6 -9
  284. package/python/tests/test_memory_recall.py +1 -4
  285. package/python/tests/test_metrics.py +0 -8
  286. package/python/tests/test_multi_chain.py +1 -13
  287. package/python/tests/test_multimodal.py +5 -18
  288. package/python/tests/test_negotiation.py +3 -13
  289. package/python/tests/test_orchestration.py +1 -11
  290. package/python/tests/test_orchestration_decoupled.py +0 -6
  291. package/python/tests/test_p3_p5_hardening.py +3 -17
  292. package/python/tests/test_parallel_dispatch.py +4 -12
  293. package/python/tests/test_pipeline.py +17 -58
  294. package/python/tests/test_plan_visible.py +4 -4
  295. package/python/tests/test_planner.py +0 -7
  296. package/python/tests/test_policy_gate.py +2 -3
  297. package/python/tests/test_profile.py +67 -0
  298. package/python/tests/test_readme_claims.py +163 -0
  299. package/python/tests/test_reasoner.py +2 -17
  300. package/python/tests/test_release_check.py +2 -13
  301. package/python/tests/test_resume_checkpoint.py +7 -15
  302. package/python/tests/test_routing.py +2 -12
  303. package/python/tests/test_scheduler.py +7 -10
  304. package/python/tests/test_search.py +1 -11
  305. package/python/tests/test_secrets.py +4 -10
  306. package/python/tests/test_semantic.py +1 -10
  307. package/python/tests/test_sessions.py +3 -4
  308. package/python/tests/test_settings.py +2 -18
  309. package/python/tests/test_shadow_learning.py +3 -43
  310. package/python/tests/test_shared_write.py +0 -7
  311. package/python/tests/test_skill_taint_gate.py +1 -10
  312. package/python/tests/test_skills.py +2 -18
  313. package/python/tests/test_skills_fetch.py +282 -0
  314. package/python/tests/test_stats_and_events.py +2 -5
  315. package/python/tests/test_streaming.py +2 -7
  316. package/python/tests/test_supervisor_pipeline.py +4 -5
  317. package/python/tests/test_supervisor_triage.py +4 -10
  318. package/python/tests/test_taint.py +2 -7
  319. package/python/tests/test_task_stream.py +1 -12
  320. package/python/tests/test_thinking_sse.py +0 -1
  321. package/python/tests/test_tool_center.py +1 -17
  322. package/python/tests/test_tool_parallel.py +2 -2
  323. package/python/tests/test_tool_params.py +1 -5
  324. package/python/tests/test_tool_use.py +0 -13
  325. package/python/tests/test_toplevel_concurrency.py +3 -4
  326. package/python/tests/test_tracing.py +7 -41
  327. package/python/tests/test_truth_crystallizer.py +4 -6
  328. package/python/tests/test_truth_flow.py +2 -7
  329. package/python/tests/test_update_check.py +95 -0
  330. package/python/tests/test_verify_evidence.py +8 -21
  331. package/python/tests/test_version_sync.py +35 -6
  332. package/python/tests/test_web_trigger.py +0 -17
  333. package/python/tools/check_faiss_type.py +0 -1
  334. package/python/tools/check_log.ps1 +0 -8
  335. package/python/tools/mypy_baseline.txt +12 -4
  336. package/python/tools/mypy_ratchet.py +52 -6
  337. package/python/tools/pack.py +0 -5
  338. package/python/tools/release_check.py +7 -7
  339. package/python/tools/restart_foxclaw.ps1 +5 -8
  340. package/python/tools/shared_write_audit.py +1 -15
  341. package/python/tools/size_check.py +1 -1
  342. package/python/tools/strip_comments.py +228 -0
  343. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  344. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -1,6 +1,6 @@
1
1
  """知识核心(HKC)端点:同进程 DirectClient,惰性装配,缺依赖降级。
2
2
 
3
- 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
4
  晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
5
5
  """
6
6
 
@@ -13,13 +13,9 @@ import foxclaw.api as _api
13
13
  from .. import app
14
14
 
15
15
 
16
- # ── 知识核心(HKC):同进程 DirectClient,惰性装配,缺依赖降级 ─────────
17
- # HKC = Foxclaw Knowledge Core:KDE 抽取 → KEE 演化 → 混合检索(BM25+向量+图)。
18
- # 首次命中任一路由时才装配(见 Foxclaw.hkc 惰性属性);未 vendor/缺依赖时 available=False。
19
-
20
16
  class KnowledgeSearchPayload(BaseModel):
21
17
  query: str
22
- mode: str = "hybrid" # bm25 | vector | graph | hybrid
18
+ mode: str = "hybrid"
23
19
  top_k: int = 10
24
20
  domain: str = ""
25
21
 
@@ -33,15 +29,11 @@ class KnowledgeIngestPayload(BaseModel):
33
29
  def _hkc_or_none():
34
30
  """惰性取 HKCService;None 表示降级(未 vendor/缺依赖/装配失败)。"""
35
31
  try:
36
- return _api._os_instance.hkc
32
+ return _api.os_instance().hkc
37
33
  except Exception:
38
34
  return None
39
35
 
40
36
 
41
- # 【P3-7】异常分类用 isinstance 优先:hasattr(e, "missing_skills") 是鸭子判断,
42
- # 任何恰好带了同名属性的无关异常都会被误吞成「覆盖不足」证据 gate(真实错误被掩盖)。
43
- # 真实类型在 vendor 的 hkc_sdk.exceptions(test_hkc.py 同源引用);缺 vendor/hkc 时
44
- # import 失败降级 None,运行时回退 hasattr 判断,行为与旧版一致。
45
37
  try:
46
38
  from hkc_sdk.exceptions import HKCInsufficientCoverage as _HKCInsuffCov
47
39
  except Exception:
@@ -76,9 +68,6 @@ async def knowledge_ingest(p: KnowledgeIngestPayload):
76
68
  hkc.client.ingest_text, p.text, p.source_title, p.domain)
77
69
  return {"ku_count": res.ku_count, "ku_ids": res.ku_ids, "counts": res.counts}
78
70
  except Exception as e:
79
- # 抽取全段失败时 KDE 现在会抛错(带 provider/model/最后一次 LLM 报错原文)。
80
- # 不能像旧版那样返回 ku_count=0 + 200 —— 那会让「库里其实什么都没有」看起来
81
- # 像「录入成功只是没内容」,复习板块于是永远 0 卡。
82
71
  msg = str(e).strip() or e.__class__.__name__
83
72
  raise HTTPException(502, f"摄入失败:{msg}")
84
73
 
@@ -103,7 +92,6 @@ async def knowledge_recent(limit: int = 10):
103
92
  except Exception as e:
104
93
  return {"available": True, "total": 0, "kus": [], "error": str(e)}
105
94
 
106
- # ISO 字符串,字典序即时间序
107
95
  kus.sort(key=lambda k: getattr(k, "created_at", "") or "", reverse=True)
108
96
  n = max(1, min(int(limit or 10), 200))
109
97
  return {"available": True, "total": len(kus), "kus": [
@@ -114,7 +102,6 @@ async def knowledge_recent(limit: int = 10):
114
102
  for ku in kus[:n]]}
115
103
 
116
104
 
117
- # ── HKC 融合第 2 档 Step1:ACE 能力(只读查询面,无 LLM、零成本)─────────
118
105
  @app.get("/api/knowledge/abilities")
119
106
  async def knowledge_abilities():
120
107
  """列出可编译的能力 key(来自 ACE skill_taxonomy)。只读、确定性、无 LLM。"""
@@ -139,11 +126,6 @@ async def knowledge_ability(ability_key: str):
139
126
  try:
140
127
  ab = await asyncio.to_thread(hkc.client.compile_ability, ability_key)
141
128
  except Exception as e:
142
- # 覆盖不足(HKCInsufficientCoverage 带 coverage/missing_skills)→ 证据 gate,非错误。
143
- # 【P3-7】isinstance 可用时**只**用 isinstance(精确判断:带 missing_skills 属性的
144
- # 无关异常不再被误吞成「覆盖不足」);import 失败(缺 vendor)才回退 hasattr 鸭子判断。
145
- # 文稿骨架写的是 isinstance OR hasattr 叠加,与其自身设计决策(「失败才回退」=二选一)
146
- # 不一致——OR 下鸭子判断永远生效,精确化形同虚设,故按设计决策实现。
147
129
  if _HKCInsuffCov is not None:
148
130
  is_insufficient = isinstance(e, _HKCInsuffCov)
149
131
  else:
@@ -28,7 +28,6 @@ CUSTOM_MODELS_PATH = _DATA_DIR / "custom_models.json"
28
28
  CURRENT_MODEL_PATH = _DATA_DIR / ".current_model"
29
29
 
30
30
 
31
- # 预置 provider(展示用:标签 + 配色 + 探测 key 名)
32
31
  _PRESET_PROVIDERS = [
33
32
  {"provider": "generic", "label": "通用 OpenAI 兼容", "color": "#7e93b8"},
34
33
  {"provider": "anthropic", "label": "Claude", "color": "#ff9d00"},
@@ -52,8 +51,6 @@ _PROVIDER_KEY_ENV = {
52
51
  }
53
52
 
54
53
 
55
- # ── 工具 ─────────────────────────────────────────────────
56
-
57
54
  def _save_key(name: str, key: str) -> dict:
58
55
  """把 key 存到最安全的地方,返回该写进 custom_models.json 的字段。
59
56
 
@@ -98,7 +95,8 @@ def _sync_hkc_llm() -> None:
98
95
  extractor 自带 ``reconfigure``(HKC 自己的界面也用它),这里复用同一条路径。
99
96
  """
100
97
  try:
101
- hkc = _api._os_instance.hkc if _api._os_instance else None
98
+ os_inst = _api.current_os()
99
+ hkc = os_inst.hkc if os_inst is not None else None
102
100
  if hkc is None or not getattr(hkc, "available", False):
103
101
  return
104
102
  from foxclaw.knowledge import _detect_llm
@@ -109,12 +107,10 @@ def _sync_hkc_llm() -> None:
109
107
  model=model or None,
110
108
  base_url=base or None,
111
109
  )
112
- except Exception as e: # 同步失败绝不影响"切模型"这个主操作
110
+ except Exception as e:
113
111
  print(f"[llm_admin] 同步知识抽取(HKC)配置失败: {e}")
114
112
 
115
113
 
116
- # ── 端点 ─────────────────────────────────────────────────
117
-
118
114
  @app.get("/api/llm/models")
119
115
  async def list_models():
120
116
  """列出所有可用模型:预置(按 .env 探测) + 自定义(从 custom_models.json) + 当前选中。"""
@@ -143,7 +139,7 @@ async def list_models():
143
139
 
144
140
 
145
141
  class _SelectReq(BaseModel):
146
- source: str # "auto" | "preset" | "custom"
142
+ source: str
147
143
  provider: Optional[str] = None
148
144
  name: Optional[str] = None
149
145
 
@@ -155,7 +151,8 @@ async def select_model(req: _SelectReq):
155
151
  (key 空 / BASE URL 非法 / 记录已被删),前端显示"已切换",聊天却继续走旧 provider,
156
152
  用户看到的是"填了填不上"。现在选中前先自检,失败直接 400 带原因。
157
153
  """
158
- router = _api._os_instance.router if _api._os_instance else None
154
+ os_inst = _api.current_os()
155
+ router = os_inst.router if os_inst is not None else None
159
156
  if req.source == "auto":
160
157
  if CURRENT_MODEL_PATH.exists():
161
158
  CURRENT_MODEL_PATH.unlink()
@@ -171,24 +168,25 @@ async def select_model(req: _SelectReq):
171
168
  raise HTTPException(400, f"{req.provider} 未在 .env 配置 Key({env_key})")
172
169
  data = {"source": "preset", "provider": req.provider}
173
170
  elif req.source == "custom":
171
+ name = req.name
172
+ if not name:
173
+ raise HTTPException(400, "缺少自定义模型名称 name")
174
174
  models = _load_custom()
175
- match = next((m for m in models if m.get("name") == req.name), None)
175
+ match = next((m for m in models if m.get("name") == name), None)
176
176
  if not match:
177
- raise HTTPException(404, f"未找到自定义模型: {req.name}")
178
- # 预检:能不能真构造出 client(否则"切换成功"是假象)
177
+ raise HTTPException(404, f"未找到自定义模型: {name}")
179
178
  if router is not None:
180
- client, _model, err = router._build_custom_client(req.name)
179
+ client, _model, err = router._build_custom_client(name)
181
180
  if client is None:
182
181
  raise HTTPException(400, f"该自定义模型不可用: {err}")
183
- data = {"source": "custom", "name": req.name, "model_id": match.get("model", "")}
182
+ data = {"source": "custom", "name": name, "model_id": match.get("model", "")}
184
183
  else:
185
184
  raise HTTPException(400, f"未知 source: {req.source}")
186
185
  CURRENT_MODEL_PATH.parent.mkdir(parents=True, exist_ok=True)
187
186
  CURRENT_MODEL_PATH.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
188
- # 立即 reload(mtime 变了);正常聊天路径也会 lazy reload,这里只是让 /runtime 立刻正确
189
187
  if router is not None:
190
188
  router._maybe_reload_selection()
191
- _sync_hkc_llm() # 知识抽取跟着切,别让它继续用旧 key
189
+ _sync_hkc_llm()
192
190
  return {"ok": True, "current": data,
193
191
  "runtime": ({"provider": router.provider,
194
192
  "model": getattr(router, "model", ""),
@@ -248,7 +246,7 @@ class _CustomUpdateReq(BaseModel):
248
246
  api_key: Optional[str] = None
249
247
  model: Optional[str] = None
250
248
  extra_headers: Optional[Dict[str, str]] = None
251
- group_id: Optional[str] = None # 显式空字符串 = 移除 MiniMax-Group-Id header
249
+ group_id: Optional[str] = None
252
250
  note: Optional[str] = None
253
251
 
254
252
 
@@ -262,7 +260,6 @@ async def get_custom_model(name: str):
262
260
  raise HTTPException(404, f"未找到: {name}")
263
261
  extra = dict(match.get("extra_headers") or {})
264
262
  gid = extra.get("MiniMax-Group-Id", "")
265
- # extra_headers 拆成 group_id + 其它(避免两边都返回重复键)
266
263
  extra_only = {k: v for k, v in extra.items() if k != "MiniMax-Group-Id"}
267
264
  return {
268
265
  "name": match.get("name", ""),
@@ -291,18 +288,15 @@ async def update_custom_model(name: str, req: _CustomUpdateReq):
291
288
  raise HTTPException(404, f"未找到: {name}")
292
289
  target = models[idx]
293
290
 
294
- # 改名冲突检测
295
291
  new_name = (req.name or "").strip() or target["name"]
296
292
  if new_name != name and any(m.get("name") == new_name for m in models):
297
293
  raise HTTPException(409, f"已存在同名模型: {new_name}")
298
294
 
299
- # 必填校验
300
295
  if req.base_url is not None and not req.base_url.strip():
301
296
  raise HTTPException(400, "base_url 不能为空")
302
297
  if req.model is not None and not req.model.strip():
303
298
  raise HTTPException(400, "model 不能为空")
304
299
 
305
- # 应用普通字段
306
300
  if req.base_url is not None:
307
301
  try:
308
302
  target["base_url"] = normalize_base_url(req.base_url)
@@ -311,12 +305,10 @@ async def update_custom_model(name: str, req: _CustomUpdateReq):
311
305
  if req.model is not None:
312
306
  target["model"] = req.model.strip()
313
307
  if req.api_key is not None and req.api_key.strip():
314
- # 留空 = 保留原 key(防误清);非空 = 覆盖(按新名字存,改名时不会留下孤儿条目)
315
308
  target.update(_save_key(new_name, req.api_key))
316
309
  if req.note is not None:
317
310
  target["note"] = req.note
318
311
 
319
- # extra_headers + group_id 合并处理
320
312
  if req.extra_headers is not None or req.group_id is not None:
321
313
  extra = dict(target.get("extra_headers") or {})
322
314
  if req.extra_headers is not None:
@@ -329,10 +321,7 @@ async def update_custom_model(name: str, req: _CustomUpdateReq):
329
321
  extra.pop("MiniMax-Group-Id", None)
330
322
  target["extra_headers"] = extra
331
323
 
332
- # 改名:同步 .current_model
333
324
  if new_name != name:
334
- # 凭据按模型名存放 → 不改名搬迁就再也 load 不到(key 静默丢失)。
335
- # 已换新 key 时新条目已在 new_name 下,这里只需清掉旧的。
336
325
  if req.api_key is not None and req.api_key.strip():
337
326
  _secrets.delete(name)
338
327
  else:
@@ -361,8 +350,7 @@ async def delete_custom_model(name: str):
361
350
  if len(new_models) == len(models):
362
351
  raise HTTPException(404, f"未找到: {name}")
363
352
  _save_custom(new_models)
364
- _secrets.delete(name) # 别在 OS 凭据库里留孤儿条目
365
- # 如果当前选的就是它 → 清空选择
353
+ _secrets.delete(name)
366
354
  if CURRENT_MODEL_PATH.exists():
367
355
  try:
368
356
  cur = json.loads(CURRENT_MODEL_PATH.read_text(encoding="utf-8"))
@@ -374,7 +362,7 @@ async def delete_custom_model(name: str):
374
362
 
375
363
 
376
364
  class _TestReq(BaseModel):
377
- source: str # "preset" | "custom"
365
+ source: str
378
366
  provider: Optional[str] = None
379
367
  name: Optional[str] = None
380
368
 
@@ -391,7 +379,8 @@ async def test_model(req: _TestReq):
391
379
  if req.source == "preset":
392
380
  if not req.provider:
393
381
  raise HTTPException(400, "preset 必须给 provider")
394
- router = _api._os_instance.router if _api._os_instance else None
382
+ os_inst = _api.current_os()
383
+ router = os_inst.router if os_inst is not None else None
395
384
  if router is None:
396
385
  raise HTTPException(503, "OS 未就绪")
397
386
  client, err = router._build_client(req.provider)
@@ -444,11 +433,10 @@ async def test_model(req: _TestReq):
444
433
  @app.get("/api/llm/runtime")
445
434
  async def get_runtime():
446
435
  """当前真正生效的 provider/model(供前端高亮 + 调试)。"""
447
- router = _api._os_instance.router if _api._os_instance else None
436
+ os_inst = _api.current_os()
437
+ router = os_inst.router if os_inst is not None else None
448
438
  if router is None:
449
439
  return {"ok": False, "error": "OS 未就绪"}
450
- # runtime 端点也走 lazy reload,让前端选完模型 → 立刻看到生效值
451
- # (complete() 入口也会调;这里额外调一次,GET 路径无副作用)
452
440
  try:
453
441
  router._maybe_reload_selection()
454
442
  except Exception:
@@ -1,7 +1,7 @@
1
1
  """记忆统计 / 会话历史 / 用户偏好 路由。
2
2
 
3
- 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
4
- 晚绑定读取(保持测试可重绑定 `api._os_instance`);路由用 `@app` 装饰器在 import 时注册。
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
+ 晚绑定读取(状态在 `_state`,测试可重绑 `_state._os_instance`);路由用 `@app` 装饰器在 import 时注册。
5
5
  """
6
6
 
7
7
  from fastapi import HTTPException
@@ -15,12 +15,11 @@ from .. import app
15
15
  async def get_memory_stats():
16
16
  """记忆中心统计:供前端 vecRecall 面板展示真实数字。"""
17
17
  try:
18
- return _api._os_instance.hmr.stats()
18
+ return _api.os_instance().hmr.stats()
19
19
  except Exception:
20
20
  return {"backend": "?", "total_memories": 0, "relations": 0}
21
21
 
22
22
 
23
- # ── 会话历史 ───────────────────────────────────────────────
24
23
  class SessionCreate(BaseModel):
25
24
  title: str = ""
26
25
  agent_id: str = "supervisor"
@@ -34,7 +33,7 @@ class SessionRename(BaseModel):
34
33
  async def list_sessions(limit: int = 50):
35
34
  """会话列表(按更新时间倒序,轻字段不含 messages)。"""
36
35
  try:
37
- return {"sessions": _api._os_instance.sessions.list(limit)}
36
+ return {"sessions": _api.os_instance().sessions.list(limit)}
38
37
  except Exception:
39
38
  return {"sessions": []}
40
39
 
@@ -42,13 +41,13 @@ async def list_sessions(limit: int = 50):
42
41
  @app.post("/api/sessions")
43
42
  async def create_session(req: SessionCreate):
44
43
  """新建会话。"""
45
- return _api._os_instance.sessions.create(req.title, req.agent_id)
44
+ return _api.os_instance().sessions.create(req.title, req.agent_id)
46
45
 
47
46
 
48
47
  @app.get("/api/sessions/{sid}")
49
48
  async def get_session(sid: str):
50
49
  """取单个会话(含完整 messages),供前端恢复对话。"""
51
- d = _api._os_instance.sessions.get(sid)
50
+ d = _api.os_instance().sessions.get(sid)
52
51
  if d is None:
53
52
  raise HTTPException(404, "会话不存在")
54
53
  return d
@@ -56,19 +55,19 @@ async def get_session(sid: str):
56
55
 
57
56
  @app.post("/api/sessions/{sid}/rename")
58
57
  async def rename_session(sid: str, req: SessionRename):
59
- return {"ok": _api._os_instance.sessions.rename(sid, req.title)}
58
+ return {"ok": _api.os_instance().sessions.rename(sid, req.title)}
60
59
 
61
60
 
62
61
  @app.delete("/api/sessions/{sid}")
63
62
  async def delete_session(sid: str):
64
- return {"ok": _api._os_instance.sessions.delete(sid)}
63
+ return {"ok": _api.os_instance().sessions.delete(sid)}
65
64
 
66
65
 
67
66
  @app.get("/api/memory/recent")
68
67
  async def get_recent_memories(limit: int = 20):
69
68
  """最近的记忆条目(倒序),供前端右栏"记忆信息条"。轻字段,不含 embedding。"""
70
69
  try:
71
- fn = getattr(_api._os_instance.hmr, "recent", None)
70
+ fn = getattr(_api.os_instance().hmr, "recent", None)
72
71
  return {"memories": fn(limit) if callable(fn) else []}
73
72
  except Exception:
74
73
  return {"memories": []}
@@ -78,12 +77,29 @@ async def get_recent_memories(limit: int = 20):
78
77
  async def list_preferences():
79
78
  """列出已记住的用户偏好(供查看/清理误存)。偏好是 shared,任一 Agent 视角一致,
80
79
  这里用主管取全量(偏好是 shared,任一 Agent 视角一致)。返回 [{id, content, ...}]。"""
81
- ag = _api._os_instance.supervisor
80
+ ag = _api.os_instance().supervisor
82
81
  return {"preferences": ag.list_preferences()}
83
82
 
84
83
 
85
84
  @app.delete("/api/preferences/{pref_id}")
86
85
  async def delete_preference(pref_id: str):
87
86
  """删一条误存的用户偏好(F3:防误报偏好持久注入污染)。返回 {ok}。"""
88
- ag = _api._os_instance.supervisor
87
+ ag = _api.os_instance().supervisor
89
88
  return {"ok": ag.delete_preference(pref_id)}
89
+
90
+
91
+ class ProfilePayload(BaseModel):
92
+ nickname: str = ""
93
+
94
+
95
+ @app.get("/api/profile")
96
+ async def get_profile():
97
+ """当前称呼。没设置过返回空串(前端据此决定要不要显示首次引导)。"""
98
+ return {"nickname": _api.os_instance().supervisor.get_nickname()}
99
+
100
+
101
+ @app.put("/api/profile")
102
+ async def update_profile(payload: ProfilePayload):
103
+ """设置称呼。传空串 = 清除。返回生效后的值(已 trim/截断)。"""
104
+ nick = _api.os_instance().supervisor.set_nickname(payload.nickname)
105
+ return {"ok": True, "nickname": nick}
@@ -1,6 +1,6 @@
1
1
  """主管编排端点(全局态势 + 跨 Agent)。
2
2
 
3
- 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
4
  晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
5
5
  """
6
6
 
@@ -11,13 +11,11 @@ import foxclaw.api as _api
11
11
  from .. import app
12
12
 
13
13
 
14
- # ── 主管编排端点(全局态势 + 跨 Agent)──────────────────────
15
-
16
14
  @app.get("/api/orchestration/situation")
17
15
  async def global_situation():
18
16
  """主管全局态势:三内核状态汇总 + 跨域告警(驾驶舱视图)。"""
19
17
  try:
20
- return _api._os_instance.supervisor.global_situation()
18
+ return _api.os_instance().supervisor.global_situation()
21
19
  except Exception as e:
22
20
  raise HTTPException(500, f"态势汇总失败: {e}")
23
21
 
@@ -34,7 +32,7 @@ class CrossReqPayload(BaseModel):
34
32
  async def cross_request(p: CrossReqPayload):
35
33
  """跨 Agent 请求:目标 Agent 用守门人裁决(如行程申请差旅预算)。"""
36
34
  try:
37
- return _api._os_instance.supervisor.cross_request(
35
+ return _api.os_instance().supervisor.cross_request(
38
36
  from_agent=p.from_agent, to_agent=p.to_agent,
39
37
  request_type=p.request_type, payload=p.payload, reason=p.reason)
40
38
  except Exception as e:
@@ -16,7 +16,7 @@ from .. import app
16
16
  @app.get("/api/recovery/status")
17
17
  async def recovery_status():
18
18
  """中断任务清单 + 每任务最近步骤 + 续跑所需 context(直接可回灌 /api/task)。"""
19
- os_ = _api._os_instance
19
+ os_ = _api.current_os()
20
20
  if os_ is None:
21
21
  return {"enabled": False, "interrupted": [], "plan": {}}
22
22
  rec = getattr(os_, "recovery", None)
@@ -34,10 +34,11 @@ async def recovery_status():
34
34
  "channel": t.channel,
35
35
  "status": t.status,
36
36
  "steps_count": len(steps),
37
- "last_steps": steps[-10:], # 只回最近 10 步,避免响应体失控
37
+ "last_steps": steps[-10:],
38
38
  "resume_hint": {"endpoint": "/api/task",
39
39
  "context": {"_resume_task_id": t.task_id}},
40
40
  })
41
41
  plan = getattr(os_, "recovery_plan", None)
42
+ as_dict = getattr(plan, "to_dict", None)
42
43
  return {"enabled": rec is not None, "interrupted": out,
43
- "plan": plan.to_dict() if hasattr(plan, "to_dict") else {}}
44
+ "plan": as_dict() if callable(as_dict) else {}}
@@ -23,12 +23,8 @@ _SR = SpacedRepetition()
23
23
 
24
24
  _CARD_TYPES = {"Concept", "Fact", "Claim", "Entity"}
25
25
 
26
- # 扫描上限(每种卡片类型)。之前统一 query_all(4000),一是把 Evidence 这种绝对不会
27
- # 成卡的原文出处也一起拉出来(大文档导入后它是绝大多数行),二是 4000 之后的部分
28
- # 会被静默截断 → 复习统计少算。这里只查四种卡片类型,上限给到实际用不满的量级。
29
26
  _SCAN_LIMIT = 50000
30
27
 
31
- # ReviewState.to_dict() 会额外塞 mastery / is_due,读回来时必须剔掉才能重建
32
28
  _STATE_FIELDS = ("repetitions", "interval", "ease_factor", "due_date",
33
29
  "last_review", "total_reviews", "lapses")
34
30
 
@@ -36,7 +32,7 @@ _STATE_FIELDS = ("repetitions", "interval", "ease_factor", "due_date",
36
32
  def _store():
37
33
  """惰性取 HKC 的 graph_store,None 表示知识库没装配好(降级成空队列)。"""
38
34
  try:
39
- hkc = _api._os_instance.hkc
35
+ hkc = _api.os_instance().hkc
40
36
  if hkc is None or not hkc.available:
41
37
  return None
42
38
  return hkc.container.graph_store
@@ -73,7 +69,7 @@ def _hours_left(state) -> float:
73
69
 
74
70
  def _load7(learned) -> dict:
75
71
  """未来 7 天(0..6)的复习负荷,口径与队列一致。"""
76
- by_day = {}
72
+ by_day: dict[str, int] = {}
77
73
  for s in learned:
78
74
  d = 0 if s.is_due else _days_left(s)
79
75
  if d <= 6:
@@ -251,11 +247,6 @@ async def review_grade(body: GradeIn):
251
247
  if ku is None or _status_of(ku) != "active":
252
248
  raise HTTPException(404, f"没有这条知识:{body.ku_id}")
253
249
 
254
- # 关键:只更新 extra 这一列,而且「读 extra → SM-2 推演 → 写回」整段在
255
- # graph_store 的锁里完成。走 put() 是整行 UPSERT,会把整行其它列按写入前的
256
- # 快照回写 —— 知识星球正在摄入时,KEE 刚合并进去的 sources / confidence /
257
- # supports 会被这次评分悄悄冲掉(反向亦然:复习进度被 KEE 写回覆盖)。
258
- # 锁内做还有个好处:连点两次评分不会两次都基于同一份旧状态推演。
259
250
  graded: list[ReviewState] = []
260
251
 
261
252
  def _apply(extra: dict) -> dict:
@@ -1,6 +1,6 @@
1
1
  """认知搜索(HSR)端点:内部记忆 + 内部知识 + 联网,统一证据融合。
2
2
 
3
- 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
4
  晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
5
5
  """
6
6
 
@@ -13,15 +13,11 @@ import foxclaw.api as _api
13
13
  from .. import app
14
14
 
15
15
 
16
- # ── 认知搜索(HSR):内部记忆 + 内部知识 + 联网,统一证据融合 ─────────
17
- # 一次 search() 统一"查 HMR 记忆 / 查 HKC 知识 / 联网(百度)",走 planner→retriever→
18
- # fusion→strategy 迭代循环(有上限),只产出证据图,不写任何持久化(边界:不代 Agent 决策)。
19
-
20
16
  class SearchQueryPayload(BaseModel):
21
17
  query: str
22
18
  agent_id: str = "supervisor"
23
19
  context: dict = {}
24
- internal_only: bool = False # True=只查内部(HMR+HKC+Agent态势),跳过联网
20
+ internal_only: bool = False
25
21
 
26
22
 
27
23
  @app.post("/api/search/query")
@@ -29,7 +25,7 @@ async def search_query(p: SearchQueryPayload):
29
25
  """认知搜索(面向 AI/Agent 的证据管线):返回证据图 + 置信度 + 迭代轨迹。
30
26
  internal_only=True 时只查内部三源(记忆/知识/Agent),不联网。搜索内核惰性构造。"""
31
27
  try:
32
- rt = _api._os_instance.search_runtime
28
+ rt = _api.os_instance().search_runtime
33
29
  result = await asyncio.to_thread(
34
30
  rt.search, p.query, p.agent_id, p.context, p.internal_only)
35
31
  return result.to_dict()