cmpnd 0.81.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.81.0 → cmpnd-0.82.0}/PKG-INFO +1 -1
  2. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/optimizations.py +220 -6
  3. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/cli.md +33 -2
  4. {cmpnd-0.81.0 → cmpnd-0.82.0}/pyproject.toml +1 -1
  5. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_optimizations.py +210 -0
  6. {cmpnd-0.81.0 → cmpnd-0.82.0}/uv.lock +1 -1
  7. {cmpnd-0.81.0 → cmpnd-0.82.0}/.gitignore +0 -0
  8. {cmpnd-0.81.0 → cmpnd-0.82.0}/AGENTS.md +0 -0
  9. {cmpnd-0.81.0 → cmpnd-0.82.0}/CLAUDE.md +0 -0
  10. {cmpnd-0.81.0 → cmpnd-0.82.0}/README.md +0 -0
  11. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/__init__.py +0 -0
  12. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_assessments.py +0 -0
  13. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_authored_import.py +0 -0
  14. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_deployments.py +0 -0
  15. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_exporter_pool.py +0 -0
  16. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_gepa_patch.py +0 -0
  17. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_http.py +0 -0
  18. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_program_patch.py +0 -0
  19. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_rlm_patch.py +0 -0
  20. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_runtime_tools.py +0 -0
  21. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_timespec.py +0 -0
  22. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_traces.py +0 -0
  23. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/_valsets.py +0 -0
  24. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/authored.py +0 -0
  25. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/callback.py +0 -0
  26. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/__init__.py +0 -0
  27. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/api_client.py +0 -0
  28. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/__init__.py +0 -0
  29. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/admin.py +0 -0
  30. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/assessments.py +0 -0
  31. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/auth.py +0 -0
  32. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/datasets.py +0 -0
  33. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/deployments.py +0 -0
  34. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/evals.py +0 -0
  35. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/login.py +0 -0
  36. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/org.py +0 -0
  37. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/programs.py +0 -0
  38. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  39. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  40. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/top.py +0 -0
  41. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/traces.py +0 -0
  42. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/commands/valsets.py +0 -0
  43. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/credentials.py +0 -0
  44. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/deprecation.py +0 -0
  45. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/pagination.py +0 -0
  46. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/shell.py +0 -0
  47. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/trace_render.py +0 -0
  48. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/tree_help.py +0 -0
  49. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/verbs.py +0 -0
  50. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/cli/views.py +0 -0
  51. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/client.py +0 -0
  52. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/configuration.py +0 -0
  53. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/context.py +0 -0
  54. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/credentials.py +0 -0
  55. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/dataset_sync.py +0 -0
  56. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/datasets.py +0 -0
  57. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/decorators.py +0 -0
  58. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/deployment.py +0 -0
  59. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/deployment_metadata.py +0 -0
  60. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/deployment_runs.py +0 -0
  61. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/eval_handler.py +0 -0
  62. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/execution.py +0 -0
  63. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/exporter.py +0 -0
  64. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/exporter_http.py +0 -0
  65. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/helpers.py +0 -0
  66. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/identity.py +0 -0
  67. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/imaging.py +0 -0
  68. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/__init__.py +0 -0
  69. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/decode.py +0 -0
  70. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/encode.py +0 -0
  71. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/hash.py +0 -0
  72. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/program.py +0 -0
  73. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/reflection.py +0 -0
  74. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/types.py +0 -0
  75. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/ir/xxh64.py +0 -0
  76. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/lm_configuration.py +0 -0
  77. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/mcp_servers.py +0 -0
  78. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/models.py +0 -0
  79. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/named_programs.py +0 -0
  80. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimization.py +0 -0
  81. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimizers/__init__.py +0 -0
  82. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimizers/gepa.py +0 -0
  83. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  84. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimizers/resume.py +0 -0
  85. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/optimizers/tracker.py +0 -0
  86. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/packaging.py +0 -0
  87. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/program_naming.py +0 -0
  88. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/programs.py +0 -0
  89. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/references.py +0 -0
  90. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/retry.py +0 -0
  91. {cmpnd-0.81.0 → cmpnd-0.82.0}/cmpnd/traces.py +0 -0
  92. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/2026-04-02-code-review.md +0 -0
  93. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/client.md +0 -0
  94. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/custom-proposer.md +0 -0
  95. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  96. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  97. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/reading-traces.md +0 -0
  98. {cmpnd-0.81.0 → cmpnd-0.82.0}/docs/sdk-instrumentation.md +0 -0
  99. {cmpnd-0.81.0 → cmpnd-0.82.0}/examples/generalizing_proposer.py +0 -0
  100. {cmpnd-0.81.0 → cmpnd-0.82.0}/examples/local_ollama.py +0 -0
  101. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/LINEAGE.md +0 -0
  102. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/README.md +0 -0
  103. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  104. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  105. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  106. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  107. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  108. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  109. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/py.typed +0 -0
  110. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  111. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/retry.py +0 -0
  112. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  113. {cmpnd-0.81.0 → cmpnd-0.82.0}/ingest/pyproject.toml +0 -0
  114. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/audit-sdist.sh +0 -0
  115. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/capture_review_run.py +0 -0
  116. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/check-pip-install.sh +0 -0
  117. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/check_release_artifacts.py +0 -0
  118. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/fixtures/review_run.json.gz +0 -0
  119. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/gepa_resume_bug.py +0 -0
  120. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/install-test-env.sh +0 -0
  121. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/release-gate.sh +0 -0
  122. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/release-real-models.sh +0 -0
  123. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/require_version_bump.py +0 -0
  124. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/seed_review_run.py +0 -0
  125. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/selfhost_compat.py +0 -0
  126. {cmpnd-0.81.0 → cmpnd-0.82.0}/scripts/test-in-linux.sh +0 -0
  127. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/__init__.pyi +0 -0
  128. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  129. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  130. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/utils/__init__.pyi +0 -0
  131. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/utils/callback.pyi +0 -0
  132. {cmpnd-0.81.0 → cmpnd-0.82.0}/stubs/dspy/utils/dummies.pyi +0 -0
  133. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/__init__.py +0 -0
  134. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_credential_route.py +0 -0
  135. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_deployment_signature_helpers.py +0 -0
  136. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_exporter_helpers.py +0 -0
  137. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_fake_clock.py +0 -0
  138. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_identity_helpers.py +0 -0
  139. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_resolve_refs.py +0 -0
  140. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/_traced_run.py +0 -0
  141. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/conftest.py +0 -0
  142. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/__init__.py +0 -0
  143. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/committee_task.py +0 -0
  144. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/conftest.py +0 -0
  145. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/continuation_task.py +0 -0
  146. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  147. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  148. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/fake_lm.py +0 -0
  149. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/helpers.py +0 -0
  150. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/target_capabilities.py +0 -0
  151. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_assessments_cli.py +0 -0
  152. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_capture_prompts.py +0 -0
  153. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_composite_module.py +0 -0
  154. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  155. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_execute.py +0 -0
  156. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_pandas.py +0 -0
  157. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_deploy_programs.py +0 -0
  158. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_edge_cases.py +0 -0
  159. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_example_hash.py +0 -0
  160. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_gepa_optimization.py +0 -0
  161. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_interpreter_spans.py +0 -0
  162. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_lm_accuracy.py +0 -0
  163. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_module_tracing.py +0 -0
  164. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimization_lineage.py +0 -0
  165. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  166. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  167. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_continuation.py +0 -0
  168. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  169. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_failures.py +0 -0
  170. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_loop.py +0 -0
  171. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  172. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  173. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_parallel_export.py +0 -0
  174. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_project_attribution.py +0 -0
  175. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_react_tool_spans.py +0 -0
  176. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_save_load.py +0 -0
  177. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_trace_structure.py +0 -0
  178. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_traces_read.py +0 -0
  179. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_user_attribution.py +0 -0
  180. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/e2e/test_valsets_cli.py +0 -0
  181. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  182. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  183. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  184. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  185. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  186. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  187. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  188. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  189. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  190. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/10_react.json +0 -0
  191. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/11_refine.json +0 -0
  192. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/12_retrieve.json +0 -0
  193. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/13_knn.json +0 -0
  194. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/14_parallel.json +0 -0
  195. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/identity_vectors/README.md +0 -0
  196. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/ingest_conformance.py +0 -0
  197. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/__init__.py +0 -0
  198. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/authored_optimize/judge.py +0 -0
  199. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  200. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/conftest.py +0 -0
  201. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  202. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/hosted_optimize_probe.py +0 -0
  203. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/optimization_run_probe.py +0 -0
  204. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  205. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/test_logs_renders_traces.py +0 -0
  206. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/test_optimization_run.py +0 -0
  207. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/test_optimizing_progress.py +0 -0
  208. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/integration/test_unhappy_path.py +0 -0
  209. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/__init__.py +0 -0
  210. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/__main__.py +0 -0
  211. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/drain.py +0 -0
  212. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/ingest.py +0 -0
  213. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/latency_transport.py +0 -0
  214. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/rlm_shape.py +0 -0
  215. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/rlm_size_report.py +0 -0
  216. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/sweep.py +0 -0
  217. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/test_rlm_shape.py +0 -0
  218. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/load/test_rlm_size_report.py +0 -0
  219. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/__init__.py +0 -0
  220. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/conftest.py +0 -0
  221. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/seed_parity.py +0 -0
  222. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_auth.py +0 -0
  223. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_datasets.py +0 -0
  224. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_deployments.py +0 -0
  225. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_evals.py +0 -0
  226. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_health.py +0 -0
  227. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_modules.py +0 -0
  228. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_optimizations.py +0 -0
  229. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_programs.py +0 -0
  230. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_signatures.py +0 -0
  231. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_trace_streaming.py +0 -0
  232. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/parity/test_traces.py +0 -0
  233. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/__init__.py +0 -0
  234. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/_provider.py +0 -0
  235. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/_readiness.py +0 -0
  236. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/_reliability.py +0 -0
  237. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/_result.py +0 -0
  238. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/mint_bedrock_token.py +0 -0
  239. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_bedrock_converse.py +0 -0
  240. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_deploy_programs.py +0 -0
  241. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_multi_client_stress.py +0 -0
  242. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  243. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_continuation.py +0 -0
  244. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_loop.py +0 -0
  245. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  246. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_optimize_stress.py +0 -0
  247. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_provider_roles.py +0 -0
  248. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_readiness.py +0 -0
  249. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_real_provider_twins.py +0 -0
  250. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_reliability.py +0 -0
  251. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_reliability_classify.py +0 -0
  252. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_rlm_wasm.py +0 -0
  253. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/real_models/test_trace_search.py +0 -0
  254. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/span_reference_fixture.py +0 -0
  255. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_adapter_reentrancy.py +0 -0
  256. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_assessments_api.py +0 -0
  257. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored.py +0 -0
  258. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored_binding_credentials.py +0 -0
  259. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored_bundle_collisions.py +0 -0
  260. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored_metric_contract.py +0 -0
  261. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored_named_models.py +0 -0
  262. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_authored_optimize.py +0 -0
  263. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_callback.py +0 -0
  264. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_candidate_lineage.py +0 -0
  265. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/__init__.py +0 -0
  266. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/conftest.py +0 -0
  267. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_admin.py +0 -0
  268. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_api_client.py +0 -0
  269. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_assessments.py +0 -0
  270. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_auth.py +0 -0
  271. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_credentials.py +0 -0
  272. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_datasets.py +0 -0
  273. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_deployment_runs.py +0 -0
  274. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_deployments.py +0 -0
  275. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_evals.py +0 -0
  276. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_login.py +0 -0
  277. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_main.py +0 -0
  278. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_org.py +0 -0
  279. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_pagination.py +0 -0
  280. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_parity.py +0 -0
  281. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_programs.py +0 -0
  282. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_provider_credentials.py +0 -0
  283. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_schema_sample.py +0 -0
  284. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_shell.py +0 -0
  285. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_tool_servers.py +0 -0
  286. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_top.py +0 -0
  287. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_traces.py +0 -0
  288. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  289. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  290. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_tree_help.py +0 -0
  291. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cli/test_valsets.py +0 -0
  292. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_client.py +0 -0
  293. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_client_floor.py +0 -0
  294. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_client_program_names.py +0 -0
  295. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_clustering/__init__.py +0 -0
  296. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_clustering/conftest.py +0 -0
  297. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_clustering/test_edge_cases.py +0 -0
  298. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  299. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_clustering/test_type_conversion.py +0 -0
  300. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_cmp551_grouping_identity.py +0 -0
  301. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_configuration.py +0 -0
  302. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_context.py +0 -0
  303. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_context_propagation.py +0 -0
  304. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_credential_kwargs.py +0 -0
  305. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_credential_selection.py +0 -0
  306. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_credential_values.py +0 -0
  307. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_dataset_polling.py +0 -0
  308. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_dataset_sync.py +0 -0
  309. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_datasets.py +0 -0
  310. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_decision_client_spans.py +0 -0
  311. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_decorators.py +0 -0
  312. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deploy.py +0 -0
  313. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  314. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deploy_program_id.py +0 -0
  315. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deployment_metadata.py +0 -0
  316. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deployment_runs.py +0 -0
  317. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_deployments_read.py +0 -0
  318. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_eval.py +0 -0
  319. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_eval_example_attribution.py +0 -0
  320. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_eval_program_id.py +0 -0
  321. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_eval_start.py +0 -0
  322. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_eval_trace_grouping.py +0 -0
  323. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_exception_paths.py +0 -0
  324. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_execute.py +0 -0
  325. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_execute_run_identity.py +0 -0
  326. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_exporter.py +0 -0
  327. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_exporter_program_names.py +0 -0
  328. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_exporter_queue.py +0 -0
  329. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_exporter_real.py +0 -0
  330. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_fake_lm.py +0 -0
  331. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_gepa_callback.py +0 -0
  332. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_gepa_integration.py +0 -0
  333. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_gepa_output_normalization.py +0 -0
  334. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_gepa_resume.py +0 -0
  335. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_identity.py +0 -0
  336. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_imaging.py +0 -0
  337. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_import_weight.py +0 -0
  338. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_ingest_conformance.py +0 -0
  339. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_ingest_second_producer.py +0 -0
  340. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_integration.py +0 -0
  341. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_interpreter_callback.py +0 -0
  342. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_ir_encoder_properties.py +0 -0
  343. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_ir_golden_vectors.py +0 -0
  344. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_ir_roundtrip.py +0 -0
  345. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_lm15_native_calls.py +0 -0
  346. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_lm_configuration.py +0 -0
  347. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_lm_usage_shapes.py +0 -0
  348. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_mcp_tools.py +0 -0
  349. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_models.py +0 -0
  350. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_multi_client_stress_readiness.py +0 -0
  351. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
  352. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimization_phase_callback.py +0 -0
  353. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimization_program_names.py +0 -0
  354. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimization_progress.py +0 -0
  355. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimization_start.py +0 -0
  356. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize.py +0 -0
  357. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_budget.py +0 -0
  358. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_lift.py +0 -0
  359. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_lms.py +0 -0
  360. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_proposer.py +0 -0
  361. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_reflection_lm.py +0 -0
  362. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_optimize_terminal_failure.py +0 -0
  363. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_packaging.py +0 -0
  364. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_packaging_callable_source.py +0 -0
  365. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_packaging_mcp_tools.py +0 -0
  366. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_packaging_program_source.py +0 -0
  367. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_poll_until.py +0 -0
  368. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_program_id_deprecation.py +0 -0
  369. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_program_identity.py +0 -0
  370. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_program_lineage_persist.py +0 -0
  371. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_program_names_in_deploy.py +0 -0
  372. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_program_naming.py +0 -0
  373. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_programs_create.py +0 -0
  374. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_project_payload.py +0 -0
  375. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_real_provider_twins_readiness.py +0 -0
  376. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_reflector_properties.py +0 -0
  377. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_reflector_smoke.py +0 -0
  378. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_release_gate_script.py +0 -0
  379. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_release_real_models_script.py +0 -0
  380. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_replaced_names.py +0 -0
  381. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_repro_reactv2_bugs.py +0 -0
  382. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_require_version_bump.py +0 -0
  383. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_retry.py +0 -0
  384. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_reversible_failures.py +0 -0
  385. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_rlm_patch.py +0 -0
  386. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_run_attribution_paths.py +0 -0
  387. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_runtime_tools.py +0 -0
  388. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_schemas.py +0 -0
  389. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_seal_lm_credential.py +0 -0
  390. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_seed_review_run.py +0 -0
  391. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_signature_inputs.py +0 -0
  392. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_span_references.py +0 -0
  393. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_span_sent_once.py +0 -0
  394. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_system_one.py +0 -0
  395. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_trace_context_concurrency.py +0 -0
  396. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_trace_fact_wire_contract.py +0 -0
  397. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_trace_facts.py +0 -0
  398. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_trace_render.py +0 -0
  399. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_traces_read.py +0 -0
  400. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_tracked_gepa.py +0 -0
  401. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_tracker_enrich.py +0 -0
  402. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_truncation.py +0 -0
  403. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_upload_auth_headers.py +0 -0
  404. {cmpnd-0.81.0 → cmpnd-0.82.0}/tests/test_valsets_api.py +0 -0
  405. {cmpnd-0.81.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.81.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>
@@ -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:
@@ -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,
@@ -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.81.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"
@@ -438,3 +438,213 @@ class TestOptimizationsCompareFailedExamples:
438
438
  parser = _build_parser()
439
439
  with pytest.raises(SystemExit):
440
440
  parser.parse_args(["optimizations", "failed-examples"])
441
+
442
+
443
+ # An open GEPA run 28 minutes in, with a budget of 30 metric calls, as serve answers it
444
+ # once the heartbeat reports its iterations and candidates.
445
+ _OPEN_RUN = {
446
+ "run_id": "op_OpenBBBBBBBB",
447
+ "status": "running",
448
+ "program_name": "headline-ranker",
449
+ "optimizer_type": "gepa",
450
+ "model_name": "openai/deepseek-4.1-flash",
451
+ "reflection_model_name": "openai/deepseek-4.1-flash",
452
+ "start_time": "2026-11-04T11:26:02Z",
453
+ "total_metric_calls": 18,
454
+ "current_metric_calls": 18,
455
+ "max_metric_calls": 30,
456
+ "total_candidates": 2,
457
+ "candidates_accepted": 1,
458
+ "candidates_rejected": 1,
459
+ "best_score": 0.557,
460
+ }
461
+ _SETTLED_RUN = {
462
+ "run_id": "op_SettledAAAAA",
463
+ "status": "completed",
464
+ "program_name": "headline-ranker",
465
+ "optimizer_type": "gepa",
466
+ "start_time": "2026-11-03T09:00:00Z",
467
+ "total_metric_calls": 33,
468
+ "max_metric_calls": 30,
469
+ "total_candidates": 3,
470
+ "best_score": 0.567,
471
+ }
472
+
473
+
474
+ def _text(argv: list[str]) -> argparse.Namespace:
475
+ return _build_parser().parse_args(["optimizations", *argv])
476
+
477
+
478
+ class TestOptimizationsListText:
479
+ def test_json_stays_the_default(self, mock_client):
480
+ mock_client.post.return_value = {"optimization_runs": [_OPEN_RUN]}
481
+ result = handle_list(mock_client, _text(["list"]))
482
+ assert result == {"optimization_runs": [_OPEN_RUN]}
483
+
484
+ def test_a_row_shows_its_status_and_an_open_runs_live_progress(self, mock_client, capsys):
485
+ mock_client.post.return_value = {"optimization_runs": [_OPEN_RUN, _SETTLED_RUN], "total": 2}
486
+ assert handle_list(mock_client, _text(["list", "--text"])) is None
487
+ lines = capsys.readouterr().out.splitlines()
488
+ header, open_row, settled_row = lines[0], lines[1], lines[2]
489
+ for column in ("RUN", "STATUS", "PROGRAM", "METRIC CALLS", "ACCEPTED/REJECTED", "BEST"):
490
+ assert column in header, header
491
+ assert "op_OpenBBBBBBBB" in open_row and "running" in open_row
492
+ assert "18/30" in open_row and "1/1" in open_row and "0.557" in open_row
493
+ assert "op_SettledAAAAA" in settled_row and "completed" in settled_row
494
+ assert "33/30" in settled_row and "0.567" in settled_row
495
+
496
+ def test_a_counter_the_run_never_reported_reads_as_a_dash(self, mock_client, capsys):
497
+ run = {k: v for k, v in _OPEN_RUN.items() if k not in ("candidates_accepted", "candidates_rejected")}
498
+ mock_client.post.return_value = {"optimization_runs": [run]}
499
+ handle_list(mock_client, _text(["list", "--text"]))
500
+ row = capsys.readouterr().out.splitlines()[1]
501
+ assert " - " in row and "0/0" not in row, row
502
+
503
+ def test_a_best_score_the_run_has_not_recorded_reads_as_a_dash(self, mock_client, capsys):
504
+ unrecorded = {**_OPEN_RUN, "best_score": 0, "best_score_recorded": False}
505
+ measured_zero = {**_SETTLED_RUN, "best_score": 0, "best_score_recorded": True}
506
+ mock_client.post.return_value = {"optimization_runs": [unrecorded, measured_zero]}
507
+ handle_list(mock_client, _text(["list", "--text"]))
508
+ lines = capsys.readouterr().out.splitlines()
509
+ header, open_row, settled_row = lines[0], lines[1], lines[2]
510
+ best = header.index("BEST")
511
+ assert open_row[best:].split()[0] == "-", open_row
512
+ assert settled_row[best:].split()[0] == "0.000", settled_row
513
+
514
+ def test_an_older_serve_without_the_flag_keeps_showing_its_score(self, mock_client, capsys):
515
+ older = {k: v for k, v in _OPEN_RUN.items() if k != "best_score_recorded"}
516
+ mock_client.post.return_value = {"optimization_runs": [{**older, "best_score": 0}]}
517
+ handle_list(mock_client, _text(["list", "--text"]))
518
+ header, row = capsys.readouterr().out.splitlines()[:2]
519
+ assert row[header.index("BEST") :].split()[0] == "0.000", row
520
+
521
+ def test_queued_runs_render_in_line_order(self, mock_client, capsys):
522
+ mock_client.get.return_value = {
523
+ "optimizations": [
524
+ {"optimization_id": "op_1", "status": "queued", "optimizer": "gepa", "position": 1},
525
+ ]
526
+ }
527
+ handle_list(mock_client, _text(["list", "--status", "queued", "--text"]))
528
+ out = capsys.readouterr().out
529
+ assert "op_1" in out and "queued" in out and "gepa" in out
530
+
531
+
532
+ class TestOptimizationsIterationsText:
533
+ _ITERATIONS = {
534
+ "iterations": [
535
+ # A point from a producer that reported only the score.
536
+ {"idx": 1, "label": "Iter 1", "score": "0.560", "parent_minibatch_score": 0.56, "eval_trace_ids": []},
537
+ {
538
+ "idx": 2,
539
+ "label": "Iter 2 · candidate 1",
540
+ "score": "0.604",
541
+ "candidate_idx": "1",
542
+ "parent_candidate_idx": 1,
543
+ "parent_minibatch_score": 0.6036,
544
+ "proposal_minibatch_score": 0.3895,
545
+ "accepted": False,
546
+ "batch_size": "3",
547
+ "duration": "6m 52s",
548
+ "duration_ms": 412000,
549
+ "eval_trace_ids": [],
550
+ },
551
+ {
552
+ "idx": 3,
553
+ "label": "Iter 3 · candidate 0",
554
+ "score": "0.389",
555
+ "candidate_idx": "0",
556
+ "parent_candidate_idx": 0,
557
+ "parent_minibatch_score": 0.389,
558
+ "proposal_minibatch_score": 0.7,
559
+ "accepted": True,
560
+ "result_candidate_idx": 2,
561
+ "validation_score": 0.567,
562
+ "batch_size": "3",
563
+ "duration": "5m 1s",
564
+ "duration_ms": 301000,
565
+ "eval_trace_ids": [],
566
+ },
567
+ ],
568
+ "total": 3,
569
+ }
570
+
571
+ def test_json_stays_the_default(self, mock_client):
572
+ mock_client.get.return_value = self._ITERATIONS
573
+ assert handle_iterations(mock_client, _text(["iterations", "op_1"])) == self._ITERATIONS
574
+
575
+ def test_each_iteration_says_what_it_did(self, mock_client, capsys):
576
+ mock_client.get.return_value = self._ITERATIONS
577
+ assert handle_iterations(mock_client, _text(["iterations", "op_1", "--text"])) is None
578
+ lines = capsys.readouterr().out.splitlines()
579
+ header = lines[0]
580
+ for column in ("ITER", "PARENT", "BATCH", "PARENT SCORE", "PROPOSAL SCORE", "VERDICT", "CANDIDATE", "DURATION"):
581
+ assert column in header, header
582
+ rejected, accepted = lines[2], lines[3]
583
+ assert "0.604" in rejected and "0.390" in rejected and "rejected" in rejected and "6m 52s" in rejected
584
+ assert "accepted" in accepted and "0.700" in accepted and "5m 1s" in accepted
585
+ # The accepted proposal became candidate 2, with its validation score.
586
+ assert "2 (0.567)" in accepted, accepted
587
+
588
+ def test_what_serve_does_not_record_is_never_shown_as_zero(self, mock_client, capsys):
589
+ mock_client.get.return_value = self._ITERATIONS
590
+ handle_iterations(mock_client, _text(["iterations", "op_1", "--text"]))
591
+ out = capsys.readouterr().out
592
+ thin = out.splitlines()[1]
593
+ assert "0µs" not in thin and " 0 " not in thin.replace("0.560", ""), thin
594
+ assert "not recorded" in out
595
+
596
+
597
+ class TestOptimizationsCandidatesText:
598
+ _CANDIDATES = {
599
+ "candidates": [
600
+ {
601
+ "candidate_id": "ca_seedAAAAAAAA",
602
+ "candidate_idx": 0,
603
+ "idx": 0,
604
+ "parent_candidate_id": "",
605
+ "aggregate_score": 0.463,
606
+ "generation": "0",
607
+ "mutation_type": "seed",
608
+ "is_pareto_member": False,
609
+ },
610
+ {
611
+ "candidate_id": "ca_nextAAAAAAAA",
612
+ "candidate_idx": 1,
613
+ "idx": 1,
614
+ "parent_candidate_id": "ca_seedAAAAAAAA",
615
+ "parent_candidate_idx": 0,
616
+ "aggregate_score": 0.557,
617
+ "accepted": True,
618
+ "generation": "1",
619
+ "mutation_type": "reflective",
620
+ "is_pareto_member": True,
621
+ },
622
+ ],
623
+ "total": 2,
624
+ "run_status": "running",
625
+ "candidates_accepted": 1,
626
+ "candidates_rejected": 1,
627
+ }
628
+
629
+ def test_json_stays_the_default(self, mock_client):
630
+ mock_client.get.return_value = self._CANDIDATES
631
+ assert handle_candidates(mock_client, _text(["candidates", "op_1"])) == self._CANDIDATES
632
+
633
+ def test_an_open_run_says_its_verdict_counts_and_lists_its_candidates(self, mock_client, capsys):
634
+ mock_client.get.return_value = self._CANDIDATES
635
+ assert handle_candidates(mock_client, _text(["candidates", "op_1", "--text"])) is None
636
+ lines = capsys.readouterr().out.splitlines()
637
+ assert "running" in lines[0] and "1 accepted" in lines[0] and "1 rejected" in lines[0], lines[0]
638
+ table = "\n".join(lines)
639
+ seed = next(line for line in lines if "ca_seedAAAAAAAA" in line)
640
+ child = next(line for line in lines if "ca_nextAAAAAAAA" in line)
641
+ assert "seed" in seed and "rejected" not in seed, seed
642
+ assert "0.557" in child and "accepted" in child, child
643
+ assert "open" in table, "an open run's list says it grows"
644
+
645
+ def test_full_prints_each_candidates_first_instruction_line(self, mock_client, capsys):
646
+ payload = json.loads(json.dumps(self._CANDIDATES))
647
+ payload["candidates"][1]["instructions"] = {"predict": "Spot rising trends.\nThen rank them."}
648
+ mock_client.get.return_value = payload
649
+ handle_candidates(mock_client, _text(["candidates", "op_1", "--full", "--text"]))
650
+ assert "Spot rising trends." in capsys.readouterr().out
@@ -505,7 +505,7 @@ wheels = [
505
505
 
506
506
  [[package]]
507
507
  name = "cmpnd"
508
- version = "0.81.0"
508
+ version = "0.82.0"
509
509
  source = { editable = "." }
510
510
  default-groups = ["dspy-3-4-1"]
511
511
  dependencies = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes