cmpnd 0.80.0__tar.gz → 0.82.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 (405) hide show
  1. {cmpnd-0.80.0 → cmpnd-0.82.0}/PKG-INFO +4 -1
  2. {cmpnd-0.80.0 → cmpnd-0.82.0}/README.md +3 -0
  3. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/__init__.py +2 -1
  4. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/optimizations.py +220 -6
  5. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/client.py +3 -0
  6. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/deployment_runs.py +70 -22
  7. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/execution.py +3 -1
  8. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/cli.md +33 -2
  9. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/sdk-instrumentation.md +14 -2
  10. {cmpnd-0.80.0 → cmpnd-0.82.0}/pyproject.toml +1 -1
  11. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_optimizations.py +210 -0
  12. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deployment_runs.py +89 -0
  13. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_execute.py +31 -0
  14. {cmpnd-0.80.0 → cmpnd-0.82.0}/uv.lock +1 -1
  15. {cmpnd-0.80.0 → cmpnd-0.82.0}/.gitignore +0 -0
  16. {cmpnd-0.80.0 → cmpnd-0.82.0}/AGENTS.md +0 -0
  17. {cmpnd-0.80.0 → cmpnd-0.82.0}/CLAUDE.md +0 -0
  18. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_assessments.py +0 -0
  19. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_authored_import.py +0 -0
  20. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_deployments.py +0 -0
  21. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_exporter_pool.py +0 -0
  22. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_gepa_patch.py +0 -0
  23. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_http.py +0 -0
  24. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_program_patch.py +0 -0
  25. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_rlm_patch.py +0 -0
  26. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_runtime_tools.py +0 -0
  27. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_timespec.py +0 -0
  28. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_traces.py +0 -0
  29. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/_valsets.py +0 -0
  30. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/authored.py +0 -0
  31. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/callback.py +0 -0
  32. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/__init__.py +0 -0
  33. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/api_client.py +0 -0
  34. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/__init__.py +0 -0
  35. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/admin.py +0 -0
  36. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/assessments.py +0 -0
  37. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/auth.py +0 -0
  38. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/datasets.py +0 -0
  39. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/deployments.py +0 -0
  40. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/evals.py +0 -0
  41. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/login.py +0 -0
  42. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/org.py +0 -0
  43. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/programs.py +0 -0
  44. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  45. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  46. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/top.py +0 -0
  47. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/traces.py +0 -0
  48. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/commands/valsets.py +0 -0
  49. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/credentials.py +0 -0
  50. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/deprecation.py +0 -0
  51. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/pagination.py +0 -0
  52. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/shell.py +0 -0
  53. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/trace_render.py +0 -0
  54. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/tree_help.py +0 -0
  55. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/verbs.py +0 -0
  56. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/cli/views.py +0 -0
  57. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/configuration.py +0 -0
  58. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/context.py +0 -0
  59. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/credentials.py +0 -0
  60. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/dataset_sync.py +0 -0
  61. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/datasets.py +0 -0
  62. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/decorators.py +0 -0
  63. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/deployment.py +0 -0
  64. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/deployment_metadata.py +0 -0
  65. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/eval_handler.py +0 -0
  66. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/exporter.py +0 -0
  67. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/exporter_http.py +0 -0
  68. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/helpers.py +0 -0
  69. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/identity.py +0 -0
  70. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/imaging.py +0 -0
  71. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/__init__.py +0 -0
  72. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/decode.py +0 -0
  73. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/encode.py +0 -0
  74. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/hash.py +0 -0
  75. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/program.py +0 -0
  76. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/reflection.py +0 -0
  77. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/types.py +0 -0
  78. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/ir/xxh64.py +0 -0
  79. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/lm_configuration.py +0 -0
  80. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/mcp_servers.py +0 -0
  81. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/models.py +0 -0
  82. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/named_programs.py +0 -0
  83. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimization.py +0 -0
  84. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimizers/__init__.py +0 -0
  85. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimizers/gepa.py +0 -0
  86. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  87. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimizers/resume.py +0 -0
  88. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/optimizers/tracker.py +0 -0
  89. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/packaging.py +0 -0
  90. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/program_naming.py +0 -0
  91. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/programs.py +0 -0
  92. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/references.py +0 -0
  93. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/retry.py +0 -0
  94. {cmpnd-0.80.0 → cmpnd-0.82.0}/cmpnd/traces.py +0 -0
  95. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/2026-04-02-code-review.md +0 -0
  96. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/client.md +0 -0
  97. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/custom-proposer.md +0 -0
  98. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  99. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  100. {cmpnd-0.80.0 → cmpnd-0.82.0}/docs/reading-traces.md +0 -0
  101. {cmpnd-0.80.0 → cmpnd-0.82.0}/examples/generalizing_proposer.py +0 -0
  102. {cmpnd-0.80.0 → cmpnd-0.82.0}/examples/local_ollama.py +0 -0
  103. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/LINEAGE.md +0 -0
  104. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/README.md +0 -0
  105. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  106. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  107. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  108. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  109. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  110. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  111. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/py.typed +0 -0
  112. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  113. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/retry.py +0 -0
  114. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  115. {cmpnd-0.80.0 → cmpnd-0.82.0}/ingest/pyproject.toml +0 -0
  116. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/audit-sdist.sh +0 -0
  117. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/capture_review_run.py +0 -0
  118. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/check-pip-install.sh +0 -0
  119. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/check_release_artifacts.py +0 -0
  120. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/fixtures/review_run.json.gz +0 -0
  121. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/gepa_resume_bug.py +0 -0
  122. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/install-test-env.sh +0 -0
  123. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/release-gate.sh +0 -0
  124. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/release-real-models.sh +0 -0
  125. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/require_version_bump.py +0 -0
  126. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/seed_review_run.py +0 -0
  127. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/selfhost_compat.py +0 -0
  128. {cmpnd-0.80.0 → cmpnd-0.82.0}/scripts/test-in-linux.sh +0 -0
  129. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/__init__.pyi +0 -0
  130. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  131. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  132. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/utils/__init__.pyi +0 -0
  133. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/utils/callback.pyi +0 -0
  134. {cmpnd-0.80.0 → cmpnd-0.82.0}/stubs/dspy/utils/dummies.pyi +0 -0
  135. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/__init__.py +0 -0
  136. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_credential_route.py +0 -0
  137. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_deployment_signature_helpers.py +0 -0
  138. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_exporter_helpers.py +0 -0
  139. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_fake_clock.py +0 -0
  140. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_identity_helpers.py +0 -0
  141. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_resolve_refs.py +0 -0
  142. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/_traced_run.py +0 -0
  143. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/conftest.py +0 -0
  144. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/__init__.py +0 -0
  145. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/committee_task.py +0 -0
  146. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/conftest.py +0 -0
  147. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/continuation_task.py +0 -0
  148. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  149. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  150. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/fake_lm.py +0 -0
  151. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/helpers.py +0 -0
  152. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/target_capabilities.py +0 -0
  153. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_assessments_cli.py +0 -0
  154. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_capture_prompts.py +0 -0
  155. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_composite_module.py +0 -0
  156. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  157. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_execute.py +0 -0
  158. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_pandas.py +0 -0
  159. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_programs.py +0 -0
  160. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_edge_cases.py +0 -0
  161. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_example_hash.py +0 -0
  162. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_gepa_optimization.py +0 -0
  163. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_interpreter_spans.py +0 -0
  164. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_lm_accuracy.py +0 -0
  165. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_module_tracing.py +0 -0
  166. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimization_lineage.py +0 -0
  167. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  168. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  169. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_continuation.py +0 -0
  170. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  171. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_failures.py +0 -0
  172. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_loop.py +0 -0
  173. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  174. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  175. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_parallel_export.py +0 -0
  176. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_project_attribution.py +0 -0
  177. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_react_tool_spans.py +0 -0
  178. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_save_load.py +0 -0
  179. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_trace_structure.py +0 -0
  180. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_traces_read.py +0 -0
  181. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_user_attribution.py +0 -0
  182. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/e2e/test_valsets_cli.py +0 -0
  183. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  184. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  185. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  186. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  187. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  188. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  189. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  190. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  191. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  192. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/10_react.json +0 -0
  193. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/11_refine.json +0 -0
  194. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/12_retrieve.json +0 -0
  195. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/13_knn.json +0 -0
  196. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/14_parallel.json +0 -0
  197. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/identity_vectors/README.md +0 -0
  198. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/ingest_conformance.py +0 -0
  199. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/__init__.py +0 -0
  200. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/authored_optimize/judge.py +0 -0
  201. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  202. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/conftest.py +0 -0
  203. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  204. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/hosted_optimize_probe.py +0 -0
  205. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/optimization_run_probe.py +0 -0
  206. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  207. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/test_logs_renders_traces.py +0 -0
  208. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/test_optimization_run.py +0 -0
  209. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/test_optimizing_progress.py +0 -0
  210. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/integration/test_unhappy_path.py +0 -0
  211. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/__init__.py +0 -0
  212. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/__main__.py +0 -0
  213. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/drain.py +0 -0
  214. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/ingest.py +0 -0
  215. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/latency_transport.py +0 -0
  216. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/rlm_shape.py +0 -0
  217. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/rlm_size_report.py +0 -0
  218. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/sweep.py +0 -0
  219. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/test_rlm_shape.py +0 -0
  220. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/load/test_rlm_size_report.py +0 -0
  221. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/__init__.py +0 -0
  222. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/conftest.py +0 -0
  223. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/seed_parity.py +0 -0
  224. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_auth.py +0 -0
  225. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_datasets.py +0 -0
  226. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_deployments.py +0 -0
  227. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_evals.py +0 -0
  228. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_health.py +0 -0
  229. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_modules.py +0 -0
  230. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_optimizations.py +0 -0
  231. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_programs.py +0 -0
  232. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_signatures.py +0 -0
  233. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_trace_streaming.py +0 -0
  234. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/parity/test_traces.py +0 -0
  235. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/__init__.py +0 -0
  236. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/_provider.py +0 -0
  237. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/_readiness.py +0 -0
  238. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/_reliability.py +0 -0
  239. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/_result.py +0 -0
  240. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/mint_bedrock_token.py +0 -0
  241. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_bedrock_converse.py +0 -0
  242. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_deploy_programs.py +0 -0
  243. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_multi_client_stress.py +0 -0
  244. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  245. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_continuation.py +0 -0
  246. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_loop.py +0 -0
  247. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  248. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_stress.py +0 -0
  249. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_provider_roles.py +0 -0
  250. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_readiness.py +0 -0
  251. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_real_provider_twins.py +0 -0
  252. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_reliability.py +0 -0
  253. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_reliability_classify.py +0 -0
  254. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_rlm_wasm.py +0 -0
  255. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/real_models/test_trace_search.py +0 -0
  256. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/span_reference_fixture.py +0 -0
  257. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_adapter_reentrancy.py +0 -0
  258. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_assessments_api.py +0 -0
  259. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored.py +0 -0
  260. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored_binding_credentials.py +0 -0
  261. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored_bundle_collisions.py +0 -0
  262. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored_metric_contract.py +0 -0
  263. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored_named_models.py +0 -0
  264. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_authored_optimize.py +0 -0
  265. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_callback.py +0 -0
  266. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_candidate_lineage.py +0 -0
  267. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/__init__.py +0 -0
  268. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/conftest.py +0 -0
  269. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_admin.py +0 -0
  270. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_api_client.py +0 -0
  271. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_assessments.py +0 -0
  272. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_auth.py +0 -0
  273. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_credentials.py +0 -0
  274. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_datasets.py +0 -0
  275. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_deployment_runs.py +0 -0
  276. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_deployments.py +0 -0
  277. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_evals.py +0 -0
  278. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_login.py +0 -0
  279. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_main.py +0 -0
  280. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_org.py +0 -0
  281. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_pagination.py +0 -0
  282. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_parity.py +0 -0
  283. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_programs.py +0 -0
  284. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_provider_credentials.py +0 -0
  285. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_schema_sample.py +0 -0
  286. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_shell.py +0 -0
  287. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_tool_servers.py +0 -0
  288. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_top.py +0 -0
  289. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_traces.py +0 -0
  290. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  291. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  292. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_tree_help.py +0 -0
  293. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cli/test_valsets.py +0 -0
  294. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_client.py +0 -0
  295. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_client_floor.py +0 -0
  296. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_client_program_names.py +0 -0
  297. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_clustering/__init__.py +0 -0
  298. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_clustering/conftest.py +0 -0
  299. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_clustering/test_edge_cases.py +0 -0
  300. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  301. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_clustering/test_type_conversion.py +0 -0
  302. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_cmp551_grouping_identity.py +0 -0
  303. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_configuration.py +0 -0
  304. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_context.py +0 -0
  305. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_context_propagation.py +0 -0
  306. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_credential_kwargs.py +0 -0
  307. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_credential_selection.py +0 -0
  308. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_credential_values.py +0 -0
  309. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_dataset_polling.py +0 -0
  310. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_dataset_sync.py +0 -0
  311. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_datasets.py +0 -0
  312. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_decision_client_spans.py +0 -0
  313. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_decorators.py +0 -0
  314. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deploy.py +0 -0
  315. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  316. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deploy_program_id.py +0 -0
  317. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deployment_metadata.py +0 -0
  318. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_deployments_read.py +0 -0
  319. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_eval.py +0 -0
  320. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_eval_example_attribution.py +0 -0
  321. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_eval_program_id.py +0 -0
  322. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_eval_start.py +0 -0
  323. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_eval_trace_grouping.py +0 -0
  324. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_exception_paths.py +0 -0
  325. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_execute_run_identity.py +0 -0
  326. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_exporter.py +0 -0
  327. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_exporter_program_names.py +0 -0
  328. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_exporter_queue.py +0 -0
  329. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_exporter_real.py +0 -0
  330. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_fake_lm.py +0 -0
  331. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_gepa_callback.py +0 -0
  332. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_gepa_integration.py +0 -0
  333. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_gepa_output_normalization.py +0 -0
  334. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_gepa_resume.py +0 -0
  335. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_identity.py +0 -0
  336. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_imaging.py +0 -0
  337. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_import_weight.py +0 -0
  338. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_ingest_conformance.py +0 -0
  339. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_ingest_second_producer.py +0 -0
  340. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_integration.py +0 -0
  341. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_interpreter_callback.py +0 -0
  342. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_ir_encoder_properties.py +0 -0
  343. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_ir_golden_vectors.py +0 -0
  344. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_ir_roundtrip.py +0 -0
  345. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_lm15_native_calls.py +0 -0
  346. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_lm_configuration.py +0 -0
  347. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_lm_usage_shapes.py +0 -0
  348. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_mcp_tools.py +0 -0
  349. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_models.py +0 -0
  350. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_multi_client_stress_readiness.py +0 -0
  351. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
  352. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimization_phase_callback.py +0 -0
  353. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimization_program_names.py +0 -0
  354. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimization_progress.py +0 -0
  355. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimization_start.py +0 -0
  356. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize.py +0 -0
  357. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_budget.py +0 -0
  358. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_lift.py +0 -0
  359. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_lms.py +0 -0
  360. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_proposer.py +0 -0
  361. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_reflection_lm.py +0 -0
  362. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_optimize_terminal_failure.py +0 -0
  363. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_packaging.py +0 -0
  364. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_packaging_callable_source.py +0 -0
  365. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_packaging_mcp_tools.py +0 -0
  366. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_packaging_program_source.py +0 -0
  367. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_poll_until.py +0 -0
  368. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_program_id_deprecation.py +0 -0
  369. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_program_identity.py +0 -0
  370. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_program_lineage_persist.py +0 -0
  371. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_program_names_in_deploy.py +0 -0
  372. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_program_naming.py +0 -0
  373. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_programs_create.py +0 -0
  374. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_project_payload.py +0 -0
  375. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_real_provider_twins_readiness.py +0 -0
  376. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_reflector_properties.py +0 -0
  377. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_reflector_smoke.py +0 -0
  378. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_release_gate_script.py +0 -0
  379. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_release_real_models_script.py +0 -0
  380. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_replaced_names.py +0 -0
  381. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_repro_reactv2_bugs.py +0 -0
  382. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_require_version_bump.py +0 -0
  383. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_retry.py +0 -0
  384. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_reversible_failures.py +0 -0
  385. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_rlm_patch.py +0 -0
  386. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_run_attribution_paths.py +0 -0
  387. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_runtime_tools.py +0 -0
  388. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_schemas.py +0 -0
  389. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_seal_lm_credential.py +0 -0
  390. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_seed_review_run.py +0 -0
  391. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_signature_inputs.py +0 -0
  392. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_span_references.py +0 -0
  393. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_span_sent_once.py +0 -0
  394. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_system_one.py +0 -0
  395. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_trace_context_concurrency.py +0 -0
  396. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_trace_fact_wire_contract.py +0 -0
  397. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_trace_facts.py +0 -0
  398. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_trace_render.py +0 -0
  399. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_traces_read.py +0 -0
  400. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_tracked_gepa.py +0 -0
  401. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_tracker_enrich.py +0 -0
  402. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_truncation.py +0 -0
  403. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_upload_auth_headers.py +0 -0
  404. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_valsets_api.py +0 -0
  405. {cmpnd-0.80.0 → cmpnd-0.82.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.80.0
3
+ Version: 0.82.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>
@@ -722,6 +722,9 @@ errors:
722
722
 
723
723
  - A refused request raises `httpx.HTTPStatusError`.
724
724
  - A lost or failed submission reply raises `RunSubmissionUncertain`.
725
+ - A connection to the platform that never opened, such as one refused, raises
726
+ `RunNotSubmitted` after a few attempts. It names the endpoint, and nothing was
727
+ sent, so submitting again is safe. It is a `RunSubmissionUncertain`.
725
728
  - A spent budget raises `RunWaitTimeout`, which holds the run ID.
726
729
  - An answer that expired before it was read raises `RunResultExpired`.
727
730
 
@@ -671,6 +671,9 @@ errors:
671
671
 
672
672
  - A refused request raises `httpx.HTTPStatusError`.
673
673
  - A lost or failed submission reply raises `RunSubmissionUncertain`.
674
+ - A connection to the platform that never opened, such as one refused, raises
675
+ `RunNotSubmitted` after a few attempts. It names the endpoint, and nothing was
676
+ sent, so submitting again is safe. It is a `RunSubmissionUncertain`.
674
677
  - A spent budget raises `RunWaitTimeout`, which holds the run ID.
675
678
  - An answer that expired before it was read raises `RunResultExpired`.
676
679
 
@@ -37,7 +37,7 @@ from .deployment_metadata import (
37
37
  DeploymentSignatureResponse,
38
38
  UnsupportedDeploymentMetadataVersion,
39
39
  )
40
- from .deployment_runs import RunResultExpired, RunSubmissionUncertain, RunWaitTimeout
40
+ from .deployment_runs import RunNotSubmitted, RunResultExpired, RunSubmissionUncertain, RunWaitTimeout
41
41
  from .execution import DeployedProgram, RetryableProviderError
42
42
  from .exporter import flush_exporter, shutdown_exporter
43
43
  from .lm_configuration import HostedLM, SystemOne, configure_lm
@@ -120,6 +120,7 @@ __all__ = [
120
120
  "DeploymentMetadataError",
121
121
  "UnsupportedDeploymentMetadataVersion",
122
122
  "RunSubmissionUncertain",
123
+ "RunNotSubmitted",
123
124
  "RunWaitTimeout",
124
125
  "RunResultExpired",
125
126
  "DeployedProgram",
@@ -6,7 +6,7 @@ import sys
6
6
  from argparse import ArgumentParser, _SubParsersAction
7
7
  from typing import Any
8
8
 
9
- from .. import api_client, deprecation, pagination
9
+ from .. import api_client, deprecation, pagination, views
10
10
  from . import top
11
11
 
12
12
 
@@ -20,6 +20,7 @@ def _add_list_args(p: ArgumentParser) -> None:
20
20
  p.add_argument("--optimizer-type", help="Filter by optimizer type")
21
21
  p.add_argument("--min-score", type=float, help="Minimum best score")
22
22
  pagination.add_args(p, with_page_aliases=True, default_limit=50)
23
+ views.add_text_flag(p)
23
24
 
24
25
 
25
26
  def _add_comparability_args(p: ArgumentParser) -> None:
@@ -172,7 +173,8 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
172
173
  failed.set_defaults(func=handle_compare_failed_examples)
173
174
 
174
175
  iters = sub.add_parser("iterations", help="List iterations for an optimization run")
175
- iters.add_argument("id", help="Optimization run ID (UUID)")
176
+ iters.add_argument("id", help="Optimization run ID (op_...)")
177
+ views.add_text_flag(iters)
176
178
  iters.set_defaults(func=handle_iterations)
177
179
 
178
180
  cands = sub.add_parser(
@@ -187,6 +189,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
187
189
  "optimization actually wrote. Off by default: the text dwarfs the metrics, "
188
190
  "and listing candidates is usually how you find the one worth reading",
189
191
  )
192
+ views.add_text_flag(cands)
190
193
  cands.set_defaults(func=handle_candidates)
191
194
 
192
195
  compiled = sub.add_parser(
@@ -226,7 +229,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
226
229
 
227
230
  def handle_list(client: api_client.ApiClient, args: Any) -> Any:
228
231
  if getattr(args, "status", None) == "queued":
229
- return _list_queued(client, args)
232
+ payload = _list_queued(client, args)
233
+ if getattr(args, "text", False):
234
+ _render_queued(payload)
235
+ return None
236
+ return payload
230
237
  # This route paginates by page/per_page; the shared helper maps the
231
238
  # user-facing --limit/--offset onto that pair.
232
239
  body: dict[str, Any] = pagination.wire(args, "per_page")
@@ -239,7 +246,112 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
239
246
  val = getattr(args, attr, None)
240
247
  if val is not None:
241
248
  body[key] = val
242
- return client.post("/api/v1/optimizations/search", json=body)
249
+ payload = client.post("/api/v1/optimizations/search", json=body)
250
+ if getattr(args, "text", False):
251
+ _render_runs(payload)
252
+ return None
253
+ return payload
254
+
255
+
256
+ # The statuses of a run that has not settled. Serve's open set: `interrupted` is open and
257
+ # not progressing, and any delivered progress returns it to `running`.
258
+ _OPEN_STATUSES = ("running", "interrupted")
259
+
260
+ # What a cell shows for a value serve does not hold. Never a zero: a zero is a value.
261
+ _UNRECORDED = "-"
262
+
263
+
264
+ def _rows(payload: Any, key: str) -> list[dict[str, Any]]:
265
+ items = payload.get(key) if isinstance(payload, dict) else None
266
+ return [item for item in items or [] if isinstance(item, dict)]
267
+
268
+
269
+ def _table(header: tuple[str, ...], rows: list[tuple[str, ...]]) -> None:
270
+ """Print rows under a header, each column as wide as its widest cell."""
271
+ widths = [max(len(row[i]) for row in [header, *rows]) for i in range(len(header))]
272
+ for row in [header, *rows]:
273
+ print(" ".join(cell.ljust(widths[i]) for i, cell in enumerate(row)).rstrip())
274
+
275
+
276
+ def _score(value: Any) -> str:
277
+ return f"{value:.3f}" if isinstance(value, (int, float)) and not isinstance(value, bool) else _UNRECORDED
278
+
279
+
280
+ def _int(value: Any) -> str:
281
+ return str(value) if isinstance(value, int) and not isinstance(value, bool) else _UNRECORDED
282
+
283
+
284
+ def _metric_calls(run: dict[str, Any]) -> str:
285
+ """Metric calls spent of the budget. An open run reads its live counter, which serve
286
+ keeps the total in step with; a settled run reads its total."""
287
+ used = run.get("total_metric_calls")
288
+ live = run.get("current_metric_calls")
289
+ if isinstance(live, int) and (not isinstance(used, int) or live > used):
290
+ used = live
291
+ budget = run.get("max_metric_calls")
292
+ if not isinstance(used, int):
293
+ return _UNRECORDED
294
+ return f"{used}/{budget}" if isinstance(budget, int) and budget > 0 else str(used)
295
+
296
+
297
+ def _best_score(run: dict[str, Any]) -> str:
298
+ """The run's best score. Serve sends `best_score` as zero until the run records one,
299
+ and flags that with `best_score_recorded: false`. A serve that sends no flag leaves
300
+ an unrecorded score indistinguishable from a measured zero, so the score shows."""
301
+ if run.get("best_score_recorded") is False:
302
+ return _UNRECORDED
303
+ return _score(run.get("best_score"))
304
+
305
+
306
+ def _verdicts(run: dict[str, Any]) -> str:
307
+ """Accepted and rejected proposals, as the run's producer counted them. A run that
308
+ never reported them shows a dash rather than 0/0."""
309
+ accepted, rejected = run.get("candidates_accepted"), run.get("candidates_rejected")
310
+ if accepted is None and rejected is None:
311
+ return _UNRECORDED
312
+ return f"{_int(accepted)}/{_int(rejected)}"
313
+
314
+
315
+ def _render_runs(payload: Any) -> None:
316
+ runs = _rows(payload, "optimization_runs")
317
+ if not runs:
318
+ print("No optimization runs match.")
319
+ return
320
+ rows = [
321
+ (
322
+ str(run.get("run_id") or ""),
323
+ str(run.get("status") or ""),
324
+ str(run.get("program_name") or _UNRECORDED),
325
+ str(run.get("optimizer_type") or ""),
326
+ _metric_calls(run),
327
+ _verdicts(run),
328
+ _best_score(run),
329
+ str(run.get("start_time") or ""),
330
+ )
331
+ for run in runs
332
+ ]
333
+ _table(("RUN", "STATUS", "PROGRAM", "OPTIMIZER", "METRIC CALLS", "ACCEPTED/REJECTED", "BEST", "STARTED"), rows)
334
+ total = payload.get("total") if isinstance(payload, dict) else None
335
+ if isinstance(total, int) and total > len(runs):
336
+ print(f"{len(runs)} of {total} runs. Page with --limit and --offset.")
337
+
338
+
339
+ def _render_queued(payload: Any) -> None:
340
+ queued = _rows(payload, "optimizations")
341
+ if not queued:
342
+ print("No optimization runs are queued.")
343
+ return
344
+ rows = [
345
+ (
346
+ _int(run.get("position")),
347
+ str(run.get("optimization_id") or ""),
348
+ str(run.get("status") or "queued"),
349
+ str(run.get("optimizer") or ""),
350
+ str(run.get("created_at") or ""),
351
+ )
352
+ for run in queued
353
+ ]
354
+ _table(("POSITION", "RUN", "STATUS", "OPTIMIZER", "QUEUED AT"), rows)
243
355
 
244
356
 
245
357
  def _list_queued(client: api_client.ApiClient, args: Any) -> Any:
@@ -370,12 +482,114 @@ def handle_compare_failed_examples(client: api_client.ApiClient, args: Any) -> A
370
482
 
371
483
 
372
484
  def handle_iterations(client: api_client.ApiClient, args: Any) -> Any:
373
- return client.get(f"/api/v1/optimizations/{args.id}/iterations")
485
+ payload = client.get(f"/api/v1/optimizations/{args.id}/iterations")
486
+ if not getattr(args, "text", False):
487
+ return payload
488
+ _render_iterations(args.id, payload)
489
+ return None
490
+
491
+
492
+ def _verdict(iteration: dict[str, Any]) -> str:
493
+ accepted = iteration.get("accepted")
494
+ if accepted is None:
495
+ return _UNRECORDED
496
+ return "accepted" if accepted else "rejected"
497
+
498
+
499
+ def _result(iteration: dict[str, Any]) -> str:
500
+ """The candidate an accepted proposal became, with its validation score."""
501
+ idx = iteration.get("result_candidate_idx")
502
+ if not isinstance(idx, int):
503
+ return _UNRECORDED
504
+ score = iteration.get("validation_score")
505
+ return f"{idx} ({_score(score)})" if isinstance(score, (int, float)) else str(idx)
506
+
507
+
508
+ def _render_iterations(run_id: str, payload: Any) -> None:
509
+ """One row per iteration: the candidate it mutated and that candidate's minibatch mean,
510
+ the proposal's mean over the same minibatch, gepa's verdict, and the candidate an
511
+ accepted proposal became. A dash is a value serve does not hold for that iteration."""
512
+ iterations = _rows(payload, "iterations")
513
+ if not iterations:
514
+ print(f"{run_id} has no iterations yet.")
515
+ return
516
+ rows = [
517
+ (
518
+ _int(it.get("idx")),
519
+ _int(it.get("parent_candidate_idx")),
520
+ str(it.get("batch_size") or _UNRECORDED),
521
+ _score(it.get("parent_minibatch_score")),
522
+ _score(it.get("proposal_minibatch_score")),
523
+ _verdict(it),
524
+ _result(it),
525
+ str(it.get("duration") or _UNRECORDED),
526
+ )
527
+ for it in iterations
528
+ ]
529
+ _table(
530
+ ("ITER", "PARENT", "BATCH", "PARENT SCORE", "PROPOSAL SCORE", "VERDICT", "CANDIDATE", "DURATION"),
531
+ rows,
532
+ )
533
+ print(
534
+ "Scores are means over the iteration's minibatch. CANDIDATE is the candidate an accepted "
535
+ "proposal became, with its validation score."
536
+ )
537
+ if any(_UNRECORDED in row[1:] for row in rows):
538
+ print(
539
+ f"{_UNRECORDED}: not recorded for that iteration. An open run holds what its optimizer "
540
+ "has reported so far, and the settled record fills the rest."
541
+ )
374
542
 
375
543
 
376
544
  def handle_candidates(client: api_client.ApiClient, args: Any) -> Any:
377
545
  params = {"include": "instructions"} if getattr(args, "full", False) else None
378
- return client.get(f"/api/v1/optimizations/{args.id}/candidates", params=params)
546
+ payload = client.get(f"/api/v1/optimizations/{args.id}/candidates", params=params)
547
+ if not getattr(args, "text", False):
548
+ return payload
549
+ _render_candidates(args.id, payload, full=getattr(args, "full", False))
550
+ return None
551
+
552
+
553
+ def _origin(candidate: dict[str, Any]) -> str:
554
+ """How the candidate entered the pool. The seed starts it; every other candidate came
555
+ from an accepted proposal, a reflective mutation or a merge."""
556
+ if "accepted" not in candidate or candidate.get("mutation_type") == "seed":
557
+ return "seed"
558
+ return f"accepted ({candidate.get('mutation_type') or 'proposal'})"
559
+
560
+
561
+ def _render_candidates(run_id: str, payload: Any, *, full: bool) -> None:
562
+ candidates = _rows(payload, "candidates")
563
+ status = payload.get("run_status") if isinstance(payload, dict) else None
564
+ summary = [f"{run_id}: {status}" if status else run_id]
565
+ if isinstance(payload, dict) and ("candidates_accepted" in payload or "candidates_rejected" in payload):
566
+ summary.append(
567
+ f"{_int(payload.get('candidates_accepted'))} accepted, {_int(payload.get('candidates_rejected'))} rejected"
568
+ )
569
+ summary.append(f"{len(candidates)} candidate{'' if len(candidates) == 1 else 's'} listed")
570
+ print(" · ".join(summary))
571
+ if candidates:
572
+ rows = [
573
+ (
574
+ str(c.get("candidate_id") or ""),
575
+ _int(c.get("candidate_idx", c.get("idx"))),
576
+ _int(c.get("parent_candidate_idx")),
577
+ str(c.get("generation") or _UNRECORDED),
578
+ _score(c.get("aggregate_score")),
579
+ _origin(c),
580
+ "yes" if c.get("is_pareto_member") else "",
581
+ )
582
+ for c in candidates
583
+ ]
584
+ _table(("CANDIDATE", "IDX", "PARENT", "GEN", "VAL SCORE", "ORIGIN", "PARETO"), rows)
585
+ if full:
586
+ for c in candidates:
587
+ print(f"{c.get('candidate_id')}: {_first_line(_instruction_texts(c.get('instructions')))}")
588
+ if status in _OPEN_STATUSES:
589
+ print(
590
+ "The run is open: the list grows as the optimizer reports each candidate it accepts, "
591
+ "and the settled record completes it."
592
+ )
379
593
 
380
594
 
381
595
  def handle_compiled_program(client: api_client.ApiClient, args: Any) -> Any:
@@ -360,6 +360,9 @@ class Client:
360
360
  failure. No run is started again automatically.
361
361
  RunSubmissionUncertain: If the POST reply was lost or was a server
362
362
  error, so serve may have accepted the run.
363
+ RunNotSubmitted: If no connection to serve opened, after a few
364
+ attempts inside the budget. Nothing was sent. It is a
365
+ ``RunSubmissionUncertain``.
363
366
  RunWaitTimeout: If the run has not settled within the local budget.
364
367
  The run continues.
365
368
  RunResultExpired: If the retained answer expired before it was read.
@@ -1,9 +1,10 @@
1
1
  """Durable deployment run submission, discovery, and result retrieval.
2
2
 
3
- Submission sends one POST and never repeats it. A lost or failed reply may hide
4
- an accepted run, so callers inspect the run list before deciding whether to
5
- submit new work. Waiting reads the accepted run's status and result routes,
6
- retrying those reads inside one local budget.
3
+ Submission sends one POST and never repeats it once a connection opened. A lost or
4
+ failed reply may hide an accepted run, so callers inspect the run list before
5
+ deciding whether to submit new work. A connection that never opened sent nothing,
6
+ so the POST is tried again a few times inside the local budget. Waiting reads the
7
+ accepted run's status and result routes, retrying those reads inside one local budget.
7
8
  """
8
9
 
9
10
  from __future__ import annotations
@@ -40,6 +41,11 @@ _UNBOUNDED_POST_READ = 60.0
40
41
  # retried rather than consuming the whole budget.
41
42
  _READ_TIMEOUT = 30.0
42
43
  _FIRST_POLL_DELAY = 0.5
44
+ # Attempts to open the POST's connection, and the first pause between them, which
45
+ # doubles. A connection that never opened sent nothing, so another attempt cannot
46
+ # submit a second run.
47
+ _CONNECT_ATTEMPTS = 3
48
+ _FIRST_CONNECT_DELAY = 0.5
43
49
  _MAX_POLL_DELAY = 5.0
44
50
  # A server poll hint is honored up to this many seconds.
45
51
  _MAX_RETRY_AFTER = 60.0
@@ -100,6 +106,30 @@ class RunSubmissionUncertain(RuntimeError):
100
106
  _set_run_id(self, run_id)
101
107
 
102
108
 
109
+ class RunNotSubmitted(RunSubmissionUncertain):
110
+ """No connection to serve opened, so the POST sent nothing and no run exists.
111
+
112
+ ``endpoint`` is the address the SDK could not connect to, and the message gives
113
+ the transport's reason, such as a refused connection. Submitting again is safe.
114
+
115
+ A ``RunSubmissionUncertain`` so a caller that handles the uncertain outcome
116
+ still catches it. Its recovery, inspecting the run list before submitting
117
+ again, is unnecessary here but never wrong.
118
+ """
119
+
120
+ def __init__(self, deployment_id: str, endpoint: str, *, attempts: int, cause: BaseException):
121
+ reason = str(cause) or type(cause).__name__
122
+ RuntimeError.__init__(
123
+ self,
124
+ f"Deployment {deployment_id} was not submitted: no connection to {endpoint} opened "
125
+ f"in {attempts} attempt(s) ({reason}). Nothing was sent, so submitting again is safe.",
126
+ )
127
+ self.deployment_id = deployment_id
128
+ self.endpoint = endpoint
129
+ self.status_code = None
130
+ _set_run_id(self, None)
131
+
132
+
103
133
  def _cancel_command(run_id: str) -> str:
104
134
  return f"cmpnd deployments runs cancel {run_id}"
105
135
 
@@ -387,24 +417,42 @@ class _RunClient:
387
417
  return response
388
418
 
389
419
  def submit(self, deployment_id: str, body: dict[str, Any], deadline: float | None, *, wait: bool) -> dict[str, Any]:
390
- """Send the one POST. Never retried: a repeat would be a second paid run."""
391
- remaining = _remaining(deadline)
392
- if remaining is not None and remaining <= 0:
393
- raise TimeoutError(f"The local budget ran out before deployment {deployment_id} was submitted")
394
- prefer = server_wait(remaining) if wait else 0
395
- # The read timeout outlasts the server wait, so the transport never gives
396
- # up while serve is still holding an accepted run's reply.
397
- read_timeout = remaining if remaining is not None else prefer + _UNBOUNDED_POST_READ
398
- try:
399
- response = self._send(
400
- "POST",
401
- f"/api/v1/deployments/{deployment_id}/runs",
402
- timeout=read_timeout,
403
- body=body,
404
- headers={"Prefer": f"wait={prefer}"} if prefer > 0 else None,
405
- )
406
- except httpx.RequestError as exc:
407
- raise RunSubmissionUncertain(deployment_id) from exc
420
+ """Send the one POST.
421
+
422
+ Never repeated once a connection opened: a repeat would be a second paid run. A
423
+ connection that never opened sent nothing, so it is attempted again while the
424
+ budget allows, and then raises ``RunNotSubmitted``.
425
+ """
426
+ attempt = 0
427
+ while True:
428
+ attempt += 1
429
+ remaining = _remaining(deadline)
430
+ if remaining is not None and remaining <= 0:
431
+ raise TimeoutError(f"The local budget ran out before deployment {deployment_id} was submitted")
432
+ prefer = server_wait(remaining) if wait else 0
433
+ # The read timeout outlasts the server wait, so the transport never gives
434
+ # up while serve is still holding an accepted run's reply.
435
+ read_timeout = remaining if remaining is not None else prefer + _UNBOUNDED_POST_READ
436
+ try:
437
+ response = self._send(
438
+ "POST",
439
+ f"/api/v1/deployments/{deployment_id}/runs",
440
+ timeout=read_timeout,
441
+ body=body,
442
+ headers={"Prefer": f"wait={prefer}"} if prefer > 0 else None,
443
+ )
444
+ break
445
+ # httpx raises these two only while it opens a connection, before any byte
446
+ # of the request is written. Every other transport error may follow a sent
447
+ # request, so it leaves acceptance uncertain.
448
+ except (httpx.ConnectError, httpx.ConnectTimeout) as exc:
449
+ pause = _jitter(_FIRST_CONNECT_DELAY * 2 ** (attempt - 1))
450
+ left = _remaining(deadline)
451
+ if attempt >= _CONNECT_ATTEMPTS or (left is not None and left <= pause):
452
+ raise RunNotSubmitted(deployment_id, self.endpoint, attempts=attempt, cause=exc) from exc
453
+ _sleep(pause)
454
+ except httpx.RequestError as exc:
455
+ raise RunSubmissionUncertain(deployment_id) from exc
408
456
  if response.status_code >= 500:
409
457
  raise RunSubmissionUncertain(
410
458
  deployment_id,
@@ -291,9 +291,11 @@ def _raise_stored_failure(run_id: str, result: object, endpoint: str) -> None:
291
291
  provider_failure = _retryable_provider_failure(response)
292
292
  if provider_failure is not None:
293
293
  provider_status, attempts, retry_after = provider_failure
294
+ # The worker's status says only that the run failed, and the provider's own
295
+ # status is in the detail, so the message leaves the worker's out.
294
296
  raise _with_run_id(
295
297
  RetryableProviderError(
296
- message,
298
+ f"Deployment run {run_id} failed" + (f": {detail}" if detail else ""),
297
299
  request=request,
298
300
  response=response,
299
301
  provider_status=provider_status,
@@ -190,8 +190,9 @@ does the same.
190
190
  A namespaced read, such as `cmpnd traces show`, `cmpnd deployments list` or
191
191
  `cmpnd tool-servers list`, prints the API's JSON by default, so a script can
192
192
  pipe it to `jq`. A human view is an opt-in flag: `--tree` and `--timeline` on
193
- `traces show`, and `--text` on `tool-servers list`, `tool-servers show` and
194
- `credentials list`. Those three accept `--json` as well, so older scripts keep
193
+ `traces show`, and `--text` on `tool-servers list`, `tool-servers show`,
194
+ `credentials list`, `optimizations list`, `optimizations iterations` and
195
+ `optimizations candidates`. Those accept `--json` as well, so older scripts keep
195
196
  working. An action command may print a short result and take `--json` for the
196
197
  whole answer: `cmpnd programs create` prints the new id, and
197
198
  `cmpnd optimizations continue` prints the best deployment. The top-level
@@ -798,6 +799,36 @@ live objects and takes named models through its `lms` argument. An authored file
798
799
  binding cannot be combined with `lms` in the parent call or `config["lms"]`.
799
800
  The CLI has no named-model flag.
800
801
 
802
+ ### Watch a run
803
+
804
+ A hosted run reports its progress while it runs, so these reads show an open
805
+ run as it is. Each prints the API's JSON, and `--text` renders a table.
806
+
807
+ - `cmpnd optimizations list --text` shows each run's status, its metric calls
808
+ of the budget, its accepted and rejected proposals and its best score. The
809
+ JSON's `total_metric_calls` keeps pace with `current_metric_calls` while the
810
+ run is open, and `total_candidates` counts the candidates the platform holds
811
+ for it, which `candidates` lists. `best_score` reads 0 until the run
812
+ records a score, and `best_score_recorded` is `false` until then, so
813
+ `--text` shows a dash. `show` and `list` read the same row, so they agree.
814
+ - `cmpnd optimizations iterations RUN_ID` lists each iteration: the candidate
815
+ it mutated (`parent_candidate_idx`) and that candidate's mean over the
816
+ iteration's minibatch (`parent_minibatch_score`), the proposal's mean over the
817
+ same minibatch (`proposal_minibatch_score`), whether gepa `accepted` it, the
818
+ candidate an accepted proposal became (`result_candidate_idx`) with its
819
+ `validation_score`, and its `batch_size` and `duration_ms`. A field the
820
+ platform does not hold for an iteration is left out, and `--text` shows a
821
+ dash. A run the SDK tracks on your machine reports only each iteration's
822
+ number and score until it settles.
823
+ - `cmpnd optimizations candidates RUN_ID` lists every candidate the run holds,
824
+ beside its `run_status` and its `candidates_accepted` and
825
+ `candidates_rejected` counts. An open hosted run lists each candidate as the
826
+ optimizer accepts it, and the settled record completes the list and keeps
827
+ each `ca_` id. A run the SDK tracks lists its candidates once it settles.
828
+ The seed carries no `accepted`, because no verdict admitted it.
829
+ - `cmpnd optimizations show RUN_ID` names the run's `reflection_model_name`
830
+ from the moment it starts.
831
+
801
832
  ### Cancel a run
802
833
 
803
834
  `cmpnd optimizations cancel RUN_ID` stops an open run and keeps its record,
@@ -457,7 +457,15 @@ Serve's not-ready `409` and `422` refusals keep the `RuntimeError` type and
457
457
  message that `run_deployment()` raises, and are `httpx.HTTPStatusError` too. A
458
458
  lost POST reply or a server `5xx` leaves acceptance uncertain, so the SDK raises
459
459
  `RunSubmissionUncertain` with `status_code` set when serve answered. Its message
460
- names the command to list recent runs. Reads that time out or answer `408`,
460
+ names the command to list recent runs. A connection that never opened sent
461
+ nothing: httpx raises `ConnectError` or `ConnectTimeout` only before any byte of
462
+ the request is written. The SDK tries the POST up to three times, pausing about
463
+ half a second and then a second, and stops early when the pause would outlast
464
+ the budget. Then it raises `RunNotSubmitted`, which names the endpoint and the
465
+ transport's reason, such as a refused connection, and says submitting again is
466
+ safe. `RunNotSubmitted` subclasses `RunSubmissionUncertain`, so a handler for the
467
+ uncertain case still catches it. A read timeout, a write failure or a broken
468
+ reply may follow a sent request, so each stays uncertain. Reads that time out or answer `408`,
461
469
  `429` or `5xx` are retried with backoff and jitter, honoring `Retry-After`. A
462
470
  `401`, `403` or `404` read raises at once. A spent budget raises
463
471
  `RunWaitTimeout`, and the accepted run continues. Its `status` and
@@ -477,7 +485,11 @@ the same metadata, for a completed run's prediction and a failed run's error.
477
485
  trace, when the server names none, and when the run failed before serve
478
486
  accepted it. A
479
487
  retained provider failure keeps its structured `RetryableProviderError` type but
480
- does not start another run. Trace delivery can finish after the result is
488
+ does not start another run. Its message is the provider's outage as the egress
489
+ service reported it, such as `Deployment run dr_… failed: the provider at
490
+ gw.lunaroute.com was unavailable: 3 attempt(s) over 3m1.367s; its last answer was
491
+ 503: backend unavailable`. It leaves out the worker's status, which the exception's
492
+ `response` still carries, and the run's `error_code` is `ProviderUnavailable`. Trace delivery can finish after the result is
481
493
  available.
482
494
 
483
495
  Call `client.submit_deployment_run(deployment, inputs)` to accept work without a
@@ -5,7 +5,7 @@ name = "cmpnd"
5
5
  # it's new) — no git tags. Bump with `uv version --bump {minor,patch}` or by
6
6
  # hand. Runtime `cmpnd.__version__` reads installed metadata, so it tracks
7
7
  # this automatically. See docs/plans/2026-07-02-sdk-pypi-tag-free-publish.md.
8
- version = "0.80.0"
8
+ version = "0.82.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"