homegraph 1.5.0 → 1.5.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.
Files changed (213) hide show
  1. package/dist/bin/command-supervision.d.ts.map +1 -1
  2. package/dist/bin/command-supervision.js +7 -4
  3. package/dist/bin/command-supervision.js.map +1 -1
  4. package/dist/extraction/index.d.ts +1 -1
  5. package/dist/extraction/index.d.ts.map +1 -1
  6. package/dist/extraction/index.js +54 -43
  7. package/dist/extraction/index.js.map +1 -1
  8. package/dist/extraction/languages/arkts.d.ts +69 -12
  9. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  10. package/dist/extraction/languages/arkts.js +739 -86
  11. package/dist/extraction/languages/arkts.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp/diff-impact.d.ts +131 -0
  16. package/dist/mcp/diff-impact.d.ts.map +1 -0
  17. package/dist/mcp/diff-impact.js +385 -0
  18. package/dist/mcp/diff-impact.js.map +1 -0
  19. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  20. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  21. package/dist/mcp/liveness-watchdog.js +134 -22
  22. package/dist/mcp/liveness-watchdog.js.map +1 -1
  23. package/dist/mcp/server-instructions.d.ts +1 -1
  24. package/dist/mcp/server-instructions.d.ts.map +1 -1
  25. package/dist/mcp/server-instructions.js +2 -0
  26. package/dist/mcp/server-instructions.js.map +1 -1
  27. package/dist/mcp/tools.d.ts +21 -5
  28. package/dist/mcp/tools.d.ts.map +1 -1
  29. package/dist/mcp/tools.js +305 -55
  30. package/dist/mcp/tools.js.map +1 -1
  31. package/dist/resolution/callback-synthesizer.d.ts +2 -1
  32. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  33. package/dist/resolution/callback-synthesizer.js +153 -53
  34. package/dist/resolution/callback-synthesizer.js.map +1 -1
  35. package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -1
  36. package/dist/resolution/frameworks/arkts-entry.js +20 -9
  37. package/dist/resolution/frameworks/arkts-entry.js.map +1 -1
  38. package/dist/resolution/index.d.ts.map +1 -1
  39. package/dist/resolution/index.js +1 -1
  40. package/dist/resolution/index.js.map +1 -1
  41. package/dist/resolution/memory-budget.d.ts +33 -0
  42. package/dist/resolution/memory-budget.d.ts.map +1 -1
  43. package/dist/resolution/memory-budget.js +58 -0
  44. package/dist/resolution/memory-budget.js.map +1 -1
  45. package/dist/resolution/resolver-pool.d.ts +2 -0
  46. package/dist/resolution/resolver-pool.d.ts.map +1 -1
  47. package/dist/resolution/resolver-pool.js +4 -0
  48. package/dist/resolution/resolver-pool.js.map +1 -1
  49. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  50. package/dist/ui/shimmer-progress.js +4 -1
  51. package/dist/ui/shimmer-progress.js.map +1 -1
  52. package/package.json +4 -5
  53. package/dist/arkts/ohos-api-index.d.ts +0 -15
  54. package/dist/arkts/ohos-api-index.d.ts.map +0 -1
  55. package/dist/arkts/ohos-api-index.js +0 -190
  56. package/dist/arkts/ohos-api-index.js.map +0 -1
  57. package/dist/arkts/ohos-sdk-input.d.ts +0 -36
  58. package/dist/arkts/ohos-sdk-input.d.ts.map +0 -1
  59. package/dist/arkts/ohos-sdk-input.js +0 -214
  60. package/dist/arkts/ohos-sdk-input.js.map +0 -1
  61. package/dist/extraction/languages/arkts-state-decorators.d.ts +0 -13
  62. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +0 -1
  63. package/dist/extraction/languages/arkts-state-decorators.js +0 -26
  64. package/dist/extraction/languages/arkts-state-decorators.js.map +0 -1
  65. package/dist/extraction/languages/arkts-viewtree.d.ts +0 -24
  66. package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
  67. package/dist/extraction/languages/arkts-viewtree.js +0 -148
  68. package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
  69. package/dist/extraction/languages/ohos-api-consumer.d.ts +0 -34
  70. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +0 -1
  71. package/dist/extraction/languages/ohos-api-consumer.js +0 -283
  72. package/dist/extraction/languages/ohos-api-consumer.js.map +0 -1
  73. package/dist/reasoning/config.d.ts +0 -45
  74. package/dist/reasoning/config.d.ts.map +0 -1
  75. package/dist/reasoning/config.js +0 -171
  76. package/dist/reasoning/config.js.map +0 -1
  77. package/dist/reasoning/credentials.d.ts +0 -5
  78. package/dist/reasoning/credentials.d.ts.map +0 -1
  79. package/dist/reasoning/credentials.js +0 -83
  80. package/dist/reasoning/credentials.js.map +0 -1
  81. package/dist/reasoning/login.d.ts +0 -21
  82. package/dist/reasoning/login.d.ts.map +0 -1
  83. package/dist/reasoning/login.js +0 -85
  84. package/dist/reasoning/login.js.map +0 -1
  85. package/dist/reasoning/reasoner.d.ts +0 -43
  86. package/dist/reasoning/reasoner.d.ts.map +0 -1
  87. package/dist/reasoning/reasoner.js +0 -308
  88. package/dist/reasoning/reasoner.js.map +0 -1
  89. package/dist/spec/build/git-scanner.d.ts +0 -93
  90. package/dist/spec/build/git-scanner.d.ts.map +0 -1
  91. package/dist/spec/build/git-scanner.js +0 -254
  92. package/dist/spec/build/git-scanner.js.map +0 -1
  93. package/dist/spec/evolve/llm-client.d.ts +0 -50
  94. package/dist/spec/evolve/llm-client.d.ts.map +0 -1
  95. package/dist/spec/evolve/llm-client.js +0 -176
  96. package/dist/spec/evolve/llm-client.js.map +0 -1
  97. package/dist/spec/evolve/logic-checker.d.ts +0 -12
  98. package/dist/spec/evolve/logic-checker.d.ts.map +0 -1
  99. package/dist/spec/evolve/logic-checker.js +0 -24
  100. package/dist/spec/evolve/logic-checker.js.map +0 -1
  101. package/dist/spec/git-utils.d.ts +0 -8
  102. package/dist/spec/git-utils.d.ts.map +0 -1
  103. package/dist/spec/git-utils.js +0 -14
  104. package/dist/spec/git-utils.js.map +0 -1
  105. package/dist/spec/llm/index.d.ts +0 -3
  106. package/dist/spec/llm/index.d.ts.map +0 -1
  107. package/dist/spec/llm/index.js +0 -11
  108. package/dist/spec/llm/index.js.map +0 -1
  109. package/dist/spec/mine/clusterer.d.ts +0 -63
  110. package/dist/spec/mine/clusterer.d.ts.map +0 -1
  111. package/dist/spec/mine/clusterer.js +0 -904
  112. package/dist/spec/mine/clusterer.js.map +0 -1
  113. package/dist/spec/mine/progress-handler.d.ts +0 -22
  114. package/dist/spec/mine/progress-handler.d.ts.map +0 -1
  115. package/dist/spec/mine/progress-handler.js +0 -108
  116. package/dist/spec/mine/progress-handler.js.map +0 -1
  117. package/dist/spec/mine/progress.d.ts +0 -23
  118. package/dist/spec/mine/progress.d.ts.map +0 -1
  119. package/dist/spec/mine/progress.js +0 -12
  120. package/dist/spec/mine/progress.js.map +0 -1
  121. package/dist/spec/mining/diff-parser.d.ts +0 -33
  122. package/dist/spec/mining/diff-parser.d.ts.map +0 -1
  123. package/dist/spec/mining/diff-parser.js +0 -166
  124. package/dist/spec/mining/diff-parser.js.map +0 -1
  125. package/dist/spec/mining/git-scanner.d.ts +0 -103
  126. package/dist/spec/mining/git-scanner.d.ts.map +0 -1
  127. package/dist/spec/mining/git-scanner.js +0 -307
  128. package/dist/spec/mining/git-scanner.js.map +0 -1
  129. package/dist/spec/mining/pipeline.d.ts +0 -53
  130. package/dist/spec/mining/pipeline.d.ts.map +0 -1
  131. package/dist/spec/mining/pipeline.js +0 -178
  132. package/dist/spec/mining/pipeline.js.map +0 -1
  133. package/dist/spec/mining/scope-resolver.d.ts +0 -45
  134. package/dist/spec/mining/scope-resolver.d.ts.map +0 -1
  135. package/dist/spec/mining/scope-resolver.js +0 -103
  136. package/dist/spec/mining/scope-resolver.js.map +0 -1
  137. package/dist/spec/mining/spec-extractor.d.ts +0 -69
  138. package/dist/spec/mining/spec-extractor.d.ts.map +0 -1
  139. package/dist/spec/mining/spec-extractor.js +0 -369
  140. package/dist/spec/mining/spec-extractor.js.map +0 -1
  141. package/dist/spec/utils.d.ts +0 -155
  142. package/dist/spec/utils.d.ts.map +0 -1
  143. package/dist/spec/utils.js +0 -411
  144. package/dist/spec/utils.js.map +0 -1
  145. package/scripts/add-lang/bench.sh +0 -60
  146. package/scripts/add-lang/check-grammar.mjs +0 -75
  147. package/scripts/add-lang/dump-ast.mjs +0 -103
  148. package/scripts/add-lang/verify-extraction.mjs +0 -70
  149. package/scripts/agent-eval/ab-adoption.sh +0 -91
  150. package/scripts/agent-eval/ab-hook.sh +0 -86
  151. package/scripts/agent-eval/ab-impl.sh +0 -78
  152. package/scripts/agent-eval/ab-new-vs-baseline.sh +0 -102
  153. package/scripts/agent-eval/ab-sufficiency.sh +0 -78
  154. package/scripts/agent-eval/arms-F.sh +0 -21
  155. package/scripts/agent-eval/arms-matrix.sh +0 -37
  156. package/scripts/agent-eval/audit.sh +0 -68
  157. package/scripts/agent-eval/bench-readme.sh +0 -28
  158. package/scripts/agent-eval/bench-why-repo.sh +0 -22
  159. package/scripts/agent-eval/block-read-hook.sh +0 -19
  160. package/scripts/agent-eval/hook-settings.json +0 -15
  161. package/scripts/agent-eval/itrun.sh +0 -120
  162. package/scripts/agent-eval/offload-eval-3arm.sh +0 -72
  163. package/scripts/agent-eval/offload-eval-cost.mjs +0 -133
  164. package/scripts/agent-eval/offload-eval-effort.mjs +0 -108
  165. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +0 -25
  166. package/scripts/agent-eval/offload-eval-frontload.sh +0 -47
  167. package/scripts/agent-eval/offload-eval-ground-truth.json +0 -18
  168. package/scripts/agent-eval/offload-eval-hook.mjs +0 -84
  169. package/scripts/agent-eval/offload-eval-judge.mjs +0 -103
  170. package/scripts/agent-eval/offload-eval-matrix.sh +0 -20
  171. package/scripts/agent-eval/offload-eval-metrics.mjs +0 -94
  172. package/scripts/agent-eval/offload-eval-refs1.sh +0 -50
  173. package/scripts/agent-eval/offload-eval-setup.sh +0 -24
  174. package/scripts/agent-eval/offload-eval-styles.sh +0 -72
  175. package/scripts/agent-eval/offload-eval-summarize.mjs +0 -68
  176. package/scripts/agent-eval/offload-eval.md +0 -76
  177. package/scripts/agent-eval/parse-arms.mjs +0 -116
  178. package/scripts/agent-eval/parse-bench-readme.mjs +0 -84
  179. package/scripts/agent-eval/parse-run.mjs +0 -45
  180. package/scripts/agent-eval/parse-session.mjs +0 -93
  181. package/scripts/agent-eval/probe-context.mjs +0 -21
  182. package/scripts/agent-eval/probe-explore.mjs +0 -40
  183. package/scripts/agent-eval/probe-node.mjs +0 -20
  184. package/scripts/agent-eval/probe-sweep.mjs +0 -119
  185. package/scripts/agent-eval/probe-trace.mjs +0 -20
  186. package/scripts/agent-eval/redirect-read-hook.sh +0 -38
  187. package/scripts/agent-eval/repro-concurrent-explore.mjs +0 -119
  188. package/scripts/agent-eval/repro-daemon-clients.mjs +0 -125
  189. package/scripts/agent-eval/run-agent.sh +0 -34
  190. package/scripts/agent-eval/run-all.sh +0 -75
  191. package/scripts/agent-eval/run-arms.sh +0 -56
  192. package/scripts/agent-eval/seq-matrix.mjs +0 -137
  193. package/scripts/build-bundle.sh +0 -123
  194. package/scripts/exp_boundary_eval/README.md +0 -247
  195. package/scripts/exp_boundary_eval/_test_mcp_chain.py +0 -78
  196. package/scripts/exp_boundary_eval/_test_stdin.py +0 -8
  197. package/scripts/exp_boundary_eval/_utils.py +0 -1116
  198. package/scripts/exp_boundary_eval/analyze.py +0 -1313
  199. package/scripts/exp_boundary_eval/deveco_arm.py +0 -519
  200. package/scripts/exp_boundary_eval/run_all.py +0 -378
  201. package/scripts/exp_boundary_eval/run_one.py +0 -165
  202. package/scripts/exp_boundary_eval/run_session.py +0 -158
  203. package/scripts/exp_boundary_eval/setup.py +0 -120
  204. package/scripts/exp_boundary_eval/win_mcp_launcher.py +0 -73
  205. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +0 -36
  206. package/scripts/exp_boundary_eval/win_node_launcher.py +0 -24
  207. package/scripts/extract-release-notes.mjs +0 -130
  208. package/scripts/local-install.sh +0 -41
  209. package/scripts/npm-sdk.js +0 -75
  210. package/scripts/npm-shim.js +0 -275
  211. package/scripts/ohos-sdk-publish.mjs +0 -133
  212. package/scripts/pack-npm.sh +0 -119
  213. package/scripts/prepare-release.mjs +0 -270
@@ -1,378 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Run boundary-eval experiments sequentially. Multi-agent + HomeGraph A/B arm support.
4
-
5
- Usage:
6
- python run_all.py --both-arms --agent deveco # 一条命令跑 baseline + homegraph
7
- python run_all.py --agent deveco --arm homegraph
8
- python run_all.py --agent deveco --arm baseline
9
- python run_all.py 1-1 1-2 1-3
10
- python run_all.py --repo-path /path/to/photos
11
- python run_all.py --both-arms --agent deveco --repo-path /path/to/photos --skip-index
12
- """
13
-
14
- import argparse
15
- import json
16
- import os
17
- import shutil
18
- import subprocess
19
- import sys
20
- from pathlib import Path
21
- from typing import Optional, Tuple
22
-
23
- import run_one
24
- import run_session
25
- import analyze
26
- from _utils import (DATA_DIR, EVAL_ROOT, LOG_DIR, OUTPUT_DIR, GREEN, NC, append_run_log,
27
- current_time_ms, create_output_dirs, error, get_agent, header,
28
- info, load_config, log, subprocess_no_window_kwargs, verify_agent_binary, warn)
29
- from deveco_arm import (ensure_homegraph_index, git_clean_repo,
30
- write_deveco_project_config)
31
-
32
- DEFAULT_EXPERIMENTS = ["1-1", "1-2", "1-3"]
33
- SHARED_CLONE_DIR = DATA_DIR / "clone"
34
-
35
-
36
- _NW = subprocess_no_window_kwargs()
37
-
38
-
39
- def clone_repo(url: str, branch: str, clone_dir: Path) -> bool:
40
- clone_dir = Path(clone_dir)
41
- if (clone_dir / ".git").is_dir():
42
- log(f"Clone exists, updating: {clone_dir}")
43
- subprocess.run(["git", "fetch", "origin", branch], cwd=clone_dir, capture_output=True, text=True, **_NW)
44
- subprocess.run(["git", "checkout", branch], cwd=clone_dir, capture_output=True, text=True, **_NW)
45
- subprocess.run(["git", "reset", "--hard", f"origin/{branch}"], cwd=clone_dir, capture_output=True, text=True, **_NW)
46
- return True
47
- log(f"Cloning {url} (branch: {branch})...")
48
- if clone_dir.exists():
49
- shutil.rmtree(clone_dir)
50
- clone_dir.parent.mkdir(parents=True, exist_ok=True)
51
- r = subprocess.run(
52
- ["git", "clone", "--depth", "1", "--branch", branch, url, str(clone_dir)],
53
- capture_output=True, text=True,
54
- **_NW,
55
- )
56
- if r.returncode != 0:
57
- error(f"Clone failed: {r.stderr}")
58
- return False
59
- r2 = subprocess.run(["git", "rev-parse", "--short", "HEAD"], capture_output=True, text=True, cwd=clone_dir, **_NW)
60
- log(f"Clone complete: {r2.stdout.strip()}")
61
- return True
62
-
63
-
64
- def reset_clone(clone_dir: Path, branch: str, arm: str = "baseline"):
65
- subprocess.run(["git", "checkout", branch], cwd=clone_dir, capture_output=True, text=True, **_NW)
66
- subprocess.run(["git", "checkout", "."], cwd=clone_dir, capture_output=True, text=True, **_NW)
67
- git_clean_repo(clone_dir, arm)
68
-
69
-
70
- def reset_local(work_dir: Path, arm: str = "baseline"):
71
- subprocess.run(["git", "checkout", "."], cwd=work_dir, capture_output=True, text=True, **_NW)
72
- git_clean_repo(work_dir, arm)
73
-
74
-
75
- def ensure_homegraph_index_for_arm(
76
- repo_root: Path, arm: str, *, skip_index: bool = False,
77
- ) -> dict:
78
- """Index repo when running the homegraph arm."""
79
- if arm != "homegraph":
80
- return {"homegraph_index_ms": 0, "homegraph_index_success": None}
81
- return ensure_homegraph_index(repo_root, skip=skip_index)
82
-
83
-
84
- def write_run_manifest(state_dir: Path, results_dir: Path, meta: dict):
85
- manifest = {
86
- **meta,
87
- "results_dir": str(results_dir),
88
- "state_dir": str(state_dir),
89
- }
90
- for d in (state_dir, results_dir):
91
- d.mkdir(parents=True, exist_ok=True)
92
- (d / "run_manifest.json").write_text(
93
- json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8"
94
- )
95
-
96
-
97
- def resolve_work_dir(args, state_dir: Path) -> Tuple[Path, Optional[Path], bool]:
98
- """Return (work_dir, ephemeral_clone_dir_or_none, owns_ephemeral_clone)."""
99
- if args.repo_path:
100
- work_dir = Path(args.repo_path).resolve()
101
- if not (work_dir / ".git").is_dir():
102
- error(f"--repo-path 不是 git 仓库: {work_dir}")
103
- sys.exit(1)
104
- os.environ["REPO_PATH"] = str(work_dir)
105
- os.environ.pop("REPO_CLONE", None)
106
- log(f"使用本地仓库: {work_dir}")
107
- return work_dir, None, False
108
-
109
- if args.both_arms:
110
- clone_dir = SHARED_CLONE_DIR
111
- if not clone_repo(args.repo_url, args.branch, clone_dir):
112
- error("Clone 失败。请检查网络,或改用 --repo-path。")
113
- sys.exit(1)
114
- os.environ["REPO_PATH"] = str(clone_dir)
115
- os.environ.pop("REPO_CLONE", None)
116
- log(f"A/B 共用仓库: {clone_dir}")
117
- return clone_dir, None, False
118
-
119
- clone_dir = state_dir / "clone"
120
- if not clone_repo(args.repo_url, args.branch, clone_dir):
121
- error("Clone 失败。请改用 --repo-path 指定本地 checkout。")
122
- sys.exit(1)
123
- os.environ["REPO_CLONE"] = str(clone_dir)
124
- os.environ.pop("REPO_PATH", None)
125
- return clone_dir, clone_dir, True
126
-
127
-
128
- def run_suite(
129
- args, arm: str, *, prebuilt_index_meta: Optional[dict] = None,
130
- ) -> dict:
131
- """Run one arm (baseline or homegraph). Returns summary dict."""
132
- agent = get_agent(args.agent)
133
- skip_perms = not args.no_skip_permissions
134
- experiments = args.experiments or DEFAULT_EXPERIMENTS
135
- total = len(experiments)
136
- passed = 0
137
- failed = 0
138
-
139
- results_dir, state_dir = create_output_dirs(args.agent or "default", arm)
140
- work_dir, ephemeral_clone, owns_clone = resolve_work_dir(args, state_dir)
141
-
142
- if arm == "baseline":
143
- hg_dir = work_dir / ".homegraph"
144
- if hg_dir.exists():
145
- log("Baseline: 保留 .homegraph(A/B 共用仓库;baseline 通过 deveco.jsonc deny homegraph_* 隔离)")
146
-
147
- if prebuilt_index_meta is not None:
148
- index_meta = prebuilt_index_meta
149
- else:
150
- index_meta = ensure_homegraph_index_for_arm(
151
- work_dir, arm, skip_index=getattr(args, "skip_index", False),
152
- )
153
-
154
- if arm == "homegraph" and not index_meta.get("homegraph_index_success"):
155
- error("HomeGraph 索引失败,homegraph 组无法评测。")
156
- err = index_meta.get("homegraph_index_error", "")
157
- if err:
158
- error(f" 原因: {err}")
159
- error(" 请先在 homegraph 仓库执行 npm run build,再重试 run_all.py --arm homegraph")
160
- append_run_log(state_dir, f"ABORT: homegraph index failed: {err}")
161
- write_run_manifest(state_dir, results_dir, {
162
- "arm": arm, "agent": agent["name"], "agent_key": args.agent,
163
- "branch": args.branch, "repo_url": args.repo_url,
164
- "repo_root": str(work_dir), "experiments": experiments,
165
- "aborted": True, **index_meta,
166
- })
167
- sys.exit(1)
168
-
169
- if agent.get("binary") == "deveco":
170
- write_deveco_project_config(work_dir, arm)
171
-
172
- write_run_manifest(state_dir, results_dir, {
173
- "arm": arm,
174
- "agent": agent["name"],
175
- "agent_key": args.agent,
176
- "branch": args.branch,
177
- "repo_url": args.repo_url,
178
- "repo_root": str(work_dir),
179
- "experiments": experiments,
180
- **index_meta,
181
- })
182
- append_run_log(state_dir, f"开始: arm={arm}, agent={agent['name']}, exps={experiments}")
183
-
184
- os.environ["EVAL_ARM"] = arm
185
-
186
- header(f"边界评测 — 档位切换 ({arm})")
187
- print(f"组别 : {arm}")
188
- print(f"Agent : {agent['name']} ({agent['binary']})")
189
- print(f"仓库 : {work_dir}")
190
- print(f"分支 : {args.branch}")
191
- print(f"权限 : {'跳过确认' if skip_perms else '需确认'}")
192
- print(f"实验数 : {total}")
193
- print(f"输出 : {results_dir}")
194
- print(f"日志 : {state_dir}\n")
195
-
196
- overall_start = current_time_ms()
197
-
198
- for exp_id in experiments:
199
- header(f"实验 {exp_id}")
200
- append_run_log(state_dir, f"开始实验 {exp_id}")
201
-
202
- if ephemeral_clone:
203
- log("重置 clone 到干净状态...")
204
- reset_clone(ephemeral_clone, args.branch, arm)
205
- else:
206
- log("重置本地仓库...")
207
- reset_local(work_dir, arm)
208
-
209
- if exp_id == "5":
210
- ok = run_session.run_session("5", skip_perms, args.agent,
211
- results_dir=results_dir, state_dir=state_dir)
212
- else:
213
- ok = run_one.run_experiment(exp_id, skip_perms, args.agent,
214
- results_dir=results_dir, state_dir=state_dir)
215
-
216
- if ok:
217
- passed += 1
218
- else:
219
- failed += 1
220
- error(f"实验 {exp_id} 失败")
221
- print()
222
-
223
- overall_end = current_time_ms()
224
- duration = (overall_end - overall_start) // 1000
225
-
226
- append_run_log(state_dir, f"完成: passed={passed}, failed={failed}, duration_s={duration}")
227
-
228
- header(f"组别完成 — {arm}")
229
- print(f" 通过 : {passed} / {total}")
230
- print(f" 失败 : {failed} / {total}")
231
- print(f" 耗时 : {duration // 60}m {duration % 60}s")
232
- print(f" 输出 : {results_dir}")
233
- print(f" 日志 : {state_dir}")
234
-
235
- if owns_clone and ephemeral_clone and not args.keep_clone and ephemeral_clone.exists():
236
- warn(f"删除临时 clone: {ephemeral_clone}")
237
- shutil.rmtree(ephemeral_clone)
238
-
239
- return {
240
- "arm": arm,
241
- "results_dir": results_dir,
242
- "state_dir": state_dir,
243
- "passed": passed,
244
- "failed": failed,
245
- "duration_s": duration,
246
- "homegraph_index_ms": index_meta.get("homegraph_index_ms", 0),
247
- }
248
-
249
-
250
- def _generate_reports(summaries: list, agent_key: str):
251
- """Run analyze.py logic after experiments; print report paths."""
252
- reports = []
253
- for s in summaries:
254
- try:
255
- path = analyze.analyze_run(s["results_dir"], verbose=False)
256
- reports.append((s["arm"], path))
257
- except ValueError as e:
258
- warn(f"分析 {s['arm']} 失败: {e}")
259
-
260
- compare_path = None
261
- if len(summaries) == 2 and len(reports) == 2:
262
- try:
263
- baseline_s = next(s for s in summaries if s["arm"] == "baseline")
264
- homegraph_s = next(s for s in summaries if s["arm"] == "homegraph")
265
- compare_path = analyze.compare_runs(
266
- baseline_s["results_dir"],
267
- homegraph_s["results_dir"],
268
- agent=agent_key,
269
- baseline_wall_s=baseline_s["duration_s"],
270
- homegraph_wall_s=homegraph_s["duration_s"],
271
- verbose=True,
272
- )
273
- except ValueError as e:
274
- warn(f"A/B 对比报告失败: {e}")
275
-
276
- baseline_report = next((p for arm, p in reports if arm == "baseline"), None)
277
- homegraph_report = next((p for arm, p in reports if arm == "homegraph"), None)
278
- single_report = reports[0][1] if len(reports) == 1 else None
279
-
280
- analyze.print_report_locations(
281
- baseline_report=baseline_report,
282
- homegraph_report=homegraph_report,
283
- compare_report=compare_path,
284
- single_report=single_report,
285
- )
286
- return reports, compare_path
287
-
288
-
289
- def _blocking_homegraph_index(work_dir: Path, *, skip_index: bool = False) -> dict:
290
- """Run full ``homegraph index`` and block until complete (or validate existing index)."""
291
- if skip_index:
292
- header("HomeGraph 索引检查(--skip-index,不执行 index)")
293
- else:
294
- header("HomeGraph 全量 index(阻塞,Photos 约 15–30 分钟)")
295
- log(f"仓库: {work_dir}")
296
- if skip_index:
297
- log("模式: 仅校验已有 .homegraph/(可从同 commit 的其他机器复制整个目录)")
298
- else:
299
- log("命令: node --stack-size=8192 … homegraph index(见 deveco_arm.HOMEGRAPH_NODE_STACK_SIZE_KB)")
300
- meta = ensure_homegraph_index(work_dir, skip=skip_index)
301
- if not meta.get("homegraph_index_success"):
302
- error("HomeGraph index 失败,中止。")
303
- err = meta.get("homegraph_index_error", "")
304
- if err:
305
- error(f" 原因: {err}")
306
- sys.exit(1)
307
- return meta
308
-
309
-
310
- def main():
311
- analyze._configure_stdout()
312
- cfg = load_config()
313
- meta = cfg.get("meta", {})
314
-
315
- parser = argparse.ArgumentParser(description="HomeGraph 档位切换边界评测")
316
- parser.add_argument("--agent", default=os.environ.get("EVAL_AGENT", "deveco"),
317
- help="Agent:claude / cursor / codex / opencode / deveco")
318
- parser.add_argument("--arm", default=os.environ.get("EVAL_ARM", "baseline"),
319
- choices=["baseline", "homegraph"],
320
- help="baseline=不带 HomeGraph;homegraph=带 HomeGraph")
321
- parser.add_argument("--both-arms", action="store_true",
322
- help="一条命令依次跑 baseline 和 homegraph 两组 A/B")
323
- parser.add_argument("--repo-path", default=os.environ.get("REPO_PATH", ""),
324
- help="使用已有本地 clone,跳过 git clone")
325
- parser.add_argument("--keep-clone", action="store_true")
326
- parser.add_argument(
327
- "--skip-index",
328
- action="store_true",
329
- help="不跑 homegraph init/index;要求仓库根目录已有健康的 .homegraph/(可复制自同 commit 的其他 checkout)",
330
- )
331
- parser.add_argument("--no-skip-permissions", action="store_true")
332
- parser.add_argument("--branch", default=os.environ.get("BRANCH", meta.get("default_branch", "weekly_20260601")))
333
- parser.add_argument("--repo-url", default=os.environ.get("REPO_URL", meta.get("repo_url", "")))
334
- parser.add_argument("experiments", nargs="*")
335
- args = parser.parse_args()
336
-
337
- if not args.experiments:
338
- args.experiments = DEFAULT_EXPERIMENTS
339
-
340
- agent = get_agent(args.agent)
341
- ok, resolved, err_msg = verify_agent_binary(agent)
342
- if not ok:
343
- error(err_msg)
344
- sys.exit(1)
345
- log(f"Agent CLI: {resolved}")
346
-
347
- if args.both_arms and args.arm != "baseline":
348
- warn("--both-arms 已指定,忽略 --arm")
349
-
350
- if args.both_arms:
351
- _rd, _sd = create_output_dirs(args.agent or "default", "baseline")
352
- work_dir, _, _ = resolve_work_dir(args, _sd)
353
-
354
- # Index FIRST so the user sees it immediately (15–30 min); baseline does not use MCP.
355
- index_meta = _blocking_homegraph_index(work_dir, skip_index=args.skip_index)
356
-
357
- summaries = []
358
- print(f"\n{'=' * 60}\n 开始 homegraph 组(先看 HomeGraph 是否生效)\n{'=' * 60}\n")
359
- summaries.append(run_suite(args, "homegraph", prebuilt_index_meta=index_meta))
360
-
361
- print(f"\n{'=' * 60}\n 开始 baseline 组(无 HomeGraph)\n{'=' * 60}\n")
362
- summaries.append(run_suite(args, "baseline"))
363
-
364
- header("A/B 全部完成")
365
- for s in summaries:
366
- print(f" [{s['arm']}] 通过 {s['passed']},失败 {s['failed']},"
367
- f"耗时 {s['duration_s'] // 60}m{s['duration_s'] % 60}s")
368
- print(f" 输出 → {s['results_dir']}")
369
- _generate_reports(summaries, args.agent or "default")
370
- sys.exit(0 if all(s["failed"] == 0 for s in summaries) else 1)
371
-
372
- summary = run_suite(args, args.arm)
373
- _generate_reports([summary], args.agent or "default")
374
- sys.exit(0 if summary["failed"] == 0 else 1)
375
-
376
-
377
- if __name__ == "__main__":
378
- main()
@@ -1,165 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Run a single one-shot experiment. Supports multiple agents via --agent."""
3
-
4
- import os
5
- import subprocess
6
- import sys
7
- from pathlib import Path
8
- from typing import Optional
9
-
10
- import setup as setup_mod
11
- from _utils import (OUTPUT_DIR, append_run_log, build_agent_cmd, current_time_ms,
12
- error, find_experiment, get_agent, info, header,
13
- parse_output, resolve_agent_model, run_monitored_subprocess,
14
- subprocess_no_window_kwargs, warn, write_json)
15
- from deveco_arm import analyze_homegraph_stream
16
-
17
-
18
- def run_experiment(exp_id: str, skip_permissions: bool = True,
19
- agent_name: str = "",
20
- results_dir: Optional[Path] = None,
21
- state_dir: Optional[Path] = None) -> bool:
22
- exp = find_experiment(exp_id)
23
- if exp is None:
24
- error(f"Experiment {exp_id} not found in config")
25
- return False
26
- if exp.get("type") == "session":
27
- error(f"Experiment {exp_id} is session type. Use run_session.py.")
28
- return False
29
-
30
- agent = get_agent(agent_name)
31
- clone_path = os.environ.get("REPO_CLONE", "")
32
- local_repo = os.environ.get("REPO_PATH", "")
33
- if clone_path:
34
- repo_root = Path(clone_path)
35
- elif local_repo:
36
- repo_root = Path(local_repo)
37
- else:
38
- error("No repo. Use run_all.py to clone, or set REPO_PATH / REPO_CLONE.")
39
- return False
40
-
41
- title = exp["title"]
42
- prompt = exp["prompt"]
43
- max_turns = exp.get("max_turns", 15)
44
-
45
- header(f"Experiment {exp_id}: {title}")
46
- info(f"Agent: {agent['name']} ({agent['binary']}) | Repo: {repo_root}")
47
- info(f"Max turns: {max_turns} | Permissions: {'skip' if skip_permissions else 'ask'}")
48
-
49
- session_id, _ = setup_mod.prepare_environment(exp_id, "full", agent_name,
50
- state_dir=state_dir)
51
-
52
- result_dir = results_dir / exp_id if results_dir else OUTPUT_DIR / exp_id
53
- result_dir.mkdir(parents=True, exist_ok=True)
54
- (result_dir / "prompt.txt").write_text(prompt, encoding="utf-8")
55
- write_json(result_dir / "experiment_config.json", {**exp, "agent": agent_name or "default"})
56
-
57
- info("Starting agent...")
58
- start_ms = current_time_ms()
59
- os.chdir(repo_root)
60
-
61
- cmd = build_agent_cmd(agent, session_id, max_turns, prompt,
62
- resume=False, skip_permissions=skip_permissions,
63
- repo_root=repo_root)
64
-
65
- stream_path = result_dir / "stream_output.jsonl"
66
- text_path = result_dir / "raw_output.txt"
67
- track_hg = os.environ.get("EVAL_ARM", "") == "homegraph"
68
-
69
- with open(stream_path, "w", encoding="utf-8") as stream_f:
70
- result, mem = run_monitored_subprocess(
71
- cmd, stdout=stream_f, stderr=subprocess.STDOUT, text=True,
72
- track_homegraph=track_hg,
73
- )
74
- exit_code = result.returncode
75
-
76
- end_ms = current_time_ms()
77
- dur_ms = end_ms - start_ms
78
- dur_s = f"{dur_ms / 1000:.1f}"
79
- info(f"Completed in {dur_s}s (exit={exit_code})")
80
-
81
- # Parse agent output
82
- parser_name = agent.get("parser", "claude_stream_json")
83
- stats = parse_output(stream_path, parser_name)
84
-
85
- if exit_code != 0:
86
- error(f"Agent 退出码 {exit_code}")
87
- for err in stats.errors[:3]:
88
- error(f" {err}")
89
- if "Unexpected server error" in " ".join(stats.errors):
90
- warn("DevEco 服务端报错 — 通常为 API/网络/配额问题,与评测脚本无关。"
91
- "请检查 DevEco 登录状态、模型配置,稍后重试 `deveco run`。")
92
- elif stats.tool_calls == 0 and not stats.errors:
93
- warn("无工具调用且无错误事件 — 检查 stream_output.jsonl 是否为空。")
94
-
95
- # Human-readable text
96
- text_content = "\n".join(stats.text_lines)
97
- text_path.write_text(text_content or "(no text output)", encoding="utf-8")
98
-
99
- # Modified files
100
- r2 = subprocess.run(
101
- ["git", "diff", "--name-only"], capture_output=True, text=True, cwd=repo_root,
102
- **subprocess_no_window_kwargs(),
103
- )
104
- mod_files = len([l for l in r2.stdout.strip().split("\n") if l])
105
-
106
- hg_stream = analyze_homegraph_stream(stream_path)
107
-
108
- results = {
109
- "experiment_id": exp_id, "title": title, "agent": agent["name"],
110
- "agent_binary": agent["binary"], "session_id": session_id,
111
- "start_time_ms": start_ms, "end_time_ms": end_ms,
112
- "duration_ms": dur_ms, "duration_s": dur_s,
113
- "exit_code": exit_code, "max_turns": max_turns,
114
- "skip_permissions": skip_permissions,
115
- "tool_calls": stats.tool_calls, "tool_names": stats.tool_names,
116
- "homegraph_tool_calls": stats.homegraph_tool_calls,
117
- "used_homegraph": stats.used_homegraph,
118
- **hg_stream,
119
- "eval_arm": os.environ.get("EVAL_ARM", ""),
120
- "files_read": list(set(stats.files_read)),
121
- "files_edited": list(set(stats.files_edited)),
122
- "model": resolve_agent_model(agent, stats),
123
- "deveco_session_id": stats.deveco_session_id,
124
- "input_tokens": stats.total_input_tokens,
125
- "output_tokens": stats.total_output_tokens,
126
- "assistant_messages": stats.assistant_messages,
127
- "max_turns_hit": stats.max_turns_hit,
128
- "errors": stats.errors[:5],
129
- "modified_files": mod_files,
130
- "memory": mem.to_dict(),
131
- }
132
- write_json(result_dir / "results.json", results)
133
-
134
- if state_dir:
135
- append_run_log(state_dir, f"Exp {exp_id} finished in {dur_s}s, tools={stats.tool_calls}, exit={exit_code}")
136
- info(f"Results saved: {result_dir}/")
137
- print(f" Duration: {dur_s}s | Tool calls: {stats.tool_calls} | "
138
- f"HG tools: {stats.homegraph_tool_calls} | "
139
- f"Files read: {len(set(stats.files_read))} | Edited: {mod_files} | "
140
- f"Peak mem: {mem.peak_combined_rss_mb:.0f} MB")
141
- if stats.max_turns_hit:
142
- print(f" ⚠️ Max turns ({max_turns}) hit")
143
- return exit_code == 0
144
-
145
-
146
- if __name__ == "__main__":
147
- # Parse: python run_one.py <id> [--agent <name>] [--no-skip-permissions]
148
- args = sys.argv[1:]
149
- exp_id = args[0] if args else ""
150
- agent_name = ""
151
- skip = True
152
-
153
- i = 1
154
- while i < len(args):
155
- if args[i] == "--agent" and i + 1 < len(args):
156
- agent_name = args[i + 1]; i += 2
157
- elif args[i] == "--no-skip-permissions":
158
- skip = False; i += 1
159
- else:
160
- i += 1
161
-
162
- if not exp_id:
163
- error("Usage: python run_one.py <experiment_id> [--agent <name>]")
164
- sys.exit(1)
165
- sys.exit(0 if run_experiment(exp_id, skip, agent_name) else 1)