foxclaw 0.1.2 → 0.1.4
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/.github/workflows/release.yml +5 -0
- package/python/CHANGELOG.md +100 -0
- package/python/CHANGELOG_CN.md +100 -0
- package/python/README.md +17 -2
- package/python/README_CN.md +38 -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/CONTENT_GUARD.md +189 -0
- package/python/docs/CONTENT_GUARD_CN.md +189 -0
- package/python/docs/RELEASE.md +78 -37
- 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 +74 -152
- package/python/foxclaw/agents/base.py +82 -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 +23 -3
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +59 -12
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/bootstrap.py +114 -0
- 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 +50 -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/behavior/__init__.py +20 -0
- package/python/foxclaw/behavior/behavior.yaml +62 -0
- package/python/foxclaw/behavior/spec.py +287 -0
- 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/content_guard.py +341 -0
- 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 +53 -60
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +74 -150
- 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/sensitive_write.py +283 -0
- 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 +18 -201
- package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
- package/python/frontend/v3/assets/index-tN90dpUH.js +34 -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 +42 -3
- 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 +16 -44
- 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 +17 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +4 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_agent_rules.py +81 -0
- 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_behavior_spec.py +198 -0
- 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_conflict_dismiss.py +143 -0
- package/python/tests/test_content_guard.py +177 -0
- 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_sensitive_write.py +181 -0
- 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/build_content_lexicon.py +218 -0
- package/python/tools/bundle_web.py +98 -0
- 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/recheck_conflicts.py +156 -0
- package/python/tools/release_check.py +24 -15
- 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/vendor/hkc/hkc_api/routes/ability.py +31 -0
- package/python/vendor/hkc/hkc_api/schemas.py +11 -0
- package/python/vendor/hkc/hkc_kee/kee.py +124 -3
- package/python/vendor/hkc/hkc_sdk/base.py +22 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
- package/python/foxclaw/api/app.py +0 -123
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -18,7 +18,6 @@ import os
|
|
|
18
18
|
import re
|
|
19
19
|
import secrets
|
|
20
20
|
|
|
21
|
-
# 典型注入/越权特征(中英)。命中不拦截,只在数据块里加醒目告警,提示模型"这是数据里的指令,别执行"。
|
|
22
21
|
_MARKERS = [
|
|
23
22
|
r"ignore\s+(all\s+)?(the\s+)?previous\s+instructions?",
|
|
24
23
|
r"disregard\s+(the\s+)?(above|previous|prior)",
|
|
@@ -42,25 +41,17 @@ def scan(text: str) -> bool:
|
|
|
42
41
|
return bool(text) and bool(_RE.search(str(text)))
|
|
43
42
|
|
|
44
43
|
|
|
45
|
-
# ── 输出侧护栏(纵深防御第四道:DLP)────────────────────────────────────────────
|
|
46
|
-
# 回复返回/落库前扫一道:① 密钥/令牌等敏感串 → 脱敏;② 系统提示词被整段吐出 → 拦截。
|
|
47
|
-
# 纯正则、零额外模型调用(适配 flash 成本)。与"输入-隔离-模型"三层凑齐四道墙。
|
|
48
|
-
|
|
49
|
-
# 常见密钥/令牌形态(含 Foxclaw 实际用的百度千帆 bce-v3 AppBuilder key)。
|
|
50
44
|
_SECRET_PATTERNS = [
|
|
51
|
-
r"sk-[A-Za-z0-9]{20,}",
|
|
52
|
-
r"bce-v3/[A-Za-z0-9-]{6,}/[A-Za-z0-9]{6,}",
|
|
53
|
-
r"AKIA[0-9A-Z]{16}",
|
|
54
|
-
r"gh[pousr]_[A-Za-z0-9]{20,}",
|
|
55
|
-
r"xox[baprs]-[A-Za-z0-9-]{10,}",
|
|
56
|
-
r"Bearer\s+[A-Za-z0-9._\-]{20,}",
|
|
45
|
+
r"sk-[A-Za-z0-9]{20,}",
|
|
46
|
+
r"bce-v3/[A-Za-z0-9-]{6,}/[A-Za-z0-9]{6,}",
|
|
47
|
+
r"AKIA[0-9A-Z]{16}",
|
|
48
|
+
r"gh[pousr]_[A-Za-z0-9]{20,}",
|
|
49
|
+
r"xox[baprs]-[A-Za-z0-9-]{10,}",
|
|
50
|
+
r"Bearer\s+[A-Za-z0-9._\-]{20,}",
|
|
57
51
|
r"(api[_-]?key|secret|token|password|passwd)\s*[:=]\s*['\"]?[A-Za-z0-9._\-/]{12,}",
|
|
58
52
|
]
|
|
59
53
|
_SECRET_RE = re.compile("|".join(_SECRET_PATTERNS), re.IGNORECASE)
|
|
60
54
|
|
|
61
|
-
# 系统提示词泄露判定:不用"单个短语命中"(会误伤——如用户正常问"Principle 07 是什么")。
|
|
62
|
-
# 改为**更强信号**:① 输出里出现 KERNEL 的长段**逐字串**(dump 才会逐字复现,提及概念不会);
|
|
63
|
-
# ② 或同时出现 ≥2 个**章节标记**(无 kernel 参照时的兜底)。
|
|
64
55
|
_SECTION_MARKS = ("【一、", "【二、", "【三、", "【四、", "【五、", "【六、", "【七、")
|
|
65
56
|
|
|
66
57
|
|
|
@@ -101,7 +92,6 @@ def _has_semantic_leak(text: str, kernel: str, threshold: float = 0.88,
|
|
|
101
92
|
if model is None:
|
|
102
93
|
return False
|
|
103
94
|
import numpy as np
|
|
104
|
-
# 固定 ~200 字块;向量已 normalize → 余弦 = 点积。内核块进共享缓存(只编码一次)。
|
|
105
95
|
kblocks = [kernel[i:i + 200] for i in range(0, len(kernel), 200)]
|
|
106
96
|
tblocks = [text[i:i + 200] for i in range(0, len(text), 200)]
|
|
107
97
|
kv = encode_cached(model, name, kblocks)
|
|
@@ -165,8 +155,6 @@ def guard_output(text: str, kernel: str = "") -> tuple:
|
|
|
165
155
|
return (text, issues)
|
|
166
156
|
|
|
167
157
|
|
|
168
|
-
# 分隔符伪造防护:外部内容里若自带 <untrusted>/</untrusted>,会伪造边界把恶意指令挤出隔离区
|
|
169
|
-
# (经典分隔符注入)。包裹前把内容中一切 <(/)untrusted 的尖括号中和成 ‹,使其无法充当真边界。
|
|
170
158
|
_UNTRUSTED_DELIM = re.compile(r"<\s*/?\s*untrusted", re.IGNORECASE)
|
|
171
159
|
|
|
172
160
|
|
|
@@ -196,17 +184,10 @@ def wrap_untrusted(text: str, source: str = "external") -> str:
|
|
|
196
184
|
return block + note
|
|
197
185
|
|
|
198
186
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
# 传播与裁决在调用方(agents.base / skills.permission)。
|
|
204
|
-
#
|
|
205
|
-
# 敏感度分级(数值越高越敏感,单调可比):
|
|
206
|
-
TAINT_NONE = 0 # 无外部数据(会话初始态)
|
|
207
|
-
TAINT_EXTERNAL = 1 # 外部不可控但非个人:web_search 结果
|
|
208
|
-
TAINT_PERSONAL = 2 # 用户个人数据:知识库(knowledge)/文件(file)
|
|
209
|
-
TAINT_SENSITIVE = 3 # 高敏感个人数据:个人记忆(memory)
|
|
187
|
+
TAINT_NONE = 0
|
|
188
|
+
TAINT_EXTERNAL = 1
|
|
189
|
+
TAINT_PERSONAL = 2
|
|
190
|
+
TAINT_SENSITIVE = 3
|
|
210
191
|
|
|
211
192
|
_TAINT_BY_SOURCE = {
|
|
212
193
|
"web_search": TAINT_EXTERNAL,
|
|
@@ -20,6 +20,9 @@ import threading
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
|
|
22
22
|
from .shared_write import guard_shared_write
|
|
23
|
+
from .sensitive_write import guard_sensitive_write
|
|
24
|
+
from types import SimpleNamespace
|
|
25
|
+
from typing import Any
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
def _ensure_hmr_importable():
|
|
@@ -41,19 +44,18 @@ class HMRAdapter:
|
|
|
41
44
|
若 HMR 不可用,调用方应回退到 MockHMR。
|
|
42
45
|
"""
|
|
43
46
|
|
|
44
|
-
def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str = None,
|
|
45
|
-
embedding_model: str = None):
|
|
47
|
+
def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str | None = None,
|
|
48
|
+
embedding_model: str | None = None):
|
|
46
49
|
"""【P1·share-bge-m3】新增 embedding_model 参数。
|
|
47
50
|
上层(Foxclaw)通常在 HKC 已装配后再调本构造,bge-m3 已入 HKC 缓存;
|
|
48
51
|
传具体模型名(如 HKC_EMBEDDING_MODEL)使 HMR 与 HKC 共享同一 SentenceTransformer 实例。
|
|
49
52
|
不传则走 vendor/hmr/storage/vector_store.py 的 env 解析链(HKC_EMBEDDING_MODEL>bge-m3)。"""
|
|
50
53
|
_ensure_hmr_importable()
|
|
51
|
-
from hmr.core.hmr import HMR
|
|
54
|
+
from hmr.core.hmr import HMR
|
|
52
55
|
|
|
53
|
-
# 不传 None 就用 .env / HKC 的配置;只有上层显式 override 才用 _embedding_model
|
|
54
56
|
import os as _os
|
|
55
57
|
resolved_model = (embedding_model
|
|
56
|
-
or _os.environ.get("EMBEDDING_MODEL")
|
|
58
|
+
or _os.environ.get("EMBEDDING_MODEL")
|
|
57
59
|
or _os.environ.get("HKC_EMBEDDING_MODEL")
|
|
58
60
|
or _os.environ.get("FOXCLAW_EMBEDDING_MODEL")
|
|
59
61
|
or "all-MiniLM-L6-v2")
|
|
@@ -61,13 +63,8 @@ class HMRAdapter:
|
|
|
61
63
|
embedding_model=resolved_model)
|
|
62
64
|
self.backend = "HMR"
|
|
63
65
|
self.version = getattr(self._hmr, "VERSION", "2.0.0")
|
|
64
|
-
# 并行子 Agent 会并发 ingest/recall,底层 HMR 的向量库/记忆图并非线程安全
|
|
65
|
-
# (向量索引并发 add+search 是未定义行为)。串行化对 HMR 的访问。叶子组件:
|
|
66
|
-
# 锁内不回调 pm/bus,故不参与全局锁序,无死锁风险。
|
|
67
66
|
self._lock = threading.RLock()
|
|
68
67
|
|
|
69
|
-
# HMR 未给 title 时会退化成 "Concept 03:11" 这类纯时间戳(见 hmr.py 的
|
|
70
|
-
# `title or f"{type.title()} {HH:MM}"`),在前端"记忆信息条"里一眼看不出是什么。
|
|
71
68
|
_AUTO_TITLE = re.compile(r"^[A-Za-z_]+\s+\d{1,2}:\d{2}$")
|
|
72
69
|
|
|
73
70
|
@staticmethod
|
|
@@ -80,23 +77,27 @@ class HMRAdapter:
|
|
|
80
77
|
if s.startswith(pfx):
|
|
81
78
|
s = s[len(pfx):].strip()
|
|
82
79
|
break
|
|
83
|
-
for sep in ("→", "->"):
|
|
80
|
+
for sep in ("→", "->"):
|
|
84
81
|
if sep in s:
|
|
85
82
|
s = s.split(sep, 1)[0].strip()
|
|
86
83
|
break
|
|
87
84
|
s = re.split(r"[。!?!?\n\r]", s, 1)[0].strip()
|
|
88
85
|
return " ".join(s.split())[:24]
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
def ingest(self, content: str, type: str = "concept", agent_id: str | None = None,
|
|
88
|
+
title: str | None = None, tags: list | None = None, shared: bool = False,
|
|
89
|
+
consent: bool = False, **kwargs):
|
|
93
90
|
"""统一摄入接口。agent_id 写入 tags 实现隔离;shared=True 则写入共享层。
|
|
94
|
-
未给 title 时从内容提炼一个可读标题(否则 HMR 会生成纯时间戳标题)。
|
|
91
|
+
未给 title 时从内容提炼一个可读标题(否则 HMR 会生成纯时间戳标题)。
|
|
92
|
+
|
|
93
|
+
consent=True 表示"用户已对该条高敏感内容点头",跳过敏感闸门。唯一允许的调用方是
|
|
94
|
+
`FoxclawOS.approve_sensitive_write`(与 PolicyGate 的 confirmed=True 同一模式):
|
|
95
|
+
别处传 True 等于绕过同意流程。"""
|
|
95
96
|
meta_tags = list(tags or [])
|
|
96
97
|
if agent_id:
|
|
97
98
|
meta_tags.append(f"agent:{agent_id}")
|
|
98
99
|
if shared:
|
|
99
|
-
meta_tags.append("shared")
|
|
100
|
+
meta_tags.append("shared")
|
|
100
101
|
metadata = {"tags": meta_tags}
|
|
101
102
|
if "confidence" in kwargs:
|
|
102
103
|
metadata["confidence"] = kwargs["confidence"]
|
|
@@ -105,15 +106,19 @@ class HMRAdapter:
|
|
|
105
106
|
mtype = type if type in valid else "concept"
|
|
106
107
|
if not title:
|
|
107
108
|
title = self._derive_title(content) or None
|
|
108
|
-
# 【P0-3】共享层写入闸门:必须在**落库之前**判定(见 core/shared_write.py)。
|
|
109
|
-
# 未挂策略时返回 None 直接放行;audit 只记事件;enforce 才抛 SharedWriteDenied。
|
|
110
109
|
if shared:
|
|
111
110
|
guard_shared_write(self, agent_id, content, mtype, meta_tags)
|
|
111
|
+
if not consent:
|
|
112
|
+
blocked = guard_sensitive_write(self, agent_id, content, mtype)
|
|
113
|
+
if blocked is not None and not blocked.allowed:
|
|
114
|
+
# 高敏感内容不落库:进待确认队列,由用户同意后再写(见 core/sensitive_write.py)
|
|
115
|
+
return SimpleNamespace(id="", type=mtype, pending=True,
|
|
116
|
+
pending_id=blocked.pending_id, reason=blocked.reason,
|
|
117
|
+
categories=list(blocked.categories))
|
|
112
118
|
with self._lock:
|
|
113
119
|
return self._hmr.ingest(content, memory_type=mtype, title=title, metadata=metadata)
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
def recall(self, query: str = "", agent_id: str = None, top_k: int = 5,
|
|
121
|
+
def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5,
|
|
117
122
|
include_shared: bool = True, **kwargs):
|
|
118
123
|
"""统一召回接口。返回 list[dict]。
|
|
119
124
|
默认召回:本 Agent 的记忆 + 共享层(shared)记忆;include_shared=False 只看自己。
|
|
@@ -153,8 +158,7 @@ class HMRAdapter:
|
|
|
153
158
|
"tags": tags,
|
|
154
159
|
"_ts": getattr(m, "created_at", None) or getattr(m, "timestamp", None) or "",
|
|
155
160
|
})
|
|
156
|
-
out.sort(key=lambda x: str(x["_ts"]), reverse=True)
|
|
157
|
-
# 按内容去重(保留最新):否则同一偏好反复表达存成多条,会挤满 limit、把别的偏好挤出召回
|
|
161
|
+
out.sort(key=lambda x: str(x["_ts"]), reverse=True)
|
|
158
162
|
deduped, seen_c = [], set()
|
|
159
163
|
for x in out:
|
|
160
164
|
if x["content"] in seen_c:
|
|
@@ -195,7 +199,7 @@ class HMRAdapter:
|
|
|
195
199
|
try:
|
|
196
200
|
with self._lock:
|
|
197
201
|
mems = fs.list_memories()
|
|
198
|
-
cand = []
|
|
202
|
+
cand = []
|
|
199
203
|
for m in mems:
|
|
200
204
|
tags = getattr(m, "tags", []) or []
|
|
201
205
|
if agent_id:
|
|
@@ -210,7 +214,7 @@ class HMRAdapter:
|
|
|
210
214
|
scored = []
|
|
211
215
|
for m, tags in cand:
|
|
212
216
|
vec = vs.vectors.get(getattr(m, "id", None))
|
|
213
|
-
if vec is None:
|
|
217
|
+
if vec is None:
|
|
214
218
|
try:
|
|
215
219
|
text = getattr(m, "semantic_summary", "") or (m.content or "")[:500]
|
|
216
220
|
vec = vs.embedder.embed(text)
|
|
@@ -232,7 +236,7 @@ class HMRAdapter:
|
|
|
232
236
|
})
|
|
233
237
|
return out
|
|
234
238
|
except Exception:
|
|
235
|
-
return None
|
|
239
|
+
return None
|
|
236
240
|
|
|
237
241
|
def _legacy_recall(self, query, agent_id, top_k, include_shared):
|
|
238
242
|
"""回退路径:全局召回再按 tag 过滤(HMR 结构不可访问时用)。有完整性局限,见 recall。"""
|
|
@@ -250,7 +254,7 @@ class HMRAdapter:
|
|
|
250
254
|
own = f"agent:{agent_id}" in tags
|
|
251
255
|
shared = include_shared and ("shared" in tags)
|
|
252
256
|
if not (own or shared):
|
|
253
|
-
continue
|
|
257
|
+
continue
|
|
254
258
|
out.append({
|
|
255
259
|
"id": getattr(m, "id", ""),
|
|
256
260
|
"title": getattr(m, "title", ""),
|
|
@@ -264,11 +268,6 @@ class HMRAdapter:
|
|
|
264
268
|
break
|
|
265
269
|
return out
|
|
266
270
|
|
|
267
|
-
# ── 工作区 ────────────────────────────────────────────
|
|
268
|
-
# 【深审修复·补齐锁覆盖】适配层自述"串行化**所有** HMR 访问"(底层向量库/记忆图非线程安全),
|
|
269
|
-
# 但此前只有 ingest/recall 加锁;下列同样触碰 self._hmr 的方法漏了锁——与并发 ingest/recall
|
|
270
|
-
# (并行子 Agent / 并发请求)同时跑会读到 mutation 中的向量索引/字典(未定义行为/迭代崩溃)。
|
|
271
|
-
# 统一补 self._lock(RLock 叶子锁,锁内不回调 pm/bus,无死锁)。
|
|
272
271
|
def get_workspace(self, agent_id: str):
|
|
273
272
|
with self._lock:
|
|
274
273
|
return self._hmr.get_workspace(agent_id)
|
|
@@ -277,7 +276,6 @@ class HMRAdapter:
|
|
|
277
276
|
with self._lock:
|
|
278
277
|
return self._hmr.save_workspace(agent_id)
|
|
279
278
|
|
|
280
|
-
# ── 运行时状态 ────────────────────────────────────────
|
|
281
279
|
def save_runtime_state(self, **kwargs):
|
|
282
280
|
with self._lock:
|
|
283
281
|
return self._hmr.save_runtime_state(**kwargs)
|
|
@@ -286,7 +284,6 @@ class HMRAdapter:
|
|
|
286
284
|
with self._lock:
|
|
287
285
|
return self._hmr.restore_runtime_state()
|
|
288
286
|
|
|
289
|
-
# ── 系统状态 ──────────────────────────────────────────
|
|
290
287
|
def get_system_status(self):
|
|
291
288
|
try:
|
|
292
289
|
with self._lock:
|
|
@@ -307,8 +304,10 @@ class HMRAdapter:
|
|
|
307
304
|
|
|
308
305
|
def _ts(m):
|
|
309
306
|
v = getattr(m, "created_at", None)
|
|
307
|
+
if v is None:
|
|
308
|
+
return 0.0
|
|
310
309
|
try:
|
|
311
|
-
return v.timestamp() if hasattr(v, "timestamp") else float(v
|
|
310
|
+
return v.timestamp() if hasattr(v, "timestamp") else float(v)
|
|
312
311
|
except Exception:
|
|
313
312
|
return 0.0
|
|
314
313
|
|
|
@@ -325,11 +324,12 @@ class HMRAdapter:
|
|
|
325
324
|
break
|
|
326
325
|
created = getattr(m, "created_at", None)
|
|
327
326
|
title = (getattr(m, "title", "") or "").strip()
|
|
328
|
-
# 历史记忆多是 "Concept 03:11" 这类时间戳标题(修复前存的)→ 展示时现推一个可读的,
|
|
329
|
-
# 让老数据也能看懂;推不出来才退回原标题。
|
|
330
327
|
if not title or self._AUTO_TITLE.match(title):
|
|
331
328
|
title = self._derive_title(
|
|
332
329
|
getattr(m, "content", "") or getattr(m, "semantic_summary", "")) or title
|
|
330
|
+
created_at = str(created or "")
|
|
331
|
+
if created is not None and hasattr(created, "isoformat"):
|
|
332
|
+
created_at = created.isoformat()
|
|
333
333
|
out.append({
|
|
334
334
|
"id": getattr(m, "id", ""),
|
|
335
335
|
"type": getattr(m, "type", ""),
|
|
@@ -337,7 +337,7 @@ class HMRAdapter:
|
|
|
337
337
|
"summary": (getattr(m, "semantic_summary", "") or getattr(m, "content", "") or "")[:180],
|
|
338
338
|
"agent": agent,
|
|
339
339
|
"shared": "shared" in tags,
|
|
340
|
-
"created_at":
|
|
340
|
+
"created_at": created_at,
|
|
341
341
|
})
|
|
342
342
|
return out
|
|
343
343
|
|
|
@@ -346,17 +346,11 @@ class HMRAdapter:
|
|
|
346
346
|
total = 0
|
|
347
347
|
relations = 0
|
|
348
348
|
shared_count = 0
|
|
349
|
-
by_agent = {}
|
|
349
|
+
by_agent: dict[str, Any] = {}
|
|
350
350
|
embedding = "TF-IDF n-gram"
|
|
351
351
|
try:
|
|
352
|
-
# 序列化对 HMR 内部结构(向量库/记忆图)的读,与 ingest/recall 的写互斥,
|
|
353
|
-
# 避免并发迭代 store.memories("dict changed size")/读 mutation 中的 faiss 索引。
|
|
354
352
|
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 = []
|
|
353
|
+
mems: list[Any] = []
|
|
360
354
|
mfs = getattr(self._hmr, "memory_fs", None)
|
|
361
355
|
if mfs is not None and hasattr(mfs, "list_memories"):
|
|
362
356
|
try:
|
|
@@ -364,12 +358,11 @@ class HMRAdapter:
|
|
|
364
358
|
except Exception:
|
|
365
359
|
mems = []
|
|
366
360
|
total = len(mems)
|
|
367
|
-
if not total:
|
|
361
|
+
if not total:
|
|
368
362
|
vs0 = getattr(self._hmr, "vector_store", None)
|
|
369
363
|
vecs = getattr(vs0, "vectors", None) if vs0 is not None else None
|
|
370
364
|
if isinstance(vecs, dict):
|
|
371
365
|
total = len(vecs)
|
|
372
|
-
# shared / 按 Agent 分布:agent_id 编码在 tags 里的 "agent:<id>"
|
|
373
366
|
for m in mems:
|
|
374
367
|
tags = getattr(m, "tags", None)
|
|
375
368
|
if tags is None and isinstance(m, dict):
|
|
@@ -386,7 +379,6 @@ class HMRAdapter:
|
|
|
386
379
|
edges = getattr(graph, "edges", None)
|
|
387
380
|
if edges is not None:
|
|
388
381
|
relations = len(edges) if hasattr(edges, "__len__") else 0
|
|
389
|
-
# embedding 模型探测
|
|
390
382
|
emb = getattr(self._hmr, "embedding", None) or getattr(self._hmr, "embedder", None)
|
|
391
383
|
if emb is not None:
|
|
392
384
|
name = type(emb).__name__
|
|
@@ -423,19 +415,20 @@ class MockHMR:
|
|
|
423
415
|
def save_workspace(self, agent_id: str):
|
|
424
416
|
pass
|
|
425
417
|
|
|
426
|
-
def ingest(self, content, type="concept", agent_id=None, shared=False,
|
|
427
|
-
|
|
428
|
-
# 策略形同虚设 —— 而测试与降级场景恰好都落在这一支,那是最需要行为一致的地方。
|
|
418
|
+
def ingest(self, content, type="concept", agent_id=None, shared=False,
|
|
419
|
+
consent=False, **kwargs):
|
|
429
420
|
if shared:
|
|
430
421
|
guard_shared_write(self, agent_id, content, type, kwargs.get("tags") or [])
|
|
431
|
-
|
|
432
|
-
|
|
422
|
+
if not consent:
|
|
423
|
+
blocked = guard_sensitive_write(self, agent_id, content, type)
|
|
424
|
+
if blocked is not None and not blocked.allowed:
|
|
425
|
+
return SimpleNamespace(id="", type=type, pending=True,
|
|
426
|
+
pending_id=blocked.pending_id, reason=blocked.reason,
|
|
427
|
+
categories=list(blocked.categories))
|
|
433
428
|
return SimpleNamespace(id="mock_mem", type=type)
|
|
434
429
|
|
|
435
|
-
def recall(self, query: str = "", agent_id: str = None, top_k: int = 5, **kwargs):
|
|
430
|
+
def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5, **kwargs):
|
|
436
431
|
from types import SimpleNamespace
|
|
437
|
-
# 返回带 memory_objects 的对象,与真实 HMR 的 RecallResult 结构一致
|
|
438
|
-
# (HMRAdapter.recall 会读 result.memory_objects)。
|
|
439
432
|
return SimpleNamespace(memory_objects=[])
|
|
440
433
|
|
|
441
434
|
def save_runtime_state(self, **kwargs):
|
|
@@ -484,7 +477,7 @@ def _memory_chain_enabled() -> bool:
|
|
|
484
477
|
return raw.lower() in ("1", "true", "yes", "on")
|
|
485
478
|
|
|
486
479
|
|
|
487
|
-
def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = None):
|
|
480
|
+
def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str | None = None):
|
|
488
481
|
"""工厂:默认 HMR;`FOXCLAW_MEMORY_BACKEND=vecrecall` 时才切 vecRecall。
|
|
489
482
|
|
|
490
483
|
顺序 vecRecall → HMR → MockHMR,任一层失败退到下一层,且**明确打印**实际用的是
|
|
@@ -508,9 +501,9 @@ def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = N
|
|
|
508
501
|
print(f"[Foxclaw] ⚠ FOXCLAW_MEMORY_BACKEND={choice!r} 无法识别,按 HMR 处理")
|
|
509
502
|
|
|
510
503
|
try:
|
|
511
|
-
|
|
512
|
-
print(f"[Foxclaw] ✓ HMR v{
|
|
513
|
-
return
|
|
504
|
+
hmr = HMRAdapter(storage_path=storage_path, llm_api_key=llm_api_key)
|
|
505
|
+
print(f"[Foxclaw] ✓ HMR v{hmr.version} 已接入 (backend={hmr.backend})")
|
|
506
|
+
return hmr
|
|
514
507
|
except Exception as e:
|
|
515
508
|
print(f"[Foxclaw] ⚠ HMR 不可用 ({e.__class__.__name__}: {e}),降级 MockHMR")
|
|
516
509
|
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()
|