cmpnd 0.40.1__tar.gz → 0.41.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.
- {cmpnd-0.40.1 → cmpnd-0.41.0}/PKG-INFO +5 -2
- {cmpnd-0.40.1 → cmpnd-0.41.0}/README.md +1 -1
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/top.py +13 -19
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/shell.py +3 -3
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/deployment.py +40 -18
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimization.py +10 -22
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/programs.py +17 -1
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/cli.md +9 -4
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/sdk-instrumentation.md +8 -5
- {cmpnd-0.40.1 → cmpnd-0.41.0}/pyproject.toml +8 -1
- cmpnd-0.41.0/scripts/check-pip-install.sh +34 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_shell.py +12 -6
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_top.py +21 -12
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deploy_program_id.py +56 -47
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_programs_create.py +15 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/uv.lock +43 -39
- {cmpnd-0.40.1 → cmpnd-0.41.0}/.gitignore +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/AGENTS.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/CLAUDE.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/client.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/context.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/models.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/references.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/client.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/README.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/drain.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_authored.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_callback.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_client.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_context.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_eval.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_execute.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_integration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_models.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_retry.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.40.1 → cmpnd-0.41.0}/tests/test_xxh64.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41.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>
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
20
|
Requires-Python: >=3.10
|
|
20
21
|
Requires-Dist: dspy>=3.1.3
|
|
@@ -27,8 +28,10 @@ Requires-Dist: xxhash>=3.0.0
|
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: basedpyright>=1.39; extra == 'dev'
|
|
29
30
|
Requires-Dist: boto3>=1.34; extra == 'dev'
|
|
31
|
+
Requires-Dist: gepa; extra == 'dev'
|
|
30
32
|
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
31
33
|
Requires-Dist: jsonschema>=4.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: litellm; extra == 'dev'
|
|
32
35
|
Requires-Dist: numpy>=1.26; (python_version < '3.11') and extra == 'dev'
|
|
33
36
|
Requires-Dist: numpy>=2.3; (python_version >= '3.11') and extra == 'dev'
|
|
34
37
|
Requires-Dist: pandas>=2.2; extra == 'dev'
|
|
@@ -161,7 +164,7 @@ cmpnd.deploy(
|
|
|
161
164
|
metric=None, # optional: a metric callable, packaged for optimization
|
|
162
165
|
timeout=300, # optional: seconds to wait for the parser after the upload
|
|
163
166
|
metadata=None, # optional: a JSON-serializable dict, at most 64 KiB, stored with the deployment
|
|
164
|
-
program_id="p_…", #
|
|
167
|
+
program_id="p_…", # the program the deployment belongs to; serve requires it after self-host 2026-001
|
|
165
168
|
)
|
|
166
169
|
```
|
|
167
170
|
|
|
@@ -111,7 +111,7 @@ cmpnd.deploy(
|
|
|
111
111
|
metric=None, # optional: a metric callable, packaged for optimization
|
|
112
112
|
timeout=300, # optional: seconds to wait for the parser after the upload
|
|
113
113
|
metadata=None, # optional: a JSON-serializable dict, at most 64 KiB, stored with the deployment
|
|
114
|
-
program_id="p_…", #
|
|
114
|
+
program_id="p_…", # the program the deployment belongs to; serve requires it after self-host 2026-001
|
|
115
115
|
)
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -811,11 +811,12 @@ def _parse_deploy_metadata(raw: str | None) -> dict[str, Any] | None:
|
|
|
811
811
|
raise api_client.CliError(f"--metadata is invalid: {exc}")
|
|
812
812
|
|
|
813
813
|
|
|
814
|
-
def _program_id_arg(value: str | None
|
|
814
|
+
def _program_id_arg(value: str | None) -> str | None:
|
|
815
815
|
"""The program id a deploy or optimize names: ``--program-id``, else ``CMPND_PROGRAM_ID``.
|
|
816
816
|
|
|
817
|
-
Checked here, before a file is staged, so a
|
|
818
|
-
|
|
817
|
+
Checked here, before a file is staged, so a mistyped id is a usage error naming
|
|
818
|
+
its source rather than a refusal after work was done. serve decides whether a
|
|
819
|
+
deployment needs an id, so a missing one is not refused here.
|
|
819
820
|
"""
|
|
820
821
|
from ... import identity
|
|
821
822
|
|
|
@@ -823,12 +824,6 @@ def _program_id_arg(value: str | None, *, required: bool) -> str | None:
|
|
|
823
824
|
if value is None and os.environ.get("CMPND_PROGRAM_ID"):
|
|
824
825
|
value, source = os.environ["CMPND_PROGRAM_ID"], "CMPND_PROGRAM_ID"
|
|
825
826
|
if value is None:
|
|
826
|
-
if required:
|
|
827
|
-
raise api_client.CliError(
|
|
828
|
-
"--program-id is required: a deployment always names its program. "
|
|
829
|
-
"Create one with `cmpnd programs create`, then pass --program-id p_… "
|
|
830
|
-
"(or set CMPND_PROGRAM_ID)."
|
|
831
|
-
)
|
|
832
827
|
return None
|
|
833
828
|
try:
|
|
834
829
|
return identity.validate_program_id(value, source=source)
|
|
@@ -855,9 +850,7 @@ def cmd_deploy(client: api_client.ApiClient, args: Any) -> None:
|
|
|
855
850
|
|
|
856
851
|
from ... import authored, configuration, deployment
|
|
857
852
|
|
|
858
|
-
|
|
859
|
-
# names its program, and the server would refuse the create anyway.
|
|
860
|
-
program_id = _program_id_arg(getattr(args, "program_id", None), required=True)
|
|
853
|
+
program_id = _program_id_arg(getattr(args, "program_id", None))
|
|
861
854
|
configuration.configure(api_key=client._api_key, endpoint=client._endpoint, program_id=program_id)
|
|
862
855
|
try:
|
|
863
856
|
if args.source == "-" or args.source is None:
|
|
@@ -881,7 +874,10 @@ def cmd_deploy(client: api_client.ApiClient, args: Any) -> None:
|
|
|
881
874
|
|
|
882
875
|
metadata = _parse_deploy_metadata(getattr(args, "metadata", None))
|
|
883
876
|
print(_c("deploying…", "dim"), file=sys.stderr)
|
|
884
|
-
|
|
877
|
+
try:
|
|
878
|
+
deployed = deployment.deploy_bundle(prepared.bundle, prepared.facts, metadata=metadata, program_id=program_id)
|
|
879
|
+
except ValueError as exc:
|
|
880
|
+
raise api_client.CliError(str(exc)) from exc
|
|
885
881
|
|
|
886
882
|
deployment_id = deployed.deployment_id
|
|
887
883
|
print(deployment_id)
|
|
@@ -946,9 +942,7 @@ def cmd_optimize(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
946
942
|
from ... import authored, configuration, optimization
|
|
947
943
|
|
|
948
944
|
deployment_id = getattr(args, "deployment_id", None)
|
|
949
|
-
|
|
950
|
-
# already names its program.
|
|
951
|
-
program_id = _program_id_arg(getattr(args, "program_id", None), required=deployment_id is None)
|
|
945
|
+
program_id = _program_id_arg(getattr(args, "program_id", None))
|
|
952
946
|
configuration.configure(api_key=client._api_key, endpoint=client._endpoint, program_id=program_id)
|
|
953
947
|
try:
|
|
954
948
|
if args.source == "-" or args.source is None:
|
|
@@ -1266,7 +1260,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
1266
1260
|
dest="program_id",
|
|
1267
1261
|
default=None,
|
|
1268
1262
|
help="Program id (p_…) the deployment belongs to, from `cmpnd programs create`. "
|
|
1269
|
-
"Required
|
|
1263
|
+
"Defaults to CMPND_PROGRAM_ID. Required by every serve after self-host release 2026-001",
|
|
1270
1264
|
)
|
|
1271
1265
|
dp.add_argument(
|
|
1272
1266
|
"--entrypoint",
|
|
@@ -1351,8 +1345,8 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
1351
1345
|
dest="program_id",
|
|
1352
1346
|
default=None,
|
|
1353
1347
|
help="Program id (p_…) being optimized, from `cmpnd programs create`; defaults to "
|
|
1354
|
-
"CMPND_PROGRAM_ID. Required
|
|
1355
|
-
"with --deployment-id",
|
|
1348
|
+
"CMPND_PROGRAM_ID. Required by every serve after self-host release 2026-001 when "
|
|
1349
|
+
"the file's program is deployed here, optional with --deployment-id",
|
|
1356
1350
|
)
|
|
1357
1351
|
opt.add_argument(
|
|
1358
1352
|
"--reflection-model",
|
|
@@ -490,8 +490,8 @@ def _make_namespace(client: api_client.ApiClient, endpoint: str) -> dict[str, An
|
|
|
490
490
|
"""Deploy authored Python from a file or ``"-"`` for stdin.
|
|
491
491
|
|
|
492
492
|
``program_id`` (``p_…``, from ``cmpnd programs create``) names the program
|
|
493
|
-
the deployment belongs to; it defaults to ``CMPND_PROGRAM_ID
|
|
494
|
-
|
|
493
|
+
the deployment belongs to; it defaults to ``CMPND_PROGRAM_ID``. serve
|
|
494
|
+
decides whether a deployment needs one. A fresh child imports the staged bundle. The shell
|
|
495
495
|
does not execute the file in its own process. Returns the
|
|
496
496
|
new Deployment entity so you can chain ``.watch()`` / ``.run({...})``
|
|
497
497
|
directly; also invalidates ``deps`` so the new deployment shows up
|
|
@@ -499,7 +499,7 @@ def _make_namespace(client: api_client.ApiClient, endpoint: str) -> dict[str, An
|
|
|
499
499
|
"""
|
|
500
500
|
from .. import authored, configuration, deployment
|
|
501
501
|
|
|
502
|
-
declared = commands.top._program_id_arg(program_id
|
|
502
|
+
declared = commands.top._program_id_arg(program_id)
|
|
503
503
|
configuration.configure(api_key=client._api_key, endpoint=client._endpoint, program_id=declared)
|
|
504
504
|
if source in ("-", None):
|
|
505
505
|
prepared = authored.prepare_stdin(
|
|
@@ -346,21 +346,35 @@ def _resolve_mcp_tool_references(client: httpx.Client, config, references: list[
|
|
|
346
346
|
|
|
347
347
|
|
|
348
348
|
MISSING_PROGRAM_ID = (
|
|
349
|
-
"
|
|
349
|
+
"this server requires a program id: pass program_id= (`cmpnd deploy --program-id`), or set "
|
|
350
350
|
"cmpnd.configure(program_id=...) or CMPND_PROGRAM_ID. Create one with `cmpnd programs create`."
|
|
351
351
|
)
|
|
352
352
|
|
|
353
|
+
# The error serve answers, with a 400, to a create that names no program. A serve
|
|
354
|
+
# from before program ids accepts the create instead. serve pins this prefix in
|
|
355
|
+
# deploymentsapi.missingProgramIDError.
|
|
356
|
+
_SERVE_MISSING_PROGRAM_ID = "program_id is required"
|
|
353
357
|
|
|
354
|
-
|
|
358
|
+
|
|
359
|
+
def declared_program_id(explicit: str | None, config: configuration.Config) -> str | None:
|
|
355
360
|
"""The program a deployment is created under: the explicit id, else the configured one.
|
|
356
361
|
|
|
357
|
-
A
|
|
358
|
-
|
|
362
|
+
A declared id is checked for shape before anything is packaged or sent. serve
|
|
363
|
+
decides whether a deployment needs an id, so a missing one is not refused here.
|
|
359
364
|
"""
|
|
360
365
|
chosen = explicit if explicit is not None else config.program_id
|
|
361
|
-
if chosen is None
|
|
362
|
-
|
|
363
|
-
|
|
366
|
+
return identity.validate_program_id(chosen) if chosen is not None else None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _refused_for_missing_program_id(response: httpx.Response, deploy_body: dict[str, Any]) -> bool:
|
|
370
|
+
"""Whether serve refused a create that named no program because it needs one."""
|
|
371
|
+
if response.status_code != 400 or "program_id" in deploy_body:
|
|
372
|
+
return False
|
|
373
|
+
try:
|
|
374
|
+
error = response.json().get("error")
|
|
375
|
+
except (ValueError, AttributeError):
|
|
376
|
+
return False
|
|
377
|
+
return isinstance(error, str) and error.startswith(_SERVE_MISSING_PROGRAM_ID)
|
|
364
378
|
|
|
365
379
|
|
|
366
380
|
def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
@@ -387,22 +401,24 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
|
387
401
|
with string keys, within 64 KiB. Read it back via the deployment
|
|
388
402
|
record (``cmpnd.status`` / the deployment GET API).
|
|
389
403
|
program_id: The program (``p_…``) the deployment belongs to, as
|
|
390
|
-
``cmpnd programs create`` printed it.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
404
|
+
``cmpnd programs create`` printed it. When omitted, the configured
|
|
405
|
+
id (``cmpnd.configure(program_id=...)`` or ``CMPND_PROGRAM_ID``) is
|
|
406
|
+
used. With neither, the create names no program, and serve decides:
|
|
407
|
+
every serve with program ids refuses it, and a serve from before
|
|
408
|
+
them, such as self-host release 2026-001, accepts it.
|
|
394
409
|
|
|
395
410
|
Returns:
|
|
396
411
|
A DeployedProgram that can be called with the same inputs as the
|
|
397
412
|
original module. Has a .deployment_id attribute and str() returns it.
|
|
398
413
|
|
|
399
414
|
Raises:
|
|
400
|
-
ValueError: If
|
|
415
|
+
ValueError: If the program id is malformed, or if serve requires one and
|
|
416
|
+
none was given or configured.
|
|
401
417
|
RuntimeError: If cmpnd is not configured, or if deployment fails.
|
|
402
418
|
TimeoutError: If the program isn't ready within timeout seconds.
|
|
403
419
|
"""
|
|
404
420
|
config = configuration.require_config()
|
|
405
|
-
|
|
421
|
+
program = declared_program_id(program_id, config)
|
|
406
422
|
|
|
407
423
|
# Resolve the program's MCP tool references against the org's registry, before
|
|
408
424
|
# anything is packaged and before a deployment row exists. A program with no MCP
|
|
@@ -423,12 +439,15 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
|
423
439
|
program_signature_hash = identity.compute_program_hash(module) or 0
|
|
424
440
|
|
|
425
441
|
deploy_body: dict[str, Any] = {
|
|
426
|
-
"program_id": declared_program_id,
|
|
427
442
|
"program_signature_hash": identity.format_signature_hash(program_signature_hash),
|
|
428
443
|
# Declare the ZIP's exact length so the server can size-bound the upload
|
|
429
444
|
# slot and reject an oversized deploy before signing it (Slice E).
|
|
430
445
|
"declared_content_length": len(zip_bytes),
|
|
431
446
|
}
|
|
447
|
+
# Left off when undeclared, so a serve from before program ids reads the body it
|
|
448
|
+
# always read and a newer serve answers with its own refusal.
|
|
449
|
+
if program is not None:
|
|
450
|
+
deploy_body["program_id"] = program
|
|
432
451
|
# Carry the caller's metadata blob (validated and made jsonb-storable at the
|
|
433
452
|
# call site) as a reserved top-level body key, committed with the deployment
|
|
434
453
|
# record server-side.
|
|
@@ -496,13 +515,14 @@ def deploy_bundle(
|
|
|
496
515
|
) -> execution.DeployedProgram:
|
|
497
516
|
"""Upload a checked authored-source bundle without packaging a live module.
|
|
498
517
|
|
|
499
|
-
``program_id``
|
|
500
|
-
|
|
518
|
+
``program_id`` works as it does for :func:`deploy`: the explicit id, else the
|
|
519
|
+
configured one, else none, and serve decides whether a deployment needs one.
|
|
501
520
|
"""
|
|
502
521
|
config = configuration.require_config()
|
|
503
|
-
|
|
522
|
+
program = declared_program_id(program_id, config)
|
|
504
523
|
deploy_body = facts.deploy_body()
|
|
505
|
-
|
|
524
|
+
if program is not None:
|
|
525
|
+
deploy_body["program_id"] = program
|
|
506
526
|
deploy_body["source_format"] = "authored"
|
|
507
527
|
deploy_body["declared_content_length"] = len(zip_bytes)
|
|
508
528
|
if metadata is not None:
|
|
@@ -531,6 +551,8 @@ def _deploy_prepared(
|
|
|
531
551
|
may_resend_with_no_answer=retry.never_left_the_client,
|
|
532
552
|
may_resend_after_answer=lambda _: False,
|
|
533
553
|
)
|
|
554
|
+
if _refused_for_missing_program_id(created, deploy_body):
|
|
555
|
+
raise ValueError(MISSING_PROGRAM_ID)
|
|
534
556
|
_raise_http_error(created, "create deployment")
|
|
535
557
|
answer = created.json()
|
|
536
558
|
deployment_id = answer["deployment_id"]
|
|
@@ -38,7 +38,7 @@ from typing import TYPE_CHECKING, Any, Callable, cast
|
|
|
38
38
|
import httpx
|
|
39
39
|
from httpx import HTTPError
|
|
40
40
|
|
|
41
|
-
from . import _deployments, _http, authored, configuration, deployment, execution,
|
|
41
|
+
from . import _deployments, _http, authored, configuration, deployment, execution, packaging, retry
|
|
42
42
|
from .deployment_metadata import DeploymentMetadataError, DeploymentSignatureResponse
|
|
43
43
|
|
|
44
44
|
if TYPE_CHECKING:
|
|
@@ -831,10 +831,11 @@ def optimize(
|
|
|
831
831
|
deployment_id: Skip the initial deploy by reusing an existing ready
|
|
832
832
|
deployment as program A.
|
|
833
833
|
program_id: The program (``p_…``) being optimized. Defaults to the
|
|
834
|
-
configured id.
|
|
835
|
-
``deployment_id``),
|
|
836
|
-
|
|
837
|
-
the first optimize step, the one that optimizes your
|
|
834
|
+
configured id. When the program is deployed here (no
|
|
835
|
+
``deployment_id``), the deploy carries it, and serve decides whether
|
|
836
|
+
that deploy needs one. A reused deployment already names its program.
|
|
837
|
+
Sent on the first optimize step, the one that optimizes your
|
|
838
|
+
deployment.
|
|
838
839
|
reflection_lm: **Required** — the model that proposes new instructions.
|
|
839
840
|
The search is only as good as it is, so this is a strong model,
|
|
840
841
|
typically not the one being optimized. Pass the ``dspy.LM`` you would
|
|
@@ -925,7 +926,7 @@ def optimize(
|
|
|
925
926
|
_apply_budget(config, max_metric_calls=max_metric_calls, auto=auto)
|
|
926
927
|
|
|
927
928
|
cfg = configuration.require_config()
|
|
928
|
-
program_id =
|
|
929
|
+
program_id = deployment.declared_program_id(program_id, cfg)
|
|
929
930
|
|
|
930
931
|
from . import lm_configuration
|
|
931
932
|
|
|
@@ -957,19 +958,6 @@ def optimize(
|
|
|
957
958
|
return _deployed_program(best_deployment_id, cfg, score=best_score)
|
|
958
959
|
|
|
959
960
|
|
|
960
|
-
def _declared_program_id(explicit: str | None, cfg: configuration.Config, *, deploys: bool) -> str | None:
|
|
961
|
-
"""The program an optimization names: the explicit id, else the configured one.
|
|
962
|
-
|
|
963
|
-
Required when this call deploys the program, since a deployment always names its
|
|
964
|
-
program, and refused before anything is sent. Optional when an existing
|
|
965
|
-
deployment is reused, because that deployment already names one.
|
|
966
|
-
"""
|
|
967
|
-
if deploys:
|
|
968
|
-
return deployment.require_program_id(explicit, cfg)
|
|
969
|
-
chosen = explicit if explicit is not None else cfg.program_id
|
|
970
|
-
return identity.validate_program_id(chosen) if chosen is not None else None
|
|
971
|
-
|
|
972
|
-
|
|
973
961
|
def _run_steps(
|
|
974
962
|
cfg: configuration.Config,
|
|
975
963
|
bundle: bytes,
|
|
@@ -1094,8 +1082,8 @@ def optimize_authored(
|
|
|
1094
1082
|
one directory on the executor, so a path they disagree about is refused, and it is
|
|
1095
1083
|
worth refusing here where both files are in reach.
|
|
1096
1084
|
|
|
1097
|
-
``program_id`` is :func:`optimize`'s:
|
|
1098
|
-
|
|
1085
|
+
``program_id`` is :func:`optimize`'s: the deploy of the file's program carries it,
|
|
1086
|
+
and serve decides whether that deploy needs one.
|
|
1099
1087
|
"""
|
|
1100
1088
|
check_optimizer(optimizer)
|
|
1101
1089
|
config = dict(config or {})
|
|
@@ -1111,7 +1099,7 @@ def optimize_authored(
|
|
|
1111
1099
|
_apply_budget(config, max_metric_calls=max_metric_calls, auto=auto)
|
|
1112
1100
|
|
|
1113
1101
|
cfg = configuration.require_config()
|
|
1114
|
-
program_id =
|
|
1102
|
+
program_id = deployment.declared_program_id(program_id, cfg)
|
|
1115
1103
|
|
|
1116
1104
|
facts = prepared.facts.program
|
|
1117
1105
|
if prepared.program_bundle is not None:
|
|
@@ -19,6 +19,11 @@ from . import _http, configuration
|
|
|
19
19
|
|
|
20
20
|
DEFAULT_ENDPOINT = "https://platform.cmpnd.ai"
|
|
21
21
|
|
|
22
|
+
# Self-host release 2026-001 answers a programs create with 405, because it routes
|
|
23
|
+
# GET requests under /api/v1/programs/ and has no create. Every newer serve accepts
|
|
24
|
+
# the create, so on this one route a 405 means a serve from before program ids.
|
|
25
|
+
UNSUPPORTED_STATUS = 405
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
def create(
|
|
24
29
|
name: str | None = None,
|
|
@@ -61,7 +66,18 @@ def send_create(
|
|
|
61
66
|
body["name"] = name
|
|
62
67
|
if optimized_from_run_id is not None:
|
|
63
68
|
body["optimized_from_run_id"] = optimized_from_run_id
|
|
64
|
-
|
|
69
|
+
try:
|
|
70
|
+
return client.post("/api/v1/programs", json=body)
|
|
71
|
+
except _http.ApiError as exc:
|
|
72
|
+
if exc.status_code == UNSUPPORTED_STATUS:
|
|
73
|
+
raise _http.ApiError(
|
|
74
|
+
"This server does not support program ids. A serve from before program ids, such as "
|
|
75
|
+
"self-host release 2026-001, deploys without one: run `cmpnd deploy` without "
|
|
76
|
+
f"--program-id.\n {exc}",
|
|
77
|
+
exc.exit_code,
|
|
78
|
+
status_code=exc.status_code,
|
|
79
|
+
) from exc
|
|
80
|
+
raise
|
|
65
81
|
|
|
66
82
|
|
|
67
83
|
def _client(api_key: str | None, endpoint: str | None) -> _http.ApiClient:
|
|
@@ -19,10 +19,15 @@ PROGRAM_ID=$(cmpnd programs create --name triage)
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
The command reports what it created on stderr, and `--json` prints the whole
|
|
22
|
-
registry row instead. `cmpnd deploy`
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
registry row instead. `cmpnd deploy` takes the id as `--program-id`, and so
|
|
23
|
+
does `cmpnd optimize` when it deploys the file's program first. With
|
|
24
|
+
`--deployment-id`, the id is optional because that deployment already names its
|
|
25
|
+
program. The CLI checks a given id's shape and leaves the requirement to serve.
|
|
26
|
+
Every serve after self-host release 2026-001 refuses a deploy with no id, and
|
|
27
|
+
the CLI then names `--program-id` and `cmpnd programs create`. Self-host release
|
|
28
|
+
2026-001 has no program ids, so a deploy there omits `--program-id`, and
|
|
29
|
+
`cmpnd programs create` reports that the server does not support them. Each
|
|
30
|
+
call creates a new program. The command never
|
|
26
31
|
retries, because a second send would create a second program.
|
|
27
32
|
`cmpnd.programs.create(name=...)` makes the same call from Python and returns
|
|
28
33
|
the row.
|
|
@@ -757,11 +757,14 @@ the key is left off the wire rather than sent as `null`, so an undeclared trace
|
|
|
757
757
|
it did before ids existed. The finalize body carries no id, because it restates no
|
|
758
758
|
creation field.
|
|
759
759
|
|
|
760
|
-
|
|
761
|
-
`
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
760
|
+
`deploy()` and `deploy_bundle()` take `program_id=` and fall back to the configured id
|
|
761
|
+
(`deployment.declared_program_id`). A given id is checked for shape before anything is
|
|
762
|
+
packaged or sent. With no id the create body leaves the key off, and serve decides.
|
|
763
|
+
Every serve after self-host release 2026-001 refuses that create with a `400`, which the
|
|
764
|
+
SDK raises as `ValueError` naming `cmpnd programs create`. Self-host release 2026-001
|
|
765
|
+
has no program ids and accepts it. `optimize()` and `optimize_authored()` pass the id to
|
|
766
|
+
the deploy when they deploy the program themselves. A reused `deployment_id` already
|
|
767
|
+
names its program, so the id is optional there. Later steps
|
|
765
768
|
optimize a result deployment the platform created and whose program it allocated, so
|
|
766
769
|
the caller's id rides only the steps on the caller's own deployment.
|
|
767
770
|
|
|
@@ -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.
|
|
8
|
+
version = "0.41.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -24,6 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python :: 3.12",
|
|
25
25
|
"Programming Language :: Python :: 3.13",
|
|
26
26
|
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Programming Language :: Python :: 3.15",
|
|
27
28
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
29
|
]
|
|
29
30
|
|
|
@@ -95,6 +96,12 @@ dev = [
|
|
|
95
96
|
"ruff>=0.4.0",
|
|
96
97
|
"basedpyright>=1.39",
|
|
97
98
|
"jsonschema>=4.0",
|
|
99
|
+
# The tests import litellm (tests/e2e/fake_lm.py and the modules that reuse it) and
|
|
100
|
+
# gepa directly. dspy 3.4.1 installs both only below Python 3.15, because both declare
|
|
101
|
+
# requires-python <3.15. uv ignores that upper bound, so the 3.15 test leg still
|
|
102
|
+
# installs them.
|
|
103
|
+
"litellm",
|
|
104
|
+
"gepa",
|
|
98
105
|
# litellm's bedrock/ provider imports boto3 in-process, so the client-side
|
|
99
106
|
# Bedrock stress tests (committee twins) need it in the test env.
|
|
100
107
|
"boto3>=1.34",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install cmpnd with pip's own resolver and check that the installed package
|
|
3
|
+
# imports and instruments DSPy. Called by .github/workflows/ci-sdk.yml (the
|
|
4
|
+
# sdk-test leg for Python 3.15).
|
|
5
|
+
#
|
|
6
|
+
# `pip install cmpnd` is the install PyPI advertises. pip honours the
|
|
7
|
+
# requires-python upper bounds that uv ignores, so this check fails when no
|
|
8
|
+
# dspy release admits the Python version. PYO3_USE_ABI3_FORWARD_COMPATIBILITY
|
|
9
|
+
# is set to 0, so an extension without a wheel for this Python fails the
|
|
10
|
+
# install and does not build from source.
|
|
11
|
+
#
|
|
12
|
+
# Usage: scripts/check-pip-install.sh <python-version> <dspy-version>
|
|
13
|
+
# e.g. scripts/check-pip-install.sh 3.15 3.4.1
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
if [ "$#" -ne 2 ]; then
|
|
17
|
+
echo "usage: $0 <python-version> <dspy-version>" >&2
|
|
18
|
+
exit 2
|
|
19
|
+
fi
|
|
20
|
+
python_version=$1
|
|
21
|
+
dspy_version=$2
|
|
22
|
+
|
|
23
|
+
sdk_dir=$(cd "$(dirname "$0")/.." && pwd)
|
|
24
|
+
venv_dir=$(mktemp -d)
|
|
25
|
+
trap 'rm -rf "$venv_dir"' EXIT
|
|
26
|
+
|
|
27
|
+
uv venv --seed --python "$python_version" "$venv_dir"
|
|
28
|
+
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=0 \
|
|
29
|
+
"$venv_dir/bin/python" -m pip install "$sdk_dir" "dspy==$dspy_version"
|
|
30
|
+
|
|
31
|
+
# -I keeps the checkout off sys.path, so the import reads the installed
|
|
32
|
+
# package and not the source tree.
|
|
33
|
+
cd "$venv_dir"
|
|
34
|
+
"$venv_dir/bin/python" -I -c "import cmpnd; cmpnd.configure(api_key='ci'); cmpnd.auto_instrument(); print('cmpnd', cmpnd.__file__)"
|
|
@@ -639,17 +639,23 @@ class TestDeployHelper:
|
|
|
639
639
|
prepare.assert_called_once()
|
|
640
640
|
assert deploy.call_args.kwargs["program_id"] == "p_0123456789Ab"
|
|
641
641
|
|
|
642
|
-
def
|
|
643
|
-
from cmpnd import authored
|
|
642
|
+
def test_a_deploy_without_a_program_id_sends_none(self, tmp_path, monkeypatch):
|
|
643
|
+
from cmpnd import authored, deployment
|
|
644
644
|
|
|
645
645
|
monkeypatch.delenv("CMPND_PROGRAM_ID", raising=False)
|
|
646
646
|
ns = self._ns(tmp_path, monkeypatch)
|
|
647
|
-
prepare = mock.
|
|
647
|
+
prepare = mock.create_autospec(authored.prepare_file)
|
|
648
|
+
prepare.return_value = mock.Mock(bundle=b"zip", facts=authored.ProgramFacts.minimal())
|
|
648
649
|
monkeypatch.setattr(authored, "prepare_file", prepare)
|
|
650
|
+
deploy = mock.create_autospec(deployment.deploy_bundle)
|
|
651
|
+
deploy.return_value = mock.Mock(deployment_id="dep-1")
|
|
652
|
+
monkeypatch.setattr(deployment, "deploy_bundle", deploy)
|
|
653
|
+
client = ns["client"]
|
|
654
|
+
monkeypatch.setattr(client, "get", lambda _path: {"deployment_id": "dep-1", "status": "ready"})
|
|
655
|
+
|
|
656
|
+
ns["deploy"](str(tmp_path / "program.py"))
|
|
649
657
|
|
|
650
|
-
|
|
651
|
-
ns["deploy"](str(tmp_path / "program.py"))
|
|
652
|
-
prepare.assert_not_called()
|
|
658
|
+
assert deploy.call_args.kwargs["program_id"] is None
|
|
653
659
|
|
|
654
660
|
|
|
655
661
|
class TestReplFallback:
|
|
@@ -758,7 +758,7 @@ def _deploy_args(source: str, program_id: str | None = PROGRAM_ID) -> argparse.N
|
|
|
758
758
|
|
|
759
759
|
|
|
760
760
|
class TestDeployNamesItsProgram:
|
|
761
|
-
"""`cmpnd deploy`
|
|
761
|
+
"""`cmpnd deploy` checks a program id's shape and leaves the requirement to serve."""
|
|
762
762
|
|
|
763
763
|
def _stubs(self, monkeypatch):
|
|
764
764
|
from cmpnd import deployment
|
|
@@ -771,17 +771,27 @@ class TestDeployNamesItsProgram:
|
|
|
771
771
|
monkeypatch.setattr(deployment, "deploy_bundle", deploy)
|
|
772
772
|
return prepare_file, prepare_stdin, deploy
|
|
773
773
|
|
|
774
|
-
def
|
|
775
|
-
prepare_file,
|
|
774
|
+
def test_a_missing_id_deploys_without_one(self, monkeypatch):
|
|
775
|
+
prepare_file, _, deploy = self._stubs(monkeypatch)
|
|
776
|
+
prepare_file.return_value = mock.Mock(bundle=b"zip", facts=authored.ProgramFacts.minimal())
|
|
777
|
+
deploy.return_value = _DeployedStub("d_0123456789ab")
|
|
778
|
+
|
|
779
|
+
top.cmd_deploy(_fake_client(), _deploy_args("program.py", program_id=None))
|
|
780
|
+
|
|
781
|
+
assert deploy.call_args.kwargs["program_id"] is None
|
|
782
|
+
|
|
783
|
+
def test_a_serve_refusal_for_a_missing_id_names_the_flag_and_the_create_command(self, monkeypatch):
|
|
784
|
+
from cmpnd import deployment
|
|
785
|
+
|
|
786
|
+
prepare_file, _, deploy = self._stubs(monkeypatch)
|
|
787
|
+
prepare_file.return_value = mock.Mock(bundle=b"zip", facts=authored.ProgramFacts.minimal())
|
|
788
|
+
deploy.side_effect = ValueError(deployment.MISSING_PROGRAM_ID)
|
|
776
789
|
|
|
777
790
|
with pytest.raises(CliError) as refused:
|
|
778
791
|
top.cmd_deploy(_fake_client(), _deploy_args("program.py", program_id=None))
|
|
779
792
|
|
|
780
793
|
assert "--program-id" in str(refused.value)
|
|
781
794
|
assert "cmpnd programs create" in str(refused.value)
|
|
782
|
-
prepare_file.assert_not_called()
|
|
783
|
-
prepare_stdin.assert_not_called()
|
|
784
|
-
deploy.assert_not_called()
|
|
785
795
|
|
|
786
796
|
def test_a_malformed_id_is_refused_before_staging(self, monkeypatch):
|
|
787
797
|
prepare_file, _, deploy = self._stubs(monkeypatch)
|
|
@@ -808,17 +818,16 @@ class TestDeployNamesItsProgram:
|
|
|
808
818
|
|
|
809
819
|
|
|
810
820
|
class TestOptimizeNamesItsProgram:
|
|
811
|
-
def
|
|
821
|
+
def test_a_file_that_deploys_its_program_without_an_id_sends_none(self, monkeypatch):
|
|
812
822
|
monkeypatch.setattr("sys.stdin", _stdin(_OPTIMIZE_FILE))
|
|
813
823
|
args = _parse_optimize(["optimize", "gepa", "-"])
|
|
814
824
|
with (
|
|
815
|
-
mock.patch("cmpnd.
|
|
816
|
-
mock.patch("cmpnd.
|
|
817
|
-
|
|
825
|
+
mock.patch("cmpnd.configuration.configure", autospec=True),
|
|
826
|
+
mock.patch("cmpnd.authored.prepare_optimize_stdin", autospec=True, return_value=_prepared()),
|
|
827
|
+
mock.patch("cmpnd.optimization.optimize_authored", autospec=True, return_value=_FakeResult()) as m_opt,
|
|
818
828
|
):
|
|
819
829
|
top.cmd_optimize(_fake_client(), args)
|
|
820
|
-
|
|
821
|
-
m_opt.assert_not_called()
|
|
830
|
+
assert m_opt.call_args.kwargs["program_id"] is None
|
|
822
831
|
|
|
823
832
|
def test_the_id_reaches_the_loop(self, monkeypatch):
|
|
824
833
|
monkeypatch.setattr("sys.stdin", _stdin(_OPTIMIZE_FILE))
|