@hupan56/wlkj 2.7.12 → 3.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/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
3
|
+
import os as _o, sys as _s
|
|
4
|
+
_f = _o.path.abspath(__file__)
|
|
5
|
+
for _ in range(10):
|
|
6
|
+
_f = _o.path.dirname(_f)
|
|
7
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
8
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
9
|
+
break
|
|
10
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
11
|
+
from bootstrap import setup; setup()
|
|
12
|
+
from foundation.core.paths import get_repo_root
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
perf_bench.py — 知识图谱关键路径性能基准
|
|
16
|
+
|
|
17
|
+
诊断"用了工作流反而更慢"的根因, 并验证常驻化(daemon)加速层的效果。
|
|
18
|
+
|
|
19
|
+
两种测量模式:
|
|
20
|
+
1. 冷启动 (subprocess 跑 kg.py) — 模拟真实用户体验: 起进程→import→连库→跑
|
|
21
|
+
2. daemon 热路径 (kgd_client) — daemon 常驻时的 IPC 调用, 模型/连接已就绪
|
|
22
|
+
|
|
23
|
+
用法:
|
|
24
|
+
python perf_bench.py # 默认: 测冷启动 (--no-daemon)
|
|
25
|
+
python perf_bench.py --with-daemon # 同时测 daemon 热路径 (需先 kgd.py start)
|
|
26
|
+
python perf_bench.py --quick # 跳过 semantic (26s), 只测轻量路径
|
|
27
|
+
"""
|
|
28
|
+
import io
|
|
29
|
+
import os
|
|
30
|
+
import subprocess
|
|
31
|
+
import sys
|
|
32
|
+
import time
|
|
33
|
+
|
|
34
|
+
# UTF-8 输出 (Windows 终端默认 GBK 会乱码)
|
|
35
|
+
try:
|
|
36
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
37
|
+
except Exception:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
KG_PY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kg.py')
|
|
41
|
+
REPO_ROOT = get_repo_root()
|
|
42
|
+
|
|
43
|
+
# ── 测试用例 (覆盖四层) ──────────────────────────────────────────────
|
|
44
|
+
# (名称, kg子命令, 参数列表, 预期层)
|
|
45
|
+
CASES = [
|
|
46
|
+
('search-考勤(web)', 'search', ['考勤', '--platform', 'web'], '结构层'),
|
|
47
|
+
('search-车辆(web)', 'search', ['车辆', '--platform', 'web'], '结构层'),
|
|
48
|
+
('search-报销(app)', 'search', ['报销', '--platform', 'app'], '结构层'),
|
|
49
|
+
('api-asset', 'api', ['asset'], '结构层'),
|
|
50
|
+
('context_pack-车辆', 'context', ['车辆', '--platform', 'web'], '①③+设计'),
|
|
51
|
+
('impact-/asset', 'impact', ['/asset'], '图谱层'),
|
|
52
|
+
('feature-资产管理', 'feature', ['资产管理'], '图谱层'),
|
|
53
|
+
('semantic-报销', 'semantic', ['报销'], '向量层'), # 26s 慢王
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _run_kg(args, use_daemon=True, timeout=120):
|
|
58
|
+
"""跑一次 kg.py 子命令, 返回 (耗时秒, 返回码)。
|
|
59
|
+
|
|
60
|
+
use_daemon=True : 走默认 (kg.py 内部会优先 daemon)
|
|
61
|
+
use_daemon=False : 加 --no-daemon 强制冷路径
|
|
62
|
+
"""
|
|
63
|
+
cmd = [sys.executable, KG_PY] + args
|
|
64
|
+
if not use_daemon:
|
|
65
|
+
cmd.append('--no-daemon')
|
|
66
|
+
env = dict(os.environ)
|
|
67
|
+
env['PYTHONIOENCODING'] = 'utf-8'
|
|
68
|
+
t0 = time.time()
|
|
69
|
+
try:
|
|
70
|
+
# stdout 重定向到 DEVNULL, 只测耗时, 不看内容
|
|
71
|
+
r = subprocess.run(cmd, stdout=subprocess.DEVNULL,
|
|
72
|
+
stderr=subprocess.DEVNULL, cwd=REPO_ROOT,
|
|
73
|
+
env=env, timeout=timeout)
|
|
74
|
+
return time.time() - t0, r.returncode
|
|
75
|
+
except subprocess.TimeoutExpired:
|
|
76
|
+
return float(timeout), -1
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _median(samples):
|
|
80
|
+
"""取中位数 (抗抖动)。"""
|
|
81
|
+
s = sorted(samples)
|
|
82
|
+
n = len(s)
|
|
83
|
+
return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2.0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def bench_case(name, cmd, args, cold=True, daemon=False, repeats=3):
|
|
87
|
+
"""跑单个用例, 返回 (中位数耗时, 状态)。
|
|
88
|
+
|
|
89
|
+
cold=True : 测冷启动 (--no-daemon)
|
|
90
|
+
daemon=True: 测 daemon 热路径 (不加 --no-daemon, 但需 daemon 在线)
|
|
91
|
+
"""
|
|
92
|
+
if cold and daemon:
|
|
93
|
+
raise ValueError('cold 和 daemon 互斥')
|
|
94
|
+
# 重量级命令 (semantic 冷启动 ~27s) 只跑 1 次, 避免基准脚本本身过慢
|
|
95
|
+
if cmd == 'semantic' and cold:
|
|
96
|
+
repeats = 1
|
|
97
|
+
use_daemon = daemon
|
|
98
|
+
samples = []
|
|
99
|
+
status = 'ok'
|
|
100
|
+
for _ in range(repeats):
|
|
101
|
+
elapsed, rc = _run_kg([cmd] + args, use_daemon=use_daemon)
|
|
102
|
+
samples.append(elapsed)
|
|
103
|
+
if rc != 0:
|
|
104
|
+
status = 'rc=%d' % rc
|
|
105
|
+
return _median(samples), status
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def daemon_online():
|
|
109
|
+
"""探测 daemon 是否在线 (通过 kg.py status, 1s 超时)。"""
|
|
110
|
+
try:
|
|
111
|
+
from kgd import kgd_client
|
|
112
|
+
return kgd_client.ping(timeout=1.0)
|
|
113
|
+
except Exception:
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def fmt_speedup(cold_s, hot_s):
|
|
118
|
+
"""格式化加速比。"""
|
|
119
|
+
if hot_s <= 0.001:
|
|
120
|
+
return '-'
|
|
121
|
+
return '%.0fx' % (cold_s / hot_s)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main():
|
|
125
|
+
args = sys.argv[1:]
|
|
126
|
+
quick = '--quick' in args
|
|
127
|
+
want_daemon = '--with-daemon' in args
|
|
128
|
+
|
|
129
|
+
print('=' * 72)
|
|
130
|
+
print('知识图谱性能基准 (repo: %s)' % os.path.basename(REPO_ROOT))
|
|
131
|
+
print('=' * 72)
|
|
132
|
+
|
|
133
|
+
if quick:
|
|
134
|
+
print('[quick 模式] 跳过 semantic (向量层 26s), 只测轻量路径\n')
|
|
135
|
+
cases = [c for c in CASES if c[1] != 'semantic']
|
|
136
|
+
else:
|
|
137
|
+
cases = CASES
|
|
138
|
+
|
|
139
|
+
cold_results = [] # [(name, secs, status, layer)]
|
|
140
|
+
hot_results = {} # name -> (secs, status)
|
|
141
|
+
|
|
142
|
+
# ── 1. 冷启动基线 (真实用户体验: 每次起一个进程) ──
|
|
143
|
+
print('[1/2] 冷启动基线 (--no-daemon, 模拟现状) ...')
|
|
144
|
+
for name, cmd, cargs, layer in cases:
|
|
145
|
+
secs, status = bench_case(name, cmd, cargs, cold=True)
|
|
146
|
+
cold_results.append((name, secs, status, layer))
|
|
147
|
+
marker = ' ' if status == 'ok' else '! '
|
|
148
|
+
print(' %s%-22s %6.2fs [%s] %s' % (marker, name, secs, layer, status))
|
|
149
|
+
print()
|
|
150
|
+
|
|
151
|
+
# ── 2. daemon 热路径 (常驻加速层) ──
|
|
152
|
+
if want_daemon:
|
|
153
|
+
online = daemon_online()
|
|
154
|
+
if not online:
|
|
155
|
+
print('[2/2] daemon 未在线 — 跳过热路径测试。')
|
|
156
|
+
print(' (先启动: python .qoder/scripts/domain/kg/server/kgd.py start)')
|
|
157
|
+
else:
|
|
158
|
+
print('[2/2] daemon 在线, 测热路径 ...')
|
|
159
|
+
for name, cmd, cargs, layer in cases:
|
|
160
|
+
secs, status = bench_case(name, cmd, cargs, cold=False, daemon=True)
|
|
161
|
+
hot_results[name] = (secs, status)
|
|
162
|
+
marker = ' ' if status == 'ok' else '! '
|
|
163
|
+
print(' %s%-22s %6.2fs [%s] %s' % (marker, name, secs, layer, status))
|
|
164
|
+
print()
|
|
165
|
+
|
|
166
|
+
# ── 汇总对比表 ──
|
|
167
|
+
print('-' * 72)
|
|
168
|
+
print('%-22s %10s %10s %10s' % ('用例', '冷启动', 'daemon', '加速'))
|
|
169
|
+
print('%-22s %10s %10s %10s' % ('─' * 18, '─' * 8, '─' * 8, '─' * 6))
|
|
170
|
+
for name, cold_s, status, layer in cold_results:
|
|
171
|
+
hot_s = hot_results.get(name, (None, None))[0]
|
|
172
|
+
hot_str = '%.2fs' % hot_s if hot_s else '-'
|
|
173
|
+
speed = fmt_speedup(cold_s, hot_s) if hot_s else '-'
|
|
174
|
+
print('%-22s %8.2fs %10s %10s' % (name, cold_s, hot_str, speed))
|
|
175
|
+
print('-' * 72)
|
|
176
|
+
|
|
177
|
+
# ── 诊断结论 ──
|
|
178
|
+
print('\n诊断:')
|
|
179
|
+
semantic_s = next((s for n, s, _, _ in cold_results if 'semantic' in n), None)
|
|
180
|
+
if semantic_s and semantic_s > 10:
|
|
181
|
+
print(' 🔴 semantic %.1fs — 模型冷加载占 99%%, 是头号慢源' % semantic_s)
|
|
182
|
+
print(' 解法: kgd.py start 常驻模型 → <0.5s')
|
|
183
|
+
elif semantic_s:
|
|
184
|
+
print(' 🟢 semantic %.1fs — 在可接受范围' % semantic_s)
|
|
185
|
+
|
|
186
|
+
total_cold = sum(s for _, s, _, _ in cold_results)
|
|
187
|
+
print(' 冷启动累积: %.1fs (一次 PRD 流程的隐性等待)' % total_cold)
|
|
188
|
+
if hot_results:
|
|
189
|
+
total_hot = sum(hot_results[n][0] for n in hot_results)
|
|
190
|
+
print(' daemon 累积: %.1fs → 整体提速 %.0fx' %
|
|
191
|
+
(total_hot, total_cold / max(total_hot, 0.01)))
|
|
192
|
+
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == '__main__':
|
|
197
|
+
sys.exit(main())
|
|
File without changes
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
3
|
+
import os as _o, sys as _s
|
|
4
|
+
_f = _o.path.abspath(__file__)
|
|
5
|
+
for _ in range(10):
|
|
6
|
+
_f = _o.path.dirname(_f)
|
|
7
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
8
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
9
|
+
break
|
|
10
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
11
|
+
from bootstrap import setup; setup()
|
|
12
|
+
|
|
2
13
|
"""
|
|
3
14
|
DuckDB knowledge graph store — replaces the JSON flat files with a single
|
|
4
15
|
embedded database for fast incremental updates.
|
|
@@ -21,17 +32,42 @@ import sys
|
|
|
21
32
|
|
|
22
33
|
import duckdb
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
# 深度 4: scripts/ → domain/ → kg/ → storage/ → kg_duckdb.py
|
|
36
|
+
SCRIPTS = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) # storage→kg→domain→scripts/根
|
|
37
|
+
if SCRIPTS not in sys.path:
|
|
38
|
+
sys.path.insert(0, SCRIPTS)
|
|
39
|
+
from foundation.core.paths import DATA_INDEX_DIR
|
|
40
|
+
from foundation.protocol.duckdb_conn import get_db, get_db_readonly, DB_PATH # L0 基础设施
|
|
29
41
|
|
|
30
42
|
|
|
31
|
-
def
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
def _load_index_json(name):
|
|
44
|
+
"""统一加载索引 JSON。build_style_index 把产物写到 _build_cache/,
|
|
45
|
+
旧布局/手动放的可能在 INDEX 直接目录。两个位置都查, 优先 _build_cache。
|
|
46
|
+
所有 import_*_layer 共用这个, 避免各写各的 load() 导致路径不一致。"""
|
|
47
|
+
for d in (str(DATA_INDEX_DIR / '_build_cache'), str(DATA_INDEX_DIR)):
|
|
48
|
+
p = os.path.join(d, name)
|
|
49
|
+
if os.path.isfile(p):
|
|
50
|
+
try:
|
|
51
|
+
return json.load(open(p, encoding='utf-8'))
|
|
52
|
+
except (json.JSONDecodeError, OSError):
|
|
53
|
+
pass
|
|
54
|
+
return {}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_db_readonly():
|
|
58
|
+
"""Open read-only DuckDB connection. Returns None if file missing.
|
|
59
|
+
|
|
60
|
+
语义检索 (semantic_search / semantic_search_files) 只读向量数据,
|
|
61
|
+
但 search_index.py 已用 read_only=True 打开了同一个 kg.duckdb。
|
|
62
|
+
DuckDB 不允许对同一文件混用 read_only 和 read_write 连接, 所以
|
|
63
|
+
只读路径必须全程用 read_only, 避免连接冲突。文件不存在则返回 None。
|
|
64
|
+
"""
|
|
65
|
+
if not os.path.isfile(DB_PATH):
|
|
66
|
+
return None
|
|
67
|
+
try:
|
|
68
|
+
return duckdb.connect(DB_PATH, read_only=True)
|
|
69
|
+
except Exception:
|
|
70
|
+
return None
|
|
35
71
|
|
|
36
72
|
|
|
37
73
|
def init_schema(con):
|
|
@@ -115,7 +151,7 @@ def init_schema(con):
|
|
|
115
151
|
# 自动补列: events.py 注册的新统计列, 表里没有就加 (ALTER ... ADD COLUMN IF NOT EXISTS)
|
|
116
152
|
# 这样 events.py 加埋点不用手动改表结构。
|
|
117
153
|
try:
|
|
118
|
-
from
|
|
154
|
+
from foundation.data.events import event_to_sql_filter
|
|
119
155
|
existing = {r[0] for r in con.execute(
|
|
120
156
|
"SELECT column_name FROM information_schema.columns "
|
|
121
157
|
"WHERE table_name = 'learning_stats'"
|
|
@@ -128,6 +164,19 @@ def init_schema(con):
|
|
|
128
164
|
)
|
|
129
165
|
except Exception:
|
|
130
166
|
pass # events.py 不可用 (循环导入等) = 退化为固定 5 列, 不影响
|
|
167
|
+
|
|
168
|
+
# 语义检索向量表 (v3.1: bge-small-zh embedding)
|
|
169
|
+
# entity_id 关联 entities.id; text 是被 embed 的文本 (canonical + cn);
|
|
170
|
+
# vec 存 FLOAT 数组 (DuckDB 用 list 类型, 运行时 cast)。
|
|
171
|
+
con.execute("""
|
|
172
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
173
|
+
entity_id TEXT NOT NULL,
|
|
174
|
+
text TEXT NOT NULL,
|
|
175
|
+
vec FLOAT[],
|
|
176
|
+
model TEXT DEFAULT 'bge-small-zh-v1.5'
|
|
177
|
+
)
|
|
178
|
+
""")
|
|
179
|
+
|
|
131
180
|
# Indexes
|
|
132
181
|
con.execute("CREATE INDEX IF NOT EXISTS idx_ent_type ON entities(type)")
|
|
133
182
|
con.execute("CREATE INDEX IF NOT EXISTS idx_alias ON aliases(alias)")
|
|
@@ -245,20 +294,12 @@ def multi_hop(con, start_id, edge_types=None, max_depth=5):
|
|
|
245
294
|
|
|
246
295
|
def bulk_import_from_json(con):
|
|
247
296
|
"""One-time full import from existing JSON indexes into DuckDB."""
|
|
248
|
-
INDEX = str(DATA_INDEX_DIR / '_build_cache')
|
|
249
|
-
|
|
250
|
-
def load(name):
|
|
251
|
-
p = os.path.join(INDEX, name)
|
|
252
|
-
if os.path.isfile(p):
|
|
253
|
-
return json.load(open(p, encoding='utf-8'))
|
|
254
|
-
return {}
|
|
255
|
-
|
|
256
297
|
con.execute("DELETE FROM entities")
|
|
257
298
|
con.execute("DELETE FROM aliases")
|
|
258
299
|
con.execute("DELETE FROM edges")
|
|
259
300
|
|
|
260
|
-
er =
|
|
261
|
-
rel =
|
|
301
|
+
er = _load_index_json('entity-registry.json')
|
|
302
|
+
rel = _load_index_json('relations.json')
|
|
262
303
|
edges_data = rel.get('edges', {})
|
|
263
304
|
|
|
264
305
|
entity_rows = []
|
|
@@ -330,27 +371,22 @@ def bulk_import_from_json(con):
|
|
|
330
371
|
|
|
331
372
|
def import_search_layer(con):
|
|
332
373
|
"""导入搜索层: keywords/endpoints/prds/wiki → DuckDB 表。"""
|
|
333
|
-
INDEX = str(DATA_INDEX_DIR)
|
|
334
|
-
def load(name):
|
|
335
|
-
p = os.path.join(INDEX, name)
|
|
336
|
-
return json.load(open(p, encoding='utf-8')) if os.path.isfile(p) else {}
|
|
337
|
-
|
|
338
374
|
# keywords (code-keyword.json: {keyword: [files]})
|
|
339
|
-
kw =
|
|
375
|
+
kw = _load_index_json('code-keyword.json')
|
|
340
376
|
kw_rows = [(k, f) for k, files in kw.items() for f in files]
|
|
341
377
|
con.execute("DELETE FROM keywords")
|
|
342
378
|
if kw_rows:
|
|
343
379
|
_batch_insert(con, 'keywords', kw_rows)
|
|
344
380
|
|
|
345
381
|
# endpoints (code-api.json: {endpoint: controller_file})
|
|
346
|
-
api =
|
|
382
|
+
api = _load_index_json('code-api.json')
|
|
347
383
|
con.execute("DELETE FROM endpoints")
|
|
348
384
|
ep_rows = [(ep, f) for ep, f in api.items()]
|
|
349
385
|
if ep_rows:
|
|
350
386
|
_batch_insert(con, 'endpoints', ep_rows, ignore=True)
|
|
351
387
|
|
|
352
388
|
# prds (prd-index.json)
|
|
353
|
-
prds =
|
|
389
|
+
prds = _load_index_json('prd-index.json')
|
|
354
390
|
con.execute("DELETE FROM prds")
|
|
355
391
|
prd_rows = [(f, info.get('title', ''),
|
|
356
392
|
json.dumps(info.get('keywords', []), ensure_ascii=False),
|
|
@@ -361,7 +397,7 @@ def import_search_layer(con):
|
|
|
361
397
|
_batch_insert(con, 'prds', prd_rows, ignore=True)
|
|
362
398
|
|
|
363
399
|
# wiki (wiki-index.json)
|
|
364
|
-
wiki =
|
|
400
|
+
wiki = _load_index_json('wiki-index.json')
|
|
365
401
|
con.execute("DELETE FROM wiki")
|
|
366
402
|
wiki_rows = []
|
|
367
403
|
for kw, entries in (wiki.get('index', {}) if isinstance(wiki, dict) else {}).items():
|
|
@@ -376,12 +412,7 @@ def import_search_layer(con):
|
|
|
376
412
|
|
|
377
413
|
def import_ui_layer(con):
|
|
378
414
|
"""导入UI层: pages/fields/modules → DuckDB 表。"""
|
|
379
|
-
|
|
380
|
-
def load(name):
|
|
381
|
-
p = os.path.join(INDEX, name)
|
|
382
|
-
return json.load(open(p, encoding='utf-8')) if os.path.isfile(p) else {}
|
|
383
|
-
|
|
384
|
-
si = load('ui-style.json')
|
|
415
|
+
si = _load_index_json('ui-style.json')
|
|
385
416
|
|
|
386
417
|
# pages (from ui-style projects.page_examples + page_types)
|
|
387
418
|
con.execute("DELETE FROM pages")
|
|
@@ -408,7 +439,7 @@ def import_ui_layer(con):
|
|
|
408
439
|
_batch_insert(con, 'fields', field_rows)
|
|
409
440
|
|
|
410
441
|
# modules (from ui-modules.json)
|
|
411
|
-
mi =
|
|
442
|
+
mi = _load_index_json('ui-modules.json')
|
|
412
443
|
con.execute("DELETE FROM modules")
|
|
413
444
|
mod_rows = [(m['dir'], m['cn'], proj, m['path'])
|
|
414
445
|
for proj, mods in mi.get('projects', {}).items() for m in mods]
|
|
@@ -434,9 +465,7 @@ def import_ui_layer(con):
|
|
|
434
465
|
|
|
435
466
|
def import_trace_layer(con):
|
|
436
467
|
"""导入trace层: traces/api_functions → DuckDB 表。"""
|
|
437
|
-
|
|
438
|
-
tc = json.load(open(os.path.join(INDEX, 'trace-chain.json'), encoding='utf-8')) \
|
|
439
|
-
if os.path.isfile(os.path.join(INDEX, 'trace-chain.json')) else {}
|
|
468
|
+
tc = _load_index_json('trace-chain.json')
|
|
440
469
|
|
|
441
470
|
con.execute("DELETE FROM traces")
|
|
442
471
|
trace_rows = [(
|
|
@@ -458,9 +487,7 @@ def import_trace_layer(con):
|
|
|
458
487
|
|
|
459
488
|
def import_test_layer(con):
|
|
460
489
|
"""导入test层: tests → DuckDB 表。"""
|
|
461
|
-
|
|
462
|
-
ta = json.load(open(os.path.join(INDEX, 'test-assertions.json'), encoding='utf-8')) \
|
|
463
|
-
if os.path.isfile(os.path.join(INDEX, 'test-assertions.json')) else {}
|
|
490
|
+
ta = _load_index_json('test-assertions.json')
|
|
464
491
|
|
|
465
492
|
con.execute("DELETE FROM tests")
|
|
466
493
|
test_rows = [(
|
|
@@ -478,9 +505,7 @@ def import_test_layer(con):
|
|
|
478
505
|
|
|
479
506
|
def import_workflow_layer(con):
|
|
480
507
|
"""导入workflow层 → DuckDB 表。"""
|
|
481
|
-
|
|
482
|
-
wf = json.load(open(os.path.join(INDEX, 'workflow-index.json'), encoding='utf-8')) \
|
|
483
|
-
if os.path.isfile(os.path.join(INDEX, 'workflow-index.json')) else {}
|
|
508
|
+
wf = _load_index_json('workflow-index.json')
|
|
484
509
|
|
|
485
510
|
con.execute("DELETE FROM workflows")
|
|
486
511
|
wf_rows = []
|
|
File without changes
|