cmpnd 0.64.0__tar.gz → 0.65.0__tar.gz

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 (396) hide show
  1. {cmpnd-0.64.0 → cmpnd-0.65.0}/PKG-INFO +24 -6
  2. {cmpnd-0.64.0 → cmpnd-0.65.0}/README.md +23 -5
  3. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/optimizations.py +117 -2
  4. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/top.py +18 -26
  5. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/shell.py +0 -4
  6. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/verbs.py +1 -0
  7. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/client.py +86 -20
  8. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/execution.py +4 -0
  9. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimization.py +192 -116
  10. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/cli.md +35 -3
  11. {cmpnd-0.64.0 → cmpnd-0.65.0}/pyproject.toml +1 -1
  12. cmpnd-0.65.0/tests/e2e/continuation_task.py +72 -0
  13. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_deploy_pandas.py +0 -1
  14. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_authored_metric.py +0 -2
  15. cmpnd-0.65.0/tests/e2e/test_optimize_continuation.py +120 -0
  16. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_custom_proposer.py +0 -1
  17. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_failures.py +0 -2
  18. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_loop.py +0 -3
  19. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -1
  20. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/hosted_optimize_probe.py +0 -1
  21. cmpnd-0.65.0/tests/stress/test_optimize_continuation.py +156 -0
  22. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_optimize_loop.py +1 -2
  23. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_optimize_loop_wasm.py +0 -1
  24. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_optimize_stress.py +0 -1
  25. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored_binding_credentials.py +3 -3
  26. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored_metric_contract.py +5 -5
  27. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored_named_models.py +0 -2
  28. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored_optimize.py +3 -3
  29. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_optimizations.py +96 -0
  30. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_top.py +4 -3
  31. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_credential_selection.py +6 -6
  32. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deploy_program_id.py +11 -14
  33. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_lm_configuration.py +1 -1
  34. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize.py +272 -81
  35. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_budget.py +3 -4
  36. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_proposer.py +0 -1
  37. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_program_names_in_deploy.py +3 -1
  38. {cmpnd-0.64.0 → cmpnd-0.65.0}/uv.lock +1 -1
  39. {cmpnd-0.64.0 → cmpnd-0.65.0}/.gitignore +0 -0
  40. {cmpnd-0.64.0 → cmpnd-0.65.0}/AGENTS.md +0 -0
  41. {cmpnd-0.64.0 → cmpnd-0.65.0}/CLAUDE.md +0 -0
  42. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/__init__.py +0 -0
  43. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_assessments.py +0 -0
  44. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_authored_import.py +0 -0
  45. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_deployments.py +0 -0
  46. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_exporter_pool.py +0 -0
  47. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_gepa_patch.py +0 -0
  48. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_http.py +0 -0
  49. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_program_patch.py +0 -0
  50. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_rlm_patch.py +0 -0
  51. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_runtime_tools.py +0 -0
  52. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_timespec.py +0 -0
  53. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_traces.py +0 -0
  54. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/_valsets.py +0 -0
  55. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/authored.py +0 -0
  56. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/callback.py +0 -0
  57. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/__init__.py +0 -0
  58. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/api_client.py +0 -0
  59. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/__init__.py +0 -0
  60. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/admin.py +0 -0
  61. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/assessments.py +0 -0
  62. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/auth.py +0 -0
  63. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/datasets.py +0 -0
  64. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/deployments.py +0 -0
  65. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/evals.py +0 -0
  66. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/login.py +0 -0
  67. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/org.py +0 -0
  68. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/programs.py +0 -0
  69. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  70. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  71. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/traces.py +0 -0
  72. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/commands/valsets.py +0 -0
  73. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/credentials.py +0 -0
  74. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/deprecation.py +0 -0
  75. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/pagination.py +0 -0
  76. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/trace_render.py +0 -0
  77. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/cli/tree_help.py +0 -0
  78. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/configuration.py +0 -0
  79. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/context.py +0 -0
  80. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/credentials.py +0 -0
  81. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/dataset_sync.py +0 -0
  82. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/datasets.py +0 -0
  83. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/decorators.py +0 -0
  84. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/deployment.py +0 -0
  85. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/deployment_metadata.py +0 -0
  86. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/deployment_runs.py +0 -0
  87. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/eval_handler.py +0 -0
  88. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/exporter.py +0 -0
  89. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/exporter_http.py +0 -0
  90. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/helpers.py +0 -0
  91. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/identity.py +0 -0
  92. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/imaging.py +0 -0
  93. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/__init__.py +0 -0
  94. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/decode.py +0 -0
  95. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/encode.py +0 -0
  96. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/hash.py +0 -0
  97. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/program.py +0 -0
  98. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/reflection.py +0 -0
  99. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/types.py +0 -0
  100. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/ir/xxh64.py +0 -0
  101. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/lm_configuration.py +0 -0
  102. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/mcp_servers.py +0 -0
  103. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/models.py +0 -0
  104. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/named_programs.py +0 -0
  105. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimizers/__init__.py +0 -0
  106. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimizers/gepa.py +0 -0
  107. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  108. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimizers/resume.py +0 -0
  109. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/optimizers/tracker.py +0 -0
  110. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/packaging.py +0 -0
  111. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/programs.py +0 -0
  112. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/references.py +0 -0
  113. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/retry.py +0 -0
  114. {cmpnd-0.64.0 → cmpnd-0.65.0}/cmpnd/traces.py +0 -0
  115. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/2026-04-02-code-review.md +0 -0
  116. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/client.md +0 -0
  117. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/custom-proposer.md +0 -0
  118. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  119. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  120. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/reading-traces.md +0 -0
  121. {cmpnd-0.64.0 → cmpnd-0.65.0}/docs/sdk-instrumentation.md +0 -0
  122. {cmpnd-0.64.0 → cmpnd-0.65.0}/examples/generalizing_proposer.py +0 -0
  123. {cmpnd-0.64.0 → cmpnd-0.65.0}/examples/local_ollama.py +0 -0
  124. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/LINEAGE.md +0 -0
  125. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/README.md +0 -0
  126. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  127. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  128. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  129. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  130. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  131. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  132. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/py.typed +0 -0
  133. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  134. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/retry.py +0 -0
  135. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  136. {cmpnd-0.64.0 → cmpnd-0.65.0}/ingest/pyproject.toml +0 -0
  137. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/audit-sdist.sh +0 -0
  138. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/capture_review_run.py +0 -0
  139. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/check-pip-install.sh +0 -0
  140. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/check_release_artifacts.py +0 -0
  141. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/fixtures/review_run.json.gz +0 -0
  142. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/gepa_resume_bug.py +0 -0
  143. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/install-test-env.sh +0 -0
  144. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/release-stress.sh +0 -0
  145. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/require_version_bump.py +0 -0
  146. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/seed_review_run.py +0 -0
  147. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/selfhost_compat.py +0 -0
  148. {cmpnd-0.64.0 → cmpnd-0.65.0}/scripts/test-in-linux.sh +0 -0
  149. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/__init__.pyi +0 -0
  150. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  151. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  152. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/utils/__init__.pyi +0 -0
  153. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/utils/callback.pyi +0 -0
  154. {cmpnd-0.64.0 → cmpnd-0.65.0}/stubs/dspy/utils/dummies.pyi +0 -0
  155. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/__init__.py +0 -0
  156. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_credential_route.py +0 -0
  157. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_deployment_signature_helpers.py +0 -0
  158. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_exporter_helpers.py +0 -0
  159. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_fake_clock.py +0 -0
  160. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_identity_helpers.py +0 -0
  161. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_resolve_refs.py +0 -0
  162. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/_traced_run.py +0 -0
  163. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/conftest.py +0 -0
  164. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/__init__.py +0 -0
  165. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/committee_task.py +0 -0
  166. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/conftest.py +0 -0
  167. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  168. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  169. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/fake_lm.py +0 -0
  170. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/helpers.py +0 -0
  171. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/target_capabilities.py +0 -0
  172. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_assessments_cli.py +0 -0
  173. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_capture_prompts.py +0 -0
  174. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_composite_module.py +0 -0
  175. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  176. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_deploy_execute.py +0 -0
  177. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_deploy_programs.py +0 -0
  178. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_edge_cases.py +0 -0
  179. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_example_hash.py +0 -0
  180. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_gepa_optimization.py +0 -0
  181. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_interpreter_spans.py +0 -0
  182. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_lm_accuracy.py +0 -0
  183. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_module_tracing.py +0 -0
  184. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimization_lineage.py +0 -0
  185. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  186. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  187. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  188. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_parallel_export.py +0 -0
  189. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_project_attribution.py +0 -0
  190. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_react_tool_spans.py +0 -0
  191. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_save_load.py +0 -0
  192. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_trace_structure.py +0 -0
  193. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_traces_read.py +0 -0
  194. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_user_attribution.py +0 -0
  195. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/e2e/test_valsets_cli.py +0 -0
  196. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  197. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  198. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  199. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  200. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  201. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  202. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  203. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  204. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  205. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/10_react.json +0 -0
  206. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/11_refine.json +0 -0
  207. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/12_retrieve.json +0 -0
  208. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/13_knn.json +0 -0
  209. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/14_parallel.json +0 -0
  210. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/identity_vectors/README.md +0 -0
  211. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/ingest_conformance.py +0 -0
  212. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/__init__.py +0 -0
  213. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/authored_optimize/judge.py +0 -0
  214. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  215. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/conftest.py +0 -0
  216. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/optimization_run_probe.py +0 -0
  217. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  218. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/test_logs_renders_traces.py +0 -0
  219. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/test_optimization_run.py +0 -0
  220. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/test_optimizing_progress.py +0 -0
  221. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/integration/test_unhappy_path.py +0 -0
  222. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/__init__.py +0 -0
  223. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/__main__.py +0 -0
  224. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/drain.py +0 -0
  225. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/ingest.py +0 -0
  226. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/latency_transport.py +0 -0
  227. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/rlm_shape.py +0 -0
  228. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/rlm_size_report.py +0 -0
  229. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/sweep.py +0 -0
  230. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/test_rlm_shape.py +0 -0
  231. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/load/test_rlm_size_report.py +0 -0
  232. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/__init__.py +0 -0
  233. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/conftest.py +0 -0
  234. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/seed_parity.py +0 -0
  235. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_auth.py +0 -0
  236. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_datasets.py +0 -0
  237. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_deployments.py +0 -0
  238. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_evals.py +0 -0
  239. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_health.py +0 -0
  240. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_modules.py +0 -0
  241. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_optimizations.py +0 -0
  242. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_programs.py +0 -0
  243. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_signatures.py +0 -0
  244. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_trace_streaming.py +0 -0
  245. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/parity/test_traces.py +0 -0
  246. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/span_reference_fixture.py +0 -0
  247. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/__init__.py +0 -0
  248. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/_provider.py +0 -0
  249. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/_readiness.py +0 -0
  250. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/_reliability.py +0 -0
  251. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/_result.py +0 -0
  252. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/mint_bedrock_token.py +0 -0
  253. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_bedrock_converse.py +0 -0
  254. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_deploy_programs.py +0 -0
  255. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_multi_client_stress.py +0 -0
  256. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_optimize_client_committee.py +0 -0
  257. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_provider_roles.py +0 -0
  258. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_readiness.py +0 -0
  259. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_real_provider_twins.py +0 -0
  260. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_reliability.py +0 -0
  261. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_reliability_classify.py +0 -0
  262. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_rlm_wasm.py +0 -0
  263. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/stress/test_trace_search.py +0 -0
  264. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_adapter_reentrancy.py +0 -0
  265. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_assessments_api.py +0 -0
  266. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored.py +0 -0
  267. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_authored_bundle_collisions.py +0 -0
  268. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_callback.py +0 -0
  269. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_candidate_lineage.py +0 -0
  270. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/__init__.py +0 -0
  271. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/conftest.py +0 -0
  272. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_admin.py +0 -0
  273. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_api_client.py +0 -0
  274. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_assessments.py +0 -0
  275. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_auth.py +0 -0
  276. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_credentials.py +0 -0
  277. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_datasets.py +0 -0
  278. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_deployment_runs.py +0 -0
  279. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_deployments.py +0 -0
  280. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_evals.py +0 -0
  281. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_login.py +0 -0
  282. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_main.py +0 -0
  283. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_org.py +0 -0
  284. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_pagination.py +0 -0
  285. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_parity.py +0 -0
  286. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_programs.py +0 -0
  287. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_provider_credentials.py +0 -0
  288. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_schema_sample.py +0 -0
  289. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_shell.py +0 -0
  290. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_tool_servers.py +0 -0
  291. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_traces.py +0 -0
  292. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  293. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  294. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_tree_help.py +0 -0
  295. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cli/test_valsets.py +0 -0
  296. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_client.py +0 -0
  297. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_client_floor.py +0 -0
  298. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_client_program_names.py +0 -0
  299. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_clustering/__init__.py +0 -0
  300. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_clustering/conftest.py +0 -0
  301. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_clustering/test_edge_cases.py +0 -0
  302. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  303. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_clustering/test_type_conversion.py +0 -0
  304. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_cmp551_grouping_identity.py +0 -0
  305. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_configuration.py +0 -0
  306. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_context.py +0 -0
  307. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_context_propagation.py +0 -0
  308. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_credential_kwargs.py +0 -0
  309. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_credential_values.py +0 -0
  310. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_dataset_polling.py +0 -0
  311. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_dataset_sync.py +0 -0
  312. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_datasets.py +0 -0
  313. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_decision_client_spans.py +0 -0
  314. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_decorators.py +0 -0
  315. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deploy.py +0 -0
  316. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  317. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deployment_metadata.py +0 -0
  318. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deployment_runs.py +0 -0
  319. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_deployments_read.py +0 -0
  320. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_eval.py +0 -0
  321. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_eval_example_attribution.py +0 -0
  322. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_eval_start.py +0 -0
  323. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_eval_trace_grouping.py +0 -0
  324. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_exception_paths.py +0 -0
  325. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_execute.py +0 -0
  326. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_execute_run_identity.py +0 -0
  327. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_exporter.py +0 -0
  328. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_exporter_program_names.py +0 -0
  329. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_exporter_queue.py +0 -0
  330. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_exporter_real.py +0 -0
  331. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_fake_lm.py +0 -0
  332. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_gepa_callback.py +0 -0
  333. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_gepa_integration.py +0 -0
  334. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_gepa_output_normalization.py +0 -0
  335. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_gepa_resume.py +0 -0
  336. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_identity.py +0 -0
  337. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_imaging.py +0 -0
  338. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_import_weight.py +0 -0
  339. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_ingest_conformance.py +0 -0
  340. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_ingest_second_producer.py +0 -0
  341. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_integration.py +0 -0
  342. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_interpreter_callback.py +0 -0
  343. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_ir_encoder_properties.py +0 -0
  344. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_ir_golden_vectors.py +0 -0
  345. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_ir_roundtrip.py +0 -0
  346. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_lm15_native_calls.py +0 -0
  347. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_lm_usage_shapes.py +0 -0
  348. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_mcp_tools.py +0 -0
  349. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_models.py +0 -0
  350. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_multi_client_stress_readiness.py +0 -0
  351. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimization_progress.py +0 -0
  352. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimization_start.py +0 -0
  353. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_lift.py +0 -0
  354. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_lms.py +0 -0
  355. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_reflection_lm.py +0 -0
  356. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_optimize_terminal_failure.py +0 -0
  357. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_packaging.py +0 -0
  358. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_packaging_callable_source.py +0 -0
  359. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_packaging_mcp_tools.py +0 -0
  360. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_packaging_program_source.py +0 -0
  361. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_poll_until.py +0 -0
  362. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_program_id_deprecation.py +0 -0
  363. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_program_identity.py +0 -0
  364. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_program_lineage_persist.py +0 -0
  365. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_programs_create.py +0 -0
  366. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_project_payload.py +0 -0
  367. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_real_provider_twins_readiness.py +0 -0
  368. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_reflector_properties.py +0 -0
  369. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_reflector_smoke.py +0 -0
  370. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_release_stress_script.py +0 -0
  371. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_replaced_names.py +0 -0
  372. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_repro_reactv2_bugs.py +0 -0
  373. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_require_version_bump.py +0 -0
  374. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_retry.py +0 -0
  375. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_reversible_failures.py +0 -0
  376. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_rlm_patch.py +0 -0
  377. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_run_attribution_paths.py +0 -0
  378. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_runtime_tools.py +0 -0
  379. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_schemas.py +0 -0
  380. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_seal_lm_credential.py +0 -0
  381. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_seed_review_run.py +0 -0
  382. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_signature_inputs.py +0 -0
  383. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_span_references.py +0 -0
  384. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_span_sent_once.py +0 -0
  385. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_system_one.py +0 -0
  386. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_trace_context_concurrency.py +0 -0
  387. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_trace_fact_wire_contract.py +0 -0
  388. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_trace_facts.py +0 -0
  389. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_trace_render.py +0 -0
  390. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_traces_read.py +0 -0
  391. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_tracked_gepa.py +0 -0
  392. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_tracker_enrich.py +0 -0
  393. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_truncation.py +0 -0
  394. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_upload_auth_headers.py +0 -0
  395. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_valsets_api.py +0 -0
  396. {cmpnd-0.64.0 → cmpnd-0.65.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.64.0
3
+ Version: 0.65.0
4
4
  Summary: DSPy observability and deployment SDK for cmpnd
5
5
  Project-URL: Homepage, https://cmpnd.ai
6
6
  Author-email: cmpnd <hello@cmpnd.ai>
@@ -497,14 +497,12 @@ sdk.optimize_deployment(
497
497
  reflection_lm=None, # required: a dspy.LM, a bare model name or a cmpnd.HostedLM
498
498
  lms=None, # optional: name -> dspy.LM, cmpnd.HostedLM or cmpnd.SystemOne
499
499
  credential=None, # optional: the cmpnd.Credential every other binding spends
500
- max_metric_calls=None, # budget: the metric-call ceiling for one step
500
+ max_metric_calls=None, # budget: the metric-call ceiling for the run
501
501
  auto=None, # budget: "light", "medium" or "heavy"
502
502
  may_author_code=None, # optional: False keeps each dspy.Flex at its deployed source
503
- max_steps=10, # optional: the most optimize steps
504
- patience=2, # optional: stop after this many steps without improvement
505
- seed=0, # optional: the starting seed, advanced each step
503
+ seed=0, # optional: the search's random seed
506
504
  poll_interval=3.0, # optional: seconds between status checks
507
- timeout=600.0, # optional: seconds to wait for each step
505
+ timeout=600.0, # optional: seconds to wait for the run
508
506
  )
509
507
  ```
510
508
 
@@ -516,6 +514,21 @@ budget is missing.
516
514
  initiation with a 4xx, such as a 409 for missing data or a metric mismatch. It
517
515
  raises `cmpnd.OptimizeFailed` when a step reaches `failed`.
518
516
 
517
+ The result's `optimization_run_id` names the run that produced it.
518
+ `continue_optimization()` starts a new run from one of that run's candidates,
519
+ reusing its data, with a budget of its own:
520
+
521
+ ```python
522
+ better = sdk.continue_optimization(best, max_metric_calls=120) # from the best candidate
523
+
524
+ for candidate in sdk.list_candidates(best.optimization_run_id): # read each one first
525
+ print(candidate["candidate_id"], candidate["aggregate_score"], candidate["instructions"])
526
+ other = sdk.continue_optimization(best, candidate="ca_...", auto="light")
527
+ ```
528
+
529
+ A refusal raises `cmpnd.OptimizeError`, whose `error_code` says why, such as
530
+ `not_completed` or `unknown_candidate`.
531
+
519
532
  ## Command line
520
533
 
521
534
  The package installs the `cmpnd` command. `cmpnd login` saves your API key for
@@ -639,6 +652,11 @@ global config. Use it as a context manager or as a plain object.
639
652
  - `optimize_deployment(deployment, ...)` optimizes a deployment on the platform
640
653
  and returns the best `DeployedProgram`, whose `.score` is the best score
641
654
  found. It raises `cmpnd.OptimizeError` or `cmpnd.OptimizeFailed` on failure.
655
+ - `list_candidates(run_id)` returns a run's candidates as dicts, each with its
656
+ `ca_` id, score, parent and instructions.
657
+ - `continue_optimization(run, candidate=None, ...)` runs a new optimization
658
+ from one candidate of a completed run, with that run's data and a new budget,
659
+ and returns its best `DeployedProgram`.
642
660
  - `list_traces(**filters)` and `get_trace(trace_id)` read traces with the
643
661
  client's credentials. They flush the client's traces first.
644
662
  - `close()` flushes the client's traces and closes the client. A closed
@@ -446,14 +446,12 @@ sdk.optimize_deployment(
446
446
  reflection_lm=None, # required: a dspy.LM, a bare model name or a cmpnd.HostedLM
447
447
  lms=None, # optional: name -> dspy.LM, cmpnd.HostedLM or cmpnd.SystemOne
448
448
  credential=None, # optional: the cmpnd.Credential every other binding spends
449
- max_metric_calls=None, # budget: the metric-call ceiling for one step
449
+ max_metric_calls=None, # budget: the metric-call ceiling for the run
450
450
  auto=None, # budget: "light", "medium" or "heavy"
451
451
  may_author_code=None, # optional: False keeps each dspy.Flex at its deployed source
452
- max_steps=10, # optional: the most optimize steps
453
- patience=2, # optional: stop after this many steps without improvement
454
- seed=0, # optional: the starting seed, advanced each step
452
+ seed=0, # optional: the search's random seed
455
453
  poll_interval=3.0, # optional: seconds between status checks
456
- timeout=600.0, # optional: seconds to wait for each step
454
+ timeout=600.0, # optional: seconds to wait for the run
457
455
  )
458
456
  ```
459
457
 
@@ -465,6 +463,21 @@ budget is missing.
465
463
  initiation with a 4xx, such as a 409 for missing data or a metric mismatch. It
466
464
  raises `cmpnd.OptimizeFailed` when a step reaches `failed`.
467
465
 
466
+ The result's `optimization_run_id` names the run that produced it.
467
+ `continue_optimization()` starts a new run from one of that run's candidates,
468
+ reusing its data, with a budget of its own:
469
+
470
+ ```python
471
+ better = sdk.continue_optimization(best, max_metric_calls=120) # from the best candidate
472
+
473
+ for candidate in sdk.list_candidates(best.optimization_run_id): # read each one first
474
+ print(candidate["candidate_id"], candidate["aggregate_score"], candidate["instructions"])
475
+ other = sdk.continue_optimization(best, candidate="ca_...", auto="light")
476
+ ```
477
+
478
+ A refusal raises `cmpnd.OptimizeError`, whose `error_code` says why, such as
479
+ `not_completed` or `unknown_candidate`.
480
+
468
481
  ## Command line
469
482
 
470
483
  The package installs the `cmpnd` command. `cmpnd login` saves your API key for
@@ -588,6 +601,11 @@ global config. Use it as a context manager or as a plain object.
588
601
  - `optimize_deployment(deployment, ...)` optimizes a deployment on the platform
589
602
  and returns the best `DeployedProgram`, whose `.score` is the best score
590
603
  found. It raises `cmpnd.OptimizeError` or `cmpnd.OptimizeFailed` on failure.
604
+ - `list_candidates(run_id)` returns a run's candidates as dicts, each with its
605
+ `ca_` id, score, parent and instructions.
606
+ - `continue_optimization(run, candidate=None, ...)` runs a new optimization
607
+ from one candidate of a completed run, with that run's data and a new budget,
608
+ and returns its best `DeployedProgram`.
591
609
  - `list_traces(**filters)` and `get_trace(trace_id)` read traces with the
592
610
  client's credentials. They flush the client's traces first.
593
611
  - `close()` flushes the client's traces and closes the client. A closed
@@ -2,10 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import sys
5
6
  from argparse import ArgumentParser, _SubParsersAction
6
7
  from typing import Any
7
8
 
8
9
  from .. import api_client, deprecation, pagination
10
+ from . import top
9
11
 
10
12
 
11
13
  def _add_list_args(p: ArgumentParser) -> None:
@@ -88,6 +90,42 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
88
90
  cancel.add_argument("id", help="Optimization run ID (op_...)")
89
91
  cancel.set_defaults(func=handle_cancel)
90
92
 
93
+ cont = sub.add_parser(
94
+ "continue",
95
+ help="Run a new optimization from one candidate of a completed run. It reuses that run's data, models "
96
+ "and credentials, and takes a new budget",
97
+ )
98
+ cont.add_argument("id", help="Completed optimization run ID (op_...)")
99
+ cont.add_argument(
100
+ "--candidate",
101
+ metavar="CANDIDATE_ID",
102
+ help="The ca_ id of the candidate to start from, as `cmpnd optimizations candidates ID` lists them "
103
+ "(default: the run's best)",
104
+ )
105
+ # The budget flags are `cmpnd optimize`'s, and the SDK refuses a run with none or two,
106
+ # so both commands answer a missing budget the same way.
107
+ cont.add_argument(
108
+ "--max-metric-calls",
109
+ dest="max_metric_calls",
110
+ type=int,
111
+ default=None,
112
+ help="Budget for the search, as metric calls (one per rollout). Required unless --auto is given",
113
+ )
114
+ cont.add_argument(
115
+ "--auto",
116
+ choices=["light", "medium", "heavy"],
117
+ default=None,
118
+ help="Budget as a t-shirt size, sized from the program and valset (alternative to --max-metric-calls)",
119
+ )
120
+ cont.add_argument(
121
+ "--async",
122
+ dest="asynchronous",
123
+ action="store_true",
124
+ help="Return the queued run at once instead of waiting for it to finish",
125
+ )
126
+ cont.add_argument("--json", action="store_true", help="Emit the result summary as JSON")
127
+ cont.set_defaults(func=handle_continue)
128
+
91
129
  delete = sub.add_parser("delete", help="Delete an optimization run and everything recorded under it")
92
130
  delete.add_argument("id", help="Optimization run ID (op_...)")
93
131
  delete.set_defaults(func=handle_delete)
@@ -124,8 +162,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
124
162
  iters.add_argument("id", help="Optimization run ID (UUID)")
125
163
  iters.set_defaults(func=handle_iterations)
126
164
 
127
- cands = sub.add_parser("candidates", help="List candidates for an optimization run")
128
- cands.add_argument("id", help="Optimization run ID (UUID)")
165
+ cands = sub.add_parser(
166
+ "candidates",
167
+ help="List a run's candidates: each one's ca_ id, score and parent, to pick one to continue from",
168
+ )
169
+ cands.add_argument("id", help="Optimization run ID (op_...)")
129
170
  cands.add_argument(
130
171
  "--full",
131
172
  action="store_true",
@@ -213,6 +254,80 @@ def handle_cancel(client: api_client.ApiClient, args: Any) -> Any:
213
254
  return client.post(f"/api/v1/optimizations/{args.id}/cancel")
214
255
 
215
256
 
257
+ def handle_continue(client: api_client.ApiClient, args: Any) -> Any:
258
+ """Run a new optimization from one candidate of a completed run, as `cmpnd optimize` runs one.
259
+
260
+ The new run reuses the completed run's data and configuration and takes a new budget.
261
+ The command names the candidate it starts from, waits for the run, and prints the
262
+ best program as `cmpnd optimize` does. `--async` returns the queued run instead.
263
+ """
264
+ from ... import optimization
265
+
266
+ cfg = top.sdk_config(client)
267
+ try:
268
+ started = optimization.start_continuation(
269
+ args.id,
270
+ cfg=cfg,
271
+ candidate=args.candidate,
272
+ max_metric_calls=args.max_metric_calls,
273
+ auto=args.auto,
274
+ )
275
+ except (optimization.OptimizeError, ValueError) as exc:
276
+ raise api_client.CliError(str(exc)) from exc
277
+ if args.asynchronous:
278
+ return started
279
+
280
+ texts = _instruction_texts(started.get("candidate_instructions"))
281
+ score = started.get("candidate_score")
282
+ print(
283
+ top._c(
284
+ f"continuing {args.id} from {started.get('continued_from_candidate_id')} "
285
+ f"(score {score:.3f}): {_first_line(texts)}"
286
+ if isinstance(score, (int, float))
287
+ else f"continuing {args.id} from {started.get('continued_from_candidate_id')}",
288
+ "dim",
289
+ ),
290
+ file=sys.stderr,
291
+ )
292
+ print(top._c(f"optimizing with {started.get('optimizer')}…", "dim"), file=sys.stderr)
293
+ try:
294
+ result = optimization.wait_for_continuation(started, cfg=cfg)
295
+ except (optimization.OptimizeError, optimization.OptimizeFailed) as exc:
296
+ raise api_client.CliError(str(exc)) from exc
297
+ top.print_optimize_result(
298
+ result,
299
+ as_json=args.json,
300
+ extra={
301
+ "optimization_id": started.get("optimization_id"),
302
+ "continued_from_run_id": started.get("continued_from_run_id"),
303
+ "continued_from_candidate_id": started.get("continued_from_candidate_id"),
304
+ },
305
+ )
306
+ return None
307
+
308
+
309
+ def _instruction_texts(instructions: Any) -> list[tuple[str, str]]:
310
+ """A candidate's instructions as (component, text) pairs, in the order serve sent them."""
311
+ if not isinstance(instructions, dict):
312
+ return []
313
+ return [(str(name), text if isinstance(text, str) else str(text)) for name, text in instructions.items()]
314
+
315
+
316
+ def _first_line(texts: list[tuple[str, str]], width: int = 72) -> str:
317
+ """The first line of the first component's text, naming the component when there are several."""
318
+ if not texts:
319
+ return "-"
320
+ name, text = texts[0]
321
+ line = next((ln.strip() for ln in text.splitlines() if ln.strip()), "")
322
+ if len(texts) > 1:
323
+ line = f"{name}: {line}"
324
+ if len(line) > width:
325
+ line = line[: width - 1] + "…"
326
+ if len(texts) > 1:
327
+ line += f" (+{len(texts) - 1} more)"
328
+ return line
329
+
330
+
216
331
  def handle_delete(client: api_client.ApiClient, args: Any) -> Any:
217
332
  # 204 No Content → delete() returns None; synthesize a confirmation.
218
333
  client.delete(f"/api/v1/optimizations/{args.id}")
@@ -1028,7 +1028,7 @@ def _optimize_credentials(
1028
1028
  ``cmpnd.HostedLM`` when ``--reflection-credential`` names its credential) and the
1029
1029
  credential per binding. :class:`_CredentialLookup` turns each name into a
1030
1030
  ``cmpnd.Credential``, so a mistyped one fails before the import and the deploy a run
1031
- makes ahead of its first step.
1031
+ makes ahead of the optimization.
1032
1032
  """
1033
1033
  from ...lm_configuration import HostedLM
1034
1034
 
@@ -1133,29 +1133,30 @@ def cmd_optimize(client: api_client.ApiClient, args: Any) -> Any:
1133
1133
  lm_credentials=lm_credentials,
1134
1134
  max_metric_calls=getattr(args, "max_metric_calls", None),
1135
1135
  auto=getattr(args, "auto", None),
1136
- max_steps=getattr(args, "max_steps", 10),
1137
- patience=getattr(args, "patience", 2),
1138
1136
  seed=getattr(args, "seed", 0),
1139
1137
  )
1140
1138
  except (optimization.OptimizeError, optimization.OptimizeFailed, ValueError) as exc:
1141
1139
  raise api_client.CliError(str(exc))
1142
1140
 
1143
- if getattr(args, "json", False):
1144
- print(
1145
- json.dumps(
1146
- {
1147
- "best_deployment_id": result.deployment_id,
1148
- "best_score": result.score,
1149
- },
1150
- indent=2,
1151
- default=str,
1152
- )
1153
- )
1141
+ print_optimize_result(result, as_json=getattr(args, "json", False))
1142
+ return result
1143
+
1144
+
1145
+ def print_optimize_result(result: Any, *, as_json: bool, extra: dict[str, Any] | None = None) -> None:
1146
+ """Print a finished optimization's best program, as every command that runs one does.
1147
+
1148
+ The best deployment's id goes to stdout and its score to stderr, so a script can
1149
+ capture the id alone. ``--json`` prints both, and ``extra`` adds fields a command
1150
+ knows about the run.
1151
+ """
1152
+ if as_json:
1153
+ summary = {"best_deployment_id": result.deployment_id, "best_score": result.score}
1154
+ summary.update(extra or {})
1155
+ print(json.dumps(summary, indent=2, default=str))
1154
1156
  else:
1155
1157
  print(result.deployment_id)
1156
1158
  score = "n/a" if result.score is None else f"{result.score}"
1157
1159
  print(_c(f"best score: {score}", "dim"), file=sys.stderr)
1158
- return result
1159
1160
 
1160
1161
 
1161
1162
  def cmd_run(client: api_client.ApiClient, args: Any) -> None:
@@ -1563,7 +1564,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
1563
1564
  dest="max_metric_calls",
1564
1565
  type=int,
1565
1566
  default=None,
1566
- help="Budget for each step's search, as metric calls (one per rollout). Required unless --auto is given",
1567
+ help="Budget for the search, as metric calls (one per rollout). Required unless --auto is given",
1567
1568
  )
1568
1569
  opt.add_argument(
1569
1570
  "--auto",
@@ -1571,15 +1572,6 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
1571
1572
  default=None,
1572
1573
  help="Budget as a t-shirt size, sized from the program and valset (alternative to --max-metric-calls)",
1573
1574
  )
1574
- opt.add_argument(
1575
- "--max-steps",
1576
- dest="max_steps",
1577
- type=int,
1578
- default=10,
1579
- help="How many times to re-optimize the winner, each step a fresh search "
1580
- "with an advanced seed (default 10). Bounds the LOOP, not one search",
1581
- )
1582
- opt.add_argument("--patience", type=int, default=2, help="Stop after this many consecutive non-improving steps")
1583
- opt.add_argument("--seed", type=int, default=0, help="Starting seed; advanced each step")
1575
+ opt.add_argument("--seed", type=int, default=0, help="The search's random seed (default 0)")
1584
1576
  opt.add_argument("--json", action="store_true", help="Emit the result summary as JSON")
1585
1577
  opt.set_defaults(func=_handler("optimize"))
@@ -537,8 +537,6 @@ def _make_namespace(client: api_client.ApiClient, endpoint: str) -> dict[str, An
537
537
  reflection_model: str | None = None,
538
538
  source_root: str | None = None,
539
539
  include: list[str] | None = None,
540
- max_steps: int = 10,
541
- patience: int = 2,
542
540
  seed: int = 0,
543
541
  # Deprecated program id input: refused, and goes when the deprecated inputs are stripped out.
544
542
  program_id: str | None = None,
@@ -568,8 +566,6 @@ def _make_namespace(client: api_client.ApiClient, endpoint: str) -> dict[str, An
568
566
  reflection_model=reflection_model,
569
567
  source_root=source_root,
570
568
  include=list(include or []),
571
- max_steps=max_steps,
572
- patience=patience,
573
569
  seed=seed,
574
570
  ),
575
571
  )
@@ -154,6 +154,7 @@ DOMAIN_VERBS: dict[str, str] = {
154
154
  "wait": "polls one accepted run until it settles or the local budget expires",
155
155
  "cancel": "settles an open run as cancelled and keeps its record; delete would read as removing the run",
156
156
  "follow": "tails a record until it settles — a list over time rather than a snapshot",
157
+ "continue": "starts a run from one candidate of a completed run; create would hide that it inherits its data",
157
158
  "select": "marks one candidate as the program's chosen version",
158
159
  "refresh": "re-reads a record from the system of record it mirrors; nothing the caller supplies is written",
159
160
  "allow": "grants permission to call a tool — a person's decision, never inferred from what code names",
@@ -507,17 +507,14 @@ class Client:
507
507
  max_metric_calls: int | None = None,
508
508
  auto: str | None = None,
509
509
  may_author_code: bool | None = None,
510
- max_steps: int = 10,
511
- patience: int = 2,
512
510
  seed: int = 0,
513
511
  poll_interval: float = 3.0,
514
512
  timeout: float = 600.0,
515
513
  ) -> Any:
516
514
  """Optimize a deployment on the platform, on this client's account, and return the best deployment.
517
515
 
518
- Runs a client-driven loop over the per-step optimize API: upload the
519
- optimization bundle once, then re-optimize the deployment with an advancing
520
- seed until improvement plateaus. Deploy the program first with
516
+ Runs one optimization: upload the optimization bundle, wait for the run to
517
+ settle, and return its result deployment. Deploy the program first with
521
518
  ``deploy_program``. The optimization belongs to the deployment's program, and
522
519
  the program's model comes from the deployment.
523
520
 
@@ -676,28 +673,23 @@ class Client:
676
673
  optimizes every ordinary predictor and leaves each ``dspy.Flex`` at
677
674
  the source you deployed. A program with no ``dspy.Flex`` in it is
678
675
  unaffected either way.
679
- max_steps: How many times to re-optimize the winner, each step a fresh
680
- ``optimize`` call with an advanced seed (default 10). Distinct from
681
- the budget above, which bounds ONE step's search: total spend is
682
- roughly the budget times the steps actually taken.
683
- patience: Stop after this many consecutive non-improving steps (default 2).
684
- seed: Starting seed; advanced by 1 each step (default 0).
676
+ seed: The search's random seed (default 0).
685
677
  poll_interval: Seconds between status polls (default 3.0).
686
- timeout: Per-step polling timeout in seconds (default 600). Also the
687
- outer bound on riding through transient answers: a step's backoff
688
- waits are clipped by it rather than extending the step past it.
678
+ timeout: Polling timeout in seconds (default 600). Also the outer bound
679
+ on riding through transient answers: the run's backoff waits are
680
+ clipped by it rather than extending the wait past it.
689
681
 
690
682
  Returns:
691
- The best ``cmpnd.DeployedProgram``. Run it with ``run_deployment``. Its
692
- ``.score`` is the best score found (``None`` if the server returned no
683
+ The run's result ``cmpnd.DeployedProgram``. Run it with ``run_deployment``.
684
+ Its ``.score`` is the run's best score (``None`` if the server returned no
693
685
  scores).
694
686
 
695
687
  Raises:
696
688
  ValueError: A retired optimizer, an unknown one, a metric that is not a
697
689
  5-arg feedback metric (or whose signature cannot be introspected), or a
698
690
  proposer the sandbox could not call or whose source cannot be recovered.
699
- OptimizeError: A 4xx initiating a step (e.g. 409 no-data / mismatch).
700
- OptimizeFailed: A step transitioned to ``status="failed"``.
691
+ OptimizeError: A 4xx initiating the run (e.g. 409 mismatch).
692
+ OptimizeFailed: The run transitioned to ``status="failed"``.
701
693
  TypeError: A string for ``credential``. Look a credential up with
702
694
  ``cmpnd.credentials.get``.
703
695
  """
@@ -719,13 +711,71 @@ class Client:
719
711
  max_metric_calls=max_metric_calls,
720
712
  auto=auto,
721
713
  may_author_code=may_author_code,
722
- max_steps=max_steps,
723
- patience=patience,
724
714
  seed=seed,
725
715
  poll_interval=poll_interval,
726
716
  timeout=timeout,
727
717
  )
728
718
 
719
+ def continue_optimization(
720
+ self,
721
+ run: Any,
722
+ *,
723
+ candidate: str | None = None,
724
+ max_metric_calls: int | None = None,
725
+ auto: str | None = None,
726
+ poll_interval: float = 3.0,
727
+ timeout: float = 600.0,
728
+ ) -> Any:
729
+ """Run a new optimization from one candidate of a completed one, and return its best program.
730
+
731
+ The new run is a fresh search that starts from the chosen candidate, with its own
732
+ budget. It reuses the completed run's training and validation data and its
733
+ configuration, so nothing is uploaded::
734
+
735
+ best = client.optimize_deployment(deployed, ..., max_metric_calls=120)
736
+ better = client.continue_optimization(best, max_metric_calls=120)
737
+
738
+ Args:
739
+ run: The completed run's ``op_`` id, or the ``DeployedProgram`` an
740
+ optimization returned.
741
+ candidate: The ``ca_`` id of the candidate to start from, as
742
+ ``list_candidates`` and ``cmpnd optimizations candidates`` list it. The
743
+ default is the run's best.
744
+ max_metric_calls: The budget as a ceiling on metric calls. Pass this or
745
+ ``auto``, exactly one.
746
+ auto: The budget as ``"light"``, ``"medium"`` or ``"heavy"``.
747
+ poll_interval: Seconds between status polls (default 3.0).
748
+ timeout: Polling timeout in seconds (default 600).
749
+
750
+ Returns:
751
+ The new run's best program, a ``cmpnd.DeployedProgram``, as
752
+ ``optimize_deployment`` returns.
753
+
754
+ Raises:
755
+ ValueError: No budget or two budgets, or a ``DeployedProgram`` that no
756
+ optimization produced.
757
+ OptimizeError: The platform refused the continuation. Its ``error_code``
758
+ says why, for example ``not_completed`` or ``unknown_candidate``.
759
+ OptimizeFailed: The new run transitioned to ``status="failed"``.
760
+ """
761
+ self._check_active()
762
+ from . import execution, optimization
763
+
764
+ run_id = run.optimization_run_id if isinstance(run, execution.DeployedProgram) else run
765
+ if not run_id:
766
+ raise ValueError(
767
+ f"deployment {run} is not the result of an optimization this client ran; pass the run's op_ id"
768
+ )
769
+ return optimization.continue_optimization(
770
+ run_id,
771
+ cfg=self._config,
772
+ candidate=candidate,
773
+ max_metric_calls=max_metric_calls,
774
+ auto=auto,
775
+ poll_interval=poll_interval,
776
+ timeout=timeout,
777
+ )
778
+
729
779
  # -- data surface (flat verb-object over the client's credentials) -----
730
780
  #
731
781
  # Flat methods (create_valset_entry, list_assessments, get_trace, …), so the
@@ -747,6 +797,22 @@ class Client:
747
797
  _exporter_pool.flush(self._config)
748
798
  return programs.program_id_for(_http.ApiClient(self._config.api_key, self._config.endpoint), program)
749
799
 
800
+ def list_candidates(self, run_id: str) -> list[dict[str, Any]]:
801
+ """The candidates of an optimization run, to choose one to continue from.
802
+
803
+ Each carries its ``candidate_id`` (``ca_…``), ``aggregate_score``,
804
+ ``parent_candidate_id`` and ``instructions``, so a caller picks by what a
805
+ candidate is::
806
+
807
+ candidates = client.list_candidates("op_…")
808
+ chosen = max(candidates, key=lambda c: c["aggregate_score"])
809
+ client.continue_optimization("op_…", candidate=chosen["candidate_id"], max_metric_calls=120)
810
+ """
811
+ self._check_active()
812
+ from . import optimization
813
+
814
+ return optimization.list_candidates(run_id, cfg=self._config)
815
+
750
816
  def get_deployment_signature(self, deployment_id: str) -> DeploymentSignatureResponse:
751
817
  """Fetch one deployment's persisted artifact metadata."""
752
818
  self._check_active()
@@ -114,8 +114,12 @@ class DeployedProgram:
114
114
  endpoint: str | None = None,
115
115
  score: float | None = None,
116
116
  deployment_signature: DeploymentSignatureResponse | None = None,
117
+ optimization_run_id: str | None = None,
117
118
  ) -> None:
118
119
  self.deployment_id = deployment_id
120
+ #: The optimization run that produced this program, when it is a run's result.
121
+ #: ``cmpnd.Client.continue_optimization`` takes such a program in place of a run id.
122
+ self.optimization_run_id = optimization_run_id
119
123
  self.deployment_signature = deployment_signature
120
124
  self.artifact_metadata: DeploymentArtifactMetadata | None = None
121
125
  self.metadata_unavailable_reason: str | None = None