cmpnd 0.60.0__tar.gz → 0.61.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.60.0 → cmpnd-0.61.0}/PKG-INFO +1 -1
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/programs.py +214 -51
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/valsets.py +29 -4
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/cli.md +45 -10
- {cmpnd-0.60.0 → cmpnd-0.61.0}/pyproject.toml +1 -1
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_programs.py +247 -38
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_valsets.py +66 -3
- {cmpnd-0.60.0 → cmpnd-0.61.0}/uv.lock +1 -1
- {cmpnd-0.60.0 → cmpnd-0.61.0}/.gitignore +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/AGENTS.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/CLAUDE.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/README.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/client.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/context.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/models.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/references.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/client.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/docs/sdk-instrumentation.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/README.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/drain.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_callback.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_client.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_context.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_eval.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_execute.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_integration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_models.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_retry.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.60.0 → cmpnd-0.61.0}/tests/test_xxh64.py +0 -0
|
@@ -13,12 +13,12 @@ from .. import api_client, pagination
|
|
|
13
13
|
from . import provider_credentials
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# The
|
|
20
|
-
#
|
|
21
|
-
#
|
|
16
|
+
# `show`, `traces`, `stats` and `usage` take a program name or a p_ id, as the
|
|
17
|
+
# Trash commands do. The `programs optimized` group and `select` take a compiled
|
|
18
|
+
# program hash, the value `programs optimized list` prints as `program_hash`.
|
|
19
|
+
# The routes resolve that hash against optimization runs, so a program
|
|
20
|
+
# signature hash 404s there. A compiled hash and a program name have the same
|
|
21
|
+
# shape, so no command takes both in one slot.
|
|
22
22
|
def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
23
23
|
parser = subparsers.add_parser("programs", help="Create and browse programs")
|
|
24
24
|
sub = parser.add_subparsers(dest="action")
|
|
@@ -83,7 +83,8 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
83
83
|
help="List optimized programs by compiled program hash",
|
|
84
84
|
description=(
|
|
85
85
|
"List the compiled programs your optimization runs produced, one row per compiled program hash, with "
|
|
86
|
-
"their run counts and scores. `programs show`, `traces`, `stats` and `usage` take the hash
|
|
86
|
+
"their run counts and scores. `programs optimized show`, `traces`, `stats` and `usage` take the hash "
|
|
87
|
+
"from the "
|
|
87
88
|
"program_hash column."
|
|
88
89
|
),
|
|
89
90
|
)
|
|
@@ -94,37 +95,54 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
94
95
|
pagination.add_args(opt_list, with_page_aliases=True, default_limit=50)
|
|
95
96
|
opt_list.set_defaults(func=handle_optimized_list)
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
opt_show = optimized_sub.add_parser("show", help="Show the optimization runs that produced a compiled program")
|
|
99
|
+
opt_show.add_argument("hash", help=_COMPILED_HASH_HELP)
|
|
100
|
+
opt_show.set_defaults(func=handle_optimized_show)
|
|
101
|
+
|
|
102
|
+
opt_traces = optimized_sub.add_parser("traces", help="List the traces of a compiled program's program")
|
|
103
|
+
opt_traces.add_argument("hash", help=_COMPILED_HASH_HELP)
|
|
104
|
+
pagination.add_args(opt_traces, with_page_aliases=False, default_limit=50)
|
|
105
|
+
opt_traces.set_defaults(func=handle_optimized_traces)
|
|
106
|
+
|
|
107
|
+
opt_stats = optimized_sub.add_parser("stats", help="Show the execution statistics of a compiled program's program")
|
|
108
|
+
opt_stats.add_argument("hash", help=_COMPILED_HASH_HELP)
|
|
109
|
+
opt_stats.set_defaults(func=handle_optimized_stats)
|
|
110
|
+
|
|
111
|
+
opt_usage = optimized_sub.add_parser("usage", help="Show the usage over time of a compiled program's program")
|
|
112
|
+
opt_usage.add_argument("hash", help=_COMPILED_HASH_HELP)
|
|
113
|
+
_add_usage_window(opt_usage)
|
|
114
|
+
opt_usage.set_defaults(func=handle_optimized_usage)
|
|
115
|
+
|
|
116
|
+
show = sub.add_parser(
|
|
117
|
+
"show",
|
|
118
|
+
help="Show a program",
|
|
119
|
+
description=(
|
|
120
|
+
"Show PROGRAM: its name, p_ id, whether it is in the Trash, its trace count and latest trace, and "
|
|
121
|
+
"the optimized programs its optimization runs produced."
|
|
122
|
+
),
|
|
100
123
|
)
|
|
124
|
+
show.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
101
125
|
show.set_defaults(func=handle_show)
|
|
102
126
|
|
|
103
|
-
tr = sub.add_parser(
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
tr = sub.add_parser(
|
|
128
|
+
"traces",
|
|
129
|
+
help="List a program's traces, newest first, over the widest window trace search reads",
|
|
130
|
+
description=(
|
|
131
|
+
"List PROGRAM's traces, newest first, through trace search over its widest window, 365 days. "
|
|
132
|
+
"`cmpnd traces list --hash ID --since DATE` sets the window itself. --limit is at most 500."
|
|
133
|
+
),
|
|
106
134
|
)
|
|
135
|
+
tr.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
107
136
|
pagination.add_args(tr, with_page_aliases=False, default_limit=50)
|
|
108
137
|
tr.set_defaults(func=handle_traces)
|
|
109
138
|
|
|
110
|
-
stats = sub.add_parser("stats", help="Show a program's execution statistics")
|
|
111
|
-
stats.add_argument(
|
|
112
|
-
"id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
|
|
113
|
-
)
|
|
139
|
+
stats = sub.add_parser("stats", help="Show a program's execution statistics over all history")
|
|
140
|
+
stats.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
114
141
|
stats.set_defaults(func=handle_stats)
|
|
115
142
|
|
|
116
143
|
usage = sub.add_parser("usage", help="Show a program's trace volume, tokens and cost over time")
|
|
117
|
-
usage.add_argument(
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
usage.add_argument("--hours", type=int, default=24, help="Rolling window in hours (default %(default)s)")
|
|
121
|
-
# Constrained on purpose: an unrecognised period buckets hourly server-side
|
|
122
|
-
# rather than failing, so a typo would answer a different question silently.
|
|
123
|
-
usage.add_argument(
|
|
124
|
-
"--period",
|
|
125
|
-
choices=["minute", "5m", "10m", "30m", "hour", "day"],
|
|
126
|
-
help="Bucket width (default: hour)",
|
|
127
|
-
)
|
|
144
|
+
usage.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
145
|
+
_add_usage_window(usage)
|
|
128
146
|
usage.set_defaults(func=handle_usage)
|
|
129
147
|
|
|
130
148
|
# The registry is the programs an SDK client registered, as distinct from
|
|
@@ -173,13 +191,32 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
173
191
|
purge.set_defaults(func=handle_purge)
|
|
174
192
|
|
|
175
193
|
select = sub.add_parser("select", help="Pin an optimization run as this program's chosen version")
|
|
176
|
-
select.add_argument(
|
|
177
|
-
"id", help="Compiled program hash, as `programs optimized list` prints it in the program_hash column"
|
|
178
|
-
)
|
|
194
|
+
select.add_argument("id", help=_COMPILED_HASH_HELP)
|
|
179
195
|
select.add_argument("--run", dest="run_id", required=True, help="Optimization run ID (op_...) to pin")
|
|
180
196
|
select.set_defaults(func=handle_select)
|
|
181
197
|
|
|
182
198
|
|
|
199
|
+
_COMPILED_HASH_HELP = "Compiled program hash, as `programs optimized list` prints it in the program_hash column"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _add_usage_window(parser: ArgumentParser) -> None:
|
|
203
|
+
parser.add_argument("--hours", type=int, default=24, help="Rolling window in hours (default %(default)s)")
|
|
204
|
+
# Constrained on purpose: an unrecognised period buckets hourly server-side
|
|
205
|
+
# rather than failing, so a typo would answer a different question silently.
|
|
206
|
+
parser.add_argument(
|
|
207
|
+
"--period",
|
|
208
|
+
choices=["minute", "5m", "10m", "30m", "hour", "day"],
|
|
209
|
+
help="Bucket width (default: hour)",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _usage_params(args: Any) -> dict[str, str]:
|
|
214
|
+
params = {"hours": str(args.hours)}
|
|
215
|
+
if args.period:
|
|
216
|
+
params["period"] = args.period
|
|
217
|
+
return params
|
|
218
|
+
|
|
219
|
+
|
|
183
220
|
def handle_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
184
221
|
"""Create the program named ``args.name``, and refuse a name that already has one.
|
|
185
222
|
|
|
@@ -246,7 +283,7 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
246
283
|
]:
|
|
247
284
|
if getattr(args, attr, default) != default:
|
|
248
285
|
raise api_client.CliError(f"{flag} does not apply to --trashed, which lists the whole Trash")
|
|
249
|
-
return client
|
|
286
|
+
return _trash_listing(client)
|
|
250
287
|
params = pagination.wire(args, "limit_offset", as_str=True)
|
|
251
288
|
# Read as text: a server from before the program list, such as self-host release 2026-001, redirects this
|
|
252
289
|
# path to its dispatcher, and the redirect's body is HTML.
|
|
@@ -310,24 +347,125 @@ def handle_optimized_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
310
347
|
return client.get("/api/v1/programs/v2", params=params)
|
|
311
348
|
|
|
312
349
|
|
|
350
|
+
# A server from before program names, p_ ids and the Trash API, such as self-host release 2026-001, reads every
|
|
351
|
+
# path under /api/v1/programs/ as a compiled program hash. A GET there answers 404 and a POST answers 405, and its
|
|
352
|
+
# trace search ignores program_id. A current server answers the Trash listing with 200, so that read tells them
|
|
353
|
+
# apart once a command has already failed.
|
|
354
|
+
_OLD_SERVER = "this cmpnd server predates program names, p_ ids and the Trash API, as self-host release 2026-001 does."
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _predates_program_ids(client: api_client.ApiClient) -> bool:
|
|
358
|
+
try:
|
|
359
|
+
client.get("/api/v1/programs/trash")
|
|
360
|
+
except api_client.CliError as exc:
|
|
361
|
+
return exc.status_code == 404
|
|
362
|
+
return False
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _old_server_refusal(remedy: str) -> api_client.CliError:
|
|
366
|
+
return api_client.CliError(f"{_OLD_SERVER} {remedy}")
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _refuse_if_old(client: api_client.ApiClient, exc: api_client.CliError, remedy: str) -> None:
|
|
370
|
+
"""Raise the old-server refusal in place of ``exc`` when the server predates program ids."""
|
|
371
|
+
if exc.status_code in (404, 405) and _predates_program_ids(client):
|
|
372
|
+
raise _old_server_refusal(remedy) from exc
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _trash_remedy() -> str:
|
|
376
|
+
return "Manage its Trash in the web app, at /ui/account/trash, or upgrade the server."
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _read_remedy(verb: str) -> str:
|
|
380
|
+
from . import top
|
|
381
|
+
|
|
382
|
+
return (
|
|
383
|
+
f"`{top.hint_programs('optimized', verb, 'HASH')}` reads a compiled program hash there, and "
|
|
384
|
+
f"`{top.hint_programs('list')}` lists the programs it recorded traces for."
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _resolve_for_read(client: api_client.ApiClient, ref: str, verb: str, *, trashed_ok: bool = False) -> str:
|
|
389
|
+
"""Return the p_ id of the program ``ref`` names, for a read of its activity.
|
|
390
|
+
|
|
391
|
+
A program in the Trash has no activity to read, so it is refused unless
|
|
392
|
+
``trashed_ok``. An unknown name may be a compiled program hash, which the
|
|
393
|
+
``programs optimized`` command of the same verb takes.
|
|
394
|
+
"""
|
|
395
|
+
from . import top
|
|
396
|
+
|
|
397
|
+
program_id, trashed = resolve(
|
|
398
|
+
client,
|
|
399
|
+
ref,
|
|
400
|
+
unknown_hint=f"A compiled program hash goes to `{top.hint_programs('optimized', verb, ref)}`.",
|
|
401
|
+
old_server_remedy=_read_remedy(verb),
|
|
402
|
+
)
|
|
403
|
+
if trashed and not trashed_ok:
|
|
404
|
+
raise api_client.CliError(
|
|
405
|
+
f"{ref} is in the Trash, so it has no activity to read. "
|
|
406
|
+
f"Restore it with `{top.hint_programs('restore', ref)}`."
|
|
407
|
+
)
|
|
408
|
+
return program_id
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _read(client: api_client.ApiClient, verb: str, path: str, params: dict[str, str] | None = None) -> Any:
|
|
412
|
+
try:
|
|
413
|
+
return client.get(path) if params is None else client.get(path, params=params)
|
|
414
|
+
except api_client.CliError as exc:
|
|
415
|
+
_refuse_if_old(client, exc, _read_remedy(verb))
|
|
416
|
+
raise
|
|
417
|
+
|
|
418
|
+
|
|
313
419
|
def handle_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
314
|
-
|
|
420
|
+
program_id = _resolve_for_read(client, args.program, "show", trashed_ok=True)
|
|
421
|
+
return _read(client, "show", f"/api/v1/programs/{program_id}")
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# Trace search reads a page_size above this as its default of 50, which would answer other rows than the ones asked
|
|
425
|
+
# for.
|
|
426
|
+
_MAX_TRACE_PAGE = 500
|
|
315
427
|
|
|
316
428
|
|
|
317
429
|
def handle_traces(client: api_client.ApiClient, args: Any) -> Any:
|
|
318
|
-
|
|
319
|
-
|
|
430
|
+
"""List the program's traces through trace search, which takes a program id, over its widest window."""
|
|
431
|
+
if args.limit > _MAX_TRACE_PAGE:
|
|
432
|
+
raise api_client.CliError(
|
|
433
|
+
f"--limit is at most {_MAX_TRACE_PAGE}, the largest page trace search returns. Page with --offset."
|
|
434
|
+
)
|
|
435
|
+
program_id = _resolve_for_read(client, args.program, "traces")
|
|
436
|
+
body: dict[str, Any] = {"program_id": program_id, **pagination.wire(args, "page_size"), "max_window": True}
|
|
437
|
+
answer = client.post("/api/v1/traces/search", json=body)
|
|
438
|
+
# A server that ignores program_id answers every trace in the org, which must not pass for the program's.
|
|
439
|
+
if "program_id" in (answer.get("ignored_fields") or []):
|
|
440
|
+
raise _old_server_refusal(_read_remedy("traces"))
|
|
441
|
+
return answer
|
|
320
442
|
|
|
321
443
|
|
|
322
444
|
def handle_stats(client: api_client.ApiClient, args: Any) -> Any:
|
|
323
|
-
|
|
445
|
+
program_id = _resolve_for_read(client, args.program, "stats")
|
|
446
|
+
return _read(client, "stats", f"/api/v1/programs/{program_id}/execution-stats")
|
|
324
447
|
|
|
325
448
|
|
|
326
449
|
def handle_usage(client: api_client.ApiClient, args: Any) -> Any:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
450
|
+
program_id = _resolve_for_read(client, args.program, "usage")
|
|
451
|
+
return _read(client, "usage", f"/api/v1/programs/{program_id}/usage-history", _usage_params(args))
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def handle_optimized_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
455
|
+
return client.get(f"/api/v1/programs/{args.hash}")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def handle_optimized_traces(client: api_client.ApiClient, args: Any) -> Any:
|
|
459
|
+
params = pagination.wire(args, "limit_offset", as_str=True)
|
|
460
|
+
return client.get(f"/api/v1/programs/{args.hash}/traces", params=params)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def handle_optimized_stats(client: api_client.ApiClient, args: Any) -> Any:
|
|
464
|
+
return client.get(f"/api/v1/programs/{args.hash}/execution-stats")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def handle_optimized_usage(client: api_client.ApiClient, args: Any) -> Any:
|
|
468
|
+
return client.get(f"/api/v1/programs/{args.hash}/usage-history", params=_usage_params(args))
|
|
331
469
|
|
|
332
470
|
|
|
333
471
|
def handle_registry_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
@@ -351,12 +489,17 @@ def handle_select(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
351
489
|
return client.post(f"/api/v1/programs/{args.id}/select", json={"optimization_run_id": args.run_id})
|
|
352
490
|
|
|
353
491
|
|
|
354
|
-
def
|
|
492
|
+
def resolve(
|
|
493
|
+
client: api_client.ApiClient, ref: str, *, unknown_hint: str = "", old_server_remedy: str = ""
|
|
494
|
+
) -> tuple[str, bool | None]:
|
|
355
495
|
"""Return the program id ``ref`` names, and whether it is in the Trash when known.
|
|
356
496
|
|
|
357
497
|
A ``p_`` id is used as given, because no program name starts with ``p_``, and its
|
|
358
498
|
Trash state is unknown until the route answers. A name is looked up, which never
|
|
359
|
-
makes a program, and the answer says whether it is in the Trash.
|
|
499
|
+
makes a program, and the answer says whether it is in the Trash. ``unknown_hint``
|
|
500
|
+
follows the error for a name no program has. On a server from before program
|
|
501
|
+
names the lookup fails too, and the error is the old-server refusal with
|
|
502
|
+
``old_server_remedy``.
|
|
360
503
|
"""
|
|
361
504
|
from ... import programs
|
|
362
505
|
|
|
@@ -370,7 +513,9 @@ def _resolve(client: api_client.ApiClient, ref: str) -> tuple[str, bool | None]:
|
|
|
370
513
|
answer = client.get("/api/v1/programs/by-name/" + urllib.parse.quote(ref, safe=""))
|
|
371
514
|
except api_client.CliError as exc:
|
|
372
515
|
if exc.status_code == 404:
|
|
373
|
-
|
|
516
|
+
_refuse_if_old(client, exc, old_server_remedy)
|
|
517
|
+
message = f"no program in your organization is named {ref!r}"
|
|
518
|
+
raise api_client.CliError(f"{message}. {unknown_hint}" if unknown_hint else message) from exc
|
|
374
519
|
if exc.status_code == 410:
|
|
375
520
|
raise api_client.CliError(f"{ref!r} belongs to a purged program, which nothing restores") from exc
|
|
376
521
|
raise
|
|
@@ -381,21 +526,21 @@ def handle_trash(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
381
526
|
"""Move a program to the Trash, which frees its place under the plan's program limit."""
|
|
382
527
|
from . import top
|
|
383
528
|
|
|
384
|
-
program_id, trashed =
|
|
529
|
+
program_id, trashed = resolve(client, args.program, old_server_remedy=_trash_remedy())
|
|
385
530
|
if trashed:
|
|
386
531
|
print(
|
|
387
532
|
f"{args.program} is already in the Trash. Restore it with `{top.hint_programs('restore', args.program)}`.",
|
|
388
533
|
file=sys.stderr,
|
|
389
534
|
)
|
|
390
535
|
return {"program_id": program_id, "trashed": True}
|
|
391
|
-
return client
|
|
536
|
+
return _trash_write(client, f"/api/v1/programs/{program_id}/trash")
|
|
392
537
|
|
|
393
538
|
|
|
394
539
|
def handle_restore(client: api_client.ApiClient, args: Any) -> Any:
|
|
395
540
|
"""Return a program from the Trash. The server refuses it when the plan's program limit is full."""
|
|
396
541
|
from . import top
|
|
397
542
|
|
|
398
|
-
program_id, trashed =
|
|
543
|
+
program_id, trashed = resolve(client, args.program, old_server_remedy=_trash_remedy())
|
|
399
544
|
if trashed is False:
|
|
400
545
|
# A name another program took from a Trashed one reaches the new, live program. The Trashed one is
|
|
401
546
|
# restored by its id.
|
|
@@ -403,7 +548,7 @@ def handle_restore(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
403
548
|
f"{args.program} is not in the Trash. If its name now belongs to another program, restore the one "
|
|
404
549
|
f"in the Trash by the p_ id that `{top.hint_programs('list', '--trashed')}` prints."
|
|
405
550
|
)
|
|
406
|
-
return client
|
|
551
|
+
return _trash_write(client, f"/api/v1/programs/{program_id}/restore")
|
|
407
552
|
|
|
408
553
|
|
|
409
554
|
def handle_purge(client: api_client.ApiClient, args: Any) -> Any:
|
|
@@ -417,20 +562,38 @@ def handle_purge(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
417
562
|
# The count is for the person confirming. --force skips the listing, which needs traces:read, so a key
|
|
418
563
|
# that may only write can still empty the Trash.
|
|
419
564
|
if not args.force:
|
|
420
|
-
listed = client
|
|
565
|
+
listed = _trash_listing(client)
|
|
421
566
|
count = len(listed.get("programs") or [])
|
|
422
567
|
if count == 0:
|
|
423
568
|
return {"purged": 0, "kept": 0}
|
|
424
569
|
noun = "program" if count == 1 else "programs"
|
|
425
570
|
provider_credentials.confirm(args, f"the {count} {noun} in the Trash for good", consequence)
|
|
426
|
-
return client
|
|
571
|
+
return _trash_write(client, "/api/v1/programs/trash/empty")
|
|
427
572
|
if args.program is None:
|
|
428
573
|
raise api_client.CliError("name the PROGRAM to purge, or pass --all to empty the Trash")
|
|
429
|
-
program_id, trashed =
|
|
574
|
+
program_id, trashed = resolve(client, args.program, old_server_remedy=_trash_remedy())
|
|
430
575
|
if trashed is False:
|
|
431
576
|
raise api_client.CliError(
|
|
432
577
|
f"{args.program} is live, and only a program in the Trash can be purged. "
|
|
433
578
|
f"Move it there first with `{top.hint_programs('trash', args.program)}`."
|
|
434
579
|
)
|
|
435
580
|
provider_credentials.confirm(args, f"the program {args.program} for good", consequence)
|
|
436
|
-
return client
|
|
581
|
+
return _trash_write(client, f"/api/v1/programs/{program_id}/purge")
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def _trash_listing(client: api_client.ApiClient) -> Any:
|
|
585
|
+
"""GET the Trash. On a server from before the Trash API it answers 404, which is the old-server signal."""
|
|
586
|
+
try:
|
|
587
|
+
return client.get("/api/v1/programs/trash")
|
|
588
|
+
except api_client.CliError as exc:
|
|
589
|
+
if exc.status_code == 404:
|
|
590
|
+
raise _old_server_refusal(_trash_remedy()) from exc
|
|
591
|
+
raise
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _trash_write(client: api_client.ApiClient, path: str) -> Any:
|
|
595
|
+
try:
|
|
596
|
+
return client.post(path)
|
|
597
|
+
except api_client.CliError as exc:
|
|
598
|
+
_refuse_if_old(client, exc, _trash_remedy())
|
|
599
|
+
raise
|
|
@@ -8,6 +8,7 @@ inputs and expected outputs of one validation example.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
+
import re
|
|
11
12
|
from argparse import ArgumentParser, _SubParsersAction
|
|
12
13
|
from typing import Any
|
|
13
14
|
|
|
@@ -23,7 +24,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
23
24
|
create.add_argument(
|
|
24
25
|
"program_hash",
|
|
25
26
|
metavar="program",
|
|
26
|
-
help="Owning program: a program id (p_…), or a legacy signature hash "
|
|
27
|
+
help="Owning program: a program name, a program id (p_…), or a legacy signature hash "
|
|
27
28
|
"(16-hex, as `cmpnd signatures list` shows)",
|
|
28
29
|
)
|
|
29
30
|
create.add_argument(
|
|
@@ -33,7 +34,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
33
34
|
create.set_defaults(func=handle_create)
|
|
34
35
|
|
|
35
36
|
ls = sub.add_parser("list", help="List a program's validation sets")
|
|
36
|
-
ls.add_argument("program", help="Program id (p_…), or a legacy signature hash (16-hex)")
|
|
37
|
+
ls.add_argument("program", help="Program name, program id (p_…), or a legacy signature hash (16-hex)")
|
|
37
38
|
pagination.add_args(ls, with_page_aliases=False, default_limit=50)
|
|
38
39
|
ls.set_defaults(func=handle_list)
|
|
39
40
|
|
|
@@ -75,15 +76,39 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
75
76
|
edelete.set_defaults(func=handle_entries_delete)
|
|
76
77
|
|
|
77
78
|
|
|
79
|
+
# A legacy program signature hash in its display form: 16 hex digits.
|
|
80
|
+
_LEGACY_HASH = re.compile(r"[0-9a-fA-F]{16}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _program(client: api_client.ApiClient, ref: str) -> str:
|
|
84
|
+
"""The value the valsets routes take for ``ref``.
|
|
85
|
+
|
|
86
|
+
A p_ id and a legacy 16-hex hash pass unchanged. Anything else is a program
|
|
87
|
+
name, which resolves to its p_ id without making a program. A program whose
|
|
88
|
+
name is 16 hex digits is reached by its p_ id.
|
|
89
|
+
"""
|
|
90
|
+
from . import programs
|
|
91
|
+
|
|
92
|
+
if _LEGACY_HASH.fullmatch(ref):
|
|
93
|
+
return ref
|
|
94
|
+
program_id, _ = programs.resolve(
|
|
95
|
+
client,
|
|
96
|
+
ref,
|
|
97
|
+
old_server_remedy="Name the program by its 16-hex signature hash, as `cmpnd signatures list` shows, "
|
|
98
|
+
"or upgrade the server.",
|
|
99
|
+
)
|
|
100
|
+
return program_id
|
|
101
|
+
|
|
102
|
+
|
|
78
103
|
def handle_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
79
104
|
# One generator (cmpnd._valsets.default_name) for both the CLI and the
|
|
80
105
|
# Client, so the collision-avoiding suffix can't drift between them.
|
|
81
106
|
name = args.name or _valsets.default_name()
|
|
82
|
-
return client.post("/api/v1/valsets", json={"program": args.program_hash, "name": name})
|
|
107
|
+
return client.post("/api/v1/valsets", json={"program": _program(client, args.program_hash), "name": name})
|
|
83
108
|
|
|
84
109
|
|
|
85
110
|
def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
86
|
-
params = {"program": args.program, **pagination.wire(args, "limit_offset", as_str=True)}
|
|
111
|
+
params = {"program": _program(client, args.program), **pagination.wire(args, "limit_offset", as_str=True)}
|
|
87
112
|
return client.get("/api/v1/valsets", params=params)
|
|
88
113
|
|
|
89
114
|
|
|
@@ -62,6 +62,32 @@ runs produced, one row per compiled program hash, from
|
|
|
62
62
|
`--order`. `cmpnd programs list` refuses those flags with an error that names
|
|
63
63
|
`cmpnd programs optimized list`.
|
|
64
64
|
|
|
65
|
+
`cmpnd programs show`, `traces`, `stats` and `usage` take a program name or a
|
|
66
|
+
`p_` id. The CLI looks a name up with `GET /api/v1/programs/by-name/{name}`,
|
|
67
|
+
which never makes a program. An unknown name fails with an error that names
|
|
68
|
+
the `cmpnd programs optimized` command for a compiled program hash.
|
|
69
|
+
|
|
70
|
+
- `show` prints the program's name and id, whether it is in the Trash, its
|
|
71
|
+
trace count, when its latest trace started, and the optimized programs its
|
|
72
|
+
completed optimization runs produced, newest first. It holds at most 499 of
|
|
73
|
+
them, and `optimized_programs_complete` is false when the program has more.
|
|
74
|
+
- `traces` lists the program's traces, newest first, over the widest window
|
|
75
|
+
trace search reads, 365 days. It sends `POST /api/v1/traces/search` with
|
|
76
|
+
`program_id` and `max_window`. `--limit` is at most 500, the largest page the
|
|
77
|
+
search returns. `cmpnd traces list --hash ID --since DATE` reaches older
|
|
78
|
+
traces.
|
|
79
|
+
- `stats` covers all history, and `usage` covers `--hours`, 24 by default.
|
|
80
|
+
- A program in the Trash has no activity to read, so `traces`, `stats` and
|
|
81
|
+
`usage` refuse it. `show` reports it as Trashed.
|
|
82
|
+
- On a server from before program names, such as self-host release 2026-001,
|
|
83
|
+
each of them fails with an error that names the `cmpnd programs optimized`
|
|
84
|
+
command for a compiled hash. Its trace search ignores `program_id`, so
|
|
85
|
+
`traces` refuses rather than print every trace in the organization.
|
|
86
|
+
|
|
87
|
+
`cmpnd programs optimized show`, `traces`, `stats` and `usage` take a compiled
|
|
88
|
+
program hash, the value in the `program_hash` column of
|
|
89
|
+
`cmpnd programs optimized list`, as `cmpnd programs select` does.
|
|
90
|
+
|
|
65
91
|
## The Trash
|
|
66
92
|
|
|
67
93
|
A program in the Trash leaves every view, and its traces with it, until it is
|
|
@@ -90,6 +116,13 @@ The purge job purges a program 7 days after it entered the Trash. Listing the
|
|
|
90
116
|
Trash needs `traces:read`, and the other commands need `traces:write` or
|
|
91
117
|
`optimizations:write`.
|
|
92
118
|
|
|
119
|
+
A server from before program names, `p_` ids and the Trash API, such as
|
|
120
|
+
self-host release 2026-001, reads every path under `/api/v1/programs/` as a
|
|
121
|
+
compiled program hash. Against one, each Trash command fails with an error that
|
|
122
|
+
says the server predates the Trash API and points at the web app's Trash page,
|
|
123
|
+
`/ui/account/trash`. The CLI recognizes such a server once a command fails, by
|
|
124
|
+
the `404` it answers to `GET /api/v1/programs/trash`.
|
|
125
|
+
|
|
93
126
|
A plan may cap an organization's live programs, the ones outside the Trash
|
|
94
127
|
that no optimization produced. At the cap, a new program name in
|
|
95
128
|
`cmpnd programs create`, `cmpnd deploy` or `cmpnd optimize` and
|
|
@@ -354,12 +387,11 @@ The `signatures` and `programs` groups name programs differently.
|
|
|
354
387
|
hash, which identifies a program's inputs and outputs and everything recorded
|
|
355
388
|
under them: traces, examples, evals and revisions. The CLI passes an id
|
|
356
389
|
through unchanged, and serve tells the two apart by the `p_` prefix.
|
|
357
|
-
`cmpnd programs
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
`--help` names the column its IDs come from.
|
|
390
|
+
`cmpnd programs` commands take a program name or id ("Program names" and "The
|
|
391
|
+
Trash" above). `cmpnd programs optimized` commands and `cmpnd programs select`
|
|
392
|
+
take a compiled program hash, which their routes resolve through optimization
|
|
393
|
+
runs, so they do not find a signature hash. Each group's `--help` names the
|
|
394
|
+
column its IDs come from.
|
|
363
395
|
|
|
364
396
|
`cmpnd deployments openapi <id>` prints the OpenAPI document a deployment
|
|
365
397
|
serves, which an agent reads before it calls the deployment.
|
|
@@ -413,16 +445,19 @@ converted key stays in use, `org list` marks its org active, and
|
|
|
413
445
|
|
|
414
446
|
`cmpnd valsets` manages validation sets that a program owns. A validation set
|
|
415
447
|
is a named set of validation examples whose fields come from the owning
|
|
416
|
-
program's signature. Name the program by its program
|
|
417
|
-
legacy 16-hex signature hash that `cmpnd signatures list` shows.
|
|
418
|
-
|
|
448
|
+
program's signature. Name the program by its program name, its program id
|
|
449
|
+
(`p_…`) or the legacy 16-hex signature hash that `cmpnd signatures list` shows.
|
|
450
|
+
The CLI looks a name up with `GET /api/v1/programs/by-name/{name}`, which never
|
|
451
|
+
makes a program, and sends the id. A value of 16 hex digits is a hash, so a
|
|
452
|
+
program whose name is 16 hex digits is named by its id. On a server from before
|
|
453
|
+
program names, a name fails with an error that asks for the hash. Entries live in an `entries` subgroup, the same
|
|
419
454
|
shape as `cmpnd datasets examples`.
|
|
420
455
|
|
|
421
456
|
The group is in development. The CLI registers it only when
|
|
422
457
|
`CMPND_CLI_DEV=1` is set. Serve still checks each action against the
|
|
423
458
|
`valsets:*` scopes.
|
|
424
459
|
|
|
425
|
-
- `cmpnd valsets create <program
|
|
460
|
+
- `cmpnd valsets create <program> [--name <name>]` creates a validation
|
|
426
461
|
set for a program. The valset ID is its identity, and the name is a label
|
|
427
462
|
that need not be unique. Without `--name`, the CLI assigns a date label
|
|
428
463
|
(`valset-<date>`).
|
|
@@ -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.61.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|