cmpnd 0.46.0__tar.gz → 0.47.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 (379) hide show
  1. {cmpnd-0.46.0 → cmpnd-0.47.0}/PKG-INFO +1 -1
  2. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment.py +4 -5
  3. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimization.py +9 -33
  4. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/packaging.py +1 -1
  5. {cmpnd-0.46.0 → cmpnd-0.47.0}/pyproject.toml +1 -1
  6. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_programs.py +5 -7
  7. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_server_owned_completion.py +7 -12
  8. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_logs_renders_traces.py +1 -2
  9. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_unhappy_path.py +1 -1
  10. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy.py +4 -4
  11. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize.py +42 -52
  12. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_upload_auth_headers.py +1 -1
  13. {cmpnd-0.46.0 → cmpnd-0.47.0}/uv.lock +1 -1
  14. {cmpnd-0.46.0 → cmpnd-0.47.0}/.gitignore +0 -0
  15. {cmpnd-0.46.0 → cmpnd-0.47.0}/AGENTS.md +0 -0
  16. {cmpnd-0.46.0 → cmpnd-0.47.0}/CLAUDE.md +0 -0
  17. {cmpnd-0.46.0 → cmpnd-0.47.0}/README.md +0 -0
  18. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/__init__.py +0 -0
  19. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_assessments.py +0 -0
  20. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_authored_import.py +0 -0
  21. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_deployments.py +0 -0
  22. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_exporter_pool.py +0 -0
  23. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_gepa_patch.py +0 -0
  24. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_http.py +0 -0
  25. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_program_patch.py +0 -0
  26. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_rlm_patch.py +0 -0
  27. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_runtime_tools.py +0 -0
  28. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_timespec.py +0 -0
  29. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_traces.py +0 -0
  30. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_valsets.py +0 -0
  31. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/authored.py +0 -0
  32. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/callback.py +0 -0
  33. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/__init__.py +0 -0
  34. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/api_client.py +0 -0
  35. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/__init__.py +0 -0
  36. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/admin.py +0 -0
  37. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/assessments.py +0 -0
  38. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/auth.py +0 -0
  39. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/datasets.py +0 -0
  40. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/deployments.py +0 -0
  41. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/evals.py +0 -0
  42. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/login.py +0 -0
  43. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/optimizations.py +0 -0
  44. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/org.py +0 -0
  45. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/programs.py +0 -0
  46. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  47. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/signatures.py +0 -0
  48. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  49. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/top.py +0 -0
  50. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/traces.py +0 -0
  51. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/valsets.py +0 -0
  52. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/credentials.py +0 -0
  53. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/deprecation.py +0 -0
  54. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/pagination.py +0 -0
  55. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/shell.py +0 -0
  56. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/trace_render.py +0 -0
  57. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/tree_help.py +0 -0
  58. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/verbs.py +0 -0
  59. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/client.py +0 -0
  60. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/configuration.py +0 -0
  61. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/context.py +0 -0
  62. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/dataset_sync.py +0 -0
  63. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/datasets.py +0 -0
  64. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/decorators.py +0 -0
  65. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment_metadata.py +0 -0
  66. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment_runs.py +0 -0
  67. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/eval_handler.py +0 -0
  68. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/execution.py +0 -0
  69. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/exporter.py +0 -0
  70. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/exporter_http.py +0 -0
  71. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/helpers.py +0 -0
  72. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/identity.py +0 -0
  73. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/imaging.py +0 -0
  74. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/__init__.py +0 -0
  75. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/decode.py +0 -0
  76. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/encode.py +0 -0
  77. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/hash.py +0 -0
  78. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/program.py +0 -0
  79. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/reflection.py +0 -0
  80. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/types.py +0 -0
  81. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/xxh64.py +0 -0
  82. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/lm_configuration.py +0 -0
  83. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/mcp_servers.py +0 -0
  84. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/models.py +0 -0
  85. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/named_programs.py +0 -0
  86. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/__init__.py +0 -0
  87. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/gepa.py +0 -0
  88. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  89. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/resume.py +0 -0
  90. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/tracker.py +0 -0
  91. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/programs.py +0 -0
  92. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/references.py +0 -0
  93. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/retry.py +0 -0
  94. {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/traces.py +0 -0
  95. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/2026-04-02-code-review.md +0 -0
  96. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/cli.md +0 -0
  97. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/client.md +0 -0
  98. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/custom-proposer.md +0 -0
  99. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  100. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  101. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/reading-traces.md +0 -0
  102. {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/sdk-instrumentation.md +0 -0
  103. {cmpnd-0.46.0 → cmpnd-0.47.0}/examples/generalizing_proposer.py +0 -0
  104. {cmpnd-0.46.0 → cmpnd-0.47.0}/examples/local_ollama.py +0 -0
  105. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/LINEAGE.md +0 -0
  106. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/README.md +0 -0
  107. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  108. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  109. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  110. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  111. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  112. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  113. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/py.typed +0 -0
  114. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  115. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/retry.py +0 -0
  116. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  117. {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/pyproject.toml +0 -0
  118. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/audit-sdist.sh +0 -0
  119. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/capture_review_run.py +0 -0
  120. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/check-pip-install.sh +0 -0
  121. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/check_release_artifacts.py +0 -0
  122. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/fixtures/review_run.json.gz +0 -0
  123. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/gepa_resume_bug.py +0 -0
  124. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/release-stress.sh +0 -0
  125. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/require_version_bump.py +0 -0
  126. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/seed_review_run.py +0 -0
  127. {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/test-in-linux.sh +0 -0
  128. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/__init__.pyi +0 -0
  129. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  130. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  131. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/__init__.pyi +0 -0
  132. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/callback.pyi +0 -0
  133. {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/dummies.pyi +0 -0
  134. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/__init__.py +0 -0
  135. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_deployment_signature_helpers.py +0 -0
  136. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_exporter_helpers.py +0 -0
  137. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_fake_clock.py +0 -0
  138. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_identity_helpers.py +0 -0
  139. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_resolve_refs.py +0 -0
  140. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_traced_run.py +0 -0
  141. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/conftest.py +0 -0
  142. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/__init__.py +0 -0
  143. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/committee_task.py +0 -0
  144. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/conftest.py +0 -0
  145. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  146. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  147. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/fake_lm.py +0 -0
  148. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/helpers.py +0 -0
  149. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/target_capabilities.py +0 -0
  150. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_assessments_cli.py +0 -0
  151. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_capture_prompts.py +0 -0
  152. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_composite_module.py +0 -0
  153. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  154. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_execute.py +0 -0
  155. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_pandas.py +0 -0
  156. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_edge_cases.py +0 -0
  157. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_example_hash.py +0 -0
  158. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_gepa_optimization.py +0 -0
  159. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_interpreter_spans.py +0 -0
  160. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_lm_accuracy.py +0 -0
  161. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_module_tracing.py +0 -0
  162. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimization_lineage.py +0 -0
  163. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  164. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_failures.py +0 -0
  165. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_loop.py +0 -0
  166. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  167. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_parallel_export.py +0 -0
  168. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_project_attribution.py +0 -0
  169. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_react_tool_spans.py +0 -0
  170. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_save_load.py +0 -0
  171. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_trace_structure.py +0 -0
  172. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_traces_read.py +0 -0
  173. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_user_attribution.py +0 -0
  174. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_valsets_cli.py +0 -0
  175. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  176. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  177. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  178. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  179. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  180. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  181. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  182. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  183. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  184. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/10_react.json +0 -0
  185. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/11_refine.json +0 -0
  186. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/12_retrieve.json +0 -0
  187. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/13_knn.json +0 -0
  188. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/14_parallel.json +0 -0
  189. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/README.md +0 -0
  190. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/ingest_conformance.py +0 -0
  191. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/__init__.py +0 -0
  192. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/authored_optimize/judge.py +0 -0
  193. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  194. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/conftest.py +0 -0
  195. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  196. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/hosted_optimize_probe.py +0 -0
  197. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/optimization_run_probe.py +0 -0
  198. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  199. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_optimization_run.py +0 -0
  200. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_optimizing_progress.py +0 -0
  201. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/__init__.py +0 -0
  202. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/__main__.py +0 -0
  203. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/drain.py +0 -0
  204. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/ingest.py +0 -0
  205. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/latency_transport.py +0 -0
  206. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/rlm_shape.py +0 -0
  207. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/rlm_size_report.py +0 -0
  208. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/sweep.py +0 -0
  209. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/test_rlm_shape.py +0 -0
  210. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/test_rlm_size_report.py +0 -0
  211. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/__init__.py +0 -0
  212. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/conftest.py +0 -0
  213. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/seed_parity.py +0 -0
  214. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_auth.py +0 -0
  215. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_datasets.py +0 -0
  216. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_deployments.py +0 -0
  217. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_evals.py +0 -0
  218. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_health.py +0 -0
  219. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_modules.py +0 -0
  220. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_optimizations.py +0 -0
  221. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_programs.py +0 -0
  222. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_signatures.py +0 -0
  223. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_trace_streaming.py +0 -0
  224. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_traces.py +0 -0
  225. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/span_reference_fixture.py +0 -0
  226. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/__init__.py +0 -0
  227. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_provider.py +0 -0
  228. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_readiness.py +0 -0
  229. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_reliability.py +0 -0
  230. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_result.py +0 -0
  231. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/mint_bedrock_token.py +0 -0
  232. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_deploy_programs.py +0 -0
  233. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_multi_client_stress.py +0 -0
  234. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_client_committee.py +0 -0
  235. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_loop.py +0 -0
  236. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
  237. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_stress.py +0 -0
  238. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_provider_roles.py +0 -0
  239. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_readiness.py +0 -0
  240. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_real_provider_twins.py +0 -0
  241. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_reliability.py +0 -0
  242. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_reliability_classify.py +0 -0
  243. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_rlm_wasm.py +0 -0
  244. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_trace_search.py +0 -0
  245. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_adapter_reentrancy.py +0 -0
  246. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_assessments_api.py +0 -0
  247. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored.py +0 -0
  248. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_bundle_collisions.py +0 -0
  249. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_metric_contract.py +0 -0
  250. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_named_models.py +0 -0
  251. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_optimize.py +0 -0
  252. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_callback.py +0 -0
  253. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_candidate_lineage.py +0 -0
  254. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/__init__.py +0 -0
  255. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/conftest.py +0 -0
  256. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_admin.py +0 -0
  257. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_api_client.py +0 -0
  258. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_assessments.py +0 -0
  259. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_auth.py +0 -0
  260. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_credentials.py +0 -0
  261. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_datasets.py +0 -0
  262. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_deployment_runs.py +0 -0
  263. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_deployments.py +0 -0
  264. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_evals.py +0 -0
  265. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_login.py +0 -0
  266. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_main.py +0 -0
  267. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_optimizations.py +0 -0
  268. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_org.py +0 -0
  269. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_pagination.py +0 -0
  270. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_parity.py +0 -0
  271. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_programs.py +0 -0
  272. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_provider_credentials.py +0 -0
  273. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_schema_sample.py +0 -0
  274. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_shell.py +0 -0
  275. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_signatures.py +0 -0
  276. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_tool_servers.py +0 -0
  277. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_top.py +0 -0
  278. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_traces.py +0 -0
  279. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  280. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_tree_help.py +0 -0
  281. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_valsets.py +0 -0
  282. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client.py +0 -0
  283. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client_floor.py +0 -0
  284. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client_program_names.py +0 -0
  285. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/__init__.py +0 -0
  286. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/conftest.py +0 -0
  287. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_edge_cases.py +0 -0
  288. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  289. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_type_conversion.py +0 -0
  290. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cmp551_grouping_identity.py +0 -0
  291. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_configuration.py +0 -0
  292. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_context.py +0 -0
  293. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_context_propagation.py +0 -0
  294. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_dataset_polling.py +0 -0
  295. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_dataset_sync.py +0 -0
  296. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_datasets.py +0 -0
  297. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_decision_client_spans.py +0 -0
  298. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_decorators.py +0 -0
  299. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  300. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy_program_id.py +0 -0
  301. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployment_metadata.py +0 -0
  302. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployment_runs.py +0 -0
  303. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployments_read.py +0 -0
  304. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval.py +0 -0
  305. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_example_attribution.py +0 -0
  306. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_start.py +0 -0
  307. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_trace_grouping.py +0 -0
  308. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exception_paths.py +0 -0
  309. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_execute.py +0 -0
  310. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_execute_run_identity.py +0 -0
  311. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter.py +0 -0
  312. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_program_names.py +0 -0
  313. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_queue.py +0 -0
  314. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_real.py +0 -0
  315. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_fake_lm.py +0 -0
  316. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_callback.py +0 -0
  317. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_integration.py +0 -0
  318. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_output_normalization.py +0 -0
  319. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_resume.py +0 -0
  320. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_identity.py +0 -0
  321. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_imaging.py +0 -0
  322. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_import_weight.py +0 -0
  323. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ingest_conformance.py +0 -0
  324. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ingest_second_producer.py +0 -0
  325. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_integration.py +0 -0
  326. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_interpreter_callback.py +0 -0
  327. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_encoder_properties.py +0 -0
  328. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_golden_vectors.py +0 -0
  329. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_roundtrip.py +0 -0
  330. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm15_native_calls.py +0 -0
  331. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm_configuration.py +0 -0
  332. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm_usage_shapes.py +0 -0
  333. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_mcp_tools.py +0 -0
  334. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_models.py +0 -0
  335. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_multi_client_stress_readiness.py +0 -0
  336. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimization_progress.py +0 -0
  337. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimization_start.py +0 -0
  338. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_budget.py +0 -0
  339. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_lift.py +0 -0
  340. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_lms.py +0 -0
  341. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_proposer.py +0 -0
  342. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_reflection_lm.py +0 -0
  343. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_terminal_failure.py +0 -0
  344. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging.py +0 -0
  345. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_callable_source.py +0 -0
  346. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_mcp_tools.py +0 -0
  347. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_program_source.py +0 -0
  348. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_poll_until.py +0 -0
  349. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_program_identity.py +0 -0
  350. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_program_lineage_persist.py +0 -0
  351. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_programs_create.py +0 -0
  352. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_project_payload.py +0 -0
  353. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_real_provider_twins_readiness.py +0 -0
  354. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reflector_properties.py +0 -0
  355. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reflector_smoke.py +0 -0
  356. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_release_stress_script.py +0 -0
  357. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_repro_reactv2_bugs.py +0 -0
  358. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_require_version_bump.py +0 -0
  359. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_retry.py +0 -0
  360. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reversible_failures.py +0 -0
  361. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_rlm_patch.py +0 -0
  362. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_run_attribution_paths.py +0 -0
  363. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_runtime_tools.py +0 -0
  364. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_schemas.py +0 -0
  365. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_seed_review_run.py +0 -0
  366. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_signature_inputs.py +0 -0
  367. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_span_references.py +0 -0
  368. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_span_sent_once.py +0 -0
  369. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_system_one.py +0 -0
  370. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_context_concurrency.py +0 -0
  371. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_fact_wire_contract.py +0 -0
  372. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_facts.py +0 -0
  373. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_render.py +0 -0
  374. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_traces_read.py +0 -0
  375. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_tracked_gepa.py +0 -0
  376. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_tracker_enrich.py +0 -0
  377. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_truncation.py +0 -0
  378. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_valsets_api.py +0 -0
  379. {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.46.0
3
+ Version: 0.47.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>
@@ -2,9 +2,8 @@
2
2
 
3
3
  The deploy flow:
4
4
  1. Package the module into a source ZIP
5
- 2. POST to the server to create a deployment (returns a presigned upload slot)
6
- 3. PUT the ZIP to that slot (one path for both postures; bytes go direct to the
7
- worker, raw body, no form)
5
+ 2. POST to the server to create a deployment (returns an upload slot on serve)
6
+ 3. PUT the ZIP to that slot as the raw body
8
7
  4. Poll until the parser finishes (status becomes "ready" or "failed")
9
8
  5. Return the deployment_id
10
9
  """
@@ -451,8 +450,8 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
451
450
 
452
451
  deploy_body: dict[str, Any] = {
453
452
  "program_signature_hash": identity.format_signature_hash(program_signature_hash),
454
- # Declare the ZIP's exact length so the server can size-bound the upload
455
- # slot and reject an oversized deploy before signing it (Slice E).
453
+ # Declare the ZIP's exact length so the server can reject an oversized
454
+ # deploy before the upload.
456
455
  "declared_content_length": len(zip_bytes),
457
456
  }
458
457
  # Left off when undeclared, so a serve from before program ids reads the body it
@@ -11,8 +11,8 @@ Flow per :func:`optimize` call:
11
11
  1. Build the optimization bundle (``trainset.ndjson`` + ``valset.ndjson`` +
12
12
  ``metric.py``) and, if no ``deployment_id`` was given, :func:`deploy` the
13
13
  program to materialize the starting deployment A.
14
- 2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get a
15
- presigned upload, PUT the bundle, then poll until the step completes. The
14
+ 2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get an
15
+ upload slot, PUT the bundle, then poll until the step completes. The
16
16
  result becomes a first-class child deployment B.
17
17
  3. Steps 1..n — ``data:"reuse"`` on the latest result deployment, advancing the
18
18
  seed each step (the key quality lever). Track the best score; stop after
@@ -1417,14 +1417,12 @@ def _initiate_step(
1417
1417
  def _upload_bundle(
1418
1418
  client: httpx.Client, initiated: dict[str, Any], bundle: bytes, *, endpoint: str, api_key: str
1419
1419
  ) -> None:
1420
- """Upload the optimization bundle to the slot the initiate step returned.
1420
+ """PUT the optimization bundle, raw, to the slot the initiate step returned.
1421
1421
 
1422
- Serve answers an ``awaiting_data`` initiate with ``upload_url``, ``upload_method:
1423
- "PUT"`` and empty ``upload_fields``, and the bundle goes out as the raw PUT body.
1424
- Serve authenticates that route with the API key, which rides the PUT only when
1425
- ``upload_url`` is on ``endpoint``'s origin (``_http.upload_auth_headers``). A
1426
- non-empty ``upload_fields`` is a presigned form, which no current server sends.
1427
- The bundle then rides as the ``file`` part of a multipart POST.
1422
+ Serve answers an ``awaiting_data`` initiate with an ``upload_url`` on its own
1423
+ bundle route, and authenticates that route with the API key. The key rides
1424
+ the PUT only when ``upload_url`` is on ``endpoint``'s origin
1425
+ (``_http.upload_auth_headers``).
1428
1426
 
1429
1427
  The slot pins its destination, so re-sending the same bytes writes the same
1430
1428
  object. That makes this call idempotent, so it rides through a transient answer
@@ -1433,31 +1431,9 @@ def _upload_bundle(
1433
1431
  upload_url = initiated.get("upload_url")
1434
1432
  if not upload_url:
1435
1433
  raise OptimizeError("upload step did not return an upload_url")
1436
- upload_fields = initiated.get("upload_fields")
1437
- method = (initiated.get("upload_method") or ("POST" if upload_fields else "PUT")).upper()
1438
1434
  what = "upload optimization bundle"
1439
- auth = _http.upload_auth_headers(upload_url, endpoint, api_key)
1440
-
1441
- if upload_fields:
1442
- resp = _request(
1443
- lambda: client.post(
1444
- upload_url,
1445
- data=dict(upload_fields),
1446
- files={"file": ("data.zip", bundle, "application/zip")},
1447
- headers=auth,
1448
- ),
1449
- what,
1450
- )
1451
- else:
1452
- resp = _request(
1453
- lambda: client.request(
1454
- method,
1455
- upload_url,
1456
- content=bundle,
1457
- headers={"Content-Type": "application/zip", **auth},
1458
- ),
1459
- what,
1460
- )
1435
+ headers = {"Content-Type": "application/zip", **_http.upload_auth_headers(upload_url, endpoint, api_key)}
1436
+ resp = _request(lambda: client.put(upload_url, content=bundle, headers=headers), what)
1461
1437
  if not resp.is_success:
1462
1438
  body = (resp.text or "").strip()[:400]
1463
1439
  raise OptimizeError(f"failed to {what}: HTTP {resp.status_code}: {body}")
@@ -1428,7 +1428,7 @@ def generate_source_zip(
1428
1428
  ) -> bytes:
1429
1429
  """Build a ZIP archive with program source, optional metric, and manifest.
1430
1430
 
1431
- The ZIP is suitable for upload to the deploy API via presigned S3 POST.
1431
+ The ZIP is the body the deploy API's source upload takes.
1432
1432
  """
1433
1433
  import dspy
1434
1434
 
@@ -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.46.0"
8
+ version = "0.47.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -212,11 +212,8 @@ class TestDeployBadImportRejected:
212
212
  headers = {"X-API-Key": config.api_key, "Content-Type": "application/json"}
213
213
 
214
214
  with httpx.Client(timeout=30.0) as client:
215
- # Step 1: create deployment. declared_content_length is required
216
- # (Slice E): the hosted presigned PUT is signed with the exact ZIP
217
- # length, so it must be declared up front — exactly as cmpnd/deploy.py
218
- # does, and a deployment names its program. This hand-crafted client
219
- # mirrors that contract.
215
+ # Step 1: create the deployment, declaring the ZIP's length and
216
+ # naming its program as cmpnd.deploy does.
220
217
  create_resp = client.post(
221
218
  f"{config.endpoint}/api/v1/deployments",
222
219
  json={
@@ -232,11 +229,12 @@ class TestDeployBadImportRejected:
232
229
  deployment_id = data["deployment_id"]
233
230
  upload_url = data["upload_url"]
234
231
 
235
- # Step 2: PUT the bad ZIP to the single upload slot (Slice E).
232
+ # Step 2: PUT the bad ZIP to the upload slot, which serve
233
+ # authenticates with the API key.
236
234
  upload_resp = client.put(
237
235
  upload_url,
238
236
  content=zip_bytes,
239
- headers={"Content-Type": "application/zip"},
237
+ headers={"X-API-Key": config.api_key, "Content-Type": "application/zip"},
240
238
  )
241
239
  upload_resp.raise_for_status()
242
240
 
@@ -106,18 +106,13 @@ def _initiate_and_abandon(deployment_id: str, trainset, valset, metric) -> str:
106
106
  resp.raise_for_status()
107
107
  initiated = resp.json()
108
108
 
109
- # serve normalizes the awaiting_data slot so upload_fields is always
110
- # present: a non-empty form means presigned POST, empty means raw PUT.
111
- upload_url = initiated["upload_url"]
112
- upload_fields = initiated.get("upload_fields") or {}
113
- if upload_fields:
114
- up = client.post(
115
- upload_url,
116
- data=dict(upload_fields),
117
- files={"file": ("data.zip", bundle, "application/zip")},
118
- )
119
- else:
120
- up = client.put(upload_url, content=bundle, headers={"Content-Type": "application/zip"})
109
+ # The slot is serve's bundle route, which takes the raw bundle as a PUT
110
+ # and authenticates it with the API key.
111
+ up = client.put(
112
+ initiated["upload_url"],
113
+ content=bundle,
114
+ headers={"X-API-Key": cfg.api_key, "Content-Type": "application/zip"},
115
+ )
121
116
  up.raise_for_status()
122
117
 
123
118
  # Deliberately NO status poll — the server-owned watcher must finish it.
@@ -64,8 +64,7 @@ def _create_and_upload_real_module(rails: httpx.Client, stub_url: str) -> str:
64
64
  created = resp.json()
65
65
  deployment_id = created["deployment_id"]
66
66
 
67
- # Upload the real source ZIP using whichever transport Rails returned
68
- # — POST presigned (Lambda) or PUT (cmpnd-serve).
67
+ # PUT the real source ZIP to the slot the create response names.
69
68
  zip_bytes = generate_source_zip(module, None)
70
69
  upload_deploy_zip(created, zip_bytes)
71
70
 
@@ -59,7 +59,7 @@ def _create_deployment(rails: httpx.Client, *, signature_hash: int) -> dict:
59
59
 
60
60
  def _upload_to_stub(stub_url: str, create_resp: dict, zip_bytes: bytes) -> None:
61
61
  """Mirror the SDK's deploy upload step — a single PUT to the upload slot
62
- (Slice E) via the shared conftest helper. ``stub_url`` is unused — kept in
62
+ via the shared conftest helper. ``stub_url`` is unused — kept in
63
63
  the signature so existing call-sites don't need editing."""
64
64
  del stub_url # historical; the helper reads upload_url from create_resp
65
65
  upload_deploy_zip(create_resp, zip_bytes)
@@ -59,7 +59,7 @@ class TestDeploy:
59
59
  }
60
60
  create_response.raise_for_status = mock.MagicMock()
61
61
 
62
- # S3 upload response (presigned PUT, Slice E)
62
+ # Upload slot response
63
63
  upload_response = mock.MagicMock()
64
64
  upload_response.status_code = 200
65
65
  upload_response.raise_for_status = mock.MagicMock()
@@ -88,7 +88,7 @@ class TestDeploy:
88
88
  signature.json.return_value = signature_response
89
89
  signature.raise_for_status = mock.MagicMock()
90
90
 
91
- # Wire up: POST = create; PUT = upload slot (Slice E); GET = polls.
91
+ # Wire up: POST = create; PUT = upload slot; GET = polls.
92
92
  client.post.side_effect = [create_response]
93
93
  client.put.side_effect = [upload_response]
94
94
  client.get.side_effect = [*poll_responses, signature]
@@ -178,7 +178,7 @@ class TestDeploy:
178
178
 
179
179
  deployment.deploy(dspy.Predict("question -> answer"))
180
180
 
181
- # Slice E: one PUT of the raw ZIP to the presigned upload slot.
181
+ # One PUT of the raw ZIP to the upload slot.
182
182
  client.post.assert_called_once()
183
183
  upload_call = client.put.call_args
184
184
  assert "s3.amazonaws.com" in upload_call.args[0]
@@ -194,7 +194,7 @@ class TestDeploy:
194
194
 
195
195
  deployment.deploy(dspy.Predict("question -> answer"))
196
196
 
197
- # The create body declares the ZIP length for the size cap (Slice E).
197
+ # The create body declares the ZIP length for the size cap.
198
198
  create_call = client.post.call_args_list[0]
199
199
  body = create_call.kwargs["json"]
200
200
  assert isinstance(body["declared_content_length"], int)
@@ -127,14 +127,13 @@ def _one_completed_step(mock_httpx):
127
127
  {
128
128
  "optimization_id": "opt-0",
129
129
  "status": "awaiting_data",
130
- "upload_url": "https://s3/b",
131
- "upload_method": "POST",
132
- "upload_fields": {"key": "k"},
130
+ "upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
131
+ "upload_method": "PUT",
133
132
  },
134
133
  status_code=201,
135
134
  ),
136
- _resp(status_code=204),
137
135
  ]
136
+ client.put.side_effect = [_resp(status_code=204)]
138
137
  client.get.side_effect = [
139
138
  _resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
140
139
  ]
@@ -348,14 +347,14 @@ class TestLoop:
348
347
  {
349
348
  "optimization_id": "opt-0",
350
349
  "status": "awaiting_data",
351
- "upload_url": "https://s3/bucket",
352
- "upload_fields": {"key": "k"},
350
+ "upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
351
+ "upload_method": "PUT",
353
352
  },
354
353
  status_code=201,
355
354
  ),
356
- _resp(is_success=True, status_code=204), # S3 upload
357
355
  _resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
358
356
  ]
357
+ client.put.side_effect = [_resp(status_code=204)]
359
358
  client.get.side_effect = [
360
359
  _resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
361
360
  _resp({"status": "completed", "best_score": 0.6, "result_deployment_id": "dep-C"}),
@@ -379,13 +378,13 @@ class TestLoop:
379
378
  assert init0.kwargs["json"]["data"] == "upload"
380
379
  assert init0.kwargs["json"]["config"]["seed"] == 0
381
380
 
382
- # The bundle is uploaded exactly once (to the presigned URL).
383
- upload = client.post.call_args_list[1]
384
- assert upload.args[0] == "https://s3/bucket"
385
- assert "file" in upload.kwargs["files"]
381
+ # The bundle is PUT exactly once, raw, to the slot serve answered.
382
+ (upload,) = client.put.call_args_list
383
+ assert upload.args[0] == "https://serve.example/api/v1/optimizations/opt-0/bundle"
384
+ assert upload.kwargs["content"]
386
385
 
387
386
  # Step 1 REUSEs the result deployment (dep-B) with seed advanced to 1.
388
- init1 = client.post.call_args_list[2]
387
+ init1 = client.post.call_args_list[1]
389
388
  assert "/deployments/dep-B/optimize/gepa" in init1.args[0]
390
389
  assert init1.kwargs["json"]["data"] == "reuse"
391
390
  assert init1.kwargs["json"]["config"]["seed"] == 1
@@ -395,17 +394,14 @@ class TestLoop:
395
394
  assert result.score == 0.6
396
395
  assert callable(result)
397
396
 
398
- def test_upload_uses_raw_put_for_worker_posture(self, mock_httpx):
399
- """On-prem (worker-sandbox) posture: serve normalizes the awaiting_data
400
- response to upload_method=PUT with empty upload_fields. The bundle must
401
- go out as a raw PUT to the slot URL — not a multipart POST — so the one
402
- SDK upload path serves both the worker (PUT) and the Lambda (presigned
403
- POST) workers (the CMP-250 × CMP-254 reconciliation)."""
397
+ def test_upload_is_a_raw_put_to_the_slot(self, mock_httpx):
398
+ """Serve answers awaiting_data with one PUT slot. The bundle goes out
399
+ as the raw PUT body to that URL, never as a multipart form."""
404
400
  client = _client(mock_httpx)
405
401
  train, val = _examples()
406
402
 
407
403
  # Only the two initiate calls go through client.post; the bundle upload
408
- # is a PUT, so it lands on client.request and must not consume a post.
404
+ # is a PUT, so it lands on client.put and must not consume a post.
409
405
  client.post.side_effect = [
410
406
  _resp(
411
407
  {
@@ -413,13 +409,12 @@ class TestLoop:
413
409
  "status": "awaiting_data",
414
410
  "upload_url": "http://worker:8082/uploads/opt-0",
415
411
  "upload_method": "PUT",
416
- "upload_fields": {},
417
412
  },
418
413
  status_code=201,
419
414
  ),
420
415
  _resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
421
416
  ]
422
- client.request.side_effect = [_resp(is_success=True, status_code=202)]
417
+ client.put.side_effect = [_resp(is_success=True, status_code=202)]
423
418
  client.get.side_effect = [
424
419
  _resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
425
420
  _resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-C"}),
@@ -439,8 +434,8 @@ class TestLoop:
439
434
  )
440
435
 
441
436
  # The bundle was PUT (raw bytes) to the worker's slot, never POSTed.
442
- put = client.request.call_args
443
- assert put.args == ("PUT", "http://worker:8082/uploads/opt-0")
437
+ put = client.put.call_args
438
+ assert put.args == ("http://worker:8082/uploads/opt-0",)
444
439
  assert put.kwargs["content"] # raw zip bytes, not a multipart `files=`
445
440
  assert put.kwargs["headers"]["Content-Type"] == "application/zip"
446
441
  # The two client.post calls are both initiate calls (no upload among them).
@@ -456,15 +451,14 @@ class TestLoop:
456
451
  {
457
452
  "optimization_id": "opt-0",
458
453
  "status": "awaiting_data",
459
- "upload_url": "https://s3/b",
460
- "upload_method": "POST",
461
- "upload_fields": {"key": "k"},
454
+ "upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
455
+ "upload_method": "PUT",
462
456
  },
463
457
  status_code=201,
464
458
  ),
465
- _resp(status_code=204),
466
459
  _resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
467
460
  ]
461
+ client.put.side_effect = [_resp(status_code=204)]
468
462
  # Both steps score the same → no improvement on step 1.
469
463
  client.get.side_effect = [
470
464
  _resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
@@ -499,14 +493,13 @@ class TestLoop:
499
493
  {
500
494
  "optimization_id": "opt-0",
501
495
  "status": "awaiting_data",
502
- "upload_url": "https://s3/b",
503
- "upload_method": "POST",
504
- "upload_fields": {"key": "k"},
496
+ "upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
497
+ "upload_method": "PUT",
505
498
  },
506
499
  status_code=201,
507
500
  ),
508
- _resp(status_code=204),
509
501
  ]
502
+ client.put.side_effect = [_resp(status_code=204)]
510
503
  client.get.side_effect = [
511
504
  _resp({"status": "completed", "best_score": 0.7, "result_deployment_id": "dep-B"}),
512
505
  ]
@@ -545,14 +538,13 @@ class TestFailures:
545
538
  {
546
539
  "optimization_id": "opt-0",
547
540
  "status": "awaiting_data",
548
- "upload_url": "https://s3/b",
549
- "upload_method": "POST",
550
- "upload_fields": {"key": "k"},
541
+ "upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
542
+ "upload_method": "PUT",
551
543
  },
552
544
  status_code=201,
553
545
  ),
554
- _resp(status_code=204),
555
546
  ]
547
+ client.put.side_effect = [_resp(status_code=204)]
556
548
  client.get.side_effect = [
557
549
  _resp({"status": "failed", "error": "trainset row 1 missing label field(s): ['answer']"}),
558
550
  ]
@@ -633,8 +625,8 @@ class TestNetworkErrorsWrapped:
633
625
 
634
626
  def test_upload_bundle_wraps_network_error(self):
635
627
  client = mock.MagicMock()
636
- client.post.side_effect = httpx.WriteTimeout("slow upload")
637
- initiated = {"upload_url": "https://s3.example/bucket", "upload_fields": {"key": "programs/x/data.zip"}}
628
+ client.put.side_effect = httpx.WriteTimeout("slow upload")
629
+ initiated = {"upload_url": "http://x/api/v1/optimizations/opt-0/bundle", "upload_method": "PUT"}
638
630
  # The upload re-sends the same bytes to the same pinned slot, so a
639
631
  # transport blip is retried; the wrapped error surfaces once the budget
640
632
  # is spent.
@@ -951,13 +943,13 @@ class TestInitiateAndUploadTolerance:
951
943
 
952
944
  def test_upload_retries_a_retryable_response(self):
953
945
  client = mock.MagicMock()
954
- client.request.side_effect = [_transient(), _resp(status_code=204)]
955
- initiated = {"upload_url": "http://worker:8082/uploads/opt-0", "upload_fields": {}}
946
+ client.put.side_effect = [_transient(), _resp(status_code=204)]
947
+ initiated = {"upload_url": "http://worker:8082/uploads/opt-0"}
956
948
 
957
949
  with _fake_clock():
958
950
  optimization._upload_bundle(client, initiated, b"zip-bytes", endpoint="http://x", api_key="k")
959
951
 
960
- assert client.request.call_count == 2
952
+ assert client.put.call_count == 2
961
953
 
962
954
 
963
955
  class TestUploadAuthentication:
@@ -982,7 +974,7 @@ class TestUploadAuthentication:
982
974
  return sent
983
975
 
984
976
  def test_upload_to_the_endpoint_carries_the_api_key(self):
985
- (upload,) = self._upload({"upload_url": self._OWN_SLOT, "upload_method": "PUT", "upload_fields": {}})
977
+ (upload,) = self._upload({"upload_url": self._OWN_SLOT, "upload_method": "PUT"})
986
978
 
987
979
  assert upload.method == "PUT"
988
980
  assert str(upload.url) == self._OWN_SLOT
@@ -991,20 +983,19 @@ class TestUploadAuthentication:
991
983
  assert upload.content == b"zip-bytes"
992
984
 
993
985
  def test_upload_to_another_origin_carries_no_api_key(self):
994
- (upload,) = self._upload(
995
- {"upload_url": "http://worker:8082/uploads/opt-0", "upload_method": "PUT", "upload_fields": {}}
996
- )
986
+ (upload,) = self._upload({"upload_url": "http://worker:8082/uploads/opt-0", "upload_method": "PUT"})
997
987
 
998
988
  assert "X-API-Key" not in upload.headers
999
989
  assert upload.headers["Content-Type"] == "application/zip"
1000
990
 
1001
- def test_presigned_form_upload_carries_no_api_key(self):
991
+ def test_upload_is_a_raw_put_whatever_form_the_answer_names(self):
1002
992
  (upload,) = self._upload(
1003
- {"upload_url": "https://s3.example/bucket", "upload_fields": {"key": "programs/x/data.zip"}}
993
+ {"upload_url": self._OWN_SLOT, "upload_method": "POST", "upload_fields": {"key": "programs/x/data.zip"}}
1004
994
  )
1005
995
 
1006
- assert upload.method == "POST"
1007
- assert "X-API-Key" not in upload.headers
996
+ assert upload.method == "PUT"
997
+ assert upload.headers["Content-Type"] == "application/zip"
998
+ assert upload.content == b"zip-bytes"
1008
999
 
1009
1000
  def test_optimize_hands_the_configured_endpoint_and_key_to_the_upload(self, mock_httpx):
1010
1001
  client = _client(mock_httpx)
@@ -1016,12 +1007,11 @@ class TestUploadAuthentication:
1016
1007
  "status": "awaiting_data",
1017
1008
  "upload_url": self._OWN_SLOT,
1018
1009
  "upload_method": "PUT",
1019
- "upload_fields": {},
1020
1010
  },
1021
1011
  status_code=201,
1022
1012
  ),
1023
1013
  ]
1024
- client.request.side_effect = [_resp(status_code=200)]
1014
+ client.put.side_effect = [_resp(status_code=200)]
1025
1015
  client.get.side_effect = [_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"})]
1026
1016
 
1027
1017
  with _no_sleep():
@@ -1036,7 +1026,7 @@ class TestUploadAuthentication:
1036
1026
  max_steps=1,
1037
1027
  )
1038
1028
 
1039
- put = client.request.call_args
1040
- assert put.args == ("PUT", self._OWN_SLOT)
1029
+ put = client.put.call_args
1030
+ assert put.args == (self._OWN_SLOT,)
1041
1031
  assert put.kwargs["headers"]["X-API-Key"] == "ct_test_key"
1042
1032
  assert put.kwargs["headers"]["Content-Type"] == "application/zip"
@@ -27,7 +27,7 @@ _KEY = "ct_test_key"
27
27
  ("http://localhost/api/v1/optimizations/op_abc/bundle", "http://localhost:80"),
28
28
  # Scheme and host compare case-insensitively.
29
29
  ("HTTPS://App.Cmpnd.AI/api/v1/deployments/d_abc/source", _ENDPOINT),
30
- ("http://localhost:3000/api/v1/uploads/source/p_abc", "http://localhost:3000"),
30
+ ("http://localhost:3000/api/v1/deployments/d_abc/source", "http://localhost:3000"),
31
31
  ],
32
32
  )
33
33
  def test_sends_the_key_to_the_endpoint_origin(upload_url: str, endpoint: str) -> None:
@@ -496,7 +496,7 @@ wheels = [
496
496
 
497
497
  [[package]]
498
498
  name = "cmpnd"
499
- version = "0.46.0"
499
+ version = "0.47.0"
500
500
  source = { editable = "." }
501
501
  dependencies = [
502
502
  { name = "dspy" },
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
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