cmpnd 0.83.0__tar.gz → 0.84.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 (408) hide show
  1. {cmpnd-0.83.0 → cmpnd-0.84.0}/AGENTS.md +7 -1
  2. {cmpnd-0.83.0 → cmpnd-0.84.0}/PKG-INFO +2 -2
  3. {cmpnd-0.83.0 → cmpnd-0.84.0}/README.md +1 -1
  4. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/callback.py +116 -21
  5. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/configuration.py +3 -3
  6. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/context.py +41 -2
  7. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/models.py +130 -6
  8. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/client.md +2 -2
  9. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/sdk-instrumentation.md +26 -5
  10. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/README.md +20 -3
  11. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/bodies.py +14 -7
  12. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/exporter_http.py +84 -47
  13. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/program_names.py +4 -29
  14. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/protocols.py +19 -1
  15. cmpnd-0.84.0/ingest/cmpnd_ingest/span_context.py +63 -0
  16. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/trace_facts.py +4 -0
  17. {cmpnd-0.83.0 → cmpnd-0.84.0}/pyproject.toml +1 -1
  18. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/test-in-linux.sh +18 -4
  19. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/rlm_shape.py +4 -0
  20. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/test_rlm_shape.py +3 -1
  21. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/span_reference_fixture.py +9 -1
  22. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_callback.py +6 -2
  23. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_context.py +20 -0
  24. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_kwargs.py +1 -1
  25. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_selection.py +1 -1
  26. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_program_names.py +7 -4
  27. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_identity.py +2 -2
  28. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_span_references.py +18 -0
  29. cmpnd-0.84.0/tests/test_span_submission.py +492 -0
  30. cmpnd-0.84.0/tests/test_span_submission_wire_contract.py +190 -0
  31. {cmpnd-0.83.0 → cmpnd-0.84.0}/uv.lock +1 -1
  32. {cmpnd-0.83.0 → cmpnd-0.84.0}/.gitignore +0 -0
  33. {cmpnd-0.83.0 → cmpnd-0.84.0}/CLAUDE.md +0 -0
  34. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/__init__.py +0 -0
  35. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_assessments.py +0 -0
  36. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_authored_import.py +0 -0
  37. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_deployments.py +0 -0
  38. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_exporter_pool.py +0 -0
  39. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_gepa_patch.py +0 -0
  40. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_http.py +0 -0
  41. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_program_patch.py +0 -0
  42. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_rlm_patch.py +0 -0
  43. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_runtime_tools.py +0 -0
  44. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_timespec.py +0 -0
  45. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_traces.py +0 -0
  46. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_valsets.py +0 -0
  47. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/authored.py +0 -0
  48. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/__init__.py +0 -0
  49. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/api_client.py +0 -0
  50. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/__init__.py +0 -0
  51. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/admin.py +0 -0
  52. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/assessments.py +0 -0
  53. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/auth.py +0 -0
  54. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/datasets.py +0 -0
  55. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/deployments.py +0 -0
  56. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/evals.py +0 -0
  57. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/login.py +0 -0
  58. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/optimizations.py +0 -0
  59. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/org.py +0 -0
  60. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/programs.py +0 -0
  61. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  62. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  63. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/top.py +0 -0
  64. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/traces.py +0 -0
  65. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/valsets.py +0 -0
  66. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/credentials.py +0 -0
  67. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/deprecation.py +0 -0
  68. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/pagination.py +0 -0
  69. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/shell.py +0 -0
  70. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/trace_render.py +0 -0
  71. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/tree_help.py +0 -0
  72. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/verbs.py +0 -0
  73. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/views.py +0 -0
  74. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/client.py +0 -0
  75. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/credentials.py +0 -0
  76. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/dataset_sync.py +0 -0
  77. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/datasets.py +0 -0
  78. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/decorators.py +0 -0
  79. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment.py +0 -0
  80. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment_metadata.py +0 -0
  81. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment_runs.py +0 -0
  82. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/eval_handler.py +0 -0
  83. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/execution.py +0 -0
  84. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/exporter.py +0 -0
  85. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/exporter_http.py +0 -0
  86. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/helpers.py +0 -0
  87. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/identity.py +0 -0
  88. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/imaging.py +0 -0
  89. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/__init__.py +0 -0
  90. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/decode.py +0 -0
  91. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/encode.py +0 -0
  92. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/hash.py +0 -0
  93. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/program.py +0 -0
  94. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/reflection.py +0 -0
  95. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/types.py +0 -0
  96. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/xxh64.py +0 -0
  97. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/lm_configuration.py +0 -0
  98. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/mcp_servers.py +0 -0
  99. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/named_programs.py +0 -0
  100. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimization.py +0 -0
  101. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/__init__.py +0 -0
  102. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/gepa.py +0 -0
  103. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  104. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/resume.py +0 -0
  105. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/tracker.py +0 -0
  106. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/packaging.py +0 -0
  107. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/program_naming.py +0 -0
  108. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/programs.py +0 -0
  109. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/references.py +0 -0
  110. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/retry.py +0 -0
  111. {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/traces.py +0 -0
  112. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/2026-04-02-code-review.md +0 -0
  113. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/cli.md +0 -0
  114. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/custom-proposer.md +0 -0
  115. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  116. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  117. {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/reading-traces.md +0 -0
  118. {cmpnd-0.83.0 → cmpnd-0.84.0}/examples/generalizing_proposer.py +0 -0
  119. {cmpnd-0.83.0 → cmpnd-0.84.0}/examples/local_ollama.py +0 -0
  120. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/LINEAGE.md +0 -0
  121. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  122. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  123. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/py.typed +0 -0
  124. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  125. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/retry.py +0 -0
  126. {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/pyproject.toml +0 -0
  127. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/audit-sdist.sh +0 -0
  128. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/capture_review_run.py +0 -0
  129. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/check-pip-install.sh +0 -0
  130. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/check_release_artifacts.py +0 -0
  131. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/fixtures/review_run.json.gz +0 -0
  132. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/gepa_resume_bug.py +0 -0
  133. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/install-test-env.sh +0 -0
  134. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/release-gate.sh +0 -0
  135. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/release-real-models.sh +0 -0
  136. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/require_version_bump.py +0 -0
  137. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/seed_review_run.py +0 -0
  138. {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/selfhost_compat.py +0 -0
  139. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/__init__.pyi +0 -0
  140. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  141. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  142. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/__init__.pyi +0 -0
  143. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/callback.pyi +0 -0
  144. {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/dummies.pyi +0 -0
  145. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/__init__.py +0 -0
  146. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_credential_route.py +0 -0
  147. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_deployment_signature_helpers.py +0 -0
  148. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_exporter_helpers.py +0 -0
  149. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_fake_clock.py +0 -0
  150. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_identity_helpers.py +0 -0
  151. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_resolve_refs.py +0 -0
  152. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_traced_run.py +0 -0
  153. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/conftest.py +0 -0
  154. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/__init__.py +0 -0
  155. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/committee_task.py +0 -0
  156. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/conftest.py +0 -0
  157. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/continuation_task.py +0 -0
  158. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  159. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  160. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/fake_lm.py +0 -0
  161. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/helpers.py +0 -0
  162. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/target_capabilities.py +0 -0
  163. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_assessments_cli.py +0 -0
  164. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_capture_prompts.py +0 -0
  165. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_composite_module.py +0 -0
  166. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  167. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_execute.py +0 -0
  168. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_pandas.py +0 -0
  169. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_programs.py +0 -0
  170. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_edge_cases.py +0 -0
  171. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_example_hash.py +0 -0
  172. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_gepa_optimization.py +0 -0
  173. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_interpreter_spans.py +0 -0
  174. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_lm_accuracy.py +0 -0
  175. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_module_tracing.py +0 -0
  176. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimization_lineage.py +0 -0
  177. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  178. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  179. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_continuation.py +0 -0
  180. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  181. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_failures.py +0 -0
  182. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_loop.py +0 -0
  183. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  184. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  185. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_parallel_export.py +0 -0
  186. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_project_attribution.py +0 -0
  187. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_react_tool_spans.py +0 -0
  188. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_save_load.py +0 -0
  189. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_trace_structure.py +0 -0
  190. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_traces_read.py +0 -0
  191. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_user_attribution.py +0 -0
  192. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_valsets_cli.py +0 -0
  193. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  194. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  195. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  196. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  197. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  198. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  199. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  200. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  201. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  202. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/10_react.json +0 -0
  203. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/11_refine.json +0 -0
  204. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/12_retrieve.json +0 -0
  205. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/13_knn.json +0 -0
  206. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/14_parallel.json +0 -0
  207. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/README.md +0 -0
  208. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/ingest_conformance.py +0 -0
  209. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/__init__.py +0 -0
  210. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/authored_optimize/judge.py +0 -0
  211. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  212. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/conftest.py +0 -0
  213. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  214. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/hosted_optimize_probe.py +0 -0
  215. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/optimization_run_probe.py +0 -0
  216. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  217. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_logs_renders_traces.py +0 -0
  218. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_optimization_run.py +0 -0
  219. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_optimizing_progress.py +0 -0
  220. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_unhappy_path.py +0 -0
  221. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/__init__.py +0 -0
  222. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/__main__.py +0 -0
  223. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/drain.py +0 -0
  224. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/ingest.py +0 -0
  225. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/latency_transport.py +0 -0
  226. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/rlm_size_report.py +0 -0
  227. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/sweep.py +0 -0
  228. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/test_rlm_size_report.py +0 -0
  229. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/__init__.py +0 -0
  230. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/conftest.py +0 -0
  231. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/seed_parity.py +0 -0
  232. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_auth.py +0 -0
  233. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_datasets.py +0 -0
  234. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_deployments.py +0 -0
  235. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_evals.py +0 -0
  236. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_health.py +0 -0
  237. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_modules.py +0 -0
  238. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_optimizations.py +0 -0
  239. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_programs.py +0 -0
  240. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_signatures.py +0 -0
  241. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_trace_streaming.py +0 -0
  242. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_traces.py +0 -0
  243. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/__init__.py +0 -0
  244. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_provider.py +0 -0
  245. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_readiness.py +0 -0
  246. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_reliability.py +0 -0
  247. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_result.py +0 -0
  248. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/mint_bedrock_token.py +0 -0
  249. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_bedrock_converse.py +0 -0
  250. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_deploy_programs.py +0 -0
  251. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_multi_client_stress.py +0 -0
  252. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  253. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_continuation.py +0 -0
  254. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_loop.py +0 -0
  255. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  256. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_stress.py +0 -0
  257. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_provider_roles.py +0 -0
  258. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_readiness.py +0 -0
  259. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_real_provider_twins.py +0 -0
  260. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_reliability.py +0 -0
  261. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_reliability_classify.py +0 -0
  262. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_rlm_wasm.py +0 -0
  263. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_trace_search.py +0 -0
  264. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_adapter_reentrancy.py +0 -0
  265. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_assessments_api.py +0 -0
  266. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored.py +0 -0
  267. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_binding_credentials.py +0 -0
  268. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_bundle_collisions.py +0 -0
  269. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_metric_contract.py +0 -0
  270. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_named_models.py +0 -0
  271. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_optimize.py +0 -0
  272. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_candidate_lineage.py +0 -0
  273. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/__init__.py +0 -0
  274. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/conftest.py +0 -0
  275. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_admin.py +0 -0
  276. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_api_client.py +0 -0
  277. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_assessments.py +0 -0
  278. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_auth.py +0 -0
  279. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_credentials.py +0 -0
  280. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_datasets.py +0 -0
  281. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_deployment_runs.py +0 -0
  282. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_deployments.py +0 -0
  283. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_evals.py +0 -0
  284. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_login.py +0 -0
  285. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_main.py +0 -0
  286. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_optimizations.py +0 -0
  287. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_org.py +0 -0
  288. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_pagination.py +0 -0
  289. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_parity.py +0 -0
  290. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_programs.py +0 -0
  291. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_provider_credentials.py +0 -0
  292. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_schema_sample.py +0 -0
  293. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_shell.py +0 -0
  294. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_tool_servers.py +0 -0
  295. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_top.py +0 -0
  296. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces.py +0 -0
  297. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  298. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  299. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_tree_help.py +0 -0
  300. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_valsets.py +0 -0
  301. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client.py +0 -0
  302. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client_floor.py +0 -0
  303. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client_program_names.py +0 -0
  304. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/__init__.py +0 -0
  305. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/conftest.py +0 -0
  306. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_edge_cases.py +0 -0
  307. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  308. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_type_conversion.py +0 -0
  309. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cmp551_grouping_identity.py +0 -0
  310. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_configuration.py +0 -0
  311. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_context_propagation.py +0 -0
  312. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_values.py +0 -0
  313. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_dataset_polling.py +0 -0
  314. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_dataset_sync.py +0 -0
  315. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_datasets.py +0 -0
  316. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_decision_client_spans.py +0 -0
  317. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_decorators.py +0 -0
  318. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy.py +0 -0
  319. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  320. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy_program_id.py +0 -0
  321. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployment_metadata.py +0 -0
  322. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployment_runs.py +0 -0
  323. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployments_read.py +0 -0
  324. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval.py +0 -0
  325. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_example_attribution.py +0 -0
  326. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_program_id.py +0 -0
  327. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_start.py +0 -0
  328. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_trace_grouping.py +0 -0
  329. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exception_paths.py +0 -0
  330. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_execute.py +0 -0
  331. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_execute_run_identity.py +0 -0
  332. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter.py +0 -0
  333. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_queue.py +0 -0
  334. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_real.py +0 -0
  335. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_fake_lm.py +0 -0
  336. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_callback.py +0 -0
  337. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_integration.py +0 -0
  338. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_output_normalization.py +0 -0
  339. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_resume.py +0 -0
  340. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_identity.py +0 -0
  341. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_imaging.py +0 -0
  342. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_import_weight.py +0 -0
  343. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ingest_conformance.py +0 -0
  344. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ingest_second_producer.py +0 -0
  345. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_integration.py +0 -0
  346. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_interpreter_callback.py +0 -0
  347. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_encoder_properties.py +0 -0
  348. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_golden_vectors.py +0 -0
  349. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_roundtrip.py +0 -0
  350. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm15_native_calls.py +0 -0
  351. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm_configuration.py +0 -0
  352. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm_usage_shapes.py +0 -0
  353. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_mcp_tools.py +0 -0
  354. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_models.py +0 -0
  355. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_multi_client_stress_readiness.py +0 -0
  356. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
  357. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_phase_callback.py +0 -0
  358. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_program_names.py +0 -0
  359. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_progress.py +0 -0
  360. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_start.py +0 -0
  361. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize.py +0 -0
  362. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_budget.py +0 -0
  363. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_lift.py +0 -0
  364. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_lms.py +0 -0
  365. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_proposer.py +0 -0
  366. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_reflection_lm.py +0 -0
  367. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_terminal_failure.py +0 -0
  368. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging.py +0 -0
  369. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_callable_source.py +0 -0
  370. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_mcp_tools.py +0 -0
  371. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_program_source.py +0 -0
  372. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_poll_until.py +0 -0
  373. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_id_deprecation.py +0 -0
  374. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_lineage_persist.py +0 -0
  375. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_names_in_deploy.py +0 -0
  376. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_naming.py +0 -0
  377. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_programs_create.py +0 -0
  378. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_project_payload.py +0 -0
  379. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_real_provider_twins_readiness.py +0 -0
  380. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reflector_properties.py +0 -0
  381. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reflector_smoke.py +0 -0
  382. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_release_gate_script.py +0 -0
  383. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_release_real_models_script.py +0 -0
  384. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_replaced_names.py +0 -0
  385. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_repro_reactv2_bugs.py +0 -0
  386. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_require_version_bump.py +0 -0
  387. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_retry.py +0 -0
  388. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reversible_failures.py +0 -0
  389. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_rlm_patch.py +0 -0
  390. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_run_attribution_paths.py +0 -0
  391. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_runtime_tools.py +0 -0
  392. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_schemas.py +0 -0
  393. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_seal_lm_credential.py +0 -0
  394. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_seed_review_run.py +0 -0
  395. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_signature_inputs.py +0 -0
  396. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_span_sent_once.py +0 -0
  397. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_system_one.py +0 -0
  398. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_context_concurrency.py +0 -0
  399. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_fact_wire_contract.py +0 -0
  400. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_facts.py +0 -0
  401. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_render.py +0 -0
  402. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_traces_read.py +0 -0
  403. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_tracked_gepa.py +0 -0
  404. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_tracker_enrich.py +0 -0
  405. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_truncation.py +0 -0
  406. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_upload_auth_headers.py +0 -0
  407. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_valsets_api.py +0 -0
  408. {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_xxh64.py +0 -0
@@ -137,7 +137,13 @@ still install against the `dspy>=…` floor in `dependencies`.
137
137
  - `scripts/install-test-env.sh <python> <dspy>` builds `.venv` from one group's
138
138
  locked tree, as each `sdk-test` leg in `ci-sdk.yml` does. From the repo root,
139
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
140
+ `.venv/bin/python -m pytest tests/ -k "not e2e"`. The root recipe
141
+ `SDK_TEST_PYTHON_VERSION=3.13 SDK_TEST_DSPY_VERSION=3.4.0 just sdk-test-linux`
142
+ runs the same locked environment in Linux. `SDK_TEST_LINUX_IMAGE` selects a
143
+ pinned CI builder when reproducing a job. The helper copies the worker pins
144
+ and truncation vectors and mounts Git metadata read-only for revision tests.
145
+ The revision tests require Git, which the CI builder provides.
146
+ `uv sync --extra dev` in
141
147
  `python-sdk/` returns to the default group.
142
148
  - `--latest` resolves every package fresh, as a user's install today would.
143
149
  With the DSPy version `newest` it pins no DSPy either. The daily
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.83.0
3
+ Version: 0.84.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>
@@ -685,7 +685,7 @@ cmpnd.configure(
685
685
  flush_interval_seconds=5.0, # optional: seconds between flushes
686
686
  capture_inputs=True, # optional: record span inputs
687
687
  capture_outputs=True, # optional: record span outputs
688
- default_tags={"env": "prod"}, # optional: tags on every trace
688
+ default_tags={"env": "prod"}, # optional: tags on every trace; a run's own tags override them
689
689
  )
690
690
  ```
691
691
 
@@ -634,7 +634,7 @@ cmpnd.configure(
634
634
  flush_interval_seconds=5.0, # optional: seconds between flushes
635
635
  capture_inputs=True, # optional: record span inputs
636
636
  capture_outputs=True, # optional: record span outputs
637
- default_tags={"env": "prod"}, # optional: tags on every trace
637
+ default_tags={"env": "prod"}, # optional: tags on every trace; a run's own tags override them
638
638
  )
639
639
  ```
640
640
 
@@ -538,6 +538,7 @@ class _CallbackBase:
538
538
  stream_trace_starts: bool | None = None,
539
539
  own_config: configuration.Config | None = None,
540
540
  optimization_phase: str | None = None,
541
+ producer_context: dict[str, Any] | None = None,
541
542
  ) -> None:
542
543
  # A client passes its own config here; the auto_instrument callback leaves
543
544
  # it None and reads the process-global config live, so a reconfigure()
@@ -547,6 +548,24 @@ class _CallbackBase:
547
548
  # later cmpnd.configure(). That fixedness is what keeps a client
548
549
  # orthogonal to the global config.
549
550
  self._own_config = own_config
551
+ producer_context = producer_context or {}
552
+ self._producer_references = tuple(
553
+ (name, producer_context[name])
554
+ for name in (
555
+ "program_id",
556
+ "deployment_id",
557
+ "deployment_run_id",
558
+ "optimization_run_id",
559
+ "eval_run_id",
560
+ "compiled_from_run_id",
561
+ )
562
+ if producer_context.get(name) is not None
563
+ )
564
+ self._producer_tags = tuple((producer_context.get("tags") or {}).items())
565
+ self._producer_metadata = tuple((producer_context.get("metadata") or {}).items())
566
+ self._producer_projects = (
567
+ tuple(producer_context["project_tags"]) if "project_tags" in producer_context else None
568
+ )
550
569
  # The optimization phase this callback traces for a run it does not own: an
551
570
  # executor that scores a hosted rollout with the author's metric ("metric") or runs
552
571
  # the author's GEPA proposer ("reflection"). Such a process knows the program the
@@ -621,7 +640,7 @@ class _CallbackBase:
621
640
 
622
641
  # Active optimization run ID - stored on instance for same thread-safety reason.
623
642
  # Prefixed text id (`op_…`), minted via cmpnd.identity.
624
- self._active_optimization_run_id: str | None = None
643
+ self._active_optimization_run_id: str | None = dict(self._producer_references).get("optimization_run_id")
625
644
 
626
645
  # Token usage from LM calls without a trace context (e.g., GEPA reflection calls)
627
646
  # Accumulated during optimization and included in the finalization payload
@@ -722,19 +741,18 @@ class _CallbackBase:
722
741
  return configuration._current_program_id()
723
742
 
724
743
  def _stamp(self, builder: Any) -> Any:
725
- """Stamp this callback's project tags, and on a trace its program, onto a
726
- builder it just created. A span's program is its trace's, so a span carries
727
- no program of its own.
744
+ """Stamp this callback's trace context before any spans begin.
728
745
 
729
746
  A trace from a call that declares a program name carries the name and no id,
730
747
  and the exporter resolves the name. The declared name wins over a program id the
731
748
  client's config read from ``CMPND_PROGRAM_ID``, and the callback warns once that it did.
732
749
  A trace that gets neither gets a generated name in `_create_trace`.
733
750
  """
734
- builder.project_tags = self._resolve_project_tags()
735
751
  if isinstance(builder, models.TraceBuilder):
736
- program_id = self._resolve_program_id()
737
- name = _declared_program_name()
752
+ builder.project_tags = list(self._resolve_project_tags() or ())
753
+ producer_program_id = dict(self._producer_references).get("program_id")
754
+ program_id = producer_program_id or self._resolve_program_id()
755
+ name = None if producer_program_id else _declared_program_name()
738
756
  if name is not None:
739
757
  if program_id is not None and not self._warned_name_over_id:
740
758
  self._warned_name_over_id = True
@@ -765,6 +783,18 @@ class _CallbackBase:
765
783
  return _exporter_pool.ensure_exporter_started(self._own_config)
766
784
  return exporter.get_exporter()
767
785
 
786
+ def _apply_producer_context(self, trace: models.TraceBuilder) -> None:
787
+ for name, value in self._producer_references:
788
+ setattr(trace, name, value)
789
+ if dict(self._producer_references).get("program_id") is not None:
790
+ trace.declared_name = trace.generated_name = None
791
+ if self._producer_projects is not None:
792
+ trace.project_tags = list(self._producer_projects)
793
+ # A run's tags and metadata are more specific than the process defaults
794
+ # already on the trace, so they override them.
795
+ trace.tags.update(self._producer_tags)
796
+ trace.metadata.update(self._producer_metadata)
797
+
768
798
  def _ensure_exporter(self) -> Any:
769
799
  """Start this callback's exporter if it is not already running."""
770
800
  if self._own_config is not None:
@@ -780,6 +810,60 @@ class _CallbackBase:
780
810
  span makes ``to_api_dict`` bound the whole body by the same limits rather
781
811
  than by the 64 KiB default.
782
812
  """
813
+ if context.get_current_trace() is None:
814
+ config = self._own_config or configuration.get_config()
815
+ kwargs.setdefault(
816
+ "project_tags",
817
+ list(
818
+ self._producer_projects
819
+ if self._producer_projects is not None
820
+ else self._resolve_project_tags() or ()
821
+ ),
822
+ )
823
+ # Most specific wins: process defaults, then the run's tags, then the span's own.
824
+ kwargs["tags"] = {
825
+ **(config.default_tags if config else {}),
826
+ **dict(self._producer_tags),
827
+ **kwargs.get("tags", {}),
828
+ }
829
+ for key, value in self._producer_references:
830
+ kwargs.setdefault(key, value)
831
+ run_id = self._active_optimization_run_id or context.get_current_optimization_run_id()
832
+ kwargs.setdefault("optimization_run_id", run_id)
833
+ kwargs.setdefault("eval_run_id", self._active_eval_run.eval_run_id if self._active_eval_run else None)
834
+ program_id = (
835
+ kwargs.get("program_id") or self._resolve_program_id() or context.optimization_program_id(run_id)
836
+ )
837
+ declared = None if kwargs.get("program_id") else _declared_program_name()
838
+ if declared is not None:
839
+ program_id = None
840
+ kwargs.setdefault("program_id", program_id)
841
+ from cmpnd_ingest.program_names import ProgramName
842
+ from cmpnd_ingest.span_context import ExportContext, SpanContext
843
+
844
+ kwargs.setdefault(
845
+ "export_context",
846
+ ExportContext(
847
+ ProgramName(declared) if declared else None,
848
+ program_id,
849
+ declared is None or program_id is not None,
850
+ ),
851
+ )
852
+ phase = self._optimization_phase or (
853
+ ("metric" if self._active_eval_run else "reflection") if run_id else None
854
+ )
855
+ if phase is not None:
856
+ kwargs["tags"]["optimization_phase"] = phase
857
+ kwargs.setdefault(
858
+ "span_context",
859
+ SpanContext(
860
+ project_tags=tuple(kwargs["project_tags"]),
861
+ tags=tuple(kwargs["tags"].items()),
862
+ program_id=program_id,
863
+ optimization_run_id=kwargs["optimization_run_id"],
864
+ eval_run_id=kwargs["eval_run_id"],
865
+ ),
866
+ )
783
867
  return context.create_child_span(
784
868
  name,
785
869
  span_type,
@@ -1220,7 +1304,7 @@ class _ModuleMixin(_CallbackBase):
1220
1304
 
1221
1305
  def _create_trace(self, instance: Any, inputs: dict[str, Any]) -> models.TraceBuilder:
1222
1306
  """Create a new trace for a root module call."""
1223
- config = configuration.get_config()
1307
+ config = self._own_config or configuration.get_config()
1224
1308
 
1225
1309
  trace = models.TraceBuilder(
1226
1310
  trace_id=uuid4(),
@@ -1364,16 +1448,16 @@ class _ModuleMixin(_CallbackBase):
1364
1448
  preview = helpers.create_preview(helpers.clean_inputs(inputs, dspy, self._max_input_field_size))
1365
1449
  trace.request_preview = preview
1366
1450
 
1367
- # Apply default tags
1451
+ # Process default tags first, then the run's own labels over them.
1368
1452
  if config and config.default_tags:
1369
1453
  trace.tags.update(config.default_tags)
1454
+ self._apply_producer_context(trace)
1370
1455
  # A trace opened while this callback traces a phase, or while a local run
1371
1456
  # calls its metric, is the metric's or the proposer's, never a rollout, even
1372
1457
  # when its module is the program's own class.
1373
1458
  phase = self._optimization_phase or context.get_current_optimization_phase()
1374
1459
  if phase is not None:
1375
1460
  trace.tags["optimization_phase"] = phase
1376
-
1377
1461
  return trace
1378
1462
 
1379
1463
  def _extract_usage_from_prediction(self, span: models.SpanBuilder, outputs: Any, dspy: Any) -> None:
@@ -1631,7 +1715,8 @@ class _LMMixin(_CallbackBase):
1631
1715
  # would double-count them in the optimization total.
1632
1716
  current_trace = context.get_current_trace()
1633
1717
  if current_trace is None and (
1634
- self._active_optimization_run_id is not None or self._optimization_phase is not None
1718
+ (span.span_context is not None and span.span_context.optimization_run_id is not None)
1719
+ or self._optimization_phase is not None
1635
1720
  ):
1636
1721
  self._emit_optimization_lm_trace(span)
1637
1722
  return # handled as a standalone GEPA-process trace
@@ -1678,11 +1763,12 @@ class _LMMixin(_CallbackBase):
1678
1763
  instead, and may have no run id: it traces the metric or the proposer of a
1679
1764
  run whose id the ingest key the trace is pushed with decides.
1680
1765
  """
1681
- run_id = self._active_optimization_run_id
1766
+ assert span.span_context is not None
1767
+ run_id = span.span_context.optimization_run_id
1682
1768
  if not run_id and self._optimization_phase is None:
1683
1769
  return
1684
1770
 
1685
- phase = self._optimization_phase or ("metric" if self._active_eval_run is not None else "reflection")
1771
+ phase = dict(span.span_context.tags).get("optimization_phase", "reflection")
1686
1772
  span.attributes["gepa.phase"] = phase
1687
1773
  span.attributes["optimization.role"] = _OPTIMIZATION_PHASE_ROLES[phase]
1688
1774
 
@@ -1692,11 +1778,14 @@ class _LMMixin(_CallbackBase):
1692
1778
  end_time=span.end_time,
1693
1779
  module_class="GEPA",
1694
1780
  )
1695
- self._stamp(trace)
1696
- # The call has no root module to name a program after, so a trace that declares
1697
- # neither an id nor a name takes the program the run optimizes.
1698
- if trace.program_id is None and trace.declared_name is None:
1699
- trace.program_id = context.optimization_program_id(run_id)
1781
+ trace.program_id = span.span_context.program_id
1782
+ trace.deployment_id = span.span_context.deployment_id
1783
+ trace.deployment_run_id = span.span_context.deployment_run_id
1784
+ trace.compiled_from_run_id = span.span_context.compiled_from_run_id
1785
+ trace.project_tags = list(span.span_context.project_tags)
1786
+ trace.tags = dict(span.span_context.tags)
1787
+ trace.metadata = dict(self._producer_metadata)
1788
+ trace._export_context = span.export_context
1700
1789
  # A metric-phase call can start while DSPy's Evaluate span is still on the
1701
1790
  # context stack, so on_lm_start stamped this span with a parent_span_id for
1702
1791
  # a span that isn't in this standalone one-span trace. This span IS the
@@ -1707,8 +1796,7 @@ class _LMMixin(_CallbackBase):
1707
1796
  trace.signature_name = span.model_name or phase
1708
1797
  trace.status = span.status
1709
1798
  trace.optimization_run_id = run_id
1710
- if self._active_eval_run is not None:
1711
- trace.eval_run_id = self._active_eval_run.eval_run_id
1799
+ trace.eval_run_id = span.span_context.eval_run_id
1712
1800
  trace.tags["optimization_phase"] = phase
1713
1801
 
1714
1802
  # Single-span trace: roll the span's usage up onto the trace so the
@@ -2169,6 +2257,11 @@ class _InterpreterMixin(_CallbackBase):
2169
2257
  "parent_span": context.get_current_span(),
2170
2258
  "start_time": datetime.now(timezone.utc),
2171
2259
  }
2260
+ trace = context.get_current_trace()
2261
+ parent = frame["parent_span"]
2262
+ frame["trace_id"] = trace.trace_id if trace else parent.trace_id
2263
+ frame["span_context"] = trace.span_context() if trace else parent.span_context
2264
+ frame["export_context"] = trace.export_context if trace else parent.export_context
2172
2265
  self._interpreter_tool_frames[call_id] = frame
2173
2266
  # Publish this frame as the one a nested on_tool_start marks, in this
2174
2267
  # context only. The token restores the prior frame (nested tool calls) on
@@ -2209,13 +2302,15 @@ class _InterpreterMixin(_CallbackBase):
2209
2302
  parent = frame["parent_span"]
2210
2303
  span = models.SpanBuilder(
2211
2304
  span_id=uuid4(),
2212
- trace_id=parent.trace_id if parent else None,
2305
+ trace_id=frame["trace_id"],
2213
2306
  name=f"Tool.{frame['tool_name']}",
2214
2307
  span_type=models.SpanType.TOOL,
2215
2308
  parent_span_id=parent.span_id if parent else None,
2216
2309
  start_time=frame["start_time"],
2217
2310
  max_input_size=self._max_input_field_size,
2218
2311
  max_output_size=self._max_output_field_size,
2312
+ span_context=frame["span_context"],
2313
+ export_context=frame["export_context"],
2219
2314
  )
2220
2315
  span.attributes["tool.name"] = str(frame["tool_name"])
2221
2316
  span.attributes["tool.synthesized"] = "true"
@@ -257,11 +257,11 @@ def require_config() -> Config:
257
257
  def _current_project_tags() -> list[str] | None: # pyright: ignore[reportUnusedFunction] # used cross-module as a dataclass default_factory
258
258
  """Read the currently-configured project tags, or None if unconfigured.
259
259
 
260
- Called at payload serialization time so that a configure(project_tags=...)
261
- call after a TraceBuilder is constructed still gets the fresh value.
260
+ Builders copy these values when execution begins, so later configuration
261
+ changes affect only future traces and spans.
262
262
  """
263
263
  cfg = get_config()
264
- return cfg.project_tags if cfg else None
264
+ return list(cfg.project_tags) if cfg and cfg.project_tags is not None else None
265
265
 
266
266
 
267
267
  def _current_program_id() -> str | None: # pyright: ignore[reportUnusedFunction] # used cross-module as a dataclass default_factory
@@ -2,11 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  from collections.abc import Generator
6
7
  from contextlib import contextmanager
7
- from contextvars import ContextVar
8
+ from contextvars import ContextVar, Token
8
9
  from datetime import datetime, timezone
9
- from typing import TYPE_CHECKING, Any
10
+ from types import TracebackType
11
+ from typing import TYPE_CHECKING, Any, Literal
10
12
  from uuid import uuid4
11
13
 
12
14
  if TYPE_CHECKING:
@@ -14,6 +16,42 @@ if TYPE_CHECKING:
14
16
 
15
17
  # Current trace context
16
18
  _current_trace: ContextVar[models.TraceBuilder | None] = ContextVar("cmpnd_current_trace", default=None)
19
+ _producer_context: ContextVar[str | None] = ContextVar("cmpnd_producer_context", default=None)
20
+
21
+
22
+ def producer_context(encoded: str | None) -> _ProducerContext:
23
+ """Bind one immutable producer snapshot without changing process configuration."""
24
+ return _ProducerContext(encoded)
25
+
26
+
27
+ class _ProducerContext:
28
+ # Generator context managers assign exception.__traceback__ while unwinding.
29
+ # A worker's frozen WIT error must cross this boundary unchanged.
30
+ def __init__(self, encoded: str | None) -> None:
31
+ self._encoded = encoded
32
+ self._token: Token[str | None] | None = None
33
+
34
+ def __enter__(self) -> None:
35
+ if self._token is not None:
36
+ raise RuntimeError("producer context is already active")
37
+ if self._encoded is not None and not isinstance(json.loads(self._encoded), dict):
38
+ raise TypeError("producer context must be a JSON object")
39
+ self._token = _producer_context.set(self._encoded)
40
+
41
+ def __exit__(
42
+ self, _kind: type[BaseException] | None, _error: BaseException | None, _traceback: TracebackType | None
43
+ ) -> Literal[False]:
44
+ if self._token is not None:
45
+ _producer_context.reset(self._token)
46
+ self._token = None
47
+ return False
48
+
49
+
50
+ def _current_producer_context() -> dict[str, Any]: # pyright: ignore[reportUnusedFunction] # read by span builders
51
+ """Return a fresh view so a builder cannot mutate another span's context."""
52
+ encoded = _producer_context.get()
53
+ return json.loads(encoded) if encoded else {}
54
+
17
55
 
18
56
  # Stack of active spans (for nested spans). A tuple, replaced on every push and
19
57
  # pop and never mutated: a context copy (``contextvars.copy_context()``, an
@@ -168,6 +206,7 @@ def create_child_span(
168
206
  def clear_context() -> None:
169
207
  """Clear all trace/span context. Useful for testing."""
170
208
  _current_trace.set(None)
209
+ _producer_context.set(None)
171
210
  _span_stack.set(())
172
211
  _current_eval_run.set(None)
173
212
  _current_optimization_run_id.set(None)
@@ -11,6 +11,9 @@ from enum import Enum
11
11
  from typing import Any
12
12
  from uuid import UUID, uuid4
13
13
 
14
+ from cmpnd_ingest.bodies import SpanBody
15
+ from cmpnd_ingest.program_names import ProgramName
16
+ from cmpnd_ingest.span_context import ExportContext, SpanContext
14
17
  from cmpnd_ingest.trace_facts import CompletionFact, MetadataFact, StartedFact, StringMapChange
15
18
 
16
19
  from . import imaging
@@ -736,9 +739,9 @@ class SpanBuilder:
736
739
  name: str
737
740
  span_type: SpanType = SpanType.UNKNOWN
738
741
  parent_span_id: UUID | None = None
739
- trace_id: UUID | None = None
742
+ trace_id: UUID | None = field(default_factory=uuid4)
740
743
 
741
- start_time: datetime | None = None
744
+ start_time: datetime | None = field(default_factory=lambda: datetime.now(timezone.utc))
742
745
  end_time: datetime | None = None
743
746
 
744
747
  status: TraceStatus = TraceStatus.UNSET
@@ -817,7 +820,73 @@ class SpanBuilder:
817
820
  max_output_size: int | None = None
818
821
 
819
822
  # Snapshot project_tags at construction time to avoid reconfigure races in async export
820
- project_tags: list[str] | None = field(default_factory=configuration._current_project_tags)
823
+ project_tags: list[str] | None = None
824
+ tags: dict[str, str] = field(default_factory=dict)
825
+ program_id: str | None = None
826
+ deployment_id: str | None = None
827
+ deployment_run_id: str | None = None
828
+ optimization_run_id: str | None = None
829
+ eval_run_id: str | None = None
830
+ compiled_from_run_id: str | None = None
831
+ span_context: SpanContext | None = field(default=None, repr=False)
832
+ export_context: ExportContext | None = field(default=None, repr=False)
833
+ _submitted_body: SpanBody | None = field(default=None, init=False, repr=False)
834
+
835
+ def __post_init__(self) -> None:
836
+ from . import context
837
+
838
+ if self.trace_id is None:
839
+ self.trace_id = uuid4()
840
+ if self.start_time is None:
841
+ self.start_time = datetime.now(timezone.utc)
842
+ trace = context.get_current_trace()
843
+ if trace is not None and self.trace_id == trace.trace_id:
844
+ inherited = trace.span_context()
845
+ if self.export_context is None:
846
+ self.export_context = trace.export_context
847
+ else:
848
+ config = configuration.get_config()
849
+ producer = context._current_producer_context()
850
+ optimization_run_id = producer.get("optimization_run_id") or context.get_current_optimization_run_id()
851
+ eval_run = context.get_current_eval_run()
852
+ inherited = SpanContext(
853
+ project_tags=tuple(producer["project_tags"])
854
+ if "project_tags" in producer
855
+ else (tuple(config.project_tags or ()) if config else ()),
856
+ # A run's tags are more specific than the process defaults, so they win.
857
+ tags=tuple({**(config.default_tags if config else {}), **producer.get("tags", {})}.items()),
858
+ program_id=producer.get("program_id")
859
+ or (config.program_id if config else None)
860
+ or context.optimization_program_id(optimization_run_id),
861
+ deployment_id=producer.get("deployment_id"),
862
+ deployment_run_id=producer.get("deployment_run_id"),
863
+ optimization_run_id=optimization_run_id,
864
+ eval_run_id=producer.get("eval_run_id") or (eval_run.eval_run_id if eval_run else None),
865
+ compiled_from_run_id=producer.get("compiled_from_run_id"),
866
+ )
867
+ inherited = self.span_context or inherited
868
+ refs = {
869
+ name: getattr(self, name) if getattr(self, name) is not None else getattr(inherited, name)
870
+ for name in (
871
+ "program_id",
872
+ "deployment_id",
873
+ "deployment_run_id",
874
+ "optimization_run_id",
875
+ "eval_run_id",
876
+ "compiled_from_run_id",
877
+ )
878
+ }
879
+ self.span_context = SpanContext(
880
+ project_tags=tuple(self.project_tags) if self.project_tags is not None else inherited.project_tags,
881
+ tags=tuple({**dict(inherited.tags), **self.tags}.items()),
882
+ **refs,
883
+ )
884
+ self.project_tags = list(self.span_context.project_tags)
885
+ self.tags = dict(self.span_context.tags)
886
+ if self.export_context is None:
887
+ self.export_context = ExportContext(program_id=self.span_context.program_id, resolved=True)
888
+ if self.span_type == SpanType.LM_CALL and self.lm_usage is None:
889
+ self.lm_usage = LMUsageObservationV1()
821
890
 
822
891
  def set_inputs(self, inputs: dict[str, Any]) -> None:
823
892
  """Set span inputs."""
@@ -882,7 +951,13 @@ class SpanBuilder:
882
951
  def to_api_dict(self) -> dict[str, Any]:
883
952
  """Convert to API-compatible dictionary."""
884
953
  max_in, max_out = self._limits()
954
+ if self.span_type == SpanType.LM_CALL:
955
+ if self.lm_usage is None:
956
+ self.lm_usage = LMUsageObservationV1()
957
+ if self.lm_usage.observed_at is None:
958
+ self.lm_usage.observed_at = self.end_time or self.start_time
885
959
  return {
960
+ "schema_version": 1,
886
961
  "span_id": str(self.span_id),
887
962
  "trace_id": str(self.trace_id) if self.trace_id else None,
888
963
  "parent_span_id": str(self.parent_span_id) if self.parent_span_id else None,
@@ -923,10 +998,18 @@ class SpanBuilder:
923
998
  "attributes": _safe_serialize(self._build_attributes(max_out)),
924
999
  "events": truncate_field(_safe_serialize(self.events), max_out),
925
1000
  "adapter_type": self.adapter_type,
926
- "project": self.project_tags[0] if self.project_tags else None,
927
- "project_tags": self.project_tags if self.project_tags else [],
1001
+ "project": self.span_context.project_tags[0]
1002
+ if self.span_context and self.span_context.project_tags
1003
+ else None,
1004
+ **(_safe_serialize(self.span_context.to_dict()) if self.span_context else {}),
928
1005
  }
929
1006
 
1007
+ def seal_for_export(self) -> SpanBody:
1008
+ """Freeze the first completed body for every route that later carries it."""
1009
+ if self._submitted_body is None:
1010
+ self._submitted_body = SpanBody(self.to_api_dict())
1011
+ return self._submitted_body
1012
+
930
1013
 
931
1014
  @dataclass
932
1015
  class TraceBuilder:
@@ -958,6 +1041,8 @@ class TraceBuilder:
958
1041
  # the configuration at construction like project_tags. None declares none,
959
1042
  # and the key is then left off the wire rather than sent as null.
960
1043
  program_id: str | None = field(default_factory=configuration._current_program_id)
1044
+ deployment_id: str | None = None
1045
+ deployment_run_id: str | None = None
961
1046
  # The program name the trace declares, or None. It never goes on the wire: the
962
1047
  # exporter resolves it to a program id and writes that into ``program_id`` on the
963
1048
  # bodies it sends, unless the trace already carries an id.
@@ -1010,10 +1095,41 @@ class TraceBuilder:
1010
1095
  _sealed_tags: dict[str, str] = field(default_factory=dict, init=False, repr=False)
1011
1096
  _sealed_metadata: dict[str, str] = field(default_factory=dict, init=False, repr=False)
1012
1097
  _sealed_project_tags: tuple[str, ...] = field(default=(), init=False, repr=False)
1098
+ _export_context: ExportContext | None = field(default=None, init=False, repr=False)
1099
+
1100
+ @property
1101
+ def export_context(self) -> ExportContext:
1102
+ """One resolution decision shared by the trace and every queued span."""
1103
+ if self._export_context is None:
1104
+ name = (
1105
+ ProgramName(self.declared_name)
1106
+ if self.declared_name
1107
+ else (ProgramName(self.generated_name, generated=True) if self.generated_name else None)
1108
+ )
1109
+ self._export_context = ExportContext(name, self.program_id, self.program_id is not None or name is None)
1110
+ return self._export_context
1111
+
1112
+ @property
1113
+ def span_export_contexts(self) -> tuple[ExportContext | None, ...]:
1114
+ return tuple(span.export_context for span in self._batch_spans)
1115
+
1116
+ def span_context(self) -> SpanContext:
1117
+ """Snapshot the context for a span beginning now."""
1118
+ return SpanContext(
1119
+ project_tags=tuple(self.project_tags or ()),
1120
+ tags=tuple(self.tags.items()),
1121
+ program_id=self.program_id,
1122
+ deployment_id=self.deployment_id,
1123
+ deployment_run_id=self.deployment_run_id,
1124
+ optimization_run_id=self.optimization_run_id,
1125
+ eval_run_id=self.eval_run_id,
1126
+ compiled_from_run_id=self.compiled_from_run_id,
1127
+ )
1013
1128
 
1014
1129
  def seal_start(self) -> None:
1015
1130
  """Retain the first creation snapshot and its independent annotations."""
1016
1131
  if self._started_fact is None:
1132
+ self.export_context
1017
1133
  self._started_fact = StartedFact.from_payload(self.to_start_dict())
1018
1134
  self.seal_metadata()
1019
1135
 
@@ -1052,8 +1168,16 @@ class TraceBuilder:
1052
1168
  self.seal_metadata()
1053
1169
 
1054
1170
  def _with_facts(self, payload: dict[str, Any], *, completion: bool = False, started: bool = True) -> dict[str, Any]:
1171
+ payload["schema_version"] = 1
1172
+ if self.deployment_id is not None:
1173
+ payload["deployment_id"] = self.deployment_id
1174
+ if self.deployment_run_id is not None:
1175
+ payload["deployment_run_id"] = self.deployment_run_id
1055
1176
  if self._started_fact is not None:
1056
1177
  creation = self._started_fact.creation_dict()
1178
+ for name in ("program_id", "deployment_id", "deployment_run_id"):
1179
+ if name not in creation:
1180
+ payload.pop(name, None)
1057
1181
  payload.update(creation if started else {key: value for key, value in creation.items() if key in payload})
1058
1182
  if started:
1059
1183
  payload["started_fact"] = self._started_fact.to_dict()
@@ -1099,7 +1223,7 @@ class TraceBuilder:
1099
1223
  "metadata": _safe_serialize(self.metadata),
1100
1224
  "project": self.project_tags[0] if self.project_tags else None,
1101
1225
  "project_tags": self.project_tags if self.project_tags else [],
1102
- "spans": [s.to_api_dict() for s in self._batch_spans] if self._batch_spans else [],
1226
+ "spans": [s.seal_for_export().to_api_dict() for s in self._batch_spans] if self._batch_spans else [],
1103
1227
  **self._declared_program(),
1104
1228
  },
1105
1229
  completion=True,
@@ -151,8 +151,8 @@ endpoint. None of these reads the global config:
151
151
  metadata=None, tags=None, lm=None, **inputs)` runs one deployment and returns
152
152
  a `dspy.Prediction`. `deployment` is a `DeployedProgram` or a deployment id.
153
153
  `entrypoint` picks the module to run inside a deployment that holds more than
154
- one. `metadata` and `tags` are `dict[str, str]` values the platform applies
155
- to the run's root trace. The run spends the credential its deployment
154
+ one. `metadata` and `tags` are `dict[str, str]` values the run's traces and
155
+ spans carry, overriding the worker process's `default_tags`. The run spends the credential its deployment
156
156
  recorded. A `program_name` among the inputs raises `TypeError` and names
157
157
  `entrypoint=`.
158
158
  - `sdk.submit_deployment_run`, `list_deployment_runs`, `get_deployment_run`,