homegraph 1.2.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/dist/bin/homegraph.js +3 -0
  2. package/dist/bin/homegraph.js.map +1 -1
  3. package/dist/context/index.d.ts.map +1 -1
  4. package/dist/context/index.js +85 -0
  5. package/dist/context/index.js.map +1 -1
  6. package/dist/db/index.d.ts +64 -5
  7. package/dist/db/index.d.ts.map +1 -1
  8. package/dist/db/index.js +178 -14
  9. package/dist/db/index.js.map +1 -1
  10. package/dist/db/migrations.d.ts +1 -1
  11. package/dist/db/migrations.d.ts.map +1 -1
  12. package/dist/db/migrations.js +19 -1
  13. package/dist/db/migrations.js.map +1 -1
  14. package/dist/db/queries.d.ts +117 -2
  15. package/dist/db/queries.d.ts.map +1 -1
  16. package/dist/db/queries.js +299 -6
  17. package/dist/db/queries.js.map +1 -1
  18. package/dist/db/schema.sql +32 -1
  19. package/dist/db/wal-valve.d.ts +106 -0
  20. package/dist/db/wal-valve.d.ts.map +1 -0
  21. package/dist/db/wal-valve.js +208 -0
  22. package/dist/db/wal-valve.js.map +1 -0
  23. package/dist/directory.d.ts +9 -5
  24. package/dist/directory.d.ts.map +1 -1
  25. package/dist/directory.js +210 -19
  26. package/dist/directory.js.map +1 -1
  27. package/dist/extraction/cfml-extractor.d.ts +107 -0
  28. package/dist/extraction/cfml-extractor.d.ts.map +1 -0
  29. package/dist/extraction/cfml-extractor.js +494 -0
  30. package/dist/extraction/cfml-extractor.js.map +1 -0
  31. package/dist/extraction/grammars.d.ts +25 -1
  32. package/dist/extraction/grammars.d.ts.map +1 -1
  33. package/dist/extraction/grammars.js +177 -24
  34. package/dist/extraction/grammars.js.map +1 -1
  35. package/dist/extraction/index.d.ts +43 -4
  36. package/dist/extraction/index.d.ts.map +1 -1
  37. package/dist/extraction/index.js +308 -5
  38. package/dist/extraction/index.js.map +1 -1
  39. package/dist/extraction/languages/c-cpp.d.ts +42 -0
  40. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  41. package/dist/extraction/languages/c-cpp.js +369 -4
  42. package/dist/extraction/languages/c-cpp.js.map +1 -1
  43. package/dist/extraction/languages/cfquery.d.ts +12 -0
  44. package/dist/extraction/languages/cfquery.d.ts.map +1 -0
  45. package/dist/extraction/languages/cfquery.js +28 -0
  46. package/dist/extraction/languages/cfquery.js.map +1 -0
  47. package/dist/extraction/languages/cfscript.d.ts +3 -0
  48. package/dist/extraction/languages/cfscript.d.ts.map +1 -0
  49. package/dist/extraction/languages/cfscript.js +73 -0
  50. package/dist/extraction/languages/cfscript.js.map +1 -0
  51. package/dist/extraction/languages/cobol.d.ts +33 -0
  52. package/dist/extraction/languages/cobol.d.ts.map +1 -0
  53. package/dist/extraction/languages/cobol.js +499 -0
  54. package/dist/extraction/languages/cobol.js.map +1 -0
  55. package/dist/extraction/languages/erlang.d.ts +3 -0
  56. package/dist/extraction/languages/erlang.d.ts.map +1 -0
  57. package/dist/extraction/languages/erlang.js +350 -0
  58. package/dist/extraction/languages/erlang.js.map +1 -0
  59. package/dist/extraction/languages/index.d.ts.map +1 -1
  60. package/dist/extraction/languages/index.js +16 -0
  61. package/dist/extraction/languages/index.js.map +1 -1
  62. package/dist/extraction/languages/nix.d.ts +3 -0
  63. package/dist/extraction/languages/nix.d.ts.map +1 -0
  64. package/dist/extraction/languages/nix.js +294 -0
  65. package/dist/extraction/languages/nix.js.map +1 -0
  66. package/dist/extraction/languages/solidity.d.ts +3 -0
  67. package/dist/extraction/languages/solidity.d.ts.map +1 -0
  68. package/dist/extraction/languages/solidity.js +293 -0
  69. package/dist/extraction/languages/solidity.js.map +1 -0
  70. package/dist/extraction/languages/terraform.d.ts +3 -0
  71. package/dist/extraction/languages/terraform.d.ts.map +1 -0
  72. package/dist/extraction/languages/terraform.js +641 -0
  73. package/dist/extraction/languages/terraform.js.map +1 -0
  74. package/dist/extraction/languages/vbnet.d.ts +11 -0
  75. package/dist/extraction/languages/vbnet.d.ts.map +1 -0
  76. package/dist/extraction/languages/vbnet.js +141 -0
  77. package/dist/extraction/languages/vbnet.js.map +1 -0
  78. package/dist/extraction/mybatis-extractor.d.ts +30 -10
  79. package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  80. package/dist/extraction/mybatis-extractor.js +140 -38
  81. package/dist/extraction/mybatis-extractor.js.map +1 -1
  82. package/dist/extraction/parse-pool.d.ts +27 -1
  83. package/dist/extraction/parse-pool.d.ts.map +1 -1
  84. package/dist/extraction/parse-pool.js +75 -7
  85. package/dist/extraction/parse-pool.js.map +1 -1
  86. package/dist/extraction/parse-worker.js +9 -2
  87. package/dist/extraction/parse-worker.js.map +1 -1
  88. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  89. package/dist/extraction/tree-sitter-helpers.js +1 -0
  90. package/dist/extraction/tree-sitter-helpers.js.map +1 -1
  91. package/dist/extraction/tree-sitter-types.d.ts +3 -1
  92. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  93. package/dist/extraction/tree-sitter.d.ts +38 -0
  94. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  95. package/dist/extraction/tree-sitter.js +754 -10
  96. package/dist/extraction/tree-sitter.js.map +1 -1
  97. package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  98. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  99. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  100. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  101. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  102. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  103. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  104. package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  105. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  106. package/dist/graph/traversal.d.ts.map +1 -1
  107. package/dist/graph/traversal.js +1 -1
  108. package/dist/graph/traversal.js.map +1 -1
  109. package/dist/index.d.ts +60 -2
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +348 -5
  112. package/dist/index.js.map +1 -1
  113. package/dist/installer/index.d.ts +42 -0
  114. package/dist/installer/index.d.ts.map +1 -1
  115. package/dist/installer/index.js +97 -2
  116. package/dist/installer/index.js.map +1 -1
  117. package/dist/mcp/daemon.d.ts +25 -3
  118. package/dist/mcp/daemon.d.ts.map +1 -1
  119. package/dist/mcp/daemon.js +62 -8
  120. package/dist/mcp/daemon.js.map +1 -1
  121. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  122. package/dist/mcp/dynamic-boundaries.js +2 -1
  123. package/dist/mcp/dynamic-boundaries.js.map +1 -1
  124. package/dist/mcp/early-ppid.d.ts +26 -0
  125. package/dist/mcp/early-ppid.d.ts.map +1 -0
  126. package/dist/mcp/early-ppid.js +29 -0
  127. package/dist/mcp/early-ppid.js.map +1 -0
  128. package/dist/mcp/engine.d.ts +4 -5
  129. package/dist/mcp/engine.d.ts.map +1 -1
  130. package/dist/mcp/engine.js +27 -1
  131. package/dist/mcp/engine.js.map +1 -1
  132. package/dist/mcp/index.d.ts.map +1 -1
  133. package/dist/mcp/index.js +41 -6
  134. package/dist/mcp/index.js.map +1 -1
  135. package/dist/mcp/liveness-watchdog.d.ts +18 -1
  136. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  137. package/dist/mcp/liveness-watchdog.js +73 -13
  138. package/dist/mcp/liveness-watchdog.js.map +1 -1
  139. package/dist/mcp/memory-budget.d.ts +30 -0
  140. package/dist/mcp/memory-budget.d.ts.map +1 -0
  141. package/dist/mcp/memory-budget.js +123 -0
  142. package/dist/mcp/memory-budget.js.map +1 -0
  143. package/dist/mcp/proxy.d.ts.map +1 -1
  144. package/dist/mcp/proxy.js +88 -4
  145. package/dist/mcp/proxy.js.map +1 -1
  146. package/dist/mcp/query-cache.d.ts +35 -4
  147. package/dist/mcp/query-cache.d.ts.map +1 -1
  148. package/dist/mcp/query-cache.js +89 -19
  149. package/dist/mcp/query-cache.js.map +1 -1
  150. package/dist/mcp/query-pool.d.ts +46 -12
  151. package/dist/mcp/query-pool.d.ts.map +1 -1
  152. package/dist/mcp/query-pool.js +100 -23
  153. package/dist/mcp/query-pool.js.map +1 -1
  154. package/dist/mcp/server-instructions.d.ts +4 -27
  155. package/dist/mcp/server-instructions.d.ts.map +1 -1
  156. package/dist/mcp/server-instructions.js +39 -84
  157. package/dist/mcp/server-instructions.js.map +1 -1
  158. package/dist/mcp/session.d.ts +14 -0
  159. package/dist/mcp/session.d.ts.map +1 -1
  160. package/dist/mcp/session.js +28 -1
  161. package/dist/mcp/session.js.map +1 -1
  162. package/dist/mcp/startup-handshake.d.ts +44 -0
  163. package/dist/mcp/startup-handshake.d.ts.map +1 -0
  164. package/dist/mcp/startup-handshake.js +73 -0
  165. package/dist/mcp/startup-handshake.js.map +1 -0
  166. package/dist/mcp/tools.d.ts +104 -4
  167. package/dist/mcp/tools.d.ts.map +1 -1
  168. package/dist/mcp/tools.js +2390 -191
  169. package/dist/mcp/tools.js.map +1 -1
  170. package/dist/mcp/transport.d.ts.map +1 -1
  171. package/dist/mcp/transport.js +18 -0
  172. package/dist/mcp/transport.js.map +1 -1
  173. package/dist/project-config.d.ts +38 -0
  174. package/dist/project-config.d.ts.map +1 -1
  175. package/dist/project-config.js +101 -2
  176. package/dist/project-config.js.map +1 -1
  177. package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -1
  178. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  179. package/dist/resolution/c-fnptr-synthesizer.js +178 -146
  180. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  181. package/dist/resolution/callback-synthesizer.d.ts +9 -1
  182. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  183. package/dist/resolution/callback-synthesizer.js +1164 -176
  184. package/dist/resolution/callback-synthesizer.js.map +1 -1
  185. package/dist/resolution/frameworks/cics.d.ts +20 -0
  186. package/dist/resolution/frameworks/cics.d.ts.map +1 -0
  187. package/dist/resolution/frameworks/cics.js +90 -0
  188. package/dist/resolution/frameworks/cics.js.map +1 -0
  189. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  190. package/dist/resolution/frameworks/index.js +3 -0
  191. package/dist/resolution/frameworks/index.js.map +1 -1
  192. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  193. package/dist/resolution/frameworks/java.js +14 -6
  194. package/dist/resolution/frameworks/java.js.map +1 -1
  195. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  196. package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  197. package/dist/resolution/frameworks/terraform.js +277 -0
  198. package/dist/resolution/frameworks/terraform.js.map +1 -0
  199. package/dist/resolution/goframe-synthesizer.d.ts +2 -1
  200. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
  201. package/dist/resolution/goframe-synthesizer.js +8 -3
  202. package/dist/resolution/goframe-synthesizer.js.map +1 -1
  203. package/dist/resolution/import-resolver.d.ts +7 -0
  204. package/dist/resolution/import-resolver.d.ts.map +1 -1
  205. package/dist/resolution/import-resolver.js +129 -4
  206. package/dist/resolution/import-resolver.js.map +1 -1
  207. package/dist/resolution/index.d.ts +60 -7
  208. package/dist/resolution/index.d.ts.map +1 -1
  209. package/dist/resolution/index.js +430 -80
  210. package/dist/resolution/index.js.map +1 -1
  211. package/dist/resolution/name-matcher.d.ts +0 -3
  212. package/dist/resolution/name-matcher.d.ts.map +1 -1
  213. package/dist/resolution/name-matcher.js +354 -33
  214. package/dist/resolution/name-matcher.js.map +1 -1
  215. package/dist/resolution/strip-comments.d.ts +1 -1
  216. package/dist/resolution/strip-comments.d.ts.map +1 -1
  217. package/dist/resolution/strip-comments.js +49 -0
  218. package/dist/resolution/strip-comments.js.map +1 -1
  219. package/dist/resolution/types.d.ts +29 -0
  220. package/dist/resolution/types.d.ts.map +1 -1
  221. package/dist/resolution/workspace-packages.d.ts +10 -0
  222. package/dist/resolution/workspace-packages.d.ts.map +1 -1
  223. package/dist/resolution/workspace-packages.js +142 -4
  224. package/dist/resolution/workspace-packages.js.map +1 -1
  225. package/dist/search/identifier-segments.d.ts +60 -0
  226. package/dist/search/identifier-segments.d.ts.map +1 -0
  227. package/dist/search/identifier-segments.js +176 -0
  228. package/dist/search/identifier-segments.js.map +1 -0
  229. package/dist/search/query-utils.d.ts +201 -0
  230. package/dist/search/query-utils.d.ts.map +1 -1
  231. package/dist/search/query-utils.js +919 -2
  232. package/dist/search/query-utils.js.map +1 -1
  233. package/dist/sync/git-hooks.d.ts.map +1 -1
  234. package/dist/sync/git-hooks.js +2 -0
  235. package/dist/sync/git-hooks.js.map +1 -1
  236. package/dist/sync/watcher.d.ts +10 -5
  237. package/dist/sync/watcher.d.ts.map +1 -1
  238. package/dist/sync/watcher.js +51 -14
  239. package/dist/sync/watcher.js.map +1 -1
  240. package/dist/sync/worktree.d.ts.map +1 -1
  241. package/dist/sync/worktree.js +5 -0
  242. package/dist/sync/worktree.js.map +1 -1
  243. package/dist/types.d.ts +19 -1
  244. package/dist/types.d.ts.map +1 -1
  245. package/dist/types.js +10 -0
  246. package/dist/types.js.map +1 -1
  247. package/dist/upgrade/index.d.ts +32 -0
  248. package/dist/upgrade/index.d.ts.map +1 -1
  249. package/dist/upgrade/index.js +162 -14
  250. package/dist/upgrade/index.js.map +1 -1
  251. package/dist/upgrade/remove-binary.d.ts +87 -0
  252. package/dist/upgrade/remove-binary.d.ts.map +1 -0
  253. package/dist/upgrade/remove-binary.js +289 -0
  254. package/dist/upgrade/remove-binary.js.map +1 -0
  255. package/dist/upgrade/update-check.d.ts +92 -0
  256. package/dist/upgrade/update-check.d.ts.map +1 -0
  257. package/dist/upgrade/update-check.js +258 -0
  258. package/dist/upgrade/update-check.js.map +1 -0
  259. package/package.json +2 -2
  260. package/scripts/agent-eval/run-all.sh +6 -0
  261. package/scripts/build-bundle.sh +5 -0
  262. package/scripts/npm-shim.js +8 -1
  263. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
  264. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
  265. package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-310.pyc +0 -0
  266. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
  267. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
  268. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
  269. package/scripts/exp_boundary_eval/data/agents.json +0 -109
  270. package/scripts/exp_boundary_eval/data/experiments.json +0 -140
  271. package/scripts/qa_eval/README.md +0 -407
  272. package/scripts/qa_eval/_test_deveco_probe.py +0 -41
  273. package/scripts/qa_eval/agent_runner.py +0 -526
  274. package/scripts/qa_eval/data/.gitignore +0 -4
  275. package/scripts/qa_eval/data/test-set.jsonl +0 -2
  276. package/scripts/qa_eval/eval_metrics.py +0 -274
  277. package/scripts/qa_eval/external_agent.py +0 -976
  278. package/scripts/qa_eval/llm_config.py +0 -92
  279. package/scripts/qa_eval/memory_monitor.py +0 -132
  280. package/scripts/qa_eval/my_answer_accuracy.py +0 -187
  281. package/scripts/qa_eval/requirements.txt +0 -2
  282. package/scripts/qa_eval/run_pipeline.py +0 -804
  283. package/scripts/qa_eval/stats_efficiency.py +0 -279
  284. package/scripts/qa_eval/stats_scores.py +0 -207
@@ -1,279 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Stage 4 — 从 Agent 日志统计效率指标(首响应、轮次、总耗时、总 Token)。
4
-
5
- 「首响应」= 从 Evaluate 开始到第一次 LLM API 返回的耗时(日志行 `first token`)。
6
-
7
- 兼容 CodeGenie / Trae 等 Agent 日志格式:
8
- - Evaluate N:
9
- - first token
10
- - the N turn
11
- - totalTokenCount = N
12
-
13
- Usage:
14
- python scripts/qa_eval/stats_efficiency.py -l scripts/qa_eval/log/agent-with-builtin.log
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- import argparse
20
- import re
21
- import sys
22
- from datetime import datetime
23
- from pathlib import Path
24
- from statistics import mean
25
-
26
- _SCRIPT_DIR = Path(__file__).resolve().parent
27
- DEFAULT_LOG = _SCRIPT_DIR / "log" / "agent-with-builtin.log"
28
-
29
- TIME_FMT = "%Y-%m-%d %H:%M:%S.%f"
30
-
31
- TIME_PATTERN = re.compile(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+)")
32
- EVALUATE_PATTERN = re.compile(r"Evaluate\s+(\d+):")
33
- FIRST_TOKEN_PATTERN = re.compile(r"first token")
34
- TURN_PATTERN = re.compile(r"the\s+(\d+)\s+turn")
35
- TOKEN_PATTERN = re.compile(r"totalTokenCount\s*=\s*(\d+)")
36
- PEAK_RSS_PATTERN = re.compile(r"peakRssMb\s*=\s*([\d.]+)")
37
- AVG_RSS_PATTERN = re.compile(r"avgRssMb\s*=\s*([\d.]+)")
38
-
39
-
40
- def parse_agent_log(log_file: Path) -> list[dict]:
41
- tasks: list[dict] = []
42
- current_task: dict | None = None
43
- last_log_time: datetime | None = None
44
-
45
- with log_file.open("r", encoding="utf-8", errors="replace") as f:
46
- for line in f:
47
- line = line.strip()
48
- if not line:
49
- continue
50
-
51
- time_match = TIME_PATTERN.search(line)
52
- if not time_match:
53
- continue
54
-
55
- try:
56
- log_time = datetime.strptime(time_match.group(1), TIME_FMT)
57
- except ValueError:
58
- continue
59
-
60
- last_log_time = log_time
61
-
62
- eval_match = EVALUATE_PATTERN.search(line)
63
- if eval_match:
64
- if current_task:
65
- current_task["end_time"] = log_time
66
- tasks.append(current_task)
67
-
68
- current_task = {
69
- "id": int(eval_match.group(1)),
70
- "start_time": log_time,
71
- "first_token_time": None,
72
- "first_api_response_time": None,
73
- "max_turn": 0,
74
- "end_time": None,
75
- "total_tokens": 0,
76
- "peak_rss_mb": None,
77
- "avg_rss_mb": None,
78
- }
79
- continue
80
-
81
- if current_task is None:
82
- continue
83
-
84
- if FIRST_TOKEN_PATTERN.search(line):
85
- current_task["first_token_time"] = log_time
86
- continue
87
-
88
- turn_match = TURN_PATTERN.search(line)
89
- if turn_match:
90
- turn = int(turn_match.group(1))
91
- current_task["max_turn"] = max(current_task["max_turn"], turn)
92
-
93
- token_match = TOKEN_PATTERN.search(line)
94
- if token_match:
95
- if current_task["first_api_response_time"] is None:
96
- current_task["first_api_response_time"] = log_time
97
- current_task["total_tokens"] += int(token_match.group(1))
98
-
99
- peak_match = PEAK_RSS_PATTERN.search(line)
100
- if peak_match:
101
- current_task["peak_rss_mb"] = float(peak_match.group(1))
102
-
103
- avg_match = AVG_RSS_PATTERN.search(line)
104
- if avg_match:
105
- current_task["avg_rss_mb"] = float(avg_match.group(1))
106
-
107
- if current_task:
108
- current_task["end_time"] = last_log_time
109
- tasks.append(current_task)
110
-
111
- return tasks
112
-
113
-
114
- def _memory_from_rows(rows: list[dict]) -> tuple[list[float], list[float]]:
115
- peaks: list[float] = []
116
- avgs: list[float] = []
117
- for row in rows:
118
- mem = row.get("agent_memory_mb") or {}
119
- if mem.get("peak_rss_mb") is not None:
120
- peaks.append(float(mem["peak_rss_mb"]))
121
- if mem.get("avg_rss_mb") is not None:
122
- avgs.append(float(mem["avg_rss_mb"]))
123
- return peaks, avgs
124
-
125
-
126
- def summarize_jsonl_usage(rows: list[dict]) -> dict:
127
- """Fallback when agent .log is missing: aggregate agent_usage from JSONL rows."""
128
- tokens: list[int] = []
129
- for row in rows:
130
- usage = row.get("agent_usage") or {}
131
- total = usage.get("total_tokens")
132
- if isinstance(total, (int, float)):
133
- tokens.append(int(total))
134
- peaks, avgs = _memory_from_rows(rows)
135
- return {
136
- "task_count": len(rows),
137
- "avg_first_token_s": None,
138
- "avg_turns": None,
139
- "avg_duration_s": None,
140
- "avg_tokens": mean(tokens) if tokens else None,
141
- "total_tokens": sum(tokens),
142
- "avg_peak_rss_mb": mean(peaks) if peaks else None,
143
- "max_peak_rss_mb": max(peaks) if peaks else None,
144
- "avg_avg_rss_mb": mean(avgs) if avgs else None,
145
- "source": "jsonl",
146
- }
147
-
148
-
149
- FIRST_TOKEN_MIN_S = 0.05
150
-
151
-
152
- def effective_first_response_time(task: dict) -> datetime | None:
153
- """首响应时间:优先用 first token 行;若与 Evaluate 几乎同时(旧日志 bug)则回退到首次 API 返回。"""
154
- start = task.get("start_time")
155
- ft = task.get("first_token_time")
156
- if start and ft:
157
- if (ft - start).total_seconds() >= FIRST_TOKEN_MIN_S:
158
- return ft
159
- return task.get("first_api_response_time") or ft
160
-
161
-
162
- def summarize_tasks(tasks: list[dict], *, print_report: bool = True) -> dict:
163
- first_token_costs: list[float] = []
164
- turns: list[int] = []
165
- durations: list[float] = []
166
- tokens_list: list[int] = []
167
- peak_rss: list[float] = []
168
- avg_rss: list[float] = []
169
-
170
- if print_report:
171
- print("=" * 100)
172
- print("任务明细")
173
- print("=" * 100)
174
-
175
- for task in tasks:
176
- first_token_seconds = None
177
- response_time = effective_first_response_time(task)
178
- if response_time and task["start_time"]:
179
- first_token_seconds = (response_time - task["start_time"]).total_seconds()
180
- first_token_costs.append(first_token_seconds)
181
-
182
- turns.append(task["max_turn"])
183
-
184
- duration_seconds = 0.0
185
- if task["end_time"] and task["start_time"]:
186
- duration_seconds = (task["end_time"] - task["start_time"]).total_seconds()
187
- durations.append(duration_seconds)
188
-
189
- tokens_list.append(task["total_tokens"])
190
- if task.get("peak_rss_mb") is not None:
191
- peak_rss.append(float(task["peak_rss_mb"]))
192
- if task.get("avg_rss_mb") is not None:
193
- avg_rss.append(float(task["avg_rss_mb"]))
194
-
195
- if print_report:
196
- first_display = f"{first_token_seconds:.2f}s" if first_token_seconds is not None else "N/A"
197
- mem_display = (
198
- f"{task['peak_rss_mb']:.0f}MB"
199
- if task.get("peak_rss_mb") is not None
200
- else "N/A"
201
- )
202
- print(
203
- f"任务 {task['id']:>3} | "
204
- f"首响应: {first_display:<8} | "
205
- f"轮次: {task['max_turn']:>3} | "
206
- f"总时间: {duration_seconds:.2f}s | "
207
- f"总Token: {task['total_tokens']:<6} | "
208
- f"峰值内存: {mem_display}"
209
- )
210
-
211
- summary = {
212
- "task_count": len(tasks),
213
- "avg_first_token_s": mean(first_token_costs) if first_token_costs else None,
214
- "avg_turns": mean(turns) if turns else None,
215
- "avg_duration_s": mean(durations) if durations else None,
216
- "avg_tokens": mean(tokens_list) if tokens_list else None,
217
- "total_tokens": sum(tokens_list),
218
- "avg_peak_rss_mb": mean(peak_rss) if peak_rss else None,
219
- "max_peak_rss_mb": max(peak_rss) if peak_rss else None,
220
- "avg_avg_rss_mb": mean(avg_rss) if avg_rss else None,
221
- "source": "log",
222
- }
223
-
224
- if print_report:
225
- print("\n" + "=" * 100)
226
- print("统计结果")
227
- print("=" * 100)
228
- print(f"任务总数: {summary['task_count']}")
229
-
230
- if summary["avg_first_token_s"] is not None:
231
- print(f"平均首响应时间: {summary['avg_first_token_s']:.2f} 秒")
232
- if summary["avg_turns"] is not None:
233
- print(f"平均轮次: {summary['avg_turns']:.2f}")
234
- if summary["avg_duration_s"] is not None:
235
- print(f"平均总时间: {summary['avg_duration_s'] / 60:.2f} min ({summary['avg_duration_s']:.2f} s)")
236
- if summary["avg_tokens"] is not None:
237
- print(f"平均总 Token: {summary['avg_tokens'] / 1000:.2f} k")
238
- print(f"合计总 Token: {summary['total_tokens']}")
239
- if summary["avg_peak_rss_mb"] is not None:
240
- print(f"平均峰值内存: {summary['avg_peak_rss_mb']:.1f} MB")
241
- if summary["max_peak_rss_mb"] is not None:
242
- print(f"最大峰值内存: {summary['max_peak_rss_mb']:.1f} MB")
243
- if summary["avg_avg_rss_mb"] is not None:
244
- print(f"平均内存占用: {summary['avg_avg_rss_mb']:.1f} MB")
245
-
246
- print("=" * 100)
247
- return summary
248
-
249
-
250
- def summarize_log(log_file: Path, *, print_report: bool = True) -> dict | None:
251
- if not log_file.is_file():
252
- return None
253
- tasks = parse_agent_log(log_file)
254
- if not tasks:
255
- return None
256
- return summarize_tasks(tasks, print_report=print_report)
257
-
258
-
259
- def main() -> int:
260
- parser = argparse.ArgumentParser(description="Agent 日志效率统计")
261
- parser.add_argument("--log", "-l", type=str, default=str(DEFAULT_LOG), help="Agent 日志路径")
262
- args = parser.parse_args()
263
-
264
- log_file = Path(args.log)
265
- if not log_file.is_file():
266
- print(f"错误: 日志文件 {log_file} 不存在")
267
- return 1
268
-
269
- tasks = parse_agent_log(log_file)
270
- if not tasks:
271
- print("警告: 日志中未解析到任何 Evaluate 任务段")
272
- return 1
273
-
274
- summarize_tasks(tasks)
275
- return 0
276
-
277
-
278
- if __name__ == "__main__":
279
- raise SystemExit(main())
@@ -1,207 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Stage 3 — Answer Accuracy Score 统计。
4
-
5
- Usage:
6
- python scripts/qa_eval/stats_scores.py -i scripts/qa_eval/log/result-with-builtin-scored.jsonl
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import argparse
12
- import json
13
- import statistics
14
- import sys
15
- from pathlib import Path
16
-
17
- _SCRIPT_DIR = Path(__file__).resolve().parent
18
- DEFAULT_INPUT = _SCRIPT_DIR / "log" / "result-with-builtin-scored.jsonl"
19
-
20
-
21
- def calculate_answer_accuracy_statistics(file_path: Path) -> dict | None:
22
- scores: list[float] = []
23
- total_samples = 0
24
- success_samples = 0
25
- failed_samples = 0
26
-
27
- print(f"正在读取文件: {file_path}")
28
-
29
- try:
30
- with file_path.open("r", encoding="utf-8") as f:
31
- for line_num, line in enumerate(f, 1):
32
- if not line.strip():
33
- continue
34
- try:
35
- data = json.loads(line)
36
- total_samples += 1
37
-
38
- if "answer_accuracy_score" not in data:
39
- failed_samples += 1
40
- print(f" 第{line_num}行: 缺少 answer_accuracy_score 字段")
41
- continue
42
-
43
- score = data["answer_accuracy_score"]
44
- eval_status = data.get("evaluation_status", "unknown")
45
-
46
- if (
47
- eval_status == "success"
48
- and isinstance(score, (int, float))
49
- and not (isinstance(score, float) and str(score).lower() == "nan")
50
- ):
51
- scores.append(float(score))
52
- success_samples += 1
53
- else:
54
- failed_samples += 1
55
- print(f" 第{line_num}行: 评估失败或无效分数 - 状态: {eval_status}, 分数: {score}")
56
-
57
- except json.JSONDecodeError as e:
58
- print(f" 第{line_num}行: JSON 解析错误 - {e}")
59
- failed_samples += 1
60
-
61
- except FileNotFoundError:
62
- print(f"错误: 文件 {file_path} 不存在")
63
- return None
64
- except OSError as e:
65
- print(f"读取文件时出错: {e}")
66
- return None
67
-
68
- if not scores:
69
- print("警告: 没有找到有效的 answer_accuracy_score 数据")
70
- return {
71
- "total_samples": total_samples,
72
- "success_samples": success_samples,
73
- "failed_samples": failed_samples,
74
- "valid_scores": 0,
75
- }
76
-
77
- stats = {
78
- "total_samples": total_samples,
79
- "success_samples": success_samples,
80
- "failed_samples": failed_samples,
81
- "valid_scores": len(scores),
82
- "mean": statistics.mean(scores),
83
- "median": statistics.median(scores),
84
- "min": min(scores),
85
- "max": max(scores),
86
- "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
87
- "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
88
- "_scores": scores,
89
- }
90
- return stats
91
-
92
-
93
- def compute_stats_from_rows(rows: list[dict]) -> dict | None:
94
- """Same stats as calculate_answer_accuracy_statistics, from in-memory rows."""
95
- scores: list[float] = []
96
- total_samples = len(rows)
97
- success_samples = 0
98
- failed_samples = 0
99
-
100
- for data in rows:
101
- if "answer_accuracy_score" not in data:
102
- failed_samples += 1
103
- continue
104
- score = data["answer_accuracy_score"]
105
- eval_status = data.get("evaluation_status", "unknown")
106
- if (
107
- eval_status == "success"
108
- and isinstance(score, (int, float))
109
- and not (isinstance(score, float) and str(score).lower() == "nan")
110
- ):
111
- scores.append(float(score))
112
- success_samples += 1
113
- else:
114
- failed_samples += 1
115
-
116
- if not scores:
117
- return {
118
- "total_samples": total_samples,
119
- "success_samples": success_samples,
120
- "failed_samples": failed_samples,
121
- "valid_scores": 0,
122
- }
123
-
124
- return {
125
- "total_samples": total_samples,
126
- "success_samples": success_samples,
127
- "failed_samples": failed_samples,
128
- "valid_scores": len(scores),
129
- "mean": statistics.mean(scores),
130
- "median": statistics.median(scores),
131
- "min": min(scores),
132
- "max": max(scores),
133
- "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
134
- "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
135
- "_scores": scores,
136
- }
137
-
138
-
139
- def print_statistics(stats: dict | None, file_path: Path | None = None, *, title: str | None = None) -> None:
140
- if not stats:
141
- return
142
-
143
- print("\n" + "=" * 60)
144
- print(title or "Answer Accuracy Score 统计结果")
145
- print("=" * 60)
146
- if file_path:
147
- print(f"文件: {file_path}")
148
-
149
- print(f"总样本数: {stats['total_samples']}")
150
- print(f"成功评估样本数: {stats['success_samples']}")
151
- print(f"失败评估样本数: {stats['failed_samples']}")
152
- print(f"有效分数数量: {stats['valid_scores']}")
153
-
154
- if stats["valid_scores"] > 0:
155
- print("\n分数统计:")
156
- print(f" 平均值 (Mean): {stats['mean']:.4f}")
157
- print(f" 中位数 (Median): {stats['median']:.4f}")
158
- print(f" 最小值 (Min): {stats['min']:.4f}")
159
- print(f" 最大值 (Max): {stats['max']:.4f}")
160
- if stats["valid_scores"] > 1:
161
- print(f" 标准差 (Std Dev): {stats['std_dev']:.4f}")
162
- print(f" 方差 (Variance): {stats['variance']:.4f}")
163
-
164
- scores = stats.get("_scores") or []
165
- print("\n分数分布:")
166
- ranges = [
167
- (0.0, 0.2, "很低 (0.0-0.2)"),
168
- (0.2, 0.4, "低 (0.2-0.4)"),
169
- (0.4, 0.6, "中等 (0.4-0.6)"),
170
- (0.6, 0.8, "高 (0.6-0.8)"),
171
- (0.8, 1.0, "很高 (0.8-1.0)"),
172
- ]
173
- for min_val, max_val, label in ranges:
174
- count = sum(
175
- 1
176
- for score in scores
177
- if min_val <= score < max_val or (max_val == 1.0 and score == 1.0)
178
- )
179
- pct = (count / len(scores)) * 100 if scores else 0
180
- print(f" {label}: {count} ({pct:.1f}%)")
181
-
182
- print("=" * 60)
183
-
184
- if stats["valid_scores"] > 0:
185
- success_rate = (stats["success_samples"] / stats["total_samples"]) * 100 if stats["total_samples"] else 0
186
- print(f"\n简要报告: {stats['total_samples']} 个样本,{stats['success_samples']} 个成功评估")
187
- print(f"Answer Accuracy Score 平均值: {stats['mean']:.4f}")
188
- print(f"评估成功率: {success_rate:.1f}%")
189
-
190
-
191
- def main() -> int:
192
- parser = argparse.ArgumentParser(description="Answer Accuracy Score 统计")
193
- parser.add_argument("--input", "-i", type=str, default=str(DEFAULT_INPUT), help="打分后的 JSONL")
194
- args = parser.parse_args()
195
-
196
- file_path = Path(args.input)
197
- if not file_path.is_file():
198
- print(f"错误: 文件 {file_path} 不存在")
199
- return 1
200
-
201
- stats = calculate_answer_accuracy_statistics(file_path)
202
- print_statistics(stats, file_path)
203
- return 0 if stats else 1
204
-
205
-
206
- if __name__ == "__main__":
207
- raise SystemExit(main())