cmpnd 0.26.0__tar.gz → 0.27.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 (331) hide show
  1. {cmpnd-0.26.0 → cmpnd-0.27.0}/PKG-INFO +1 -1
  2. cmpnd-0.27.0/cmpnd/_authored_import.py +355 -0
  3. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/authored.py +259 -20
  4. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/top.py +85 -76
  5. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/shell.py +11 -4
  6. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimization.py +243 -13
  7. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/cli.md +49 -30
  8. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/sdk-instrumentation.md +6 -2
  9. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/exporter_http.py +23 -25
  10. {cmpnd-0.26.0 → cmpnd-0.27.0}/pyproject.toml +1 -1
  11. cmpnd-0.27.0/tests/integration/authored_optimize/judge.py +46 -0
  12. cmpnd-0.27.0/tests/integration/authored_optimize/optimize_hosted.py +98 -0
  13. cmpnd-0.27.0/tests/test_authored_bundle_collisions.py +61 -0
  14. cmpnd-0.27.0/tests/test_authored_metric_contract.py +106 -0
  15. cmpnd-0.27.0/tests/test_authored_named_models.py +188 -0
  16. cmpnd-0.27.0/tests/test_authored_optimize.py +407 -0
  17. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_top.py +86 -25
  18. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter.py +79 -10
  19. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter_real.py +23 -12
  20. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ingest_second_producer.py +5 -6
  21. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_integration.py +9 -15
  22. cmpnd-0.27.0/tests/test_span_sent_once.py +178 -0
  23. {cmpnd-0.26.0 → cmpnd-0.27.0}/uv.lock +1 -1
  24. cmpnd-0.26.0/cmpnd/_authored_import.py +0 -201
  25. cmpnd-0.26.0/cmpnd/cli/snippet.py +0 -52
  26. {cmpnd-0.26.0 → cmpnd-0.27.0}/.gitignore +0 -0
  27. {cmpnd-0.26.0 → cmpnd-0.27.0}/AGENTS.md +0 -0
  28. {cmpnd-0.26.0 → cmpnd-0.27.0}/CLAUDE.md +0 -0
  29. {cmpnd-0.26.0 → cmpnd-0.27.0}/README.md +0 -0
  30. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/__init__.py +0 -0
  31. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_assessments.py +0 -0
  32. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_exporter_pool.py +0 -0
  33. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_gepa_patch.py +0 -0
  34. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_http.py +0 -0
  35. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_program_patch.py +0 -0
  36. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_rlm_patch.py +0 -0
  37. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_timespec.py +0 -0
  38. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_traces.py +0 -0
  39. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_valsets.py +0 -0
  40. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/callback.py +0 -0
  41. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/__init__.py +0 -0
  42. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/api_client.py +0 -0
  43. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/__init__.py +0 -0
  44. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/admin.py +0 -0
  45. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/assessments.py +0 -0
  46. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/auth.py +0 -0
  47. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/datasets.py +0 -0
  48. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/deployments.py +0 -0
  49. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/evals.py +0 -0
  50. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/login.py +0 -0
  51. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/optimizations.py +0 -0
  52. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/org.py +0 -0
  53. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/programs.py +0 -0
  54. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  55. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/signatures.py +0 -0
  56. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  57. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/traces.py +0 -0
  58. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/valsets.py +0 -0
  59. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/credentials.py +0 -0
  60. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/deprecation.py +0 -0
  61. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/pagination.py +0 -0
  62. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/trace_render.py +0 -0
  63. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/tree_help.py +0 -0
  64. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/verbs.py +0 -0
  65. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/client.py +0 -0
  66. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/configuration.py +0 -0
  67. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/context.py +0 -0
  68. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/dataset_sync.py +0 -0
  69. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/datasets.py +0 -0
  70. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/decorators.py +0 -0
  71. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/deployment.py +0 -0
  72. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/eval_handler.py +0 -0
  73. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/execution.py +0 -0
  74. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/exporter.py +0 -0
  75. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/exporter_http.py +0 -0
  76. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/helpers.py +0 -0
  77. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/identity.py +0 -0
  78. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/imaging.py +0 -0
  79. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/__init__.py +0 -0
  80. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/decode.py +0 -0
  81. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/encode.py +0 -0
  82. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/hash.py +0 -0
  83. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/program.py +0 -0
  84. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/reflection.py +0 -0
  85. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/types.py +0 -0
  86. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/xxh64.py +0 -0
  87. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/mcp_servers.py +0 -0
  88. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/models.py +0 -0
  89. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/__init__.py +0 -0
  90. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/gepa.py +0 -0
  91. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  92. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/resume.py +0 -0
  93. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/tracker.py +0 -0
  94. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/packaging.py +0 -0
  95. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/retry.py +0 -0
  96. {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/traces.py +0 -0
  97. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/2026-04-02-code-review.md +0 -0
  98. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/client.md +0 -0
  99. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/custom-proposer.md +0 -0
  100. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  101. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  102. {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/reading-traces.md +0 -0
  103. {cmpnd-0.26.0 → cmpnd-0.27.0}/examples/generalizing_proposer.py +0 -0
  104. {cmpnd-0.26.0 → cmpnd-0.27.0}/examples/local_ollama.py +0 -0
  105. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/LINEAGE.md +0 -0
  106. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/README.md +0 -0
  107. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  108. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  109. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  110. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/py.typed +0 -0
  111. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  112. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/retry.py +0 -0
  113. {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/pyproject.toml +0 -0
  114. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/audit-sdist.sh +0 -0
  115. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/capture_review_run.py +0 -0
  116. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/check_release_artifacts.py +0 -0
  117. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/fixtures/review_run.json.gz +0 -0
  118. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/gepa_resume_bug.py +0 -0
  119. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/require_version_bump.py +0 -0
  120. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/seed_review_run.py +0 -0
  121. {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/test-in-linux.sh +0 -0
  122. {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/__init__.pyi +0 -0
  123. {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  124. {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  125. {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/utils/__init__.pyi +0 -0
  126. {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/utils/callback.pyi +0 -0
  127. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/__init__.py +0 -0
  128. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_exporter_helpers.py +0 -0
  129. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_fake_clock.py +0 -0
  130. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_identity_helpers.py +0 -0
  131. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/conftest.py +0 -0
  132. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/__init__.py +0 -0
  133. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/committee_task.py +0 -0
  134. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/conftest.py +0 -0
  135. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  136. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  137. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/fake_lm.py +0 -0
  138. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/helpers.py +0 -0
  139. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/target_capabilities.py +0 -0
  140. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_assessments_cli.py +0 -0
  141. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_capture_prompts.py +0 -0
  142. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_composite_module.py +0 -0
  143. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  144. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_deploy_execute.py +0 -0
  145. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_deploy_programs.py +0 -0
  146. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_edge_cases.py +0 -0
  147. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_example_hash.py +0 -0
  148. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_gepa_optimization.py +0 -0
  149. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_interpreter_spans.py +0 -0
  150. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_lm_accuracy.py +0 -0
  151. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_module_tracing.py +0 -0
  152. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimization_lineage.py +0 -0
  153. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  154. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_failures.py +0 -0
  155. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_loop.py +0 -0
  156. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  157. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  158. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_parallel_export.py +0 -0
  159. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_project_attribution.py +0 -0
  160. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_react_tool_spans.py +0 -0
  161. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_save_load.py +0 -0
  162. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_trace_structure.py +0 -0
  163. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_traces_read.py +0 -0
  164. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_user_attribution.py +0 -0
  165. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_valsets_cli.py +0 -0
  166. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  167. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  168. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  169. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  170. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  171. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  172. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  173. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  174. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  175. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/10_react.json +0 -0
  176. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/11_refine.json +0 -0
  177. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/12_retrieve.json +0 -0
  178. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/13_knn.json +0 -0
  179. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/14_parallel.json +0 -0
  180. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/README.md +0 -0
  181. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/ingest_conformance.py +0 -0
  182. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/__init__.py +0 -0
  183. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/conftest.py +0 -0
  184. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  185. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/hosted_optimize_probe.py +0 -0
  186. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/optimization_run_probe.py +0 -0
  187. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  188. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_logs_renders_traces.py +0 -0
  189. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_optimization_run.py +0 -0
  190. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_optimizing_progress.py +0 -0
  191. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_unhappy_path.py +0 -0
  192. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/__init__.py +0 -0
  193. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/__main__.py +0 -0
  194. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/drain.py +0 -0
  195. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/ingest.py +0 -0
  196. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/latency_transport.py +0 -0
  197. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/rlm_shape.py +0 -0
  198. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/sweep.py +0 -0
  199. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/test_rlm_shape.py +0 -0
  200. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/__init__.py +0 -0
  201. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/conftest.py +0 -0
  202. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/seed_parity.py +0 -0
  203. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_auth.py +0 -0
  204. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_datasets.py +0 -0
  205. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_deployments.py +0 -0
  206. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_evals.py +0 -0
  207. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_health.py +0 -0
  208. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_modules.py +0 -0
  209. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_optimizations.py +0 -0
  210. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_programs.py +0 -0
  211. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_signatures.py +0 -0
  212. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_trace_streaming.py +0 -0
  213. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_traces.py +0 -0
  214. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/__init__.py +0 -0
  215. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_provider.py +0 -0
  216. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_reliability.py +0 -0
  217. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_result.py +0 -0
  218. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/mint_bedrock_token.py +0 -0
  219. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_deploy_programs.py +0 -0
  220. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_multi_client_stress.py +0 -0
  221. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_client_committee.py +0 -0
  222. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_loop.py +0 -0
  223. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
  224. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_stress.py +0 -0
  225. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_provider_roles.py +0 -0
  226. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_real_provider_twins.py +0 -0
  227. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_reliability.py +0 -0
  228. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_reliability_classify.py +0 -0
  229. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_rlm_wasm.py +0 -0
  230. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_trace_search.py +0 -0
  231. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_assessments_api.py +0 -0
  232. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_authored.py +0 -0
  233. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_callback.py +0 -0
  234. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_candidate_lineage.py +0 -0
  235. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/__init__.py +0 -0
  236. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/conftest.py +0 -0
  237. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_admin.py +0 -0
  238. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_api_client.py +0 -0
  239. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_assessments.py +0 -0
  240. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_auth.py +0 -0
  241. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_credentials.py +0 -0
  242. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_datasets.py +0 -0
  243. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_deployments.py +0 -0
  244. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_evals.py +0 -0
  245. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_login.py +0 -0
  246. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_main.py +0 -0
  247. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_optimizations.py +0 -0
  248. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_org.py +0 -0
  249. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_pagination.py +0 -0
  250. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_parity.py +0 -0
  251. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_programs.py +0 -0
  252. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_provider_credentials.py +0 -0
  253. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_schema_sample.py +0 -0
  254. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_shell.py +0 -0
  255. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_signatures.py +0 -0
  256. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_tool_servers.py +0 -0
  257. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_traces.py +0 -0
  258. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  259. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_tree_help.py +0 -0
  260. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_valsets.py +0 -0
  261. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_client.py +0 -0
  262. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/__init__.py +0 -0
  263. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/conftest.py +0 -0
  264. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_edge_cases.py +0 -0
  265. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  266. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_type_conversion.py +0 -0
  267. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cmp551_grouping_identity.py +0 -0
  268. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_configuration.py +0 -0
  269. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_context.py +0 -0
  270. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_context_propagation.py +0 -0
  271. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_dataset_polling.py +0 -0
  272. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_dataset_sync.py +0 -0
  273. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_datasets.py +0 -0
  274. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_decorators.py +0 -0
  275. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_deploy.py +0 -0
  276. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  277. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval.py +0 -0
  278. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_example_attribution.py +0 -0
  279. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_start.py +0 -0
  280. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_trace_grouping.py +0 -0
  281. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exception_paths.py +0 -0
  282. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_execute.py +0 -0
  283. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter_queue.py +0 -0
  284. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_fake_lm.py +0 -0
  285. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_callback.py +0 -0
  286. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_integration.py +0 -0
  287. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_resume.py +0 -0
  288. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_identity.py +0 -0
  289. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_imaging.py +0 -0
  290. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_import_weight.py +0 -0
  291. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ingest_conformance.py +0 -0
  292. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_interpreter_callback.py +0 -0
  293. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_encoder_properties.py +0 -0
  294. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_golden_vectors.py +0 -0
  295. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_roundtrip.py +0 -0
  296. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_lm_usage_shapes.py +0 -0
  297. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_mcp_tools.py +0 -0
  298. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_models.py +0 -0
  299. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimization_progress.py +0 -0
  300. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimization_start.py +0 -0
  301. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize.py +0 -0
  302. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_budget.py +0 -0
  303. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_lift.py +0 -0
  304. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_lms.py +0 -0
  305. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_proposer.py +0 -0
  306. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_reflection_lm.py +0 -0
  307. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_terminal_failure.py +0 -0
  308. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging.py +0 -0
  309. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_callable_source.py +0 -0
  310. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_mcp_tools.py +0 -0
  311. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_program_source.py +0 -0
  312. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_poll_until.py +0 -0
  313. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_program_lineage_persist.py +0 -0
  314. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_project_payload.py +0 -0
  315. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reflector_properties.py +0 -0
  316. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reflector_smoke.py +0 -0
  317. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_repro_reactv2_bugs.py +0 -0
  318. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_require_version_bump.py +0 -0
  319. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_retry.py +0 -0
  320. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reversible_failures.py +0 -0
  321. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_rlm_patch.py +0 -0
  322. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_schemas.py +0 -0
  323. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_seed_review_run.py +0 -0
  324. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_trace_render.py +0 -0
  325. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_traces_read.py +0 -0
  326. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_tracked_gepa.py +0 -0
  327. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_tracker_enrich.py +0 -0
  328. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_truncation.py +0 -0
  329. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_valsets_api.py +0 -0
  330. {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_xxh64.py +0 -0
  331. {cmpnd-0.26.0 → cmpnd-0.27.0}/todo.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.26.0
3
+ Version: 0.27.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>
@@ -0,0 +1,355 @@
1
+ """Isolated import checker for authored deployment bundles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ import importlib
7
+ import inspect
8
+ import json
9
+ import sys
10
+ import tempfile
11
+ import traceback
12
+ import zipfile
13
+ from dataclasses import asdict
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+
18
+ def _program(module: Any, entrypoint: str) -> tuple[Any, str]:
19
+ import dspy
20
+
21
+ if not hasattr(module, entrypoint):
22
+ raise ValueError(f"entrypoint {entrypoint!r} is not defined by the authored program")
23
+ value = getattr(module, entrypoint)
24
+ if isinstance(value, dspy.Module):
25
+ return value, "instance"
26
+ if inspect.isclass(value) and issubclass(value, dspy.Module):
27
+ _require_zero_arguments(value, f"entrypoint class {entrypoint!r}")
28
+ return value(), "class"
29
+ if callable(value):
30
+ _require_zero_arguments(value, f"entrypoint factory {entrypoint!r}")
31
+ built = value()
32
+ if isinstance(built, dspy.Module):
33
+ return built, "factory"
34
+ raise ValueError(
35
+ f"entrypoint {entrypoint!r} must be a dspy.Module instance, a dspy.Module class, or a zero-argument factory"
36
+ )
37
+
38
+
39
+ def _require_zero_arguments(value: Any, label: str) -> None:
40
+ """Refuse required parameters without mislabelling errors inside the call."""
41
+ try:
42
+ signature = inspect.signature(value)
43
+ except (TypeError, ValueError):
44
+ return
45
+ try:
46
+ signature.bind()
47
+ except TypeError as exc:
48
+ raise ValueError(f"{label} must accept no arguments: {exc}") from exc
49
+
50
+
51
+ def _configured(request: dict[str, Any]) -> dict[str, int | None]:
52
+ """Give the check its own platform configuration, and watch what the file asks for.
53
+
54
+ The child has no environment, so an authored ``cmpnd.configure(max_input_size=…)``
55
+ would otherwise have nothing to configure against. The wrapper keeps the check's own
56
+ transport while letting the file change the settings it owns, and records the two
57
+ trace limits the create call carries.
58
+ """
59
+ from . import configuration
60
+
61
+ original_configure = configuration.configure
62
+ authored_limits: dict[str, int | None] = {"max_input_size": None, "max_output_size": None}
63
+ if not request.get("api_key"):
64
+ return authored_limits
65
+ original_configure(api_key=request["api_key"], endpoint=request.get("endpoint"))
66
+
67
+ def source_configure(api_key=None, endpoint=None, *args, **kwargs):
68
+ for name in authored_limits:
69
+ value = kwargs.get(name)
70
+ authored_limits[name] = value if isinstance(value, int) else None
71
+ return original_configure(
72
+ api_key=api_key or request["api_key"],
73
+ endpoint=endpoint or request.get("endpoint"),
74
+ *args,
75
+ **kwargs,
76
+ )
77
+
78
+ # The check has its own platform configuration but exposes no secret in the child
79
+ # environment. Serve resolves any tool binding again on create, so nothing the file
80
+ # configures here decides what the run may reach.
81
+ configuration.configure = source_configure
82
+ setattr(sys.modules["cmpnd"], "configure", source_configure)
83
+ return authored_limits
84
+
85
+
86
+ @contextlib.contextmanager
87
+ def _staged(request: dict[str, Any]):
88
+ """Extract the exact staged ZIP and put its root first on ``sys.path``.
89
+
90
+ The root stays on the path for the whole block rather than only for the first
91
+ import: an authored file's sibling may be imported lazily inside a function the
92
+ checks below call, and a path removed at the end of the entry import would make that
93
+ fail here and work in the kernel, which mounts the whole tree for the run's life.
94
+ """
95
+ with tempfile.TemporaryDirectory(prefix="cmpnd-authored-") as directory:
96
+ root = Path(directory)
97
+ with zipfile.ZipFile(request["bundle"]) as archive:
98
+ archive.extractall(root)
99
+ sys.path.insert(0, str(root))
100
+ try:
101
+ yield root
102
+ finally:
103
+ if str(root) in sys.path:
104
+ sys.path.remove(str(root))
105
+
106
+
107
+ def _import_member(member: str) -> Any:
108
+ """Import one archive member as the module the kernel will import it as."""
109
+ return importlib.import_module(str(member).removesuffix(".py").replace("/", "."))
110
+
111
+
112
+ def _program_facts(
113
+ program: Any,
114
+ kind: str,
115
+ authored_limits: dict[str, int | None],
116
+ ) -> dict[str, Any]:
117
+ """Everything the deployment create call reads off the imported program."""
118
+ import dspy
119
+
120
+ from . import configuration, deployment, identity, mcp_servers, packaging
121
+ from .authored import ProgramFacts
122
+
123
+ references = deployment._mcp_tool_references(program)
124
+ deployment._refuse_mcp_tools_outside_rlm(program, references)
125
+ pinned: dict[tuple[str, str], Any] = {}
126
+ if references:
127
+ import httpx
128
+
129
+ config = configuration.require_config()
130
+ with httpx.Client(timeout=30) as client:
131
+ pinned = deployment._resolve_mcp_tool_references(client, config, references)
132
+ tool_references = [
133
+ {
134
+ # The child already checked an explicit credential name against the
135
+ # registered server binding. Serve resolves that binding again and
136
+ # never accepts a client-selected credential name in this declaration.
137
+ "server_url": item.server_url,
138
+ "tool": item.tool_name,
139
+ "schema_hash": item.schema_hash,
140
+ "parameters": mcp_servers.synthesized_signature(item).parameter_names,
141
+ }
142
+ for item in pinned.values()
143
+ ]
144
+ tool_declarations = [
145
+ {
146
+ "server_url": item.server_url,
147
+ "name": item.tool_name,
148
+ "schema_hash": item.schema_hash,
149
+ "input_schema": item.input_schema,
150
+ "description": item.description,
151
+ "parameters": mcp_servers.synthesized_signature(item).parameter_names,
152
+ }
153
+ for item in pinned.values()
154
+ ]
155
+
156
+ signature = packaging._get_module_signature(program)
157
+ fields: dict[str, Any] = {}
158
+ if signature is not None:
159
+ fields = {
160
+ "signature_name": packaging._signature_to_string(signature),
161
+ "input_fields": list(signature.input_fields),
162
+ "output_fields": list(signature.output_fields),
163
+ "input_field_types": [identity.get_type_string(f.annotation) for f in signature.input_fields.values()],
164
+ "output_field_types": [identity.get_type_string(f.annotation) for f in signature.output_fields.values()],
165
+ }
166
+ lm = dspy.settings.lm
167
+ lm_kwargs = getattr(lm, "kwargs", {}) if lm is not None else {}
168
+ facts = ProgramFacts(
169
+ program_signature_hash=identity.compute_program_hash(program),
170
+ entrypoint_kind=kind,
171
+ module_class=type(program).__name__,
172
+ litellm_model=getattr(lm, "model", None),
173
+ temperature=lm_kwargs.get("temperature"),
174
+ max_tokens=lm_kwargs.get("max_tokens"),
175
+ thinking_effort=lm_kwargs.get("thinking_effort"),
176
+ max_input_size=authored_limits["max_input_size"],
177
+ max_output_size=authored_limits["max_output_size"],
178
+ tool_references=tool_references,
179
+ tool_declarations=tool_declarations,
180
+ **fields,
181
+ )
182
+ return asdict(facts)
183
+
184
+
185
+ def _check(request: dict[str, Any]) -> dict[str, Any]:
186
+ """The program bundle's answer: one import, the entrypoint's facts."""
187
+ authored_limits = _configured(request)
188
+ with _staged(request) as root:
189
+ manifest = json.loads((root / "manifest.json").read_text())
190
+ module = _import_member(str(manifest["program"]))
191
+ program, kind = _program(module, str(manifest["entrypoint"]))
192
+ return {"facts": _program_facts(program, kind, authored_limits)}
193
+
194
+
195
+ def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
196
+ """The optimize file's answer: one import, every entrypoint the run needs.
197
+
198
+ One import for all of it, rather than one per callable: the file is one module, and
199
+ importing it three times would run its module scope three times — which is neither
200
+ what the author's own script does nor what the kernel does.
201
+ """
202
+ from . import optimization
203
+
204
+ ask = request["optimize"]
205
+ authored_limits = _configured(request)
206
+ with _staged(request):
207
+ module = _import_member(str(ask["entry"]))
208
+ declared_lms: dict[str, Any] | None = None
209
+ if hasattr(module, "lms"):
210
+ lms_config: dict[str, Any] = {}
211
+ optimization._lms_config(lms_config, getattr(module, "lms"))
212
+ declared_lms = lms_config.get("lms", {})
213
+ metric_entrypoint = str(ask["metric_entrypoint"])
214
+ metric = getattr(module, metric_entrypoint, None)
215
+ if metric is None:
216
+ raise ValueError(
217
+ f"the optimize file binds no {metric_entrypoint!r}. An optimization scores every rollout "
218
+ f"with it, so define `def {metric_entrypoint}(gold, pred, trace, pred_name, pred_trace)` "
219
+ "returning dspy.Prediction(score=, feedback=)."
220
+ )
221
+ if not callable(metric):
222
+ raise ValueError(
223
+ f"{metric_entrypoint!r} in the optimize file is a {type(metric).__name__}, not a callable, "
224
+ "so nothing can score a rollout with it."
225
+ )
226
+ optimization._check_metric_call(metric, str(ask["optimizer"]))
227
+
228
+ program_entrypoint = str(ask["program_entrypoint"])
229
+ declared_program = getattr(module, program_entrypoint, None)
230
+ facts: dict[str, Any] | None = None
231
+ found_program: str | None = None
232
+ if declared_program is not None and not ask["program_allowed"]:
233
+ raise ValueError(
234
+ f"the optimize file binds {program_entrypoint!r} and a deployment id was given as well. "
235
+ "Those are two answers to which program this run optimizes, so neither is taken: remove "
236
+ f"the deployment id to optimize the file's own {program_entrypoint!r}, or remove "
237
+ f"{program_entrypoint!r} to optimize the deployment."
238
+ )
239
+ if declared_program is None and ask["program_allowed"]:
240
+ raise ValueError(
241
+ f"the optimize file binds no {program_entrypoint!r}, and no deployment id names one. "
242
+ f"Bind `{program_entrypoint} = ...` to optimize the file's own program, or pass an "
243
+ "existing deployment id to optimize that."
244
+ )
245
+ if declared_program is not None:
246
+ program, kind = _program(module, program_entrypoint)
247
+ facts = _program_facts(program, kind, authored_limits)
248
+ found_program = program_entrypoint
249
+
250
+ proposer_entrypoint = str(ask["proposer_entrypoint"])
251
+ declared_proposer = getattr(module, proposer_entrypoint, None)
252
+ found_proposer: str | None = None
253
+ if declared_proposer is not None:
254
+ optimization._check_proposer_call(declared_proposer)
255
+ found_proposer = proposer_entrypoint
256
+
257
+ work = Path(request["work_dir"])
258
+ trainset = _rows(module, "trainset")
259
+ valset = _rows(module, "valset")
260
+ (work / "trainset.ndjson").write_text(_ndjson(trainset))
261
+ (work / "valset.ndjson").write_text(_ndjson(valset))
262
+
263
+ answer: dict[str, Any] = {
264
+ "optimize": {
265
+ "metric_entrypoint": metric_entrypoint,
266
+ "program_entrypoint": found_program,
267
+ "proposer_entrypoint": found_proposer,
268
+ "lms": declared_lms,
269
+ "trainset_rows": len(trainset),
270
+ "valset_rows": len(valset),
271
+ }
272
+ }
273
+ if facts is not None:
274
+ answer["facts"] = facts
275
+ return answer
276
+
277
+
278
+ def _rows(module: Any, name: str) -> list[Any]:
279
+ """One example set the file built, refused here rather than at the first rollout."""
280
+ rows = getattr(module, name, None)
281
+ if rows is None:
282
+ raise ValueError(
283
+ f"the optimize file binds no {name!r}. An optimization needs both `trainset` and `valset` "
284
+ "as lists of dspy.Example."
285
+ )
286
+ if not isinstance(rows, (list, tuple)) or not rows:
287
+ raise ValueError(
288
+ f"{name!r} in the optimize file is {type(rows).__name__} and must be a non-empty list of "
289
+ "dspy.Example — an empty one leaves the search nothing to score."
290
+ )
291
+ return list(rows)
292
+
293
+
294
+ def _ndjson(rows: list[Any]) -> str:
295
+ """The dataset as the optimizer reads it: one flattened example per line.
296
+
297
+ Serialized HERE, in the process that imported the file, because that is the only
298
+ process that ever holds the Python objects. Authored source is for code the kernel
299
+ imports; a dataset is data the optimizer reads, and this is the shape it reads.
300
+ """
301
+ from . import optimization
302
+
303
+ return "\n".join(json.dumps(optimization._example_to_dict(row)) for row in rows) + "\n"
304
+
305
+
306
+ def main() -> int:
307
+ try:
308
+ request = json.loads(sys.stdin.read())
309
+ with contextlib.redirect_stdout(sys.stderr):
310
+ # Anything the imported file prints goes to stderr: this process answers one
311
+ # JSON document on stdout, and a module-scope `print` would otherwise be read
312
+ # as part of it.
313
+ answer = _check_optimize(request) if request.get("optimize") else _check(request)
314
+ print(json.dumps(answer))
315
+ return 0
316
+ except ModuleNotFoundError as exc:
317
+ print(
318
+ json.dumps(
319
+ {
320
+ "error": (
321
+ f"the authored source imports {exc.name!r}, which the local import check cannot find. "
322
+ "If it is a sibling module, add its file or directory with --include; otherwise install "
323
+ "the dependency locally and ensure the worker supports it."
324
+ )
325
+ }
326
+ )
327
+ )
328
+ except KeyError as exc:
329
+ print(
330
+ json.dumps(
331
+ {
332
+ "error": (
333
+ f"authored source import failed with KeyError: {exc}. The check runs without your "
334
+ "environment because the worker has none. Do not read provider or MCP credentials at "
335
+ "module scope. Name the model without its key and use cmpnd.mcp_tools(url) for a "
336
+ "registered server.\n" + _concise_traceback()
337
+ )
338
+ }
339
+ )
340
+ )
341
+ except BaseException as exc:
342
+ print(
343
+ json.dumps(
344
+ {"error": (f"authored source import failed with {type(exc).__name__}: {exc}\n" + _concise_traceback())}
345
+ )
346
+ )
347
+ return 1
348
+
349
+
350
+ def _concise_traceback() -> str:
351
+ return "".join(traceback.format_exc(limit=6)).strip()
352
+
353
+
354
+ if __name__ == "__main__":
355
+ raise SystemExit(main())