foxclaw 0.1.0 → 0.1.2

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 (495) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +11 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +165 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +122 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/Dockerfile +64 -0
  11. package/python/LICENSE +21 -0
  12. package/python/MANIFEST.in +14 -0
  13. package/python/README.md +192 -0
  14. package/python/README_CN.md +324 -0
  15. package/python/bench/bm25_audit.py +199 -0
  16. package/python/bench/bm25_regression.py +178 -0
  17. package/python/bench/bm25_stress.py +159 -0
  18. package/python/bench/cd_audit.py +238 -0
  19. package/python/bench/chaos.py +142 -0
  20. package/python/bench/concurrency.py +103 -0
  21. package/python/bench/e2e_real_models.py +95 -0
  22. package/python/bench/llm_retry_test.py +248 -0
  23. package/python/bench/load_test.py +196 -0
  24. package/python/bench/vector_ivf_upgrade.py +134 -0
  25. package/python/docs/CHANNELS.md +195 -0
  26. package/python/docs/GOVERNANCE.md +164 -0
  27. package/python/docs/RELEASE.md +109 -0
  28. package/python/docs/RUN.md +75 -0
  29. package/python/docs/RUN_CN.md +121 -0
  30. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  31. package/python/evals/__init__.py +0 -0
  32. package/python/evals/governance/README.md +61 -0
  33. package/python/evals/governance/__init__.py +0 -0
  34. package/python/evals/governance/cases.py +152 -0
  35. package/python/evals/governance/compare_embeddings.py +94 -0
  36. package/python/evals/governance/convergence.svg +11 -0
  37. package/python/evals/governance/report.json +371 -0
  38. package/python/evals/governance/run_evals.py +400 -0
  39. package/python/examples/governance_middleware_demo.py +77 -0
  40. package/python/examples/langgraph_governance_demo.py +108 -0
  41. package/python/examples/sample_itinerary.csv +6 -0
  42. package/python/examples/sample_knowledge.csv +9 -0
  43. package/python/examples/sample_transactions.csv +14 -0
  44. package/python/foxclaw/__init__.py +526 -0
  45. package/python/foxclaw/agents/__init__.py +12 -0
  46. package/python/foxclaw/agents/agents.py +35 -0
  47. package/python/foxclaw/agents/base.py +865 -0
  48. package/python/foxclaw/agents/impl/__init__.py +1 -0
  49. package/python/foxclaw/agents/impl/_helpers.py +78 -0
  50. package/python/foxclaw/agents/impl/finance.py +248 -0
  51. package/python/foxclaw/agents/impl/office.py +367 -0
  52. package/python/foxclaw/agents/impl/supervisor.py +659 -0
  53. package/python/foxclaw/agents/mixins/_context.py +151 -0
  54. package/python/foxclaw/agents/mixins/augment.py +149 -0
  55. package/python/foxclaw/agents/mixins/llm.py +66 -0
  56. package/python/foxclaw/agents/mixins/memory.py +35 -0
  57. package/python/foxclaw/agents/mixins/preference.py +81 -0
  58. package/python/foxclaw/agents/mixins/prompt.py +77 -0
  59. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  60. package/python/foxclaw/agents/mixins/tool_exec.py +443 -0
  61. package/python/foxclaw/api/__init__.py +283 -0
  62. package/python/foxclaw/api/app.py +123 -0
  63. package/python/foxclaw/api/middleware.py +317 -0
  64. package/python/foxclaw/api/routes/ahs.py +417 -0
  65. package/python/foxclaw/api/routes/attachments.py +117 -0
  66. package/python/foxclaw/api/routes/browser.py +39 -0
  67. package/python/foxclaw/api/routes/domain.py +314 -0
  68. package/python/foxclaw/api/routes/knowledge.py +163 -0
  69. package/python/foxclaw/api/routes/llm_admin.py +461 -0
  70. package/python/foxclaw/api/routes/memory.py +89 -0
  71. package/python/foxclaw/api/routes/orchestration.py +41 -0
  72. package/python/foxclaw/api/routes/recovery.py +43 -0
  73. package/python/foxclaw/api/routes/review.py +275 -0
  74. package/python/foxclaw/api/routes/search.py +37 -0
  75. package/python/foxclaw/api/routes/system.py +327 -0
  76. package/python/foxclaw/api/routes/task.py +281 -0
  77. package/python/foxclaw/api/routes/ws.py +68 -0
  78. package/python/foxclaw/api/spine.py +55 -0
  79. package/python/foxclaw/browser/__init__.py +13 -0
  80. package/python/foxclaw/browser/action/__init__.py +1 -0
  81. package/python/foxclaw/browser/action/action_executor.py +54 -0
  82. package/python/foxclaw/browser/action/action_types.py +45 -0
  83. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  84. package/python/foxclaw/browser/action/url_policy.py +147 -0
  85. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  86. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  87. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  88. package/python/foxclaw/browser/driver/__init__.py +1 -0
  89. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  90. package/python/foxclaw/browser/driver/playwright_driver.py +166 -0
  91. package/python/foxclaw/browser/perception/__init__.py +1 -0
  92. package/python/foxclaw/browser/perception/dom_extractor.py +43 -0
  93. package/python/foxclaw/browser/perception/element_indexer.py +57 -0
  94. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  95. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  96. package/python/foxclaw/browser/service.py +148 -0
  97. package/python/foxclaw/browser/session/__init__.py +1 -0
  98. package/python/foxclaw/browser/session/session_manager.py +57 -0
  99. package/python/foxclaw/capability/__init__.py +12 -0
  100. package/python/foxclaw/capability/embedding_service.py +67 -0
  101. package/python/foxclaw/channels/__init__.py +140 -0
  102. package/python/foxclaw/channels/discord.py +184 -0
  103. package/python/foxclaw/channels/dispatcher.py +204 -0
  104. package/python/foxclaw/channels/feishu.py +225 -0
  105. package/python/foxclaw/channels/openclaw_bridge.py +156 -0
  106. package/python/foxclaw/channels/signing.py +187 -0
  107. package/python/foxclaw/channels/telegram.py +121 -0
  108. package/python/foxclaw/channels/weixin.py +225 -0
  109. package/python/foxclaw/cli.py +154 -0
  110. package/python/foxclaw/cognitive/__init__.py +4 -0
  111. package/python/foxclaw/cognitive/reasoner.py +82 -0
  112. package/python/foxclaw/cognitive/state.py +147 -0
  113. package/python/foxclaw/core/__init__.py +9 -0
  114. package/python/foxclaw/core/audit_chain.py +133 -0
  115. package/python/foxclaw/core/audit_sink.py +100 -0
  116. package/python/foxclaw/core/compaction.py +283 -0
  117. package/python/foxclaw/core/config.py +65 -0
  118. package/python/foxclaw/core/confirm_nonce.py +157 -0
  119. package/python/foxclaw/core/dispatch.py +27 -0
  120. package/python/foxclaw/core/envfile.py +48 -0
  121. package/python/foxclaw/core/failure_policy.py +151 -0
  122. package/python/foxclaw/core/gateway.py +85 -0
  123. package/python/foxclaw/core/governance_audit.py +175 -0
  124. package/python/foxclaw/core/idempotency.py +289 -0
  125. package/python/foxclaw/core/injection_guard.py +221 -0
  126. package/python/foxclaw/core/memory_adapter.py +516 -0
  127. package/python/foxclaw/core/metrics.py +89 -0
  128. package/python/foxclaw/core/model_router.py +1097 -0
  129. package/python/foxclaw/core/paths.py +103 -0
  130. package/python/foxclaw/core/recovery.py +362 -0
  131. package/python/foxclaw/core/scheduler.py +144 -0
  132. package/python/foxclaw/core/secrets.py +134 -0
  133. package/python/foxclaw/core/sessions.py +157 -0
  134. package/python/foxclaw/core/settings.py +430 -0
  135. package/python/foxclaw/core/shared_write.py +198 -0
  136. package/python/foxclaw/core/steering_queue.py +153 -0
  137. package/python/foxclaw/core/system_bus.py +84 -0
  138. package/python/foxclaw/core/thought_chain.py +318 -0
  139. package/python/foxclaw/core/token_meter.py +235 -0
  140. package/python/foxclaw/core/tools.py +172 -0
  141. package/python/foxclaw/core/tracing.py +390 -0
  142. package/python/foxclaw/core/truth_pipeline.py +114 -0
  143. package/python/foxclaw/core/vec_recall_adapter.py +503 -0
  144. package/python/foxclaw/core/verify.py +181 -0
  145. package/python/foxclaw/core/web_search.py +242 -0
  146. package/python/foxclaw/finance/__init__.py +15 -0
  147. package/python/foxclaw/finance/evidence.py +153 -0
  148. package/python/foxclaw/finance/fx.py +100 -0
  149. package/python/foxclaw/finance/graph.py +188 -0
  150. package/python/foxclaw/finance/models.py +150 -0
  151. package/python/foxclaw/finance/nlparse.py +575 -0
  152. package/python/foxclaw/finance/policy.py +329 -0
  153. package/python/foxclaw/finance/reasoner.py +129 -0
  154. package/python/foxclaw/finance/reasoning.py +340 -0
  155. package/python/foxclaw/finance/reconcile.py +180 -0
  156. package/python/foxclaw/finance/runtime.py +527 -0
  157. package/python/foxclaw/governance/__init__.py +24 -0
  158. package/python/foxclaw/governance/middleware.py +184 -0
  159. package/python/foxclaw/harness/__init__.py +17 -0
  160. package/python/foxclaw/harness/_base.py +72 -0
  161. package/python/foxclaw/harness/_policy.py +359 -0
  162. package/python/foxclaw/harness/drift.py +89 -0
  163. package/python/foxclaw/harness/event_bus.py +31 -0
  164. package/python/foxclaw/harness/harness.py +446 -0
  165. package/python/foxclaw/harness/loop_detection.py +152 -0
  166. package/python/foxclaw/itinerary/__init__.py +10 -0
  167. package/python/foxclaw/itinerary/amap_client.py +117 -0
  168. package/python/foxclaw/itinerary/models.py +80 -0
  169. package/python/foxclaw/itinerary/reasoner.py +101 -0
  170. package/python/foxclaw/itinerary/reasoning.py +174 -0
  171. package/python/foxclaw/itinerary/runtime.py +148 -0
  172. package/python/foxclaw/kernel/__init__.py +13 -0
  173. package/python/foxclaw/kernel/capabilities.py +73 -0
  174. package/python/foxclaw/kernel/policy_gate.py +114 -0
  175. package/python/foxclaw/kernel/process_manager.py +205 -0
  176. package/python/foxclaw/kernel/scheduler.py +131 -0
  177. package/python/foxclaw/kernel/sentinel.py +453 -0
  178. package/python/foxclaw/kernel/sentinel_client.py +246 -0
  179. package/python/foxclaw/kernel/spine.py +97 -0
  180. package/python/foxclaw/knowledge/__init__.py +376 -0
  181. package/python/foxclaw/knowledge/truth_crystallizer.py +92 -0
  182. package/python/foxclaw/learning/__init__.py +12 -0
  183. package/python/foxclaw/learning/knowledge_graph.py +149 -0
  184. package/python/foxclaw/learning/models.py +93 -0
  185. package/python/foxclaw/learning/reasoner.py +70 -0
  186. package/python/foxclaw/learning/runtime.py +363 -0
  187. package/python/foxclaw/learning/spaced_repetition.py +96 -0
  188. package/python/foxclaw/mcp_server.py +189 -0
  189. package/python/foxclaw/orchestration/__init__.py +184 -0
  190. package/python/foxclaw/pipeline/__init__.py +23 -0
  191. package/python/foxclaw/pipeline/cognitive_pipeline.py +481 -0
  192. package/python/foxclaw/pipeline/supervisor_stages.py +230 -0
  193. package/python/foxclaw/planning/__init__.py +17 -0
  194. package/python/foxclaw/planning/llm_planner.py +232 -0
  195. package/python/foxclaw/planning/planner.py +99 -0
  196. package/python/foxclaw/search/__init__.py +24 -0
  197. package/python/foxclaw/search/agent_port.py +194 -0
  198. package/python/foxclaw/search/evidence.py +113 -0
  199. package/python/foxclaw/search/external_search.py +50 -0
  200. package/python/foxclaw/search/hkc_read_port.py +60 -0
  201. package/python/foxclaw/search/internal_search.py +73 -0
  202. package/python/foxclaw/search/models.py +129 -0
  203. package/python/foxclaw/search/planner.py +44 -0
  204. package/python/foxclaw/search/retriever.py +39 -0
  205. package/python/foxclaw/search/runtime.py +134 -0
  206. package/python/foxclaw/search/strategy.py +44 -0
  207. package/python/foxclaw/skills/__init__.py +613 -0
  208. package/python/foxclaw/skills/hooks_spec.py +73 -0
  209. package/python/foxclaw/skills/lifecycle.py +278 -0
  210. package/python/foxclaw/skills/markdown_skill.py +174 -0
  211. package/python/foxclaw/skills/markdown_store.py +39 -0
  212. package/python/foxclaw/skills/mcp_client.py +190 -0
  213. package/python/foxclaw/skills/mcp_store.py +43 -0
  214. package/python/foxclaw/skills/permission.py +180 -0
  215. package/python/foxclaw/skills/pipeline.py +386 -0
  216. package/python/foxclaw/skills/policy.py +80 -0
  217. package/python/foxclaw/skills/review.py +72 -0
  218. package/python/foxclaw/skills/store.py +57 -0
  219. package/python/frontend/assets/core_bg.jpg +0 -0
  220. package/python/frontend/deck/auth.js +32 -0
  221. package/python/frontend/deck/chat_model_picker.js +537 -0
  222. package/python/frontend/deck/domain.js +296 -0
  223. package/python/frontend/deck/main.js +1950 -0
  224. package/python/frontend/deck/markdown.js +48 -0
  225. package/python/frontend/deck/settings.js +324 -0
  226. package/python/frontend/deck/state.js +6 -0
  227. package/python/frontend/deck/util.js +6 -0
  228. package/python/frontend/deck/visuals.js +212 -0
  229. package/python/frontend/deck.css +855 -0
  230. package/python/frontend/deck.html +686 -0
  231. package/python/frontend/deck_v2.html +341 -0
  232. package/python/frontend/planet/index.html +266 -0
  233. package/python/frontend/planet/planet.css +579 -0
  234. package/python/frontend/planet/planet.js +1637 -0
  235. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  236. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  237. package/python/frontend/v3/assets/index-BD7VPbMV.js +33 -0
  238. package/python/frontend/v3/assets/index-CAvnYWGH.css +1 -0
  239. package/python/frontend/v3/index.html +15 -0
  240. package/python/frontend/widget_runtime.js +295 -0
  241. package/python/frontend/widgets.js +696 -0
  242. package/python/frontend_src/index.html +14 -0
  243. package/python/frontend_src/package-lock.json +1376 -0
  244. package/python/frontend_src/package.json +19 -0
  245. package/python/frontend_src/src/App.vue +62 -0
  246. package/python/frontend_src/src/api.js +89 -0
  247. package/python/frontend_src/src/components/ChatDock.vue +377 -0
  248. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  249. package/python/frontend_src/src/components/DocViewer.vue +171 -0
  250. package/python/frontend_src/src/components/Inspector.vue +241 -0
  251. package/python/frontend_src/src/components/LeftRail.vue +394 -0
  252. package/python/frontend_src/src/components/MenuPopover.vue +130 -0
  253. package/python/frontend_src/src/components/MessageStream.vue +401 -0
  254. package/python/frontend_src/src/components/ModelPicker.vue +355 -0
  255. package/python/frontend_src/src/components/QuickIngest.vue +118 -0
  256. package/python/frontend_src/src/components/Stage.vue +226 -0
  257. package/python/frontend_src/src/components/ToolMenu.vue +144 -0
  258. package/python/frontend_src/src/components/TopBar.vue +90 -0
  259. package/python/frontend_src/src/components/panels/ActivityPanel.vue +84 -0
  260. package/python/frontend_src/src/components/panels/AgentsPanel.vue +86 -0
  261. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +441 -0
  262. package/python/frontend_src/src/components/panels/MemoryPanel.vue +118 -0
  263. package/python/frontend_src/src/components/panels/PanelBox.vue +51 -0
  264. package/python/frontend_src/src/components/panels/ReviewPanel.vue +308 -0
  265. package/python/frontend_src/src/components/panels/SettingsPanel.vue +254 -0
  266. package/python/frontend_src/src/components/panels/TasksPanel.vue +74 -0
  267. package/python/frontend_src/src/components/panels/ToolsPanel.vue +640 -0
  268. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  269. package/python/frontend_src/src/lib/fmt.js +48 -0
  270. package/python/frontend_src/src/lib/md.js +204 -0
  271. package/python/frontend_src/src/main.js +7 -0
  272. package/python/frontend_src/src/stores/app.js +106 -0
  273. package/python/frontend_src/src/stores/chat.js +484 -0
  274. package/python/frontend_src/src/stores/model.js +104 -0
  275. package/python/frontend_src/src/stores/panels.js +330 -0
  276. package/python/frontend_src/src/styles/base.css +138 -0
  277. package/python/frontend_src/src/styles/panel.css +117 -0
  278. package/python/frontend_src/src/styles/tokens.css +78 -0
  279. package/python/frontend_src/vite.config.js +46 -0
  280. package/python/pyproject.toml +265 -0
  281. package/python/skills/README.md +123 -0
  282. package/python/skills/execution-contract/SKILL.md +30 -0
  283. package/python/skills/general-assistant/SKILL.md +34 -0
  284. package/python/skills/planner/SKILL.md +33 -0
  285. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  286. package/python/skills/skill-composition/SKILL.md +31 -0
  287. package/python/skills/skill-guard/SKILL.md +67 -0
  288. package/python/skills/skill-memory/SKILL.md +50 -0
  289. package/python/skills/skill-router/SKILL.md +35 -0
  290. package/python/tests/conftest.py +76 -0
  291. package/python/tests/run_all.py +263 -0
  292. package/python/tests/test_agent_lifecycle.py +360 -0
  293. package/python/tests/test_ahs_p2.py +414 -0
  294. package/python/tests/test_anthropic_tools.py +206 -0
  295. package/python/tests/test_api.py +87 -0
  296. package/python/tests/test_api_auth.py +83 -0
  297. package/python/tests/test_api_host_guard.py +69 -0
  298. package/python/tests/test_attachments.py +167 -0
  299. package/python/tests/test_audit_chain.py +121 -0
  300. package/python/tests/test_audit_sink.py +136 -0
  301. package/python/tests/test_browser.py +153 -0
  302. package/python/tests/test_browser_request_guard.py +184 -0
  303. package/python/tests/test_capabilities.py +158 -0
  304. package/python/tests/test_channel_signing.py +192 -0
  305. package/python/tests/test_channel_trust.py +109 -0
  306. package/python/tests/test_channels.py +184 -0
  307. package/python/tests/test_cli.py +46 -0
  308. package/python/tests/test_compaction_toolpair.py +119 -0
  309. package/python/tests/test_config_layering.py +65 -0
  310. package/python/tests/test_confirm_nonce.py +197 -0
  311. package/python/tests/test_context_compaction_wiring.py +130 -0
  312. package/python/tests/test_dispatch.py +95 -0
  313. package/python/tests/test_domain_evals.py +403 -0
  314. package/python/tests/test_domain_import.py +78 -0
  315. package/python/tests/test_domain_reasoners.py +148 -0
  316. package/python/tests/test_e2e.py +178 -0
  317. package/python/tests/test_emb_cache.py +107 -0
  318. package/python/tests/test_embedding_singleton.py +89 -0
  319. package/python/tests/test_envfile.py +83 -0
  320. package/python/tests/test_evals_smoke.py +114 -0
  321. package/python/tests/test_event_spine.py +182 -0
  322. package/python/tests/test_evidence_semantic.py +101 -0
  323. package/python/tests/test_evidence_sse.py +97 -0
  324. package/python/tests/test_file_ingest.py +146 -0
  325. package/python/tests/test_finance.py +153 -0
  326. package/python/tests/test_finance_ledger.py +252 -0
  327. package/python/tests/test_finance_persist.py +178 -0
  328. package/python/tests/test_gateway.py +144 -0
  329. package/python/tests/test_governance.py +468 -0
  330. package/python/tests/test_governance_audit.py +359 -0
  331. package/python/tests/test_governance_middleware.py +143 -0
  332. package/python/tests/test_hkc.py +286 -0
  333. package/python/tests/test_hkc_index_build_manager.py +339 -0
  334. package/python/tests/test_hkc_manifest_reuse.py +317 -0
  335. package/python/tests/test_hkc_search_event_sync.py +533 -0
  336. package/python/tests/test_import_path_guard.py +161 -0
  337. package/python/tests/test_injection_guard.py +211 -0
  338. package/python/tests/test_integration.py +141 -0
  339. package/python/tests/test_itinerary.py +103 -0
  340. package/python/tests/test_kee_semantic.py +89 -0
  341. package/python/tests/test_kernel.py +222 -0
  342. package/python/tests/test_learning.py +110 -0
  343. package/python/tests/test_llm_admin.py +160 -0
  344. package/python/tests/test_llm_planner.py +366 -0
  345. package/python/tests/test_markdown_skills.py +73 -0
  346. package/python/tests/test_markdown_skills_openclaw.py +71 -0
  347. package/python/tests/test_mcp_connectors.py +109 -0
  348. package/python/tests/test_mcp_guardian_discovery.py +91 -0
  349. package/python/tests/test_mcp_no_memory.py +43 -0
  350. package/python/tests/test_mcp_policy_gate.py +77 -0
  351. package/python/tests/test_mcp_server.py +95 -0
  352. package/python/tests/test_memory_recall.py +95 -0
  353. package/python/tests/test_metrics.py +66 -0
  354. package/python/tests/test_multi_chain.py +102 -0
  355. package/python/tests/test_multimodal.py +135 -0
  356. package/python/tests/test_negotiation.py +146 -0
  357. package/python/tests/test_orchestration.py +119 -0
  358. package/python/tests/test_orchestration_decoupled.py +109 -0
  359. package/python/tests/test_p3_p5_hardening.py +338 -0
  360. package/python/tests/test_parallel_dispatch.py +215 -0
  361. package/python/tests/test_pipeline.py +533 -0
  362. package/python/tests/test_plan_visible.py +86 -0
  363. package/python/tests/test_planner.py +95 -0
  364. package/python/tests/test_policy_gate.py +109 -0
  365. package/python/tests/test_reasoner.py +185 -0
  366. package/python/tests/test_release_check.py +263 -0
  367. package/python/tests/test_resume_checkpoint.py +156 -0
  368. package/python/tests/test_routing.py +95 -0
  369. package/python/tests/test_scheduler.py +141 -0
  370. package/python/tests/test_search.py +204 -0
  371. package/python/tests/test_secrets.py +159 -0
  372. package/python/tests/test_semantic.py +78 -0
  373. package/python/tests/test_sessions.py +91 -0
  374. package/python/tests/test_settings.py +281 -0
  375. package/python/tests/test_shadow_learning.py +330 -0
  376. package/python/tests/test_shared_write.py +207 -0
  377. package/python/tests/test_skill_taint_gate.py +154 -0
  378. package/python/tests/test_skills.py +133 -0
  379. package/python/tests/test_stats_and_events.py +165 -0
  380. package/python/tests/test_streaming.py +60 -0
  381. package/python/tests/test_supervisor_pipeline.py +177 -0
  382. package/python/tests/test_supervisor_triage.py +108 -0
  383. package/python/tests/test_taint.py +117 -0
  384. package/python/tests/test_task_stream.py +85 -0
  385. package/python/tests/test_thinking_sse.py +67 -0
  386. package/python/tests/test_thinking_stream.py +76 -0
  387. package/python/tests/test_tool_center.py +129 -0
  388. package/python/tests/test_tool_parallel.py +113 -0
  389. package/python/tests/test_tool_params.py +103 -0
  390. package/python/tests/test_tool_use.py +242 -0
  391. package/python/tests/test_toplevel_concurrency.py +117 -0
  392. package/python/tests/test_tracing.py +290 -0
  393. package/python/tests/test_truth_crystallizer.py +127 -0
  394. package/python/tests/test_truth_flow.py +93 -0
  395. package/python/tests/test_url_policy.py +53 -0
  396. package/python/tests/test_verify_evidence.py +225 -0
  397. package/python/tests/test_version_sync.py +71 -0
  398. package/python/tests/test_web_trigger.py +99 -0
  399. package/python/tools/check_faiss_type.py +31 -0
  400. package/python/tools/check_log.ps1 +46 -0
  401. package/python/tools/ivf_status.py +72 -0
  402. package/python/tools/mypy_baseline.txt +5 -0
  403. package/python/tools/mypy_ratchet.py +97 -0
  404. package/python/tools/pack.py +178 -0
  405. package/python/tools/release_check.py +349 -0
  406. package/python/tools/restart_foxclaw.ps1 +74 -0
  407. package/python/tools/shared_write_audit.py +204 -0
  408. package/python/tools/size_baseline.txt +28 -0
  409. package/python/tools/size_check.py +102 -0
  410. package/python/tools/verify_env.bat +2 -0
  411. package/python/tools/verify_env.py +25 -0
  412. package/python/vendor/HMR_LICENSE +37 -0
  413. package/python/vendor/HMR_README.md +65 -0
  414. package/python/vendor/hkc/LICENSE +37 -0
  415. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  416. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  417. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  418. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  419. package/python/vendor/hkc/hkc_api/container.py +416 -0
  420. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  421. package/python/vendor/hkc/hkc_api/main.py +157 -0
  422. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  423. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  424. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  425. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  426. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  427. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  428. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  429. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  430. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  431. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  432. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  433. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  434. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  435. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  436. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  437. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  438. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  439. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  440. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  441. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  442. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  443. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  444. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  445. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  446. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  447. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  448. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  449. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  450. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  451. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  452. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  453. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  454. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  455. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  456. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  457. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  458. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  459. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  460. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  461. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  462. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  463. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  464. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  465. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  466. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  467. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  468. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  469. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  470. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  471. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  472. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  473. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  474. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  475. package/python/vendor/hmr/__init__.py +0 -0
  476. package/python/vendor/hmr/core/__init__.py +0 -0
  477. package/python/vendor/hmr/core/hmr.py +684 -0
  478. package/python/vendor/hmr/core/models.py +310 -0
  479. package/python/vendor/hmr/engines/__init__.py +0 -0
  480. package/python/vendor/hmr/engines/evolution.py +710 -0
  481. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  482. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  483. package/python/vendor/hmr/engines/policy.py +625 -0
  484. package/python/vendor/hmr/engines/recall.py +260 -0
  485. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  486. package/python/vendor/hmr/engines/scheduler.py +323 -0
  487. package/python/vendor/hmr/engines/semantic.py +150 -0
  488. package/python/vendor/hmr/engines/temporal.py +261 -0
  489. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  490. package/python/vendor/hmr/graph/__init__.py +0 -0
  491. package/python/vendor/hmr/graph/cwg.py +338 -0
  492. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  493. package/python/vendor/hmr/storage/__init__.py +0 -0
  494. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  495. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,205 @@
1
+ """
2
+ hkc-kep / event_bus.py
3
+ Knowledge Event Protocol — 事件总线。
4
+
5
+ v1:进程内同步执行,零依赖。
6
+ v2:可替换为 Redis Pub/Sub,接口不变。
7
+
8
+ 所有事件统一格式:
9
+ {
10
+ "event_id": "EVT_00000001",
11
+ "event": "knowledge.created",
12
+ "version": "1.0",
13
+ "timestamp": "2025-01-01T00:00:00Z",
14
+ "source": "KDE",
15
+ "payload": { ... }
16
+ }
17
+ """
18
+ import json
19
+ import logging
20
+ import sqlite3
21
+ import threading
22
+
23
+ logger = logging.getLogger(__name__)
24
+ from collections import defaultdict
25
+ from datetime import datetime, timezone
26
+ from pathlib import Path
27
+ from typing import Callable
28
+
29
+
30
+ # ── 事件类型常量 ─────────────────────────────────────────────
31
+
32
+ class KEPEvents:
33
+ # KDE 产出
34
+ KNOWLEDGE_CREATED = "knowledge.created"
35
+ KNOWLEDGE_UPDATED = "knowledge.updated"
36
+ KNOWLEDGE_MERGED = "knowledge.merged"
37
+ KNOWLEDGE_DELETED = "knowledge.deleted"
38
+
39
+ # KEE 产出
40
+ CONFLICT_DETECTED = "conflict.detected"
41
+ CONFLICT_RESOLVED = "conflict.resolved"
42
+ CLAIM_STATUS_CHANGED = "claim.status_changed"
43
+
44
+ # ACE 产出
45
+ ABILITY_CREATED = "ability.created"
46
+ ABILITY_UPDATED = "ability.updated"
47
+
48
+ # HMR 来源
49
+ MEMORY_CREATED = "memory.created"
50
+ MEMORY_UPDATED = "memory.updated"
51
+ MEMORY_DELETED = "memory.deleted"
52
+
53
+ # HAR 来源
54
+ AGENT_USED_ABILITY = "agent.used_ability" # Feedback Loop 入口
55
+
56
+
57
+ # ── EventBus ─────────────────────────────────────────────────
58
+
59
+ class EventBus:
60
+ """
61
+ 线程安全的进程内事件总线。
62
+ 事件审计日志写入 SQLite,方便调试和回放。
63
+ """
64
+
65
+ def __init__(self, db_path: str = ":memory:"):
66
+ self._lock = threading.RLock()
67
+ self._handlers: dict[str, list[Callable]] = defaultdict(list)
68
+ self._counter = 0
69
+
70
+ # 审计日志
71
+ log_path = Path(db_path)
72
+ if db_path != ":memory:":
73
+ log_path.parent.mkdir(parents=True, exist_ok=True)
74
+ self._log_db = sqlite3.connect(str(db_path), check_same_thread=False)
75
+ self._init_log_table()
76
+
77
+ def _init_log_table(self):
78
+ self._log_db.execute("""
79
+ CREATE TABLE IF NOT EXISTS event_log (
80
+ event_id TEXT PRIMARY KEY,
81
+ event TEXT NOT NULL,
82
+ source TEXT,
83
+ payload TEXT,
84
+ created_at TEXT
85
+ )
86
+ """)
87
+ self._log_db.commit()
88
+
89
+ def _gen_id(self) -> str:
90
+ with self._lock:
91
+ self._counter += 1
92
+ return f"EVT_{self._counter:08d}"
93
+
94
+ # ── 订阅 ─────────────────────────────────────────────────
95
+
96
+ def subscribe(self, event: str, handler: Callable) -> None:
97
+ """
98
+ 注册事件处理器。
99
+ 同一事件可注册多个处理器,按注册顺序执行。
100
+ """
101
+ with self._lock:
102
+ self._handlers[event].append(handler)
103
+
104
+ def unsubscribe(self, event: str, handler: Callable) -> None:
105
+ with self._lock:
106
+ handlers = self._handlers.get(event, [])
107
+ if handler in handlers:
108
+ handlers.remove(handler)
109
+
110
+ # ── 发布 ─────────────────────────────────────────────────
111
+
112
+ def publish(self, event_dict: dict) -> str:
113
+ """
114
+ 发布事件,同步调用所有已注册处理器。
115
+ 返回 event_id。
116
+ 处理器异常不阻断后续处理器执行。
117
+ """
118
+ event_id = self._gen_id()
119
+ event_dict = {
120
+ "event_id": event_id,
121
+ "version": "1.0",
122
+ "timestamp": datetime.now(timezone.utc).isoformat(),
123
+ **event_dict,
124
+ }
125
+
126
+ event_type = event_dict.get("event", "")
127
+ with self._lock:
128
+ handlers = list(self._handlers.get(event_type, []))
129
+
130
+ # 写审计日志(处理器不在锁内执行,避免长时间持锁/重入死锁)
131
+ self._write_log(event_dict)
132
+
133
+ # 调用处理器
134
+ for handler in handlers:
135
+ try:
136
+ handler(event_dict)
137
+ except Exception:
138
+ # 记录错误但不中断其他处理器
139
+ logger.error("Handler error for %s", event_type, exc_info=True)
140
+
141
+ return event_id
142
+
143
+ def _write_log(self, event_dict: dict):
144
+ try:
145
+ # 加锁:并发 publish(两个摄入任务)会从多个线程共用这一个连接,
146
+ # 不加锁 sqlite3 会 "recursive use of cursors"/损坏。与 GraphStore 一致。
147
+ with self._lock:
148
+ self._log_db.execute(
149
+ "INSERT OR IGNORE INTO event_log "
150
+ "(event_id,event,source,payload,created_at) VALUES (?,?,?,?,?)",
151
+ (
152
+ event_dict.get("event_id"),
153
+ event_dict.get("event"),
154
+ event_dict.get("source"),
155
+ json.dumps(event_dict.get("payload", {})),
156
+ event_dict.get("timestamp"),
157
+ )
158
+ )
159
+ self._log_db.commit()
160
+ except Exception:
161
+ pass # 日志失败不影响主流程
162
+
163
+ # ── 查询日志 ─────────────────────────────────────────────
164
+
165
+ def recent_events(self, limit: int = 50) -> list[dict]:
166
+ with self._lock:
167
+ rows = self._log_db.execute(
168
+ "SELECT * FROM event_log ORDER BY created_at DESC LIMIT ?",
169
+ (limit,)
170
+ ).fetchall()
171
+ return [
172
+ {
173
+ "event_id": r[0],
174
+ "event": r[1],
175
+ "source": r[2],
176
+ "payload": json.loads(r[3] or "{}"),
177
+ "created_at": r[4],
178
+ }
179
+ for r in rows
180
+ ]
181
+
182
+ def events_by_type(self, event_type: str, limit: int = 20) -> list[dict]:
183
+ with self._lock:
184
+ rows = self._log_db.execute(
185
+ "SELECT * FROM event_log WHERE event=? "
186
+ "ORDER BY created_at DESC LIMIT ?",
187
+ (event_type, limit)
188
+ ).fetchall()
189
+ return [
190
+ {
191
+ "event_id": r[0],
192
+ "event": r[1],
193
+ "source": r[2],
194
+ "payload": json.loads(r[3] or "{}"),
195
+ "created_at": r[4],
196
+ }
197
+ for r in rows
198
+ ]
199
+
200
+ def close(self) -> None:
201
+ """显式关闭事件日志的 SQLite 连接。容器 close/reset 时必须调,
202
+ 否则 events.db 句柄泄漏——Windows 下导致数据目录删不掉(WinError 32)、
203
+ 长期运行累积文件描述符泄漏。与 GraphStore.close 一致,带锁关闭。"""
204
+ with self._lock:
205
+ self._log_db.close()
@@ -0,0 +1,63 @@
1
+ """
2
+ hkc_sdk
3
+ Foxclaw Knowledge Core — Python SDK
4
+
5
+ 统一入口 connect(),两种后端无感切换:
6
+
7
+ from hkc_sdk import connect
8
+
9
+ # 远程 HTTP(HKC 独立部署)
10
+ hkc = connect("http://localhost:8000")
11
+
12
+ # 进程内直连(HAR 与 HKC 同进程)
13
+ from hkc_api.container import HKCContainer
14
+ hkc = connect(container=HKCContainer(data_dir="./data"))
15
+
16
+ # 用法完全一样
17
+ hkc.ingest_text("巴菲特认为价值投资长期跑赢市场", domain="Investment")
18
+ hits = hkc.search("价值投资")
19
+ ability = hkc.ensure_ability("quant_analyst")
20
+ """
21
+ from .base import BaseClient
22
+ from .http_client import HTTPClient
23
+ from .direct_client import DirectClient
24
+ from .models import (
25
+ KU, SearchHit, Ability, Skill, CoverageReport, Conflict, IngestResult,
26
+ )
27
+ from .exceptions import (
28
+ HKCError, HKCNotFoundError, HKCInsufficientCoverage,
29
+ HKCBadRequest, HKCServerError,
30
+ )
31
+
32
+ __version__ = "1.0.0"
33
+
34
+ __all__ = [
35
+ "connect",
36
+ "BaseClient", "HTTPClient", "DirectClient",
37
+ "KU", "SearchHit", "Ability", "Skill",
38
+ "CoverageReport", "Conflict", "IngestResult",
39
+ "HKCError", "HKCNotFoundError", "HKCInsufficientCoverage",
40
+ "HKCBadRequest", "HKCServerError",
41
+ ]
42
+
43
+
44
+ def connect(
45
+ base_url: str = None,
46
+ *,
47
+ container=None,
48
+ timeout: int = 60,
49
+ ) -> BaseClient:
50
+ """
51
+ 创建 HKC 客户端。
52
+
53
+ 二选一:
54
+ connect("http://localhost:8000") → HTTPClient
55
+ connect(container=my_container) → DirectClient
56
+
57
+ 两者都返回 BaseClient,方法签名一致。
58
+ """
59
+ if container is not None:
60
+ return DirectClient(container)
61
+ if base_url is not None:
62
+ return HTTPClient(base_url, timeout=timeout)
63
+ raise ValueError("必须提供 base_url(HTTP)或 container(直连)之一")
@@ -0,0 +1,151 @@
1
+ """
2
+ hkc_sdk / base.py
3
+ SDK 客户端抽象接口。
4
+
5
+ 两种实现共享这套方法签名:
6
+ HTTPClient 通过 REST 调用 hkc-api(远程/跨进程)
7
+ DirectClient 直接调 HKCContainer 的引擎(进程内)
8
+
9
+ 用户代码只依赖 BaseClient,底层随时可换:
10
+ from hkc_sdk import connect
11
+ hkc = connect("http://localhost:8000") # HTTP
12
+ hkc = connect(container=my_container) # 直连
13
+ hits = hkc.search("价值投资") # 两种写法完全一样
14
+ """
15
+ from __future__ import annotations
16
+ from abc import ABC, abstractmethod
17
+
18
+ from .models import (
19
+ KU, SearchHit, Ability, CoverageReport, Conflict, IngestResult,
20
+ )
21
+
22
+
23
+ class BaseClient(ABC):
24
+
25
+ # ── 知识摄入 ─────────────────────────────────────────────
26
+
27
+ @abstractmethod
28
+ def ingest_text(
29
+ self,
30
+ text: str,
31
+ source_title: str = "",
32
+ domain: str = "",
33
+ source: str = "inline",
34
+ source_year: int = 0,
35
+ ) -> IngestResult:
36
+ """摄入文本,返回写入的 KU 信息。"""
37
+ ...
38
+
39
+ @abstractmethod
40
+ def ingest_url(
41
+ self,
42
+ url: str,
43
+ source_title: str = "",
44
+ domain: str = "",
45
+ source_year: int = 0,
46
+ ) -> IngestResult:
47
+ """摄入网页 URL。"""
48
+ ...
49
+
50
+ # ── KU 查询 ──────────────────────────────────────────────
51
+
52
+ @abstractmethod
53
+ def get_ku(self, ku_id: str) -> KU:
54
+ """按 ID 获取 KU,不存在抛 HKCNotFoundError。"""
55
+ ...
56
+
57
+ @abstractmethod
58
+ def list_by_domain(self, domain: str, limit: int = 50) -> list[KU]:
59
+ """列出某领域的所有 KU。"""
60
+ ...
61
+
62
+ @abstractmethod
63
+ def neighbors(self, ku_id: str, max_depth: int = 2, top_k: int = 20) -> list[SearchHit]:
64
+ """获取 KU 的图谱邻居。"""
65
+ ...
66
+
67
+ # ── 搜索 ─────────────────────────────────────────────────
68
+
69
+ @abstractmethod
70
+ def search(
71
+ self,
72
+ query: str,
73
+ mode: str = "hybrid",
74
+ top_k: int = 10,
75
+ domain: str = "",
76
+ ku_types: list[str] | None = None,
77
+ ) -> list[SearchHit]:
78
+ """统一搜索。mode: bm25 | vector | graph | hybrid。"""
79
+ ...
80
+
81
+ @abstractmethod
82
+ def find_path(self, from_id: str, to_id: str) -> list[str]:
83
+ """两 KU 间最短路径。"""
84
+ ...
85
+
86
+ # ── 能力(ACE)───────────────────────────────────────────
87
+
88
+ @abstractmethod
89
+ def list_abilities(self) -> list[str]:
90
+ """列出所有可编译的 Ability key。"""
91
+ ...
92
+
93
+ @abstractmethod
94
+ def coverage_report(self, ability_key: str) -> CoverageReport:
95
+ """查看某 Ability 的知识覆盖情况(不编译)。"""
96
+ ...
97
+
98
+ @abstractmethod
99
+ def compile_ability(self, ability_key: str) -> Ability:
100
+ """
101
+ 编译能力包。覆盖度不足时抛 HKCInsufficientCoverage。
102
+ """
103
+ ...
104
+
105
+ @abstractmethod
106
+ def get_ability(self, ability_key: str) -> Ability:
107
+ """加载已编译的能力包,未编译抛 HKCNotFoundError。"""
108
+ ...
109
+
110
+ # ── 冲突(KEE)───────────────────────────────────────────
111
+
112
+ @abstractmethod
113
+ def list_conflicts(self, status: str = "open", domain: str = "") -> list[Conflict]:
114
+ """列出冲突卡。"""
115
+ ...
116
+
117
+ @abstractmethod
118
+ def resolve_conflict(
119
+ self,
120
+ conflict_id: str,
121
+ winner_id: str,
122
+ note: str = "",
123
+ resolved_by: str = "sdk",
124
+ ) -> bool:
125
+ """人工裁决冲突。"""
126
+ ...
127
+
128
+ # ── 系统 ─────────────────────────────────────────────────
129
+
130
+ @abstractmethod
131
+ def stats(self) -> dict:
132
+ """系统统计。"""
133
+ ...
134
+
135
+ @abstractmethod
136
+ def health(self) -> bool:
137
+ """检查 HKC 是否可用。"""
138
+ ...
139
+
140
+ # ── 便捷方法(默认实现,子类无需重写)────────────────────
141
+
142
+ def ensure_ability(self, ability_key: str) -> Ability:
143
+ """
144
+ 获取能力包,若未编译则尝试编译。
145
+ 覆盖度足够时返回 Ability,不足时抛 HKCInsufficientCoverage。
146
+ """
147
+ from .exceptions import HKCNotFoundError
148
+ try:
149
+ return self.get_ability(ability_key)
150
+ except HKCNotFoundError:
151
+ return self.compile_ability(ability_key)
@@ -0,0 +1,153 @@
1
+ """
2
+ hkc_sdk / direct_client.py
3
+ 直连客户端:直接调用 HKCContainer 的引擎,不走 HTTP。
4
+
5
+ 用于 HAR 和 HKC 跑在同一进程的场景,省掉网络序列化开销。
6
+ 方法签名与 HTTPClient 完全一致,上层代码无感切换。
7
+ """
8
+ from __future__ import annotations
9
+ import logging
10
+
11
+ from .base import BaseClient
12
+ from .models import (
13
+ KU, SearchHit, Ability, CoverageReport, Conflict, IngestResult,
14
+ )
15
+ from .exceptions import (
16
+ HKCNotFoundError, HKCInsufficientCoverage, HKCBadRequest,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class DirectClient(BaseClient):
23
+
24
+ def __init__(self, container):
25
+ """
26
+ container: 已 assemble 的 HKCContainer 实例。
27
+ """
28
+ if not getattr(container, "_assembled", False):
29
+ container.assemble()
30
+ self.c = container
31
+
32
+ # ── 知识摄入 ─────────────────────────────────────────────
33
+
34
+ def ingest_text(self, text, source_title="", domain="",
35
+ source="inline", source_year=0) -> IngestResult:
36
+ kus = self.c.kde.ingest_text(
37
+ text=text, source=source, source_title=source_title,
38
+ source_year=source_year, domain=domain,
39
+ )
40
+ return self._ingest_result(kus)
41
+
42
+ def ingest_url(self, url, source_title="", domain="", source_year=0) -> IngestResult:
43
+ kus = self.c.kde.ingest_url(
44
+ url=url, source_title=source_title,
45
+ source_year=source_year, domain=domain,
46
+ )
47
+ return self._ingest_result(kus)
48
+
49
+ def _ingest_result(self, kus) -> IngestResult:
50
+ counts = {}
51
+ for ku in kus:
52
+ t = ku.ku_type.value
53
+ counts[t] = counts.get(t, 0) + 1
54
+ return IngestResult(
55
+ ku_count=len(kus),
56
+ ku_ids=[ku.ku_id for ku in kus],
57
+ counts=counts,
58
+ )
59
+
60
+ # ── KU 查询 ──────────────────────────────────────────────
61
+
62
+ def get_ku(self, ku_id) -> KU:
63
+ ku = self.c.graph_store.get(ku_id)
64
+ if not ku:
65
+ raise HKCNotFoundError(f"KU 不存在: {ku_id}")
66
+ return KU.from_dict(ku.to_dict())
67
+
68
+ def list_by_domain(self, domain, limit=50) -> list[KU]:
69
+ kus = self.c.graph_store.query_by_domain(domain, limit=limit)
70
+ return [KU.from_dict(ku.to_dict()) for ku in kus]
71
+
72
+ def neighbors(self, ku_id, max_depth=2, top_k=20) -> list[SearchHit]:
73
+ ku = self.c.graph_store.get(ku_id)
74
+ if not ku:
75
+ raise HKCNotFoundError(f"KU 不存在: {ku_id}")
76
+ hits = self.c.search.search_neighbors(ku_id, max_depth=max_depth, top_k=top_k)
77
+ return [
78
+ SearchHit(ku_id=h.ku_id, score=h.score, name=h.name,
79
+ summary=h.summary, mode="graph")
80
+ for h in hits
81
+ ]
82
+
83
+ # ── 搜索 ─────────────────────────────────────────────────
84
+
85
+ def search(self, query, mode="hybrid", top_k=10, domain="",
86
+ ku_types=None) -> list[SearchHit]:
87
+ if mode not in ("bm25", "vector", "graph", "hybrid"):
88
+ raise HKCBadRequest(f"无效 mode: {mode}")
89
+ results = self.c.search.search(
90
+ query=query, mode=mode, top_k=top_k,
91
+ domain=domain, ku_types=ku_types,
92
+ )
93
+ return [
94
+ SearchHit(ku_id=r.ku_id, score=r.score, name=r.name,
95
+ summary=r.summary, mode=r.mode, ku_type=r.ku_type)
96
+ for r in results
97
+ ]
98
+
99
+ def find_path(self, from_id, to_id) -> list[str]:
100
+ return self.c.search.find_path(from_id, to_id)
101
+
102
+ # ── 能力 ─────────────────────────────────────────────────
103
+
104
+ def list_abilities(self) -> list[str]:
105
+ return self.c.ace.list_available()
106
+
107
+ def coverage_report(self, ability_key) -> CoverageReport:
108
+ report = self.c.ace.coverage_report(ability_key)
109
+ if not report:
110
+ raise HKCNotFoundError(f"未知 Ability: {ability_key}")
111
+ return CoverageReport.from_dict(report)
112
+
113
+ def compile_ability(self, ability_key) -> Ability:
114
+ try:
115
+ pkg = self.c.ace.compile(ability_key)
116
+ except ValueError as e:
117
+ raise HKCNotFoundError(str(e))
118
+ if pkg is None:
119
+ report = self.c.ace.coverage_report(ability_key)
120
+ raise HKCInsufficientCoverage(
121
+ "知识覆盖度不足,无法编译此 Ability",
122
+ {"coverage": report.get("coverage", {}),
123
+ "missing_skills": report.get("missing_skills", [])},
124
+ )
125
+ return Ability.from_dict(pkg.to_dict())
126
+
127
+ def get_ability(self, ability_key) -> Ability:
128
+ pkg = self.c.ace.load_ability(ability_key)
129
+ if not pkg:
130
+ raise HKCNotFoundError(f"Ability 尚未编译: {ability_key}")
131
+ return Ability.from_dict(pkg.to_dict())
132
+
133
+ # ── 冲突 ─────────────────────────────────────────────────
134
+
135
+ def list_conflicts(self, status="open", domain="") -> list[Conflict]:
136
+ cards = self.c.graph_store.list_conflicts(status=status, domain=domain)
137
+ return [Conflict.from_dict(card.to_dict()) for card in cards]
138
+
139
+ def resolve_conflict(self, conflict_id, winner_id, note="",
140
+ resolved_by="sdk") -> bool:
141
+ return self.c.kee.manual_resolve(
142
+ conflict_id=conflict_id, winner_id=winner_id,
143
+ note=note, resolved_by=resolved_by,
144
+ )
145
+
146
+ # ── 系统 ─────────────────────────────────────────────────
147
+
148
+ def stats(self) -> dict:
149
+ return self.c.stats()
150
+
151
+ def health(self) -> bool:
152
+ """直连模式:容器已装配即健康。"""
153
+ return getattr(self.c, "_assembled", False)
@@ -0,0 +1,39 @@
1
+ """
2
+ hkc_sdk / exceptions.py
3
+ SDK 异常体系。
4
+
5
+ 让用户能用一致的方式捕获错误,不管底层是 HTTP 还是直连:
6
+ HKCError 基类
7
+ ├── HKCNotFoundError 资源不存在(404)
8
+ ├── HKCInsufficientCoverage 能力覆盖度不足(422)
9
+ ├── HKCBadRequest 请求参数错误(400 / 413)
10
+ └── HKCServerError 服务内部错误(500 / 连接失败)
11
+ """
12
+
13
+
14
+ class HKCError(Exception):
15
+ """所有 SDK 异常的基类。"""
16
+ def __init__(self, message: str, detail=None):
17
+ super().__init__(message)
18
+ self.message = message
19
+ self.detail = detail
20
+
21
+
22
+ class HKCNotFoundError(HKCError):
23
+ """请求的资源不存在(KU / Ability / Conflict)。"""
24
+
25
+
26
+ class HKCInsufficientCoverage(HKCError):
27
+ """编译能力时知识覆盖度不足。detail 含 missing_skills。"""
28
+ def __init__(self, message: str, detail=None):
29
+ super().__init__(message, detail)
30
+ self.missing_skills = (detail or {}).get("missing_skills", [])
31
+ self.coverage = (detail or {}).get("coverage", {})
32
+
33
+
34
+ class HKCBadRequest(HKCError):
35
+ """请求参数错误或请求体过大。"""
36
+
37
+
38
+ class HKCServerError(HKCError):
39
+ """服务内部错误或连接失败。"""