cmpnd 0.74.0__tar.gz → 0.75.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 (404) hide show
  1. {cmpnd-0.74.0 → cmpnd-0.75.0}/PKG-INFO +1 -1
  2. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_gepa_patch.py +1 -2
  3. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/callback.py +2 -14
  4. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/eval_handler.py +5 -19
  5. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/identity.py +0 -17
  6. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/models.py +1 -15
  7. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/gepa.py +1 -8
  8. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/resume.py +12 -11
  9. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/tracker.py +3 -14
  10. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/program_naming.py +4 -7
  11. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/sdk-instrumentation.md +14 -11
  12. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/trace_facts.py +0 -6
  13. {cmpnd-0.74.0 → cmpnd-0.75.0}/pyproject.toml +1 -1
  14. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/seed_review_run.py +2 -4
  15. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/conftest.py +14 -0
  16. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_composite_module.py +4 -4
  17. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_module_tracing.py +2 -3
  18. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimization_lineage.py +0 -2
  19. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_review_committee.py +0 -1
  20. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_save_load.py +28 -33
  21. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_trace_structure.py +21 -20
  22. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/rlm_shape.py +0 -8
  23. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_trace_streaming.py +0 -1
  24. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_multi_client_stress.py +0 -4
  25. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_callback.py +20 -9
  26. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client_program_names.py +12 -7
  27. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval.py +4 -6
  28. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_program_id.py +2 -9
  29. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter.py +1 -2
  30. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_program_names.py +0 -1
  31. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_queue.py +0 -1
  32. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_real.py +2 -3
  33. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_callback.py +16 -12
  34. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_resume.py +15 -12
  35. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_identity.py +0 -21
  36. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ingest_conformance.py +8 -18
  37. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_models.py +17 -7
  38. cmpnd-0.75.0/tests/test_no_signature_hash_on_the_wire.py +188 -0
  39. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_identity.py +3 -4
  40. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_naming.py +8 -8
  41. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_repro_reactv2_bugs.py +0 -1
  42. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reversible_failures.py +1 -2
  43. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_schemas.py +0 -2
  44. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_fact_wire_contract.py +0 -1
  45. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_facts.py +0 -1
  46. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_tracker_enrich.py +3 -2
  47. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_xxh64.py +0 -2
  48. {cmpnd-0.74.0 → cmpnd-0.75.0}/uv.lock +1 -1
  49. {cmpnd-0.74.0 → cmpnd-0.75.0}/.gitignore +0 -0
  50. {cmpnd-0.74.0 → cmpnd-0.75.0}/AGENTS.md +0 -0
  51. {cmpnd-0.74.0 → cmpnd-0.75.0}/CLAUDE.md +0 -0
  52. {cmpnd-0.74.0 → cmpnd-0.75.0}/README.md +0 -0
  53. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/__init__.py +0 -0
  54. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_assessments.py +0 -0
  55. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_authored_import.py +0 -0
  56. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_deployments.py +0 -0
  57. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_exporter_pool.py +0 -0
  58. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_http.py +0 -0
  59. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_program_patch.py +0 -0
  60. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_rlm_patch.py +0 -0
  61. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_runtime_tools.py +0 -0
  62. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_timespec.py +0 -0
  63. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_traces.py +0 -0
  64. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_valsets.py +0 -0
  65. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/authored.py +0 -0
  66. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/__init__.py +0 -0
  67. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/api_client.py +0 -0
  68. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/__init__.py +0 -0
  69. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/admin.py +0 -0
  70. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/assessments.py +0 -0
  71. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/auth.py +0 -0
  72. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/datasets.py +0 -0
  73. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/deployments.py +0 -0
  74. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/evals.py +0 -0
  75. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/login.py +0 -0
  76. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/optimizations.py +0 -0
  77. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/org.py +0 -0
  78. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/programs.py +0 -0
  79. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  80. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  81. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/top.py +0 -0
  82. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/traces.py +0 -0
  83. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/valsets.py +0 -0
  84. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/credentials.py +0 -0
  85. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/deprecation.py +0 -0
  86. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/pagination.py +0 -0
  87. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/shell.py +0 -0
  88. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/trace_render.py +0 -0
  89. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/tree_help.py +0 -0
  90. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/verbs.py +0 -0
  91. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/views.py +0 -0
  92. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/client.py +0 -0
  93. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/configuration.py +0 -0
  94. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/context.py +0 -0
  95. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/credentials.py +0 -0
  96. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/dataset_sync.py +0 -0
  97. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/datasets.py +0 -0
  98. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/decorators.py +0 -0
  99. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment.py +0 -0
  100. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment_metadata.py +0 -0
  101. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment_runs.py +0 -0
  102. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/execution.py +0 -0
  103. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/exporter.py +0 -0
  104. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/exporter_http.py +0 -0
  105. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/helpers.py +0 -0
  106. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/imaging.py +0 -0
  107. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/__init__.py +0 -0
  108. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/decode.py +0 -0
  109. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/encode.py +0 -0
  110. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/hash.py +0 -0
  111. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/program.py +0 -0
  112. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/reflection.py +0 -0
  113. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/types.py +0 -0
  114. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/xxh64.py +0 -0
  115. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/lm_configuration.py +0 -0
  116. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/mcp_servers.py +0 -0
  117. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/named_programs.py +0 -0
  118. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimization.py +0 -0
  119. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/__init__.py +0 -0
  120. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  121. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/packaging.py +0 -0
  122. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/programs.py +0 -0
  123. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/references.py +0 -0
  124. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/retry.py +0 -0
  125. {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/traces.py +0 -0
  126. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/2026-04-02-code-review.md +0 -0
  127. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/cli.md +0 -0
  128. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/client.md +0 -0
  129. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/custom-proposer.md +0 -0
  130. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  131. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  132. {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/reading-traces.md +0 -0
  133. {cmpnd-0.74.0 → cmpnd-0.75.0}/examples/generalizing_proposer.py +0 -0
  134. {cmpnd-0.74.0 → cmpnd-0.75.0}/examples/local_ollama.py +0 -0
  135. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/LINEAGE.md +0 -0
  136. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/README.md +0 -0
  137. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  138. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  139. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  140. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  141. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  142. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  143. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/py.typed +0 -0
  144. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  145. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/retry.py +0 -0
  146. {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/pyproject.toml +0 -0
  147. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/audit-sdist.sh +0 -0
  148. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/capture_review_run.py +0 -0
  149. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/check-pip-install.sh +0 -0
  150. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/check_release_artifacts.py +0 -0
  151. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/fixtures/review_run.json.gz +0 -0
  152. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/gepa_resume_bug.py +0 -0
  153. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/install-test-env.sh +0 -0
  154. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/release-gate.sh +0 -0
  155. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/release-real-models.sh +0 -0
  156. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/require_version_bump.py +0 -0
  157. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/selfhost_compat.py +0 -0
  158. {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/test-in-linux.sh +0 -0
  159. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/__init__.pyi +0 -0
  160. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  161. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  162. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/__init__.pyi +0 -0
  163. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/callback.pyi +0 -0
  164. {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/dummies.pyi +0 -0
  165. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/__init__.py +0 -0
  166. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_credential_route.py +0 -0
  167. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_deployment_signature_helpers.py +0 -0
  168. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_exporter_helpers.py +0 -0
  169. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_fake_clock.py +0 -0
  170. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_identity_helpers.py +0 -0
  171. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_resolve_refs.py +0 -0
  172. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_traced_run.py +0 -0
  173. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/conftest.py +0 -0
  174. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/__init__.py +0 -0
  175. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/committee_task.py +0 -0
  176. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/continuation_task.py +0 -0
  177. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  178. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  179. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/fake_lm.py +0 -0
  180. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/helpers.py +0 -0
  181. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/target_capabilities.py +0 -0
  182. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_assessments_cli.py +0 -0
  183. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_capture_prompts.py +0 -0
  184. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  185. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_execute.py +0 -0
  186. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_pandas.py +0 -0
  187. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_programs.py +0 -0
  188. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_edge_cases.py +0 -0
  189. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_example_hash.py +0 -0
  190. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_gepa_optimization.py +0 -0
  191. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_interpreter_spans.py +0 -0
  192. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_lm_accuracy.py +0 -0
  193. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  194. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  195. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_continuation.py +0 -0
  196. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  197. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_failures.py +0 -0
  198. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_loop.py +0 -0
  199. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  200. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_parallel_export.py +0 -0
  201. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_project_attribution.py +0 -0
  202. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_react_tool_spans.py +0 -0
  203. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_traces_read.py +0 -0
  204. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_user_attribution.py +0 -0
  205. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_valsets_cli.py +0 -0
  206. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  207. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  208. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  209. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  210. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  211. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  212. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  213. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  214. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  215. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/10_react.json +0 -0
  216. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/11_refine.json +0 -0
  217. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/12_retrieve.json +0 -0
  218. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/13_knn.json +0 -0
  219. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/14_parallel.json +0 -0
  220. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/README.md +0 -0
  221. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/ingest_conformance.py +0 -0
  222. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/__init__.py +0 -0
  223. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/authored_optimize/judge.py +0 -0
  224. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  225. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/conftest.py +0 -0
  226. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  227. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/hosted_optimize_probe.py +0 -0
  228. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/optimization_run_probe.py +0 -0
  229. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  230. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_logs_renders_traces.py +0 -0
  231. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_optimization_run.py +0 -0
  232. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_optimizing_progress.py +0 -0
  233. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_unhappy_path.py +0 -0
  234. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/__init__.py +0 -0
  235. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/__main__.py +0 -0
  236. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/drain.py +0 -0
  237. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/ingest.py +0 -0
  238. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/latency_transport.py +0 -0
  239. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/rlm_size_report.py +0 -0
  240. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/sweep.py +0 -0
  241. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/test_rlm_shape.py +0 -0
  242. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/test_rlm_size_report.py +0 -0
  243. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/__init__.py +0 -0
  244. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/conftest.py +0 -0
  245. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/seed_parity.py +0 -0
  246. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_auth.py +0 -0
  247. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_datasets.py +0 -0
  248. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_deployments.py +0 -0
  249. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_evals.py +0 -0
  250. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_health.py +0 -0
  251. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_modules.py +0 -0
  252. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_optimizations.py +0 -0
  253. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_programs.py +0 -0
  254. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_signatures.py +0 -0
  255. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_traces.py +0 -0
  256. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/__init__.py +0 -0
  257. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_provider.py +0 -0
  258. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_readiness.py +0 -0
  259. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_reliability.py +0 -0
  260. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_result.py +0 -0
  261. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/mint_bedrock_token.py +0 -0
  262. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_bedrock_converse.py +0 -0
  263. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_deploy_programs.py +0 -0
  264. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  265. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_continuation.py +0 -0
  266. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_loop.py +0 -0
  267. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  268. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_stress.py +0 -0
  269. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_provider_roles.py +0 -0
  270. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_readiness.py +0 -0
  271. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_real_provider_twins.py +0 -0
  272. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_reliability.py +0 -0
  273. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_reliability_classify.py +0 -0
  274. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_rlm_wasm.py +0 -0
  275. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_trace_search.py +0 -0
  276. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/span_reference_fixture.py +0 -0
  277. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_adapter_reentrancy.py +0 -0
  278. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_assessments_api.py +0 -0
  279. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored.py +0 -0
  280. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_binding_credentials.py +0 -0
  281. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_bundle_collisions.py +0 -0
  282. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_metric_contract.py +0 -0
  283. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_named_models.py +0 -0
  284. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_optimize.py +0 -0
  285. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_candidate_lineage.py +0 -0
  286. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/__init__.py +0 -0
  287. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/conftest.py +0 -0
  288. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_admin.py +0 -0
  289. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_api_client.py +0 -0
  290. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_assessments.py +0 -0
  291. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_auth.py +0 -0
  292. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_credentials.py +0 -0
  293. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_datasets.py +0 -0
  294. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_deployment_runs.py +0 -0
  295. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_deployments.py +0 -0
  296. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_evals.py +0 -0
  297. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_login.py +0 -0
  298. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_main.py +0 -0
  299. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_optimizations.py +0 -0
  300. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_org.py +0 -0
  301. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_pagination.py +0 -0
  302. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_parity.py +0 -0
  303. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_programs.py +0 -0
  304. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_provider_credentials.py +0 -0
  305. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_schema_sample.py +0 -0
  306. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_shell.py +0 -0
  307. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_tool_servers.py +0 -0
  308. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_top.py +0 -0
  309. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces.py +0 -0
  310. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  311. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  312. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_tree_help.py +0 -0
  313. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_valsets.py +0 -0
  314. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client.py +0 -0
  315. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client_floor.py +0 -0
  316. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/__init__.py +0 -0
  317. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/conftest.py +0 -0
  318. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_edge_cases.py +0 -0
  319. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  320. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_type_conversion.py +0 -0
  321. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cmp551_grouping_identity.py +0 -0
  322. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_configuration.py +0 -0
  323. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_context.py +0 -0
  324. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_context_propagation.py +0 -0
  325. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_kwargs.py +0 -0
  326. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_selection.py +0 -0
  327. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_values.py +0 -0
  328. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_dataset_polling.py +0 -0
  329. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_dataset_sync.py +0 -0
  330. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_datasets.py +0 -0
  331. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_decision_client_spans.py +0 -0
  332. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_decorators.py +0 -0
  333. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy.py +0 -0
  334. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  335. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy_program_id.py +0 -0
  336. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployment_metadata.py +0 -0
  337. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployment_runs.py +0 -0
  338. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployments_read.py +0 -0
  339. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_example_attribution.py +0 -0
  340. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_start.py +0 -0
  341. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_trace_grouping.py +0 -0
  342. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exception_paths.py +0 -0
  343. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_execute.py +0 -0
  344. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_execute_run_identity.py +0 -0
  345. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_fake_lm.py +0 -0
  346. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_integration.py +0 -0
  347. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_output_normalization.py +0 -0
  348. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_imaging.py +0 -0
  349. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_import_weight.py +0 -0
  350. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ingest_second_producer.py +0 -0
  351. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_integration.py +0 -0
  352. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_interpreter_callback.py +0 -0
  353. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_encoder_properties.py +0 -0
  354. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_golden_vectors.py +0 -0
  355. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_roundtrip.py +0 -0
  356. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm15_native_calls.py +0 -0
  357. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm_configuration.py +0 -0
  358. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm_usage_shapes.py +0 -0
  359. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_mcp_tools.py +0 -0
  360. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_multi_client_stress_readiness.py +0 -0
  361. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_program_names.py +0 -0
  362. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_progress.py +0 -0
  363. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_start.py +0 -0
  364. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize.py +0 -0
  365. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_budget.py +0 -0
  366. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_lift.py +0 -0
  367. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_lms.py +0 -0
  368. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_proposer.py +0 -0
  369. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_reflection_lm.py +0 -0
  370. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_terminal_failure.py +0 -0
  371. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging.py +0 -0
  372. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_callable_source.py +0 -0
  373. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_mcp_tools.py +0 -0
  374. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_program_source.py +0 -0
  375. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_poll_until.py +0 -0
  376. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_id_deprecation.py +0 -0
  377. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_lineage_persist.py +0 -0
  378. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_names_in_deploy.py +0 -0
  379. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_programs_create.py +0 -0
  380. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_project_payload.py +0 -0
  381. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_real_provider_twins_readiness.py +0 -0
  382. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reflector_properties.py +0 -0
  383. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reflector_smoke.py +0 -0
  384. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_release_gate_script.py +0 -0
  385. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_release_real_models_script.py +0 -0
  386. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_replaced_names.py +0 -0
  387. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_require_version_bump.py +0 -0
  388. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_retry.py +0 -0
  389. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_rlm_patch.py +0 -0
  390. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_run_attribution_paths.py +0 -0
  391. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_runtime_tools.py +0 -0
  392. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_seal_lm_credential.py +0 -0
  393. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_seed_review_run.py +0 -0
  394. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_signature_inputs.py +0 -0
  395. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_span_references.py +0 -0
  396. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_span_sent_once.py +0 -0
  397. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_system_one.py +0 -0
  398. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_context_concurrency.py +0 -0
  399. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_render.py +0 -0
  400. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_traces_read.py +0 -0
  401. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_tracked_gepa.py +0 -0
  402. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_truncation.py +0 -0
  403. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_upload_auth_headers.py +0 -0
  404. {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_valsets_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.74.0
3
+ Version: 0.75.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>
@@ -133,9 +133,8 @@ def _run_tracked_compile(
133
133
  max_metric_calls=max_metric_calls,
134
134
  )
135
135
 
136
- # `enrich_from_program` hashed the student, so its generated name reuses that hash.
137
136
  if client_config is None and trk.program_id is None and dspy_callback._declared_program_name() is None:
138
- trk.generated_program_name = program_naming.generated_program_name(student, trk.program_signature_hash)
137
+ trk.generated_program_name = program_naming.generated_program_name(student)
139
138
 
140
139
  restore_attribution = callback.start_trace_attribution()
141
140
 
@@ -1188,16 +1188,11 @@ class _ModuleMixin(_CallbackBase):
1188
1188
  identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
1189
1189
  ]
1190
1190
 
1191
- span.program_signature_hash = identity.compute_program_hash(instance) or 0
1192
-
1193
1191
  # For ChainOfThought, also capture extended signature
1194
1192
  if hasattr(instance, "extended_signature"):
1195
1193
  ext_sig = instance.extended_signature
1196
1194
  span.attributes["extended_signature"] = getattr(ext_sig, "signature", str(ext_sig))
1197
1195
 
1198
- else:
1199
- span.program_signature_hash = identity.compute_program_hash(instance) or 0
1200
-
1201
1196
  # Capture demo count and contents if available
1202
1197
  demos = getattr(instance, "demos", None)
1203
1198
  if demos:
@@ -1257,9 +1252,6 @@ class _ModuleMixin(_CallbackBase):
1257
1252
  elif hasattr(instance, "predict") and hasattr(instance.predict, "signature"):
1258
1253
  signature = instance.predict.signature
1259
1254
 
1260
- # The root module's signature hash, once a branch below computes it. An unnamed
1261
- # program's generated name reuses it rather than reflecting the module again.
1262
- program_hash: int | None = None
1263
1255
  if signature:
1264
1256
  # Single-signature module (Predict, ChainOfThought, etc.)
1265
1257
  sig_name = getattr(signature, "__name__", None)
@@ -1287,12 +1279,10 @@ class _ModuleMixin(_CallbackBase):
1287
1279
  identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
1288
1280
  ]
1289
1281
 
1290
- program_hash = identity.compute_program_hash(instance)
1291
- trace.program_signature_hash = program_hash or 0
1292
1282
  trace.signature_summary = identity.build_signature_summary(signature)
1293
1283
  else:
1294
1284
  # Multi-child module: collect Predict children via DSPy's own walker
1295
- # for metadata; the hash is the structural one from the IR reflector.
1285
+ # for metadata.
1296
1286
  dspy_mod, _ = _get_dspy()
1297
1287
  children: list[tuple[str, Any]] = []
1298
1288
  if dspy_mod and hasattr(instance, "named_predictors"):
@@ -1303,8 +1293,6 @@ class _ModuleMixin(_CallbackBase):
1303
1293
 
1304
1294
  if children:
1305
1295
  trace.signature_name = instance.__class__.__name__
1306
- program_hash = identity.compute_program_hash(instance)
1307
- trace.program_signature_hash = program_hash or 0
1308
1296
 
1309
1297
  # Build component_signatures for grouped field display
1310
1298
  components = []
@@ -1340,7 +1328,7 @@ class _ModuleMixin(_CallbackBase):
1340
1328
  if trace.program_id is None and trace.declared_name is None:
1341
1329
  trace.program_id = context.optimization_program_id(trace.optimization_run_id)
1342
1330
  if trace.program_id is None and trace.declared_name is None:
1343
- trace.generated_name = program_naming.generated_program_name(instance, program_hash)
1331
+ trace.generated_name = program_naming.generated_program_name(instance)
1344
1332
  _warn_once_about_generated_name(trace)
1345
1333
 
1346
1334
  # Detect if this is a compiled program (from optimization)
@@ -87,16 +87,10 @@ class EvalHandler:
87
87
  if program:
88
88
  eval_run.module_class = program.__class__.__name__
89
89
 
90
- # Hash via the IR reflector on the live module so the class tag
91
- # discriminates correctly; signature_summary is still derived from
92
- # the root signature for human display.
93
- program_hash = identity.compute_program_hash(program)
94
- if program_hash is not None:
95
- eval_run.program_signature_hash = program_hash
90
+ # The summary comes from the root signature, for human display.
96
91
  signature = identity.get_program_signature(program)
97
92
  if signature:
98
93
  eval_run.signature_summary = identity.build_signature_summary(signature)
99
- logger.debug(f"Eval run program_signature_hash: {eval_run.program_signature_hash}")
100
94
 
101
95
  # Extract input field names for filtering example inputs in on_evaluate_end
102
96
  if hasattr(signature, "input_fields"):
@@ -177,9 +171,7 @@ class EvalHandler:
177
171
  eval_run._dataset_exists = None # Will include examples as fallback
178
172
 
179
173
  exp = self._callback._current_exporter()
180
- eval_run.program_id = self._program_id(
181
- exp, program, eval_run.program_signature_hash, eval_run.optimization_run_id
182
- )
174
+ eval_run.program_id = self._program_id(exp, program, eval_run.optimization_run_id)
183
175
 
184
176
  logger.info(
185
177
  f"EvalRun created: program_id={eval_run.program_id}, "
@@ -216,9 +208,7 @@ class EvalHandler:
216
208
  self._callback._call_id_to_span[call_id] = span
217
209
  context.push_span(span)
218
210
 
219
- def _program_id(
220
- self, exp: Any, program: Any, program_hash: int | None, optimization_run_id: str | None
221
- ) -> str | None:
211
+ def _program_id(self, exp: Any, program: Any, optimization_run_id: str | None) -> str | None:
222
212
  """The program id the eval run carries, resolved the way a trace's is.
223
213
 
224
214
  A program name the call declares wins over the callback's program id. A run
@@ -227,9 +217,7 @@ class EvalHandler:
227
217
  The exporter resolves either name now, on the caller's thread, because the run's
228
218
  start is sent synchronously and must carry the id. The exporter keeps the answer
229
219
  for the process, so a name its traces already resolved costs no request. A name
230
- the exporter cannot resolve leaves the run without an id. ``program_hash`` is the
231
- evaluated program's signature hash, already computed for the run, so naming the
232
- program does not reflect it again.
220
+ the exporter cannot resolve leaves the run without an id.
233
221
  """
234
222
  from . import callback
235
223
 
@@ -238,9 +226,7 @@ class EvalHandler:
238
226
  program_id = self._callback._resolve_program_id() or context.optimization_program_id(optimization_run_id)
239
227
  if program_id is not None or program is None:
240
228
  return program_id
241
- return program_naming.resolve_generated_name(
242
- exp, program_naming.generated_program_name(program, program_hash)
243
- )
229
+ return program_naming.resolve_generated_name(exp, program_naming.generated_program_name(program))
244
230
  # Read with getattr because the member is optional: an in-memory collector
245
231
  # that resolves no names has none.
246
232
  resolve = getattr(exp, "resolve_program_name", None)
@@ -97,23 +97,6 @@ def _unsigned_to_signed_int64(value: int) -> int: # pyright: ignore[reportUnuse
97
97
  return masked - 0x10000000000000000 if masked >= 0x8000000000000000 else masked
98
98
 
99
99
 
100
- def format_signature_hash(value: int | None) -> str | None:
101
- """Render a ``program_signature_hash`` for the JSON wire as a quoted decimal
102
- string (e.g. ``"-935109221252960526"``), or ``None`` when absent.
103
-
104
- The hash is a signed int64 (xxhash64). Emitting it as a JSON *number* is
105
- unsafe: any consumer that decodes through an untyped path floats it and
106
- loses precision above 2^53 (Go ``any``/``map[string]any``, JavaScript
107
- natively). Carrying it as a string makes the safe representation automatic
108
- end to end. Storage stays signed int64; only the wire value is a
109
- string. ``None`` stays ``None`` (nullable optimization hash); the trace/span
110
- ``0`` sentinel renders as ``"0"``.
111
- """
112
- if value is None:
113
- return None
114
- return str(value)
115
-
116
-
117
100
  # A program id is server-issued: ``p_`` and twelve base62 characters, the shape
118
101
  # every platform id shares. The SDK never mints one; it carries the id serve gives
119
102
  # a program name, or the id a deployment's worker passes to the kernel.
@@ -13,7 +13,7 @@ from uuid import UUID, uuid4
13
13
 
14
14
  from cmpnd_ingest.trace_facts import CompletionFact, MetadataFact, StartedFact, StringMapChange
15
15
 
16
- from . import identity, imaging
16
+ from . import imaging
17
17
 
18
18
  # U+0000 (NUL) and lone UTF-16 surrogates are valid JSON, and valid in a Python
19
19
  # str, but the server cannot store either: a JSON column rejects both, and a NUL
@@ -758,8 +758,6 @@ class SpanBuilder:
758
758
  # Each element is None for primitives, or a schema dict for complex types
759
759
  signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
760
760
  signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
761
- # Hash of input/output fields+types for program identity (xxhash64, signed)
762
- program_signature_hash: int = 0
763
761
 
764
762
  # DSPy-specific: Module info
765
763
  module_type: str | None = None
@@ -905,7 +903,6 @@ class SpanBuilder:
905
903
  "signature_output_field_descs": self.signature_output_field_descs,
906
904
  "signature_input_field_schemas": self.signature_input_field_schemas,
907
905
  "signature_output_field_schemas": self.signature_output_field_schemas,
908
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
909
906
  "module_type": self.module_type,
910
907
  "module_name": self.module_name,
911
908
  "model_name": self.model_name,
@@ -945,7 +942,6 @@ class TraceBuilder:
945
942
  # The root DSPy module's class. The program's own name is `declared_name`,
946
943
  # which the transport resolves to `program_id`.
947
944
  module_class: str | None = None
948
- program_version: str | None = None
949
945
 
950
946
  # Signature info - identifies the program by its inputs/outputs
951
947
  signature_name: str | None = None
@@ -956,7 +952,6 @@ class TraceBuilder:
956
952
  # Schemas for complex types (Pydantic models, dataclasses)
957
953
  signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
958
954
  signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
959
- program_signature_hash: int = 0 # Hash of input/output fields+types (legacy program key)
960
955
  signature_summary: str | None = None # Human-readable: "input1, input2 -> output1, output2"
961
956
  component_signatures: list[dict[str, Any]] = field(default_factory=list)
962
957
  # The server-issued program id the trace declares (`p_…`), snapshotted from
@@ -1080,7 +1075,6 @@ class TraceBuilder:
1080
1075
  "module_class": self.module_class,
1081
1076
  # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1082
1077
  "program_name": self.module_class,
1083
- "program_version": self.program_version,
1084
1078
  "signature_name": self.signature_name,
1085
1079
  "signature_input_fields": self.signature_input_fields,
1086
1080
  "signature_output_fields": self.signature_output_fields,
@@ -1088,7 +1082,6 @@ class TraceBuilder:
1088
1082
  "signature_output_field_types": self.signature_output_field_types,
1089
1083
  "signature_input_field_schemas": self.signature_input_field_schemas,
1090
1084
  "signature_output_field_schemas": self.signature_output_field_schemas,
1091
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1092
1085
  "signature_summary": self.signature_summary,
1093
1086
  # The server forwards this blob verbatim into a jsonb column, and its
1094
1087
  # program registration is best-effort (a rejection is only logged), so
@@ -1123,13 +1116,11 @@ class TraceBuilder:
1123
1116
  "module_class": self.module_class,
1124
1117
  # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1125
1118
  "program_name": self.module_class,
1126
- "program_version": self.program_version,
1127
1119
  "signature_name": self.signature_name,
1128
1120
  "signature_input_fields": self.signature_input_fields,
1129
1121
  "signature_output_fields": self.signature_output_fields,
1130
1122
  "signature_input_field_types": self.signature_input_field_types,
1131
1123
  "signature_output_field_types": self.signature_output_field_types,
1132
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1133
1124
  "signature_summary": self.signature_summary,
1134
1125
  "component_signatures": _safe_serialize(self.component_signatures),
1135
1126
  "root_span_type": self.root_span_type,
@@ -1287,9 +1278,6 @@ class EvalRunBuilder:
1287
1278
  metric_name: str | None = None
1288
1279
  dataset_hash: str | None = None
1289
1280
  dataset_size: int = 0
1290
- # The evaluated module's signature hash (legacy program key). None when the run has
1291
- # no module, and then sent as null.
1292
- program_signature_hash: int | None = None
1293
1281
  signature_summary: str | None = None # Human-readable: "input1, input2 -> output1"
1294
1282
 
1295
1283
  # Model Configuration
@@ -1411,7 +1399,6 @@ class EvalRunBuilder:
1411
1399
  "metric_name": self.metric_name or "",
1412
1400
  "dataset_hash": self.dataset_hash or "",
1413
1401
  "dataset_size": self.dataset_size,
1414
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1415
1402
  "signature_summary": self.signature_summary,
1416
1403
  "model_name": self.model_name,
1417
1404
  "model_provider": self.model_provider,
@@ -1432,7 +1419,6 @@ class EvalRunBuilder:
1432
1419
  "metric_name": self.metric_name or "",
1433
1420
  "dataset_hash": self.dataset_hash or "",
1434
1421
  "dataset_size": self.dataset_size,
1435
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1436
1422
  "signature_summary": self.signature_summary,
1437
1423
  "model_name": self.model_name,
1438
1424
  "model_provider": self.model_provider,
@@ -146,10 +146,6 @@ class TrackedGEPA:
146
146
 
147
147
  component_signature_hash = identity.compute_signature_hash(config.get("components", []))
148
148
 
149
- # Compute program_signature_hash via the IR reflector — same encoder
150
- # the trace path uses, so optimization_runs and traces join on identity.
151
- program_signature_hash = identity.compute_program_hash(student) or 0
152
-
153
149
  # Initialize optimization tracking
154
150
  self._tracker = tracker.OptimizationTracker(
155
151
  optimizer_type="gepa",
@@ -159,7 +155,6 @@ class TrackedGEPA:
159
155
  reflection_model_name=reflection_model_name,
160
156
  valset_hash=valset_hash,
161
157
  component_signature_hash=component_signature_hash,
162
- program_signature_hash=program_signature_hash,
163
158
  )
164
159
 
165
160
  # This path builds its tracker without `enrich_from_program`, so it types the
@@ -170,9 +165,7 @@ class TrackedGEPA:
170
165
  # SDK generates for the student, the name its rollouts' traces carry, and
171
166
  # resolves it when the run starts.
172
167
  if self._tracker.program_id is None and callback._declared_program_name() is None:
173
- self._tracker.generated_program_name = program_naming.generated_program_name(
174
- student, program_signature_hash
175
- )
168
+ self._tracker.generated_program_name = program_naming.generated_program_name(student)
176
169
 
177
170
  # Store both sets for dataset payload building
178
171
  self._tracker._trainset = list(trainset)
@@ -30,7 +30,7 @@ def sidecar_path(log_dir: str) -> str:
30
30
  return os.path.join(log_dir, _SIDECAR)
31
31
 
32
32
 
33
- def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None, start_time: datetime | None) -> None:
33
+ def write_run_id(log_dir: str, run_id: str, program_id: str | None, start_time: datetime | None) -> None:
34
34
  """Persist the server-assigned ``run_id`` into ``log_dir`` so a later resume
35
35
  can re-adopt it. Called at ``start()``, which runs before GEPA creates its
36
36
  ``run_dir`` — so create the directory if it isn't there yet. Best-effort: a
@@ -38,7 +38,7 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
38
38
  """
39
39
  payload = {
40
40
  "run_id": run_id,
41
- "program_signature_hash": program_signature_hash,
41
+ "program_id": program_id,
42
42
  "start_time": start_time.isoformat() if start_time is not None else None,
43
43
  }
44
44
  try:
@@ -49,15 +49,16 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
49
49
  logger.warning("could not write resume sidecar in %s: %s", log_dir, e)
50
50
 
51
51
 
52
- def read_resumable(log_dir: str, program_signature_hash: int | None) -> tuple[str, datetime | None] | None:
52
+ def read_resumable(log_dir: str, program_id: str | None) -> tuple[str, datetime | None] | None:
53
53
  """Return ``(run_id, start_time)`` to re-adopt, or ``None`` if this is not a
54
54
  resumable job.
55
55
 
56
56
  Resumable requires: GEPA's ``gepa_state.bin`` checkpoint present (else GEPA
57
57
  itself starts fresh) AND a readable sidecar carrying a ``run_id``. A definite
58
- ``program_signature_hash`` mismatch (a different program reusing the same
59
- ``log_dir``) is rejected so it can't hijack the id; a missing hash on either
60
- side errs toward stitching, since ``log_dir`` is the strong identity signal.
58
+ ``program_id`` mismatch (a different program reusing the same ``log_dir``) is
59
+ rejected so it can't hijack the id. A missing program id on either side errs
60
+ toward stitching, since ``log_dir`` is the strong identity signal. A sidecar an
61
+ older SDK wrote names no program id, so it stitches.
61
62
  """
62
63
  if not os.path.exists(os.path.join(log_dir, _CHECKPOINT)):
63
64
  return None
@@ -71,13 +72,13 @@ def read_resumable(log_dir: str, program_signature_hash: int | None) -> tuple[st
71
72
  if not run_id:
72
73
  return None
73
74
 
74
- prior_hash = data.get("program_signature_hash")
75
- if prior_hash is not None and program_signature_hash is not None and prior_hash != program_signature_hash:
75
+ prior_program = data.get("program_id")
76
+ if prior_program is not None and program_id is not None and prior_program != program_id:
76
77
  logger.warning(
77
- "resume sidecar in %s is for a different program (hash %s != %s); not re-adopting",
78
+ "resume sidecar in %s is for a different program (%s != %s); not re-adopting",
78
79
  log_dir,
79
- prior_hash,
80
- program_signature_hash,
80
+ prior_program,
81
+ program_id,
81
82
  )
82
83
  return None
83
84
 
@@ -274,7 +274,6 @@ class OptimizationTracker:
274
274
  reflection_model_name: str = "",
275
275
  valset_hash: str = "",
276
276
  component_signature_hash: str = "",
277
- program_signature_hash: int = 0,
278
277
  ):
279
278
  # The SDK no longer mints this id. It stays None until start()
280
279
  # runs the synchronous start POST and adopts the id assigned by the
@@ -303,8 +302,6 @@ class OptimizationTracker:
303
302
  self.trainset_hash = "" # individual trainset hash
304
303
  self.valset_individual_hash = "" # individual valset hash
305
304
  self.component_signature_hash = component_signature_hash # SHA256[:16] for model sweep grouping
306
- # Program signature hash - matches trace format (64-bit int from input/output fields+types)
307
- self.program_signature_hash = program_signature_hash
308
305
  self.start_time = datetime.now(timezone.utc)
309
306
 
310
307
  # Collected data
@@ -751,7 +748,7 @@ class OptimizationTracker:
751
748
  log_dir = self.config.get("log_dir") if self.config else None
752
749
  if not log_dir or not self.optimization_run_id:
753
750
  return False
754
- readopted = resume.read_resumable(log_dir, self.program_signature_hash)
751
+ readopted = resume.read_resumable(log_dir, self.program_id)
755
752
  return readopted is not None and readopted[0] == self.optimization_run_id
756
753
 
757
754
  def finalize_error(self, exception: BaseException) -> None:
@@ -853,7 +850,7 @@ class OptimizationTracker:
853
850
  # that id (and its original start_time, so duration stays cumulative)
854
851
  # instead of minting a fresh row. Jobs without a log_dir stay per-run.
855
852
  log_dir = self.config.get("log_dir") if self.config else None
856
- readopted = resume.read_resumable(log_dir, self.program_signature_hash) if log_dir else None
853
+ readopted = resume.read_resumable(log_dir, self.program_id) if log_dir else None
857
854
  if readopted is not None:
858
855
  self.optimization_run_id, prior_start = readopted
859
856
  if prior_start is not None:
@@ -870,7 +867,7 @@ class OptimizationTracker:
870
867
  self._publish_program()
871
868
  # Persist the id so a later resume of the same log_dir re-adopts it.
872
869
  if log_dir and self.optimization_run_id:
873
- resume.write_run_id(log_dir, self.optimization_run_id, self.program_signature_hash, self.start_time)
870
+ resume.write_run_id(log_dir, self.optimization_run_id, self.program_id, self.start_time)
874
871
  logger.info(
875
872
  f"Started optimization tracking: run_id={self.optimization_run_id}, "
876
873
  f"max_metric_calls={self.max_metric_calls}"
@@ -1169,7 +1166,6 @@ class OptimizationTracker:
1169
1166
  "reflection_model_name": self.reflection_model_name,
1170
1167
  "valset_hash": self.valset_hash,
1171
1168
  "component_signature_hash": self.component_signature_hash,
1172
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1173
1169
  "start_time": self.start_time.isoformat() if self.start_time else None,
1174
1170
  "end_time": self.end_time.isoformat() if self.end_time else None,
1175
1171
  "duration_ms": duration_ms,
@@ -1435,13 +1431,6 @@ def enrich_from_program(
1435
1431
  trk.component_signature_hash = identity.compute_signature_hash(components)
1436
1432
  trk.component_types = code_component_types(student)
1437
1433
 
1438
- # Program signature hash from the live module (routes through the IR
1439
- # reflector so Predict vs ChainOfThought discriminates — don't pass a bare
1440
- # signature).
1441
- program_hash = identity.compute_program_hash(student)
1442
- if program_hash is not None:
1443
- trk.program_signature_hash = program_hash
1444
-
1445
1434
  # Model names.
1446
1435
  if lm is not None and getattr(lm, "model", None):
1447
1436
  trk.model_name = lm.model or ""
@@ -34,7 +34,7 @@ _NAME_PUNCTUATION = "._-"
34
34
  _RESERVED_PREFIX = "p_"
35
35
 
36
36
 
37
- def generated_program_name(module: Any, signature_hash: int | None = None) -> str:
37
+ def generated_program_name(module: Any) -> str:
38
38
  """The name the SDK gives the program ``module`` runs as, when its user named none.
39
39
 
40
40
  The name is the module's class and twelve hex digits of its signature hash, such as
@@ -43,13 +43,10 @@ def generated_program_name(module: Any, signature_hash: int | None = None) -> st
43
43
  names. An optimizer's new instructions and demos keep the shape, so they keep the
44
44
  name. The name always fits the program-name syntax, whatever the class is called.
45
45
 
46
- A caller that already holds ``identity.compute_program_hash(module)`` passes it as
47
- ``signature_hash``, so the module is reflected once. Without it, this function
48
- computes the hash.
46
+ The SDK sends the signature hash nowhere else, so naming the program is the only
47
+ place that reflects the module for it.
49
48
  """
50
- if signature_hash is None:
51
- signature_hash = identity.compute_program_hash(module)
52
- signature_hash = signature_hash or 0
49
+ signature_hash = identity.compute_program_hash(module) or 0
53
50
  suffix = f"{signature_hash & 0xFFFF_FFFF_FFFF_FFFF:016x}"[:_SUFFIX_DIGITS]
54
51
  return f"{_class_part(module.__class__.__name__)}-{suffix}"
55
52
 
@@ -139,14 +139,16 @@ and every other name still raises. `TestDspyHookCoverage` in
139
139
  `tests/test_callback.py` asserts the coverage against whatever DSPy is installed,
140
140
  so the next release that adds a hook is caught by CI rather than by a user's logs.
141
141
 
142
- ### Signature extraction and signature_hash
142
+ ### Signature extraction and the program signature hash
143
143
 
144
- This doc does not re-derive hash semantics — see [`./clustering.md`](../../docs/CLUSTERING.md) for why `signature_hash` exists and how xxhash maps to a signed Int64. Here's only _where_ it gets attached:
144
+ No record the SDK sends carries the program signature hash. A trace, an eval run and an
145
+ optimization run name their program by `program_id`.
146
+ [`CLUSTERING.md`](../../docs/CLUSTERING.md) explains how the hash is computed.
145
147
 
146
- - **On a span** — `_capture_module_attributes` in `callback.py`. After walking `instance.signature.input_fields` / `output_fields` and recording types and schemas, it calls `compute_signature_hash(signature)` and stores the result on `span.signature_hash`. Composite modules (multi-child `dspy.Module` subclasses) take the `compute_composite_signature_hash` branch.
147
- - **On a trace** — `_create_trace` in `callback.py`. Same split: single-signature modules hash directly; composite modules walk `dspy.Predict` children and call the composite helper.
148
+ - **On a span** — `_capture_module_attributes` in `callback.py` walks `instance.signature.input_fields` / `output_fields` and records names, types, descriptions and schemas. It reflects nothing, because a span never names a program.
149
+ - **On a trace** — `_create_trace` in `callback.py` records the root signature, or for a composite module the `dspy.Predict` children's signatures as `component_signatures`. A root module call that names no program reflects the module once, for the name the SDK generates (`program_naming.generated_program_name`). The hash goes into that name and nowhere else.
148
150
 
149
- Both code paths import from `python-sdk/cmpnd/identity.py`, which is the authoritative home of every hash the SDK computes.
151
+ `python-sdk/cmpnd/identity.py` is the home of every hash the SDK computes.
150
152
 
151
153
  ---
152
154
 
@@ -690,7 +692,10 @@ the top level and in `started_fact`, and again under `program_name`, the key a
690
692
  serve from before `module_class` reads and digests a start from. A standalone
691
693
  optimizer LM trace sends `GEPA` there. No trace body carries the program's own name: a declared name
692
694
  travels as the program id it resolves to, as "A declared program name resolves
693
- to an id in the exporter" describes.
695
+ to an id in the exporter" describes. No trace body carries a version either. A
696
+ trace serve executed names its version by the `deployment_id` serve stamps on
697
+ it, and `TraceBuilder` has no `program_version` field. A program that wants to
698
+ record a version of its own, such as a git SHA, adds it as a project tag.
694
699
 
695
700
  The callback seals `started_fact` after it sets the root type, before the
696
701
  module executes. It keeps that snapshot even when start emission is disabled.
@@ -735,7 +740,7 @@ What it captures beyond the normal span flow:
735
740
  - An `EvalRunBuilder` (constructed at `eval_handler.py:79`) that groups all per-example traces under one eval ID.
736
741
  - The metric name (via `get_metric_name` from `python-sdk/cmpnd/helpers.py`) and inferred provider.
737
742
  - Links to the enclosing optimization run when one exists (`eval_handler.py:85`), so eval runs triggered inside `GEPA.compile` roll up under the optimization in the UI.
738
- - The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads. A run with no module sends `program_signature_hash` as null.
743
+ - The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads. The run sends no `program_signature_hash`.
739
744
  - The exporter it publishes through: the callback's own (`_current_exporter`), so a client's eval runs go to the client's account.
740
745
  - The dataset — on `on_evaluate_start` it triggers the `dataset_sync` pre-flight described above.
741
746
 
@@ -761,7 +766,6 @@ The trace payload is built in `python-sdk/cmpnd/models.py` by `TraceBuilder.to_a
761
766
  "trace_id": "...",
762
767
  "parent_span_id": "...",
763
768
  "span_type": "predict",
764
- "signature_hash": -3847510293847, # signed Int64
765
769
  "signature_input_fields": ["question"],
766
770
  "signature_output_fields": ["answer"],
767
771
  "model_name": "gpt-4o-mini",
@@ -813,9 +817,8 @@ name form. A program name wins over the config's id. `configure()`
813
817
  checks the id's shape and raises `ValueError` on a bad one, and a `Config` built directly
814
818
  is checked the same way.
815
819
 
816
- The id rides every body that can declare a program. A deployment create names its program
817
- by the id alone and sends no `program_signature_hash`. The other bodies still carry the
818
- hash beside the id:
820
+ The id rides every body that can declare a program. No body carries a
821
+ `program_signature_hash`:
819
822
 
820
823
  | Body | Where | Built by |
821
824
  |---|---|---|
@@ -81,8 +81,6 @@ class StartedFact:
81
81
 
82
82
  start_time: str | None
83
83
  module_class: str | None
84
- program_version: str | None
85
- program_signature_hash: str
86
84
  signature_name: str | None
87
85
  signature_summary: str | None
88
86
  signature_input_fields: tuple[str, ...]
@@ -105,8 +103,6 @@ class StartedFact:
105
103
  return cls(
106
104
  start_time=payload.get("start_time"),
107
105
  module_class=payload.get("module_class"),
108
- program_version=payload.get("program_version"),
109
- program_signature_hash=str(payload["program_signature_hash"]),
110
106
  signature_name=payload.get("signature_name"),
111
107
  signature_summary=payload.get("signature_summary"),
112
108
  signature_input_fields=tuple(payload.get("signature_input_fields") or ()),
@@ -131,8 +127,6 @@ class StartedFact:
131
127
  # a start it accepted matching its replay to a later serve. Dropped
132
128
  # once no such serve runs.
133
129
  "program_name": self.module_class,
134
- "program_version": self.program_version,
135
- "program_signature_hash": self.program_signature_hash,
136
130
  "signature_name": self.signature_name,
137
131
  "signature_summary": self.signature_summary,
138
132
  "signature_input_fields": list(self.signature_input_fields),
@@ -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.74.0"
8
+ version = "0.75.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -28,7 +28,8 @@ shifted so the run lands at "now".
28
28
  Every seeded program is named. The seeder resolves each name to its `p_` id
29
29
  through `PUT /api/v1/programs/by-name/{name}` and declares that id on the
30
30
  program's traces, runs and deployments, and pins by it. The replayed payloads
31
- keep the `program_signature_hash` the SDK captured, as the SDK still sends it.
31
+ keep the `program_signature_hash` an older SDK captured. Serve still accepts one,
32
+ and a program is never found by it.
32
33
 
33
34
  Non-gating: any failure logs and exits 0 (the review pipeline must not break on it).
34
35
 
@@ -396,7 +397,6 @@ def _notebook_trace(now: datetime.datetime) -> dict:
396
397
  "duration_ns": total_ns,
397
398
  "status": "ok",
398
399
  "module_class": "RLM", # DSPy module class → programs.module_class → sidebar badge
399
- "program_version": "0.1.0",
400
400
  "program_signature_hash": fx["program_signature_hash"],
401
401
  "signature_name": fx["signature_name"],
402
402
  "signature_input_fields": fx["input_fields"],
@@ -546,7 +546,6 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
546
546
  "start_time": at(0),
547
547
  "status": "in_progress",
548
548
  "module_class": "RLM",
549
- "program_version": "0.1.0",
550
549
  "program_signature_hash": fx["program_signature_hash"],
551
550
  "signature_name": fx["signature_name"],
552
551
  "signature_input_fields": fx["input_fields"],
@@ -615,7 +614,6 @@ def _replay_flat_fixture(
615
614
  "duration_ns": total_ns,
616
615
  "status": "ok",
617
616
  "module_class": module_class, # DSPy module class → programs.module_class → sidebar badge
618
- "program_version": "0.1.0",
619
617
  "program_signature_hash": root["program_signature_hash"],
620
618
  "signature_name": root["signature_name"],
621
619
  "signature_input_fields": root["signature_input_fields"],
@@ -119,6 +119,20 @@ def flush_and_wait(seconds: float = 3.0) -> None:
119
119
  time.sleep(seconds)
120
120
 
121
121
 
122
+ def generated_program_id(module: Any) -> str:
123
+ """The program id serve gives the name the SDK generates for ``module``.
124
+
125
+ An unnamed program's traces carry this id, so it is the program identity a test
126
+ compares. The name resolves to the same id on every call.
127
+ """
128
+ from cmpnd import program_naming
129
+
130
+ exporter = ensure_exporter_started()
131
+ program_id = program_naming.resolve_generated_name(exporter, program_naming.generated_program_name(module))
132
+ assert program_id is not None, "serve did not resolve the generated program name"
133
+ return program_id
134
+
135
+
122
136
  # ---------------------------------------------------------------------------
123
137
  # ServerClient — thin wrapper around the serve API
124
138
  # ---------------------------------------------------------------------------
@@ -7,14 +7,14 @@ import json
7
7
  import dspy
8
8
  import pytest
9
9
 
10
- from tests.e2e.conftest import flush_and_wait
10
+ from tests.e2e.conftest import flush_and_wait, generated_program_id
11
11
 
12
12
  pytestmark = pytest.mark.e2e
13
13
 
14
14
 
15
15
  class TestCompositeModuleIdentity:
16
- def test_custom_module_has_program_signature_hash(self, server_client, trace_capture):
17
- """A custom Module with Predict children should produce a trace with program_signature_hash."""
16
+ def test_custom_module_trace_names_its_program(self, server_client, trace_capture):
17
+ """A custom Module with Predict children produces a trace that names its program."""
18
18
 
19
19
  class ArticleWriter(dspy.Module):
20
20
  def __init__(self):
@@ -42,7 +42,7 @@ class TestCompositeModuleIdentity:
42
42
  data = server_client.get_trace(trace_id)
43
43
  trace = data["trace"]
44
44
 
45
- assert trace["program_signature_hash"] != 0, "Custom module should have a program_signature_hash"
45
+ assert trace["program_id"] == generated_program_id(writer), "Custom module should name its program"
46
46
  assert trace["module_class"] == "ArticleWriter"
47
47
 
48
48
  def test_custom_module_has_component_signatures(self, server_client, trace_capture):
@@ -35,9 +35,8 @@ class TestPredictBasic:
35
35
  assert span_types & {"predict", "module"}, f"Expected predict or module span, got {span_types}"
36
36
  assert "lm_call" in span_types, f"Expected lm_call span, got {span_types}"
37
37
 
38
- # program_signature_hash present and != 0
39
- assert trace.get("program_signature_hash") is not None
40
- assert trace["program_signature_hash"] != 0
38
+ # The trace names its program by id
39
+ assert trace.get("program_id") is not None
41
40
 
42
41
  # total_tokens > 0
43
42
  assert trace.get("total_tokens", 0) > 0