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.
- package/README.md +54 -29
- package/bin/foxclaw.cjs +51 -0
- package/index.js +11 -10
- package/package.json +46 -38
- package/python/.dockerignore +35 -0
- package/python/.env.example +165 -0
- package/python/.gitattributes +46 -0
- package/python/.github/workflows/ci.yml +122 -0
- package/python/.github/workflows/release.yml +183 -0
- package/python/Dockerfile +64 -0
- package/python/LICENSE +21 -0
- package/python/MANIFEST.in +14 -0
- package/python/README.md +192 -0
- package/python/README_CN.md +324 -0
- package/python/bench/bm25_audit.py +199 -0
- package/python/bench/bm25_regression.py +178 -0
- package/python/bench/bm25_stress.py +159 -0
- package/python/bench/cd_audit.py +238 -0
- package/python/bench/chaos.py +142 -0
- package/python/bench/concurrency.py +103 -0
- package/python/bench/e2e_real_models.py +95 -0
- package/python/bench/llm_retry_test.py +248 -0
- package/python/bench/load_test.py +196 -0
- package/python/bench/vector_ivf_upgrade.py +134 -0
- package/python/docs/CHANNELS.md +195 -0
- package/python/docs/GOVERNANCE.md +164 -0
- package/python/docs/RELEASE.md +109 -0
- package/python/docs/RUN.md +75 -0
- package/python/docs/RUN_CN.md +121 -0
- package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
- package/python/evals/__init__.py +0 -0
- package/python/evals/governance/README.md +61 -0
- package/python/evals/governance/__init__.py +0 -0
- package/python/evals/governance/cases.py +152 -0
- package/python/evals/governance/compare_embeddings.py +94 -0
- package/python/evals/governance/convergence.svg +11 -0
- package/python/evals/governance/report.json +371 -0
- package/python/evals/governance/run_evals.py +400 -0
- package/python/examples/governance_middleware_demo.py +77 -0
- package/python/examples/langgraph_governance_demo.py +108 -0
- package/python/examples/sample_itinerary.csv +6 -0
- package/python/examples/sample_knowledge.csv +9 -0
- package/python/examples/sample_transactions.csv +14 -0
- package/python/foxclaw/__init__.py +526 -0
- package/python/foxclaw/agents/__init__.py +12 -0
- package/python/foxclaw/agents/agents.py +35 -0
- package/python/foxclaw/agents/base.py +865 -0
- package/python/foxclaw/agents/impl/__init__.py +1 -0
- package/python/foxclaw/agents/impl/_helpers.py +78 -0
- package/python/foxclaw/agents/impl/finance.py +248 -0
- package/python/foxclaw/agents/impl/office.py +367 -0
- package/python/foxclaw/agents/impl/supervisor.py +659 -0
- package/python/foxclaw/agents/mixins/_context.py +151 -0
- package/python/foxclaw/agents/mixins/augment.py +149 -0
- package/python/foxclaw/agents/mixins/llm.py +66 -0
- package/python/foxclaw/agents/mixins/memory.py +35 -0
- package/python/foxclaw/agents/mixins/preference.py +81 -0
- package/python/foxclaw/agents/mixins/prompt.py +77 -0
- package/python/foxclaw/agents/mixins/skillbox.py +58 -0
- package/python/foxclaw/agents/mixins/tool_exec.py +443 -0
- package/python/foxclaw/api/__init__.py +283 -0
- package/python/foxclaw/api/app.py +123 -0
- package/python/foxclaw/api/middleware.py +317 -0
- package/python/foxclaw/api/routes/ahs.py +417 -0
- package/python/foxclaw/api/routes/attachments.py +117 -0
- package/python/foxclaw/api/routes/browser.py +39 -0
- package/python/foxclaw/api/routes/domain.py +314 -0
- package/python/foxclaw/api/routes/knowledge.py +163 -0
- package/python/foxclaw/api/routes/llm_admin.py +461 -0
- package/python/foxclaw/api/routes/memory.py +89 -0
- package/python/foxclaw/api/routes/orchestration.py +41 -0
- package/python/foxclaw/api/routes/recovery.py +43 -0
- package/python/foxclaw/api/routes/review.py +275 -0
- package/python/foxclaw/api/routes/search.py +37 -0
- package/python/foxclaw/api/routes/system.py +327 -0
- package/python/foxclaw/api/routes/task.py +281 -0
- package/python/foxclaw/api/routes/ws.py +68 -0
- package/python/foxclaw/api/spine.py +55 -0
- package/python/foxclaw/browser/__init__.py +13 -0
- package/python/foxclaw/browser/action/__init__.py +1 -0
- package/python/foxclaw/browser/action/action_executor.py +54 -0
- package/python/foxclaw/browser/action/action_types.py +45 -0
- package/python/foxclaw/browser/action/action_verifier.py +68 -0
- package/python/foxclaw/browser/action/url_policy.py +147 -0
- package/python/foxclaw/browser/adapter/__init__.py +1 -0
- package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
- package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
- package/python/foxclaw/browser/driver/__init__.py +1 -0
- package/python/foxclaw/browser/driver/base_driver.py +61 -0
- package/python/foxclaw/browser/driver/playwright_driver.py +166 -0
- package/python/foxclaw/browser/perception/__init__.py +1 -0
- package/python/foxclaw/browser/perception/dom_extractor.py +43 -0
- package/python/foxclaw/browser/perception/element_indexer.py +57 -0
- package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
- package/python/foxclaw/browser/perception/snapshot.py +29 -0
- package/python/foxclaw/browser/service.py +148 -0
- package/python/foxclaw/browser/session/__init__.py +1 -0
- package/python/foxclaw/browser/session/session_manager.py +57 -0
- package/python/foxclaw/capability/__init__.py +12 -0
- package/python/foxclaw/capability/embedding_service.py +67 -0
- package/python/foxclaw/channels/__init__.py +140 -0
- package/python/foxclaw/channels/discord.py +184 -0
- package/python/foxclaw/channels/dispatcher.py +204 -0
- package/python/foxclaw/channels/feishu.py +225 -0
- package/python/foxclaw/channels/openclaw_bridge.py +156 -0
- package/python/foxclaw/channels/signing.py +187 -0
- package/python/foxclaw/channels/telegram.py +121 -0
- package/python/foxclaw/channels/weixin.py +225 -0
- package/python/foxclaw/cli.py +154 -0
- package/python/foxclaw/cognitive/__init__.py +4 -0
- package/python/foxclaw/cognitive/reasoner.py +82 -0
- package/python/foxclaw/cognitive/state.py +147 -0
- package/python/foxclaw/core/__init__.py +9 -0
- package/python/foxclaw/core/audit_chain.py +133 -0
- package/python/foxclaw/core/audit_sink.py +100 -0
- package/python/foxclaw/core/compaction.py +283 -0
- package/python/foxclaw/core/config.py +65 -0
- package/python/foxclaw/core/confirm_nonce.py +157 -0
- package/python/foxclaw/core/dispatch.py +27 -0
- package/python/foxclaw/core/envfile.py +48 -0
- package/python/foxclaw/core/failure_policy.py +151 -0
- package/python/foxclaw/core/gateway.py +85 -0
- package/python/foxclaw/core/governance_audit.py +175 -0
- package/python/foxclaw/core/idempotency.py +289 -0
- package/python/foxclaw/core/injection_guard.py +221 -0
- package/python/foxclaw/core/memory_adapter.py +516 -0
- package/python/foxclaw/core/metrics.py +89 -0
- package/python/foxclaw/core/model_router.py +1097 -0
- package/python/foxclaw/core/paths.py +103 -0
- package/python/foxclaw/core/recovery.py +362 -0
- package/python/foxclaw/core/scheduler.py +144 -0
- package/python/foxclaw/core/secrets.py +134 -0
- package/python/foxclaw/core/sessions.py +157 -0
- package/python/foxclaw/core/settings.py +430 -0
- package/python/foxclaw/core/shared_write.py +198 -0
- package/python/foxclaw/core/steering_queue.py +153 -0
- package/python/foxclaw/core/system_bus.py +84 -0
- package/python/foxclaw/core/thought_chain.py +318 -0
- package/python/foxclaw/core/token_meter.py +235 -0
- package/python/foxclaw/core/tools.py +172 -0
- package/python/foxclaw/core/tracing.py +390 -0
- package/python/foxclaw/core/truth_pipeline.py +114 -0
- package/python/foxclaw/core/vec_recall_adapter.py +503 -0
- package/python/foxclaw/core/verify.py +181 -0
- package/python/foxclaw/core/web_search.py +242 -0
- package/python/foxclaw/finance/__init__.py +15 -0
- package/python/foxclaw/finance/evidence.py +153 -0
- package/python/foxclaw/finance/fx.py +100 -0
- package/python/foxclaw/finance/graph.py +188 -0
- package/python/foxclaw/finance/models.py +150 -0
- package/python/foxclaw/finance/nlparse.py +575 -0
- package/python/foxclaw/finance/policy.py +329 -0
- package/python/foxclaw/finance/reasoner.py +129 -0
- package/python/foxclaw/finance/reasoning.py +340 -0
- package/python/foxclaw/finance/reconcile.py +180 -0
- package/python/foxclaw/finance/runtime.py +527 -0
- package/python/foxclaw/governance/__init__.py +24 -0
- package/python/foxclaw/governance/middleware.py +184 -0
- package/python/foxclaw/harness/__init__.py +17 -0
- package/python/foxclaw/harness/_base.py +72 -0
- package/python/foxclaw/harness/_policy.py +359 -0
- package/python/foxclaw/harness/drift.py +89 -0
- package/python/foxclaw/harness/event_bus.py +31 -0
- package/python/foxclaw/harness/harness.py +446 -0
- package/python/foxclaw/harness/loop_detection.py +152 -0
- package/python/foxclaw/itinerary/__init__.py +10 -0
- package/python/foxclaw/itinerary/amap_client.py +117 -0
- package/python/foxclaw/itinerary/models.py +80 -0
- package/python/foxclaw/itinerary/reasoner.py +101 -0
- package/python/foxclaw/itinerary/reasoning.py +174 -0
- package/python/foxclaw/itinerary/runtime.py +148 -0
- package/python/foxclaw/kernel/__init__.py +13 -0
- package/python/foxclaw/kernel/capabilities.py +73 -0
- package/python/foxclaw/kernel/policy_gate.py +114 -0
- package/python/foxclaw/kernel/process_manager.py +205 -0
- package/python/foxclaw/kernel/scheduler.py +131 -0
- package/python/foxclaw/kernel/sentinel.py +453 -0
- package/python/foxclaw/kernel/sentinel_client.py +246 -0
- package/python/foxclaw/kernel/spine.py +97 -0
- package/python/foxclaw/knowledge/__init__.py +376 -0
- package/python/foxclaw/knowledge/truth_crystallizer.py +92 -0
- package/python/foxclaw/learning/__init__.py +12 -0
- package/python/foxclaw/learning/knowledge_graph.py +149 -0
- package/python/foxclaw/learning/models.py +93 -0
- package/python/foxclaw/learning/reasoner.py +70 -0
- package/python/foxclaw/learning/runtime.py +363 -0
- package/python/foxclaw/learning/spaced_repetition.py +96 -0
- package/python/foxclaw/mcp_server.py +189 -0
- package/python/foxclaw/orchestration/__init__.py +184 -0
- package/python/foxclaw/pipeline/__init__.py +23 -0
- package/python/foxclaw/pipeline/cognitive_pipeline.py +481 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +230 -0
- package/python/foxclaw/planning/__init__.py +17 -0
- package/python/foxclaw/planning/llm_planner.py +232 -0
- package/python/foxclaw/planning/planner.py +99 -0
- package/python/foxclaw/search/__init__.py +24 -0
- package/python/foxclaw/search/agent_port.py +194 -0
- package/python/foxclaw/search/evidence.py +113 -0
- package/python/foxclaw/search/external_search.py +50 -0
- package/python/foxclaw/search/hkc_read_port.py +60 -0
- package/python/foxclaw/search/internal_search.py +73 -0
- package/python/foxclaw/search/models.py +129 -0
- package/python/foxclaw/search/planner.py +44 -0
- package/python/foxclaw/search/retriever.py +39 -0
- package/python/foxclaw/search/runtime.py +134 -0
- package/python/foxclaw/search/strategy.py +44 -0
- package/python/foxclaw/skills/__init__.py +613 -0
- package/python/foxclaw/skills/hooks_spec.py +73 -0
- package/python/foxclaw/skills/lifecycle.py +278 -0
- package/python/foxclaw/skills/markdown_skill.py +174 -0
- package/python/foxclaw/skills/markdown_store.py +39 -0
- package/python/foxclaw/skills/mcp_client.py +190 -0
- package/python/foxclaw/skills/mcp_store.py +43 -0
- package/python/foxclaw/skills/permission.py +180 -0
- package/python/foxclaw/skills/pipeline.py +386 -0
- package/python/foxclaw/skills/policy.py +80 -0
- package/python/foxclaw/skills/review.py +72 -0
- package/python/foxclaw/skills/store.py +57 -0
- package/python/frontend/assets/core_bg.jpg +0 -0
- package/python/frontend/deck/auth.js +32 -0
- package/python/frontend/deck/chat_model_picker.js +537 -0
- package/python/frontend/deck/domain.js +296 -0
- package/python/frontend/deck/main.js +1950 -0
- package/python/frontend/deck/markdown.js +48 -0
- package/python/frontend/deck/settings.js +324 -0
- package/python/frontend/deck/state.js +6 -0
- package/python/frontend/deck/util.js +6 -0
- package/python/frontend/deck/visuals.js +212 -0
- package/python/frontend/deck.css +855 -0
- package/python/frontend/deck.html +686 -0
- package/python/frontend/deck_v2.html +341 -0
- package/python/frontend/planet/index.html +266 -0
- package/python/frontend/planet/planet.css +579 -0
- package/python/frontend/planet/planet.js +1637 -0
- package/python/frontend/planet/vendor/three.core.min.js +6 -0
- package/python/frontend/planet/vendor/three.module.min.js +6 -0
- package/python/frontend/v3/assets/index-BD7VPbMV.js +33 -0
- package/python/frontend/v3/assets/index-CAvnYWGH.css +1 -0
- package/python/frontend/v3/index.html +15 -0
- package/python/frontend/widget_runtime.js +295 -0
- package/python/frontend/widgets.js +696 -0
- package/python/frontend_src/index.html +14 -0
- package/python/frontend_src/package-lock.json +1376 -0
- package/python/frontend_src/package.json +19 -0
- package/python/frontend_src/src/App.vue +62 -0
- package/python/frontend_src/src/api.js +89 -0
- package/python/frontend_src/src/components/ChatDock.vue +377 -0
- package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
- package/python/frontend_src/src/components/DocViewer.vue +171 -0
- package/python/frontend_src/src/components/Inspector.vue +241 -0
- package/python/frontend_src/src/components/LeftRail.vue +394 -0
- package/python/frontend_src/src/components/MenuPopover.vue +130 -0
- package/python/frontend_src/src/components/MessageStream.vue +401 -0
- package/python/frontend_src/src/components/ModelPicker.vue +355 -0
- package/python/frontend_src/src/components/QuickIngest.vue +118 -0
- package/python/frontend_src/src/components/Stage.vue +226 -0
- package/python/frontend_src/src/components/ToolMenu.vue +144 -0
- package/python/frontend_src/src/components/TopBar.vue +90 -0
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +84 -0
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +86 -0
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +441 -0
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +118 -0
- package/python/frontend_src/src/components/panels/PanelBox.vue +51 -0
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +308 -0
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +254 -0
- package/python/frontend_src/src/components/panels/TasksPanel.vue +74 -0
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +640 -0
- package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
- package/python/frontend_src/src/lib/fmt.js +48 -0
- package/python/frontend_src/src/lib/md.js +204 -0
- package/python/frontend_src/src/main.js +7 -0
- package/python/frontend_src/src/stores/app.js +106 -0
- package/python/frontend_src/src/stores/chat.js +484 -0
- package/python/frontend_src/src/stores/model.js +104 -0
- package/python/frontend_src/src/stores/panels.js +330 -0
- package/python/frontend_src/src/styles/base.css +138 -0
- package/python/frontend_src/src/styles/panel.css +117 -0
- package/python/frontend_src/src/styles/tokens.css +78 -0
- package/python/frontend_src/vite.config.js +46 -0
- package/python/pyproject.toml +265 -0
- package/python/skills/README.md +123 -0
- package/python/skills/execution-contract/SKILL.md +30 -0
- package/python/skills/general-assistant/SKILL.md +34 -0
- package/python/skills/planner/SKILL.md +33 -0
- package/python/skills/schedule-itinerary/SKILL.md +35 -0
- package/python/skills/skill-composition/SKILL.md +31 -0
- package/python/skills/skill-guard/SKILL.md +67 -0
- package/python/skills/skill-memory/SKILL.md +50 -0
- package/python/skills/skill-router/SKILL.md +35 -0
- package/python/tests/conftest.py +76 -0
- package/python/tests/run_all.py +263 -0
- package/python/tests/test_agent_lifecycle.py +360 -0
- package/python/tests/test_ahs_p2.py +414 -0
- package/python/tests/test_anthropic_tools.py +206 -0
- package/python/tests/test_api.py +87 -0
- package/python/tests/test_api_auth.py +83 -0
- package/python/tests/test_api_host_guard.py +69 -0
- package/python/tests/test_attachments.py +167 -0
- package/python/tests/test_audit_chain.py +121 -0
- package/python/tests/test_audit_sink.py +136 -0
- package/python/tests/test_browser.py +153 -0
- package/python/tests/test_browser_request_guard.py +184 -0
- package/python/tests/test_capabilities.py +158 -0
- package/python/tests/test_channel_signing.py +192 -0
- package/python/tests/test_channel_trust.py +109 -0
- package/python/tests/test_channels.py +184 -0
- package/python/tests/test_cli.py +46 -0
- package/python/tests/test_compaction_toolpair.py +119 -0
- package/python/tests/test_config_layering.py +65 -0
- package/python/tests/test_confirm_nonce.py +197 -0
- package/python/tests/test_context_compaction_wiring.py +130 -0
- package/python/tests/test_dispatch.py +95 -0
- package/python/tests/test_domain_evals.py +403 -0
- package/python/tests/test_domain_import.py +78 -0
- package/python/tests/test_domain_reasoners.py +148 -0
- package/python/tests/test_e2e.py +178 -0
- package/python/tests/test_emb_cache.py +107 -0
- package/python/tests/test_embedding_singleton.py +89 -0
- package/python/tests/test_envfile.py +83 -0
- package/python/tests/test_evals_smoke.py +114 -0
- package/python/tests/test_event_spine.py +182 -0
- package/python/tests/test_evidence_semantic.py +101 -0
- package/python/tests/test_evidence_sse.py +97 -0
- package/python/tests/test_file_ingest.py +146 -0
- package/python/tests/test_finance.py +153 -0
- package/python/tests/test_finance_ledger.py +252 -0
- package/python/tests/test_finance_persist.py +178 -0
- package/python/tests/test_gateway.py +144 -0
- package/python/tests/test_governance.py +468 -0
- package/python/tests/test_governance_audit.py +359 -0
- package/python/tests/test_governance_middleware.py +143 -0
- package/python/tests/test_hkc.py +286 -0
- package/python/tests/test_hkc_index_build_manager.py +339 -0
- package/python/tests/test_hkc_manifest_reuse.py +317 -0
- package/python/tests/test_hkc_search_event_sync.py +533 -0
- package/python/tests/test_import_path_guard.py +161 -0
- package/python/tests/test_injection_guard.py +211 -0
- package/python/tests/test_integration.py +141 -0
- package/python/tests/test_itinerary.py +103 -0
- package/python/tests/test_kee_semantic.py +89 -0
- package/python/tests/test_kernel.py +222 -0
- package/python/tests/test_learning.py +110 -0
- package/python/tests/test_llm_admin.py +160 -0
- package/python/tests/test_llm_planner.py +366 -0
- package/python/tests/test_markdown_skills.py +73 -0
- package/python/tests/test_markdown_skills_openclaw.py +71 -0
- package/python/tests/test_mcp_connectors.py +109 -0
- package/python/tests/test_mcp_guardian_discovery.py +91 -0
- package/python/tests/test_mcp_no_memory.py +43 -0
- package/python/tests/test_mcp_policy_gate.py +77 -0
- package/python/tests/test_mcp_server.py +95 -0
- package/python/tests/test_memory_recall.py +95 -0
- package/python/tests/test_metrics.py +66 -0
- package/python/tests/test_multi_chain.py +102 -0
- package/python/tests/test_multimodal.py +135 -0
- package/python/tests/test_negotiation.py +146 -0
- package/python/tests/test_orchestration.py +119 -0
- package/python/tests/test_orchestration_decoupled.py +109 -0
- package/python/tests/test_p3_p5_hardening.py +338 -0
- package/python/tests/test_parallel_dispatch.py +215 -0
- package/python/tests/test_pipeline.py +533 -0
- package/python/tests/test_plan_visible.py +86 -0
- package/python/tests/test_planner.py +95 -0
- package/python/tests/test_policy_gate.py +109 -0
- package/python/tests/test_reasoner.py +185 -0
- package/python/tests/test_release_check.py +263 -0
- package/python/tests/test_resume_checkpoint.py +156 -0
- package/python/tests/test_routing.py +95 -0
- package/python/tests/test_scheduler.py +141 -0
- package/python/tests/test_search.py +204 -0
- package/python/tests/test_secrets.py +159 -0
- package/python/tests/test_semantic.py +78 -0
- package/python/tests/test_sessions.py +91 -0
- package/python/tests/test_settings.py +281 -0
- package/python/tests/test_shadow_learning.py +330 -0
- package/python/tests/test_shared_write.py +207 -0
- package/python/tests/test_skill_taint_gate.py +154 -0
- package/python/tests/test_skills.py +133 -0
- package/python/tests/test_stats_and_events.py +165 -0
- package/python/tests/test_streaming.py +60 -0
- package/python/tests/test_supervisor_pipeline.py +177 -0
- package/python/tests/test_supervisor_triage.py +108 -0
- package/python/tests/test_taint.py +117 -0
- package/python/tests/test_task_stream.py +85 -0
- package/python/tests/test_thinking_sse.py +67 -0
- package/python/tests/test_thinking_stream.py +76 -0
- package/python/tests/test_tool_center.py +129 -0
- package/python/tests/test_tool_parallel.py +113 -0
- package/python/tests/test_tool_params.py +103 -0
- package/python/tests/test_tool_use.py +242 -0
- package/python/tests/test_toplevel_concurrency.py +117 -0
- package/python/tests/test_tracing.py +290 -0
- package/python/tests/test_truth_crystallizer.py +127 -0
- package/python/tests/test_truth_flow.py +93 -0
- package/python/tests/test_url_policy.py +53 -0
- package/python/tests/test_verify_evidence.py +225 -0
- package/python/tests/test_version_sync.py +71 -0
- package/python/tests/test_web_trigger.py +99 -0
- package/python/tools/check_faiss_type.py +31 -0
- package/python/tools/check_log.ps1 +46 -0
- package/python/tools/ivf_status.py +72 -0
- package/python/tools/mypy_baseline.txt +5 -0
- package/python/tools/mypy_ratchet.py +97 -0
- package/python/tools/pack.py +178 -0
- package/python/tools/release_check.py +349 -0
- package/python/tools/restart_foxclaw.ps1 +74 -0
- package/python/tools/shared_write_audit.py +204 -0
- package/python/tools/size_baseline.txt +28 -0
- package/python/tools/size_check.py +102 -0
- package/python/tools/verify_env.bat +2 -0
- package/python/tools/verify_env.py +25 -0
- package/python/vendor/HMR_LICENSE +37 -0
- package/python/vendor/HMR_README.md +65 -0
- package/python/vendor/hkc/LICENSE +37 -0
- package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
- package/python/vendor/hkc/hkc_ace/ace.py +457 -0
- package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
- package/python/vendor/hkc/hkc_api/__init__.py +0 -0
- package/python/vendor/hkc/hkc_api/container.py +416 -0
- package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
- package/python/vendor/hkc/hkc_api/main.py +157 -0
- package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
- package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
- package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
- package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
- package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
- package/python/vendor/hkc/hkc_api/schemas.py +153 -0
- package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
- package/python/vendor/hkc/hkc_core/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
- package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
- package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
- package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
- package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
- package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
- package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
- package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
- package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
- package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
- package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
- package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
- package/python/vendor/hkc/hkc_kde/kde.py +298 -0
- package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
- package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
- package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
- package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
- package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
- package/python/vendor/hkc/hkc_kde/models.py +74 -0
- package/python/vendor/hkc/hkc_kde/packager.py +329 -0
- package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
- package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
- package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
- package/python/vendor/hkc/hkc_kee/kee.py +541 -0
- package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
- package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
- package/python/vendor/hkc/hkc_sdk/base.py +151 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
- package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
- package/python/vendor/hkc/hkc_sdk/models.py +171 -0
- package/python/vendor/hkc/hkc_search/__init__.py +0 -0
- package/python/vendor/hkc/hkc_search/bm25.py +325 -0
- package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
- package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
- package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
- package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
- package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
- package/python/vendor/hmr/__init__.py +0 -0
- package/python/vendor/hmr/core/__init__.py +0 -0
- package/python/vendor/hmr/core/hmr.py +684 -0
- package/python/vendor/hmr/core/models.py +310 -0
- package/python/vendor/hmr/engines/__init__.py +0 -0
- package/python/vendor/hmr/engines/evolution.py +710 -0
- package/python/vendor/hmr/engines/jit_compiler.py +472 -0
- package/python/vendor/hmr/engines/lifecycle.py +264 -0
- package/python/vendor/hmr/engines/policy.py +625 -0
- package/python/vendor/hmr/engines/recall.py +260 -0
- package/python/vendor/hmr/engines/runtime_state.py +158 -0
- package/python/vendor/hmr/engines/scheduler.py +323 -0
- package/python/vendor/hmr/engines/semantic.py +150 -0
- package/python/vendor/hmr/engines/temporal.py +261 -0
- package/python/vendor/hmr/engines/thought_chain.py +635 -0
- package/python/vendor/hmr/graph/__init__.py +0 -0
- package/python/vendor/hmr/graph/cwg.py +338 -0
- package/python/vendor/hmr/graph/memory_graph.py +530 -0
- package/python/vendor/hmr/storage/__init__.py +0 -0
- package/python/vendor/hmr/storage/memory_fs.py +300 -0
- package/python/vendor/hmr/storage/vector_store.py +520 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""领域内核端点(B 阶段三内核):财务 / 学习 / 行程 + 多格式文件导入。
|
|
2
|
+
|
|
3
|
+
从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
|
|
4
|
+
晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from fastapi import HTTPException, UploadFile, File
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
import foxclaw.api as _api
|
|
17
|
+
from foxclaw.core.paths import resolve_import_path
|
|
18
|
+
from .. import app
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ── 领域内核端点(B 阶段三内核)─────────────────────────────
|
|
22
|
+
|
|
23
|
+
class ImportPayload(BaseModel):
|
|
24
|
+
csv_path: Optional[str] = None
|
|
25
|
+
rows: Optional[list] = None # 也可直接传记录数组
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_csv_path(raw: str) -> str:
|
|
29
|
+
"""把请求里的 csv_path 收敛到允许目录内(默认 FOXCLAW_DATA_DIR)。
|
|
30
|
+
|
|
31
|
+
此前 csv_path 原样进 import_csv → open(),等于给 HTTP 面开放「读本机任意文件」,
|
|
32
|
+
且内容会落入知识库/账本再从 /api 读回。库调用(CLI/测试/示例)不走这里:
|
|
33
|
+
那是本地可信代码,本身就有文件权限,加限制只破坏用法不增加安全。
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
return str(resolve_import_path(raw))
|
|
37
|
+
except ValueError as e:
|
|
38
|
+
raise HTTPException(400, f"csv_path 不允许: {e}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.get("/api/finance/analysis")
|
|
42
|
+
async def finance_analysis(period: Optional[str] = None,
|
|
43
|
+
current_balance: Optional[float] = None):
|
|
44
|
+
"""财务内核综合分析:预算/异常/订阅/烧钱/守门人裁决。"""
|
|
45
|
+
try:
|
|
46
|
+
return _api._os_instance.finance.analyze_finances(
|
|
47
|
+
period=period, current_balance=current_balance)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
raise HTTPException(500, f"财务分析失败: {e}")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.post("/api/finance/import")
|
|
53
|
+
async def finance_import(p: ImportPayload):
|
|
54
|
+
"""导入交易数据(CSV 路径或记录数组)。"""
|
|
55
|
+
rt = _api._os_instance.finance_runtime
|
|
56
|
+
src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
|
|
57
|
+
try:
|
|
58
|
+
if src:
|
|
59
|
+
n = rt.import_csv(src)
|
|
60
|
+
elif p.rows:
|
|
61
|
+
from foxclaw.finance import Transaction
|
|
62
|
+
n = 0
|
|
63
|
+
for row in p.rows:
|
|
64
|
+
# save=False:整批导完再落盘一次,别在循环里做 N 次磁盘 IO
|
|
65
|
+
rt.add_transaction(Transaction(**row), run_gatekeeper=False, save=False)
|
|
66
|
+
n += 1
|
|
67
|
+
if n:
|
|
68
|
+
rt.save()
|
|
69
|
+
else:
|
|
70
|
+
return {"imported": 0, "note": "未提供数据"}
|
|
71
|
+
return {"imported": n, "status": rt.status()}
|
|
72
|
+
except Exception as e:
|
|
73
|
+
raise HTTPException(400, f"导入失败: {e}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ── 自然语言记账(第一线)────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
class RecordPayload(BaseModel):
|
|
79
|
+
text: str
|
|
80
|
+
account: str = "default"
|
|
81
|
+
dry_run: bool = False # 只解析预览,不落账
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class FxPayload(BaseModel):
|
|
85
|
+
currency: str
|
|
86
|
+
rate: float
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.post("/api/finance/record")
|
|
90
|
+
async def finance_record(p: RecordPayload):
|
|
91
|
+
"""一句话记账:「今天买了一件衣服花了 105 块」/「明天交房租 3000」。"""
|
|
92
|
+
rt = _api._os_instance.finance_runtime
|
|
93
|
+
try:
|
|
94
|
+
return rt.record_text(p.text, account=p.account, dry_run=p.dry_run)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
raise HTTPException(400, f"记账失败: {e}")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.get("/api/finance/ledger")
|
|
100
|
+
async def finance_ledger(period: Optional[str] = None):
|
|
101
|
+
"""记账本:按月汇总(已发生/计划、按天、按分类、按币种)。"""
|
|
102
|
+
rt = _api._os_instance.finance_runtime
|
|
103
|
+
try:
|
|
104
|
+
return rt.ledger(period)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
raise HTTPException(500, f"记账本汇总失败: {e}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@app.get("/api/finance/fx")
|
|
110
|
+
async def finance_fx():
|
|
111
|
+
"""当前汇率表(本位币 CNY)。"""
|
|
112
|
+
return _api._os_instance.finance_runtime.fx.snapshot()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@app.post("/api/finance/fx")
|
|
116
|
+
async def finance_set_fx(p: FxPayload):
|
|
117
|
+
"""设置某个币种的记账汇率(比如刷卡当天的实际结算价)。"""
|
|
118
|
+
try:
|
|
119
|
+
return _api._os_instance.finance_runtime.fx.set_rate(p.currency, p.rate)
|
|
120
|
+
except ValueError as e:
|
|
121
|
+
raise HTTPException(400, str(e))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@app.get("/api/learning/analysis")
|
|
125
|
+
async def learning_analysis():
|
|
126
|
+
"""学习内核综合分析:掌握度/遗忘风险/知识图谱/学习策略。"""
|
|
127
|
+
try:
|
|
128
|
+
return _api._os_instance.office.analyze_learning()
|
|
129
|
+
except Exception as e:
|
|
130
|
+
raise HTTPException(500, f"学习分析失败: {e}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@app.post("/api/learning/import")
|
|
134
|
+
async def learning_import(p: ImportPayload):
|
|
135
|
+
"""导入知识点(办公学习内核)。learn() 内部已把内容存进 HKC(连续 ConceptKU,知识真源),
|
|
136
|
+
不再另做桥接。"""
|
|
137
|
+
rt = _api._os_instance.learning_runtime
|
|
138
|
+
src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
|
|
139
|
+
try:
|
|
140
|
+
if src:
|
|
141
|
+
n = rt.import_csv(src)
|
|
142
|
+
elif p.rows:
|
|
143
|
+
n = 0
|
|
144
|
+
for row in p.rows:
|
|
145
|
+
rt.learn(**row); n += 1
|
|
146
|
+
else:
|
|
147
|
+
return {"imported": 0, "note": "未提供数据"}
|
|
148
|
+
rt.build_connections()
|
|
149
|
+
return {"imported": n, "status": rt.status()}
|
|
150
|
+
except Exception as e:
|
|
151
|
+
raise HTTPException(400, f"导入失败: {e}")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@app.delete("/api/learning/item/{item_id}")
|
|
155
|
+
async def learning_delete(item_id: str):
|
|
156
|
+
"""删除一个学习知识点(内存+复习状态+图谱,并落盘)。"""
|
|
157
|
+
rt = _api._os_instance.learning_runtime
|
|
158
|
+
ok = rt.delete(item_id)
|
|
159
|
+
return {"deleted": ok, "status": rt.status()}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class NotePayload(BaseModel):
|
|
163
|
+
item_id: str
|
|
164
|
+
note: str
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@app.post("/api/learning/note")
|
|
168
|
+
async def learning_note(p: NotePayload):
|
|
169
|
+
"""给一个学习知识点追加一条笔记(有内容才存;没新增不写盘)。"""
|
|
170
|
+
rt = _api._os_instance.learning_runtime
|
|
171
|
+
r = rt.add_note(p.item_id, p.note)
|
|
172
|
+
if "error" in r:
|
|
173
|
+
raise HTTPException(400, r["error"])
|
|
174
|
+
return r
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@app.get("/api/itinerary/plan")
|
|
178
|
+
async def itinerary_plan(mode: str = "driving"):
|
|
179
|
+
"""行程内核规划:路线优化/可达性/冲突/守门人 + 高德可视化数据。"""
|
|
180
|
+
try:
|
|
181
|
+
return _api._os_instance.office.plan_itinerary(mode=mode)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
raise HTTPException(500, f"行程规划失败: {e}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@app.post("/api/itinerary/import")
|
|
187
|
+
async def itinerary_import(p: ImportPayload):
|
|
188
|
+
"""导入行程点。"""
|
|
189
|
+
rt = _api._os_instance.itinerary_runtime
|
|
190
|
+
src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
|
|
191
|
+
try:
|
|
192
|
+
if src:
|
|
193
|
+
n = rt.import_csv(src)
|
|
194
|
+
elif p.rows:
|
|
195
|
+
n = 0
|
|
196
|
+
for row in p.rows:
|
|
197
|
+
rt.add_waypoint(**row); n += 1
|
|
198
|
+
else:
|
|
199
|
+
return {"imported": 0, "note": "未提供数据"}
|
|
200
|
+
return {"imported": n, "status": rt.status()}
|
|
201
|
+
except Exception as e:
|
|
202
|
+
raise HTTPException(400, f"导入失败: {e}")
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ── 领域导入:超越 csv_path,支持上传 csv / xlsx / json ─────────────────────
|
|
206
|
+
# 三领域运行时的 import_csv 是"列→类型转换"的唯一权威。多格式文件在此统一解析成 rows,
|
|
207
|
+
# 再桥接成运行时认识的临时 CSV → 复用 import_csv 的转换逻辑(不碰运行时,单一真源)。
|
|
208
|
+
_IMPORT_MAX_BYTES = 10 * 1024 * 1024
|
|
209
|
+
_IMPORT_COLUMNS = {
|
|
210
|
+
"finance": ["amount", "category", "entity", "account", "timestamp", "tags"],
|
|
211
|
+
"learning": ["content", "topic", "source", "tags"],
|
|
212
|
+
"itinerary": ["name", "address", "lng", "lat", "start_time", "duration_min", "city"],
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _parse_import_rows(filename: str, data: bytes) -> list:
|
|
217
|
+
"""把上传的 csv / xlsx / json 统一解析成 [dict]。"""
|
|
218
|
+
ext = os.path.splitext(filename or "")[1].lower()
|
|
219
|
+
if ext == ".json":
|
|
220
|
+
obj = json.loads(data.decode("utf-8"))
|
|
221
|
+
rows = obj.get("rows") if isinstance(obj, dict) else obj
|
|
222
|
+
if not isinstance(rows, list):
|
|
223
|
+
raise ValueError("JSON 应为对象数组或 {rows:[...]}")
|
|
224
|
+
return [dict(r) for r in rows if isinstance(r, dict)]
|
|
225
|
+
if ext in (".csv", ".txt", ""):
|
|
226
|
+
import csv as _csv
|
|
227
|
+
import io as _io
|
|
228
|
+
text = data.decode("utf-8-sig", errors="replace")
|
|
229
|
+
return list(_csv.DictReader(_io.StringIO(text)))
|
|
230
|
+
if ext == ".xlsx":
|
|
231
|
+
import openpyxl as _openpyxl
|
|
232
|
+
import io as _io
|
|
233
|
+
wb = _openpyxl.load_workbook(_io.BytesIO(data), read_only=True, data_only=True)
|
|
234
|
+
try:
|
|
235
|
+
grid = list(wb.active.iter_rows(values_only=True))
|
|
236
|
+
finally:
|
|
237
|
+
wb.close()
|
|
238
|
+
if not grid:
|
|
239
|
+
return []
|
|
240
|
+
headers = [str(h).strip() if h is not None else "" for h in grid[0]]
|
|
241
|
+
out = []
|
|
242
|
+
for r in grid[1:]:
|
|
243
|
+
d = {headers[i]: ("" if v is None else v)
|
|
244
|
+
for i, v in enumerate(r) if i < len(headers) and headers[i]}
|
|
245
|
+
if any(str(x).strip() for x in d.values()):
|
|
246
|
+
out.append(d)
|
|
247
|
+
return out
|
|
248
|
+
raise ValueError(f"不支持的格式 {ext};支持 .csv .xlsx .json")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _rows_to_temp_csv(rows: list, columns: list) -> str:
|
|
252
|
+
"""把任意来源 rows 写成运行时 import_csv 认识的 CSV(tags 列表 → | 连接),返回临时路径。"""
|
|
253
|
+
import csv as _csv
|
|
254
|
+
import tempfile
|
|
255
|
+
fd, path = tempfile.mkstemp(suffix=".csv")
|
|
256
|
+
with os.fdopen(fd, "w", newline="", encoding="utf-8") as f:
|
|
257
|
+
w = _csv.DictWriter(f, fieldnames=columns, extrasaction="ignore")
|
|
258
|
+
w.writeheader()
|
|
259
|
+
for r in rows:
|
|
260
|
+
rr = dict(r)
|
|
261
|
+
if isinstance(rr.get("tags"), (list, tuple)):
|
|
262
|
+
rr["tags"] = "|".join(str(t) for t in rr["tags"])
|
|
263
|
+
w.writerow({c: ("" if rr.get(c) is None else rr.get(c)) for c in columns})
|
|
264
|
+
return path
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
async def _domain_import_file(domain: str, runtime, file: UploadFile):
|
|
268
|
+
"""统一领域文件导入:解析 → 桥接 CSV → 运行时 import_csv(线程池,含阻塞 I/O)。"""
|
|
269
|
+
data = await file.read()
|
|
270
|
+
if len(data) > _IMPORT_MAX_BYTES:
|
|
271
|
+
raise HTTPException(413, f"文件过大(上限 {_IMPORT_MAX_BYTES // 1048576}MB)")
|
|
272
|
+
try:
|
|
273
|
+
rows = _parse_import_rows(file.filename or "", data)
|
|
274
|
+
except Exception as e:
|
|
275
|
+
raise HTTPException(400, f"解析失败: {e}")
|
|
276
|
+
if not rows:
|
|
277
|
+
return {"imported": 0, "note": "文件无有效数据行"}
|
|
278
|
+
tmp = _rows_to_temp_csv(rows, _IMPORT_COLUMNS[domain])
|
|
279
|
+
try:
|
|
280
|
+
n = await asyncio.to_thread(runtime.import_csv, tmp)
|
|
281
|
+
except Exception as e:
|
|
282
|
+
raise HTTPException(400, f"导入失败(检查列名是否匹配): {e}")
|
|
283
|
+
finally:
|
|
284
|
+
try:
|
|
285
|
+
os.unlink(tmp)
|
|
286
|
+
except OSError:
|
|
287
|
+
pass
|
|
288
|
+
return {"imported": n, "status": runtime.status()}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@app.post("/api/finance/import/file")
|
|
292
|
+
async def finance_import_file(file: UploadFile = File(...)):
|
|
293
|
+
"""上传 csv/xlsx/json 导入交易。列:amount,category,entity,account,timestamp,tags。"""
|
|
294
|
+
rt = _api._os_instance.finance_runtime
|
|
295
|
+
return await _domain_import_file("finance", rt, file)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@app.post("/api/learning/import/file")
|
|
299
|
+
async def learning_import_file(file: UploadFile = File(...)):
|
|
300
|
+
"""上传 csv/xlsx/json 导入知识点。列:content,topic,source,tags。"""
|
|
301
|
+
rt = _api._os_instance.learning_runtime
|
|
302
|
+
res = await _domain_import_file("learning", rt, file)
|
|
303
|
+
try:
|
|
304
|
+
rt.build_connections()
|
|
305
|
+
except Exception:
|
|
306
|
+
pass
|
|
307
|
+
return res
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@app.post("/api/itinerary/import/file")
|
|
311
|
+
async def itinerary_import_file(file: UploadFile = File(...)):
|
|
312
|
+
"""上传 csv/xlsx/json 导入行程点。列:name,address,lng,lat,start_time,duration_min,city。"""
|
|
313
|
+
rt = _api._os_instance.itinerary_runtime
|
|
314
|
+
return await _domain_import_file("itinerary", rt, file)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""知识核心(HKC)端点:同进程 DirectClient,惰性装配,缺依赖降级。
|
|
2
|
+
|
|
3
|
+
从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api._os_instance`
|
|
4
|
+
晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from fastapi import HTTPException
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
import foxclaw.api as _api
|
|
13
|
+
from .. import app
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ── 知识核心(HKC):同进程 DirectClient,惰性装配,缺依赖降级 ─────────
|
|
17
|
+
# HKC = Foxclaw Knowledge Core:KDE 抽取 → KEE 演化 → 混合检索(BM25+向量+图)。
|
|
18
|
+
# 首次命中任一路由时才装配(见 Foxclaw.hkc 惰性属性);未 vendor/缺依赖时 available=False。
|
|
19
|
+
|
|
20
|
+
class KnowledgeSearchPayload(BaseModel):
|
|
21
|
+
query: str
|
|
22
|
+
mode: str = "hybrid" # bm25 | vector | graph | hybrid
|
|
23
|
+
top_k: int = 10
|
|
24
|
+
domain: str = ""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class KnowledgeIngestPayload(BaseModel):
|
|
28
|
+
text: str
|
|
29
|
+
source_title: str = ""
|
|
30
|
+
domain: str = ""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _hkc_or_none():
|
|
34
|
+
"""惰性取 HKCService;None 表示降级(未 vendor/缺依赖/装配失败)。"""
|
|
35
|
+
try:
|
|
36
|
+
return _api._os_instance.hkc
|
|
37
|
+
except Exception:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
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
|
+
try:
|
|
46
|
+
from hkc_sdk.exceptions import HKCInsufficientCoverage as _HKCInsuffCov
|
|
47
|
+
except Exception:
|
|
48
|
+
_HKCInsuffCov = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@app.post("/api/knowledge/search")
|
|
52
|
+
async def knowledge_search(p: KnowledgeSearchPayload):
|
|
53
|
+
"""混合检索知识库。HKC 未启用或检索失败时返回空 hits(与整体降级哲学一致)。"""
|
|
54
|
+
hkc = _hkc_or_none()
|
|
55
|
+
if hkc is None or not hkc.available:
|
|
56
|
+
return {"available": False, "hits": []}
|
|
57
|
+
try:
|
|
58
|
+
hits = await asyncio.to_thread(
|
|
59
|
+
hkc.client.search, p.query, p.mode, p.top_k, p.domain)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
return {"available": True, "hits": [], "error": str(e)}
|
|
62
|
+
return {"available": True, "hits": [
|
|
63
|
+
{"ku_id": h.ku_id, "score": h.score, "name": h.name,
|
|
64
|
+
"summary": h.summary, "mode": h.mode, "ku_type": getattr(h, "ku_type", "")}
|
|
65
|
+
for h in hits]}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.post("/api/knowledge/ingest")
|
|
69
|
+
async def knowledge_ingest(p: KnowledgeIngestPayload):
|
|
70
|
+
"""摄入文本 → KDE 抽取 KU → KEE 演化(守 Principle 07)。需配置 LLM;未配则抽取降级。"""
|
|
71
|
+
hkc = _hkc_or_none()
|
|
72
|
+
if hkc is None or not hkc.available:
|
|
73
|
+
raise HTTPException(503, "HKC 未启用(未 vendor 或缺依赖)")
|
|
74
|
+
try:
|
|
75
|
+
res = await asyncio.to_thread(
|
|
76
|
+
hkc.client.ingest_text, p.text, p.source_title, p.domain)
|
|
77
|
+
return {"ku_count": res.ku_count, "ku_ids": res.ku_ids, "counts": res.counts}
|
|
78
|
+
except Exception as e:
|
|
79
|
+
# 抽取全段失败时 KDE 现在会抛错(带 provider/model/最后一次 LLM 报错原文)。
|
|
80
|
+
# 不能像旧版那样返回 ku_count=0 + 200 —— 那会让「库里其实什么都没有」看起来
|
|
81
|
+
# 像「录入成功只是没内容」,复习板块于是永远 0 卡。
|
|
82
|
+
msg = str(e).strip() or e.__class__.__name__
|
|
83
|
+
raise HTTPException(502, f"摄入失败:{msg}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@app.get("/api/knowledge/recent")
|
|
87
|
+
async def knowledge_recent(limit: int = 10):
|
|
88
|
+
"""最近录入的知识单元。右栏「最近知识」看前 10 条,知识库面板看全量。
|
|
89
|
+
|
|
90
|
+
只读、无 LLM。短知识经 `/api/knowledge/ingest` 进来,长文档走知识星球(3D 图谱);
|
|
91
|
+
两边落的是同一个图库,所以这里能看到全部新增 KU 而不只是「短知识」。
|
|
92
|
+
|
|
93
|
+
为什么不复用 HKC 的 `/knowledge/graph`:那条路径的 KUResponse 不带 created_at
|
|
94
|
+
(schema 里没有),无法按时间倒序。这里直接读已装配容器的 graph_store 拿原始
|
|
95
|
+
KU 对象(带 created_at),避免为了排序去改 vendor 里的公开 schema。
|
|
96
|
+
"""
|
|
97
|
+
hkc = _hkc_or_none()
|
|
98
|
+
if hkc is None or not hkc.available:
|
|
99
|
+
return {"available": False, "total": 0, "kus": []}
|
|
100
|
+
try:
|
|
101
|
+
store = hkc.container.graph_store
|
|
102
|
+
kus = await asyncio.to_thread(store.query_all, 4000)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
return {"available": True, "total": 0, "kus": [], "error": str(e)}
|
|
105
|
+
|
|
106
|
+
# ISO 字符串,字典序即时间序
|
|
107
|
+
kus.sort(key=lambda k: getattr(k, "created_at", "") or "", reverse=True)
|
|
108
|
+
n = max(1, min(int(limit or 10), 200))
|
|
109
|
+
return {"available": True, "total": len(kus), "kus": [
|
|
110
|
+
{"ku_id": ku.ku_id, "ku_type": ku.ku_type.value, "name": ku.name,
|
|
111
|
+
"summary": ku.summary or "", "domain": ku.domain or "",
|
|
112
|
+
"created_at": getattr(ku, "created_at", "") or "",
|
|
113
|
+
"tags": list(ku.tags or [])}
|
|
114
|
+
for ku in kus[:n]]}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# ── HKC 融合第 2 档 Step1:ACE 能力(只读查询面,无 LLM、零成本)─────────
|
|
118
|
+
@app.get("/api/knowledge/abilities")
|
|
119
|
+
async def knowledge_abilities():
|
|
120
|
+
"""列出可编译的能力 key(来自 ACE skill_taxonomy)。只读、确定性、无 LLM。"""
|
|
121
|
+
hkc = _hkc_or_none()
|
|
122
|
+
if hkc is None or not hkc.available:
|
|
123
|
+
return {"available": False, "abilities": []}
|
|
124
|
+
try:
|
|
125
|
+
keys = await asyncio.to_thread(hkc.client.list_abilities)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
return {"available": True, "abilities": [], "error": str(e)}
|
|
128
|
+
return {"available": True, "abilities": list(keys)}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@app.get("/api/knowledge/ability/{ability_key}")
|
|
132
|
+
async def knowledge_ability(ability_key: str):
|
|
133
|
+
"""编译一个能力,返回 coverage/skills/workflows。**证据驱动 gate**:知识覆盖不足时
|
|
134
|
+
返回 `sufficient=False` + coverage/missing_skills——这不是错误,是"知识还不足以具备该能力"
|
|
135
|
+
(Foxclaw 证据驱动哲学:没根据的能力不予授予)。只读、无 LLM。"""
|
|
136
|
+
hkc = _hkc_or_none()
|
|
137
|
+
if hkc is None or not hkc.available:
|
|
138
|
+
return {"available": False}
|
|
139
|
+
try:
|
|
140
|
+
ab = await asyncio.to_thread(hkc.client.compile_ability, ability_key)
|
|
141
|
+
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
|
+
if _HKCInsuffCov is not None:
|
|
148
|
+
is_insufficient = isinstance(e, _HKCInsuffCov)
|
|
149
|
+
else:
|
|
150
|
+
is_insufficient = hasattr(e, "missing_skills")
|
|
151
|
+
if is_insufficient:
|
|
152
|
+
return {"available": True, "sufficient": False,
|
|
153
|
+
"coverage": getattr(e, "coverage", {}),
|
|
154
|
+
"missing_skills": getattr(e, "missing_skills", []),
|
|
155
|
+
"reason": str(e)}
|
|
156
|
+
raise HTTPException(404, f"能力编译失败(未知 key 或图错误): {e}")
|
|
157
|
+
return {"available": True, "sufficient": True, "ability": {
|
|
158
|
+
"ability_key": ab.ability_key, "display_name": ab.display_name,
|
|
159
|
+
"domain": ab.domain, "coverage": ab.coverage, "version": ab.version,
|
|
160
|
+
"skills": [{"skill_key": s.skill_key, "display_name": s.display_name,
|
|
161
|
+
"coverage": s.coverage, "concept_hits": s.concept_hits}
|
|
162
|
+
for s in ab.skills],
|
|
163
|
+
"workflows": ab.workflows}}
|