self-evolve-framework 1.5.0 → 1.6.0

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 (149) hide show
  1. package/package.json +1 -1
  2. package/template/skills/skillopt-sleep/SKILL.md +42 -0
  3. package/template/skills/skillopt-sleep/configs/_base_/default.yaml +103 -0
  4. package/template/skills/skillopt-sleep/configs/alfworld/default.yaml +29 -0
  5. package/template/skills/skillopt-sleep/configs/docvqa/default.yaml +28 -0
  6. package/template/skills/skillopt-sleep/configs/features/soft_gate.yaml +47 -0
  7. package/template/skills/skillopt-sleep/configs/livemathematicianbench/default.yaml +22 -0
  8. package/template/skills/skillopt-sleep/configs/officeqa/default.yaml +34 -0
  9. package/template/skills/skillopt-sleep/configs/searchqa/default.yaml +32 -0
  10. package/template/skills/skillopt-sleep/configs/spreadsheetbench/default.yaml +34 -0
  11. package/template/skills/skillopt-sleep/scripts/framework/skillopt/__init__.py +28 -0
  12. package/template/skills/skillopt-sleep/scripts/framework/skillopt/config.py +282 -0
  13. package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/__init__.py +7 -0
  14. package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/base.py +512 -0
  15. package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/__init__.py +9 -0
  16. package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/trainer.py +2379 -0
  17. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/__init__.py +1 -0
  18. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/README.md +43 -0
  19. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/config_template.yaml +55 -0
  20. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/env_template.py +151 -0
  21. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/loader_template.py +87 -0
  22. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/__init__.py +5 -0
  23. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/adapter.py +428 -0
  24. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/dataloader.py +123 -0
  25. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_error.md +55 -0
  26. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_success.md +33 -0
  27. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_no_history.md +8 -0
  28. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_history.md +9 -0
  29. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_memory.md +16 -0
  30. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/reflect.py +4 -0
  31. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/rollout.py +366 -0
  32. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/skills/initial.md +45 -0
  33. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/__init__.py +9 -0
  34. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_envs.py +221 -0
  35. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_projection.py +60 -0
  36. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_prompts.py +8 -0
  37. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/config_tw.yaml +145 -0
  38. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_base.py +84 -0
  39. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_manager.py +139 -0
  40. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/memory.py +87 -0
  41. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/base.py +329 -0
  42. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/__init__.py +1 -0
  43. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/adapter.py +90 -0
  44. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/dataloader.py +61 -0
  45. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/evaluator.py +113 -0
  46. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_error.md +35 -0
  47. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_success.md +24 -0
  48. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/rollout_system.md +12 -0
  49. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/rollout.py +391 -0
  50. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/skills/initial.md +11 -0
  51. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/__init__.py +1 -0
  52. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/adapter.py +129 -0
  53. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/dataloader.py +308 -0
  54. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/evaluator.py +62 -0
  55. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_error.md +37 -0
  56. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_success.md +25 -0
  57. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/rollout_system.md +12 -0
  58. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/reflect.py +4 -0
  59. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/rollout.py +434 -0
  60. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/skills/initial.md +16 -0
  61. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/__init__.py +1 -0
  62. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/adapter.py +112 -0
  63. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/dataloader.py +71 -0
  64. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/evaluator.py +46 -0
  65. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_error.md +37 -0
  66. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_success.md +25 -0
  67. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/rollout_system.md +15 -0
  68. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/rollout.py +799 -0
  69. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/skills/initial.md +15 -0
  70. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/tool_runtime.py +552 -0
  71. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/__init__.py +1 -0
  72. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/adapter.py +96 -0
  73. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/dataloader.py +42 -0
  74. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/evaluator.py +100 -0
  75. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_error.md +46 -0
  76. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_success.md +32 -0
  77. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/rollout_system.md +13 -0
  78. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/reflect.py +4 -0
  79. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/rollout.py +494 -0
  80. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/skills/initial.md +3 -0
  81. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/__init__.py +5 -0
  82. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/adapter.py +159 -0
  83. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/codegen_agent.py +731 -0
  84. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/dataloader.py +37 -0
  85. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/evaluator.py +158 -0
  86. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/executor.py +67 -0
  87. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_error.md +46 -0
  88. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_success.md +32 -0
  89. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/codegen_system.md +1 -0
  90. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/critical_rules.md +9 -0
  91. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/react_system.md +21 -0
  92. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/react_agent.py +395 -0
  93. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/reflect.py +4 -0
  94. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/rollout.py +979 -0
  95. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/skills/initial.md +56 -0
  96. package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/__init__.py +13 -0
  97. package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/gate.py +148 -0
  98. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/__init__.py +15 -0
  99. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/aggregate.py +253 -0
  100. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/reflect.py +635 -0
  101. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/__init__.py +514 -0
  102. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/azure_openai.py +915 -0
  103. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/backend_config.py +185 -0
  104. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/claude_backend.py +371 -0
  105. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_backend.py +666 -0
  106. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_harness.py +1057 -0
  107. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/common.py +229 -0
  108. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/minimax_backend.py +277 -0
  109. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/qwen_backend.py +456 -0
  110. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/router.py +236 -0
  111. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/__init__.py +15 -0
  112. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/appendix.py +156 -0
  113. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/clip.py +109 -0
  114. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/lr_autonomous.py +108 -0
  115. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/meta_skill.py +79 -0
  116. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/rewrite.py +59 -0
  117. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/scheduler.py +127 -0
  118. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/select.py +4 -0
  119. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill.py +201 -0
  120. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill_aware.py +206 -0
  121. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/slow_update.py +396 -0
  122. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/update_modes.py +135 -0
  123. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/__init__.py +63 -0
  124. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error.md +41 -0
  125. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_full_rewrite.md +32 -0
  126. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_rewrite.md +44 -0
  127. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success.md +36 -0
  128. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_full_rewrite.md +30 -0
  129. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_rewrite.md +33 -0
  130. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/lr_autonomous.md +20 -0
  131. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure.md +30 -0
  132. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_full_rewrite.md +28 -0
  133. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_rewrite.md +26 -0
  134. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final.md +33 -0
  135. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_full_rewrite.md +28 -0
  136. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_rewrite.md +25 -0
  137. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success.md +28 -0
  138. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_full_rewrite.md +28 -0
  139. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_rewrite.md +25 -0
  140. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/meta_skill.md +40 -0
  141. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking.md +20 -0
  142. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking_rewrite.md +15 -0
  143. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/rewrite_skill.md +25 -0
  144. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/slow_update.md +60 -0
  145. package/template/skills/skillopt-sleep/scripts/framework/skillopt/scheduler/__init__.py +8 -0
  146. package/template/skills/skillopt-sleep/scripts/framework/skillopt/types.py +306 -0
  147. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/__init__.py +4 -0
  148. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/json_utils.py +172 -0
  149. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/scoring.py +28 -0
@@ -0,0 +1,395 @@
1
+ """ReAct agent with bash tool for SpreadsheetBench evaluation.
2
+
3
+ Adapted from the original SpreadsheetBench react agent implementation.
4
+
5
+ Uses the unified ``skillopt.model`` router so SpreadsheetBench follows the same
6
+ backend selection as the rest of the framework.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import subprocess
13
+
14
+ from skillopt.model import chat_target_messages
15
+ from skillopt.prompts import load_prompt
16
+
17
+ # ── Tool schemas ─────────────────────────────────────────────────────────────
18
+
19
+ BASH_TOOL_CHAT = {
20
+ "type": "function",
21
+ "function": {
22
+ "name": "bash",
23
+ "description": (
24
+ "Execute a bash command and receive stdout+stderr (truncated to 4000 chars). "
25
+ "Use Python to read / write Excel files."
26
+ ),
27
+ "parameters": {
28
+ "type": "object",
29
+ "properties": {
30
+ "cmd": {"type": "string", "description": "Bash command to execute."}
31
+ },
32
+ "required": ["cmd"],
33
+ },
34
+ },
35
+ }
36
+
37
+ BASH_TOOL_RESPONSES = {
38
+ "type": "function",
39
+ "name": "bash",
40
+ "description": (
41
+ "Execute a bash command and receive stdout+stderr (truncated to 4000 chars). "
42
+ "Use Python to read / write Excel files."
43
+ ),
44
+ "parameters": {
45
+ "type": "object",
46
+ "properties": {
47
+ "cmd": {"type": "string", "description": "Bash command to execute."}
48
+ },
49
+ "required": ["cmd"],
50
+ },
51
+ }
52
+
53
+ WRITE_FILE_TOOL_CHAT = {
54
+ "type": "function",
55
+ "function": {
56
+ "name": "write_file",
57
+ "description": (
58
+ "Write content to a file. Use this instead of echo/cat for multi-line "
59
+ "Python scripts to avoid shell escaping issues."
60
+ ),
61
+ "parameters": {
62
+ "type": "object",
63
+ "properties": {
64
+ "path": {
65
+ "type": "string",
66
+ "description": "File path to write (relative to working directory).",
67
+ },
68
+ "content": {
69
+ "type": "string",
70
+ "description": "File content to write.",
71
+ },
72
+ },
73
+ "required": ["path", "content"],
74
+ },
75
+ },
76
+ }
77
+
78
+ WRITE_FILE_TOOL_RESPONSES = {
79
+ "type": "function",
80
+ "name": "write_file",
81
+ "description": (
82
+ "Write content to a file. Use this instead of echo/cat for multi-line "
83
+ "Python scripts to avoid shell escaping issues."
84
+ ),
85
+ "parameters": {
86
+ "type": "object",
87
+ "properties": {
88
+ "path": {
89
+ "type": "string",
90
+ "description": "File path to write (relative to working directory).",
91
+ },
92
+ "content": {
93
+ "type": "string",
94
+ "description": "File content to write.",
95
+ },
96
+ },
97
+ "required": ["path", "content"],
98
+ },
99
+ }
100
+
101
+ # ── System prompt ─────────────────────────────────────────────────────────────
102
+
103
+
104
+ def _build_system(skill_content: str) -> str:
105
+ if skill_content.strip():
106
+ skill_section = f"## Skill\n{skill_content.strip()}\n\n"
107
+ else:
108
+ skill_section = ""
109
+ return load_prompt("react_system", env="spreadsheetbench").format(
110
+ critical_rules=load_prompt("critical_rules", env="spreadsheetbench"),
111
+ skill_section=skill_section,
112
+ )
113
+
114
+
115
+ def _build_user(
116
+ instruction: str,
117
+ input_path: str,
118
+ output_path: str,
119
+ instruction_type: str,
120
+ answer_position: str,
121
+ diagnostic_mode: bool = False,
122
+ diagnostic_instruction: str = "",
123
+ diagnostic_trace_context: str = "",
124
+ ) -> str:
125
+ parts = []
126
+ if diagnostic_trace_context.strip():
127
+ parts.append(
128
+ "# Previous Codex Trace Snapshot\n"
129
+ "This is a partial transcript from an earlier attempt. Use it as your current reasoning context.\n\n"
130
+ f"{diagnostic_trace_context.strip()}"
131
+ )
132
+ parts.extend([
133
+ f"# Instruction\n{instruction}",
134
+ f"# Input file\n{input_path}",
135
+ f"# Output file\n{output_path}",
136
+ ])
137
+ if instruction_type:
138
+ parts.append(f"# Instruction type\n{instruction_type}")
139
+ if answer_position:
140
+ parts.append(f"# Answer position\n{answer_position}")
141
+ if diagnostic_mode and diagnostic_instruction.strip():
142
+ parts.append(f"# Training readout\n{diagnostic_instruction.strip()}")
143
+ parts.append(
144
+ "Manipulate the input spreadsheet according to the instruction "
145
+ "and save the result to the output file."
146
+ )
147
+ return "\n\n".join(parts)
148
+
149
+
150
+ # ── File write (bypass shell escaping) ────────────────────────────────────────
151
+
152
+ def _write_file(path: str, content: str, work_dir: str) -> str:
153
+ """Write content to a file, bypassing shell escaping issues."""
154
+ try:
155
+ full_path = os.path.join(work_dir, path) if not os.path.isabs(path) else path
156
+ parent = os.path.dirname(full_path)
157
+ if parent:
158
+ os.makedirs(parent, exist_ok=True)
159
+ with open(full_path, "w") as f:
160
+ f.write(content)
161
+ return f"File written: {full_path} ({len(content)} chars)"
162
+ except Exception as e: # noqa: BLE001
163
+ return f"[write_file error: {e}]"
164
+
165
+
166
+ # ── Auto-verification ─────────────────────────────────────────────────────────
167
+
168
+ def _auto_verify(work_dir: str) -> str:
169
+ """Auto-verify output xlsx after solution.py runs."""
170
+ import glob as _glob
171
+
172
+ sol_path = os.path.join(work_dir, "solution.py")
173
+ output_path = None
174
+ if os.path.exists(sol_path):
175
+ with open(sol_path) as f:
176
+ for line in f:
177
+ stripped = line.strip()
178
+ if stripped.startswith("OUTPUT_PATH"):
179
+ try:
180
+ val = stripped.split("=", 1)[1].strip()
181
+ output_path = val.strip("'\"").strip()
182
+ except Exception: # noqa: BLE001
183
+ pass
184
+ break
185
+
186
+ if not output_path or not os.path.exists(output_path):
187
+ xlsx_files = [
188
+ f for f in _glob.glob(os.path.join(work_dir, "*.xlsx"))
189
+ if "_pred" in os.path.basename(f)
190
+ ]
191
+ if xlsx_files:
192
+ output_path = xlsx_files[0]
193
+
194
+ if not output_path or not os.path.exists(output_path):
195
+ return (
196
+ "\n\n[AUTO-VERIFY] WARNING: Output file not found! "
197
+ "Make sure OUTPUT_PATH is correct and wb.save(OUTPUT_PATH) is called."
198
+ )
199
+
200
+ try:
201
+ import openpyxl
202
+
203
+ wb_formula = openpyxl.load_workbook(output_path, data_only=False)
204
+ wb_value = openpyxl.load_workbook(output_path, data_only=True)
205
+ lines = [f"\n\n[AUTO-VERIFY] Output file exists: {output_path}"]
206
+
207
+ sn = wb_formula.sheetnames[0]
208
+ ws_f = wb_formula[sn]
209
+ ws_v = wb_value[sn]
210
+ lines.append(f" Sheet '{sn}': {ws_f.dimensions}")
211
+
212
+ for row in ws_v.iter_rows(
213
+ min_row=1, max_row=min(5, ws_v.max_row), values_only=True,
214
+ ):
215
+ lines.append(f" {list(row)}")
216
+
217
+ none_cells: list[str] = []
218
+ for row_f, row_v in zip(
219
+ ws_f.iter_rows(min_row=1, max_row=min(30, ws_f.max_row)),
220
+ ws_v.iter_rows(min_row=1, max_row=min(30, ws_v.max_row)),
221
+ ):
222
+ for cf, cv in zip(row_f, row_v):
223
+ formula_val = cf.value
224
+ cached_val = cv.value
225
+ if (
226
+ isinstance(formula_val, str)
227
+ and formula_val.startswith("=")
228
+ and cached_val is None
229
+ ):
230
+ none_cells.append(cf.coordinate)
231
+
232
+ if none_cells:
233
+ lines.append(
234
+ f" WARNING: {len(none_cells)} cells have formulas but NO cached "
235
+ f"value -- evaluator will see None: {none_cells[:10]}"
236
+ )
237
+ lines.append(
238
+ " FIX: Compute values in Python and write literal "
239
+ "numbers/strings instead of formulas."
240
+ )
241
+ else:
242
+ lines.append(" All cells have concrete values. Looks good.")
243
+
244
+ wb_formula.close()
245
+ wb_value.close()
246
+ return "\n".join(lines)
247
+ except Exception as e: # noqa: BLE001
248
+ return f"\n\n[AUTO-VERIFY] Could not inspect output: {e}"
249
+
250
+
251
+ # ── Bash execution ────────────────────────────────────────────────────────────
252
+
253
+ def _run_bash(cmd: str, work_dir: str, timeout: int = 60) -> str:
254
+ try:
255
+ proc = subprocess.run(
256
+ cmd,
257
+ shell=True,
258
+ capture_output=True,
259
+ text=True,
260
+ timeout=timeout,
261
+ cwd=work_dir,
262
+ )
263
+ out = (proc.stdout + proc.stderr).strip()
264
+ except subprocess.TimeoutExpired:
265
+ return f"[timeout after {timeout}s]"
266
+ except Exception as e: # noqa: BLE001
267
+ return f"[error: {e}]"
268
+ if len(out) > 4000:
269
+ out = out[:3800] + f"\n...[truncated, {len(out)} total chars]"
270
+ result = out or "(no output)"
271
+
272
+ if "solution.py" in cmd and "python" in cmd.lower():
273
+ result += _auto_verify(work_dir)
274
+
275
+ return result
276
+
277
+
278
+ def _assistant_tool_calls(message) -> list[dict]:
279
+ tool_calls = getattr(message, "tool_calls", None) or []
280
+ return [
281
+ tool_call.model_dump() if hasattr(tool_call, "model_dump") else dict(tool_call)
282
+ for tool_call in tool_calls
283
+ ]
284
+
285
+
286
+ def _react_loop(
287
+ system: str,
288
+ user: str,
289
+ work_dir: str,
290
+ max_turns: int,
291
+ max_output_tokens: int,
292
+ ) -> dict:
293
+ messages: list[dict] = [
294
+ {"role": "system", "content": system},
295
+ {"role": "user", "content": user},
296
+ ]
297
+ conversation: list[dict] = []
298
+ n_turns = 0
299
+
300
+ for _ in range(max_turns):
301
+ message, _ = chat_target_messages(
302
+ messages=messages,
303
+ tools=[BASH_TOOL_CHAT, WRITE_FILE_TOOL_CHAT],
304
+ tool_choice="auto",
305
+ max_completion_tokens=max_output_tokens,
306
+ retries=5,
307
+ stage="rollout",
308
+ return_message=True,
309
+ )
310
+
311
+ assistant_text = str(getattr(message, "content", "") or "")
312
+ tool_calls = _assistant_tool_calls(message)
313
+ assistant_payload: dict = {"role": "assistant", "content": assistant_text}
314
+ if tool_calls:
315
+ assistant_payload["tool_calls"] = tool_calls
316
+ messages.append(assistant_payload)
317
+
318
+ if not tool_calls:
319
+ conversation.append({"type": "message", "content": assistant_text})
320
+ break
321
+
322
+ for tool_call in tool_calls:
323
+ n_turns += 1
324
+ function = tool_call.get("function", {}) or {}
325
+ try:
326
+ args = json.loads(str(function.get("arguments", "{}") or "{}"))
327
+ except json.JSONDecodeError:
328
+ args = {}
329
+
330
+ if function.get("name") == "write_file":
331
+ obs = _write_file(
332
+ args.get("path", ""),
333
+ args.get("content", ""),
334
+ work_dir,
335
+ )
336
+ conversation.append({
337
+ "type": "tool_call",
338
+ "cmd": f"[write_file] {args.get('path', '')}",
339
+ "obs": obs,
340
+ })
341
+ else:
342
+ cmd = args.get("cmd", "")
343
+ obs = _run_bash(cmd, work_dir)
344
+ conversation.append({"type": "tool_call", "cmd": cmd, "obs": obs})
345
+
346
+ messages.append(
347
+ {
348
+ "role": "tool",
349
+ "tool_call_id": tool_call.get("id", ""),
350
+ "content": obs,
351
+ }
352
+ )
353
+
354
+ return {"conversation": conversation, "n_turns": n_turns}
355
+
356
+
357
+ # ── Public API ────────────────────────────────────────────────────────────────
358
+
359
+ def run_react(
360
+ instruction: str,
361
+ input_path: str,
362
+ output_path: str,
363
+ work_dir: str,
364
+ instruction_type: str = "",
365
+ answer_position: str = "",
366
+ skill_content: str = "",
367
+ max_turns: int = 30,
368
+ max_output_tokens: int = 16384,
369
+ diagnostic_mode: bool = False,
370
+ diagnostic_instruction: str = "",
371
+ diagnostic_trace_context: str = "",
372
+ ) -> dict:
373
+ """Run the ReAct agent for one task.
374
+
375
+ Returns:
376
+ {
377
+ "conversation": [...], # list of {type, cmd/content, obs?}
378
+ "n_turns": int, # number of bash tool calls made
379
+ }
380
+ """
381
+ system = _build_system(skill_content)
382
+ user = _build_user(
383
+ instruction,
384
+ input_path,
385
+ output_path,
386
+ instruction_type,
387
+ answer_position,
388
+ diagnostic_mode=diagnostic_mode,
389
+ diagnostic_instruction=diagnostic_instruction,
390
+ diagnostic_trace_context=diagnostic_trace_context,
391
+ )
392
+ result = _react_loop(system, user, work_dir, max_turns, max_output_tokens)
393
+ result["target_system_prompt"] = system
394
+ result["target_user_prompt"] = user
395
+ return result
@@ -0,0 +1,4 @@
1
+ """SpreadsheetBench Reflect stage.
2
+
3
+ Prompts are now loaded from .md files by the base adapter.
4
+ """