cmpnd 0.66.0__tar.gz → 0.67.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.66.0 → cmpnd-0.67.0}/AGENTS.md +5 -2
- {cmpnd-0.66.0 → cmpnd-0.67.0}/PKG-INFO +7 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/README.md +6 -0
- cmpnd-0.67.0/cmpnd/_deployments.py +105 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/__init__.py +5 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/provider_credentials.py +4 -4
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/tool_servers.py +10 -10
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/top.py +24 -6
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/verbs.py +7 -4
- cmpnd-0.67.0/cmpnd/cli/views.py +25 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/client.py +41 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/deployment.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/cli.md +30 -12
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/client.md +7 -2
- {cmpnd-0.66.0 → cmpnd-0.67.0}/pyproject.toml +5 -5
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/capture_review_run.py +1 -1
- cmpnd-0.67.0/scripts/release-gate.sh +11 -0
- cmpnd-0.67.0/scripts/release-real-models.sh +15 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/seed_review_run.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/committee_task.py +2 -2
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/conftest.py +3 -3
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_deploy_programs.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_loop.py +2 -2
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_review_committee.py +1 -1
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/_provider.py +1 -1
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/mint_bedrock_token.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_bedrock_converse.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_deploy_programs.py +3 -3
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_multi_client_stress.py +3 -3
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_optimize_client_committee.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_optimize_continuation.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_optimize_loop.py +6 -6
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_optimize_loop_wasm.py +6 -6
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_optimize_stress.py +3 -3
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_provider_roles.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_readiness.py +2 -2
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_real_provider_twins.py +3 -3
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_reliability.py +1 -1
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_reliability_classify.py +1 -1
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_rlm_wasm.py +7 -3
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/test_trace_search.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_main.py +9 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_parity.py +78 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_provider_credentials.py +19 -5
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_tool_servers.py +29 -7
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_client.py +59 -0
- cmpnd-0.67.0/tests/test_deployments_read.py +161 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_multi_client_stress_readiness.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_lift.py +1 -1
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_real_provider_twins_readiness.py +1 -1
- cmpnd-0.67.0/tests/test_release_gate_script.py +11 -0
- cmpnd-0.67.0/tests/test_release_real_models_script.py +15 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/uv.lock +1 -1
- cmpnd-0.66.0/cmpnd/_deployments.py +0 -37
- cmpnd-0.66.0/scripts/release-stress.sh +0 -14
- cmpnd-0.66.0/tests/test_deployments_read.py +0 -41
- cmpnd-0.66.0/tests/test_release_stress_script.py +0 -14
- {cmpnd-0.66.0 → cmpnd-0.67.0}/.gitignore +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/CLAUDE.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/context.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/models.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/references.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/docs/sdk-instrumentation.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/README.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/conftest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/drain.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/__init__.py +0 -0
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/_readiness.py +0 -0
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/_reliability.py +0 -0
- {cmpnd-0.66.0/tests/stress → cmpnd-0.67.0/tests/real_models}/_result.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_callback.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_context.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_eval.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_execute.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_integration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_models.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_retry.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.66.0 → cmpnd-0.67.0}/tests/test_xxh64.py +0 -0
|
@@ -92,7 +92,7 @@ knowing:
|
|
|
92
92
|
Run `uv lock` and commit the result. A stale lock makes every `uv` command
|
|
93
93
|
rewrite it.
|
|
94
94
|
- `basedpyright` takes no path argument. A path overrides pyproject's
|
|
95
|
-
`include`, so `basedpyright cmpnd` skips `tests/e2e` and `tests/
|
|
95
|
+
`include`, so `basedpyright cmpnd` skips `tests/e2e` and `tests/real_models` and
|
|
96
96
|
passes changes CI rejects.
|
|
97
97
|
|
|
98
98
|
**This tree's SDK and CLI run against each live self-host release.**
|
|
@@ -234,7 +234,10 @@ first. Its failures name the attribute or class that moved.
|
|
|
234
234
|
|
|
235
235
|
To add a verb to both front-ends, add a `Verb(...)` row to `cli/verbs.py`, add
|
|
236
236
|
its `add_parser` call in `top.register()`, and add the entity method or the
|
|
237
|
-
`_make_namespace` function. The parity test fails until all three agree.
|
|
237
|
+
`_make_namespace` function. The parity test fails until all three agree. A
|
|
238
|
+
verb that repeats a namespaced command sets `shortcut_for`, and its help then
|
|
239
|
+
starts with "Shortcut for `cmpnd deployments list`". The parity test checks
|
|
240
|
+
that the named command exists.
|
|
238
241
|
|
|
239
242
|
When you touch a handler:
|
|
240
243
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.67.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>
|
|
@@ -669,6 +669,12 @@ global config. Use it as a context manager or as a plain object.
|
|
|
669
669
|
its best `DeployedProgram`.
|
|
670
670
|
- `list_traces(**filters)` and `get_trace(trace_id)` read traces with the
|
|
671
671
|
client's credentials. They flush the client's traces first.
|
|
672
|
+
- `list_deployments(status=None, since=None, limit=50, offset=0)` and
|
|
673
|
+
`get_deployment(deployment_id)` read deployment records, as
|
|
674
|
+
`cmpnd deployments list` and `show` do.
|
|
675
|
+
- `get_deployment_source(deployment_id, path=None)` returns the program file a
|
|
676
|
+
deployment stores, as `cmpnd inspect --source` does. A continuation's
|
|
677
|
+
deployment holds the program the continuation runs.
|
|
672
678
|
- `close()` flushes the client's traces and closes the client. A closed
|
|
673
679
|
client raises `RuntimeError` on every method.
|
|
674
680
|
|
|
@@ -618,6 +618,12 @@ global config. Use it as a context manager or as a plain object.
|
|
|
618
618
|
its best `DeployedProgram`.
|
|
619
619
|
- `list_traces(**filters)` and `get_trace(trace_id)` read traces with the
|
|
620
620
|
client's credentials. They flush the client's traces first.
|
|
621
|
+
- `list_deployments(status=None, since=None, limit=50, offset=0)` and
|
|
622
|
+
`get_deployment(deployment_id)` read deployment records, as
|
|
623
|
+
`cmpnd deployments list` and `show` do.
|
|
624
|
+
- `get_deployment_source(deployment_id, path=None)` returns the program file a
|
|
625
|
+
deployment stores, as `cmpnd inspect --source` does. A continuation's
|
|
626
|
+
deployment holds the program the continuation runs.
|
|
621
627
|
- `close()` flushes the client's traces and closes the client. A closed
|
|
622
628
|
client raises `RuntimeError` on every method.
|
|
623
629
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Deployment reads behind ``cmpnd.Client``: the record list, one record, its stored source and its typed metadata."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import tokenize
|
|
7
|
+
from typing import Any, Protocol
|
|
8
|
+
|
|
9
|
+
from . import _http
|
|
10
|
+
from .deployment_metadata import DeploymentMetadataError, DeploymentSignatureResponse
|
|
11
|
+
|
|
12
|
+
MAX_DEPLOYMENTS_PER_PAGE = 500
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _JSONReader(Protocol):
|
|
16
|
+
def get(self, path: str) -> Any: ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_signature(client: _JSONReader, deployment_id: str) -> DeploymentSignatureResponse:
|
|
20
|
+
"""Read one deployment's persisted signature through an existing client."""
|
|
21
|
+
value = client.get(f"/api/v1/deployments/{deployment_id}/signature")
|
|
22
|
+
response = DeploymentSignatureResponse.from_dict(value)
|
|
23
|
+
if response.deployment_id != deployment_id:
|
|
24
|
+
raise DeploymentMetadataError(
|
|
25
|
+
f"response deployment_id {response.deployment_id!r} does not match {deployment_id!r}"
|
|
26
|
+
)
|
|
27
|
+
return response
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_signature(
|
|
31
|
+
deployment_id: str,
|
|
32
|
+
*,
|
|
33
|
+
api_key: str,
|
|
34
|
+
endpoint: str,
|
|
35
|
+
timeout: float | None = None,
|
|
36
|
+
) -> DeploymentSignatureResponse:
|
|
37
|
+
"""Read one deployment's persisted signature with explicit credentials."""
|
|
38
|
+
client = (
|
|
39
|
+
_http.ApiClient(api_key, endpoint) if timeout is None else _http.ApiClient(api_key, endpoint, timeout=timeout)
|
|
40
|
+
)
|
|
41
|
+
return read_signature(client, deployment_id)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def list_deployments(
|
|
45
|
+
*,
|
|
46
|
+
status: str | None = None,
|
|
47
|
+
since: str | None = None,
|
|
48
|
+
limit: int = 50,
|
|
49
|
+
offset: int = 0,
|
|
50
|
+
api_key: str,
|
|
51
|
+
endpoint: str,
|
|
52
|
+
) -> dict[str, Any]:
|
|
53
|
+
"""List the organization's deployments, newest first.
|
|
54
|
+
|
|
55
|
+
Returns ``{"deployments": [...], "has_more": bool, ...}``. ``status`` is one
|
|
56
|
+
of ``pending``, ``parsing``, ``optimizing``, ``ready`` or ``failed``, and
|
|
57
|
+
``since`` is an ISO 8601 timestamp. The route pages by page number, so
|
|
58
|
+
``offset`` must be a whole multiple of ``limit``. The route returns at most
|
|
59
|
+
500 deployments per page, so ``limit`` must not exceed 500.
|
|
60
|
+
"""
|
|
61
|
+
if limit <= 0:
|
|
62
|
+
raise _http.ApiError("limit must be positive")
|
|
63
|
+
if limit > MAX_DEPLOYMENTS_PER_PAGE:
|
|
64
|
+
raise _http.ApiError(
|
|
65
|
+
f"limit must not exceed {MAX_DEPLOYMENTS_PER_PAGE}; the deployments route returns at most "
|
|
66
|
+
f"{MAX_DEPLOYMENTS_PER_PAGE} per page"
|
|
67
|
+
)
|
|
68
|
+
if offset < 0:
|
|
69
|
+
raise _http.ApiError("offset must not be negative")
|
|
70
|
+
if offset % limit != 0:
|
|
71
|
+
lower = (offset // limit) * limit
|
|
72
|
+
raise _http.ApiError(
|
|
73
|
+
f"offset must be a multiple of limit (the deployments route pages by page number); "
|
|
74
|
+
f"try offset {lower} or {lower + limit}"
|
|
75
|
+
)
|
|
76
|
+
params = {"per_page": str(limit), "page": str(offset // limit + 1)}
|
|
77
|
+
if status is not None:
|
|
78
|
+
params["status"] = status
|
|
79
|
+
if since is not None:
|
|
80
|
+
params["since"] = since
|
|
81
|
+
return _http.ApiClient(api_key, endpoint).get("/api/v1/deployments", params=params)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_deployment(deployment_id: str, *, api_key: str, endpoint: str) -> dict[str, Any]:
|
|
85
|
+
"""Read one deployment's record: its status, program, model and metadata."""
|
|
86
|
+
return _http.ApiClient(api_key, endpoint).get(f"/api/v1/deployments/{deployment_id}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_source(deployment_id: str, *, path: str | None = None, api_key: str, endpoint: str) -> str:
|
|
90
|
+
"""Read one file of a deployment's stored source as text.
|
|
91
|
+
|
|
92
|
+
Without ``path`` it reads the program file the stored bundle names. With
|
|
93
|
+
``path`` it reads that archive-relative member. It decodes the file with the
|
|
94
|
+
encoding the file declares in a PEP 263 coding comment, and with UTF-8 when
|
|
95
|
+
the file declares none.
|
|
96
|
+
"""
|
|
97
|
+
client = _http.ApiClient(api_key, endpoint)
|
|
98
|
+
route = f"/api/v1/deployments/{deployment_id}/source"
|
|
99
|
+
if path is None:
|
|
100
|
+
path = client.get(route).get("program")
|
|
101
|
+
if not path:
|
|
102
|
+
raise _http.ApiError(f"the stored source of deployment {deployment_id} names no program file")
|
|
103
|
+
raw = client.get_bytes(route, params={"path": path})
|
|
104
|
+
encoding, _lines = tokenize.detect_encoding(io.BytesIO(raw).readline)
|
|
105
|
+
return raw.decode(encoding)
|
|
@@ -107,7 +107,11 @@ def parse(parser: argparse.ArgumentParser, argv: list[str]) -> argparse.Namespac
|
|
|
107
107
|
def main() -> None:
|
|
108
108
|
parser = tree_help.TreeHelpParser(
|
|
109
109
|
prog="cmpnd",
|
|
110
|
-
description=
|
|
110
|
+
description=(
|
|
111
|
+
"CMPND platform CLI — deploy, run, and inspect DSPy programs.\n\n"
|
|
112
|
+
"The top-level commands are the Docker-style deploy-and-run loop. ps, run and inspect are\n"
|
|
113
|
+
"shortcuts for deployments list, run and show."
|
|
114
|
+
),
|
|
111
115
|
epilog=_EPILOG,
|
|
112
116
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
113
117
|
)
|
|
@@ -5,7 +5,7 @@ platform spend on your behalf when a program of yours runs?
|
|
|
5
5
|
|
|
6
6
|
cmpnd credentials create --name luna-exec --preset lunaroute # a model provider
|
|
7
7
|
cmpnd credentials create --kind mcp --server URL --name n … # an MCP tool server
|
|
8
|
-
cmpnd credentials list
|
|
8
|
+
cmpnd credentials list --text
|
|
9
9
|
cmpnd credentials protocols luna-exec --add system_one
|
|
10
10
|
cmpnd credentials default luna-exec
|
|
11
11
|
cmpnd credentials delete --name luna-exec --force
|
|
@@ -70,7 +70,7 @@ import sys
|
|
|
70
70
|
from argparse import ArgumentParser, _SubParsersAction
|
|
71
71
|
from typing import Any
|
|
72
72
|
|
|
73
|
-
from .. import api_client, deprecation
|
|
73
|
+
from .. import api_client, deprecation, views
|
|
74
74
|
|
|
75
75
|
#: The placements the platform's credentialed egress can compose a credential into.
|
|
76
76
|
#: Closed set, and the same three strings `cmpnd.mcp_tools`'s auth shapes record.
|
|
@@ -156,7 +156,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
156
156
|
deprecation.alias(sub, "add", "create", func=handle_create, configure=_seal_args)
|
|
157
157
|
|
|
158
158
|
listing = sub.add_parser("list", help="Show your org's credentials, their protocols and the default")
|
|
159
|
-
|
|
159
|
+
views.add_text_flag(listing)
|
|
160
160
|
listing.set_defaults(func=handle_list)
|
|
161
161
|
|
|
162
162
|
protocols = sub.add_parser(
|
|
@@ -393,7 +393,7 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
393
393
|
# Metadata only: the platform's projection carries no key material, not even its
|
|
394
394
|
# length, so there is nothing here to redact.
|
|
395
395
|
payload = client.get(_ROOT)
|
|
396
|
-
if getattr(args, "
|
|
396
|
+
if not getattr(args, "text", False) or not isinstance(payload, dict):
|
|
397
397
|
return payload
|
|
398
398
|
credentials = [c for c in payload.get("credentials") or [] if isinstance(c, dict)]
|
|
399
399
|
default = payload.get("default")
|
|
@@ -6,8 +6,8 @@ agreed those declarations may reach. A server here carries the credential it is
|
|
|
6
6
|
with, and the allow-list of tools on it a deploy is permitted to name.
|
|
7
7
|
|
|
8
8
|
cmpnd tool-servers create --url https://mcp.exa.ai/mcp --credential exa-prod
|
|
9
|
-
cmpnd tool-servers list
|
|
10
|
-
cmpnd tool-servers show <server_id>
|
|
9
|
+
cmpnd tool-servers list --text
|
|
10
|
+
cmpnd tool-servers show <server_id> --text
|
|
11
11
|
cmpnd tool-servers allow <server_id> web_search_exa
|
|
12
12
|
cmpnd tool-servers deny <server_id> --all
|
|
13
13
|
cmpnd tool-servers refresh <server_id>
|
|
@@ -48,7 +48,7 @@ import json
|
|
|
48
48
|
from argparse import ArgumentParser, _SubParsersAction
|
|
49
49
|
from typing import Any
|
|
50
50
|
|
|
51
|
-
from .. import api_client
|
|
51
|
+
from .. import api_client, views
|
|
52
52
|
from . import provider_credentials
|
|
53
53
|
|
|
54
54
|
_ROOT = "/api/v1/org/tool-servers"
|
|
@@ -66,7 +66,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
66
66
|
create.set_defaults(func=handle_create)
|
|
67
67
|
|
|
68
68
|
listing = sub.add_parser("list", help="Show the tool servers your org has registered")
|
|
69
|
-
|
|
69
|
+
views.add_text_flag(listing)
|
|
70
70
|
listing.set_defaults(func=handle_list)
|
|
71
71
|
|
|
72
72
|
show = sub.add_parser("show", help="Show one server, its credential, and its tools")
|
|
@@ -74,9 +74,9 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
74
74
|
show.add_argument(
|
|
75
75
|
"--schemas",
|
|
76
76
|
action="store_true",
|
|
77
|
-
help="
|
|
77
|
+
help="With --text, also print each tool's reduced argument schema — the shape the platform will enforce",
|
|
78
78
|
)
|
|
79
|
-
|
|
79
|
+
views.add_text_flag(show)
|
|
80
80
|
show.set_defaults(func=handle_show)
|
|
81
81
|
|
|
82
82
|
for verb, effect in (
|
|
@@ -232,7 +232,7 @@ def handle_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
232
232
|
|
|
233
233
|
def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
234
234
|
payload = client.get(_ROOT)
|
|
235
|
-
if getattr(args, "
|
|
235
|
+
if not getattr(args, "text", False):
|
|
236
236
|
return payload
|
|
237
237
|
servers = _servers(payload)
|
|
238
238
|
if not servers:
|
|
@@ -246,13 +246,13 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
246
246
|
f"{str(server.get('id', '')).ljust(14)} {str(server.get('name') or '').ljust(16)} "
|
|
247
247
|
f"{server.get('url', '')} [{server.get('credential_name') or 'no credential'}]"
|
|
248
248
|
)
|
|
249
|
-
print("Read one server's tools and its allow-list with `cmpnd tool-servers show <server_id
|
|
249
|
+
print("Read one server's tools and its allow-list with `cmpnd tool-servers show <server_id> --text`.")
|
|
250
250
|
return None
|
|
251
251
|
|
|
252
252
|
|
|
253
253
|
def handle_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
254
254
|
server = client.get(f"{_ROOT}/{args.server_id}")
|
|
255
|
-
if getattr(args, "
|
|
255
|
+
if not getattr(args, "text", False):
|
|
256
256
|
return server
|
|
257
257
|
_render_server(server if isinstance(server, dict) else {}, schemas=getattr(args, "schemas", False))
|
|
258
258
|
return None
|
|
@@ -283,7 +283,7 @@ def _grant(client: api_client.ApiClient, args: Any, verb: str) -> Any:
|
|
|
283
283
|
if not getattr(args, "all", False) and not tools:
|
|
284
284
|
raise SystemExit(
|
|
285
285
|
f"`tool-servers {verb}` needs at least one tool name, or --all. Run "
|
|
286
|
-
f"`cmpnd tool-servers show {args.server_id}` to see what this server lists."
|
|
286
|
+
f"`cmpnd tool-servers show {args.server_id} --text` to see what this server lists."
|
|
287
287
|
)
|
|
288
288
|
body: dict[str, Any] = {"all": True} if getattr(args, "all", False) else {"tools": tools}
|
|
289
289
|
server = client.post(f"{_ROOT}/{args.server_id}/{verb}", json=body)
|
|
@@ -1301,10 +1301,22 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
1301
1301
|
def _handler(name: str) -> Any:
|
|
1302
1302
|
return verbs.by_name(name).handler
|
|
1303
1303
|
|
|
1304
|
+
# A verb that repeats a namespaced command says so first, from its VERBS
|
|
1305
|
+
# row, so `cmpnd -h` tells a shortcut from a separate feature.
|
|
1306
|
+
def _help(name: str, text: str = "") -> str:
|
|
1307
|
+
shortcut_for = verbs.by_name(name).shortcut_for
|
|
1308
|
+
if not shortcut_for:
|
|
1309
|
+
return text
|
|
1310
|
+
prefix = f"Shortcut for `cmpnd {shortcut_for}`"
|
|
1311
|
+
return f"{prefix}; {text}" if text else prefix
|
|
1312
|
+
|
|
1304
1313
|
ps = subparsers.add_parser(
|
|
1305
1314
|
"ps",
|
|
1306
|
-
help="
|
|
1307
|
-
description=
|
|
1315
|
+
help=_help("ps", "renders a table"),
|
|
1316
|
+
description=(
|
|
1317
|
+
"List deployments belonging to the calling org, newest first. Shortcut for "
|
|
1318
|
+
"`cmpnd deployments list` that renders a table; --json prints the same response."
|
|
1319
|
+
),
|
|
1308
1320
|
epilog="Examples:\n cmpnd ps\n cmpnd ps --status ready --json\n",
|
|
1309
1321
|
formatter_class=raw,
|
|
1310
1322
|
)
|
|
@@ -1352,8 +1364,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
1352
1364
|
|
|
1353
1365
|
insp = subparsers.add_parser(
|
|
1354
1366
|
"inspect",
|
|
1355
|
-
help="
|
|
1356
|
-
description=
|
|
1367
|
+
help=_help("inspect", "--source prints the stored program"),
|
|
1368
|
+
description=(
|
|
1369
|
+
"Print the full deployment record as JSON, as `cmpnd deployments show` does. "
|
|
1370
|
+
"--source prints the deployment's stored program file instead."
|
|
1371
|
+
),
|
|
1357
1372
|
formatter_class=raw,
|
|
1358
1373
|
)
|
|
1359
1374
|
insp.add_argument("id")
|
|
@@ -1396,8 +1411,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
1396
1411
|
|
|
1397
1412
|
rn = subparsers.add_parser(
|
|
1398
1413
|
"run",
|
|
1399
|
-
help="
|
|
1400
|
-
description=
|
|
1414
|
+
help=_help("run"),
|
|
1415
|
+
description=(
|
|
1416
|
+
"Submit one durable run to a ready deployment and wait for its result. "
|
|
1417
|
+
"Shortcut for `cmpnd deployments run`."
|
|
1418
|
+
),
|
|
1401
1419
|
epilog=(
|
|
1402
1420
|
"Examples:\n"
|
|
1403
1421
|
' cmpnd run d_a1b2c3d4 \'{"question": "what is dspy?"}\'\n'
|
|
@@ -41,7 +41,9 @@ class Verb:
|
|
|
41
41
|
``deploy(path)``); ``"entity"`` binds it as a method on
|
|
42
42
|
``shell_entity`` (e.g. ``deps.d_xxx.status()``). ``shell_method``
|
|
43
43
|
defaults to ``name`` and gives the registry a single hook to rename
|
|
44
|
-
a method without the hint helpers going stale.
|
|
44
|
+
a method without the hint helpers going stale. ``shortcut_for`` names the
|
|
45
|
+
namespaced command a verb repeats, such as ``"deployments list"`` for
|
|
46
|
+
``ps``. Its CLI help starts with "Shortcut for `cmpnd deployments list`".
|
|
45
47
|
"""
|
|
46
48
|
|
|
47
49
|
name: str
|
|
@@ -49,6 +51,7 @@ class Verb:
|
|
|
49
51
|
shell_kind: Literal["top_level", "entity"]
|
|
50
52
|
shell_entity: str | None = None
|
|
51
53
|
shell_method: str | None = None
|
|
54
|
+
shortcut_for: str | None = None
|
|
52
55
|
|
|
53
56
|
def __post_init__(self) -> None:
|
|
54
57
|
if self.shell_kind == "entity" and not self.shell_entity:
|
|
@@ -65,13 +68,13 @@ class Verb:
|
|
|
65
68
|
# functions (`_shell_method`, `register`), never at module scope.
|
|
66
69
|
# Module-scope binding also makes the table trivially inspectable.
|
|
67
70
|
_VERBS: tuple["Verb", ...] = (
|
|
68
|
-
Verb("ps", commands.top.cmd_ps, "top_level"),
|
|
71
|
+
Verb("ps", commands.top.cmd_ps, "top_level", shortcut_for="deployments list"),
|
|
69
72
|
Verb("deploy", commands.top.cmd_deploy, "top_level"),
|
|
70
73
|
Verb("optimize", commands.top.cmd_optimize, "top_level"),
|
|
71
74
|
Verb("status", commands.top.cmd_status, "entity", "Deployment"),
|
|
72
75
|
Verb("logs", commands.top.cmd_logs, "entity", "Deployment"),
|
|
73
|
-
Verb("inspect", commands.top.cmd_inspect, "entity", "Deployment"),
|
|
74
|
-
Verb("run", commands.top.cmd_run, "entity", "Deployment"),
|
|
76
|
+
Verb("inspect", commands.top.cmd_inspect, "entity", "Deployment", shortcut_for="deployments show"),
|
|
77
|
+
Verb("run", commands.top.cmd_run, "entity", "Deployment", shortcut_for="deployments run"),
|
|
75
78
|
Verb("schema", commands.top.cmd_schema, "entity", "Deployment"),
|
|
76
79
|
Verb("sample", commands.top.cmd_sample, "entity", "Deployment"),
|
|
77
80
|
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""The output rule for namespaced commands.
|
|
2
|
+
|
|
3
|
+
A namespaced read, such as `cmpnd traces show` or `cmpnd tool-servers list`, prints
|
|
4
|
+
the platform's JSON by default, and a human view is an opt-in flag: `--tree` and
|
|
5
|
+
`--timeline` on `traces show`, `--text` on the reads below. An action command may
|
|
6
|
+
print a short result instead and take `--json` for the whole answer, as
|
|
7
|
+
`cmpnd programs create` prints the id. The top-level commands (`cmpnd ps`,
|
|
8
|
+
`cmpnd status`) are the human loop: they render text and take `--json`.
|
|
9
|
+
`tests/test_cli/test_parity.py` refuses a namespaced read that offers `--json`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_text_flag(parser: argparse.ArgumentParser) -> None:
|
|
18
|
+
"""Give a namespaced read the opt-in `--text` view.
|
|
19
|
+
|
|
20
|
+
Some reads rendered text by default and took `--json` before the CLI settled on
|
|
21
|
+
the rule, so `--json` stays accepted and hidden, and a script that passes it
|
|
22
|
+
keeps working.
|
|
23
|
+
"""
|
|
24
|
+
parser.add_argument("--text", action="store_true", help="Render for a person instead of printing JSON")
|
|
25
|
+
parser.add_argument("--json", action="store_true", help=argparse.SUPPRESS)
|
|
@@ -836,6 +836,47 @@ class Client:
|
|
|
836
836
|
|
|
837
837
|
return optimization.list_candidates(run_id, cfg=self._config)
|
|
838
838
|
|
|
839
|
+
def list_deployments(
|
|
840
|
+
self, *, status: str | None = None, since: str | None = None, limit: int = 50, offset: int = 0
|
|
841
|
+
) -> list[dict[str, Any]]:
|
|
842
|
+
"""The organization's deployments, newest first.
|
|
843
|
+
|
|
844
|
+
Returns the list itself, not the response envelope. ``status`` is one of
|
|
845
|
+
``pending``, ``parsing``, ``optimizing``, ``ready`` or ``failed``, and
|
|
846
|
+
``since`` is an ISO 8601 timestamp. ``offset`` must be a whole multiple of
|
|
847
|
+
``limit``, because the route pages by page number. ``limit`` must not
|
|
848
|
+
exceed 500, the most deployments the route returns per page.
|
|
849
|
+
"""
|
|
850
|
+
self._check_active()
|
|
851
|
+
page = _deployments.list_deployments(
|
|
852
|
+
status=status,
|
|
853
|
+
since=since,
|
|
854
|
+
limit=limit,
|
|
855
|
+
offset=offset,
|
|
856
|
+
api_key=self._config.api_key,
|
|
857
|
+
endpoint=self._config.endpoint,
|
|
858
|
+
)
|
|
859
|
+
return page.get("deployments", [])
|
|
860
|
+
|
|
861
|
+
def get_deployment(self, deployment_id: str) -> dict[str, Any]:
|
|
862
|
+
"""Fetch one deployment's record: its status, program, model and metadata."""
|
|
863
|
+
self._check_active()
|
|
864
|
+
return _deployments.get_deployment(deployment_id, api_key=self._config.api_key, endpoint=self._config.endpoint)
|
|
865
|
+
|
|
866
|
+
def get_deployment_source(self, deployment_id: str, path: str | None = None) -> str:
|
|
867
|
+
"""Read the program source a deployment stores, as text.
|
|
868
|
+
|
|
869
|
+
Without ``path`` it returns the program file the deployment runs. A
|
|
870
|
+
continuation's deployment holds the chosen candidate's program, so this
|
|
871
|
+
reads what the continuation runs. ``path`` names another archive-relative
|
|
872
|
+
file of the stored bundle. The file decodes with the encoding its PEP 263
|
|
873
|
+
coding comment declares, and with UTF-8 when it declares none.
|
|
874
|
+
"""
|
|
875
|
+
self._check_active()
|
|
876
|
+
return _deployments.get_source(
|
|
877
|
+
deployment_id, path=path, api_key=self._config.api_key, endpoint=self._config.endpoint
|
|
878
|
+
)
|
|
879
|
+
|
|
839
880
|
def get_deployment_signature(self, deployment_id: str) -> DeploymentSignatureResponse:
|
|
840
881
|
"""Fetch one deployment's persisted artifact metadata."""
|
|
841
882
|
self._check_active()
|
|
@@ -345,7 +345,7 @@ def _resolve_mcp_tool_references(client: httpx.Client, config, references: list[
|
|
|
345
345
|
f"your organization's registry holds no tool named {reference.tool_name!r} on "
|
|
346
346
|
f"{reference.server_url}, so this deploy cannot pin the schema its calls would be "
|
|
347
347
|
"validated against. Read what the registry does hold with "
|
|
348
|
-
f"`cmpnd tool-servers show {server_id}`, and if the server has grown the tool since it "
|
|
348
|
+
f"`cmpnd tool-servers show {server_id} --text`, and if the server has grown the tool since it "
|
|
349
349
|
f"was registered, `cmpnd tool-servers refresh {server_id}` re-reads its listing."
|
|
350
350
|
)
|
|
351
351
|
pinned[(reference.server_url, reference.tool_name)] = mcp_servers.PinnedTool(
|
|
@@ -154,11 +154,28 @@ release without the warning, set `CMPND_NO_UPGRADE_WARNING=1`. In code,
|
|
|
154
154
|
`warnings.filterwarnings("ignore", category=cmpnd.UpgradeRequiredWarning)`
|
|
155
155
|
does the same.
|
|
156
156
|
|
|
157
|
+
## Output of namespaced and top-level commands
|
|
158
|
+
|
|
159
|
+
A namespaced read, such as `cmpnd traces show`, `cmpnd deployments list` or
|
|
160
|
+
`cmpnd tool-servers list`, prints the API's JSON by default, so a script can
|
|
161
|
+
pipe it to `jq`. A human view is an opt-in flag: `--tree` and `--timeline` on
|
|
162
|
+
`traces show`, and `--text` on `tool-servers list`, `tool-servers show` and
|
|
163
|
+
`credentials list`. Those three accept `--json` as well, so older scripts keep
|
|
164
|
+
working. An action command may print a short result and take `--json` for the
|
|
165
|
+
whole answer: `cmpnd programs create` prints the new id, and
|
|
166
|
+
`cmpnd optimizations continue` prints the best deployment. The top-level
|
|
167
|
+
commands below are the human loop: they render text and take `--json`.
|
|
168
|
+
`cmpnd/cli/views.py` states the rule, and `tests/test_cli/test_parity.py`
|
|
169
|
+
refuses a namespaced read that offers `--json`.
|
|
170
|
+
|
|
157
171
|
## Deploy flow (Docker-style)
|
|
158
172
|
|
|
159
173
|
These commands print human-readable output by default. Each takes `--json` for
|
|
160
174
|
a stable, scriptable shape. A deployment ID may be any unambiguous prefix,
|
|
161
|
-
such as `d_a1b2c3d4`.
|
|
175
|
+
such as `d_a1b2c3d4`. Three of them are shortcuts, and their help says so:
|
|
176
|
+
`cmpnd ps` repeats `cmpnd deployments list`, `cmpnd run` repeats
|
|
177
|
+
`cmpnd deployments run`, and `cmpnd inspect` repeats `cmpnd deployments show`.
|
|
178
|
+
`Verb.shortcut_for` in `cmpnd/cli/verbs.py` declares each one.
|
|
162
179
|
|
|
163
180
|
- `cmpnd deploy <source.py|-> PROGRAM_NAME [--entrypoint ATTRIBUTE]
|
|
164
181
|
[--source-root DIR] [--include PATH ...] [--run <json>] [--metadata <json>]`
|
|
@@ -560,10 +577,10 @@ cmpnd credentials create --name luna-exec --preset lunaroute < key.txt
|
|
|
560
577
|
op read 'op://vault/openai/key' | cmpnd credentials create --name openai --preset openai
|
|
561
578
|
```
|
|
562
579
|
|
|
563
|
-
`cmpnd credentials list`
|
|
564
|
-
implies them, its address and the
|
|
565
|
-
|
|
566
|
-
|
|
580
|
+
`cmpnd credentials list` prints the listing as the API returns it: each
|
|
581
|
+
credential's protocols, whether its host implies them, its address and the
|
|
582
|
+
org's default. `--text` renders it for a person. A member sees the scheme and
|
|
583
|
+
host of the address, and an owner sees the whole of it.
|
|
567
584
|
|
|
568
585
|
`cmpnd credentials protocols NAME` shows a credential's protocols. `--add` and
|
|
569
586
|
`--remove` start from them, whether stated or implied by the host, and `--set`
|
|
@@ -620,18 +637,19 @@ server, the credential it uses and the allow-list of tools a deploy may name.
|
|
|
620
637
|
|
|
621
638
|
```bash
|
|
622
639
|
cmpnd tool-servers create --url https://mcp.exa.ai/mcp --credential exa-prod [--name exa]
|
|
623
|
-
cmpnd tool-servers list
|
|
624
|
-
cmpnd tool-servers show <server_id> [--schemas]
|
|
640
|
+
cmpnd tool-servers list [--text]
|
|
641
|
+
cmpnd tool-servers show <server_id> [--text [--schemas]]
|
|
625
642
|
cmpnd tool-servers refresh <server_id>
|
|
626
643
|
cmpnd tool-servers delete <server_id> --force
|
|
627
644
|
```
|
|
628
645
|
|
|
629
646
|
`create` registers the server and has the platform list its tools. The
|
|
630
647
|
platform contacts the server, and the CLI never does. `refresh` lists the
|
|
631
|
-
tools again after a vendor changes them. `
|
|
632
|
-
allow-list state and a
|
|
633
|
-
|
|
634
|
-
|
|
648
|
+
tools again after a vendor changes them. `list` and `show` print the API's
|
|
649
|
+
JSON. With `--text`, `show` prints each tool with its allow-list state and a
|
|
650
|
+
one-line description, and `--schemas` adds the reduced argument schema the
|
|
651
|
+
platform enforces. `create`, `allow`, `deny` and `refresh` render the server
|
|
652
|
+
as it now stands, and `--json` prints the raw payload instead.
|
|
635
653
|
|
|
636
654
|
When the platform cannot list a server's tools because of the server itself,
|
|
637
655
|
`create` and `refresh` fail with a `424` that names the URL, the credential and
|
|
@@ -881,7 +899,7 @@ creator after the creator leaves. The customer then:
|
|
|
881
899
|
same name, and `cmpnd credentials create --kind mcp --server URL
|
|
882
900
|
--name NAME` with the same server and name, replace the sealed keys in
|
|
883
901
|
place. The tool server and the deployment keep pointing at them.
|
|
884
|
-
3. Runs `cmpnd tool-servers show SERVER_ID` and
|
|
902
|
+
3. Runs `cmpnd tool-servers show SERVER_ID --text` and
|
|
885
903
|
`cmpnd tool-servers refresh SERVER_ID` to read the tools and allow-list
|
|
886
904
|
under the new key.
|
|
887
905
|
4. Runs `cmpnd ps`, `cmpnd sample ID` and `cmpnd run ID '<json>'`. The
|
|
@@ -211,8 +211,13 @@ methods:
|
|
|
211
211
|
|
|
212
212
|
- Traces: `sdk.list_traces(**filters)` and `sdk.get_trace(trace_id)`. The
|
|
213
213
|
`program=` filter takes a program name or a `p_` id.
|
|
214
|
-
- Deployments: `sdk.
|
|
215
|
-
|
|
214
|
+
- Deployments: `sdk.list_deployments(status=..., since=..., limit=...,
|
|
215
|
+
offset=...)` lists the organization's deployments, newest first.
|
|
216
|
+
`sdk.get_deployment(deployment_id)` returns one deployment's record.
|
|
217
|
+
`sdk.get_deployment_source(deployment_id, path=None)` returns the program
|
|
218
|
+
file the deployment stores, or the archive-relative file `path` names.
|
|
219
|
+
`sdk.get_deployment_signature(deployment_id)` returns the selected
|
|
220
|
+
artifact's typed metadata. It reads no worker status or executions.
|
|
216
221
|
- Assessments, in development behind `CMPND_CLI_DEV=1`:
|
|
217
222
|
`sdk.create_assessment(trace_id, conclusion=..., body=...,
|
|
218
223
|
annotations=...)`, `sdk.list_assessments(program=... | trace_id=...,
|
|
@@ -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.67.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -261,7 +261,7 @@ select = ["E", "F", "I", "W"]
|
|
|
261
261
|
# attributes cmpnd reads. We co-maintain dspy, so the stubs stay in
|
|
262
262
|
# sync with the real package by hand. See stubs/dspy/__init__.pyi.
|
|
263
263
|
[tool.basedpyright]
|
|
264
|
-
# `cmpnd` is the SDK source (strict bar). `tests/
|
|
264
|
+
# `cmpnd` is the SDK source (strict bar). `tests/real_models` and all of `tests/e2e`
|
|
265
265
|
# are checked too, with relaxed rules in the executionEnvironments below — so a
|
|
266
266
|
# stale `cmpnd.*` reference in a test (PR #163 shipped a
|
|
267
267
|
# `cmpnd.identity.program_hash_for_sig` that crashed at runtime; see CMP-154)
|
|
@@ -275,7 +275,7 @@ select = ["E", "F", "I", "W"]
|
|
|
275
275
|
# the exporter's private counters and monkeypatch its methods, so a rename in the
|
|
276
276
|
# transport turns a rig into a lie rather than an error. It typechecks clean with
|
|
277
277
|
# no relaxations, so it gets no executionEnvironment of its own.
|
|
278
|
-
include = ["cmpnd", "ingest/cmpnd_ingest", "examples", "tests/
|
|
278
|
+
include = ["cmpnd", "ingest/cmpnd_ingest", "examples", "tests/real_models", "tests/e2e", "tests/load"]
|
|
279
279
|
stubPath = "stubs"
|
|
280
280
|
pythonVersion = "3.11"
|
|
281
281
|
reportAny = false
|
|
@@ -304,9 +304,9 @@ root = "examples"
|
|
|
304
304
|
reportArgumentType = false
|
|
305
305
|
|
|
306
306
|
# Test files inherit every rule above and relax a few more. These overrides
|
|
307
|
-
# apply only to files under tests/
|
|
307
|
+
# apply only to files under tests/real_models; `cmpnd` source keeps the strict bar.
|
|
308
308
|
[[tool.basedpyright.executionEnvironments]]
|
|
309
|
-
root = "tests/
|
|
309
|
+
root = "tests/real_models"
|
|
310
310
|
# DSPy dynamic-API noise: e.g. dspy.Predict("q -> a") is read as a `callbacks`
|
|
311
311
|
# argument. These two rules are what make a full tests/ scope unviable
|
|
312
312
|
# (~850 false positives); reportAttributeAccessIssue stays on.
|
|
@@ -6,7 +6,7 @@ review app. The fixture is a list of `{"kind", "payload"}` items — one per SDK
|
|
|
6
6
|
exporter HTTP call — captured by hooking `cmpnd.exporter_http._send_json` (the single
|
|
7
7
|
chokepoint every export POST routes through) while running the exact committee
|
|
8
8
|
GEPA optimization the stress suite runs
|
|
9
|
-
(`tests/
|
|
9
|
+
(`tests/real_models/test_optimize_client_committee.py` → `tests/e2e/committee_task`):
|
|
10
10
|
a weak Bedrock student + a Bedrock reflection LM over the vendored committee
|
|
11
11
|
subset. Capturing a *real* run (rather than fabricating payloads) keeps the seed
|
|
12
12
|
faithful — real prompts, token counts, span trees, and now the GEPA optimizer
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CI dependency: .github/workflows/release-staging.yml and release-prod.yml.
|
|
3
|
+
#
|
|
4
|
+
# The release gate that calls no model: it ingests one trace into the
|
|
5
|
+
# just-released stack and searches for it. A provider outage cannot fail it, so
|
|
6
|
+
# it can block a merge.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
uv run --extra dev python -m pytest \
|
|
10
|
+
tests/real_models/test_trace_search.py \
|
|
11
|
+
-v -rs
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CI dependency: .github/workflows/release-staging.yml and release-prod.yml.
|
|
3
|
+
#
|
|
4
|
+
# The real-model suite: tests that call real providers against a released stack.
|
|
5
|
+
# It blocks no merge. The release gate (release-gate.sh) runs test_trace_search.py,
|
|
6
|
+
# which calls no model, so this script leaves it out.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
uv run --extra dev python -m pytest \
|
|
10
|
+
tests/real_models/ \
|
|
11
|
+
--ignore=tests/real_models/test_trace_search.py \
|
|
12
|
+
-m "forall_workers or worker_independent" \
|
|
13
|
+
-v -rs \
|
|
14
|
+
--reruns 2 --reruns-delay 120 \
|
|
15
|
+
--only-rerun "RetryableProviderError"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""Replay a captured real optimization run into a review app each deploy.
|
|
3
3
|
|
|
4
4
|
A live GEPA against a review app (real third-party inference) is the stress suite's
|
|
5
|
-
job (tests/
|
|
5
|
+
job (tests/real_models/test_optimize_client_committee.py). For per-deploy review data we
|
|
6
6
|
instead replay a **captured real run** (real emails, token counts, span trees,
|
|
7
7
|
datasets) from `fixtures/review_run.json.gz` — the exact SDK export payloads of one
|
|
8
8
|
real run — so every review app is born with a realistic optimization + its traces
|