cmpnd 0.56.0__tar.gz → 0.58.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 (392) hide show
  1. {cmpnd-0.56.0 → cmpnd-0.58.0}/AGENTS.md +34 -0
  2. {cmpnd-0.56.0 → cmpnd-0.58.0}/PKG-INFO +174 -12
  3. {cmpnd-0.56.0 → cmpnd-0.58.0}/README.md +172 -10
  4. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/__init__.py +8 -2
  5. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_authored_import.py +113 -4
  6. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_gepa_patch.py +6 -9
  7. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/callback.py +4 -3
  8. cmpnd-0.58.0/cmpnd/cli/commands/provider_credentials.py +532 -0
  9. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/top.py +14 -1
  10. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/verbs.py +2 -0
  11. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/client.py +54 -9
  12. cmpnd-0.58.0/cmpnd/credentials.py +199 -0
  13. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/deployment.py +105 -22
  14. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/helpers.py +116 -0
  15. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/lm_configuration.py +71 -15
  16. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimization.py +109 -53
  17. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimizers/tracker.py +3 -5
  18. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/cli.md +78 -25
  19. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/sdk-instrumentation.md +13 -5
  20. {cmpnd-0.56.0 → cmpnd-0.58.0}/pyproject.toml +31 -4
  21. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/capture_review_run.py +4 -1
  22. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/check-pip-install.sh +1 -1
  23. cmpnd-0.58.0/scripts/install-test-env.sh +67 -0
  24. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/test-in-linux.sh +8 -7
  25. cmpnd-0.58.0/tests/_credential_route.py +96 -0
  26. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/committee_task.py +49 -54
  27. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/helpers.py +93 -0
  28. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +3 -3
  29. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/hosted_optimize_probe.py +51 -26
  30. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/test_logs_renders_traces.py +2 -2
  31. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/span_reference_fixture.py +13 -7
  32. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/_provider.py +31 -24
  33. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_deploy_programs.py +7 -4
  34. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_optimize_loop.py +19 -18
  35. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_optimize_loop_wasm.py +23 -50
  36. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_optimize_stress.py +16 -18
  37. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_provider_roles.py +65 -3
  38. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_real_provider_twins.py +11 -9
  39. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_rlm_wasm.py +8 -13
  40. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_adapter_reentrancy.py +2 -8
  41. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_authored_named_models.py +19 -5
  42. cmpnd-0.58.0/tests/test_cli/test_provider_credentials.py +660 -0
  43. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_top.py +31 -1
  44. cmpnd-0.58.0/tests/test_credential_kwargs.py +306 -0
  45. cmpnd-0.58.0/tests/test_credential_selection.py +320 -0
  46. cmpnd-0.58.0/tests/test_credential_values.py +193 -0
  47. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deploy.py +55 -84
  48. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_lm_usage_shapes.py +2 -4
  49. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_lms.py +29 -15
  50. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_reflection_lm.py +18 -34
  51. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_program_lineage_persist.py +4 -5
  52. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_rlm_patch.py +19 -6
  53. cmpnd-0.58.0/tests/test_seal_lm_credential.py +153 -0
  54. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_span_references.py +3 -3
  55. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_system_one.py +31 -12
  56. {cmpnd-0.56.0 → cmpnd-0.58.0}/uv.lock +421 -194
  57. cmpnd-0.56.0/cmpnd/cli/commands/provider_credentials.py +0 -321
  58. cmpnd-0.56.0/tests/test_cli/test_provider_credentials.py +0 -319
  59. {cmpnd-0.56.0 → cmpnd-0.58.0}/.gitignore +0 -0
  60. {cmpnd-0.56.0 → cmpnd-0.58.0}/CLAUDE.md +0 -0
  61. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_assessments.py +0 -0
  62. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_deployments.py +0 -0
  63. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_exporter_pool.py +0 -0
  64. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_http.py +0 -0
  65. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_program_patch.py +0 -0
  66. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_rlm_patch.py +0 -0
  67. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_runtime_tools.py +0 -0
  68. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_timespec.py +0 -0
  69. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_traces.py +0 -0
  70. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/_valsets.py +0 -0
  71. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/authored.py +0 -0
  72. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/__init__.py +0 -0
  73. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/api_client.py +0 -0
  74. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/__init__.py +0 -0
  75. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/admin.py +0 -0
  76. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/assessments.py +0 -0
  77. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/auth.py +0 -0
  78. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/datasets.py +0 -0
  79. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/deployments.py +0 -0
  80. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/evals.py +0 -0
  81. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/login.py +0 -0
  82. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/optimizations.py +0 -0
  83. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/org.py +0 -0
  84. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/programs.py +0 -0
  85. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/signatures.py +0 -0
  86. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  87. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/traces.py +0 -0
  88. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/commands/valsets.py +0 -0
  89. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/credentials.py +0 -0
  90. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/deprecation.py +0 -0
  91. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/pagination.py +0 -0
  92. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/shell.py +0 -0
  93. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/trace_render.py +0 -0
  94. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/cli/tree_help.py +0 -0
  95. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/configuration.py +0 -0
  96. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/context.py +0 -0
  97. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/dataset_sync.py +0 -0
  98. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/datasets.py +0 -0
  99. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/decorators.py +0 -0
  100. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/deployment_metadata.py +0 -0
  101. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/deployment_runs.py +0 -0
  102. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/eval_handler.py +0 -0
  103. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/execution.py +0 -0
  104. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/exporter.py +0 -0
  105. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/exporter_http.py +0 -0
  106. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/identity.py +0 -0
  107. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/imaging.py +0 -0
  108. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/__init__.py +0 -0
  109. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/decode.py +0 -0
  110. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/encode.py +0 -0
  111. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/hash.py +0 -0
  112. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/program.py +0 -0
  113. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/reflection.py +0 -0
  114. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/types.py +0 -0
  115. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/ir/xxh64.py +0 -0
  116. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/mcp_servers.py +0 -0
  117. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/models.py +0 -0
  118. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/named_programs.py +0 -0
  119. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimizers/__init__.py +0 -0
  120. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimizers/gepa.py +0 -0
  121. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  122. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/optimizers/resume.py +0 -0
  123. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/packaging.py +0 -0
  124. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/programs.py +0 -0
  125. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/references.py +0 -0
  126. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/retry.py +0 -0
  127. {cmpnd-0.56.0 → cmpnd-0.58.0}/cmpnd/traces.py +0 -0
  128. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/2026-04-02-code-review.md +0 -0
  129. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/client.md +0 -0
  130. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/custom-proposer.md +0 -0
  131. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  132. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  133. {cmpnd-0.56.0 → cmpnd-0.58.0}/docs/reading-traces.md +0 -0
  134. {cmpnd-0.56.0 → cmpnd-0.58.0}/examples/generalizing_proposer.py +0 -0
  135. {cmpnd-0.56.0 → cmpnd-0.58.0}/examples/local_ollama.py +0 -0
  136. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/LINEAGE.md +0 -0
  137. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/README.md +0 -0
  138. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  139. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  140. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  141. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  142. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  143. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  144. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/py.typed +0 -0
  145. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  146. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/retry.py +0 -0
  147. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  148. {cmpnd-0.56.0 → cmpnd-0.58.0}/ingest/pyproject.toml +0 -0
  149. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/audit-sdist.sh +0 -0
  150. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/check_release_artifacts.py +0 -0
  151. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/fixtures/review_run.json.gz +0 -0
  152. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/gepa_resume_bug.py +0 -0
  153. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/release-stress.sh +0 -0
  154. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/require_version_bump.py +0 -0
  155. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/seed_review_run.py +0 -0
  156. {cmpnd-0.56.0 → cmpnd-0.58.0}/scripts/selfhost_compat.py +0 -0
  157. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/__init__.pyi +0 -0
  158. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  159. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  160. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/utils/__init__.pyi +0 -0
  161. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/utils/callback.pyi +0 -0
  162. {cmpnd-0.56.0 → cmpnd-0.58.0}/stubs/dspy/utils/dummies.pyi +0 -0
  163. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/__init__.py +0 -0
  164. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_deployment_signature_helpers.py +0 -0
  165. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_exporter_helpers.py +0 -0
  166. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_fake_clock.py +0 -0
  167. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_identity_helpers.py +0 -0
  168. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_resolve_refs.py +0 -0
  169. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/_traced_run.py +0 -0
  170. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/conftest.py +0 -0
  171. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/__init__.py +0 -0
  172. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/conftest.py +0 -0
  173. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  174. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  175. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/fake_lm.py +0 -0
  176. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/target_capabilities.py +0 -0
  177. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_assessments_cli.py +0 -0
  178. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_capture_prompts.py +0 -0
  179. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_composite_module.py +0 -0
  180. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  181. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_deploy_execute.py +0 -0
  182. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_deploy_pandas.py +0 -0
  183. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_deploy_programs.py +0 -0
  184. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_edge_cases.py +0 -0
  185. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_example_hash.py +0 -0
  186. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_gepa_optimization.py +0 -0
  187. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_interpreter_spans.py +0 -0
  188. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_lm_accuracy.py +0 -0
  189. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_module_tracing.py +0 -0
  190. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimization_lineage.py +0 -0
  191. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  192. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimize_failures.py +0 -0
  193. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimize_loop.py +0 -0
  194. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  195. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  196. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_parallel_export.py +0 -0
  197. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_project_attribution.py +0 -0
  198. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_react_tool_spans.py +0 -0
  199. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_save_load.py +0 -0
  200. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_trace_structure.py +0 -0
  201. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_traces_read.py +0 -0
  202. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_user_attribution.py +0 -0
  203. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/e2e/test_valsets_cli.py +0 -0
  204. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  205. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  206. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  207. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  208. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  209. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  210. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  211. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  212. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  213. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/10_react.json +0 -0
  214. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/11_refine.json +0 -0
  215. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/12_retrieve.json +0 -0
  216. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/13_knn.json +0 -0
  217. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/14_parallel.json +0 -0
  218. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/identity_vectors/README.md +0 -0
  219. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/ingest_conformance.py +0 -0
  220. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/__init__.py +0 -0
  221. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/authored_optimize/judge.py +0 -0
  222. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  223. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/conftest.py +0 -0
  224. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/optimization_run_probe.py +0 -0
  225. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  226. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/test_optimization_run.py +0 -0
  227. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/test_optimizing_progress.py +0 -0
  228. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/integration/test_unhappy_path.py +0 -0
  229. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/__init__.py +0 -0
  230. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/__main__.py +0 -0
  231. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/drain.py +0 -0
  232. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/ingest.py +0 -0
  233. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/latency_transport.py +0 -0
  234. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/rlm_shape.py +0 -0
  235. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/rlm_size_report.py +0 -0
  236. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/sweep.py +0 -0
  237. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/test_rlm_shape.py +0 -0
  238. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/load/test_rlm_size_report.py +0 -0
  239. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/__init__.py +0 -0
  240. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/conftest.py +0 -0
  241. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/seed_parity.py +0 -0
  242. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_auth.py +0 -0
  243. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_datasets.py +0 -0
  244. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_deployments.py +0 -0
  245. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_evals.py +0 -0
  246. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_health.py +0 -0
  247. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_modules.py +0 -0
  248. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_optimizations.py +0 -0
  249. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_programs.py +0 -0
  250. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_signatures.py +0 -0
  251. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_trace_streaming.py +0 -0
  252. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/parity/test_traces.py +0 -0
  253. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/__init__.py +0 -0
  254. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/_readiness.py +0 -0
  255. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/_reliability.py +0 -0
  256. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/_result.py +0 -0
  257. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/mint_bedrock_token.py +0 -0
  258. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_multi_client_stress.py +0 -0
  259. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_optimize_client_committee.py +0 -0
  260. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_readiness.py +0 -0
  261. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_reliability.py +0 -0
  262. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_reliability_classify.py +0 -0
  263. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/stress/test_trace_search.py +0 -0
  264. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_assessments_api.py +0 -0
  265. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_authored.py +0 -0
  266. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_authored_bundle_collisions.py +0 -0
  267. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_authored_metric_contract.py +0 -0
  268. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_authored_optimize.py +0 -0
  269. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_callback.py +0 -0
  270. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_candidate_lineage.py +0 -0
  271. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/__init__.py +0 -0
  272. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/conftest.py +0 -0
  273. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_admin.py +0 -0
  274. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_api_client.py +0 -0
  275. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_assessments.py +0 -0
  276. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_auth.py +0 -0
  277. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_credentials.py +0 -0
  278. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_datasets.py +0 -0
  279. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_deployment_runs.py +0 -0
  280. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_deployments.py +0 -0
  281. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_evals.py +0 -0
  282. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_login.py +0 -0
  283. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_main.py +0 -0
  284. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_optimizations.py +0 -0
  285. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_org.py +0 -0
  286. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_pagination.py +0 -0
  287. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_parity.py +0 -0
  288. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_programs.py +0 -0
  289. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_schema_sample.py +0 -0
  290. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_shell.py +0 -0
  291. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_signatures.py +0 -0
  292. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_tool_servers.py +0 -0
  293. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_traces.py +0 -0
  294. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  295. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_tree_help.py +0 -0
  296. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cli/test_valsets.py +0 -0
  297. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_client.py +0 -0
  298. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_client_floor.py +0 -0
  299. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_client_program_names.py +0 -0
  300. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_clustering/__init__.py +0 -0
  301. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_clustering/conftest.py +0 -0
  302. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_clustering/test_edge_cases.py +0 -0
  303. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  304. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_clustering/test_type_conversion.py +0 -0
  305. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_cmp551_grouping_identity.py +0 -0
  306. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_configuration.py +0 -0
  307. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_context.py +0 -0
  308. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_context_propagation.py +0 -0
  309. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_dataset_polling.py +0 -0
  310. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_dataset_sync.py +0 -0
  311. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_datasets.py +0 -0
  312. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_decision_client_spans.py +0 -0
  313. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_decorators.py +0 -0
  314. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  315. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deploy_program_id.py +0 -0
  316. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deployment_metadata.py +0 -0
  317. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deployment_runs.py +0 -0
  318. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_deployments_read.py +0 -0
  319. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_eval.py +0 -0
  320. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_eval_example_attribution.py +0 -0
  321. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_eval_start.py +0 -0
  322. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_eval_trace_grouping.py +0 -0
  323. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_exception_paths.py +0 -0
  324. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_execute.py +0 -0
  325. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_execute_run_identity.py +0 -0
  326. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_exporter.py +0 -0
  327. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_exporter_program_names.py +0 -0
  328. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_exporter_queue.py +0 -0
  329. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_exporter_real.py +0 -0
  330. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_fake_lm.py +0 -0
  331. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_gepa_callback.py +0 -0
  332. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_gepa_integration.py +0 -0
  333. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_gepa_output_normalization.py +0 -0
  334. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_gepa_resume.py +0 -0
  335. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_identity.py +0 -0
  336. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_imaging.py +0 -0
  337. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_import_weight.py +0 -0
  338. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_ingest_conformance.py +0 -0
  339. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_ingest_second_producer.py +0 -0
  340. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_integration.py +0 -0
  341. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_interpreter_callback.py +0 -0
  342. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_ir_encoder_properties.py +0 -0
  343. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_ir_golden_vectors.py +0 -0
  344. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_ir_roundtrip.py +0 -0
  345. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_lm15_native_calls.py +0 -0
  346. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_lm_configuration.py +0 -0
  347. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_mcp_tools.py +0 -0
  348. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_models.py +0 -0
  349. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_multi_client_stress_readiness.py +0 -0
  350. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimization_progress.py +0 -0
  351. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimization_start.py +0 -0
  352. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize.py +0 -0
  353. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_budget.py +0 -0
  354. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_lift.py +0 -0
  355. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_proposer.py +0 -0
  356. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_optimize_terminal_failure.py +0 -0
  357. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_packaging.py +0 -0
  358. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_packaging_callable_source.py +0 -0
  359. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_packaging_mcp_tools.py +0 -0
  360. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_packaging_program_source.py +0 -0
  361. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_poll_until.py +0 -0
  362. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_program_id_deprecation.py +0 -0
  363. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_program_identity.py +0 -0
  364. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_program_names_in_deploy.py +0 -0
  365. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_programs_create.py +0 -0
  366. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_project_payload.py +0 -0
  367. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_real_provider_twins_readiness.py +0 -0
  368. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_reflector_properties.py +0 -0
  369. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_reflector_smoke.py +0 -0
  370. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_release_stress_script.py +0 -0
  371. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_replaced_names.py +0 -0
  372. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_repro_reactv2_bugs.py +0 -0
  373. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_require_version_bump.py +0 -0
  374. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_retry.py +0 -0
  375. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_reversible_failures.py +0 -0
  376. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_run_attribution_paths.py +0 -0
  377. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_runtime_tools.py +0 -0
  378. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_schemas.py +0 -0
  379. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_seed_review_run.py +0 -0
  380. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_signature_inputs.py +0 -0
  381. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_span_sent_once.py +0 -0
  382. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_trace_context_concurrency.py +0 -0
  383. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_trace_fact_wire_contract.py +0 -0
  384. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_trace_facts.py +0 -0
  385. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_trace_render.py +0 -0
  386. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_traces_read.py +0 -0
  387. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_tracked_gepa.py +0 -0
  388. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_tracker_enrich.py +0 -0
  389. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_truncation.py +0 -0
  390. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_upload_auth_headers.py +0 -0
  391. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_valsets_api.py +0 -0
  392. {cmpnd-0.56.0 → cmpnd-0.58.0}/tests/test_xxh64.py +0 -0
@@ -123,6 +123,40 @@ parameter, and a spec-less mock accepts one anyway.
123
123
  with real LLMs (`CMPND_E2E_*` keys). It never uses a mock or an in-process
124
124
  fake, and it runs only locally.
125
125
 
126
+ ## DSPy versions under test
127
+
128
+ The SDK supports DSPy 3.3.x and 3.4.x, and `dependencies` floors DSPy at
129
+ 3.3.0. `pyproject.toml` declares one dependency group per DSPy version the
130
+ `sdk-test` matrix tests, such as `dspy-3-4-1 = ["dspy==3.4.1"]`.
131
+ `[tool.uv] conflicts` makes the groups mutually exclusive, so `uv.lock` holds a
132
+ whole tree for each DSPy. Dependency groups never reach the wheel, so users
133
+ still install against the `dspy>=…` floor in `dependencies`.
134
+
135
+ - A plain `uv sync` or `uv run` installs the default group, the newest tested
136
+ DSPy.
137
+ - `scripts/install-test-env.sh <python> <dspy>` builds `.venv` from one group's
138
+ locked tree, as each `sdk-test` leg in `ci-sdk.yml` does. From the repo root,
139
+ run `just sdk-test-env 3.13 3.3.0`, then, from `python-sdk/`,
140
+ `.venv/bin/python -m pytest tests/ -k "not e2e"`. `uv sync --extra dev` in
141
+ `python-sdk/` returns to the default group.
142
+ - `--latest` resolves every package fresh, as a user's install today would.
143
+ With the DSPy version `newest` it pins no DSPy either. The daily
144
+ `sdk-latest-dependencies.yml` run uses both: every sdk-test leg, plus one
145
+ leg on the newest DSPy, which is the first to see a DSPy release no group
146
+ tests yet. A failure there alerts Slack.
147
+ - A DSPy release can pin some packages exactly, such as gepa (`dspy==3.3.0`
148
+ pins gepa 0.1.1). Its group locks those packages at the pinned versions. The
149
+ `mcp` extra floors DSPy at 3.3.1, so it conflicts with the `dspy-3-3-0`
150
+ group.
151
+
152
+ To test another DSPy version, add its group to `[dependency-groups]` and to
153
+ the first `conflicts` list, add a conflict with the `mcp` extra if it is below
154
+ 3.3.1, add the leg to `sdk-test`'s matrix and to
155
+ `sdk-latest-dependencies.yml`, and run `uv lock`.
156
+ `.github/scripts/test_workflows.py` fails while the groups and the matrix
157
+ disagree. To move the default, change `default-groups` and refresh the span
158
+ reference fixture with `uv run python -m tests.span_reference_fixture`.
159
+
126
160
  ## Two packages, one published distribution
127
161
 
128
162
  `cmpnd` (`cmpnd/`) is the SDK. `cmpnd_ingest` (`ingest/cmpnd_ingest/`) is the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.56.0
3
+ Version: 0.58.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>
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.14
17
17
  Classifier: Programming Language :: Python :: 3.15
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.11
20
- Requires-Dist: dspy>=3.1.3
20
+ Requires-Dist: dspy>=3.3.0
21
21
  Requires-Dist: httpx>=0.27.0
22
22
  Requires-Dist: ipython>=8.20
23
23
  Requires-Dist: pydantic>=2.0
@@ -205,6 +205,145 @@ The platform makes the program the first time your organization uses the
205
205
  program name. Later deploys under the same program name go to the same
206
206
  program, and so do the traces of a client that runs it.
207
207
 
208
+ ### Credentials
209
+
210
+ A hosted program spends a credential your organization sealed, and the deploy
211
+ names it. The program's code stays the code you run on your laptop:
212
+
213
+ ```python
214
+ lm = dspy.LM("openai/deepseek-4.1-flash", api_base="https://gw.lunaroute.com/v1") # unchanged
215
+
216
+ luna = cmpnd.credentials.get("luna-exec")
217
+ deployed = sdk.deploy_program(program, credential=luna)
218
+ # deploy: default-lm: chat_completions openai/deepseek-4.1-flash → luna-exec (https://gw.lunaroute.com)
219
+ ```
220
+
221
+ - `cmpnd.credentials.get(name)` returns a `cmpnd.Credential` holding the name,
222
+ the protocols its endpoint serves and its `api_base`. It holds no key. A name
223
+ the organization does not hold raises `cmpnd.CredentialNotFound`, which lists
224
+ the names it does hold and the nearest one. `cmpnd.credentials.list()`
225
+ returns them all.
226
+ - `credential=` takes a `cmpnd.Credential`, never a string. A string raises
227
+ and shows the `cmpnd.credentials.get(...)` line to write.
228
+ - Without `credential=`, the deployment spends the organization's default,
229
+ which an owner sets with `cmpnd credentials default NAME`.
230
+ - The deployment records the credential's name. Each call spends the newest
231
+ credential sealed under that name, so an owner who rotates a key needs no
232
+ redeploy.
233
+ - The deploy prints the protocol, model and credential of each binding, as
234
+ above. A server that prints none chose the credential from the model's
235
+ provider prefix, and the SDK warns.
236
+ - A hosted call goes to the credential's `api_base`. The LM's own `api_base`
237
+ serves your laptop run and is not sent.
238
+
239
+ Keys never leave your laptop. The SDK sends no key on the LM, and a key reaches
240
+ the platform only through `cmpnd credentials create`. `cmpnd deploy FILE`
241
+ refuses source whose import builds an LM carrying a key, and names the module,
242
+ the model and the field.
243
+
244
+ ### The same source on your laptop and hosted
245
+
246
+ A program's logic runs unchanged on your laptop and hosted. On your laptop it
247
+ spends the key in your environment. Hosted, it spends a credential your
248
+ organization sealed. Keep the logic in one file and let a small entry point
249
+ choose where it runs.
250
+
251
+ The program file holds the logic and names its model. It holds no key:
252
+
253
+ ```python
254
+ # qa.py
255
+ import dspy
256
+
257
+ lm = dspy.LM("openai/gpt-5-mini") # no api_key: litellm reads OPENAI_API_KEY on your laptop
258
+ program = dspy.Predict("question -> answer")
259
+
260
+
261
+ def metric(gold, pred, trace=None, pred_name=None, pred_trace=None, lms=None):
262
+ verdict = dspy.Predict("answer, gold -> correct: bool")(answer=pred.answer, gold=gold.answer, lm=lms["critic"])
263
+ return dspy.Prediction(score=float(verdict.correct), feedback="correct" if verdict.correct else "wrong")
264
+ ```
265
+
266
+ Seal the key once for the organization and make it the default:
267
+
268
+ ```bash
269
+ printf '%s' "$OPENAI_API_KEY" | cmpnd credentials create --name openai --preset openai
270
+ cmpnd credentials default openai
271
+ ```
272
+
273
+ The entry point is the only code that knows where the run happens:
274
+
275
+ ```python
276
+ # main.py
277
+ import functools
278
+ import sys
279
+
280
+ import cmpnd
281
+ import dspy
282
+
283
+ from qa import lm, metric, program
284
+
285
+ HOSTED = "--hosted" in sys.argv
286
+
287
+ sdk = cmpnd.Client()
288
+ qa = cmpnd.Program("qa", module=program, lm=lm)
289
+ reflection = dspy.LM("openai/gpt-5", max_tokens=32000)
290
+ lms = {"critic": dspy.LM("openai/gpt-5-mini")}
291
+ train, val = load_examples() # your dspy.Example lists
292
+
293
+ if HOSTED:
294
+ deployed = sdk.deploy_program(qa) # spends the default credential, openai
295
+ print(sdk.run_deployment(deployed, question="What is DSPy?"))
296
+ best = sdk.optimize_deployment(
297
+ deployed,
298
+ trainset=train,
299
+ valset=val,
300
+ metric=metric, # the platform hands it lms
301
+ reflection_lm=cmpnd.HostedLM(reflection),
302
+ lms={name: cmpnd.HostedLM(model) for name, model in lms.items()},
303
+ auto="light",
304
+ )
305
+ else:
306
+ print(sdk.run_program(qa, question="What is DSPy?")) # spends $OPENAI_API_KEY
307
+ gepa = dspy.GEPA(metric=functools.partial(metric, lms=lms), reflection_lm=reflection, auto="light")
308
+ best = sdk.optimize_program(qa, gepa, trainset=train, valset=val)
309
+ ```
310
+
311
+ `python main.py` runs everything on your laptop, and `python main.py --hosted`
312
+ runs it on the platform. `cmpnd deploy qa.py qa` deploys the same file from the
313
+ terminal.
314
+
315
+ | | On your laptop | Hosted |
316
+ |---|---|---|
317
+ | Key | the provider's variable in your environment, such as `OPENAI_API_KEY` | the credential the call names, else the organization's default |
318
+ | Address | the LM's `api_base`, else the provider's endpoint | the credential's `api_base` |
319
+ | Model | `gpt-5-mini`, the model after its first `/` | `gpt-5-mini` |
320
+ | The metric's `lms` | the dict the entry point binds | the run's `lms=`, which the platform hands it |
321
+
322
+ These rules keep the program file portable:
323
+
324
+ - **Leave the key off the program's LM** and let litellm read the provider's
325
+ own variable. For a key in a variable litellm does not read, write
326
+ `api_key=os.environ.get("NAME")`. cmpnd imports a deploy file with an empty
327
+ environment, so the key is `None` there and present on your laptop.
328
+ - **Never write a key into the file, and never read it as
329
+ `os.environ["NAME"]` at module scope.** The deploy refuses the first. The
330
+ second raises `KeyError` hosted, where the environment holds no key.
331
+ - **Seal the credential at the address your laptop LM reaches.** A program
332
+ that sets `api_base="https://gw.lunaroute.com/v1"` needs a credential sealed
333
+ there, with `--preset lunaroute` or `--api-base`. Hosted calls go to the
334
+ credential's address and ignore the LM's `api_base`. The deploy prints the
335
+ address it chose, so you can compare the two.
336
+ - **Wrap a model you hand the hosted optimization in `cmpnd.HostedLM`.** The
337
+ entry point builds `reflection_lm=` and each `lms=` entry, so those LMs may
338
+ hold a key for the laptop run. The wrapper leaves that key on your machine.
339
+ `optimize_deployment` refuses a key on an unwrapped LM, because there it would
340
+ look spent. `cmpnd.HostedLM(model, credential=c)` makes a model spend a
341
+ credential other than the run's.
342
+
343
+ A judge built on `dspy.experimental.TypeSafe` also runs unchanged in both
344
+ places. [`docs/sdk-instrumentation.md`](docs/sdk-instrumentation.md), under
345
+ "System One judges", describes the hosted side.
346
+
208
347
  ### Options
209
348
 
210
349
  ```python
@@ -213,6 +352,7 @@ sdk.deploy_program(
213
352
  metric=None, # optional: a metric callable, packaged for optimization
214
353
  timeout=300, # optional: seconds to wait for the deployment to be ready
215
354
  metadata=None, # optional: a JSON-serializable dict, at most 64 KiB, stored with the deployment
355
+ credential=None, # optional: a cmpnd.Credential the program's model spends
216
356
  )
217
357
  ```
218
358
 
@@ -270,11 +410,32 @@ from the deployment.
270
410
  `reflection_lm` is the model that proposes each new instruction. The search is
271
411
  only as good as this model, so it is usually a strong model and not the one
272
412
  being optimized. A `dspy.LM` passes its sampling settings to the provider. A
273
- bare model name gives the same model with no sampling settings. The platform
274
- supplies the credential and the endpoint, so the SDK refuses an `api_key`,
275
- `api_base` or `base_url` on the LM before any network call. On a hosted org,
276
- both live on the provider credential (Account → Model Providers), keyed by the
277
- model's protocol. One `openai` credential serves every `openai/*` model.
413
+ bare model name gives the same model with no sampling settings. A run spends
414
+ sealed credentials only, so the SDK refuses a key on the LM before any network
415
+ call. `cmpnd.HostedLM(lm)` keeps a key your laptop runs spend on this machine
416
+ and passes the LM's model and sampling.
417
+
418
+ `credential=` names the run's credential, which every binding that names none
419
+ spends: the program's model, the reflection model and each entry of `lms`. A
420
+ binding names its own credential through a wrapper for its protocol:
421
+
422
+ ```python
423
+ luna = cmpnd.credentials.get("luna-exec") # serves chat_completions and system_one
424
+ openai = cmpnd.credentials.get("openai")
425
+ best = sdk.optimize_deployment(
426
+ deployed, trainset=train, valset=val, metric=metric,
427
+ credential=luna,
428
+ reflection_lm=cmpnd.HostedLM(dspy.LM("openai/gpt-5"), credential=openai),
429
+ lms={"judge": cmpnd.SystemOne("lunaroute/djev", credential=luna)},
430
+ auto="light",
431
+ )
432
+ ```
433
+
434
+ `cmpnd.HostedLM(lm, credential=c)` wraps a chat LM, and
435
+ `cmpnd.SystemOne(model, credential=c)` declares a System One model. A binding
436
+ without a credential spends the run's, or the organization's default. The
437
+ platform refuses a credential whose endpoint does not serve a binding's
438
+ protocol, and names the `credential=` to add.
278
439
 
279
440
  The SDK checks the metric before any network call. GEPA writes each new
280
441
  instruction from the textual feedback the metric returns, so it needs a
@@ -322,9 +483,9 @@ rules:
322
483
  names.
323
484
  - `"default-lm"` is reserved for the program's own model, which comes from the
324
485
  deployment. Declaring it raises an error that names it.
325
- - The platform refuses a model your org holds no credential for when it dispatches
326
- the run, before the run starts. These LMs, like `reflection_lm`, must not set
327
- `api_key`, `api_base` or `base_url`.
486
+ - The platform refuses a binding it finds no credential for when it dispatches
487
+ the run, before the run starts. These LMs, like `reflection_lm`, must not
488
+ carry a key.
328
489
 
329
490
  The `lms` parameter does not change the arity check. Five or more positional
330
491
  arguments make a feedback metric, and a metric without `lms` never receives
@@ -340,8 +501,9 @@ sdk.optimize_deployment(
340
501
  valset=val, # required: validation examples
341
502
  metric=metric, # required: the metric, arity-checked
342
503
  config=None, # optional: optimizer config, allow-listed by the platform
343
- reflection_lm=None, # required: a dspy.LM or a bare model name
344
- lms=None, # optional: name -> dspy.LM, the run's named models
504
+ reflection_lm=None, # required: a dspy.LM, a bare model name or a cmpnd.HostedLM
505
+ lms=None, # optional: name -> dspy.LM, cmpnd.HostedLM or cmpnd.SystemOne
506
+ credential=None, # optional: the cmpnd.Credential every other binding spends
345
507
  max_metric_calls=None, # budget: the metric-call ceiling for one step
346
508
  auto=None, # budget: "light", "medium" or "heavy"
347
509
  may_author_code=None, # optional: False keeps each dspy.Flex at its deployed source
@@ -154,6 +154,145 @@ The platform makes the program the first time your organization uses the
154
154
  program name. Later deploys under the same program name go to the same
155
155
  program, and so do the traces of a client that runs it.
156
156
 
157
+ ### Credentials
158
+
159
+ A hosted program spends a credential your organization sealed, and the deploy
160
+ names it. The program's code stays the code you run on your laptop:
161
+
162
+ ```python
163
+ lm = dspy.LM("openai/deepseek-4.1-flash", api_base="https://gw.lunaroute.com/v1") # unchanged
164
+
165
+ luna = cmpnd.credentials.get("luna-exec")
166
+ deployed = sdk.deploy_program(program, credential=luna)
167
+ # deploy: default-lm: chat_completions openai/deepseek-4.1-flash → luna-exec (https://gw.lunaroute.com)
168
+ ```
169
+
170
+ - `cmpnd.credentials.get(name)` returns a `cmpnd.Credential` holding the name,
171
+ the protocols its endpoint serves and its `api_base`. It holds no key. A name
172
+ the organization does not hold raises `cmpnd.CredentialNotFound`, which lists
173
+ the names it does hold and the nearest one. `cmpnd.credentials.list()`
174
+ returns them all.
175
+ - `credential=` takes a `cmpnd.Credential`, never a string. A string raises
176
+ and shows the `cmpnd.credentials.get(...)` line to write.
177
+ - Without `credential=`, the deployment spends the organization's default,
178
+ which an owner sets with `cmpnd credentials default NAME`.
179
+ - The deployment records the credential's name. Each call spends the newest
180
+ credential sealed under that name, so an owner who rotates a key needs no
181
+ redeploy.
182
+ - The deploy prints the protocol, model and credential of each binding, as
183
+ above. A server that prints none chose the credential from the model's
184
+ provider prefix, and the SDK warns.
185
+ - A hosted call goes to the credential's `api_base`. The LM's own `api_base`
186
+ serves your laptop run and is not sent.
187
+
188
+ Keys never leave your laptop. The SDK sends no key on the LM, and a key reaches
189
+ the platform only through `cmpnd credentials create`. `cmpnd deploy FILE`
190
+ refuses source whose import builds an LM carrying a key, and names the module,
191
+ the model and the field.
192
+
193
+ ### The same source on your laptop and hosted
194
+
195
+ A program's logic runs unchanged on your laptop and hosted. On your laptop it
196
+ spends the key in your environment. Hosted, it spends a credential your
197
+ organization sealed. Keep the logic in one file and let a small entry point
198
+ choose where it runs.
199
+
200
+ The program file holds the logic and names its model. It holds no key:
201
+
202
+ ```python
203
+ # qa.py
204
+ import dspy
205
+
206
+ lm = dspy.LM("openai/gpt-5-mini") # no api_key: litellm reads OPENAI_API_KEY on your laptop
207
+ program = dspy.Predict("question -> answer")
208
+
209
+
210
+ def metric(gold, pred, trace=None, pred_name=None, pred_trace=None, lms=None):
211
+ verdict = dspy.Predict("answer, gold -> correct: bool")(answer=pred.answer, gold=gold.answer, lm=lms["critic"])
212
+ return dspy.Prediction(score=float(verdict.correct), feedback="correct" if verdict.correct else "wrong")
213
+ ```
214
+
215
+ Seal the key once for the organization and make it the default:
216
+
217
+ ```bash
218
+ printf '%s' "$OPENAI_API_KEY" | cmpnd credentials create --name openai --preset openai
219
+ cmpnd credentials default openai
220
+ ```
221
+
222
+ The entry point is the only code that knows where the run happens:
223
+
224
+ ```python
225
+ # main.py
226
+ import functools
227
+ import sys
228
+
229
+ import cmpnd
230
+ import dspy
231
+
232
+ from qa import lm, metric, program
233
+
234
+ HOSTED = "--hosted" in sys.argv
235
+
236
+ sdk = cmpnd.Client()
237
+ qa = cmpnd.Program("qa", module=program, lm=lm)
238
+ reflection = dspy.LM("openai/gpt-5", max_tokens=32000)
239
+ lms = {"critic": dspy.LM("openai/gpt-5-mini")}
240
+ train, val = load_examples() # your dspy.Example lists
241
+
242
+ if HOSTED:
243
+ deployed = sdk.deploy_program(qa) # spends the default credential, openai
244
+ print(sdk.run_deployment(deployed, question="What is DSPy?"))
245
+ best = sdk.optimize_deployment(
246
+ deployed,
247
+ trainset=train,
248
+ valset=val,
249
+ metric=metric, # the platform hands it lms
250
+ reflection_lm=cmpnd.HostedLM(reflection),
251
+ lms={name: cmpnd.HostedLM(model) for name, model in lms.items()},
252
+ auto="light",
253
+ )
254
+ else:
255
+ print(sdk.run_program(qa, question="What is DSPy?")) # spends $OPENAI_API_KEY
256
+ gepa = dspy.GEPA(metric=functools.partial(metric, lms=lms), reflection_lm=reflection, auto="light")
257
+ best = sdk.optimize_program(qa, gepa, trainset=train, valset=val)
258
+ ```
259
+
260
+ `python main.py` runs everything on your laptop, and `python main.py --hosted`
261
+ runs it on the platform. `cmpnd deploy qa.py qa` deploys the same file from the
262
+ terminal.
263
+
264
+ | | On your laptop | Hosted |
265
+ |---|---|---|
266
+ | Key | the provider's variable in your environment, such as `OPENAI_API_KEY` | the credential the call names, else the organization's default |
267
+ | Address | the LM's `api_base`, else the provider's endpoint | the credential's `api_base` |
268
+ | Model | `gpt-5-mini`, the model after its first `/` | `gpt-5-mini` |
269
+ | The metric's `lms` | the dict the entry point binds | the run's `lms=`, which the platform hands it |
270
+
271
+ These rules keep the program file portable:
272
+
273
+ - **Leave the key off the program's LM** and let litellm read the provider's
274
+ own variable. For a key in a variable litellm does not read, write
275
+ `api_key=os.environ.get("NAME")`. cmpnd imports a deploy file with an empty
276
+ environment, so the key is `None` there and present on your laptop.
277
+ - **Never write a key into the file, and never read it as
278
+ `os.environ["NAME"]` at module scope.** The deploy refuses the first. The
279
+ second raises `KeyError` hosted, where the environment holds no key.
280
+ - **Seal the credential at the address your laptop LM reaches.** A program
281
+ that sets `api_base="https://gw.lunaroute.com/v1"` needs a credential sealed
282
+ there, with `--preset lunaroute` or `--api-base`. Hosted calls go to the
283
+ credential's address and ignore the LM's `api_base`. The deploy prints the
284
+ address it chose, so you can compare the two.
285
+ - **Wrap a model you hand the hosted optimization in `cmpnd.HostedLM`.** The
286
+ entry point builds `reflection_lm=` and each `lms=` entry, so those LMs may
287
+ hold a key for the laptop run. The wrapper leaves that key on your machine.
288
+ `optimize_deployment` refuses a key on an unwrapped LM, because there it would
289
+ look spent. `cmpnd.HostedLM(model, credential=c)` makes a model spend a
290
+ credential other than the run's.
291
+
292
+ A judge built on `dspy.experimental.TypeSafe` also runs unchanged in both
293
+ places. [`docs/sdk-instrumentation.md`](docs/sdk-instrumentation.md), under
294
+ "System One judges", describes the hosted side.
295
+
157
296
  ### Options
158
297
 
159
298
  ```python
@@ -162,6 +301,7 @@ sdk.deploy_program(
162
301
  metric=None, # optional: a metric callable, packaged for optimization
163
302
  timeout=300, # optional: seconds to wait for the deployment to be ready
164
303
  metadata=None, # optional: a JSON-serializable dict, at most 64 KiB, stored with the deployment
304
+ credential=None, # optional: a cmpnd.Credential the program's model spends
165
305
  )
166
306
  ```
167
307
 
@@ -219,11 +359,32 @@ from the deployment.
219
359
  `reflection_lm` is the model that proposes each new instruction. The search is
220
360
  only as good as this model, so it is usually a strong model and not the one
221
361
  being optimized. A `dspy.LM` passes its sampling settings to the provider. A
222
- bare model name gives the same model with no sampling settings. The platform
223
- supplies the credential and the endpoint, so the SDK refuses an `api_key`,
224
- `api_base` or `base_url` on the LM before any network call. On a hosted org,
225
- both live on the provider credential (Account → Model Providers), keyed by the
226
- model's protocol. One `openai` credential serves every `openai/*` model.
362
+ bare model name gives the same model with no sampling settings. A run spends
363
+ sealed credentials only, so the SDK refuses a key on the LM before any network
364
+ call. `cmpnd.HostedLM(lm)` keeps a key your laptop runs spend on this machine
365
+ and passes the LM's model and sampling.
366
+
367
+ `credential=` names the run's credential, which every binding that names none
368
+ spends: the program's model, the reflection model and each entry of `lms`. A
369
+ binding names its own credential through a wrapper for its protocol:
370
+
371
+ ```python
372
+ luna = cmpnd.credentials.get("luna-exec") # serves chat_completions and system_one
373
+ openai = cmpnd.credentials.get("openai")
374
+ best = sdk.optimize_deployment(
375
+ deployed, trainset=train, valset=val, metric=metric,
376
+ credential=luna,
377
+ reflection_lm=cmpnd.HostedLM(dspy.LM("openai/gpt-5"), credential=openai),
378
+ lms={"judge": cmpnd.SystemOne("lunaroute/djev", credential=luna)},
379
+ auto="light",
380
+ )
381
+ ```
382
+
383
+ `cmpnd.HostedLM(lm, credential=c)` wraps a chat LM, and
384
+ `cmpnd.SystemOne(model, credential=c)` declares a System One model. A binding
385
+ without a credential spends the run's, or the organization's default. The
386
+ platform refuses a credential whose endpoint does not serve a binding's
387
+ protocol, and names the `credential=` to add.
227
388
 
228
389
  The SDK checks the metric before any network call. GEPA writes each new
229
390
  instruction from the textual feedback the metric returns, so it needs a
@@ -271,9 +432,9 @@ rules:
271
432
  names.
272
433
  - `"default-lm"` is reserved for the program's own model, which comes from the
273
434
  deployment. Declaring it raises an error that names it.
274
- - The platform refuses a model your org holds no credential for when it dispatches
275
- the run, before the run starts. These LMs, like `reflection_lm`, must not set
276
- `api_key`, `api_base` or `base_url`.
435
+ - The platform refuses a binding it finds no credential for when it dispatches
436
+ the run, before the run starts. These LMs, like `reflection_lm`, must not
437
+ carry a key.
277
438
 
278
439
  The `lms` parameter does not change the arity check. Five or more positional
279
440
  arguments make a feedback metric, and a metric without `lms` never receives
@@ -289,8 +450,9 @@ sdk.optimize_deployment(
289
450
  valset=val, # required: validation examples
290
451
  metric=metric, # required: the metric, arity-checked
291
452
  config=None, # optional: optimizer config, allow-listed by the platform
292
- reflection_lm=None, # required: a dspy.LM or a bare model name
293
- lms=None, # optional: name -> dspy.LM, the run's named models
453
+ reflection_lm=None, # required: a dspy.LM, a bare model name or a cmpnd.HostedLM
454
+ lms=None, # optional: name -> dspy.LM, cmpnd.HostedLM or cmpnd.SystemOne
455
+ credential=None, # optional: the cmpnd.Credential every other binding spends
294
456
  max_metric_calls=None, # budget: the metric-call ceiling for one step
295
457
  auto=None, # budget: "light", "medium" or "heavy"
296
458
  may_author_code=None, # optional: False keeps each dspy.Flex at its deployed source
@@ -17,7 +17,7 @@ from typing import TYPE_CHECKING
17
17
 
18
18
  from cmpnd_ingest.client_floor import UpgradeRequiredWarning
19
19
 
20
- from . import authored, programs, traces
20
+ from . import authored, credentials, programs, traces
21
21
  from ._http import ApiError
22
22
  from .callback import CmpndCallback, trace_id
23
23
  from .client import Client
@@ -27,6 +27,7 @@ from .context import (
27
27
  get_current_span,
28
28
  get_current_trace,
29
29
  )
30
+ from .credentials import Credential, CredentialNotFound
30
31
  from .datasets import DatasetNotFoundError, VersionNotFoundError, load_dataset
31
32
  from .decorators import start_span, trace
32
33
  from .deployment import DeployFailed
@@ -39,7 +40,7 @@ from .deployment_metadata import (
39
40
  from .deployment_runs import RunResultExpired, RunSubmissionUncertain, RunWaitTimeout
40
41
  from .execution import DeployedProgram, RetryableProviderError
41
42
  from .exporter import flush_exporter, shutdown_exporter
42
- from .lm_configuration import SystemOne, configure_lm
43
+ from .lm_configuration import HostedLM, SystemOne, configure_lm
43
44
  from .mcp_servers import (
44
45
  Bearer,
45
46
  McpToolReference,
@@ -70,6 +71,7 @@ __all__ = [
70
71
  "configure",
71
72
  "configure_lm",
72
73
  "SystemOne",
74
+ "HostedLM",
73
75
  "get_config",
74
76
  "require_config",
75
77
  # Main callback
@@ -95,6 +97,10 @@ __all__ = [
95
97
  "traces",
96
98
  # Program name checks (cmpnd.programs.validate_program_name)
97
99
  "programs",
100
+ # The organization's provider credentials (cmpnd.credentials.get / list)
101
+ "credentials",
102
+ "Credential",
103
+ "CredentialNotFound",
98
104
  # A program declared by name
99
105
  "Program",
100
106
  "program",