cmpnd 0.61.0__tar.gz → 0.62.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 (398) hide show
  1. {cmpnd-0.61.0 → cmpnd-0.62.0}/PKG-INFO +1 -1
  2. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_traces.py +7 -2
  3. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/callback.py +2 -2
  4. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/top.py +17 -4
  5. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/traces.py +40 -1
  6. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/shell.py +6 -4
  7. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/trace_render.py +29 -2
  8. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/models.py +9 -3
  9. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/cli.md +26 -4
  10. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/reading-traces.md +3 -2
  11. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/sdk-instrumentation.md +7 -0
  12. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/trace_facts.py +8 -3
  13. {cmpnd-0.61.0 → cmpnd-0.62.0}/pyproject.toml +1 -1
  14. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/capture_review_run.py +5 -5
  15. cmpnd-0.62.0/scripts/fixtures/review_run.json.gz +0 -0
  16. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/seed_review_run.py +5 -5
  17. cmpnd-0.62.0/tests/e2e/target_capabilities.py +58 -0
  18. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_composite_module.py +1 -1
  19. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_deploy_execute.py +0 -3
  20. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_deploy_pandas.py +6 -0
  21. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_deploy_programs.py +5 -0
  22. cmpnd-0.62.0/tests/e2e/test_optimize_authored_metric.py +78 -0
  23. cmpnd-0.62.0/tests/e2e/test_optimize_custom_proposer.py +158 -0
  24. cmpnd-0.62.0/tests/e2e/test_optimize_failures.py +69 -0
  25. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_optimize_server_owned_completion.py +7 -1
  26. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_parallel_export.py +2 -2
  27. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_project_attribution.py +1 -1
  28. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/rlm_shape.py +1 -1
  29. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_trace_streaming.py +1 -1
  30. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_traces.py +4 -4
  31. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/span_reference_fixture.py +1 -1
  32. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/_provider.py +19 -25
  33. cmpnd-0.62.0/tests/stress/test_bedrock_converse.py +127 -0
  34. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_multi_client_stress.py +15 -5
  35. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_callback.py +4 -4
  36. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_shell.py +3 -1
  37. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_traces.py +2 -2
  38. cmpnd-0.62.0/tests/test_cli/test_traces_program_and_module.py +274 -0
  39. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_traces_search_wire.py +4 -1
  40. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_client.py +2 -2
  41. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_client_program_names.py +2 -2
  42. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_context_propagation.py +1 -1
  43. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_eval_example_attribution.py +6 -6
  44. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_eval_trace_grouping.py +3 -3
  45. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_exporter.py +3 -3
  46. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_exporter_program_names.py +1 -1
  47. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_exporter_queue.py +1 -1
  48. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_exporter_real.py +3 -3
  49. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_ingest_conformance.py +1 -1
  50. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_integration.py +1 -1
  51. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_models.py +6 -6
  52. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_multi_client_stress_readiness.py +6 -5
  53. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_program_identity.py +1 -1
  54. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_project_payload.py +7 -7
  55. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_repro_reactv2_bugs.py +1 -1
  56. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_run_attribution_paths.py +1 -1
  57. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_schemas.py +1 -1
  58. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_trace_fact_wire_contract.py +1 -1
  59. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_trace_facts.py +8 -8
  60. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_trace_render.py +3 -2
  61. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_truncation.py +2 -2
  62. {cmpnd-0.61.0 → cmpnd-0.62.0}/uv.lock +1 -1
  63. cmpnd-0.61.0/scripts/fixtures/review_run.json.gz +0 -0
  64. cmpnd-0.61.0/tests/e2e/target_capabilities.py +0 -70
  65. cmpnd-0.61.0/tests/e2e/test_optimize_failures.py +0 -136
  66. {cmpnd-0.61.0 → cmpnd-0.62.0}/.gitignore +0 -0
  67. {cmpnd-0.61.0 → cmpnd-0.62.0}/AGENTS.md +0 -0
  68. {cmpnd-0.61.0 → cmpnd-0.62.0}/CLAUDE.md +0 -0
  69. {cmpnd-0.61.0 → cmpnd-0.62.0}/README.md +0 -0
  70. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/__init__.py +0 -0
  71. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_assessments.py +0 -0
  72. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_authored_import.py +0 -0
  73. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_deployments.py +0 -0
  74. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_exporter_pool.py +0 -0
  75. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_gepa_patch.py +0 -0
  76. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_http.py +0 -0
  77. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_program_patch.py +0 -0
  78. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_rlm_patch.py +0 -0
  79. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_runtime_tools.py +0 -0
  80. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_timespec.py +0 -0
  81. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/_valsets.py +0 -0
  82. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/authored.py +0 -0
  83. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/__init__.py +0 -0
  84. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/api_client.py +0 -0
  85. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/__init__.py +0 -0
  86. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/admin.py +0 -0
  87. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/assessments.py +0 -0
  88. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/auth.py +0 -0
  89. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/datasets.py +0 -0
  90. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/deployments.py +0 -0
  91. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/evals.py +0 -0
  92. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/login.py +0 -0
  93. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/optimizations.py +0 -0
  94. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/org.py +0 -0
  95. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/programs.py +0 -0
  96. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  97. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/signatures.py +0 -0
  98. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  99. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/commands/valsets.py +0 -0
  100. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/credentials.py +0 -0
  101. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/deprecation.py +0 -0
  102. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/pagination.py +0 -0
  103. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/tree_help.py +0 -0
  104. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/cli/verbs.py +0 -0
  105. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/client.py +0 -0
  106. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/configuration.py +0 -0
  107. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/context.py +0 -0
  108. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/credentials.py +0 -0
  109. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/dataset_sync.py +0 -0
  110. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/datasets.py +0 -0
  111. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/decorators.py +0 -0
  112. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/deployment.py +0 -0
  113. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/deployment_metadata.py +0 -0
  114. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/deployment_runs.py +0 -0
  115. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/eval_handler.py +0 -0
  116. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/execution.py +0 -0
  117. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/exporter.py +0 -0
  118. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/exporter_http.py +0 -0
  119. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/helpers.py +0 -0
  120. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/identity.py +0 -0
  121. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/imaging.py +0 -0
  122. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/__init__.py +0 -0
  123. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/decode.py +0 -0
  124. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/encode.py +0 -0
  125. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/hash.py +0 -0
  126. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/program.py +0 -0
  127. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/reflection.py +0 -0
  128. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/types.py +0 -0
  129. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/ir/xxh64.py +0 -0
  130. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/lm_configuration.py +0 -0
  131. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/mcp_servers.py +0 -0
  132. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/named_programs.py +0 -0
  133. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimization.py +0 -0
  134. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimizers/__init__.py +0 -0
  135. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimizers/gepa.py +0 -0
  136. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  137. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimizers/resume.py +0 -0
  138. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/optimizers/tracker.py +0 -0
  139. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/packaging.py +0 -0
  140. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/programs.py +0 -0
  141. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/references.py +0 -0
  142. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/retry.py +0 -0
  143. {cmpnd-0.61.0 → cmpnd-0.62.0}/cmpnd/traces.py +0 -0
  144. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/2026-04-02-code-review.md +0 -0
  145. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/client.md +0 -0
  146. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/custom-proposer.md +0 -0
  147. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  148. {cmpnd-0.61.0 → cmpnd-0.62.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  149. {cmpnd-0.61.0 → cmpnd-0.62.0}/examples/generalizing_proposer.py +0 -0
  150. {cmpnd-0.61.0 → cmpnd-0.62.0}/examples/local_ollama.py +0 -0
  151. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/LINEAGE.md +0 -0
  152. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/README.md +0 -0
  153. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  154. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  155. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  156. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  157. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  158. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  159. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/py.typed +0 -0
  160. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  161. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/cmpnd_ingest/retry.py +0 -0
  162. {cmpnd-0.61.0 → cmpnd-0.62.0}/ingest/pyproject.toml +0 -0
  163. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/audit-sdist.sh +0 -0
  164. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/check-pip-install.sh +0 -0
  165. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/check_release_artifacts.py +0 -0
  166. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/gepa_resume_bug.py +0 -0
  167. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/install-test-env.sh +0 -0
  168. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/release-stress.sh +0 -0
  169. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/require_version_bump.py +0 -0
  170. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/selfhost_compat.py +0 -0
  171. {cmpnd-0.61.0 → cmpnd-0.62.0}/scripts/test-in-linux.sh +0 -0
  172. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/__init__.pyi +0 -0
  173. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  174. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  175. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/utils/__init__.pyi +0 -0
  176. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/utils/callback.pyi +0 -0
  177. {cmpnd-0.61.0 → cmpnd-0.62.0}/stubs/dspy/utils/dummies.pyi +0 -0
  178. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/__init__.py +0 -0
  179. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_credential_route.py +0 -0
  180. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_deployment_signature_helpers.py +0 -0
  181. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_exporter_helpers.py +0 -0
  182. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_fake_clock.py +0 -0
  183. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_identity_helpers.py +0 -0
  184. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_resolve_refs.py +0 -0
  185. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/_traced_run.py +0 -0
  186. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/conftest.py +0 -0
  187. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/__init__.py +0 -0
  188. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/committee_task.py +0 -0
  189. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/conftest.py +0 -0
  190. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  191. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  192. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/fake_lm.py +0 -0
  193. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/helpers.py +0 -0
  194. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_assessments_cli.py +0 -0
  195. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_capture_prompts.py +0 -0
  196. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  197. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_edge_cases.py +0 -0
  198. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_example_hash.py +0 -0
  199. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_gepa_optimization.py +0 -0
  200. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_interpreter_spans.py +0 -0
  201. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_lm_accuracy.py +0 -0
  202. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_module_tracing.py +0 -0
  203. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_optimization_lineage.py +0 -0
  204. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  205. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_optimize_loop.py +0 -0
  206. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  207. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_react_tool_spans.py +0 -0
  208. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_save_load.py +0 -0
  209. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_trace_structure.py +0 -0
  210. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_traces_read.py +0 -0
  211. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_user_attribution.py +0 -0
  212. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/e2e/test_valsets_cli.py +0 -0
  213. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  214. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  215. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  216. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  217. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  218. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  219. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  220. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  221. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  222. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/10_react.json +0 -0
  223. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/11_refine.json +0 -0
  224. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/12_retrieve.json +0 -0
  225. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/13_knn.json +0 -0
  226. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/14_parallel.json +0 -0
  227. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/identity_vectors/README.md +0 -0
  228. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/ingest_conformance.py +0 -0
  229. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/__init__.py +0 -0
  230. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/authored_optimize/judge.py +0 -0
  231. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  232. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/conftest.py +0 -0
  233. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  234. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/hosted_optimize_probe.py +0 -0
  235. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/optimization_run_probe.py +0 -0
  236. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  237. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/test_logs_renders_traces.py +0 -0
  238. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/test_optimization_run.py +0 -0
  239. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/test_optimizing_progress.py +0 -0
  240. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/integration/test_unhappy_path.py +0 -0
  241. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/__init__.py +0 -0
  242. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/__main__.py +0 -0
  243. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/drain.py +0 -0
  244. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/ingest.py +0 -0
  245. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/latency_transport.py +0 -0
  246. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/rlm_size_report.py +0 -0
  247. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/sweep.py +0 -0
  248. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/test_rlm_shape.py +0 -0
  249. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/load/test_rlm_size_report.py +0 -0
  250. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/__init__.py +0 -0
  251. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/conftest.py +0 -0
  252. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/seed_parity.py +0 -0
  253. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_auth.py +0 -0
  254. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_datasets.py +0 -0
  255. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_deployments.py +0 -0
  256. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_evals.py +0 -0
  257. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_health.py +0 -0
  258. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_modules.py +0 -0
  259. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_optimizations.py +0 -0
  260. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_programs.py +0 -0
  261. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/parity/test_signatures.py +0 -0
  262. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/__init__.py +0 -0
  263. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/_readiness.py +0 -0
  264. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/_reliability.py +0 -0
  265. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/_result.py +0 -0
  266. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/mint_bedrock_token.py +0 -0
  267. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_deploy_programs.py +0 -0
  268. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_optimize_client_committee.py +0 -0
  269. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_optimize_loop.py +0 -0
  270. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
  271. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_optimize_stress.py +0 -0
  272. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_provider_roles.py +0 -0
  273. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_readiness.py +0 -0
  274. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_real_provider_twins.py +0 -0
  275. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_reliability.py +0 -0
  276. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_reliability_classify.py +0 -0
  277. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_rlm_wasm.py +0 -0
  278. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/stress/test_trace_search.py +0 -0
  279. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_adapter_reentrancy.py +0 -0
  280. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_assessments_api.py +0 -0
  281. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored.py +0 -0
  282. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored_binding_credentials.py +0 -0
  283. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored_bundle_collisions.py +0 -0
  284. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored_metric_contract.py +0 -0
  285. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored_named_models.py +0 -0
  286. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_authored_optimize.py +0 -0
  287. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_candidate_lineage.py +0 -0
  288. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/__init__.py +0 -0
  289. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/conftest.py +0 -0
  290. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_admin.py +0 -0
  291. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_api_client.py +0 -0
  292. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_assessments.py +0 -0
  293. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_auth.py +0 -0
  294. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_credentials.py +0 -0
  295. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_datasets.py +0 -0
  296. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_deployment_runs.py +0 -0
  297. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_deployments.py +0 -0
  298. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_evals.py +0 -0
  299. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_login.py +0 -0
  300. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_main.py +0 -0
  301. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_optimizations.py +0 -0
  302. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_org.py +0 -0
  303. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_pagination.py +0 -0
  304. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_parity.py +0 -0
  305. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_programs.py +0 -0
  306. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_provider_credentials.py +0 -0
  307. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_schema_sample.py +0 -0
  308. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_signatures.py +0 -0
  309. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_tool_servers.py +0 -0
  310. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_top.py +0 -0
  311. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_tree_help.py +0 -0
  312. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cli/test_valsets.py +0 -0
  313. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_client_floor.py +0 -0
  314. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_clustering/__init__.py +0 -0
  315. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_clustering/conftest.py +0 -0
  316. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_clustering/test_edge_cases.py +0 -0
  317. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  318. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_clustering/test_type_conversion.py +0 -0
  319. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_cmp551_grouping_identity.py +0 -0
  320. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_configuration.py +0 -0
  321. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_context.py +0 -0
  322. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_credential_kwargs.py +0 -0
  323. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_credential_selection.py +0 -0
  324. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_credential_values.py +0 -0
  325. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_dataset_polling.py +0 -0
  326. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_dataset_sync.py +0 -0
  327. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_datasets.py +0 -0
  328. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_decision_client_spans.py +0 -0
  329. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_decorators.py +0 -0
  330. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deploy.py +0 -0
  331. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  332. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deploy_program_id.py +0 -0
  333. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deployment_metadata.py +0 -0
  334. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deployment_runs.py +0 -0
  335. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_deployments_read.py +0 -0
  336. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_eval.py +0 -0
  337. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_eval_start.py +0 -0
  338. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_exception_paths.py +0 -0
  339. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_execute.py +0 -0
  340. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_execute_run_identity.py +0 -0
  341. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_fake_lm.py +0 -0
  342. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_gepa_callback.py +0 -0
  343. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_gepa_integration.py +0 -0
  344. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_gepa_output_normalization.py +0 -0
  345. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_gepa_resume.py +0 -0
  346. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_identity.py +0 -0
  347. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_imaging.py +0 -0
  348. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_import_weight.py +0 -0
  349. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_ingest_second_producer.py +0 -0
  350. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_interpreter_callback.py +0 -0
  351. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_ir_encoder_properties.py +0 -0
  352. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_ir_golden_vectors.py +0 -0
  353. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_ir_roundtrip.py +0 -0
  354. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_lm15_native_calls.py +0 -0
  355. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_lm_configuration.py +0 -0
  356. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_lm_usage_shapes.py +0 -0
  357. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_mcp_tools.py +0 -0
  358. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimization_progress.py +0 -0
  359. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimization_start.py +0 -0
  360. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize.py +0 -0
  361. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_budget.py +0 -0
  362. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_lift.py +0 -0
  363. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_lms.py +0 -0
  364. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_proposer.py +0 -0
  365. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_reflection_lm.py +0 -0
  366. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_optimize_terminal_failure.py +0 -0
  367. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_packaging.py +0 -0
  368. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_packaging_callable_source.py +0 -0
  369. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_packaging_mcp_tools.py +0 -0
  370. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_packaging_program_source.py +0 -0
  371. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_poll_until.py +0 -0
  372. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_program_id_deprecation.py +0 -0
  373. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_program_lineage_persist.py +0 -0
  374. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_program_names_in_deploy.py +0 -0
  375. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_programs_create.py +0 -0
  376. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_real_provider_twins_readiness.py +0 -0
  377. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_reflector_properties.py +0 -0
  378. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_reflector_smoke.py +0 -0
  379. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_release_stress_script.py +0 -0
  380. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_replaced_names.py +0 -0
  381. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_require_version_bump.py +0 -0
  382. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_retry.py +0 -0
  383. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_reversible_failures.py +0 -0
  384. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_rlm_patch.py +0 -0
  385. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_runtime_tools.py +0 -0
  386. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_seal_lm_credential.py +0 -0
  387. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_seed_review_run.py +0 -0
  388. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_signature_inputs.py +0 -0
  389. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_span_references.py +0 -0
  390. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_span_sent_once.py +0 -0
  391. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_system_one.py +0 -0
  392. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_trace_context_concurrency.py +0 -0
  393. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_traces_read.py +0 -0
  394. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_tracked_gepa.py +0 -0
  395. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_tracker_enrich.py +0 -0
  396. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_upload_auth_headers.py +0 -0
  397. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_valsets_api.py +0 -0
  398. {cmpnd-0.61.0 → cmpnd-0.62.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.61.0
3
+ Version: 0.62.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>
@@ -4,7 +4,7 @@ The implementation of ``list`` and ``show`` over the shared ``_http`` transport.
4
4
  :class:`cmpnd.Client`'s ``list_traces`` / ``get_trace`` call these directly; the
5
5
  deprecated :mod:`cmpnd.traces` shim wraps them with a warning. The filter and
6
6
  verb names match the ``cmpnd traces`` CLI vocabulary — ``list`` filtered by
7
- ``query`` / ``program`` / ``hash`` / ``deployment`` / ``since`` / ``until`` /
7
+ ``query`` / ``program`` / ``module`` / ``hash`` / ``deployment`` / ``since`` / ``until`` /
8
8
  ``all_history``, and ``show`` for one trace — so the surfaces do not drift.
9
9
  """
10
10
 
@@ -28,6 +28,7 @@ def list(
28
28
  query: str | None = None,
29
29
  model: str | None = None,
30
30
  program: str | None = None,
31
+ module: str | None = None,
31
32
  hash: str | None = None,
32
33
  deployment: str | None = None,
33
34
  metadata: dict[str, str] | None = None,
@@ -49,7 +50,10 @@ def list(
49
50
 
50
51
  The filters mirror ``cmpnd traces list``: ``query`` searches indexed span text;
51
52
  ``program`` names the program by name, and ``hash`` by its program id (``p_…``)
52
- or a legacy signature hash;
53
+ or a legacy signature hash; ``module`` is the root module's class, matched exactly.
54
+ No index serves the class, so without ``program`` or a program id in ``hash`` it
55
+ reads every trace in the time range and slows as the range grows. Pair it with a program or a shorter
56
+ ``since``;
53
57
  ``deployment`` is a deployment id; ``metadata`` is matched by containment (every
54
58
  pair must be present); ``tags`` matches any of the given tags. ``since`` /
55
59
  ``until`` accept an ISO 8601 timestamp, a bare date (``YYYY-MM-DD``), or a
@@ -68,6 +72,7 @@ def list(
68
72
  ("q", query),
69
73
  ("model", model),
70
74
  ("program_name", program),
75
+ ("module_class", module),
71
76
  ("deployment_id", deployment),
72
77
  ("tags", tags),
73
78
  ):
@@ -1190,7 +1190,7 @@ class _ModuleMixin(_CallbackBase):
1190
1190
  trace = models.TraceBuilder(
1191
1191
  trace_id=uuid4(),
1192
1192
  start_time=datetime.now(timezone.utc),
1193
- program_name=instance.__class__.__name__,
1193
+ module_class=instance.__class__.__name__,
1194
1194
  )
1195
1195
  self._stamp(trace)
1196
1196
 
@@ -1633,7 +1633,7 @@ class _LMMixin(_CallbackBase):
1633
1633
  trace_id=span.trace_id or uuid4(),
1634
1634
  start_time=span.start_time,
1635
1635
  end_time=span.end_time,
1636
- program_name="GEPA",
1636
+ module_class="GEPA",
1637
1637
  )
1638
1638
  self._stamp(trace)
1639
1639
  # A metric-phase call can start while DSPy's Evaluate span is still on the
@@ -18,7 +18,7 @@ from typing import Any, NoReturn
18
18
 
19
19
  from ... import _deployments, deployment_runs
20
20
  from ...deployment_metadata import DeploymentMetadataError
21
- from .. import api_client, pagination
21
+ from .. import api_client, pagination, trace_render
22
22
  from . import deployments as deployment_commands
23
23
  from . import signatures
24
24
 
@@ -338,8 +338,20 @@ def render_deployments_table(deployments: list[dict[str, Any]]) -> str:
338
338
  return "\n".join(lines)
339
339
 
340
340
 
341
+ def trace_program_cells(trace: dict[str, Any]) -> tuple[str, str]:
342
+ """The PROGRAM and MODULE cells for one trace record.
343
+
344
+ PROGRAM is the name the user gave the program, or a dash when it has none.
345
+ MODULE is the root module's class, or a dash when the trace carries no
346
+ class. `trace_render.program_and_module` reads an older server's shape,
347
+ where `program_name` holds the class.
348
+ """
349
+ name, module = trace_render.program_and_module(trace)
350
+ return name or "—", module or "—"
351
+
352
+
341
353
  def render_traces_table(traces: list[dict[str, Any]]) -> str:
342
- """Return a colored TRACE / STATUS / PROGRAM / AGE table as a string.
354
+ """Return a colored TRACE / STATUS / PROGRAM / MODULE / AGE table as a string.
343
355
 
344
356
  Reused by the shell when typing `d.traces` at the REPL (via
345
357
  `Trace._render_table`). Mirrors `render_deployments_table` so the
@@ -353,13 +365,13 @@ def render_traces_table(traces: list[dict[str, Any]]) -> str:
353
365
  (
354
366
  id_render(t["trace_id"]),
355
367
  t.get("status", "—"),
356
- _truncate(t.get("program_name") or _hash_fallback(t.get("program_signature_hash")), 28),
368
+ *(_truncate(cell, 28) for cell in trace_program_cells(t)),
357
369
  _age(t.get("start_time")),
358
370
  )
359
371
  for t in traces
360
372
  ]
361
373
 
362
- headers = ("TRACE", "STATUS", "PROGRAM", "AGE")
374
+ headers = ("TRACE", "STATUS", "PROGRAM", "MODULE", "AGE")
363
375
  widths = [max(len(h), max((len(r[i]) for r in rows), default=0)) for i, h in enumerate(headers)]
364
376
 
365
377
  header_line = " ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
@@ -370,6 +382,7 @@ def render_traces_table(traces: list[dict[str, Any]]) -> str:
370
382
  _trace_status(r[1]).ljust(widths[1] + (len(_trace_status(r[1])) - len(r[1]))),
371
383
  r[2].ljust(widths[2]),
372
384
  r[3].ljust(widths[3]),
385
+ r[4].ljust(widths[4]),
373
386
  ]
374
387
  lines.append(" ".join(cells))
375
388
  return "\n".join(lines)
@@ -28,6 +28,16 @@ def _metadata_pair(value: str) -> tuple[str, str]:
28
28
  return key, val
29
29
 
30
30
 
31
+ # The wait after which a class search with no program filter gets a hint.
32
+ MODULE_SLOW_HINT_SECONDS = 5.0
33
+
34
+ # The search-body keys that name a program, whichever spelling the flag took.
35
+ _PROGRAM_FILTER_KEYS = ("program_name", "program_id", "program_signature_hash")
36
+
37
+ # The clock the hint measures with, a seam for tests.
38
+ _monotonic = time.monotonic
39
+
40
+
31
41
  def _add_list_args(p: ArgumentParser) -> None:
32
42
  p.add_argument("--status", help="Filter by status (ok, error, incomplete, in_progress)")
33
43
  p.add_argument(
@@ -49,7 +59,20 @@ def _add_list_args(p: ArgumentParser) -> None:
49
59
  help="Filter by a metadata pair your program recorded; repeat for all-of. "
50
60
  "This is how you find a trace by your own identifier",
51
61
  )
52
- p.add_argument("--program", dest="program_name", help="Filter by program name")
62
+ p.add_argument(
63
+ "--program",
64
+ dest="program_name",
65
+ metavar="NAME",
66
+ help="Filter by the program's name, the one given with cmpnd.Program or `cmpnd programs create`",
67
+ )
68
+ p.add_argument(
69
+ "--module",
70
+ dest="module_class",
71
+ metavar="CLASS",
72
+ help="Filter by the root module's class (exact, e.g. ReAct). No index serves the class: "
73
+ "without --program or --hash it reads every trace in the time range, so it gets slower "
74
+ "as the range grows. Narrow it with --program, --hash or --since",
75
+ )
53
76
  p.add_argument(
54
77
  "--hash",
55
78
  dest="program_signature_hash",
@@ -189,6 +212,7 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
189
212
  ("q", "q"),
190
213
  ("model", "model"),
191
214
  ("program_name", "program_name"),
215
+ ("module_class", "module_class"),
192
216
  ("deployment_id", "deployment_id"),
193
217
  ("tags", "tags"),
194
218
  ("start_time_from", "start_time_from"),
@@ -211,7 +235,22 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
211
235
  body["start_time_from"] = _timespec.resolve_search_time(str(body["start_time_from"]))
212
236
  if "start_time_to" in body:
213
237
  body["start_time_to"] = _timespec.resolve_search_time(str(body["start_time_to"]), end_of_day=True)
238
+ started = _monotonic()
214
239
  resp = client.post("/api/v1/traces/search", json=body)
240
+ # No index serves the class, so a class search with no program filter reads
241
+ # every trace in the range. Say what narrows it once the wait is long enough
242
+ # to notice, on stderr so stdout stays the response.
243
+ if (
244
+ "module_class" in body
245
+ and not any(key in body for key in _PROGRAM_FILTER_KEYS)
246
+ and _monotonic() - started > MODULE_SLOW_HINT_SECONDS
247
+ ):
248
+ print(
249
+ "note: --module reads every trace in the time range when no program is given. "
250
+ "Consider filtering by program (--program or --hash) or a shorter range (--since) "
251
+ "for faster results.",
252
+ file=sys.stderr,
253
+ )
215
254
 
216
255
  # A window the caller did not ask for has to be visible: read as an org
217
256
  # figure, a windowed total is simply wrong, and nothing in the numbers says
@@ -32,7 +32,7 @@ from pathlib import Path
32
32
  from types import SimpleNamespace
33
33
  from typing import Any, Callable
34
34
 
35
- from . import api_client, commands, credentials, verbs
35
+ from . import api_client, commands, credentials, trace_render, verbs
36
36
 
37
37
  # --------------------------------------------------------------------------
38
38
  # Config + dismissible-tips marker
@@ -259,13 +259,15 @@ class Trace(Entity):
259
259
  super().__init__(client, record)
260
260
  self.status_str = record.get("status")
261
261
  self.start_time = record.get("start_time")
262
- self.program_name = record.get("program_name")
262
+ # The program's own name (None when it has none) and the root module's
263
+ # class, read the same way for a server that predates module_class.
264
+ self.program_name, self.module_class = trace_render.program_and_module(record)
263
265
 
264
266
  def __repr__(self) -> str:
265
267
  short = self.id.replace("-", "")[:12]
266
- program = self.program_name or commands.top._hash_fallback(self._record.get("program_signature_hash"))
268
+ program, module = commands.top.trace_program_cells(self._record)
267
269
  age = commands.top._age(self.start_time)
268
- return f"<Trace {short} status={self.status_str} program={program!r} age={age}>"
270
+ return f"<Trace {short} status={self.status_str} program={program!r} module={module!r} age={age}>"
269
271
 
270
272
  def inspect(self) -> dict[str, Any]:
271
273
  """Return the full trace record (with spans)."""
@@ -267,8 +267,35 @@ def _int(v: Any) -> int:
267
267
  return 0
268
268
 
269
269
 
270
+ def program_and_module(trace: dict[str, Any]) -> tuple[str | None, str | None]:
271
+ """The program's name and the root module's class a trace record carries.
272
+
273
+ A server that sends ``module_class`` puts the program's own name in
274
+ ``program_name``, empty when the program has none. An older server sends
275
+ no ``module_class``, and its ``program_name`` holds the class, so that
276
+ value is read as the class and the program counts as unnamed.
277
+ """
278
+ if "module_class" in trace:
279
+ return trace.get("program_name") or None, trace.get("module_class") or None
280
+ return None, trace.get("program_name") or None
281
+
282
+
283
+ def trace_label(trace: dict[str, Any]) -> str:
284
+ """One label for a trace: the program's name, else its id, else its class, else its signature.
285
+
286
+ The id shows only for a program with no name: a name already identifies it.
287
+ """
288
+ name, module = program_and_module(trace)
289
+ return name or trace.get("program_id") or module or trace.get("signature_name") or "trace"
290
+
291
+
270
292
  def _header(trace: dict[str, Any], span_total: int, color: bool, arrivals: bool = False) -> str:
271
- program = trace.get("program_name") or trace.get("signature_name") or "trace"
293
+ _, module = program_and_module(trace)
294
+ label = trace_label(trace)
295
+ program = _paint(label, "bold", color)
296
+ if module and label != module:
297
+ # The label names the program; the class beside it says what ran.
298
+ program += " " + _paint(module, "dim", color)
272
299
  tid = str(trace.get("trace_id") or "")
273
300
  status = trace.get("status")
274
301
  meta = [
@@ -278,7 +305,7 @@ def _header(trace: dict[str, Any], span_total: int, color: bool, arrivals: bool
278
305
  ]
279
306
  if _int(trace.get("total_tokens")) > 0:
280
307
  meta.append(f"{_int(trace.get('total_tokens'))} tokens")
281
- line1 = (_paint(str(program), "bold", color) + " " + _paint(tid, "grey", color)).rstrip()
308
+ line1 = (program + " " + _paint(tid, "grey", color)).rstrip()
282
309
  out = line1 + "\n" + " · ".join(meta)
283
310
  if arrivals:
284
311
  # Absolute landing time on the header (the one place there is room for
@@ -942,7 +942,9 @@ class TraceBuilder:
942
942
  status: TraceStatus = TraceStatus.UNSET
943
943
  error_message: str | None = None
944
944
 
945
- program_name: str | None = None
945
+ # The root DSPy module's class. The program's own name is `declared_name`,
946
+ # which the transport resolves to `program_id`.
947
+ module_class: str | None = None
946
948
  program_version: str | None = None
947
949
 
948
950
  # Signature info - identifies the program by its inputs/outputs
@@ -1071,7 +1073,9 @@ class TraceBuilder:
1071
1073
  "end_time": self.end_time.isoformat() if self.end_time else None,
1072
1074
  "status": self.status.value,
1073
1075
  "error_message": _strip_unstorable_opt(self.error_message),
1074
- "program_name": self.program_name,
1076
+ "module_class": self.module_class,
1077
+ # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1078
+ "program_name": self.module_class,
1075
1079
  "program_version": self.program_version,
1076
1080
  "signature_name": self.signature_name,
1077
1081
  "signature_input_fields": self.signature_input_fields,
@@ -1112,7 +1116,9 @@ class TraceBuilder:
1112
1116
  return self._with_facts(
1113
1117
  {
1114
1118
  "start_time": self.start_time.isoformat() if self.start_time else None,
1115
- "program_name": self.program_name,
1119
+ "module_class": self.module_class,
1120
+ # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1121
+ "program_name": self.module_class,
1116
1122
  "program_version": self.program_version,
1117
1123
  "signature_name": self.signature_name,
1118
1124
  "signature_input_fields": self.signature_input_fields,
@@ -291,8 +291,9 @@ caller reads it. To find where a program is slow, list recent traces, then
291
291
  read each one's spans and durations.
292
292
 
293
293
  - `cmpnd traces list [-q TEXT] [--model] [--metadata k=v …] [--status]
294
- [--program] [--hash ID-OR-HASH] [--deployment] [--tag …] [--since]
295
- [--until] [--all] [--limit N] [--offset N]` lists traces, newest first.
294
+ [--program NAME] [--module CLASS] [--hash ID-OR-HASH] [--deployment]
295
+ [--tag …] [--since] [--until] [--all] [--limit N] [--offset N]` lists
296
+ traces, newest first.
296
297
  - `--limit N` returns the N most recent, and `--offset N` skips N rows.
297
298
  - `--since` and `--until` bound the start time. Each takes ISO 8601, a date
298
299
  (`YYYY-MM-DD`, UTC, the start of the day for `--since` and the end for
@@ -305,6 +306,17 @@ read each one's spans and durations.
305
306
  must appear.
306
307
  - `--metadata` repeats and matches metadata you attached at trace time, so
307
308
  you can find a trace by your own identifier.
309
+ - `--program` takes the program's name, the one you gave with
310
+ `cmpnd.Program` or `cmpnd programs create`. The CLI sends it as the search
311
+ body's `program_name`. A name no program answers to is an error that names
312
+ it, not an empty list.
313
+ - `--module` takes the root module's class, such as `ReAct`, and matches it
314
+ exactly. The CLI sends it as `module_class`. No index serves the class.
315
+ Without `--program` or a program id in `--hash` it reads every trace in
316
+ the time range, so it slows as the range grows. When such a search takes
317
+ longer than five seconds, the CLI prints a line on stderr that suggests a
318
+ program filter or a shorter range. Stdout is unchanged. A search that
319
+ times out answers 503 with the same advice.
308
320
  - `--hash` takes a program id or a legacy signature hash. The CLI sends an
309
321
  id as the search body's `program_id` and a hash as
310
322
  `program_signature_hash`. `cmpnd.Client().list_traces(hash=...)` splits
@@ -312,8 +324,10 @@ read each one's spans and durations.
312
324
  - `--deployment` takes a deployment ID (`d_...`).
313
325
  - `cmpnd traces show <id> [--tree|--timeline|--notebook] [--arrivals]` prints
314
326
  one trace. The default is raw JSON with each span's duration and its
315
- `created_at`. `--tree` and `--timeline` render human views, and
316
- `--notebook` prints an RLM trace as a Jupyter notebook. With `--tree` or
327
+ `created_at`. `--tree` and `--timeline` render human views headed by the
328
+ program's name and its root module's class. A program with no name is headed
329
+ by its `p_` id instead, and a trace that names no program by its class
330
+ alone. `--notebook` prints an RLM trace as a Jupyter notebook. With `--tree` or
317
331
  `--timeline`, `--arrivals` shows when the server stored each span and how
318
332
  far that is from the start time the client reported, which exposes a skewed
319
333
  client clock.
@@ -322,6 +336,14 @@ read each one's spans and durations.
322
336
  - `cmpnd traces update <id> --metadata k=v …` merges metadata onto a trace,
323
337
  and `cmpnd traces delete <id>` deletes a trace and its spans.
324
338
 
339
+ Each trace row and each `traces show` trace names what ran in three fields.
340
+ `program_name` is the program's name, empty when the program has none or the
341
+ trace names no program. `module_class` is the root DSPy module's class, and
342
+ `program_id` is the program's `p_` id. A server that predates `module_class`
343
+ sends the class in `program_name`. The shell's traces table and the `--tree`
344
+ and `--timeline` headers then show that value as the class and leave the
345
+ program unnamed.
346
+
325
347
  `-q` matches text that serve indexes on each span when it stores the span.
326
348
  Spans stored before the index existed match nothing until a backfill indexes
327
349
  them. An empty result says so, because the list alone cannot tell "nothing
@@ -44,8 +44,9 @@ offers only `list`.
44
44
  | `status` | `--status` | `ok`, `error`, `incomplete` or `in_progress` |
45
45
  | `query` | `--query` / `-q` | Full-text search over indexed span text: prompts, completions, tool calls and errors |
46
46
  | `model` | `--model` | Exact model name, such as `openai/gpt-6-luna` |
47
- | `program` | `--program` | Program name |
48
- | `hash` | `--hash` | Program signature hash, as a string |
47
+ | `program` | `--program` | The program's name. A name no program answers to raises an error that names it. |
48
+ | `module` | `--module` | The root module's class, matched exactly. No index serves it, so without `program` or a program id in `hash` it reads every trace in the time range. Pair it with a program or a shorter `since`. |
49
+ | `hash` | `--hash` | A program id (`p_…`), or a legacy signature hash as a string |
49
50
  | `deployment` | `--deployment` | Deployment ID (`d_…`) |
50
51
  | `metadata` | `--metadata` | A `dict` matched by containment. Every pair must be on the trace. |
51
52
  | `tags` | `--tag` | A `list`. A trace matches if it has any of the project tags. |
@@ -673,6 +673,13 @@ Its first payload carries the optimization id, plus the active eval id for a
673
673
  metric call. A reflection call outside evaluation has no eval id. Manual
674
674
  decorators and `start_span` create spans rather than another automatic trace.
675
675
 
676
+ A trace's creation fields name the root module's class in `module_class`, at
677
+ the top level and in `started_fact`, and again under `program_name`, the key a
678
+ serve from before `module_class` reads and digests a start from. A standalone
679
+ optimizer LM trace sends `GEPA` there. No trace body carries the program's own name: a declared name
680
+ travels as the program id it resolves to, as "A declared program name resolves
681
+ to an id in the exporter" describes.
682
+
676
683
  The callback seals `started_fact` after it sets the root type, before the
677
684
  module executes. It keeps that snapshot even when start emission is disabled.
678
685
  The completion callback seals one independently identified `completion_fact`
@@ -80,7 +80,7 @@ class StartedFact:
80
80
  """Creation fields that later builder mutation cannot change."""
81
81
 
82
82
  start_time: str | None
83
- program_name: str | None
83
+ module_class: str | None
84
84
  program_version: str | None
85
85
  program_signature_hash: str
86
86
  signature_name: str | None
@@ -104,7 +104,7 @@ class StartedFact:
104
104
  def from_payload(cls, payload: dict[str, Any]) -> StartedFact:
105
105
  return cls(
106
106
  start_time=payload.get("start_time"),
107
- program_name=payload.get("program_name"),
107
+ module_class=payload.get("module_class"),
108
108
  program_version=payload.get("program_version"),
109
109
  program_signature_hash=str(payload["program_signature_hash"]),
110
110
  signature_name=payload.get("signature_name"),
@@ -125,7 +125,12 @@ class StartedFact:
125
125
  def creation_dict(self) -> dict[str, Any]:
126
126
  payload = {
127
127
  "start_time": self.start_time,
128
- "program_name": self.program_name,
128
+ "module_class": self.module_class,
129
+ # A serve from before module_class reads the class only under
130
+ # program_name, and digests a start from that key. Sending both keeps
131
+ # a start it accepted matching its replay to a later serve. Dropped
132
+ # once no such serve runs.
133
+ "program_name": self.module_class,
129
134
  "program_version": self.program_version,
130
135
  "program_signature_hash": self.program_signature_hash,
131
136
  "signature_name": self.signature_name,
@@ -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.61.0"
8
+ version = "0.62.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -10,7 +10,7 @@ GEPA optimization the stress suite runs
10
10
  a weak Bedrock student + a Bedrock reflection LM over the vendored committee
11
11
  subset. Capturing a *real* run (rather than fabricating payloads) keeps the seed
12
12
  faithful — real prompts, token counts, span trees, and now the GEPA optimizer
13
- traces (program_name "GEPA", `gepa.phase` reflection/metric — CMP-485).
13
+ traces (module_class "GEPA", `gepa.phase` reflection/metric).
14
14
 
15
15
  Only the four kinds `seed_review_run.py` replays are kept — `optimization` (the
16
16
  completed run header), `traces_batch`, `spans`, `stats`; the optimization *start*
@@ -146,12 +146,12 @@ def main() -> int:
146
146
  kinds = [it["kind"] for it in captured]
147
147
  n_opt = kinds.count("optimization")
148
148
  trace_payloads = [t for it in captured if it["kind"] == "traces_batch" for t in it["payload"]]
149
- programs = {t.get("program_name") for t in trace_payloads}
149
+ classes = {t.get("module_class") for t in trace_payloads}
150
150
  if n_opt != 1:
151
151
  print(f"expected exactly 1 completed optimization, captured {n_opt}; not writing", file=sys.stderr)
152
152
  return 1
153
- if "GEPA" not in programs:
154
- print(f"no GEPA optimizer traces captured (programs={programs}); not writing", file=sys.stderr)
153
+ if "GEPA" not in classes:
154
+ print(f"no GEPA optimizer traces captured (module classes={classes}); not writing", file=sys.stderr)
155
155
  return 1
156
156
 
157
157
  _FIXTURE.parent.mkdir(parents=True, exist_ok=True)
@@ -160,7 +160,7 @@ def main() -> int:
160
160
  from collections import Counter
161
161
 
162
162
  print(f"wrote {_FIXTURE} — {len(captured)} items {dict(Counter(kinds))}")
163
- print(f" trace program_names: {dict(Counter(t.get('program_name') for t in trace_payloads))}")
163
+ print(f" trace module classes: {dict(Counter(t.get('module_class') for t in trace_payloads))}")
164
164
  return 0
165
165
 
166
166
 
@@ -258,7 +258,7 @@ _TOOL_KIND_DEMO_FIXTURE = (
258
258
  # A dspy.ReAct(ExtractCommittee) run (email_body->committee) that reasons, calls
259
259
  # a `lookup_disclaimer` tool to pull the "Paid for by" line, then extracts the
260
260
  # committee (CMP-634 golden-path). Flat-replay format like the two fixtures
261
- # above; program_name "ReAct" so the sidebar/Type badge reads ReAct. Loaded
261
+ # above; module_class "ReAct" so the sidebar/Type badge reads ReAct. Loaded
262
262
  # lazily so a missing file degrades to skipping that seed.
263
263
  _REACT_COMMITTEE_FIXTURE = (
264
264
  pathlib.Path(__file__).resolve().parents[2] / "serve" / "scripts" / "seed_fixtures" / "react_committee.json"
@@ -390,7 +390,7 @@ def _notebook_trace(now: datetime.datetime) -> dict:
390
390
  "end_time": at(total_ns),
391
391
  "duration_ns": total_ns,
392
392
  "status": "ok",
393
- "program_name": "RLM", # DSPy module class → programs.module_class → sidebar badge
393
+ "module_class": "RLM", # DSPy module class → programs.module_class → sidebar badge
394
394
  "program_version": "0.1.0",
395
395
  "program_signature_hash": fx["program_signature_hash"],
396
396
  "signature_name": fx["signature_name"],
@@ -540,7 +540,7 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
540
540
  "trace_id": trace_id,
541
541
  "start_time": at(0),
542
542
  "status": "in_progress",
543
- "program_name": "RLM",
543
+ "module_class": "RLM",
544
544
  "program_version": "0.1.0",
545
545
  "program_signature_hash": fx["program_signature_hash"],
546
546
  "signature_name": fx["signature_name"],
@@ -559,7 +559,7 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
559
559
  def _replay_flat_fixture(
560
560
  fixture_path: pathlib.Path,
561
561
  now: datetime.datetime,
562
- program_name: str,
562
+ module_class: str,
563
563
  project_tags: list[str],
564
564
  request_preview: str,
565
565
  response_preview: str,
@@ -609,7 +609,7 @@ def _replay_flat_fixture(
609
609
  "end_time": at(total_ns),
610
610
  "duration_ns": total_ns,
611
611
  "status": "ok",
612
- "program_name": program_name, # DSPy module class → programs.module_class → sidebar badge
612
+ "module_class": module_class, # DSPy module class → programs.module_class → sidebar badge
613
613
  "program_version": "0.1.0",
614
614
  "program_signature_hash": root["program_signature_hash"],
615
615
  "signature_name": root["signature_name"],
@@ -0,0 +1,58 @@
1
+ """What the platform under test can and cannot do, as skip marks.
2
+
3
+ This suite runs against several targets: a local stack, a review app, a staging
4
+ install. Some of what it asserts needs a capability a target may not have, so these
5
+ marks say what a test needs, and the LANE says what its target lacks. Read that
6
+ direction round: a test declares a requirement, rather than a target being
7
+ special-cased in a test.
8
+
9
+ A review app has every capability here (CMP-1643). It runs an optimizer, and every LM
10
+ call it makes reaches its fake-provider through a ``cmpnd-fake`` credential the
11
+ app seals at each start. Staging and production hold no such credential. A local serve with no
12
+ optimizer, such as ``just dev-wasm``, cannot optimize.
13
+
14
+ No variable is set by default, so every target runs everything.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+
21
+ import pytest
22
+
23
+ # The target runs no optimizer. Serve then routes optimize to the wasm worker, which
24
+ # does not implement it, so ``POST /optimize/{optimizer}`` answers 502.
25
+ NO_OPTIMIZER = os.environ.get("CMPND_E2E_NO_OPTIMIZER") == "1"
26
+
27
+ # The target's models are fakes, as a review app's fake-provider is. A fake follows
28
+ # demos but does not produce every adapter's shape: a ReAct signature asking for
29
+ # ``[next_thought, next_tool_name, next_tool_args]`` gets ``[[ ## completed ## ]]``.
30
+ FAKE_LM = os.environ.get("CMPND_E2E_FAKE_LM") == "1"
31
+
32
+ # Whether the target answers the ``cmpnd-fake/`` models the suite deploys. A review app
33
+ # does: at each start it seals a ``cmpnd-fake`` credential at its fake-provider and makes
34
+ # it the org's default. Staging and production hold no such credential, so egress refuses
35
+ # every call on those models.
36
+ #
37
+ # ``PLATFORM_WASM_WORKER=1`` marks the staging and production lanes, so it implies this
38
+ # and the lanes need not set a second variable. ``CMPND_E2E_NO_FAKE_MODEL_CREDENTIAL=1``
39
+ # declares it for any other target.
40
+ NO_FAKE_MODEL_CREDENTIAL = (
41
+ os.environ.get("CMPND_E2E_NO_FAKE_MODEL_CREDENTIAL") == "1" or os.environ.get("PLATFORM_WASM_WORKER") == "1"
42
+ )
43
+
44
+ needs_the_fake_model_credential = pytest.mark.skipif(
45
+ NO_FAKE_MODEL_CREDENTIAL,
46
+ reason="target holds no credential for the cmpnd-fake models, so a program deployed on one "
47
+ "has no reachable destination",
48
+ )
49
+
50
+ needs_an_optimizer = pytest.mark.skipif(
51
+ NO_OPTIMIZER,
52
+ reason="target runs no optimizer, so it cannot serve /optimize",
53
+ )
54
+
55
+ needs_a_real_lm = pytest.mark.skipif(
56
+ FAKE_LM,
57
+ reason="target answers LM calls with a fake that does not produce every adapter's shape",
58
+ )
@@ -43,7 +43,7 @@ class TestCompositeModuleIdentity:
43
43
  trace = data["trace"]
44
44
 
45
45
  assert trace["program_signature_hash"] != 0, "Custom module should have a program_signature_hash"
46
- assert trace["program_name"] == "ArticleWriter"
46
+ assert trace["module_class"] == "ArticleWriter"
47
47
 
48
48
  def test_custom_module_has_component_signatures(self, server_client, trace_capture):
49
49
  """The trace should include component_signatures with children field info."""
@@ -92,9 +92,6 @@ class TestDeployCarriesState:
92
92
  deploy round trip — without a real LM. Demos dropped (the regression) →
93
93
  the served prompt has no demo turn → the fake returns "fake"."""
94
94
 
95
- # Asserts a demo steered the served program's answer, which an in-process
96
- # field-filling fake cannot do — it was shown no demos.
97
- @needs_a_real_lm
98
95
  def test_deploy_carries_demos(self):
99
96
  from dspy.primitives.prediction import Prediction
100
97
 
@@ -100,6 +100,12 @@ class TestDeployPandasProgram:
100
100
 
101
101
 
102
102
  @needs_an_optimizer
103
+ @pytest.mark.skip(
104
+ reason="serve answers this optimize with 422, saying the deployment's public interface is "
105
+ "unavailable until retained-source inspection finishes (recordedPublicInterface in "
106
+ "serve/internal/deploymentsapi/optimize.go); whether a pandas program can be optimized "
107
+ "or the test must wait is unknown"
108
+ )
103
109
  class TestOptimizePandasProgram:
104
110
  def test_gepa_step_with_a_pandas_metric_completes(self, server_client, deployed):
105
111
  examples = [
@@ -15,6 +15,7 @@ import zipfile
15
15
  import dspy
16
16
  import pytest
17
17
 
18
+ from tests.e2e import fake_lm
18
19
  from tests.e2e.helpers import named_program, platform_client, program_id_for
19
20
  from tests.e2e.target_capabilities import needs_a_real_lm
20
21
 
@@ -221,6 +222,10 @@ class TestDeployBadImportRejected:
221
222
  "program_signature_hash": 0,
222
223
  "declared_content_length": len(zip_bytes),
223
224
  "program_id": program_id_for(f"test_deploy_programs-{uuid.uuid4().hex[:8]}"),
225
+ # A worker that spends egress tokens needs the model a token names, so
226
+ # serve refuses a deploy that declares none. The SDK sends the program's
227
+ # own model, and this hand-made request sends the suite's.
228
+ "litellm_model": fake_lm.FAKE_MODEL,
224
229
  },
225
230
  headers=headers,
226
231
  )