cmpnd 0.59.0__tar.gz → 0.60.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 (391) hide show
  1. {cmpnd-0.59.0 → cmpnd-0.60.0}/PKG-INFO +1 -1
  2. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/programs.py +124 -18
  3. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/verbs.py +1 -0
  4. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/cli.md +29 -7
  5. {cmpnd-0.59.0 → cmpnd-0.60.0}/pyproject.toml +1 -1
  6. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_main.py +3 -1
  7. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_programs.py +139 -26
  8. {cmpnd-0.59.0 → cmpnd-0.60.0}/uv.lock +1 -1
  9. {cmpnd-0.59.0 → cmpnd-0.60.0}/.gitignore +0 -0
  10. {cmpnd-0.59.0 → cmpnd-0.60.0}/AGENTS.md +0 -0
  11. {cmpnd-0.59.0 → cmpnd-0.60.0}/CLAUDE.md +0 -0
  12. {cmpnd-0.59.0 → cmpnd-0.60.0}/README.md +0 -0
  13. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/__init__.py +0 -0
  14. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_assessments.py +0 -0
  15. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_authored_import.py +0 -0
  16. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_deployments.py +0 -0
  17. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_exporter_pool.py +0 -0
  18. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_gepa_patch.py +0 -0
  19. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_http.py +0 -0
  20. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_program_patch.py +0 -0
  21. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_rlm_patch.py +0 -0
  22. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_runtime_tools.py +0 -0
  23. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_timespec.py +0 -0
  24. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_traces.py +0 -0
  25. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/_valsets.py +0 -0
  26. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/authored.py +0 -0
  27. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/callback.py +0 -0
  28. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/__init__.py +0 -0
  29. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/api_client.py +0 -0
  30. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/__init__.py +0 -0
  31. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/admin.py +0 -0
  32. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/assessments.py +0 -0
  33. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/auth.py +0 -0
  34. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/datasets.py +0 -0
  35. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/deployments.py +0 -0
  36. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/evals.py +0 -0
  37. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/login.py +0 -0
  38. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/optimizations.py +0 -0
  39. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/org.py +0 -0
  40. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  41. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/signatures.py +0 -0
  42. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  43. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/top.py +0 -0
  44. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/traces.py +0 -0
  45. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/commands/valsets.py +0 -0
  46. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/credentials.py +0 -0
  47. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/deprecation.py +0 -0
  48. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/pagination.py +0 -0
  49. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/shell.py +0 -0
  50. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/trace_render.py +0 -0
  51. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/cli/tree_help.py +0 -0
  52. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/client.py +0 -0
  53. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/configuration.py +0 -0
  54. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/context.py +0 -0
  55. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/credentials.py +0 -0
  56. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/dataset_sync.py +0 -0
  57. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/datasets.py +0 -0
  58. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/decorators.py +0 -0
  59. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/deployment.py +0 -0
  60. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/deployment_metadata.py +0 -0
  61. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/deployment_runs.py +0 -0
  62. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/eval_handler.py +0 -0
  63. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/execution.py +0 -0
  64. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/exporter.py +0 -0
  65. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/exporter_http.py +0 -0
  66. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/helpers.py +0 -0
  67. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/identity.py +0 -0
  68. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/imaging.py +0 -0
  69. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/__init__.py +0 -0
  70. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/decode.py +0 -0
  71. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/encode.py +0 -0
  72. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/hash.py +0 -0
  73. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/program.py +0 -0
  74. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/reflection.py +0 -0
  75. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/types.py +0 -0
  76. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/ir/xxh64.py +0 -0
  77. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/lm_configuration.py +0 -0
  78. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/mcp_servers.py +0 -0
  79. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/models.py +0 -0
  80. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/named_programs.py +0 -0
  81. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimization.py +0 -0
  82. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimizers/__init__.py +0 -0
  83. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimizers/gepa.py +0 -0
  84. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  85. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimizers/resume.py +0 -0
  86. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/optimizers/tracker.py +0 -0
  87. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/packaging.py +0 -0
  88. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/programs.py +0 -0
  89. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/references.py +0 -0
  90. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/retry.py +0 -0
  91. {cmpnd-0.59.0 → cmpnd-0.60.0}/cmpnd/traces.py +0 -0
  92. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/2026-04-02-code-review.md +0 -0
  93. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/client.md +0 -0
  94. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/custom-proposer.md +0 -0
  95. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  96. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  97. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/reading-traces.md +0 -0
  98. {cmpnd-0.59.0 → cmpnd-0.60.0}/docs/sdk-instrumentation.md +0 -0
  99. {cmpnd-0.59.0 → cmpnd-0.60.0}/examples/generalizing_proposer.py +0 -0
  100. {cmpnd-0.59.0 → cmpnd-0.60.0}/examples/local_ollama.py +0 -0
  101. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/LINEAGE.md +0 -0
  102. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/README.md +0 -0
  103. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  104. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  105. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  106. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  107. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  108. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  109. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/py.typed +0 -0
  110. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  111. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/retry.py +0 -0
  112. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  113. {cmpnd-0.59.0 → cmpnd-0.60.0}/ingest/pyproject.toml +0 -0
  114. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/audit-sdist.sh +0 -0
  115. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/capture_review_run.py +0 -0
  116. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/check-pip-install.sh +0 -0
  117. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/check_release_artifacts.py +0 -0
  118. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/fixtures/review_run.json.gz +0 -0
  119. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/gepa_resume_bug.py +0 -0
  120. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/install-test-env.sh +0 -0
  121. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/release-stress.sh +0 -0
  122. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/require_version_bump.py +0 -0
  123. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/seed_review_run.py +0 -0
  124. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/selfhost_compat.py +0 -0
  125. {cmpnd-0.59.0 → cmpnd-0.60.0}/scripts/test-in-linux.sh +0 -0
  126. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/__init__.pyi +0 -0
  127. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  128. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  129. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/utils/__init__.pyi +0 -0
  130. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/utils/callback.pyi +0 -0
  131. {cmpnd-0.59.0 → cmpnd-0.60.0}/stubs/dspy/utils/dummies.pyi +0 -0
  132. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/__init__.py +0 -0
  133. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_credential_route.py +0 -0
  134. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_deployment_signature_helpers.py +0 -0
  135. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_exporter_helpers.py +0 -0
  136. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_fake_clock.py +0 -0
  137. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_identity_helpers.py +0 -0
  138. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_resolve_refs.py +0 -0
  139. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/_traced_run.py +0 -0
  140. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/conftest.py +0 -0
  141. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/__init__.py +0 -0
  142. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/committee_task.py +0 -0
  143. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/conftest.py +0 -0
  144. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  145. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  146. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/fake_lm.py +0 -0
  147. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/helpers.py +0 -0
  148. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/target_capabilities.py +0 -0
  149. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_assessments_cli.py +0 -0
  150. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_capture_prompts.py +0 -0
  151. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_composite_module.py +0 -0
  152. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  153. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_deploy_execute.py +0 -0
  154. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_deploy_pandas.py +0 -0
  155. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_deploy_programs.py +0 -0
  156. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_edge_cases.py +0 -0
  157. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_example_hash.py +0 -0
  158. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_gepa_optimization.py +0 -0
  159. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_interpreter_spans.py +0 -0
  160. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_lm_accuracy.py +0 -0
  161. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_module_tracing.py +0 -0
  162. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimization_lineage.py +0 -0
  163. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  164. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimize_failures.py +0 -0
  165. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimize_loop.py +0 -0
  166. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  167. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  168. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_parallel_export.py +0 -0
  169. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_project_attribution.py +0 -0
  170. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_react_tool_spans.py +0 -0
  171. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_save_load.py +0 -0
  172. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_trace_structure.py +0 -0
  173. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_traces_read.py +0 -0
  174. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_user_attribution.py +0 -0
  175. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/e2e/test_valsets_cli.py +0 -0
  176. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  177. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  178. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  179. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  180. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  181. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  182. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  183. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  184. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  185. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/10_react.json +0 -0
  186. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/11_refine.json +0 -0
  187. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/12_retrieve.json +0 -0
  188. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/13_knn.json +0 -0
  189. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/14_parallel.json +0 -0
  190. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/identity_vectors/README.md +0 -0
  191. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/ingest_conformance.py +0 -0
  192. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/__init__.py +0 -0
  193. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/authored_optimize/judge.py +0 -0
  194. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  195. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/conftest.py +0 -0
  196. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  197. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/hosted_optimize_probe.py +0 -0
  198. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/optimization_run_probe.py +0 -0
  199. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  200. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/test_logs_renders_traces.py +0 -0
  201. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/test_optimization_run.py +0 -0
  202. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/test_optimizing_progress.py +0 -0
  203. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/integration/test_unhappy_path.py +0 -0
  204. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/__init__.py +0 -0
  205. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/__main__.py +0 -0
  206. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/drain.py +0 -0
  207. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/ingest.py +0 -0
  208. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/latency_transport.py +0 -0
  209. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/rlm_shape.py +0 -0
  210. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/rlm_size_report.py +0 -0
  211. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/sweep.py +0 -0
  212. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/test_rlm_shape.py +0 -0
  213. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/load/test_rlm_size_report.py +0 -0
  214. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/__init__.py +0 -0
  215. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/conftest.py +0 -0
  216. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/seed_parity.py +0 -0
  217. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_auth.py +0 -0
  218. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_datasets.py +0 -0
  219. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_deployments.py +0 -0
  220. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_evals.py +0 -0
  221. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_health.py +0 -0
  222. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_modules.py +0 -0
  223. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_optimizations.py +0 -0
  224. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_programs.py +0 -0
  225. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_signatures.py +0 -0
  226. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_trace_streaming.py +0 -0
  227. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/parity/test_traces.py +0 -0
  228. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/span_reference_fixture.py +0 -0
  229. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/__init__.py +0 -0
  230. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/_provider.py +0 -0
  231. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/_readiness.py +0 -0
  232. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/_reliability.py +0 -0
  233. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/_result.py +0 -0
  234. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/mint_bedrock_token.py +0 -0
  235. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_deploy_programs.py +0 -0
  236. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_multi_client_stress.py +0 -0
  237. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_optimize_client_committee.py +0 -0
  238. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_optimize_loop.py +0 -0
  239. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
  240. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_optimize_stress.py +0 -0
  241. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_provider_roles.py +0 -0
  242. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_readiness.py +0 -0
  243. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_real_provider_twins.py +0 -0
  244. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_reliability.py +0 -0
  245. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_reliability_classify.py +0 -0
  246. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_rlm_wasm.py +0 -0
  247. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/stress/test_trace_search.py +0 -0
  248. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_adapter_reentrancy.py +0 -0
  249. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_assessments_api.py +0 -0
  250. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored.py +0 -0
  251. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored_binding_credentials.py +0 -0
  252. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored_bundle_collisions.py +0 -0
  253. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored_metric_contract.py +0 -0
  254. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored_named_models.py +0 -0
  255. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_authored_optimize.py +0 -0
  256. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_callback.py +0 -0
  257. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_candidate_lineage.py +0 -0
  258. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/__init__.py +0 -0
  259. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/conftest.py +0 -0
  260. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_admin.py +0 -0
  261. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_api_client.py +0 -0
  262. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_assessments.py +0 -0
  263. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_auth.py +0 -0
  264. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_credentials.py +0 -0
  265. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_datasets.py +0 -0
  266. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_deployment_runs.py +0 -0
  267. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_deployments.py +0 -0
  268. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_evals.py +0 -0
  269. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_login.py +0 -0
  270. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_optimizations.py +0 -0
  271. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_org.py +0 -0
  272. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_pagination.py +0 -0
  273. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_parity.py +0 -0
  274. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_provider_credentials.py +0 -0
  275. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_schema_sample.py +0 -0
  276. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_shell.py +0 -0
  277. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_signatures.py +0 -0
  278. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_tool_servers.py +0 -0
  279. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_top.py +0 -0
  280. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_traces.py +0 -0
  281. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  282. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_tree_help.py +0 -0
  283. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cli/test_valsets.py +0 -0
  284. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_client.py +0 -0
  285. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_client_floor.py +0 -0
  286. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_client_program_names.py +0 -0
  287. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_clustering/__init__.py +0 -0
  288. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_clustering/conftest.py +0 -0
  289. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_clustering/test_edge_cases.py +0 -0
  290. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  291. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_clustering/test_type_conversion.py +0 -0
  292. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_cmp551_grouping_identity.py +0 -0
  293. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_configuration.py +0 -0
  294. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_context.py +0 -0
  295. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_context_propagation.py +0 -0
  296. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_credential_kwargs.py +0 -0
  297. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_credential_selection.py +0 -0
  298. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_credential_values.py +0 -0
  299. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_dataset_polling.py +0 -0
  300. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_dataset_sync.py +0 -0
  301. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_datasets.py +0 -0
  302. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_decision_client_spans.py +0 -0
  303. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_decorators.py +0 -0
  304. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deploy.py +0 -0
  305. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  306. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deploy_program_id.py +0 -0
  307. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deployment_metadata.py +0 -0
  308. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deployment_runs.py +0 -0
  309. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_deployments_read.py +0 -0
  310. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_eval.py +0 -0
  311. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_eval_example_attribution.py +0 -0
  312. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_eval_start.py +0 -0
  313. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_eval_trace_grouping.py +0 -0
  314. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_exception_paths.py +0 -0
  315. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_execute.py +0 -0
  316. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_execute_run_identity.py +0 -0
  317. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_exporter.py +0 -0
  318. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_exporter_program_names.py +0 -0
  319. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_exporter_queue.py +0 -0
  320. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_exporter_real.py +0 -0
  321. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_fake_lm.py +0 -0
  322. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_gepa_callback.py +0 -0
  323. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_gepa_integration.py +0 -0
  324. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_gepa_output_normalization.py +0 -0
  325. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_gepa_resume.py +0 -0
  326. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_identity.py +0 -0
  327. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_imaging.py +0 -0
  328. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_import_weight.py +0 -0
  329. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_ingest_conformance.py +0 -0
  330. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_ingest_second_producer.py +0 -0
  331. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_integration.py +0 -0
  332. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_interpreter_callback.py +0 -0
  333. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_ir_encoder_properties.py +0 -0
  334. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_ir_golden_vectors.py +0 -0
  335. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_ir_roundtrip.py +0 -0
  336. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_lm15_native_calls.py +0 -0
  337. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_lm_configuration.py +0 -0
  338. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_lm_usage_shapes.py +0 -0
  339. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_mcp_tools.py +0 -0
  340. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_models.py +0 -0
  341. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_multi_client_stress_readiness.py +0 -0
  342. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimization_progress.py +0 -0
  343. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimization_start.py +0 -0
  344. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize.py +0 -0
  345. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_budget.py +0 -0
  346. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_lift.py +0 -0
  347. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_lms.py +0 -0
  348. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_proposer.py +0 -0
  349. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_reflection_lm.py +0 -0
  350. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_optimize_terminal_failure.py +0 -0
  351. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_packaging.py +0 -0
  352. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_packaging_callable_source.py +0 -0
  353. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_packaging_mcp_tools.py +0 -0
  354. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_packaging_program_source.py +0 -0
  355. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_poll_until.py +0 -0
  356. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_program_id_deprecation.py +0 -0
  357. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_program_identity.py +0 -0
  358. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_program_lineage_persist.py +0 -0
  359. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_program_names_in_deploy.py +0 -0
  360. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_programs_create.py +0 -0
  361. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_project_payload.py +0 -0
  362. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_real_provider_twins_readiness.py +0 -0
  363. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_reflector_properties.py +0 -0
  364. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_reflector_smoke.py +0 -0
  365. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_release_stress_script.py +0 -0
  366. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_replaced_names.py +0 -0
  367. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_repro_reactv2_bugs.py +0 -0
  368. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_require_version_bump.py +0 -0
  369. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_retry.py +0 -0
  370. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_reversible_failures.py +0 -0
  371. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_rlm_patch.py +0 -0
  372. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_run_attribution_paths.py +0 -0
  373. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_runtime_tools.py +0 -0
  374. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_schemas.py +0 -0
  375. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_seal_lm_credential.py +0 -0
  376. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_seed_review_run.py +0 -0
  377. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_signature_inputs.py +0 -0
  378. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_span_references.py +0 -0
  379. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_span_sent_once.py +0 -0
  380. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_system_one.py +0 -0
  381. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_trace_context_concurrency.py +0 -0
  382. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_trace_fact_wire_contract.py +0 -0
  383. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_trace_facts.py +0 -0
  384. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_trace_render.py +0 -0
  385. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_traces_read.py +0 -0
  386. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_tracked_gepa.py +0 -0
  387. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_tracker_enrich.py +0 -0
  388. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_truncation.py +0 -0
  389. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_upload_auth_headers.py +0 -0
  390. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_valsets_api.py +0 -0
  391. {cmpnd-0.59.0 → cmpnd-0.60.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.59.0
3
+ Version: 0.60.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>
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
+ import json
6
7
  import sys
7
8
  import urllib.parse
8
9
  from argparse import ArgumentParser, _SubParsersAction
@@ -12,8 +13,8 @@ from .. import api_client, pagination
12
13
  from . import provider_credentials
13
14
 
14
15
 
15
- # Every id in this group is the value `programs list` prints as `program_hash`,
16
- # which the routes resolve as a *compiled* program hash — they look it up
16
+ # The hash that show, traces, stats, usage and select take is the value `programs optimized list`
17
+ # prints as `program_hash`, which the routes resolve as a *compiled* program hash — they look it up
17
18
  # against optimization runs and map it to a program signature hash internally.
18
19
  # The two hashes are different values, so a real signature hash 404s here. The
19
20
  # argument help says which one it wants rather than naming the concept the
@@ -48,34 +49,74 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
48
49
  create.add_argument("--json", action="store_true", help="Emit the created program as JSON instead of its id")
49
50
  create.set_defaults(func=handle_create)
50
51
 
51
- ls = sub.add_parser("list", help="List programs")
52
- ls.add_argument("--name", dest="program_name", help="Filter by program name")
53
- ls.add_argument("--model", dest="model_name", help="Filter by model name")
52
+ ls = sub.add_parser(
53
+ "list",
54
+ help="List your organization's programs",
55
+ description=(
56
+ "List your organization's live programs, newest first, with each program's name and p_ id. A program "
57
+ "lists from its creation on, before anything runs under it. With --trashed, list the Trash instead."
58
+ ),
59
+ )
54
60
  ls.add_argument(
55
61
  "--trashed",
56
62
  action="store_true",
57
63
  help="List the programs in the Trash instead, with when each is purged",
58
64
  )
59
- ls.add_argument("--order-by", default="run_count", help="Sort field (default: run_count)")
60
- ls.add_argument("--order", default="desc", choices=["asc", "desc"], help="Sort direction")
61
65
  pagination.add_args(ls, with_page_aliases=True, default_limit=50)
66
+ # The optimized-program filters and sort moved to `programs optimized list`. They stay declared so that a
67
+ # command that passes one gets an error naming the command that takes it.
68
+ for flag, dest in [
69
+ ("--name", "program_name"),
70
+ ("--model", "model_name"),
71
+ ("--order-by", "order_by"),
72
+ ("--order", "order"),
73
+ ]:
74
+ ls.add_argument(flag, dest=dest, default=None, help=argparse.SUPPRESS)
62
75
  ls.set_defaults(func=handle_list)
63
76
 
77
+ # The optimized programs are the compiled programs optimization runs produced, grouped by compiled program
78
+ # hash. They are different records from the org's programs, so they nest, as the registry does.
79
+ optimized = sub.add_parser("optimized", help="Browse the optimized programs that optimization runs produced")
80
+ optimized_sub = optimized.add_subparsers(dest="optimized_action")
81
+ opt_list = optimized_sub.add_parser(
82
+ "list",
83
+ help="List optimized programs by compiled program hash",
84
+ description=(
85
+ "List the compiled programs your optimization runs produced, one row per compiled program hash, with "
86
+ "their run counts and scores. `programs show`, `traces`, `stats` and `usage` take the hash from the "
87
+ "program_hash column."
88
+ ),
89
+ )
90
+ opt_list.add_argument("--name", dest="program_name", help="Filter by program name")
91
+ opt_list.add_argument("--model", dest="model_name", help="Filter by model name")
92
+ opt_list.add_argument("--order-by", default="run_count", help="Sort field (default: run_count)")
93
+ opt_list.add_argument("--order", default="desc", choices=["asc", "desc"], help="Sort direction")
94
+ pagination.add_args(opt_list, with_page_aliases=True, default_limit=50)
95
+ opt_list.set_defaults(func=handle_optimized_list)
96
+
64
97
  show = sub.add_parser("show", help="Show program detail")
65
- show.add_argument("id", help="Program hash, as `programs list` prints it in the program_hash column")
98
+ show.add_argument(
99
+ "id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
100
+ )
66
101
  show.set_defaults(func=handle_show)
67
102
 
68
103
  tr = sub.add_parser("traces", help="List traces for a program")
69
- tr.add_argument("id", help="Program hash, as `programs list` prints it in the program_hash column")
104
+ tr.add_argument(
105
+ "id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
106
+ )
70
107
  pagination.add_args(tr, with_page_aliases=False, default_limit=50)
71
108
  tr.set_defaults(func=handle_traces)
72
109
 
73
110
  stats = sub.add_parser("stats", help="Show a program's execution statistics")
74
- stats.add_argument("id", help="Program hash, as `programs list` prints it in the program_hash column")
111
+ stats.add_argument(
112
+ "id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
113
+ )
75
114
  stats.set_defaults(func=handle_stats)
76
115
 
77
116
  usage = sub.add_parser("usage", help="Show a program's trace volume, tokens and cost over time")
78
- usage.add_argument("id", help="Program hash, as `programs list` prints it in the program_hash column")
117
+ usage.add_argument(
118
+ "id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
119
+ )
79
120
  usage.add_argument("--hours", type=int, default=24, help="Rolling window in hours (default %(default)s)")
80
121
  # Constrained on purpose: an unrecognised period buckets hourly server-side
81
122
  # rather than failing, so a typo would answer a different question silently.
@@ -132,7 +173,9 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
132
173
  purge.set_defaults(func=handle_purge)
133
174
 
134
175
  select = sub.add_parser("select", help="Pin an optimization run as this program's chosen version")
135
- select.add_argument("id", help="Program hash, as `programs list` prints it in the program_hash column")
176
+ select.add_argument(
177
+ "id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
178
+ )
136
179
  select.add_argument("--run", dest="run_id", required=True, help="Optimization run ID (op_...) to pin")
137
180
  select.set_defaults(func=handle_select)
138
181
 
@@ -178,14 +221,24 @@ def handle_create(client: api_client.ApiClient, args: Any) -> Any:
178
221
 
179
222
 
180
223
  def handle_list(client: api_client.ApiClient, args: Any) -> Any:
224
+ """List the org's live programs by name and p_ id, or the Trash with ``--trashed``."""
225
+ for attr, flag in [
226
+ ("program_name", "--name"),
227
+ ("model_name", "--model"),
228
+ ("order_by", "--order-by"),
229
+ ("order", "--order"),
230
+ ]:
231
+ if getattr(args, attr, None) is not None:
232
+ from . import top
233
+
234
+ raise api_client.CliError(
235
+ f"{flag} filters the optimized programs, which `{top.hint_programs('optimized', 'list')}` lists. "
236
+ f"`{top.hint_programs('list')}` lists your organization's programs by name and p_ id."
237
+ )
181
238
  if getattr(args, "trashed", False):
182
- # The Trash is its own short list, unpaged and newest first, so the search filters, sort and paging do
183
- # not apply. A flag left at its default cannot be told apart from one not passed.
239
+ # The Trash is its own short list, unpaged and newest first, so paging does not apply. A flag left at its
240
+ # default cannot be told apart from one not passed.
184
241
  for attr, flag, default in [
185
- ("program_name", "--name", None),
186
- ("model_name", "--model", None),
187
- ("order_by", "--order-by", "run_count"),
188
- ("order", "--order", "desc"),
189
242
  ("limit", "--limit", 50),
190
243
  ("offset", "--offset", 0),
191
244
  ("per_page", "--per-page", None),
@@ -194,6 +247,59 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
194
247
  if getattr(args, attr, default) != default:
195
248
  raise api_client.CliError(f"{flag} does not apply to --trashed, which lists the whole Trash")
196
249
  return client.get("/api/v1/programs/trash")
250
+ params = pagination.wire(args, "limit_offset", as_str=True)
251
+ # Read as text: a server from before the program list, such as self-host release 2026-001, redirects this
252
+ # path to its dispatcher, and the redirect's body is HTML.
253
+ try:
254
+ raw = client.get_text("/api/v1/programs", params=params)
255
+ except api_client.CliError as exc:
256
+ if exc.status_code not in (404, 405):
257
+ raise
258
+ return _list_recorded(client, params)
259
+ try:
260
+ listed = json.loads(raw)
261
+ except ValueError:
262
+ listed = None
263
+ if isinstance(listed, dict) and "programs" in listed:
264
+ return listed
265
+ return _list_recorded(client, params)
266
+
267
+
268
+ # GET /api/v1/modules on a server from before the program list answers at most this many programs
269
+ # (modulesPageCap in self-host release 2026-001), and nothing pages past them.
270
+ _RECORDED_PROGRAMS_CAP = 200
271
+
272
+
273
+ def _list_recorded(client: api_client.ApiClient, params: dict[str, str]) -> dict[str, Any]:
274
+ """List the programs a server from before the program list recorded traces for, paged here.
275
+
276
+ GET /api/v1/modules answers them in one array, by name and legacy hash, up to its cap. A program with no
277
+ traces yet is not in it. A full array may be missing programs past the cap, so the answer then has no total
278
+ and says it is incomplete.
279
+ """
280
+ rows = client.get("/api/v1/modules")
281
+ if not isinstance(rows, list):
282
+ raise api_client.CliError(
283
+ "this cmpnd server answered neither the program list nor the list of recorded programs"
284
+ )
285
+ complete = len(rows) < _RECORDED_PROGRAMS_CAP
286
+ note = "note: this cmpnd server predates the program list, so this lists the programs it recorded traces for."
287
+ if not complete:
288
+ note += f" It answers at most {_RECORDED_PROGRAMS_CAP} of them, so programs past those are missing."
289
+ print(note + " Upgrade the server to list every program by name and p_ id.", file=sys.stderr)
290
+ limit, offset = int(params["limit"]), int(params["offset"])
291
+ return {
292
+ "programs": rows[offset : offset + limit],
293
+ "total": len(rows) if complete else None,
294
+ "complete": complete,
295
+ "limit": limit,
296
+ "offset": offset,
297
+ "has_more": offset + limit < len(rows),
298
+ }
299
+
300
+
301
+ def handle_optimized_list(client: api_client.ApiClient, args: Any) -> Any:
302
+ """List the optimized programs, one row per compiled program hash."""
197
303
  params: dict[str, str] = pagination.wire(args, "per_page", as_str=True)
198
304
  params["order_by"] = args.order_by
199
305
  params["order_direction"] = args.order
@@ -185,6 +185,7 @@ RELATION_NOUNS: dict[str, str] = {
185
185
  "failed-examples": "the examples a compared candidate got wrong",
186
186
  "examples": "the examples belonging to a dataset or an eval run",
187
187
  "registry": "the registered programs or signatures, as distinct from the observed ones",
188
+ "optimized": "the compiled programs optimization runs produced, as distinct from the organization's programs",
188
189
  "stats": "a program's execution statistics",
189
190
  "usage": "a program's usage over time",
190
191
  "openapi": "the OpenAPI document a deployment serves",
@@ -41,6 +41,27 @@ The CLI refuses `--program-id`, `cmpnd programs create` without a program
41
41
  name, and its `--name`. These inputs are deprecated, and the error shows the
42
42
  program name form.
43
43
 
44
+ `cmpnd programs list` lists your organization's live programs, newest first,
45
+ with each program's `name` and `program_id`. It reads `GET /api/v1/programs`,
46
+ which lists a program from its creation on, before anything runs under it. A
47
+ program with no name has a null `name`. The answer also holds `total`, the
48
+ count of live programs, and `has_more`. The command pages with `--limit` and
49
+ `--offset`, and needs `traces:read`.
50
+
51
+ A server from before the program list, such as self-host release 2026-001, has
52
+ no `GET /api/v1/programs`. Against one, `cmpnd programs list` says so on stderr
53
+ and lists the programs that server recorded traces for, from
54
+ `GET /api/v1/modules`, by name and legacy hash. A program with no traces yet is
55
+ not in that list. That route answers at most 200 programs, so when it is full
56
+ the answer has a null `total`, `complete` is false, and the note says programs
57
+ are missing.
58
+
59
+ `cmpnd programs optimized list` lists the compiled programs that optimization
60
+ runs produced, one row per compiled program hash, from
61
+ `GET /api/v1/programs/v2`. It takes `--name`, `--model`, `--order-by` and
62
+ `--order`. `cmpnd programs list` refuses those flags with an error that names
63
+ `cmpnd programs optimized list`.
64
+
44
65
  ## The Trash
45
66
 
46
67
  A program in the Trash leaves every view, and its traces with it, until it is
@@ -281,10 +302,10 @@ Some older spellings still run as deprecated aliases and print a warning:
281
302
  - `credentials add` for `create`
282
303
 
283
304
  Every list command pages with `--limit` and `--offset`. The `traces`, `evals`,
284
- `optimizations`, `deployments` and `programs` lists also accept `--page` and
285
- `--per-page` as deprecated aliases. On those page-number commands, `--offset`
286
- must be a multiple of `--limit`, and the error names the nearest valid
287
- offsets. `deployments runs list` still accepts the deprecated `--cursor`, which
305
+ `optimizations`, `deployments`, `programs` and `programs optimized` lists also
306
+ accept `--page` and `--per-page` as deprecated aliases. Every one of those
307
+ except `programs list` pages by page number on the wire, so its `--offset` must
308
+ be a multiple of `--limit`, and the error names the nearest valid offsets. `deployments runs list` still accepts the deprecated `--cursor`, which
288
309
  pages from a previous response's `next_cursor` and cannot be combined with
289
310
  `--offset`.
290
311
 
@@ -333,9 +354,10 @@ The `signatures` and `programs` groups name programs differently.
333
354
  hash, which identifies a program's inputs and outputs and everything recorded
334
355
  under them: traces, examples, evals and revisions. The CLI passes an id
335
356
  through unchanged, and serve tells the two apart by the `p_` prefix.
336
- `cmpnd programs` reads take a compiled program hash, the value in the
337
- `program_hash` column of `cmpnd programs list`, and its Trash commands take a
338
- program name or id ("The Trash" above). Its routes resolve the hash
357
+ `cmpnd programs show`, `traces`, `stats`, `usage` and `select` take a compiled
358
+ program hash, the value in the `program_hash` column of
359
+ `cmpnd programs optimized list`. `cmpnd programs list` prints names and ids, and
360
+ the Trash commands take a program name or id ("The Trash" above). Its routes resolve the hash
339
361
  through optimization runs, so they do not find a signature hash. Each group's
340
362
  `--help` names the column its IDs come from.
341
363
 
@@ -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.59.0"
8
+ version = "0.60.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -103,7 +103,9 @@ class TestDevGate:
103
103
  mock_response = {"programs": [], "total": 0}
104
104
  with mock.patch("sys.argv", ["cmpnd", "programs", "list"]):
105
105
  with mock.patch.dict(os.environ, env, clear=True):
106
- with mock.patch("cmpnd.cli.api_client.ApiClient.get", return_value=mock_response):
106
+ with mock.patch(
107
+ "cmpnd.cli.api_client.ApiClient.get_text", autospec=True, return_value=json.dumps(mock_response)
108
+ ):
107
109
  main()
108
110
  captured = capsys.readouterr()
109
111
  output = json.loads(captured.out)
@@ -3,13 +3,15 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
+ import json
7
+ from unittest import mock
6
8
 
9
+ import httpx
7
10
  import pytest
8
11
 
9
- from cmpnd.cli.api_client import CliError
12
+ from cmpnd.cli.api_client import ApiClient, CliError
10
13
  from cmpnd.cli.commands import programs
11
14
  from cmpnd.cli.commands.programs import (
12
- handle_list,
13
15
  handle_registry_list,
14
16
  handle_registry_show,
15
17
  handle_select,
@@ -28,44 +30,164 @@ def _build_parser() -> argparse.ArgumentParser:
28
30
 
29
31
 
30
32
  class TestProgramsList:
33
+ def test_lists_the_orgs_programs_by_limit_and_offset(self, mock_client):
34
+ listed = {"programs": [{"program_id": "p_0123456789Ab", "name": "triage"}], "total": 1}
35
+ mock_client.get_text.return_value = json.dumps(listed)
36
+ args = _build_parser().parse_args(["programs", "list"])
37
+ assert args.func(mock_client, args) == listed
38
+ mock_client.get_text.assert_called_once_with("/api/v1/programs", params={"limit": "50", "offset": "0"})
39
+
40
+ def test_pages_by_any_offset(self, mock_client):
41
+ mock_client.get_text.return_value = json.dumps({"programs": []})
42
+ args = _build_parser().parse_args(["programs", "list", "--limit", "10", "--offset", "5"])
43
+ args.func(mock_client, args)
44
+ assert mock_client.get_text.call_args[1]["params"] == {"limit": "10", "offset": "5"}
45
+
46
+ def test_deprecated_paging_still_works_and_warns(self, mock_client, capsys):
47
+ args = _build_parser().parse_args(["programs", "list", "--page", "2", "--per-page", "5"])
48
+ mock_client.get_text.return_value = json.dumps({"programs": []})
49
+ args.func(mock_client, args)
50
+ assert mock_client.get_text.call_args[1]["params"] == {"limit": "5", "offset": "5"}
51
+ assert "deprecated" in capsys.readouterr().err
52
+
53
+ @pytest.mark.parametrize(
54
+ "flag",
55
+ [["--name", "RAG"], ["--model", "gpt-4o"], ["--order-by", "best_score"], ["--order", "asc"]],
56
+ )
57
+ def test_optimized_program_flags_point_at_their_command(self, mock_client, flag):
58
+ args = _build_parser().parse_args(["programs", "list", *flag])
59
+ with pytest.raises(CliError, match=f"{flag[0]} .*`cmpnd programs optimized list`"):
60
+ args.func(mock_client, args)
61
+ mock_client.get.assert_not_called()
62
+ mock_client.get_text.assert_not_called()
63
+
64
+
65
+ class TestProgramsListOnAnOlderServer:
66
+ """A server from before the program list, such as self-host release 2026-001, has no
67
+ GET /api/v1/programs. Its mux redirects the path to the dispatcher, which answers 404.
68
+ The CLI then lists the programs that server recorded traces for, from /api/v1/modules."""
69
+
70
+ MODULES = [
71
+ {"program_signature_hash": "08729442d1b3a3bf", "name": "Triage"},
72
+ {"program_signature_hash": "0000000000abcdef", "name": "Rag"},
73
+ {"program_signature_hash": "00000000000c0ffe", "name": "Summarize"},
74
+ ]
75
+
76
+ def _over_the_wire(self, answers):
77
+ """A real client over a transport that answers by path: `sent` collects the requests."""
78
+ sent: list[httpx.Request] = []
79
+
80
+ def handle(request: httpx.Request) -> httpx.Response:
81
+ sent.append(request)
82
+ return answers[request.url.path]
83
+
84
+ real = httpx.Client
85
+ patch = mock.patch("httpx.Client", lambda **kwargs: real(**kwargs, transport=httpx.MockTransport(handle)))
86
+ return sent, patch
87
+
88
+ def _list(self, answers, *argv: str):
89
+ sent, patch = self._over_the_wire(answers)
90
+ args = _build_parser().parse_args(["programs", "list", *argv])
91
+ with patch:
92
+ result = args.func(ApiClient(api_key="k", endpoint="http://serve.test"), args)
93
+ return sent, result
94
+
95
+ def test_a_redirect_falls_back_to_the_recorded_programs(self, capsys):
96
+ redirect = httpx.Response(
97
+ 307,
98
+ headers={"Location": "/api/v1/programs/?limit=2&offset=1"},
99
+ text='<a href="/api/v1/programs/?limit=2&amp;offset=1">Temporary Redirect</a>.',
100
+ )
101
+ sent, result = self._list(
102
+ {"/api/v1/programs": redirect, "/api/v1/modules": httpx.Response(200, json=self.MODULES)},
103
+ "--limit",
104
+ "2",
105
+ "--offset",
106
+ "1",
107
+ )
108
+ assert [r.url.path for r in sent] == ["/api/v1/programs", "/api/v1/modules"]
109
+ assert result == {
110
+ "programs": self.MODULES[1:3],
111
+ "total": 3,
112
+ "complete": True,
113
+ "limit": 2,
114
+ "offset": 1,
115
+ "has_more": False,
116
+ }
117
+ assert "predates the program list" in capsys.readouterr().err
118
+
119
+ def test_a_full_answer_has_no_total_and_says_programs_are_missing(self, capsys):
120
+ # Self-host release 2026-001 answers at most 200 recorded programs, and nothing pages past them.
121
+ recorded = [{"program_signature_hash": f"{n:016x}", "name": f"P{n}"} for n in range(200)]
122
+ _, result = self._list(
123
+ {
124
+ "/api/v1/programs": httpx.Response(404, json={"error": "not found"}),
125
+ "/api/v1/modules": httpx.Response(200, json=recorded),
126
+ },
127
+ "--limit",
128
+ "50",
129
+ "--offset",
130
+ "150",
131
+ )
132
+ assert result["programs"] == recorded[150:200]
133
+ assert result["total"] is None
134
+ assert result["complete"] is False
135
+ assert result["has_more"] is False
136
+ assert "at most 200" in capsys.readouterr().err
137
+
138
+ def test_a_not_found_falls_back_too(self):
139
+ sent, result = self._list(
140
+ {
141
+ "/api/v1/programs": httpx.Response(404, json={"error": "not found"}),
142
+ "/api/v1/modules": httpx.Response(200, json=self.MODULES),
143
+ }
144
+ )
145
+ assert [r.url.path for r in sent] == ["/api/v1/programs", "/api/v1/modules"]
146
+ assert result["total"] == 3
147
+
148
+ def test_a_current_server_answers_without_a_fallback(self):
149
+ listed = {"programs": [{"program_id": "p_0123456789Ab", "name": "triage"}], "total": 1}
150
+ sent, result = self._list({"/api/v1/programs": httpx.Response(200, json=listed)})
151
+ assert [r.url.path for r in sent] == ["/api/v1/programs"]
152
+ assert result == listed
153
+
154
+
155
+ class TestProgramsOptimizedList:
31
156
  def test_default_params(self, mock_client):
32
157
  mock_client.get.return_value = {"programs": []}
33
- args = argparse.Namespace(
34
- program_name=None, model_name=None, order_by="run_count", order="desc", limit=50, offset=0
35
- )
36
- result = handle_list(mock_client, args)
158
+ args = _build_parser().parse_args(["programs", "optimized", "list"])
159
+ assert args.func(mock_client, args) == {"programs": []}
37
160
  mock_client.get.assert_called_once_with(
38
161
  "/api/v1/programs/v2",
39
162
  params={"page": "1", "per_page": "50", "order_by": "run_count", "order_direction": "desc"},
40
163
  )
41
- assert result == {"programs": []}
42
164
 
43
165
  def test_with_filters(self, mock_client):
44
166
  mock_client.get.return_value = {"programs": []}
45
- args = argparse.Namespace(
46
- program_name="RAG", model_name="gpt-4o", order_by="run_count", order="asc", limit=10, offset=10
167
+ args = _build_parser().parse_args(
168
+ ["programs", "optimized", "list", "--name", "RAG", "--model", "gpt-4o", "--order", "asc"]
169
+ + ["--limit", "10", "--offset", "10"]
47
170
  )
48
- handle_list(mock_client, args)
171
+ args.func(mock_client, args)
49
172
  params = mock_client.get.call_args[1]["params"]
50
173
  assert params["program_name"] == "RAG"
51
174
  assert params["model_name"] == "gpt-4o"
175
+ assert params["order_direction"] == "asc"
52
176
  assert params["page"] == "2"
53
177
 
54
178
  def test_deprecated_paging_still_works_and_warns(self, mock_client, capsys):
55
- parser = _build_parser()
56
- args = parser.parse_args(["programs", "list", "--page", "2", "--per-page", "5"])
179
+ args = _build_parser().parse_args(["programs", "optimized", "list", "--page", "2", "--per-page", "5"])
57
180
  mock_client.get.return_value = {"programs": []}
58
- handle_list(mock_client, args)
181
+ args.func(mock_client, args)
59
182
  params = mock_client.get.call_args[1]["params"]
60
183
  assert params["page"] == "2"
61
184
  assert params["per_page"] == "5"
62
185
  assert "deprecated" in capsys.readouterr().err
63
186
 
64
187
  def test_non_multiple_offset_errors(self, mock_client):
65
- parser = _build_parser()
66
- args = parser.parse_args(["programs", "list", "--limit", "10", "--offset", "5"])
188
+ args = _build_parser().parse_args(["programs", "optimized", "list", "--limit", "10", "--offset", "5"])
67
189
  with pytest.raises(CliError):
68
- handle_list(mock_client, args)
190
+ args.func(mock_client, args)
69
191
 
70
192
 
71
193
  class TestProgramsShow:
@@ -273,16 +395,7 @@ class TestProgramsListTrashed:
273
395
  assert args.func(mock_client, args) == {"programs": [], "retention_days": 7}
274
396
  mock_client.get.assert_called_once_with("/api/v1/programs/trash")
275
397
 
276
- def test_search_filters_do_not_apply(self, mock_client):
277
- args = _build_parser().parse_args(["programs", "list", "--trashed", "--name", "x"])
278
- with pytest.raises(CliError, match="--name does not apply"):
279
- args.func(mock_client, args)
280
- mock_client.get.assert_not_called()
281
-
282
- @pytest.mark.parametrize(
283
- "flag",
284
- [["--order-by", "name"], ["--order", "asc"], ["--limit", "5"], ["--offset", "10"]],
285
- )
398
+ @pytest.mark.parametrize("flag", [["--limit", "5"], ["--offset", "10"], ["--page", "2"]])
286
399
  def test_paging_and_sort_do_not_apply(self, mock_client, flag):
287
400
  args = _build_parser().parse_args(["programs", "list", "--trashed", *flag])
288
401
  with pytest.raises(CliError, match=f"{flag[0]} does not apply"):
@@ -505,7 +505,7 @@ wheels = [
505
505
 
506
506
  [[package]]
507
507
  name = "cmpnd"
508
- version = "0.59.0"
508
+ version = "0.60.0"
509
509
  source = { editable = "." }
510
510
  default-groups = ["dspy-3-4-1"]
511
511
  dependencies = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes