self-evolve-framework 1.4.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 (171) hide show
  1. package/package.json +1 -1
  2. package/template/rules/ponytail.mdc +98 -23
  3. package/template/skills/skillopt-sleep/SKILL.md +42 -0
  4. package/template/skills/skillopt-sleep/configs/_base_/default.yaml +103 -0
  5. package/template/skills/skillopt-sleep/configs/alfworld/default.yaml +29 -0
  6. package/template/skills/skillopt-sleep/configs/docvqa/default.yaml +28 -0
  7. package/template/skills/skillopt-sleep/configs/features/soft_gate.yaml +47 -0
  8. package/template/skills/skillopt-sleep/configs/livemathematicianbench/default.yaml +22 -0
  9. package/template/skills/skillopt-sleep/configs/officeqa/default.yaml +34 -0
  10. package/template/skills/skillopt-sleep/configs/searchqa/default.yaml +32 -0
  11. package/template/skills/skillopt-sleep/configs/spreadsheetbench/default.yaml +34 -0
  12. package/template/skills/skillopt-sleep/scripts/framework/skillopt/__init__.py +28 -0
  13. package/template/skills/skillopt-sleep/scripts/framework/skillopt/config.py +282 -0
  14. package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/__init__.py +7 -0
  15. package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/base.py +512 -0
  16. package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/__init__.py +9 -0
  17. package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/trainer.py +2379 -0
  18. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/__init__.py +1 -0
  19. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/README.md +43 -0
  20. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/config_template.yaml +55 -0
  21. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/env_template.py +151 -0
  22. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/loader_template.py +87 -0
  23. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/__init__.py +5 -0
  24. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/adapter.py +428 -0
  25. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/dataloader.py +123 -0
  26. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_error.md +55 -0
  27. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_success.md +33 -0
  28. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_no_history.md +8 -0
  29. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_history.md +9 -0
  30. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_memory.md +16 -0
  31. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/reflect.py +4 -0
  32. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/rollout.py +366 -0
  33. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/skills/initial.md +45 -0
  34. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/__init__.py +9 -0
  35. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_envs.py +221 -0
  36. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_projection.py +60 -0
  37. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_prompts.py +8 -0
  38. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/config_tw.yaml +145 -0
  39. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_base.py +84 -0
  40. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_manager.py +139 -0
  41. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/memory.py +87 -0
  42. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/base.py +329 -0
  43. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/__init__.py +1 -0
  44. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/adapter.py +90 -0
  45. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/dataloader.py +61 -0
  46. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/evaluator.py +113 -0
  47. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_error.md +35 -0
  48. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_success.md +24 -0
  49. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/rollout_system.md +12 -0
  50. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/rollout.py +391 -0
  51. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/skills/initial.md +11 -0
  52. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/__init__.py +1 -0
  53. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/adapter.py +129 -0
  54. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/dataloader.py +308 -0
  55. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/evaluator.py +62 -0
  56. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_error.md +37 -0
  57. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_success.md +25 -0
  58. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/rollout_system.md +12 -0
  59. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/reflect.py +4 -0
  60. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/rollout.py +434 -0
  61. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/skills/initial.md +16 -0
  62. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/__init__.py +1 -0
  63. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/adapter.py +112 -0
  64. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/dataloader.py +71 -0
  65. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/evaluator.py +46 -0
  66. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_error.md +37 -0
  67. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_success.md +25 -0
  68. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/rollout_system.md +15 -0
  69. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/rollout.py +799 -0
  70. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/skills/initial.md +15 -0
  71. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/tool_runtime.py +552 -0
  72. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/__init__.py +1 -0
  73. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/adapter.py +96 -0
  74. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/dataloader.py +42 -0
  75. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/evaluator.py +100 -0
  76. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_error.md +46 -0
  77. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_success.md +32 -0
  78. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/rollout_system.md +13 -0
  79. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/reflect.py +4 -0
  80. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/rollout.py +494 -0
  81. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/skills/initial.md +3 -0
  82. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/__init__.py +5 -0
  83. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/adapter.py +159 -0
  84. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/codegen_agent.py +731 -0
  85. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/dataloader.py +37 -0
  86. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/evaluator.py +158 -0
  87. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/executor.py +67 -0
  88. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_error.md +46 -0
  89. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_success.md +32 -0
  90. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/codegen_system.md +1 -0
  91. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/critical_rules.md +9 -0
  92. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/react_system.md +21 -0
  93. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/react_agent.py +395 -0
  94. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/reflect.py +4 -0
  95. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/rollout.py +979 -0
  96. package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/skills/initial.md +56 -0
  97. package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/__init__.py +13 -0
  98. package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/gate.py +148 -0
  99. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/__init__.py +15 -0
  100. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/aggregate.py +253 -0
  101. package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/reflect.py +635 -0
  102. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/__init__.py +514 -0
  103. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/azure_openai.py +915 -0
  104. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/backend_config.py +185 -0
  105. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/claude_backend.py +371 -0
  106. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_backend.py +666 -0
  107. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_harness.py +1057 -0
  108. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/common.py +229 -0
  109. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/minimax_backend.py +277 -0
  110. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/qwen_backend.py +456 -0
  111. package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/router.py +236 -0
  112. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/__init__.py +15 -0
  113. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/appendix.py +156 -0
  114. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/clip.py +109 -0
  115. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/lr_autonomous.py +108 -0
  116. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/meta_skill.py +79 -0
  117. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/rewrite.py +59 -0
  118. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/scheduler.py +127 -0
  119. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/select.py +4 -0
  120. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill.py +201 -0
  121. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill_aware.py +206 -0
  122. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/slow_update.py +396 -0
  123. package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/update_modes.py +135 -0
  124. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/__init__.py +63 -0
  125. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error.md +41 -0
  126. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_full_rewrite.md +32 -0
  127. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_rewrite.md +44 -0
  128. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success.md +36 -0
  129. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_full_rewrite.md +30 -0
  130. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_rewrite.md +33 -0
  131. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/lr_autonomous.md +20 -0
  132. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure.md +30 -0
  133. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_full_rewrite.md +28 -0
  134. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_rewrite.md +26 -0
  135. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final.md +33 -0
  136. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_full_rewrite.md +28 -0
  137. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_rewrite.md +25 -0
  138. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success.md +28 -0
  139. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_full_rewrite.md +28 -0
  140. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_rewrite.md +25 -0
  141. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/meta_skill.md +40 -0
  142. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking.md +20 -0
  143. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking_rewrite.md +15 -0
  144. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/rewrite_skill.md +25 -0
  145. package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/slow_update.md +60 -0
  146. package/template/skills/skillopt-sleep/scripts/framework/skillopt/scheduler/__init__.py +8 -0
  147. package/template/skills/skillopt-sleep/scripts/framework/skillopt/types.py +306 -0
  148. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/__init__.py +4 -0
  149. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/json_utils.py +172 -0
  150. package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/scoring.py +28 -0
  151. package/template/skills/ponytail/SKILL.md +0 -133
  152. package/template/skills/ponytail/scripts/hooks/claude-codex-hooks.json +0 -44
  153. package/template/skills/ponytail/scripts/hooks/copilot-hooks.json +0 -21
  154. package/template/skills/ponytail/scripts/hooks/ponytail-activate.js +0 -91
  155. package/template/skills/ponytail/scripts/hooks/ponytail-config.js +0 -122
  156. package/template/skills/ponytail/scripts/hooks/ponytail-instructions.js +0 -94
  157. package/template/skills/ponytail/scripts/hooks/ponytail-mode-tracker.js +0 -55
  158. package/template/skills/ponytail/scripts/hooks/ponytail-runtime.js +0 -68
  159. package/template/skills/ponytail/scripts/hooks/ponytail-statusline.ps1 +0 -21
  160. package/template/skills/ponytail/scripts/hooks/ponytail-statusline.sh +0 -12
  161. package/template/skills/ponytail/scripts/hooks/ponytail-subagent.js +0 -22
  162. package/template/skills/ponytail/scripts/mcp/README.md +0 -46
  163. package/template/skills/ponytail/scripts/mcp/index.js +0 -48
  164. package/template/skills/ponytail/scripts/mcp/instructions.js +0 -26
  165. package/template/skills/ponytail/scripts/mcp/package.json +0 -13
  166. package/template/skills/ponytail/scripts/mcp/test/instructions.test.js +0 -22
  167. package/template/skills/ponytail-audit/SKILL.md +0 -41
  168. package/template/skills/ponytail-debt/SKILL.md +0 -44
  169. package/template/skills/ponytail-gain/SKILL.md +0 -50
  170. package/template/skills/ponytail-help/SKILL.md +0 -69
  171. package/template/skills/ponytail-review/SKILL.md +0 -57
@@ -0,0 +1,731 @@
1
+ """Codegen agent for SpreadsheetBench — no tool-call, pure code generation.
2
+
3
+ Two modes:
4
+ - **single**: One LLM call → extract ```python``` block → done.
5
+ - **multi**: Up to max_turns LLM calls; after each, execute code and
6
+ feed errors back for correction.
7
+
8
+ This matches the official SpreadsheetBench evaluation setting (LLM generates
9
+ a Python code block, no function-calling / tool-use).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import os
15
+ import random
16
+ import signal
17
+ import time
18
+
19
+ import openpyxl
20
+
21
+
22
+ # ── Timeout helper ──────────────────────────────────────────────────────────
23
+
24
+ class TaskTimeout(Exception):
25
+ """Raised when a task exceeds its time budget."""
26
+
27
+
28
+ def _timeout_handler(signum, frame):
29
+ raise TaskTimeout("Task timed out")
30
+
31
+ from skillopt.model.azure_openai import (
32
+ get_reasoning_effort,
33
+ get_target_client,
34
+ _needs_responses_api,
35
+ tracker,
36
+ )
37
+ from skillopt.model import get_codex_exec_config, get_target_backend, is_target_exec_backend
38
+ from skillopt.model.codex_harness import prepare_workspace, render_skill_md, run_target_exec
39
+ from skillopt.prompts import load_prompt
40
+ from skillopt.envs.spreadsheetbench.executor import run_generated_code
41
+ from skillopt.envs.spreadsheetbench.evaluator import evaluate
42
+
43
+
44
+ # ── Eval feedback helper (no golden value leakage) ─────────────────────────
45
+
46
+ def _build_eval_feedback(verify_report: str) -> str:
47
+ """Build Target feedback from a verify report, hiding expected values.
48
+
49
+ The verify report contains lines like:
50
+ Sheet1!D2: got=None, expected=0 ✗
51
+ Sheet1!D10: got=None, expected=None ✓
52
+
53
+ We strip the ``expected=...`` part so the Target sees only its own
54
+ output and whether each cell is correct or wrong.
55
+ """
56
+ import re
57
+ wrong_lines = []
58
+ n_correct = 0
59
+ for raw_line in verify_report.splitlines():
60
+ raw_line = raw_line.strip()
61
+ if not raw_line:
62
+ continue
63
+ # Match enrichment lines like " Sheet1!D2: got=None, expected=0 ✗"
64
+ m = re.match(
65
+ r"(\S+!?\w+):\s*got=(.+?),\s*expected=.+?\s*(✓|✗)$",
66
+ raw_line,
67
+ )
68
+ if m:
69
+ cell, got_val, mark = m.groups()
70
+ if mark == "✗":
71
+ wrong_lines.append(f" {cell}: your output = {got_val} (WRONG)")
72
+ else:
73
+ n_correct += 1
74
+ lines = ["Your code executed successfully but produced incorrect results.",
75
+ "The following cells have wrong values:"]
76
+ lines.extend(wrong_lines)
77
+ if n_correct:
78
+ lines.append(f" ({n_correct} other cells are correct.)")
79
+ lines.append(
80
+ "\nPlease analyze the spreadsheet data more carefully and fix the code. "
81
+ "Return a complete corrected Python script inside a ```python``` block."
82
+ )
83
+ return "\n".join(lines)
84
+
85
+
86
+ # ── Workbook preview (same as official prompt.py) ────────────────────────────
87
+
88
+ def _preview_workbook(path: str, max_rows: int = 5, max_cols: int = 20) -> str:
89
+ """Generate a text preview of the first few rows of each sheet."""
90
+ wb = openpyxl.load_workbook(path, data_only=False)
91
+ chunks: list[str] = []
92
+ for sheet_name in wb.sheetnames:
93
+ ws = wb[sheet_name]
94
+ chunks.append(
95
+ f"## Sheet: {sheet_name} "
96
+ f"(dim={ws.dimensions}, max_row={ws.max_row}, max_col={ws.max_column})"
97
+ )
98
+ for row in ws.iter_rows(
99
+ min_row=1,
100
+ max_row=min(ws.max_row, max_rows),
101
+ max_col=min(ws.max_column, max_cols),
102
+ values_only=False,
103
+ ):
104
+ cells = []
105
+ for cell in row:
106
+ v = cell.value
107
+ if v is None:
108
+ cells.append(f"{cell.coordinate}=")
109
+ else:
110
+ s = str(v)
111
+ if len(s) > 40:
112
+ s = s[:37] + "..."
113
+ cells.append(f"{cell.coordinate}={s}")
114
+ chunks.append(" | ".join(cells))
115
+ if ws.max_row > max_rows:
116
+ chunks.append(f"... ({ws.max_row - max_rows} more rows)")
117
+ chunks.append("")
118
+ wb.close()
119
+ return "\n".join(chunks)
120
+
121
+
122
+ # ── Code extraction (same as official prompt.py) ────────────────────────────
123
+
124
+ def extract_code(text: str) -> str:
125
+ """Extract the first ```python``` fenced code block from LLM output."""
126
+ if "```" not in text:
127
+ return text.strip()
128
+ start = text.find("```")
129
+ nl = text.find("\n", start)
130
+ end = text.find("```", nl + 1)
131
+ if nl == -1 or end == -1:
132
+ return text.strip()
133
+ return text[nl + 1 : end].strip()
134
+
135
+
136
+ # ── Prompt construction (official SpreadsheetBench prompts) ─────────────────
137
+
138
+
139
+ def _build_system(skill_content: str) -> str:
140
+ base = load_prompt("codegen_system", env="spreadsheetbench")
141
+ if skill_content.strip():
142
+ base += f"\n\n## Skill\n{skill_content.strip()}"
143
+ return base
144
+
145
+
146
+ def _build_user(
147
+ instruction: str,
148
+ input_xlsx: str,
149
+ instruction_type: str = "",
150
+ answer_position: str = "",
151
+ diagnostic_mode: bool = False,
152
+ diagnostic_instruction: str = "",
153
+ diagnostic_trace_context: str = "",
154
+ ) -> str:
155
+ try:
156
+ preview = _preview_workbook(input_xlsx)
157
+ except Exception as e: # noqa: BLE001
158
+ preview = f"(failed to preview workbook: {e})"
159
+ extra = ""
160
+ if instruction_type:
161
+ extra += f"\nInstruction type: {instruction_type}"
162
+ if answer_position:
163
+ extra += f"\nExpected answer position: {answer_position}"
164
+ task_suffix = "Return only a ```python``` code block."
165
+ diagnostic = ""
166
+ if diagnostic_mode and diagnostic_instruction.strip():
167
+ task_suffix = (
168
+ "First provide a short diagnostic readout that follows the training "
169
+ "instruction below, then return a single complete ```python``` code block."
170
+ )
171
+ diagnostic = f"\n\n# Training readout\n{diagnostic_instruction.strip()}"
172
+ prefix = ""
173
+ if diagnostic_trace_context.strip():
174
+ prefix = (
175
+ "# Previous Codex Trace Snapshot\n"
176
+ "This is a partial transcript from an earlier attempt. Use it as your current reasoning context.\n\n"
177
+ f"{diagnostic_trace_context.strip()}\n\n"
178
+ )
179
+ return (
180
+ f"{prefix}"
181
+ f"# Instruction\n{instruction}\n{extra}\n\n"
182
+ f"# Input spreadsheet preview\n{preview}\n\n"
183
+ "# Task\n"
184
+ "Write a Python script that reads the workbook from the variable `INPUT_PATH`, "
185
+ "applies the instruction, and writes the modified workbook to `OUTPUT_PATH`. "
186
+ "Preserve all other cells unchanged. "
187
+ "The preview may be truncated — do not hardcode row counts or assume the data ends at the last previewed row; "
188
+ "iterate over all actual rows in the workbook instead. "
189
+ f"{task_suffix}"
190
+ f"{diagnostic}"
191
+ )
192
+
193
+
194
+ # ── LLM call with retry ────────────────────────────────────────────────────
195
+
196
+ def _llm_call_with_retry(call_fn, *, retries: int = 5, timeout: int | None = 120):
197
+ """Wrap an LLM API call with retry and per-call timeout."""
198
+ last_err = None
199
+ for attempt in range(retries):
200
+ try:
201
+ return call_fn(timeout=timeout)
202
+ except Exception as e: # noqa: BLE001
203
+ last_err = e
204
+ sleep = min(2 ** attempt + random.random(), 60)
205
+ time.sleep(sleep)
206
+ raise RuntimeError(f"LLM call failed after {retries} retries: {last_err}")
207
+
208
+
209
+ def _get_deployment() -> str:
210
+ from skillopt.model import azure_openai as _llm
211
+ return _llm.TARGET_DEPLOYMENT
212
+
213
+
214
+ def _build_codex_skill(skill_content: str) -> str:
215
+ return render_skill_md(
216
+ skill_content,
217
+ description="Dynamic ReflACT skill for solving the current SpreadsheetBench task.",
218
+ preamble=(
219
+ "Use this skill when solving the current SpreadsheetBench task in this workspace.\n"
220
+ "Write a single self-contained Python solution to `solution.py`.\n"
221
+ "The solution must operate on the provided `INPUT_PATH` and `OUTPUT_PATH` variables.\n"
222
+ "You may inspect `input.xlsx` and run `python run_solution.py` to validate locally,\n"
223
+ "but do not hardcode values from the preview or from one specific workbook."
224
+ ),
225
+ )
226
+
227
+
228
+ def _build_codex_task(
229
+ instruction: str,
230
+ input_xlsx: str,
231
+ instruction_type: str,
232
+ answer_position: str,
233
+ *,
234
+ diagnostic_mode: bool,
235
+ diagnostic_instruction: str,
236
+ diagnostic_trace_context: str,
237
+ ) -> str:
238
+ prompt = _build_user(
239
+ instruction,
240
+ input_xlsx,
241
+ instruction_type,
242
+ answer_position,
243
+ diagnostic_mode=diagnostic_mode,
244
+ diagnostic_instruction=diagnostic_instruction,
245
+ diagnostic_trace_context=diagnostic_trace_context,
246
+ )
247
+ return (
248
+ f"{prompt}\n\n"
249
+ "## Codex Harness Task\n"
250
+ "- Read `.agents/skills/skillopt-target/SKILL.md` before writing code; do not call a Skill tool.\n"
251
+ "- Read and optionally inspect `input.xlsx` in this workspace.\n"
252
+ "- Write the final Python solution to `solution.py`.\n"
253
+ "- The script should use the provided `INPUT_PATH` and `OUTPUT_PATH` variables.\n"
254
+ "- If you want to validate locally, run `python run_solution.py`.\n"
255
+ "- Do not return a code fence as the primary artifact; the source of truth is `solution.py`.\n"
256
+ )
257
+
258
+
259
+ def _build_codex_driver() -> str:
260
+ return (
261
+ "import pathlib\n"
262
+ "import re\n"
263
+ "import sys\n"
264
+ "import traceback\n\n"
265
+ 'INPUT_PATH = "input.xlsx"\n'
266
+ 'OUTPUT_PATH = "output.xlsx"\n'
267
+ "code = pathlib.Path('solution.py').read_text(encoding='utf-8')\n"
268
+ "code = re.sub(r'^\\s*(INPUT_PATH|OUTPUT_PATH)\\s*=\\s*.+$', '', code, flags=re.MULTILINE)\n"
269
+ "globals_dict = {'__name__': '__main__', 'INPUT_PATH': INPUT_PATH, 'OUTPUT_PATH': OUTPUT_PATH}\n"
270
+ "try:\n"
271
+ " exec(compile(code, 'solution.py', 'exec'), globals_dict, globals_dict)\n"
272
+ "except Exception:\n"
273
+ " traceback.print_exc()\n"
274
+ " sys.exit(2)\n"
275
+ )
276
+
277
+
278
+ def _prepare_codex_workspace(
279
+ *,
280
+ instruction: str,
281
+ input_xlsx: str,
282
+ output_path: str,
283
+ instruction_type: str,
284
+ answer_position: str,
285
+ skill_content: str,
286
+ diagnostic_mode: bool,
287
+ diagnostic_instruction: str,
288
+ diagnostic_trace_context: str,
289
+ workspace_name: str = "codex_single",
290
+ ) -> tuple[str, str, str, str]:
291
+ task_out_dir = os.path.dirname(output_path)
292
+ work_dir = os.path.join(task_out_dir, workspace_name)
293
+ skill_md = _build_codex_skill(skill_content)
294
+ task_md = _build_codex_task(
295
+ instruction,
296
+ input_xlsx,
297
+ instruction_type,
298
+ answer_position,
299
+ diagnostic_mode=diagnostic_mode,
300
+ diagnostic_instruction=diagnostic_instruction,
301
+ diagnostic_trace_context=diagnostic_trace_context,
302
+ )
303
+ prompt = (
304
+ "Read `.agents/skills/skillopt-target/SKILL.md` directly; do not call a Skill tool.\n"
305
+ "Read `task.md`, inspect `input.xlsx` if useful, and write the final solution to `solution.py`.\n"
306
+ "You may run `python run_solution.py` to validate the script locally.\n"
307
+ "In your final response, briefly confirm whether `solution.py` was written and summarize the approach."
308
+ )
309
+ prepare_workspace(
310
+ work_dir=work_dir,
311
+ skill_md=skill_md,
312
+ task_text=task_md,
313
+ extra_files={"run_solution.py": _build_codex_driver()},
314
+ copy_files=[(input_xlsx, "input.xlsx")],
315
+ )
316
+
317
+ return work_dir, skill_md, task_md, prompt
318
+
319
+
320
+ def _run_exec_backend(
321
+ *,
322
+ work_dir: str,
323
+ prompt: str,
324
+ model: str,
325
+ timeout: int,
326
+ ) -> tuple[str, str]:
327
+ return run_target_exec(
328
+ work_dir=work_dir,
329
+ prompt=prompt,
330
+ model=model,
331
+ timeout=timeout,
332
+ allow_file_edits=True,
333
+ )
334
+
335
+
336
+ # ── Chat (no tools) ────────────────────────────────────────────────────────
337
+
338
+ def _chat_call(
339
+ client,
340
+ deployment: str,
341
+ messages: list[dict],
342
+ max_output_tokens: int,
343
+ llm_timeout: int | None = 120,
344
+ ) -> str:
345
+ """Single LLM call, no tools. Returns raw text."""
346
+ reasoning_effort = get_reasoning_effort()
347
+ if _needs_responses_api(deployment):
348
+ # Responses API
349
+ system = ""
350
+ api_input = []
351
+ for m in messages:
352
+ if m["role"] == "system":
353
+ system = m["content"]
354
+ else:
355
+ api_input.append({"role": m["role"], "content": m["content"]})
356
+ resp = _llm_call_with_retry(lambda timeout: client.responses.create(
357
+ model=deployment,
358
+ instructions=system,
359
+ input=api_input,
360
+ max_output_tokens=max_output_tokens,
361
+ **({"reasoning": {"effort": reasoning_effort}} if reasoning_effort else {}),
362
+ timeout=timeout,
363
+ ), timeout=llm_timeout)
364
+ if hasattr(resp, "usage") and resp.usage:
365
+ tracker.record(
366
+ "rollout",
367
+ getattr(resp.usage, "input_tokens", 0) or 0,
368
+ getattr(resp.usage, "output_tokens", 0) or 0,
369
+ )
370
+ text = getattr(resp, "output_text", None) or ""
371
+ if text:
372
+ return text
373
+ for item in getattr(resp, "output", None) or []:
374
+ for part in getattr(item, "content", []):
375
+ if getattr(part, "type", "") == "output_text":
376
+ return part.text or ""
377
+ return ""
378
+ else:
379
+ # Chat Completions API — no tools
380
+ kwargs = {
381
+ "model": deployment,
382
+ "messages": messages,
383
+ "max_completion_tokens": max_output_tokens,
384
+ }
385
+ if reasoning_effort is not None:
386
+ kwargs["reasoning_effort"] = reasoning_effort
387
+ resp = _llm_call_with_retry(lambda timeout: client.chat.completions.create(
388
+ **kwargs,
389
+ timeout=timeout,
390
+ ), timeout=llm_timeout)
391
+ if resp.usage:
392
+ tracker.record(
393
+ "rollout",
394
+ resp.usage.prompt_tokens or 0,
395
+ resp.usage.completion_tokens or 0,
396
+ )
397
+ return resp.choices[0].message.content or ""
398
+
399
+
400
+ # ── Public API ──────────────────────────────────────────────────────────────
401
+
402
+ def run_single(
403
+ instruction: str,
404
+ input_xlsx: str,
405
+ output_path: str,
406
+ instruction_type: str = "",
407
+ answer_position: str = "",
408
+ skill_content: str = "",
409
+ max_output_tokens: int = 16384,
410
+ llm_timeout: int | None = 120,
411
+ task_timeout: int | None = 300,
412
+ diagnostic_mode: bool = False,
413
+ diagnostic_instruction: str = "",
414
+ diagnostic_trace_context: str = "",
415
+ ) -> dict:
416
+ """Single-round code generation. One LLM call, no tools.
417
+
418
+ Args:
419
+ llm_timeout: Per-LLM-call timeout in seconds (default 120).
420
+ task_timeout: Total task timeout in seconds (default 300).
421
+
422
+ Returns ``{"code": str, "raw": str, "n_turns": 1}``.
423
+ """
424
+ no_task_timeout = task_timeout is None or task_timeout <= 0
425
+ if is_target_exec_backend():
426
+ deadline = None if no_task_timeout else time.time() + task_timeout
427
+ deployment = _get_deployment()
428
+ work_dir, skill_md, task_md, prompt = _prepare_codex_workspace(
429
+ instruction=instruction,
430
+ input_xlsx=input_xlsx,
431
+ output_path=output_path,
432
+ instruction_type=instruction_type,
433
+ answer_position=answer_position,
434
+ skill_content=skill_content,
435
+ diagnostic_mode=diagnostic_mode,
436
+ diagnostic_instruction=diagnostic_instruction,
437
+ diagnostic_trace_context=diagnostic_trace_context,
438
+ )
439
+ if deadline is None:
440
+ effective_timeout = 10**9
441
+ else:
442
+ remaining = max(10, int(deadline - time.time()))
443
+ effective_timeout = min(task_timeout, remaining)
444
+ final_message, raw = _run_exec_backend(
445
+ work_dir=work_dir,
446
+ prompt=prompt,
447
+ model=deployment,
448
+ timeout=effective_timeout,
449
+ )
450
+ solution_path = os.path.join(work_dir, "solution.py")
451
+ if os.path.exists(solution_path):
452
+ with open(solution_path, encoding="utf-8") as f:
453
+ code = f.read()
454
+ else:
455
+ code = extract_code(final_message or raw)
456
+ return {
457
+ "code": code,
458
+ "raw": raw or final_message,
459
+ "n_turns": 1,
460
+ "conversation": [{"role": "assistant", "content": final_message or raw}],
461
+ "target_system_prompt": skill_md,
462
+ "target_user_prompt": f"{prompt}\n\n## Task File\n\n{task_md}",
463
+ }
464
+
465
+ deadline = None if no_task_timeout else time.time() + task_timeout
466
+ client = get_target_client()
467
+ deployment = _get_deployment()
468
+ system = _build_system(skill_content)
469
+ user = _build_user(
470
+ instruction,
471
+ input_xlsx,
472
+ instruction_type,
473
+ answer_position,
474
+ diagnostic_mode=diagnostic_mode,
475
+ diagnostic_instruction=diagnostic_instruction,
476
+ diagnostic_trace_context=diagnostic_trace_context,
477
+ )
478
+
479
+ messages = [
480
+ {"role": "system", "content": system},
481
+ {"role": "user", "content": user},
482
+ ]
483
+
484
+ if deadline is None:
485
+ effective_timeout = None
486
+ else:
487
+ remaining = max(10, int(deadline - time.time()))
488
+ effective_timeout = min(llm_timeout or remaining, remaining)
489
+ raw = _chat_call(client, deployment, messages, max_output_tokens, llm_timeout=effective_timeout)
490
+ time.sleep(3) # Rate-limit cooldown after successful LLM call
491
+ code = extract_code(raw)
492
+
493
+ return {
494
+ "code": code,
495
+ "raw": raw,
496
+ "n_turns": 1,
497
+ "conversation": [{"role": "assistant", "content": raw}],
498
+ "target_system_prompt": system,
499
+ "target_user_prompt": user,
500
+ }
501
+
502
+
503
+ def run_multi(
504
+ instruction: str,
505
+ input_xlsx: str,
506
+ output_path: str,
507
+ instruction_type: str = "",
508
+ answer_position: str = "",
509
+ skill_content: str = "",
510
+ max_turns: int = 5,
511
+ max_output_tokens: int = 16384,
512
+ llm_timeout: int | None = 120,
513
+ task_timeout: int | None = 600,
514
+ gold_path: str = "",
515
+ diagnostic_mode: bool = False,
516
+ diagnostic_instruction: str = "",
517
+ diagnostic_trace_context: str = "",
518
+ ) -> dict:
519
+ """Multi-round code generation with execution feedback. No tools.
520
+
521
+ Each round: LLM generates code → execute → if error, feed back and retry.
522
+
523
+ Args:
524
+ llm_timeout: Per-LLM-call timeout in seconds (default 120).
525
+ task_timeout: Total task timeout in seconds (default 600).
526
+ gold_path: Path to golden answer xlsx for eval feedback during
527
+ training. When non-empty, a successful execution is followed
528
+ by an eval check; if the output is wrong the agent receives
529
+ cell-level feedback (without revealing expected values) and
530
+ gets another turn. Leave empty for eval/test to avoid
531
+ data leakage.
532
+
533
+ Returns ``{"code": str, "raw": str, "n_turns": int, "conversation": [...]}``.
534
+ """
535
+ no_task_timeout = task_timeout is None or task_timeout <= 0
536
+ if is_target_exec_backend():
537
+ deadline = None if no_task_timeout else time.time() + task_timeout
538
+ deployment = _get_deployment()
539
+ work_dir, skill_md, task_md, initial_prompt = _prepare_codex_workspace(
540
+ instruction=instruction,
541
+ input_xlsx=input_xlsx,
542
+ output_path=output_path,
543
+ instruction_type=instruction_type,
544
+ answer_position=answer_position,
545
+ skill_content=skill_content,
546
+ diagnostic_mode=diagnostic_mode,
547
+ diagnostic_instruction=diagnostic_instruction,
548
+ diagnostic_trace_context=diagnostic_trace_context,
549
+ workspace_name="codex_multi",
550
+ )
551
+ prompt = (
552
+ f"{initial_prompt}\n\n"
553
+ "## Multi-Turn Repair Mode\n"
554
+ "- This is turn 1. Write or overwrite `solution.py`.\n"
555
+ "- After each turn, the harness will execute your `solution.py`; if it fails, you will receive feedback and may revise it.\n"
556
+ "- Keep the script general: use `INPUT_PATH` and `OUTPUT_PATH`, and do not hardcode one workbook's values."
557
+ )
558
+ conversation: list[dict] = []
559
+ code = ""
560
+ raw = ""
561
+ final_message = ""
562
+ solution_path = os.path.join(work_dir, "solution.py")
563
+
564
+ for turn in range(max_turns):
565
+ if deadline is None:
566
+ effective_timeout = 10**9
567
+ else:
568
+ remaining = deadline - time.time()
569
+ if remaining <= 10:
570
+ break
571
+ effective_timeout = max(10, int(remaining))
572
+ final_message, raw = _run_exec_backend(
573
+ work_dir=work_dir,
574
+ prompt=prompt,
575
+ model=deployment,
576
+ timeout=effective_timeout,
577
+ )
578
+ conversation.append({"role": "assistant", "content": final_message or raw})
579
+
580
+ if os.path.exists(solution_path):
581
+ with open(solution_path, encoding="utf-8") as f:
582
+ code = f.read()
583
+ else:
584
+ code = extract_code(final_message or raw)
585
+ if code.strip():
586
+ with open(solution_path, "w", encoding="utf-8") as f:
587
+ f.write(code)
588
+
589
+ if not code.strip():
590
+ feedback = (
591
+ "No usable `solution.py` or Python code block was produced. "
592
+ "Write a complete `solution.py` that reads `INPUT_PATH` and saves `OUTPUT_PATH`."
593
+ )
594
+ else:
595
+ ok, err = run_generated_code(
596
+ code,
597
+ input_xlsx,
598
+ output_path,
599
+ timeout=None if no_task_timeout else 120,
600
+ )
601
+ if ok:
602
+ if gold_path and answer_position:
603
+ from skillopt.envs.spreadsheetbench.rollout import _auto_verify_output
604
+ eval_result = evaluate(
605
+ output_path, gold_path, instruction_type, answer_position,
606
+ )
607
+ if eval_result["ok"]:
608
+ break
609
+ verify = _auto_verify_output(output_path, gold_path, answer_position)
610
+ feedback = _build_eval_feedback(verify)
611
+ else:
612
+ break
613
+ else:
614
+ feedback = (
615
+ "The current `solution.py` raised an error during harness execution:\n\n"
616
+ f"```\n{err[:3000]}\n```\n\n"
617
+ "Revise `solution.py` to fix the error. Keep using `INPUT_PATH` and `OUTPUT_PATH`."
618
+ )
619
+
620
+ feedback_path = os.path.join(work_dir, f"feedback_turn_{turn + 1:02d}.md")
621
+ with open(feedback_path, "w", encoding="utf-8") as f:
622
+ f.write(feedback)
623
+ conversation.append({"role": "user", "content": feedback})
624
+ prompt = (
625
+ f"The previous `solution.py` was evaluated and needs another revision.\n"
626
+ f"Read `{os.path.basename(feedback_path)}` and update `solution.py` accordingly.\n"
627
+ "You may run `python run_solution.py` for a local syntax/runtime check, but the harness will run the final code separately.\n"
628
+ "Do not hardcode workbook-specific answers; preserve unrelated cells."
629
+ )
630
+
631
+ return {
632
+ "code": code,
633
+ "raw": raw or final_message,
634
+ "n_turns": len([m for m in conversation if m["role"] == "assistant"]),
635
+ "conversation": conversation,
636
+ "target_system_prompt": skill_md,
637
+ "target_user_prompt": f"{initial_prompt}\n\n## Task File\n\n{task_md}",
638
+ }
639
+
640
+ deadline = None if no_task_timeout else time.time() + task_timeout
641
+ client = get_target_client()
642
+ deployment = _get_deployment()
643
+ system = _build_system(skill_content)
644
+ user = _build_user(
645
+ instruction,
646
+ input_xlsx,
647
+ instruction_type,
648
+ answer_position,
649
+ diagnostic_mode=diagnostic_mode,
650
+ diagnostic_instruction=diagnostic_instruction,
651
+ diagnostic_trace_context=diagnostic_trace_context,
652
+ )
653
+
654
+ messages: list[dict] = [
655
+ {"role": "system", "content": system},
656
+ {"role": "user", "content": user},
657
+ ]
658
+ conversation: list[dict] = []
659
+ code = ""
660
+ raw = ""
661
+
662
+ for turn in range(max_turns):
663
+ if deadline is None:
664
+ effective_timeout = None
665
+ else:
666
+ remaining = deadline - time.time()
667
+ if remaining <= 10:
668
+ # Not enough time for another round
669
+ break
670
+ effective_timeout = min(llm_timeout or int(remaining), int(remaining))
671
+ raw = _chat_call(client, deployment, messages, max_output_tokens, llm_timeout=effective_timeout)
672
+ time.sleep(3) # Rate-limit cooldown after successful LLM call
673
+ code = extract_code(raw)
674
+ conversation.append({"role": "assistant", "content": raw})
675
+ messages.append({"role": "assistant", "content": raw})
676
+
677
+ if not code.strip():
678
+ # No code extracted — ask again
679
+ feedback = (
680
+ "No Python code block was found in your response. "
681
+ "Please return a complete Python script inside a ```python``` block."
682
+ )
683
+ messages.append({"role": "user", "content": feedback})
684
+ conversation.append({"role": "user", "content": feedback})
685
+ continue
686
+
687
+ # Execute the code
688
+ ok, err = run_generated_code(
689
+ code,
690
+ input_xlsx,
691
+ output_path,
692
+ timeout=None if no_task_timeout else 120,
693
+ )
694
+ if ok:
695
+ # Execution succeeded — check correctness if gold_path available
696
+ if gold_path and answer_position:
697
+ from skillopt.envs.spreadsheetbench.rollout import _auto_verify_output
698
+ eval_result = evaluate(
699
+ output_path, gold_path, instruction_type, answer_position,
700
+ )
701
+ if eval_result["ok"]:
702
+ break # Genuinely correct — stop
703
+
704
+ # Output is wrong — build feedback without leaking golden values
705
+ verify = _auto_verify_output(output_path, gold_path, answer_position)
706
+ feedback = _build_eval_feedback(verify)
707
+ messages.append({"role": "user", "content": feedback})
708
+ conversation.append({"role": "user", "content": feedback})
709
+ continue
710
+ else:
711
+ # No gold path (eval/test) — accept execution success
712
+ break
713
+
714
+ # Execution failed — feed error back
715
+ feedback = (
716
+ f"The code raised an error during execution:\n\n"
717
+ f"```\n{err[:3000]}\n```\n\n"
718
+ f"Please fix the code and return a complete corrected Python script "
719
+ f"inside a ```python``` block."
720
+ )
721
+ messages.append({"role": "user", "content": feedback})
722
+ conversation.append({"role": "user", "content": feedback})
723
+
724
+ return {
725
+ "code": code,
726
+ "raw": raw,
727
+ "n_turns": turn + 1,
728
+ "conversation": conversation,
729
+ "target_system_prompt": system,
730
+ "target_user_prompt": user,
731
+ }