orizu 0.5.19 → 0.5.21

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 (378) hide show
  1. package/README.md +23 -0
  2. package/dist/archive-artifacts-cli.d.ts +50 -0
  3. package/dist/archive-artifacts-cli.d.ts.map +1 -0
  4. package/dist/artifact-archive.d.ts +48 -0
  5. package/dist/artifact-archive.d.ts.map +1 -0
  6. package/dist/artifact-pull.d.ts +22 -0
  7. package/dist/artifact-pull.d.ts.map +1 -0
  8. package/dist/banner.d.ts +3 -0
  9. package/dist/banner.d.ts.map +1 -0
  10. package/dist/connectors-cli.d.ts +31 -0
  11. package/dist/connectors-cli.d.ts.map +1 -0
  12. package/dist/credentials.d.ts +46 -0
  13. package/dist/credentials.d.ts.map +1 -0
  14. package/dist/csv.d.ts +49 -0
  15. package/dist/csv.d.ts.map +1 -0
  16. package/dist/dataset-download.d.ts +6 -0
  17. package/dist/dataset-download.d.ts.map +1 -0
  18. package/dist/dataset-edit-rows.d.ts +10 -0
  19. package/dist/dataset-edit-rows.d.ts.map +1 -0
  20. package/dist/dataset-upload-snapshot.d.ts +13 -0
  21. package/dist/dataset-upload-snapshot.d.ts.map +1 -0
  22. package/dist/daytona-slice-rehearsal.d.ts +89 -0
  23. package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
  24. package/dist/daytona-slice.d.ts +106 -0
  25. package/dist/daytona-slice.d.ts.map +1 -0
  26. package/dist/deploy-key-merge-core.d.ts +121 -0
  27. package/dist/deploy-key-merge-core.d.ts.map +1 -0
  28. package/dist/diff-comments-cli.d.ts +12 -0
  29. package/dist/diff-comments-cli.d.ts.map +1 -0
  30. package/dist/diff-comments-cli.js +21 -2
  31. package/dist/egress-allowlist-cli.d.ts +23 -0
  32. package/dist/egress-allowlist-cli.d.ts.map +1 -0
  33. package/dist/egress-policy.d.ts +108 -0
  34. package/dist/egress-policy.d.ts.map +1 -0
  35. package/dist/error-response.d.ts +9 -0
  36. package/dist/error-response.d.ts.map +1 -0
  37. package/dist/file-parser.d.ts +5 -0
  38. package/dist/file-parser.d.ts.map +1 -0
  39. package/dist/gepa-engine-dispatch.d.ts +9 -0
  40. package/dist/gepa-engine-dispatch.d.ts.map +1 -0
  41. package/dist/gepa-engine-dispatch.js +307 -0
  42. package/dist/gepa-python-command.d.ts +6 -0
  43. package/dist/gepa-python-command.d.ts.map +1 -0
  44. package/dist/gepa-python-command.js +26 -0
  45. package/dist/gepa-python-paths.d.ts +6 -0
  46. package/dist/gepa-python-paths.d.ts.map +1 -0
  47. package/dist/gepa-python-paths.js +42 -0
  48. package/dist/git-credential.d.ts +69 -0
  49. package/dist/git-credential.d.ts.map +1 -0
  50. package/dist/git-recovery-environment.d.ts +31 -0
  51. package/dist/git-recovery-environment.d.ts.map +1 -0
  52. package/dist/github-setup.d.ts +122 -0
  53. package/dist/github-setup.d.ts.map +1 -0
  54. package/dist/global-flags.d.ts +12 -0
  55. package/dist/global-flags.d.ts.map +1 -0
  56. package/dist/help.d.ts +46 -0
  57. package/dist/help.d.ts.map +1 -0
  58. package/dist/help.js +93 -25
  59. package/dist/hosted-boot.d.ts +273 -0
  60. package/dist/hosted-boot.d.ts.map +1 -0
  61. package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
  62. package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
  63. package/dist/hosted-bootstrap.d.ts +188 -0
  64. package/dist/hosted-bootstrap.d.ts.map +1 -0
  65. package/dist/hosted-broker-server.d.ts +50 -0
  66. package/dist/hosted-broker-server.d.ts.map +1 -0
  67. package/dist/hosted-git-http-server.d.ts +36 -0
  68. package/dist/hosted-git-http-server.d.ts.map +1 -0
  69. package/dist/hosted-harness-claude.d.ts +195 -0
  70. package/dist/hosted-harness-claude.d.ts.map +1 -0
  71. package/dist/hosted-harness-opencode.d.ts +227 -0
  72. package/dist/hosted-harness-opencode.d.ts.map +1 -0
  73. package/dist/hosted-harness.d.ts +118 -0
  74. package/dist/hosted-harness.d.ts.map +1 -0
  75. package/dist/hosted-harvest.d.ts +68 -0
  76. package/dist/hosted-harvest.d.ts.map +1 -0
  77. package/dist/hosted-headless.d.ts +72 -0
  78. package/dist/hosted-headless.d.ts.map +1 -0
  79. package/dist/hosted-loop-lifecycle.d.ts +206 -0
  80. package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
  81. package/dist/hosted-loop.d.ts +170 -0
  82. package/dist/hosted-loop.d.ts.map +1 -0
  83. package/dist/hosted-run-event-sink.d.ts +222 -0
  84. package/dist/hosted-run-event-sink.d.ts.map +1 -0
  85. package/dist/hosted-runtime-assets.d.ts +186 -0
  86. package/dist/hosted-runtime-assets.d.ts.map +1 -0
  87. package/dist/hosted-session-cli.d.ts +258 -0
  88. package/dist/hosted-session-cli.d.ts.map +1 -0
  89. package/dist/hosted-skill-staging.d.ts +73 -0
  90. package/dist/hosted-skill-staging.d.ts.map +1 -0
  91. package/dist/http.d.ts +8 -0
  92. package/dist/http.d.ts.map +1 -0
  93. package/dist/index.d.ts +22 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +119 -129
  96. package/dist/instruction-set-gepa-launch.d.ts +3 -0
  97. package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
  98. package/dist/instruction-set-gepa-launch.js +15 -0
  99. package/dist/instruction-set-loader.d.ts +6 -0
  100. package/dist/instruction-set-loader.d.ts.map +1 -0
  101. package/dist/instruction-set-loader.js +59 -0
  102. package/dist/instruction-set-manifest.d.ts +14 -0
  103. package/dist/instruction-set-manifest.d.ts.map +1 -0
  104. package/dist/instruction-set-manifest.js +162 -0
  105. package/dist/instruction-sets-cli.d.ts +40 -0
  106. package/dist/instruction-sets-cli.d.ts.map +1 -0
  107. package/dist/instruction-sets-cli.js +361 -0
  108. package/dist/json-response.d.ts +4 -0
  109. package/dist/json-response.d.ts.map +1 -0
  110. package/dist/jsonl-stream.d.ts +8 -0
  111. package/dist/jsonl-stream.d.ts.map +1 -0
  112. package/dist/kill-switch-cli.d.ts +30 -0
  113. package/dist/kill-switch-cli.d.ts.map +1 -0
  114. package/dist/loopback-serve.d.ts +25 -0
  115. package/dist/loopback-serve.d.ts.map +1 -0
  116. package/dist/manifests-cli.d.ts +22 -0
  117. package/dist/manifests-cli.d.ts.map +1 -0
  118. package/dist/markdown-report-input.d.ts +7 -0
  119. package/dist/markdown-report-input.d.ts.map +1 -0
  120. package/dist/markdown-report-input.js +15 -1
  121. package/dist/merge-job.d.ts +155 -0
  122. package/dist/merge-job.d.ts.map +1 -0
  123. package/dist/model-configs-cli.d.ts +19 -0
  124. package/dist/model-configs-cli.d.ts.map +1 -0
  125. package/dist/model-configs-cli.js +104 -0
  126. package/dist/optimization-export-cli.d.ts +19 -0
  127. package/dist/optimization-export-cli.d.ts.map +1 -0
  128. package/dist/optimizations-list-cli.d.ts +20 -0
  129. package/dist/optimizations-list-cli.d.ts.map +1 -0
  130. package/dist/optimizations-promote-cli.d.ts +8 -0
  131. package/dist/optimizations-promote-cli.d.ts.map +1 -0
  132. package/dist/optimizations-promote-cli.js +34 -0
  133. package/dist/option-validation.d.ts +15 -0
  134. package/dist/option-validation.d.ts.map +1 -0
  135. package/dist/preview-runtime.d.ts +63 -0
  136. package/dist/preview-runtime.d.ts.map +1 -0
  137. package/dist/prompt-draft-push.d.ts +60 -0
  138. package/dist/prompt-draft-push.d.ts.map +1 -0
  139. package/dist/prompt-label-cli.d.ts +11 -0
  140. package/dist/prompt-label-cli.d.ts.map +1 -0
  141. package/dist/prompt-label-cli.js +29 -0
  142. package/dist/prompt-length-wire.d.ts +28 -0
  143. package/dist/prompt-length-wire.d.ts.map +1 -0
  144. package/dist/prompt-length-wire.js +2 -2
  145. package/dist/prompt-report-cli.d.ts +10 -0
  146. package/dist/prompt-report-cli.d.ts.map +1 -0
  147. package/dist/prompt-report-cli.js +2 -2
  148. package/dist/prompt-summary-table.d.ts +15 -0
  149. package/dist/prompt-summary-table.d.ts.map +1 -0
  150. package/dist/prompt-summary-table.js +6 -0
  151. package/dist/registration-tag-warning.d.ts +32 -0
  152. package/dist/registration-tag-warning.d.ts.map +1 -0
  153. package/dist/report-comments-cli.d.ts +16 -0
  154. package/dist/report-comments-cli.d.ts.map +1 -0
  155. package/dist/report-markdown-blankness.d.ts +2 -0
  156. package/dist/report-markdown-blankness.d.ts.map +1 -0
  157. package/dist/report-markdown-blankness.js +6 -0
  158. package/dist/report-replacement-warning.d.ts +17 -0
  159. package/dist/report-replacement-warning.d.ts.map +1 -0
  160. package/dist/report-replacement-warning.js +31 -0
  161. package/dist/repository-backup-core.d.ts +225 -0
  162. package/dist/repository-backup-core.d.ts.map +1 -0
  163. package/dist/repository-recovery-manifest.d.ts +83 -0
  164. package/dist/repository-recovery-manifest.d.ts.map +1 -0
  165. package/dist/run-event-digest.d.ts +74 -0
  166. package/dist/run-event-digest.d.ts.map +1 -0
  167. package/dist/runner-dir-verify.d.ts +66 -0
  168. package/dist/runner-dir-verify.d.ts.map +1 -0
  169. package/dist/runner-instruction-set-contract.d.ts +24 -0
  170. package/dist/runner-instruction-set-contract.d.ts.map +1 -0
  171. package/dist/runner-instruction-set-contract.js +50 -0
  172. package/dist/sandbox-hygiene.d.ts +45 -0
  173. package/dist/sandbox-hygiene.d.ts.map +1 -0
  174. package/dist/sandbox-provider.d.ts +193 -0
  175. package/dist/sandbox-provider.d.ts.map +1 -0
  176. package/dist/scorer-draft-push.d.ts +50 -0
  177. package/dist/scorer-draft-push.d.ts.map +1 -0
  178. package/dist/scorers-cli.d.ts +20 -0
  179. package/dist/scorers-cli.d.ts.map +1 -0
  180. package/dist/scorers-cli.js +22 -0
  181. package/dist/scorers-verify-parity-bridge.d.ts +16 -0
  182. package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
  183. package/dist/scorers-verify-parity-bridge.js +90 -0
  184. package/dist/scorers-verify-parity.d.ts +81 -0
  185. package/dist/scorers-verify-parity.d.ts.map +1 -0
  186. package/dist/scorers-verify-parity.js +502 -0
  187. package/dist/scores-accept-cli.d.ts +21 -0
  188. package/dist/scores-accept-cli.d.ts.map +1 -0
  189. package/dist/secret-redaction.d.ts +47 -0
  190. package/dist/secret-redaction.d.ts.map +1 -0
  191. package/dist/session-timeline.d.ts +54 -0
  192. package/dist/session-timeline.d.ts.map +1 -0
  193. package/dist/setup-prompt.d.ts +5 -0
  194. package/dist/setup-prompt.d.ts.map +1 -0
  195. package/dist/skill-installer.d.ts +68 -0
  196. package/dist/skill-installer.d.ts.map +1 -0
  197. package/dist/skilled-proposer-launch.d.ts +7 -0
  198. package/dist/skilled-proposer-launch.d.ts.map +1 -0
  199. package/dist/skilled-proposer-launch.js +60 -0
  200. package/dist/smoke.d.ts +102 -0
  201. package/dist/smoke.d.ts.map +1 -0
  202. package/dist/start-intent.d.ts +12 -0
  203. package/dist/start-intent.d.ts.map +1 -0
  204. package/dist/task-assignment-manifest.d.ts +15 -0
  205. package/dist/task-assignment-manifest.d.ts.map +1 -0
  206. package/dist/task-create-error.d.ts +18 -0
  207. package/dist/task-create-error.d.ts.map +1 -0
  208. package/dist/team-connectors-cli.d.ts +29 -0
  209. package/dist/team-connectors-cli.d.ts.map +1 -0
  210. package/dist/types.d.ts +39 -0
  211. package/dist/types.d.ts.map +1 -0
  212. package/dist/vercel-sandbox-provider.d.ts +156 -0
  213. package/dist/vercel-sandbox-provider.d.ts.map +1 -0
  214. package/dist/workbench-cli.d.ts +233 -0
  215. package/dist/workbench-cli.d.ts.map +1 -0
  216. package/dist/workspace-sync.d.ts +207 -0
  217. package/dist/workspace-sync.d.ts.map +1 -0
  218. package/dist/workspace.d.ts +92 -0
  219. package/dist/workspace.d.ts.map +1 -0
  220. package/dist/zip-draft-push.d.ts +55 -0
  221. package/dist/zip-draft-push.d.ts.map +1 -0
  222. package/package.json +15 -2
  223. package/requirements/README.md +138 -0
  224. package/requirements/skilled-proposer.in +66 -0
  225. package/requirements/skilled-proposer.lock +265 -0
  226. package/scripts/build-cli-dist.mjs +30 -0
  227. package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
  228. package/scripts/orizu-source-checkout.d.mts +1 -0
  229. package/scripts/orizu-source-checkout.mjs +10 -0
  230. package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
  231. package/scripts/vendor-gepa-python.mjs +187 -4
  232. package/src/diff-comments-cli.ts +41 -3
  233. package/src/gepa-engine-dispatch.ts +340 -0
  234. package/src/gepa-python-command.ts +31 -0
  235. package/src/gepa-python-paths.ts +56 -0
  236. package/src/help.ts +95 -25
  237. package/src/index.ts +131 -166
  238. package/src/instruction-set-gepa-launch.ts +28 -0
  239. package/src/instruction-set-loader.ts +37 -0
  240. package/src/instruction-set-manifest.ts +120 -0
  241. package/src/instruction-sets-cli.ts +306 -0
  242. package/src/markdown-report-input.ts +19 -1
  243. package/src/model-configs-cli.ts +119 -0
  244. package/src/optimizations-promote-cli.ts +41 -0
  245. package/src/prompt-label-cli.ts +55 -0
  246. package/src/prompt-length-wire.ts +1 -1
  247. package/src/prompt-report-cli.ts +2 -2
  248. package/src/prompt-summary-table.ts +12 -0
  249. package/src/report-markdown-blankness.ts +6 -0
  250. package/src/report-replacement-warning.ts +35 -0
  251. package/src/runner-instruction-set-contract.ts +64 -0
  252. package/src/scorers-cli.ts +40 -0
  253. package/src/scorers-verify-parity-bridge.ts +90 -0
  254. package/src/scorers-verify-parity.ts +564 -0
  255. package/src/skilled-proposer-launch.ts +89 -0
  256. package/src/skilled-proposer-venv-manager.mjs +757 -0
  257. package/tsconfig.json +2 -0
  258. package/vendor/gepa-python/LICENSE +7 -0
  259. package/vendor/gepa-python/manifest.json +107 -0
  260. package/vendor/gepa-python/src/gepa/__init__.py +22 -0
  261. package/vendor/gepa-python/src/gepa/adapters/__init__.py +0 -0
  262. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/__init__.py +1 -0
  263. package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
  264. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/__init__.py +26 -0
  265. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/confidence_adapter.py +618 -0
  266. package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/scoring.py +206 -0
  267. package/vendor/gepa-python/src/gepa/adapters/default_adapter/__init__.py +0 -0
  268. package/vendor/gepa-python/src/gepa/adapters/default_adapter/default_adapter.py +206 -0
  269. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/__init__.py +0 -0
  270. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/dspy_adapter.py +525 -0
  271. package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/instruction_proposal.py +500 -0
  272. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/__init__.py +6 -0
  273. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
  274. package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/full_program_adapter.py +269 -0
  275. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/__init__.py +56 -0
  276. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
  277. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
  278. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
  279. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
  280. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
  281. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
  282. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
  283. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
  284. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
  285. package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
  286. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/__init__.py +11 -0
  287. package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/langchain_adapter.py +261 -0
  288. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/__init__.py +37 -0
  289. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_adapter.py +693 -0
  290. package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_client.py +364 -0
  291. package/vendor/gepa-python/src/gepa/adapters/optimize_anything_adapter/optimize_anything_adapter.py +879 -0
  292. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/__init__.py +0 -0
  293. package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
  294. package/vendor/gepa-python/src/gepa/api.py +468 -0
  295. package/vendor/gepa-python/src/gepa/core/__init__.py +0 -0
  296. package/vendor/gepa-python/src/gepa/core/adapter.py +227 -0
  297. package/vendor/gepa-python/src/gepa/core/callbacks.py +551 -0
  298. package/vendor/gepa-python/src/gepa/core/data_loader.py +74 -0
  299. package/vendor/gepa-python/src/gepa/core/engine.py +1025 -0
  300. package/vendor/gepa-python/src/gepa/core/result.py +271 -0
  301. package/vendor/gepa-python/src/gepa/core/state.py +706 -0
  302. package/vendor/gepa-python/src/gepa/examples/__init__.py +0 -0
  303. package/vendor/gepa-python/src/gepa/examples/aime.py +24 -0
  304. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/eval_default.py +111 -0
  305. package/vendor/gepa-python/src/gepa/examples/anymaths-bench/train_anymaths.py +177 -0
  306. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/__init__.py +4 -0
  307. package/vendor/gepa-python/src/gepa/examples/mcp_adapter/mcp_optimization_example.py +456 -0
  308. package/vendor/gepa-python/src/gepa/examples/rag_adapter/__init__.py +9 -0
  309. package/vendor/gepa-python/src/gepa/examples/rag_adapter/rag_optimization.py +824 -0
  310. package/vendor/gepa-python/src/gepa/examples/terminal-bench/train_terminus.py +161 -0
  311. package/vendor/gepa-python/src/gepa/gepa_utils.py +117 -0
  312. package/vendor/gepa-python/src/gepa/gskill/__init__.py +1 -0
  313. package/vendor/gepa-python/src/gepa/gskill/gskill/__init__.py +0 -0
  314. package/vendor/gepa-python/src/gepa/gskill/gskill/cost_tracker.py +194 -0
  315. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/__init__.py +0 -0
  316. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code.py +1117 -0
  317. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code_skills.py +613 -0
  318. package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/mini_swe_agent.py +440 -0
  319. package/vendor/gepa-python/src/gepa/gskill/gskill/experiment_logger.py +367 -0
  320. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_fitness_fn.py +209 -0
  321. package/vendor/gepa-python/src/gepa/gskill/gskill/swe_harness.py +352 -0
  322. package/vendor/gepa-python/src/gepa/gskill/gskill/train_optimize_anything.py +830 -0
  323. package/vendor/gepa-python/src/gepa/image.py +104 -0
  324. package/vendor/gepa-python/src/gepa/lm.py +261 -0
  325. package/vendor/gepa-python/src/gepa/logging/__init__.py +0 -0
  326. package/vendor/gepa-python/src/gepa/logging/experiment_tracker.py +457 -0
  327. package/vendor/gepa-python/src/gepa/logging/logger.py +75 -0
  328. package/vendor/gepa-python/src/gepa/logging/utils.py +130 -0
  329. package/vendor/gepa-python/src/gepa/optimize_anything.py +1701 -0
  330. package/vendor/gepa-python/src/gepa/proposer/__init__.py +0 -0
  331. package/vendor/gepa-python/src/gepa/proposer/base.py +53 -0
  332. package/vendor/gepa-python/src/gepa/proposer/merge.py +403 -0
  333. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/__init__.py +0 -0
  334. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/base.py +64 -0
  335. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflection_lm.py +178 -0
  336. package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflective_mutation.py +641 -0
  337. package/vendor/gepa-python/src/gepa/py.typed +0 -0
  338. package/vendor/gepa-python/src/gepa/strategies/__init__.py +0 -0
  339. package/vendor/gepa-python/src/gepa/strategies/acceptance.py +66 -0
  340. package/vendor/gepa-python/src/gepa/strategies/batch_sampler.py +103 -0
  341. package/vendor/gepa-python/src/gepa/strategies/candidate_selector.py +82 -0
  342. package/vendor/gepa-python/src/gepa/strategies/component_selector.py +36 -0
  343. package/vendor/gepa-python/src/gepa/strategies/eval_policy.py +64 -0
  344. package/vendor/gepa-python/src/gepa/strategies/instruction_proposal.py +153 -0
  345. package/vendor/gepa-python/src/gepa/strategies/proposal_sampling.py +128 -0
  346. package/vendor/gepa-python/src/gepa/strategies/proposal_selection.py +84 -0
  347. package/vendor/gepa-python/src/gepa/utils/__init__.py +63 -0
  348. package/vendor/gepa-python/src/gepa/utils/code_execution.py +626 -0
  349. package/vendor/gepa-python/src/gepa/utils/stdio_capture.py +145 -0
  350. package/vendor/gepa-python/src/gepa/utils/stop_condition.py +230 -0
  351. package/vendor/gepa-python/src/gepa/visualization.py +359 -0
  352. package/vendor/orizu-gepa/pyproject.toml +18 -0
  353. package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +9 -0
  354. package/vendor/orizu-gepa/src/orizu_gepa_connector/__main__.py +5 -0
  355. package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +138 -0
  356. package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +666 -0
  357. package/vendor/orizu-gepa/src/orizu_gepa_connector/cli.py +7 -0
  358. package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +269 -0
  359. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
  360. package/vendor/orizu-gepa/src/orizu_gepa_connector/preflight.py +64 -0
  361. package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +186 -0
  362. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +721 -0
  363. package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
  364. package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +55 -0
  365. package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +203 -0
  366. package/vendor/orizu-gepa-python/pyproject.toml +1 -1
  367. package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
  368. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
  369. package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +52 -0
  370. package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
  371. package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
  372. package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
  373. package/vendor/skills/orizu-cli/SKILL.md +3 -1
  374. package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
  375. package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
  376. package/vendor/skills/orizu-cli/references/optimization-reports.md +1 -1
  377. package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +26 -1
  378. package/vendor/skills/orizu-cli/references/prompt-control-plane.md +113 -10
@@ -0,0 +1,693 @@
1
+ # Copyright (c) 2025 Lakshya A Agrawal and the GEPA contributors
2
+ # https://github.com/gepa-ai/gepa
3
+
4
+ """
5
+ MCP Adapter for GEPA - Optimizes tool descriptions and system prompts.
6
+
7
+ Supports local (stdio) and remote (SSE/StreamableHTTP) MCP servers.
8
+ Enables optimization of tool descriptions, system prompts, and tool selection
9
+ across single or multiple tools.
10
+ """
11
+
12
+ import asyncio
13
+ import json
14
+ import logging
15
+ from typing import Any, Callable, TypedDict, cast
16
+
17
+ from gepa.core.adapter import EvaluationBatch, GEPAAdapter
18
+
19
+ try:
20
+ from mcp import StdioServerParameters # type: ignore[import-untyped]
21
+ except ImportError as e:
22
+ raise ImportError("MCP Python SDK is required. Install it with: pip install mcp") from e
23
+
24
+ from .mcp_client import create_mcp_client
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ # ============================================================================
30
+ # Type Definitions
31
+ # ============================================================================
32
+
33
+
34
+ class MCPDataInst(TypedDict):
35
+ """
36
+ Dataset item for MCP tool optimization.
37
+
38
+ Attributes:
39
+ user_query: The user's question or request
40
+ tool_arguments: Expected tool arguments (for validation/guidance)
41
+ reference_answer: Optional reference answer for scoring
42
+ additional_context: Optional additional context
43
+ """
44
+
45
+ user_query: str
46
+ tool_arguments: dict[str, Any]
47
+ reference_answer: str | None
48
+ additional_context: dict[str, str]
49
+
50
+
51
+ class MCPTrajectory(TypedDict):
52
+ """
53
+ Execution trace for MCP tool invocation.
54
+
55
+ Captures the full workflow including tool selection, execution,
56
+ and model behavior at each stage.
57
+ """
58
+
59
+ user_query: str
60
+ tool_names: list[str]
61
+ selected_tool: str | None
62
+ tool_called: bool
63
+ tool_arguments: dict[str, Any] | None
64
+ tool_response: str | None
65
+ tool_description_used: str
66
+ system_prompt_used: str
67
+ model_first_pass_output: str
68
+ model_final_output: str
69
+ score: float
70
+
71
+
72
+ class MCPOutput(TypedDict):
73
+ """
74
+ Output from MCP evaluation.
75
+
76
+ Attributes:
77
+ final_answer: The final answer from the model
78
+ tool_called: Whether a tool was called
79
+ selected_tool: Which tool was selected (if any)
80
+ tool_response: The tool's response (if called)
81
+ """
82
+
83
+ final_answer: str
84
+ tool_called: bool
85
+ selected_tool: str | None
86
+ tool_response: str | None
87
+
88
+
89
+ # ============================================================================
90
+ # MCP Adapter
91
+ # ============================================================================
92
+
93
+
94
+ class MCPAdapter(GEPAAdapter[MCPDataInst, MCPTrajectory, MCPOutput]):
95
+ """
96
+ GEPA adapter for optimizing MCP tool usage.
97
+
98
+ This adapter enables optimization of:
99
+ - Tool descriptions (single or multiple tools)
100
+ - System prompts for tool usage guidance
101
+ - Tool selection logic
102
+
103
+ Features:
104
+ - Multi-tool support: Optimize multiple tools simultaneously
105
+ - Two-pass workflow: Tool call + answer generation
106
+ - Multiple transports: stdio (local), SSE, StreamableHTTP (remote)
107
+ - Reflective datasets: Generate training data for refinement
108
+
109
+ Example (Local):
110
+ >>> from mcp import StdioServerParameters
111
+ >>> adapter = MCPAdapter(
112
+ ... tool_names=["read_file", "write_file"],
113
+ ... task_model="gpt-4o-mini",
114
+ ... metric_fn=lambda item, output: 1.0 if item["reference_answer"] in output else 0.0,
115
+ ... server_params=StdioServerParameters(
116
+ ... command="python",
117
+ ... args=["server.py"],
118
+ ... ),
119
+ ... )
120
+
121
+ Example (Remote):
122
+ >>> adapter = MCPAdapter(
123
+ ... tool_names="search_web",
124
+ ... task_model="gpt-4o-mini",
125
+ ... metric_fn=accuracy_metric,
126
+ ... remote_url="https://mcp-server.com/sse",
127
+ ... remote_transport="sse",
128
+ ... )
129
+ """
130
+
131
+ def __init__(
132
+ self,
133
+ tool_names: str | list[str],
134
+ task_model: str | Callable,
135
+ metric_fn: Callable[[MCPDataInst, str], float],
136
+ # Local server configuration
137
+ server_params: StdioServerParameters | None = None,
138
+ # Remote server configuration
139
+ remote_url: str | None = None,
140
+ remote_transport: str = "sse",
141
+ remote_headers: dict[str, str] | None = None,
142
+ remote_timeout: float = 30,
143
+ # Adapter configuration
144
+ base_system_prompt: str = "You are a helpful assistant with access to tools.",
145
+ enable_two_pass: bool = True,
146
+ failure_score: float = 0.0,
147
+ ):
148
+ """
149
+ Initialize MCPAdapter.
150
+
151
+ Args:
152
+ tool_names: Name(s) of tool(s) to optimize (str or list[str])
153
+ task_model: Model for task execution (litellm string or callable)
154
+ metric_fn: Scoring function: (data_inst, output) -> float
155
+ server_params: Local MCP server configuration (stdio)
156
+ remote_url: Remote MCP server URL
157
+ remote_transport: "sse" or "streamable_http"
158
+ remote_headers: HTTP headers for remote (e.g., auth tokens)
159
+ remote_timeout: Timeout for remote HTTP operations
160
+ base_system_prompt: Base system prompt template
161
+ enable_two_pass: Use two-pass workflow (tool + answer)
162
+ failure_score: Score assigned when execution fails
163
+ """
164
+ # Store transport configuration
165
+ self.server_params = server_params
166
+ self.remote_url = remote_url
167
+ self.remote_transport = remote_transport
168
+ self.remote_headers = remote_headers or {}
169
+ self.remote_timeout = remote_timeout
170
+
171
+ # Normalize tool_names to list
172
+ self.tool_names = [tool_names] if isinstance(tool_names, str) else tool_names
173
+
174
+ # Store adapter configuration
175
+ self.base_system_prompt = base_system_prompt
176
+ self.enable_two_pass = enable_two_pass
177
+ self.failure_score = failure_score
178
+ self.metric_fn = metric_fn
179
+
180
+ # Setup model
181
+ if isinstance(task_model, str):
182
+ from gepa.lm import LM
183
+
184
+ self._lm: LM | None = LM(task_model)
185
+ else:
186
+ self._lm = None
187
+ self.task_model = task_model
188
+
189
+ def evaluate(
190
+ self,
191
+ batch: list[MCPDataInst],
192
+ candidate: dict[str, str],
193
+ capture_traces: bool = False,
194
+ ) -> EvaluationBatch[MCPTrajectory, MCPOutput]:
195
+ """
196
+ Evaluate candidate on batch using MCP tools.
197
+
198
+ Args:
199
+ batch: Dataset items to evaluate
200
+ candidate: Component mapping (e.g., {"tool_description": "..."})
201
+ capture_traces: Whether to capture detailed trajectories
202
+
203
+ Returns:
204
+ EvaluationBatch with outputs, scores, and optional trajectories
205
+ """
206
+ return asyncio.run(self._evaluate_async(batch, candidate, capture_traces))
207
+
208
+ async def _evaluate_async(
209
+ self,
210
+ batch: list[MCPDataInst],
211
+ candidate: dict[str, str],
212
+ capture_traces: bool,
213
+ ) -> EvaluationBatch[MCPTrajectory, MCPOutput]:
214
+ """Async implementation of evaluation."""
215
+ outputs: list[MCPOutput] = []
216
+ scores: list[float] = []
217
+ trajectories: list[MCPTrajectory] | None = [] if capture_traces else None
218
+
219
+ client = None
220
+ try:
221
+ # Create MCP client using factory
222
+ logger.info(f"Starting MCP session for batch of {len(batch)} items...")
223
+ client = create_mcp_client(
224
+ server_params=self.server_params,
225
+ remote_url=self.remote_url,
226
+ remote_transport=self.remote_transport,
227
+ remote_headers=self.remote_headers,
228
+ remote_timeout=self.remote_timeout,
229
+ )
230
+
231
+ await client.start()
232
+ init_result = await client.initialize()
233
+ logger.info(f"MCP session initialized: {init_result.get('serverInfo', {}).get('name', 'unknown')}")
234
+
235
+ # Get available tools
236
+ tools_list = await client.list_tools()
237
+ available_tools = [t for t in tools_list if t.get("name") in self.tool_names]
238
+
239
+ if not available_tools:
240
+ available_names = [t.get("name") for t in tools_list]
241
+ raise ValueError(f"Tools {self.tool_names} not found. Available: {available_names}")
242
+
243
+ # Build system prompt with tools
244
+ system_prompt = self._build_system_prompt(candidate, available_tools)
245
+
246
+ # Evaluate each item
247
+ for idx, item in enumerate(batch):
248
+ try:
249
+ logger.info(f"Evaluating item {idx + 1}/{len(batch)}: {item['user_query'][:50]}...")
250
+
251
+ # First pass: Model calls tool
252
+ first_pass = await self._first_pass(client, item, system_prompt, available_tools)
253
+ logger.info(f"First pass complete for item {idx + 1}")
254
+
255
+ # Second pass: Model uses tool response (if enabled)
256
+ if self.enable_two_pass and first_pass["tool_called"]:
257
+ final_output = await self._second_pass(client, item, system_prompt, first_pass["tool_response"])
258
+ else:
259
+ final_output = first_pass["output"]
260
+
261
+ # Score the output
262
+ score = self.metric_fn(item, final_output)
263
+
264
+ # Collect results
265
+ outputs.append(
266
+ {
267
+ "final_answer": final_output,
268
+ "tool_called": first_pass["tool_called"],
269
+ "selected_tool": first_pass["selected_tool"],
270
+ "tool_response": first_pass["tool_response"],
271
+ }
272
+ )
273
+ scores.append(score)
274
+
275
+ # Capture trajectory
276
+ if capture_traces and trajectories is not None:
277
+ trajectories.append(
278
+ {
279
+ "user_query": item["user_query"],
280
+ "tool_names": self.tool_names,
281
+ "selected_tool": first_pass["selected_tool"],
282
+ "tool_called": first_pass["tool_called"],
283
+ "tool_arguments": first_pass["tool_arguments"],
284
+ "tool_response": first_pass["tool_response"],
285
+ "tool_description_used": candidate.get("tool_description", ""),
286
+ "system_prompt_used": system_prompt,
287
+ "model_first_pass_output": first_pass["output"],
288
+ "model_final_output": final_output,
289
+ "score": score,
290
+ }
291
+ )
292
+
293
+ except Exception as e:
294
+ logger.exception(f"Failed to evaluate item: {item['user_query']}")
295
+ outputs.append(
296
+ {
297
+ "final_answer": "",
298
+ "tool_called": False,
299
+ "selected_tool": None,
300
+ "tool_response": None,
301
+ }
302
+ )
303
+ scores.append(self.failure_score)
304
+
305
+ if capture_traces and trajectories is not None:
306
+ trajectories.append(
307
+ {
308
+ "user_query": item["user_query"],
309
+ "tool_names": self.tool_names,
310
+ "selected_tool": None,
311
+ "tool_called": False,
312
+ "tool_arguments": None,
313
+ "tool_response": None,
314
+ "tool_description_used": candidate.get("tool_description", ""),
315
+ "system_prompt_used": system_prompt,
316
+ "model_first_pass_output": f"ERROR: {e!s}",
317
+ "model_final_output": "",
318
+ "score": self.failure_score,
319
+ }
320
+ )
321
+
322
+ except Exception as e:
323
+ logger.exception("Failed to create MCP session")
324
+ # Return failure for entire batch
325
+ for item in batch:
326
+ outputs.append(
327
+ {
328
+ "final_answer": "",
329
+ "tool_called": False,
330
+ "selected_tool": None,
331
+ "tool_response": None,
332
+ }
333
+ )
334
+ scores.append(self.failure_score)
335
+ if capture_traces and trajectories is not None:
336
+ trajectories.append(
337
+ {
338
+ "user_query": item["user_query"],
339
+ "tool_names": self.tool_names,
340
+ "selected_tool": None,
341
+ "tool_called": False,
342
+ "tool_arguments": None,
343
+ "tool_response": None,
344
+ "tool_description_used": "",
345
+ "system_prompt_used": "",
346
+ "model_first_pass_output": f"SESSION ERROR: {e!s}",
347
+ "model_final_output": "",
348
+ "score": self.failure_score,
349
+ }
350
+ )
351
+ finally:
352
+ if client:
353
+ await client.close()
354
+
355
+ return EvaluationBatch(outputs=outputs, scores=scores, trajectories=trajectories)
356
+
357
+ async def _first_pass(
358
+ self,
359
+ client,
360
+ item: MCPDataInst,
361
+ system_prompt: str,
362
+ available_tools: list[dict[str, Any]],
363
+ ) -> dict[str, Any]:
364
+ """
365
+ First pass: Model receives query and calls tool if needed.
366
+
367
+ Returns dict with:
368
+ - output: Raw model output
369
+ - tool_called: Whether tool was called
370
+ - selected_tool: Which tool was selected
371
+ - tool_arguments: Tool arguments
372
+ - tool_response: Tool response
373
+ """
374
+ messages = [
375
+ {"role": "system", "content": system_prompt},
376
+ {"role": "user", "content": item["user_query"]},
377
+ ]
378
+
379
+ try:
380
+ if self._lm is not None:
381
+ logger.debug(f"Calling model with messages: {messages}")
382
+ model_output = self._lm(messages).strip()
383
+ logger.debug(f"Model output: '{model_output}'")
384
+ else:
385
+ model_output = cast(Callable, self.task_model)(messages)
386
+
387
+ # Parse tool call (JSON format)
388
+ tool_called = False
389
+ selected_tool = None
390
+ tool_arguments = None
391
+ tool_response = None
392
+
393
+ try:
394
+ parsed = json.loads(model_output)
395
+ if parsed.get("action") == "call_tool":
396
+ tool_called = True
397
+ selected_tool = parsed.get("tool")
398
+ tool_arguments = parsed.get("arguments", {})
399
+
400
+ # Validate tool selection
401
+ if selected_tool not in self.tool_names:
402
+ logger.warning(f"Invalid tool '{selected_tool}', available: {self.tool_names}")
403
+ tool_called = False
404
+ selected_tool = None
405
+ else:
406
+ # Call the tool
407
+ result = await client.call_tool(selected_tool, tool_arguments)
408
+ tool_response = self._extract_tool_response(result)
409
+
410
+ except (json.JSONDecodeError, KeyError):
411
+ # Model didn't follow JSON format
412
+ pass
413
+
414
+ return {
415
+ "output": model_output,
416
+ "tool_called": tool_called,
417
+ "selected_tool": selected_tool,
418
+ "tool_arguments": tool_arguments,
419
+ "tool_response": tool_response,
420
+ }
421
+
422
+ except Exception as e:
423
+ logger.exception("First pass failed")
424
+ return {
425
+ "output": f"ERROR: {e!s}",
426
+ "tool_called": False,
427
+ "selected_tool": None,
428
+ "tool_arguments": None,
429
+ "tool_response": None,
430
+ }
431
+
432
+ async def _second_pass(
433
+ self,
434
+ client,
435
+ item: MCPDataInst,
436
+ system_prompt: str,
437
+ tool_response: str | None,
438
+ ) -> str:
439
+ """Second pass: Model receives tool response and generates final answer."""
440
+ messages = [
441
+ {"role": "system", "content": system_prompt},
442
+ {"role": "user", "content": item["user_query"]},
443
+ {
444
+ "role": "assistant",
445
+ "content": f"I'll use the tool to help answer this. Tool response: {tool_response}",
446
+ },
447
+ {
448
+ "role": "user",
449
+ "content": "Based on the tool response, please provide your final answer.",
450
+ },
451
+ ]
452
+
453
+ try:
454
+ if self._lm is not None:
455
+ return self._lm(messages).strip()
456
+ else:
457
+ return cast(Callable, self.task_model)(messages)
458
+
459
+ except Exception as e:
460
+ logger.exception("Second pass failed")
461
+ return f"ERROR: {e!s}"
462
+
463
+ def _build_system_prompt(self, candidate: dict[str, str], available_tools: list[dict[str, Any]]) -> str:
464
+ """Build system prompt with tool information."""
465
+ custom_system_prompt = candidate.get("system_prompt", self.base_system_prompt)
466
+
467
+ # Build tool descriptions
468
+ tool_descriptions = {}
469
+ for tool in available_tools:
470
+ tool_name = tool.get("name")
471
+ # Use optimized description if available
472
+ # Support both tool_description_{tool_name} (multi-tool) and tool_description (single-tool)
473
+ optimized_desc = candidate.get(f"tool_description_{tool_name}") or candidate.get("tool_description")
474
+ tool_descriptions[tool_name] = optimized_desc or tool.get("description", "")
475
+
476
+ # Build tools section
477
+ tools_section = "You have access to the following tools:\n\n"
478
+
479
+ for tool in available_tools:
480
+ tool_name = tool.get("name")
481
+ tool_description = tool_descriptions[tool_name]
482
+ input_schema = tool.get("inputSchema", {})
483
+
484
+ # Build example arguments from schema
485
+ properties = input_schema.get("properties", {})
486
+ example_args = {}
487
+ for param_name, param_info in properties.items():
488
+ if param_info.get("type") == "string":
489
+ example_args[param_name] = "example_value"
490
+ elif param_info.get("type") == "number":
491
+ example_args[param_name] = 123
492
+ elif param_info.get("type") == "boolean":
493
+ example_args[param_name] = True
494
+ else:
495
+ example_args[param_name] = "value"
496
+
497
+ if not example_args:
498
+ example_args = {"param": "value"}
499
+
500
+ example_json = json.dumps(example_args)
501
+
502
+ tools_section += f"""Tool: {tool_name}
503
+ Description: {tool_description}
504
+ Input Schema: {json.dumps(input_schema, indent=2)}
505
+ Example usage: {{"action": "call_tool", "tool": "{tool_name}", "arguments": {example_json}}}
506
+
507
+ """
508
+
509
+ # Add usage instructions
510
+ usage_instructions = f"""
511
+ When you need to use a tool, respond ONLY with JSON:
512
+ {{"action": "call_tool", "tool": "tool_name", "arguments": {{"param": "value"}}}}
513
+
514
+ When you can answer directly, respond ONLY with JSON:
515
+ {{"action": "answer", "text": "your answer"}}
516
+
517
+ Choose the most appropriate tool for the task. Available tools: {[t.get("name") for t in available_tools]}
518
+
519
+ Always respond with valid JSON. No other text.
520
+ """
521
+
522
+ return f"{custom_system_prompt}\n{tools_section}{usage_instructions}"
523
+
524
+ def _extract_tool_response(self, result) -> str:
525
+ """
526
+ Extract text from MCP tool response.
527
+
528
+ Handles multiple content types following MCP SDK best practices:
529
+ - TextContent: Plain text responses
530
+ - EmbeddedResource: Resource references
531
+ - ImageContent: Image data (converted to description)
532
+ - structuredContent: Structured JSON data
533
+
534
+ Based on latest MCP SDK examples and DSPy implementation.
535
+ """
536
+ try:
537
+ # Import MCP types for proper parsing
538
+ from mcp.types import EmbeddedResource, ImageContent, TextContent # type: ignore[import-untyped]
539
+
540
+ # Check for errors first (following DSPy pattern)
541
+ if hasattr(result, "isError") and result.isError:
542
+ # Extract error message from content
543
+ error_texts = []
544
+ for content_item in result.content:
545
+ if isinstance(content_item, TextContent):
546
+ error_texts.append(content_item.text)
547
+ error_msg = "\n".join(error_texts) if error_texts else "Tool execution failed"
548
+ logger.warning(f"Tool returned error: {error_msg}")
549
+ return f"ERROR: {error_msg}"
550
+
551
+ # Try structured content first (modern MCP pattern)
552
+ if hasattr(result, "structuredContent") and result.structuredContent:
553
+ import json
554
+
555
+ return json.dumps(result.structuredContent, indent=2)
556
+
557
+ # Parse content array
558
+ if hasattr(result, "content"):
559
+ texts = []
560
+ for content_item in result.content:
561
+ if isinstance(content_item, TextContent):
562
+ texts.append(content_item.text)
563
+ elif isinstance(content_item, EmbeddedResource):
564
+ # Handle embedded resources
565
+ resource = content_item.resource
566
+ if hasattr(resource, "text"):
567
+ texts.append(resource.text)
568
+ else:
569
+ texts.append(f"[Resource: {getattr(resource, 'uri', 'unknown')}]")
570
+ elif isinstance(content_item, ImageContent):
571
+ # Handle images with description
572
+ mime_type = getattr(content_item, "mimeType", "unknown")
573
+ data_len = len(getattr(content_item, "data", b""))
574
+ texts.append(f"[Image: {mime_type}, {data_len} bytes]")
575
+
576
+ if texts:
577
+ return "\n".join(texts)
578
+
579
+ # Fallback to dict access for backward compatibility
580
+ if isinstance(result, dict):
581
+ content = result.get("content", [])
582
+ if isinstance(content, list):
583
+ texts = []
584
+ for item in content:
585
+ if isinstance(item, dict) and item.get("type") == "text":
586
+ texts.append(item.get("text", ""))
587
+ # Return empty string if content list is present but empty
588
+ return "\n".join(texts)
589
+
590
+ return str(result)
591
+
592
+ except Exception as e:
593
+ logger.exception("Failed to extract tool response")
594
+ return f"ERROR extracting response: {e!s}"
595
+
596
+ def make_reflective_dataset(
597
+ self,
598
+ candidate: dict[str, str],
599
+ eval_batch: EvaluationBatch[MCPTrajectory, MCPOutput],
600
+ components_to_update: list[str],
601
+ ) -> dict[str, list[dict[str, Any]]]:
602
+ """
603
+ Build reflective dataset for instruction refinement.
604
+
605
+ Args:
606
+ candidate: Current candidate components
607
+ eval_batch: Evaluation results with trajectories
608
+ components_to_update: Which components to generate data for
609
+
610
+ Returns:
611
+ Dictionary mapping component names to reflective examples
612
+ """
613
+ reflective_data: dict[str, list[dict[str, Any]]] = {}
614
+
615
+ for component in components_to_update:
616
+ examples: list[dict[str, Any]] = []
617
+
618
+ for traj, score, _output in zip(
619
+ eval_batch.trajectories or [],
620
+ eval_batch.scores,
621
+ eval_batch.outputs,
622
+ strict=False,
623
+ ):
624
+ if component == "tool_description":
625
+ feedback = self._generate_tool_feedback(traj, score)
626
+ examples.append(
627
+ {
628
+ "Inputs": {
629
+ "user_query": traj["user_query"],
630
+ "tool_description": traj["tool_description_used"],
631
+ },
632
+ "Generated Outputs": {
633
+ "tool_called": traj["tool_called"],
634
+ "selected_tool": traj["selected_tool"],
635
+ "tool_arguments": traj["tool_arguments"],
636
+ "final_answer": traj["model_final_output"],
637
+ },
638
+ "Feedback": feedback,
639
+ }
640
+ )
641
+
642
+ elif component == "system_prompt":
643
+ feedback = self._generate_system_prompt_feedback(traj, score)
644
+ examples.append(
645
+ {
646
+ "Inputs": {
647
+ "user_query": traj["user_query"],
648
+ "system_prompt": traj["system_prompt_used"],
649
+ },
650
+ "Generated Outputs": traj["model_final_output"],
651
+ "Feedback": feedback,
652
+ }
653
+ )
654
+
655
+ reflective_data[component] = examples
656
+
657
+ return reflective_data
658
+
659
+ def _generate_tool_feedback(self, traj: MCPTrajectory, score: float) -> str:
660
+ """Generate feedback focused on tool usage and selection."""
661
+ if score > 0.5:
662
+ if traj["tool_called"]:
663
+ return f"Good! The tool '{traj['selected_tool']}' was used appropriately. Score: {score:.2f}"
664
+ else:
665
+ return f"Good! No tool needed, direct answer was correct. Score: {score:.2f}"
666
+ else:
667
+ feedback_parts = [f"Incorrect response (score: {score:.2f})."]
668
+
669
+ if not traj["tool_called"]:
670
+ feedback_parts.append("Tool was not called. Consider if a tool would help.")
671
+ else:
672
+ selected_tool = traj["selected_tool"]
673
+ available_tools = traj["tool_names"]
674
+ feedback_parts.append(
675
+ f"Tool '{selected_tool}' was called with {traj['tool_arguments']}, but answer was incorrect."
676
+ )
677
+ if len(available_tools) > 1:
678
+ feedback_parts.append(f"Consider a different tool from {available_tools} or clearer description.")
679
+ else:
680
+ feedback_parts.append("Tool description may need improvement.")
681
+
682
+ return " ".join(feedback_parts)
683
+
684
+ def _generate_system_prompt_feedback(self, traj: MCPTrajectory, score: float) -> str:
685
+ """Generate feedback focused on system prompt guidance."""
686
+ if score > 0.5:
687
+ return f"System prompt provided good guidance. Score: {score:.2f}"
688
+ else:
689
+ return (
690
+ f"System prompt may need improvement (score: {score:.2f}). "
691
+ f"Model {'called' if traj['tool_called'] else 'did not call'} tool, "
692
+ f"but answer was incorrect."
693
+ )