foxclaw 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -8
- package/package.json +1 -1
- package/python/.env.example +8 -0
- package/python/.github/workflows/ci.yml +47 -2
- package/python/CHANGELOG.md +57 -0
- package/python/CHANGELOG_CN.md +57 -0
- package/python/README.md +11 -2
- package/python/README_CN.md +32 -20
- package/python/bench/bm25_audit.py +1 -14
- package/python/bench/bm25_regression.py +0 -1
- package/python/bench/bm25_stress.py +0 -4
- package/python/bench/cd_audit.py +0 -23
- package/python/bench/chaos.py +6 -8
- package/python/bench/e2e_real_models.py +0 -1
- package/python/bench/llm_retry_test.py +2 -9
- package/python/bench/load_test.py +5 -8
- package/python/bench/vector_ivf_upgrade.py +1 -7
- package/python/docs/RELEASE.md +4 -3
- package/python/docs/RUN_CN.md +2 -2
- package/python/evals/governance/cases.py +0 -37
- package/python/evals/governance/run_evals.py +5 -37
- package/python/examples/governance_middleware_demo.py +1 -6
- package/python/examples/langgraph_governance_demo.py +7 -11
- package/python/foxclaw/__init__.py +34 -152
- package/python/foxclaw/agents/base.py +65 -136
- package/python/foxclaw/agents/impl/_helpers.py +2 -10
- package/python/foxclaw/agents/impl/finance.py +4 -21
- package/python/foxclaw/agents/impl/office.py +7 -24
- package/python/foxclaw/agents/impl/supervisor.py +12 -79
- package/python/foxclaw/agents/mixins/_context.py +0 -23
- package/python/foxclaw/agents/mixins/augment.py +10 -16
- package/python/foxclaw/agents/mixins/llm.py +7 -6
- package/python/foxclaw/agents/mixins/memory.py +7 -1
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +0 -6
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
- package/python/foxclaw/api/middleware.py +5 -28
- package/python/foxclaw/api/routes/ahs.py +60 -73
- package/python/foxclaw/api/routes/attachments.py +5 -6
- package/python/foxclaw/api/routes/browser.py +7 -11
- package/python/foxclaw/api/routes/domain.py +18 -26
- package/python/foxclaw/api/routes/knowledge.py +3 -21
- package/python/foxclaw/api/routes/llm_admin.py +21 -33
- package/python/foxclaw/api/routes/memory.py +28 -12
- package/python/foxclaw/api/routes/orchestration.py +3 -5
- package/python/foxclaw/api/routes/recovery.py +4 -3
- package/python/foxclaw/api/routes/review.py +2 -11
- package/python/foxclaw/api/routes/search.py +3 -7
- package/python/foxclaw/api/routes/system.py +33 -49
- package/python/foxclaw/api/routes/task.py +31 -49
- package/python/foxclaw/api/routes/ws.py +2 -6
- package/python/foxclaw/api/spine.py +1 -4
- package/python/foxclaw/browser/action/action_executor.py +1 -5
- package/python/foxclaw/browser/action/action_types.py +0 -1
- package/python/foxclaw/browser/action/url_policy.py +1 -5
- package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
- package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
- package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
- package/python/foxclaw/browser/perception/element_indexer.py +1 -2
- package/python/foxclaw/browser/service.py +7 -11
- package/python/foxclaw/capability/embedding_service.py +4 -6
- package/python/foxclaw/channels/__init__.py +9 -11
- package/python/foxclaw/channels/discord.py +4 -13
- package/python/foxclaw/channels/dispatcher.py +2 -27
- package/python/foxclaw/channels/feishu.py +10 -20
- package/python/foxclaw/channels/openclaw_bridge.py +4 -9
- package/python/foxclaw/channels/signing.py +9 -9
- package/python/foxclaw/channels/telegram.py +3 -4
- package/python/foxclaw/channels/weixin.py +7 -17
- package/python/foxclaw/cli.py +42 -13
- package/python/foxclaw/cognitive/reasoner.py +9 -10
- package/python/foxclaw/cognitive/state.py +18 -24
- package/python/foxclaw/core/audit_chain.py +1 -3
- package/python/foxclaw/core/audit_sink.py +3 -3
- package/python/foxclaw/core/compaction.py +8 -26
- package/python/foxclaw/core/config.py +2 -2
- package/python/foxclaw/core/confirm_nonce.py +6 -12
- package/python/foxclaw/core/envfile.py +2 -2
- package/python/foxclaw/core/failure_policy.py +17 -26
- package/python/foxclaw/core/gateway.py +6 -10
- package/python/foxclaw/core/governance_audit.py +3 -7
- package/python/foxclaw/core/idempotency.py +15 -22
- package/python/foxclaw/core/injection_guard.py +10 -29
- package/python/foxclaw/core/memory_adapter.py +30 -57
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +36 -148
- package/python/foxclaw/core/paths.py +1 -1
- package/python/foxclaw/core/recovery.py +21 -34
- package/python/foxclaw/core/scheduler.py +23 -26
- package/python/foxclaw/core/secrets.py +0 -3
- package/python/foxclaw/core/sessions.py +4 -6
- package/python/foxclaw/core/settings.py +16 -32
- package/python/foxclaw/core/shared_write.py +11 -23
- package/python/foxclaw/core/steering_queue.py +12 -11
- package/python/foxclaw/core/system_bus.py +1 -1
- package/python/foxclaw/core/thought_chain.py +30 -32
- package/python/foxclaw/core/token_meter.py +9 -13
- package/python/foxclaw/core/tools.py +4 -12
- package/python/foxclaw/core/tracing.py +25 -40
- package/python/foxclaw/core/truth_pipeline.py +5 -9
- package/python/foxclaw/core/update_check.py +149 -0
- package/python/foxclaw/core/vec_recall_adapter.py +11 -37
- package/python/foxclaw/core/verify.py +6 -14
- package/python/foxclaw/core/web_search.py +4 -28
- package/python/foxclaw/finance/evidence.py +14 -18
- package/python/foxclaw/finance/fx.py +1 -3
- package/python/foxclaw/finance/graph.py +9 -18
- package/python/foxclaw/finance/models.py +20 -24
- package/python/foxclaw/finance/nlparse.py +12 -72
- package/python/foxclaw/finance/policy.py +8 -36
- package/python/foxclaw/finance/reasoner.py +3 -11
- package/python/foxclaw/finance/reasoning.py +13 -34
- package/python/foxclaw/finance/reconcile.py +5 -10
- package/python/foxclaw/finance/runtime.py +19 -46
- package/python/foxclaw/governance/middleware.py +13 -21
- package/python/foxclaw/harness/_base.py +2 -8
- package/python/foxclaw/harness/_policy.py +19 -38
- package/python/foxclaw/harness/drift.py +1 -13
- package/python/foxclaw/harness/event_bus.py +2 -5
- package/python/foxclaw/harness/harness.py +9 -97
- package/python/foxclaw/harness/loop_detection.py +11 -18
- package/python/foxclaw/itinerary/amap_client.py +2 -5
- package/python/foxclaw/itinerary/models.py +7 -7
- package/python/foxclaw/itinerary/reasoner.py +1 -6
- package/python/foxclaw/itinerary/reasoning.py +15 -21
- package/python/foxclaw/itinerary/runtime.py +8 -12
- package/python/foxclaw/kernel/capabilities.py +5 -6
- package/python/foxclaw/kernel/policy_gate.py +4 -10
- package/python/foxclaw/kernel/process_manager.py +17 -36
- package/python/foxclaw/kernel/scheduler.py +3 -9
- package/python/foxclaw/kernel/sentinel.py +13 -23
- package/python/foxclaw/kernel/sentinel_client.py +12 -15
- package/python/foxclaw/kernel/spine.py +9 -9
- package/python/foxclaw/knowledge/__init__.py +4 -20
- package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
- package/python/foxclaw/learning/knowledge_graph.py +4 -9
- package/python/foxclaw/learning/models.py +13 -13
- package/python/foxclaw/learning/reasoner.py +1 -4
- package/python/foxclaw/learning/runtime.py +19 -31
- package/python/foxclaw/learning/spaced_repetition.py +2 -7
- package/python/foxclaw/mcp_server.py +2 -3
- package/python/foxclaw/orchestration/__init__.py +16 -33
- package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
- package/python/foxclaw/pipeline/stage_meta.py +21 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
- package/python/foxclaw/planning/llm_planner.py +3 -3
- package/python/foxclaw/planning/planner.py +6 -11
- package/python/foxclaw/search/agent_port.py +7 -11
- package/python/foxclaw/search/evidence.py +0 -4
- package/python/foxclaw/search/internal_search.py +3 -5
- package/python/foxclaw/search/models.py +11 -13
- package/python/foxclaw/search/planner.py +3 -3
- package/python/foxclaw/search/retriever.py +1 -1
- package/python/foxclaw/search/runtime.py +1 -8
- package/python/foxclaw/skills/__init__.py +46 -100
- package/python/foxclaw/skills/archive.py +111 -0
- package/python/foxclaw/skills/fetch.py +226 -0
- package/python/foxclaw/skills/hooks_spec.py +21 -25
- package/python/foxclaw/skills/install_cmd.py +216 -0
- package/python/foxclaw/skills/lifecycle.py +16 -42
- package/python/foxclaw/skills/markdown_skill.py +10 -9
- package/python/foxclaw/skills/mcp_client.py +5 -11
- package/python/foxclaw/skills/mcp_store.py +0 -3
- package/python/foxclaw/skills/permission.py +13 -48
- package/python/foxclaw/skills/pipeline.py +21 -53
- package/python/foxclaw/skills/policy.py +4 -10
- package/python/foxclaw/skills/review.py +3 -3
- package/python/frontend/deck/auth.js +0 -7
- package/python/frontend/deck/chat_model_picker.js +0 -54
- package/python/frontend/deck/domain.js +0 -20
- package/python/frontend/deck/main.js +0 -144
- package/python/frontend/deck/markdown.js +0 -4
- package/python/frontend/deck/settings.js +1 -18
- package/python/frontend/deck/state.js +0 -1
- package/python/frontend/deck/util.js +0 -2
- package/python/frontend/deck/visuals.js +0 -22
- package/python/frontend/planet/planet.js +0 -201
- package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
- package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
- package/python/frontend/v3/index.html +3 -3
- package/python/frontend/widget_runtime.js +0 -43
- package/python/frontend/widgets.js +0 -57
- package/python/frontend_src/index.html +1 -1
- package/python/frontend_src/package-lock.json +2 -2
- package/python/frontend_src/package.json +1 -1
- package/python/frontend_src/src/App.vue +3 -7
- package/python/frontend_src/src/api.js +0 -7
- package/python/frontend_src/src/components/ChatDock.vue +27 -39
- package/python/frontend_src/src/components/DocViewer.vue +0 -9
- package/python/frontend_src/src/components/Inspector.vue +0 -6
- package/python/frontend_src/src/components/LeftRail.vue +60 -39
- package/python/frontend_src/src/components/MenuPopover.vue +9 -11
- package/python/frontend_src/src/components/MessageStream.vue +18 -30
- package/python/frontend_src/src/components/ModelPicker.vue +0 -5
- package/python/frontend_src/src/components/QuickIngest.vue +0 -6
- package/python/frontend_src/src/components/Stage.vue +71 -25
- package/python/frontend_src/src/components/ToolMenu.vue +0 -3
- package/python/frontend_src/src/components/TopBar.vue +38 -10
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
- package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
- package/python/frontend_src/src/lib/fmt.js +0 -4
- package/python/frontend_src/src/lib/i18n.js +53 -0
- package/python/frontend_src/src/lib/locales/en.js +173 -0
- package/python/frontend_src/src/lib/locales/zh.js +174 -0
- package/python/frontend_src/src/lib/md.js +0 -14
- package/python/frontend_src/src/main.js +2 -0
- package/python/frontend_src/src/stores/app.js +17 -32
- package/python/frontend_src/src/stores/chat.js +0 -34
- package/python/frontend_src/src/stores/model.js +0 -8
- package/python/frontend_src/src/stores/panels.js +0 -41
- package/python/frontend_src/src/stores/profile.js +31 -0
- package/python/frontend_src/src/stores/system.js +25 -0
- package/python/frontend_src/vite.config.js +0 -7
- package/python/pyproject.toml +6 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +3 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_ahs_p2.py +4 -25
- package/python/tests/test_anthropic_tools.py +1 -15
- package/python/tests/test_api.py +19 -9
- package/python/tests/test_api_auth.py +1 -5
- package/python/tests/test_attachments.py +9 -25
- package/python/tests/test_audit_chain.py +2 -7
- package/python/tests/test_audit_sink.py +0 -6
- package/python/tests/test_browser.py +2 -6
- package/python/tests/test_browser_request_guard.py +0 -4
- package/python/tests/test_capabilities.py +0 -6
- package/python/tests/test_channel_signing.py +2 -9
- package/python/tests/test_channels.py +1 -11
- package/python/tests/test_config_layering.py +0 -4
- package/python/tests/test_confirm_nonce.py +1 -12
- package/python/tests/test_context_compaction_wiring.py +1 -7
- package/python/tests/test_dispatch.py +0 -6
- package/python/tests/test_domain_evals.py +11 -80
- package/python/tests/test_domain_import.py +0 -6
- package/python/tests/test_domain_reasoners.py +0 -14
- package/python/tests/test_e2e.py +0 -17
- package/python/tests/test_emb_cache.py +7 -17
- package/python/tests/test_embedding_singleton.py +1 -3
- package/python/tests/test_evals_smoke.py +0 -24
- package/python/tests/test_event_spine.py +10 -18
- package/python/tests/test_evidence_semantic.py +1 -14
- package/python/tests/test_evidence_sse.py +3 -6
- package/python/tests/test_file_ingest.py +0 -11
- package/python/tests/test_finance.py +0 -9
- package/python/tests/test_finance_ledger.py +5 -12
- package/python/tests/test_finance_persist.py +8 -12
- package/python/tests/test_gateway.py +3 -22
- package/python/tests/test_governance.py +5 -52
- package/python/tests/test_governance_audit.py +4 -39
- package/python/tests/test_governance_middleware.py +1 -7
- package/python/tests/test_hkc.py +6 -24
- package/python/tests/test_hkc_index_build_manager.py +0 -33
- package/python/tests/test_hkc_manifest_reuse.py +4 -27
- package/python/tests/test_hkc_search_event_sync.py +3 -54
- package/python/tests/test_import_path_guard.py +0 -6
- package/python/tests/test_injection_guard.py +18 -36
- package/python/tests/test_integration.py +5 -29
- package/python/tests/test_itinerary.py +0 -3
- package/python/tests/test_kee_semantic.py +0 -5
- package/python/tests/test_kernel.py +1 -11
- package/python/tests/test_learning.py +3 -12
- package/python/tests/test_llm_admin.py +4 -19
- package/python/tests/test_llm_planner.py +1 -23
- package/python/tests/test_markdown_skills.py +3 -14
- package/python/tests/test_markdown_skills_openclaw.py +4 -9
- package/python/tests/test_mcp_connectors.py +3 -15
- package/python/tests/test_mcp_guardian_discovery.py +2 -6
- package/python/tests/test_mcp_no_memory.py +3 -3
- package/python/tests/test_mcp_policy_gate.py +0 -1
- package/python/tests/test_mcp_server.py +6 -9
- package/python/tests/test_memory_recall.py +1 -4
- package/python/tests/test_metrics.py +0 -8
- package/python/tests/test_multi_chain.py +1 -13
- package/python/tests/test_multimodal.py +5 -18
- package/python/tests/test_negotiation.py +3 -13
- package/python/tests/test_orchestration.py +1 -11
- package/python/tests/test_orchestration_decoupled.py +0 -6
- package/python/tests/test_p3_p5_hardening.py +3 -17
- package/python/tests/test_parallel_dispatch.py +4 -12
- package/python/tests/test_pipeline.py +17 -58
- package/python/tests/test_plan_visible.py +4 -4
- package/python/tests/test_planner.py +0 -7
- package/python/tests/test_policy_gate.py +2 -3
- package/python/tests/test_profile.py +67 -0
- package/python/tests/test_readme_claims.py +163 -0
- package/python/tests/test_reasoner.py +2 -17
- package/python/tests/test_release_check.py +2 -13
- package/python/tests/test_resume_checkpoint.py +7 -15
- package/python/tests/test_routing.py +2 -12
- package/python/tests/test_scheduler.py +7 -10
- package/python/tests/test_search.py +1 -11
- package/python/tests/test_secrets.py +4 -10
- package/python/tests/test_semantic.py +1 -10
- package/python/tests/test_sessions.py +3 -4
- package/python/tests/test_settings.py +2 -18
- package/python/tests/test_shadow_learning.py +3 -43
- package/python/tests/test_shared_write.py +0 -7
- package/python/tests/test_skill_taint_gate.py +1 -10
- package/python/tests/test_skills.py +2 -18
- package/python/tests/test_skills_fetch.py +282 -0
- package/python/tests/test_stats_and_events.py +2 -5
- package/python/tests/test_streaming.py +2 -7
- package/python/tests/test_supervisor_pipeline.py +4 -5
- package/python/tests/test_supervisor_triage.py +4 -10
- package/python/tests/test_taint.py +2 -7
- package/python/tests/test_task_stream.py +1 -12
- package/python/tests/test_thinking_sse.py +0 -1
- package/python/tests/test_tool_center.py +1 -17
- package/python/tests/test_tool_parallel.py +2 -2
- package/python/tests/test_tool_params.py +1 -5
- package/python/tests/test_tool_use.py +0 -13
- package/python/tests/test_toplevel_concurrency.py +3 -4
- package/python/tests/test_tracing.py +7 -41
- package/python/tests/test_truth_crystallizer.py +4 -6
- package/python/tests/test_truth_flow.py +2 -7
- package/python/tests/test_update_check.py +95 -0
- package/python/tests/test_verify_evidence.py +8 -21
- package/python/tests/test_version_sync.py +35 -6
- package/python/tests/test_web_trigger.py +0 -17
- package/python/tools/check_faiss_type.py +0 -1
- package/python/tools/check_log.ps1 +0 -8
- package/python/tools/mypy_baseline.txt +12 -4
- package/python/tools/mypy_ratchet.py +52 -6
- package/python/tools/pack.py +0 -5
- package/python/tools/release_check.py +7 -7
- package/python/tools/restart_foxclaw.ps1 +5 -8
- package/python/tools/shared_write_audit.py +1 -15
- package/python/tools/size_check.py +1 -1
- package/python/tools/strip_comments.py +228 -0
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -20,6 +20,7 @@ import threading
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
|
|
22
22
|
from .shared_write import guard_shared_write
|
|
23
|
+
from typing import Any
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def _ensure_hmr_importable():
|
|
@@ -41,19 +42,18 @@ class HMRAdapter:
|
|
|
41
42
|
若 HMR 不可用,调用方应回退到 MockHMR。
|
|
42
43
|
"""
|
|
43
44
|
|
|
44
|
-
def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str = None,
|
|
45
|
-
embedding_model: str = None):
|
|
45
|
+
def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str | None = None,
|
|
46
|
+
embedding_model: str | None = None):
|
|
46
47
|
"""【P1·share-bge-m3】新增 embedding_model 参数。
|
|
47
48
|
上层(Foxclaw)通常在 HKC 已装配后再调本构造,bge-m3 已入 HKC 缓存;
|
|
48
49
|
传具体模型名(如 HKC_EMBEDDING_MODEL)使 HMR 与 HKC 共享同一 SentenceTransformer 实例。
|
|
49
50
|
不传则走 vendor/hmr/storage/vector_store.py 的 env 解析链(HKC_EMBEDDING_MODEL>bge-m3)。"""
|
|
50
51
|
_ensure_hmr_importable()
|
|
51
|
-
from hmr.core.hmr import HMR
|
|
52
|
+
from hmr.core.hmr import HMR
|
|
52
53
|
|
|
53
|
-
# 不传 None 就用 .env / HKC 的配置;只有上层显式 override 才用 _embedding_model
|
|
54
54
|
import os as _os
|
|
55
55
|
resolved_model = (embedding_model
|
|
56
|
-
or _os.environ.get("EMBEDDING_MODEL")
|
|
56
|
+
or _os.environ.get("EMBEDDING_MODEL")
|
|
57
57
|
or _os.environ.get("HKC_EMBEDDING_MODEL")
|
|
58
58
|
or _os.environ.get("FOXCLAW_EMBEDDING_MODEL")
|
|
59
59
|
or "all-MiniLM-L6-v2")
|
|
@@ -61,13 +61,8 @@ class HMRAdapter:
|
|
|
61
61
|
embedding_model=resolved_model)
|
|
62
62
|
self.backend = "HMR"
|
|
63
63
|
self.version = getattr(self._hmr, "VERSION", "2.0.0")
|
|
64
|
-
# 并行子 Agent 会并发 ingest/recall,底层 HMR 的向量库/记忆图并非线程安全
|
|
65
|
-
# (向量索引并发 add+search 是未定义行为)。串行化对 HMR 的访问。叶子组件:
|
|
66
|
-
# 锁内不回调 pm/bus,故不参与全局锁序,无死锁风险。
|
|
67
64
|
self._lock = threading.RLock()
|
|
68
65
|
|
|
69
|
-
# HMR 未给 title 时会退化成 "Concept 03:11" 这类纯时间戳(见 hmr.py 的
|
|
70
|
-
# `title or f"{type.title()} {HH:MM}"`),在前端"记忆信息条"里一眼看不出是什么。
|
|
71
66
|
_AUTO_TITLE = re.compile(r"^[A-Za-z_]+\s+\d{1,2}:\d{2}$")
|
|
72
67
|
|
|
73
68
|
@staticmethod
|
|
@@ -80,23 +75,22 @@ class HMRAdapter:
|
|
|
80
75
|
if s.startswith(pfx):
|
|
81
76
|
s = s[len(pfx):].strip()
|
|
82
77
|
break
|
|
83
|
-
for sep in ("→", "->"):
|
|
78
|
+
for sep in ("→", "->"):
|
|
84
79
|
if sep in s:
|
|
85
80
|
s = s.split(sep, 1)[0].strip()
|
|
86
81
|
break
|
|
87
82
|
s = re.split(r"[。!?!?\n\r]", s, 1)[0].strip()
|
|
88
83
|
return " ".join(s.split())[:24]
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
title: str = None, tags: list = None, shared: bool = False, **kwargs):
|
|
85
|
+
def ingest(self, content: str, type: str = "concept", agent_id: str | None = None,
|
|
86
|
+
title: str | None = None, tags: list | None = None, shared: bool = False, **kwargs):
|
|
93
87
|
"""统一摄入接口。agent_id 写入 tags 实现隔离;shared=True 则写入共享层。
|
|
94
88
|
未给 title 时从内容提炼一个可读标题(否则 HMR 会生成纯时间戳标题)。"""
|
|
95
89
|
meta_tags = list(tags or [])
|
|
96
90
|
if agent_id:
|
|
97
91
|
meta_tags.append(f"agent:{agent_id}")
|
|
98
92
|
if shared:
|
|
99
|
-
meta_tags.append("shared")
|
|
93
|
+
meta_tags.append("shared")
|
|
100
94
|
metadata = {"tags": meta_tags}
|
|
101
95
|
if "confidence" in kwargs:
|
|
102
96
|
metadata["confidence"] = kwargs["confidence"]
|
|
@@ -105,15 +99,12 @@ class HMRAdapter:
|
|
|
105
99
|
mtype = type if type in valid else "concept"
|
|
106
100
|
if not title:
|
|
107
101
|
title = self._derive_title(content) or None
|
|
108
|
-
# 【P0-3】共享层写入闸门:必须在**落库之前**判定(见 core/shared_write.py)。
|
|
109
|
-
# 未挂策略时返回 None 直接放行;audit 只记事件;enforce 才抛 SharedWriteDenied。
|
|
110
102
|
if shared:
|
|
111
103
|
guard_shared_write(self, agent_id, content, mtype, meta_tags)
|
|
112
104
|
with self._lock:
|
|
113
105
|
return self._hmr.ingest(content, memory_type=mtype, title=title, metadata=metadata)
|
|
114
106
|
|
|
115
|
-
|
|
116
|
-
def recall(self, query: str = "", agent_id: str = None, top_k: int = 5,
|
|
107
|
+
def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5,
|
|
117
108
|
include_shared: bool = True, **kwargs):
|
|
118
109
|
"""统一召回接口。返回 list[dict]。
|
|
119
110
|
默认召回:本 Agent 的记忆 + 共享层(shared)记忆;include_shared=False 只看自己。
|
|
@@ -153,8 +144,7 @@ class HMRAdapter:
|
|
|
153
144
|
"tags": tags,
|
|
154
145
|
"_ts": getattr(m, "created_at", None) or getattr(m, "timestamp", None) or "",
|
|
155
146
|
})
|
|
156
|
-
out.sort(key=lambda x: str(x["_ts"]), reverse=True)
|
|
157
|
-
# 按内容去重(保留最新):否则同一偏好反复表达存成多条,会挤满 limit、把别的偏好挤出召回
|
|
147
|
+
out.sort(key=lambda x: str(x["_ts"]), reverse=True)
|
|
158
148
|
deduped, seen_c = [], set()
|
|
159
149
|
for x in out:
|
|
160
150
|
if x["content"] in seen_c:
|
|
@@ -195,7 +185,7 @@ class HMRAdapter:
|
|
|
195
185
|
try:
|
|
196
186
|
with self._lock:
|
|
197
187
|
mems = fs.list_memories()
|
|
198
|
-
cand = []
|
|
188
|
+
cand = []
|
|
199
189
|
for m in mems:
|
|
200
190
|
tags = getattr(m, "tags", []) or []
|
|
201
191
|
if agent_id:
|
|
@@ -210,7 +200,7 @@ class HMRAdapter:
|
|
|
210
200
|
scored = []
|
|
211
201
|
for m, tags in cand:
|
|
212
202
|
vec = vs.vectors.get(getattr(m, "id", None))
|
|
213
|
-
if vec is None:
|
|
203
|
+
if vec is None:
|
|
214
204
|
try:
|
|
215
205
|
text = getattr(m, "semantic_summary", "") or (m.content or "")[:500]
|
|
216
206
|
vec = vs.embedder.embed(text)
|
|
@@ -232,7 +222,7 @@ class HMRAdapter:
|
|
|
232
222
|
})
|
|
233
223
|
return out
|
|
234
224
|
except Exception:
|
|
235
|
-
return None
|
|
225
|
+
return None
|
|
236
226
|
|
|
237
227
|
def _legacy_recall(self, query, agent_id, top_k, include_shared):
|
|
238
228
|
"""回退路径:全局召回再按 tag 过滤(HMR 结构不可访问时用)。有完整性局限,见 recall。"""
|
|
@@ -250,7 +240,7 @@ class HMRAdapter:
|
|
|
250
240
|
own = f"agent:{agent_id}" in tags
|
|
251
241
|
shared = include_shared and ("shared" in tags)
|
|
252
242
|
if not (own or shared):
|
|
253
|
-
continue
|
|
243
|
+
continue
|
|
254
244
|
out.append({
|
|
255
245
|
"id": getattr(m, "id", ""),
|
|
256
246
|
"title": getattr(m, "title", ""),
|
|
@@ -264,11 +254,6 @@ class HMRAdapter:
|
|
|
264
254
|
break
|
|
265
255
|
return out
|
|
266
256
|
|
|
267
|
-
# ── 工作区 ────────────────────────────────────────────
|
|
268
|
-
# 【深审修复·补齐锁覆盖】适配层自述"串行化**所有** HMR 访问"(底层向量库/记忆图非线程安全),
|
|
269
|
-
# 但此前只有 ingest/recall 加锁;下列同样触碰 self._hmr 的方法漏了锁——与并发 ingest/recall
|
|
270
|
-
# (并行子 Agent / 并发请求)同时跑会读到 mutation 中的向量索引/字典(未定义行为/迭代崩溃)。
|
|
271
|
-
# 统一补 self._lock(RLock 叶子锁,锁内不回调 pm/bus,无死锁)。
|
|
272
257
|
def get_workspace(self, agent_id: str):
|
|
273
258
|
with self._lock:
|
|
274
259
|
return self._hmr.get_workspace(agent_id)
|
|
@@ -277,7 +262,6 @@ class HMRAdapter:
|
|
|
277
262
|
with self._lock:
|
|
278
263
|
return self._hmr.save_workspace(agent_id)
|
|
279
264
|
|
|
280
|
-
# ── 运行时状态 ────────────────────────────────────────
|
|
281
265
|
def save_runtime_state(self, **kwargs):
|
|
282
266
|
with self._lock:
|
|
283
267
|
return self._hmr.save_runtime_state(**kwargs)
|
|
@@ -286,7 +270,6 @@ class HMRAdapter:
|
|
|
286
270
|
with self._lock:
|
|
287
271
|
return self._hmr.restore_runtime_state()
|
|
288
272
|
|
|
289
|
-
# ── 系统状态 ──────────────────────────────────────────
|
|
290
273
|
def get_system_status(self):
|
|
291
274
|
try:
|
|
292
275
|
with self._lock:
|
|
@@ -307,8 +290,10 @@ class HMRAdapter:
|
|
|
307
290
|
|
|
308
291
|
def _ts(m):
|
|
309
292
|
v = getattr(m, "created_at", None)
|
|
293
|
+
if v is None:
|
|
294
|
+
return 0.0
|
|
310
295
|
try:
|
|
311
|
-
return v.timestamp() if hasattr(v, "timestamp") else float(v
|
|
296
|
+
return v.timestamp() if hasattr(v, "timestamp") else float(v)
|
|
312
297
|
except Exception:
|
|
313
298
|
return 0.0
|
|
314
299
|
|
|
@@ -325,11 +310,12 @@ class HMRAdapter:
|
|
|
325
310
|
break
|
|
326
311
|
created = getattr(m, "created_at", None)
|
|
327
312
|
title = (getattr(m, "title", "") or "").strip()
|
|
328
|
-
# 历史记忆多是 "Concept 03:11" 这类时间戳标题(修复前存的)→ 展示时现推一个可读的,
|
|
329
|
-
# 让老数据也能看懂;推不出来才退回原标题。
|
|
330
313
|
if not title or self._AUTO_TITLE.match(title):
|
|
331
314
|
title = self._derive_title(
|
|
332
315
|
getattr(m, "content", "") or getattr(m, "semantic_summary", "")) or title
|
|
316
|
+
created_at = str(created or "")
|
|
317
|
+
if created is not None and hasattr(created, "isoformat"):
|
|
318
|
+
created_at = created.isoformat()
|
|
333
319
|
out.append({
|
|
334
320
|
"id": getattr(m, "id", ""),
|
|
335
321
|
"type": getattr(m, "type", ""),
|
|
@@ -337,7 +323,7 @@ class HMRAdapter:
|
|
|
337
323
|
"summary": (getattr(m, "semantic_summary", "") or getattr(m, "content", "") or "")[:180],
|
|
338
324
|
"agent": agent,
|
|
339
325
|
"shared": "shared" in tags,
|
|
340
|
-
"created_at":
|
|
326
|
+
"created_at": created_at,
|
|
341
327
|
})
|
|
342
328
|
return out
|
|
343
329
|
|
|
@@ -346,17 +332,11 @@ class HMRAdapter:
|
|
|
346
332
|
total = 0
|
|
347
333
|
relations = 0
|
|
348
334
|
shared_count = 0
|
|
349
|
-
by_agent = {}
|
|
335
|
+
by_agent: dict[str, Any] = {}
|
|
350
336
|
embedding = "TF-IDF n-gram"
|
|
351
337
|
try:
|
|
352
|
-
# 序列化对 HMR 内部结构(向量库/记忆图)的读,与 ingest/recall 的写互斥,
|
|
353
|
-
# 避免并发迭代 store.memories("dict changed size")/读 mutation 中的 faiss 索引。
|
|
354
338
|
with self._lock:
|
|
355
|
-
|
|
356
|
-
# 历史 bug:此前从 vector_store 探测 count/size/memories 取 total——但真实记忆存在
|
|
357
|
-
# memory_fs,vector_store 只存嵌入且无这些属性 → total 恒为 0,而 relations 却有值(109),
|
|
358
|
-
# 自相矛盾。改为直接数 memory_fs.list_memories()(与 get_statistics 同源)。
|
|
359
|
-
mems = []
|
|
339
|
+
mems: list[Any] = []
|
|
360
340
|
mfs = getattr(self._hmr, "memory_fs", None)
|
|
361
341
|
if mfs is not None and hasattr(mfs, "list_memories"):
|
|
362
342
|
try:
|
|
@@ -364,12 +344,11 @@ class HMRAdapter:
|
|
|
364
344
|
except Exception:
|
|
365
345
|
mems = []
|
|
366
346
|
total = len(mems)
|
|
367
|
-
if not total:
|
|
347
|
+
if not total:
|
|
368
348
|
vs0 = getattr(self._hmr, "vector_store", None)
|
|
369
349
|
vecs = getattr(vs0, "vectors", None) if vs0 is not None else None
|
|
370
350
|
if isinstance(vecs, dict):
|
|
371
351
|
total = len(vecs)
|
|
372
|
-
# shared / 按 Agent 分布:agent_id 编码在 tags 里的 "agent:<id>"
|
|
373
352
|
for m in mems:
|
|
374
353
|
tags = getattr(m, "tags", None)
|
|
375
354
|
if tags is None and isinstance(m, dict):
|
|
@@ -386,7 +365,6 @@ class HMRAdapter:
|
|
|
386
365
|
edges = getattr(graph, "edges", None)
|
|
387
366
|
if edges is not None:
|
|
388
367
|
relations = len(edges) if hasattr(edges, "__len__") else 0
|
|
389
|
-
# embedding 模型探测
|
|
390
368
|
emb = getattr(self._hmr, "embedding", None) or getattr(self._hmr, "embedder", None)
|
|
391
369
|
if emb is not None:
|
|
392
370
|
name = type(emb).__name__
|
|
@@ -424,18 +402,13 @@ class MockHMR:
|
|
|
424
402
|
pass
|
|
425
403
|
|
|
426
404
|
def ingest(self, content, type="concept", agent_id=None, shared=False, **kwargs):
|
|
427
|
-
# 【P0-3】与 HMRAdapter 同一道闸门:Mock 后端也过策略。否则在无 HMR 的环境里
|
|
428
|
-
# 策略形同虚设 —— 而测试与降级场景恰好都落在这一支,那是最需要行为一致的地方。
|
|
429
405
|
if shared:
|
|
430
406
|
guard_shared_write(self, agent_id, content, type, kwargs.get("tags") or [])
|
|
431
407
|
from types import SimpleNamespace
|
|
432
|
-
# 注意:不能用内置 type() 构造对象——参数名 type 已覆盖内置 type。
|
|
433
408
|
return SimpleNamespace(id="mock_mem", type=type)
|
|
434
409
|
|
|
435
|
-
def recall(self, query: str = "", agent_id: str = None, top_k: int = 5, **kwargs):
|
|
410
|
+
def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5, **kwargs):
|
|
436
411
|
from types import SimpleNamespace
|
|
437
|
-
# 返回带 memory_objects 的对象,与真实 HMR 的 RecallResult 结构一致
|
|
438
|
-
# (HMRAdapter.recall 会读 result.memory_objects)。
|
|
439
412
|
return SimpleNamespace(memory_objects=[])
|
|
440
413
|
|
|
441
414
|
def save_runtime_state(self, **kwargs):
|
|
@@ -484,7 +457,7 @@ def _memory_chain_enabled() -> bool:
|
|
|
484
457
|
return raw.lower() in ("1", "true", "yes", "on")
|
|
485
458
|
|
|
486
459
|
|
|
487
|
-
def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = None):
|
|
460
|
+
def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str | None = None):
|
|
488
461
|
"""工厂:默认 HMR;`FOXCLAW_MEMORY_BACKEND=vecrecall` 时才切 vecRecall。
|
|
489
462
|
|
|
490
463
|
顺序 vecRecall → HMR → MockHMR,任一层失败退到下一层,且**明确打印**实际用的是
|
|
@@ -508,9 +481,9 @@ def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = N
|
|
|
508
481
|
print(f"[Foxclaw] ⚠ FOXCLAW_MEMORY_BACKEND={choice!r} 无法识别,按 HMR 处理")
|
|
509
482
|
|
|
510
483
|
try:
|
|
511
|
-
|
|
512
|
-
print(f"[Foxclaw] ✓ HMR v{
|
|
513
|
-
return
|
|
484
|
+
hmr = HMRAdapter(storage_path=storage_path, llm_api_key=llm_api_key)
|
|
485
|
+
print(f"[Foxclaw] ✓ HMR v{hmr.version} 已接入 (backend={hmr.backend})")
|
|
486
|
+
return hmr
|
|
514
487
|
except Exception as e:
|
|
515
488
|
print(f"[Foxclaw] ⚠ HMR 不可用 ({e.__class__.__name__}: {e}),降级 MockHMR")
|
|
516
489
|
return MockHMR()
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
import threading
|
|
6
6
|
from collections import defaultdict, deque
|
|
7
7
|
|
|
8
|
-
_MAX_SAMPLES = 500
|
|
8
|
+
_MAX_SAMPLES = 500
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class _Stat:
|
|
@@ -14,7 +14,7 @@ class _Stat:
|
|
|
14
14
|
def __init__(self):
|
|
15
15
|
self.count = 0
|
|
16
16
|
self.errors = 0
|
|
17
|
-
self.samples = deque(maxlen=_MAX_SAMPLES)
|
|
17
|
+
self.samples: deque[float] = deque(maxlen=_MAX_SAMPLES)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _pct(sorted_vals, p: float) -> float:
|
|
@@ -29,8 +29,8 @@ class MetricsCollector:
|
|
|
29
29
|
|
|
30
30
|
def __init__(self):
|
|
31
31
|
self._lock = threading.RLock()
|
|
32
|
-
self._ep: dict = defaultdict(_Stat)
|
|
33
|
-
self._llm: dict = defaultdict(_Stat)
|
|
32
|
+
self._ep: dict = defaultdict(_Stat)
|
|
33
|
+
self._llm: dict = defaultdict(_Stat)
|
|
34
34
|
self._start = time.time()
|
|
35
35
|
|
|
36
36
|
def record_request(self, endpoint: str, status: int, ms: float):
|
|
@@ -85,5 +85,4 @@ class MetricsCollector:
|
|
|
85
85
|
return "\n".join(out) + "\n"
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
# 进程级单例:中间件 + router 都往这里写,端点从这里读。
|
|
89
88
|
METRICS = MetricsCollector()
|