cmpnd 0.63.0__tar.gz → 0.64.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.63.0 → cmpnd-0.64.0}/PKG-INFO +40 -46
- {cmpnd-0.63.0 → cmpnd-0.64.0}/README.md +39 -45
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_assessments.py +6 -5
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_traces.py +11 -14
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_valsets.py +9 -7
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/callback.py +25 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/__init__.py +0 -2
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/assessments.py +11 -2
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/evals.py +1 -1
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/programs.py +59 -16
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/top.py +19 -46
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/traces.py +8 -14
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/valsets.py +6 -25
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/verbs.py +5 -5
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/client.py +32 -9
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/programs.py +22 -1
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/cli.md +59 -52
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/client.md +18 -10
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/reading-traces.md +2 -3
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/sdk-instrumentation.md +16 -3
- {cmpnd-0.63.0 → cmpnd-0.64.0}/pyproject.toml +1 -1
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_traces_read.py +1 -1
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_valsets_cli.py +11 -10
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_assessments_api.py +15 -5
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_assessments.py +34 -7
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_evals.py +1 -1
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_parity.py +0 -4
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_programs.py +90 -4
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_schema_sample.py +12 -8
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_shell.py +7 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_top.py +77 -7
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_traces.py +0 -4
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_traces_program_and_module.py +4 -5
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_traces_search_wire.py +28 -16
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_valsets.py +18 -14
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_client.py +12 -2
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_client_program_names.py +142 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_traces_read.py +0 -2
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_valsets_api.py +34 -8
- {cmpnd-0.63.0 → cmpnd-0.64.0}/uv.lock +1 -1
- cmpnd-0.63.0/cmpnd/cli/commands/signatures.py +0 -164
- cmpnd-0.63.0/tests/test_cli/test_signatures.py +0 -225
- {cmpnd-0.63.0 → cmpnd-0.64.0}/.gitignore +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/AGENTS.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/CLAUDE.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/context.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/models.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/references.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/README.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/drain.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_bedrock_converse.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_callback.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_context.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_eval.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_execute.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_integration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_models.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_retry.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.63.0 → cmpnd-0.64.0}/tests/test_xxh64.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.64.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>
|
|
@@ -51,9 +51,9 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
|
|
52
52
|
# cmpnd
|
|
53
53
|
|
|
54
|
-
The `cmpnd` package traces
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
The `cmpnd` package traces, deploys and optimizes DSPy programs on the cmpnd
|
|
55
|
+
platform. You give a program a name, and a client records its traces,
|
|
56
|
+
deployments and optimization runs under that name.
|
|
57
57
|
|
|
58
58
|
## Installation
|
|
59
59
|
|
|
@@ -66,46 +66,15 @@ uv add cmpnd # or: pip install cmpnd
|
|
|
66
66
|
The `cmpnd` library works with the cmpnd service at
|
|
67
67
|
[platform.cmpnd.ai](https://platform.cmpnd.ai). Create an API key on the
|
|
68
68
|
platform under Settings → API keys, and pass it as `api_key=` to
|
|
69
|
-
`cmpnd.
|
|
69
|
+
`cmpnd.Client()` or `cmpnd.configure()`. For a self-hosted cmpnd install, also
|
|
70
70
|
pass the install's URL as `endpoint=`.
|
|
71
71
|
|
|
72
72
|
## Quick start
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```python
|
|
79
|
-
import cmpnd
|
|
80
|
-
import dspy
|
|
81
|
-
|
|
82
|
-
cmpnd.configure(api_key="ck_xxx", project_tags="my-project")
|
|
83
|
-
cmpnd.auto_instrument()
|
|
84
|
-
|
|
85
|
-
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
86
|
-
|
|
87
|
-
cot = dspy.ChainOfThought("question -> answer")
|
|
88
|
-
result = cot(question="What is DSPy?") # the trace goes to cmpnd
|
|
89
|
-
|
|
90
|
-
cmpnd.trace_id(result) # "a1b2c3d4-...", or None outside instrumentation
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
`cmpnd.trace_id()` links a result to its platform trace, for example to attach
|
|
94
|
-
feedback later. Traces recorded this way have no program name. The platform
|
|
95
|
-
groups them by the structure of the program's modules. The API reference
|
|
96
|
-
below lists the other options of `cmpnd.configure()`.
|
|
97
|
-
|
|
98
|
-
## Program names
|
|
99
|
-
|
|
100
|
-
When you’re ready to name programs, evolve them, and share them with your team, switch
|
|
101
|
-
to `cmpnd.Client`. A client replaces `configure()` and `auto_instrument()` and offers
|
|
102
|
-
more features. A client records only the programs you run through it, and each of
|
|
103
|
-
those programs has a program name. A client takes most of the same arguments as `configure()`.
|
|
104
|
-
|
|
105
|
-
A program name is the name your organization gives a program, such as `question-answerer`.
|
|
106
|
-
Traces, deployments and optimization runs under one program name go to one
|
|
107
|
-
program. The platform makes the program the first time your organization uses
|
|
108
|
-
the program name.
|
|
74
|
+
A program name is the name your organization gives a program, such as
|
|
75
|
+
`question-answerer`. Traces, deployments and optimization runs under one
|
|
76
|
+
program name go to one program. The platform makes the program the first time
|
|
77
|
+
your organization uses the program name.
|
|
109
78
|
|
|
110
79
|
`@cmpnd.program(name)` above a `dspy.Module` class gives the class a program
|
|
111
80
|
name. Building the class takes its own arguments and a required `lm=`. The
|
|
@@ -128,17 +97,23 @@ class QA(dspy.Predict):
|
|
|
128
97
|
qa1 = QA(lm=dspy.LM("openai/gpt-6-luna"))
|
|
129
98
|
```
|
|
130
99
|
|
|
131
|
-
A
|
|
100
|
+
A `cmpnd.Client` runs the program and records its trace under the program
|
|
101
|
+
name:
|
|
132
102
|
|
|
133
103
|
```python
|
|
134
104
|
gepa = dspy.GEPA(metric=metric, reflection_lm=dspy.LM("openai/gpt-6-luna"), auto="light")
|
|
135
105
|
|
|
136
106
|
with cmpnd.Client(api_key="ck_xxx", project_tags="my-project") as sdk:
|
|
137
|
-
# the trace
|
|
107
|
+
# the trace goes to the program "question-answerer"
|
|
138
108
|
result = sdk.run_program(qa1, question="What is DSPy?")
|
|
139
109
|
qa2 = sdk.optimize_program(qa1, gepa, trainset=train, valset=val)
|
|
110
|
+
|
|
111
|
+
cmpnd.trace_id(result) # "a1b2c3d4-...", the trace of this run
|
|
140
112
|
```
|
|
141
113
|
|
|
114
|
+
`cmpnd.trace_id()` links a result to its platform trace, for example to attach
|
|
115
|
+
feedback later. A client records only the programs you run through it.
|
|
116
|
+
|
|
142
117
|
A client is active from construction. The `with` form flushes the client's
|
|
143
118
|
traces and closes it on exit. Without `with`, the client stays active until
|
|
144
119
|
you call `close()` or the process exits, and its traces go out at exit:
|
|
@@ -167,8 +142,26 @@ qa3 = cmpnd.Program(
|
|
|
167
142
|
- `optimize_program` runs the optimizer in your process and returns a copy of
|
|
168
143
|
the program that holds the optimized module. The copy keeps the program name
|
|
169
144
|
and the LM.
|
|
170
|
-
|
|
171
|
-
|
|
145
|
+
|
|
146
|
+
## Sketching with `auto_instrument()`
|
|
147
|
+
|
|
148
|
+
`cmpnd.auto_instrument()` records a trace of every DSPy call in the process,
|
|
149
|
+
with no other change to your code. Use it to sketch a
|
|
150
|
+
[DSPy program](https://dspy.ai/) and see what it does:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
cmpnd.configure(api_key="ck_xxx", project_tags="my-project")
|
|
154
|
+
cmpnd.auto_instrument()
|
|
155
|
+
|
|
156
|
+
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
157
|
+
dspy.ChainOfThought("question -> answer")(question="What is DSPy?")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
These traces carry no program, so the platform files them under Unattributed.
|
|
161
|
+
The first one in a process logs a warning that points to `@cmpnd.program` and
|
|
162
|
+
`cmpnd.Client`. Calling a `cmpnd.Program` directly while `auto_instrument()` is
|
|
163
|
+
on raises a `TypeError` that points to the client. The API reference below
|
|
164
|
+
lists the options of `cmpnd.configure()`.
|
|
172
165
|
|
|
173
166
|
## Deployed programs
|
|
174
167
|
|
|
@@ -531,7 +524,7 @@ describes one:
|
|
|
531
524
|
|
|
532
525
|
```
|
|
533
526
|
usage: cmpnd [-h] [--version] [--api-key API_KEY] [--endpoint ENDPOINT]
|
|
534
|
-
{auth,login,logout,org,credentials,tool-servers,optimizations,programs,
|
|
527
|
+
{auth,login,logout,org,credentials,tool-servers,optimizations,programs,traces,evals,datasets,deployments,ps,status,logs,inspect,schema,sample,run,deploy,optimize}
|
|
535
528
|
...
|
|
536
529
|
|
|
537
530
|
CMPND platform CLI — deploy, run, and inspect DSPy programs.
|
|
@@ -625,7 +618,8 @@ captured input and output size limits, 65,536 bytes each by default.
|
|
|
625
618
|
|
|
626
619
|
### `cmpnd.auto_instrument()`
|
|
627
620
|
|
|
628
|
-
Registers the callback with DSPy, so every DSPy call records a trace.
|
|
621
|
+
Registers the callback with DSPy, so every DSPy call records a trace. The
|
|
622
|
+
traces carry no program and land in Unattributed.
|
|
629
623
|
|
|
630
624
|
### `cmpnd.Client()`
|
|
631
625
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# cmpnd
|
|
2
2
|
|
|
3
|
-
The `cmpnd` package traces
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The `cmpnd` package traces, deploys and optimizes DSPy programs on the cmpnd
|
|
4
|
+
platform. You give a program a name, and a client records its traces,
|
|
5
|
+
deployments and optimization runs under that name.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -15,46 +15,15 @@ uv add cmpnd # or: pip install cmpnd
|
|
|
15
15
|
The `cmpnd` library works with the cmpnd service at
|
|
16
16
|
[platform.cmpnd.ai](https://platform.cmpnd.ai). Create an API key on the
|
|
17
17
|
platform under Settings → API keys, and pass it as `api_key=` to
|
|
18
|
-
`cmpnd.
|
|
18
|
+
`cmpnd.Client()` or `cmpnd.configure()`. For a self-hosted cmpnd install, also
|
|
19
19
|
pass the install's URL as `endpoint=`.
|
|
20
20
|
|
|
21
21
|
## Quick start
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
import cmpnd
|
|
29
|
-
import dspy
|
|
30
|
-
|
|
31
|
-
cmpnd.configure(api_key="ck_xxx", project_tags="my-project")
|
|
32
|
-
cmpnd.auto_instrument()
|
|
33
|
-
|
|
34
|
-
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
35
|
-
|
|
36
|
-
cot = dspy.ChainOfThought("question -> answer")
|
|
37
|
-
result = cot(question="What is DSPy?") # the trace goes to cmpnd
|
|
38
|
-
|
|
39
|
-
cmpnd.trace_id(result) # "a1b2c3d4-...", or None outside instrumentation
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
`cmpnd.trace_id()` links a result to its platform trace, for example to attach
|
|
43
|
-
feedback later. Traces recorded this way have no program name. The platform
|
|
44
|
-
groups them by the structure of the program's modules. The API reference
|
|
45
|
-
below lists the other options of `cmpnd.configure()`.
|
|
46
|
-
|
|
47
|
-
## Program names
|
|
48
|
-
|
|
49
|
-
When you’re ready to name programs, evolve them, and share them with your team, switch
|
|
50
|
-
to `cmpnd.Client`. A client replaces `configure()` and `auto_instrument()` and offers
|
|
51
|
-
more features. A client records only the programs you run through it, and each of
|
|
52
|
-
those programs has a program name. A client takes most of the same arguments as `configure()`.
|
|
53
|
-
|
|
54
|
-
A program name is the name your organization gives a program, such as `question-answerer`.
|
|
55
|
-
Traces, deployments and optimization runs under one program name go to one
|
|
56
|
-
program. The platform makes the program the first time your organization uses
|
|
57
|
-
the program name.
|
|
23
|
+
A program name is the name your organization gives a program, such as
|
|
24
|
+
`question-answerer`. Traces, deployments and optimization runs under one
|
|
25
|
+
program name go to one program. The platform makes the program the first time
|
|
26
|
+
your organization uses the program name.
|
|
58
27
|
|
|
59
28
|
`@cmpnd.program(name)` above a `dspy.Module` class gives the class a program
|
|
60
29
|
name. Building the class takes its own arguments and a required `lm=`. The
|
|
@@ -77,17 +46,23 @@ class QA(dspy.Predict):
|
|
|
77
46
|
qa1 = QA(lm=dspy.LM("openai/gpt-6-luna"))
|
|
78
47
|
```
|
|
79
48
|
|
|
80
|
-
A
|
|
49
|
+
A `cmpnd.Client` runs the program and records its trace under the program
|
|
50
|
+
name:
|
|
81
51
|
|
|
82
52
|
```python
|
|
83
53
|
gepa = dspy.GEPA(metric=metric, reflection_lm=dspy.LM("openai/gpt-6-luna"), auto="light")
|
|
84
54
|
|
|
85
55
|
with cmpnd.Client(api_key="ck_xxx", project_tags="my-project") as sdk:
|
|
86
|
-
# the trace
|
|
56
|
+
# the trace goes to the program "question-answerer"
|
|
87
57
|
result = sdk.run_program(qa1, question="What is DSPy?")
|
|
88
58
|
qa2 = sdk.optimize_program(qa1, gepa, trainset=train, valset=val)
|
|
59
|
+
|
|
60
|
+
cmpnd.trace_id(result) # "a1b2c3d4-...", the trace of this run
|
|
89
61
|
```
|
|
90
62
|
|
|
63
|
+
`cmpnd.trace_id()` links a result to its platform trace, for example to attach
|
|
64
|
+
feedback later. A client records only the programs you run through it.
|
|
65
|
+
|
|
91
66
|
A client is active from construction. The `with` form flushes the client's
|
|
92
67
|
traces and closes it on exit. Without `with`, the client stays active until
|
|
93
68
|
you call `close()` or the process exits, and its traces go out at exit:
|
|
@@ -116,8 +91,26 @@ qa3 = cmpnd.Program(
|
|
|
116
91
|
- `optimize_program` runs the optimizer in your process and returns a copy of
|
|
117
92
|
the program that holds the optimized module. The copy keeps the program name
|
|
118
93
|
and the LM.
|
|
119
|
-
|
|
120
|
-
|
|
94
|
+
|
|
95
|
+
## Sketching with `auto_instrument()`
|
|
96
|
+
|
|
97
|
+
`cmpnd.auto_instrument()` records a trace of every DSPy call in the process,
|
|
98
|
+
with no other change to your code. Use it to sketch a
|
|
99
|
+
[DSPy program](https://dspy.ai/) and see what it does:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
cmpnd.configure(api_key="ck_xxx", project_tags="my-project")
|
|
103
|
+
cmpnd.auto_instrument()
|
|
104
|
+
|
|
105
|
+
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
106
|
+
dspy.ChainOfThought("question -> answer")(question="What is DSPy?")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
These traces carry no program, so the platform files them under Unattributed.
|
|
110
|
+
The first one in a process logs a warning that points to `@cmpnd.program` and
|
|
111
|
+
`cmpnd.Client`. Calling a `cmpnd.Program` directly while `auto_instrument()` is
|
|
112
|
+
on raises a `TypeError` that points to the client. The API reference below
|
|
113
|
+
lists the options of `cmpnd.configure()`.
|
|
121
114
|
|
|
122
115
|
## Deployed programs
|
|
123
116
|
|
|
@@ -480,7 +473,7 @@ describes one:
|
|
|
480
473
|
|
|
481
474
|
```
|
|
482
475
|
usage: cmpnd [-h] [--version] [--api-key API_KEY] [--endpoint ENDPOINT]
|
|
483
|
-
{auth,login,logout,org,credentials,tool-servers,optimizations,programs,
|
|
476
|
+
{auth,login,logout,org,credentials,tool-servers,optimizations,programs,traces,evals,datasets,deployments,ps,status,logs,inspect,schema,sample,run,deploy,optimize}
|
|
484
477
|
...
|
|
485
478
|
|
|
486
479
|
CMPND platform CLI — deploy, run, and inspect DSPy programs.
|
|
@@ -574,7 +567,8 @@ captured input and output size limits, 65,536 bytes each by default.
|
|
|
574
567
|
|
|
575
568
|
### `cmpnd.auto_instrument()`
|
|
576
569
|
|
|
577
|
-
Registers the callback with DSPy, so every DSPy call records a trace.
|
|
570
|
+
Registers the callback with DSPy, so every DSPy call records a trace. The
|
|
571
|
+
traces carry no program and land in Unattributed.
|
|
578
572
|
|
|
579
573
|
### `cmpnd.Client()`
|
|
580
574
|
|
|
@@ -13,7 +13,7 @@ import os
|
|
|
13
13
|
from collections.abc import Sequence
|
|
14
14
|
from typing import Any, Literal
|
|
15
15
|
|
|
16
|
-
from . import _http, configuration
|
|
16
|
+
from . import _http, configuration, programs
|
|
17
17
|
|
|
18
18
|
DEFAULT_ENDPOINT = "https://platform.cmpnd.ai"
|
|
19
19
|
|
|
@@ -58,22 +58,23 @@ def list(
|
|
|
58
58
|
) -> dict[str, Any]:
|
|
59
59
|
"""List assessments by program or by trace, newest first.
|
|
60
60
|
|
|
61
|
-
Pass exactly one of ``program`` (every assessment of that program
|
|
62
|
-
``trace_id`` (only that trace's assessments). Returns
|
|
61
|
+
Pass exactly one of ``program`` (every assessment of that program, named by its
|
|
62
|
+
name or ``p_`` id) or ``trace_id`` (only that trace's assessments). Returns
|
|
63
63
|
``{"assessments": [...], "total": N, "limit", "offset", "has_more"}``.
|
|
64
64
|
``limit`` (max rows) and ``offset`` (skip this many) page the full set — the
|
|
65
65
|
server clamps ``limit`` to at most 200.
|
|
66
66
|
"""
|
|
67
67
|
if program is not None and trace_id is not None:
|
|
68
68
|
raise ValueError("pass exactly one of program or trace_id")
|
|
69
|
+
client = _client(api_key, endpoint)
|
|
69
70
|
if trace_id is not None:
|
|
70
71
|
scope: dict[str, str] = {"trace_id": trace_id}
|
|
71
72
|
elif program is not None:
|
|
72
|
-
scope = {"program": program}
|
|
73
|
+
scope = {"program": programs.program_id_for(client, program)}
|
|
73
74
|
else:
|
|
74
75
|
raise ValueError("pass exactly one of program or trace_id")
|
|
75
76
|
params = {**scope, "limit": str(limit), "offset": str(offset)}
|
|
76
|
-
return
|
|
77
|
+
return client.get("/api/v1/assessments", params=params)
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
def get(assessment_id: str, *, api_key: str | None = None, endpoint: str | None = None) -> dict[str, Any]:
|
|
@@ -4,7 +4,7 @@ The implementation of ``list`` and ``show`` over the shared ``_http`` transport.
|
|
|
4
4
|
:class:`cmpnd.Client`'s ``list_traces`` / ``get_trace`` call these directly; the
|
|
5
5
|
deprecated :mod:`cmpnd.traces` shim wraps them with a warning. The filter and
|
|
6
6
|
verb names match the ``cmpnd traces`` CLI vocabulary — ``list`` filtered by
|
|
7
|
-
``query`` / ``program`` / ``module`` / ``
|
|
7
|
+
``query`` / ``program`` / ``module`` / ``deployment`` / ``since`` / ``until`` /
|
|
8
8
|
``all_history``, and ``show`` for one trace — so the surfaces do not drift.
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -29,7 +29,6 @@ def list(
|
|
|
29
29
|
model: str | None = None,
|
|
30
30
|
program: str | None = None,
|
|
31
31
|
module: str | None = None,
|
|
32
|
-
hash: str | None = None,
|
|
33
32
|
deployment: str | None = None,
|
|
34
33
|
metadata: dict[str, str] | None = None,
|
|
35
34
|
tags: list[str] | None = None,
|
|
@@ -49,11 +48,10 @@ def list(
|
|
|
49
48
|
``ignored_fields`` naming any request field an older server did not understand.
|
|
50
49
|
|
|
51
50
|
The filters mirror ``cmpnd traces list``: ``query`` searches indexed span text;
|
|
52
|
-
``program`` names the program by name
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
``since``;
|
|
51
|
+
``program`` names the program by its name or its program id (``p_…``);
|
|
52
|
+
``module`` is the root module's class, matched exactly. No index serves the
|
|
53
|
+
class, so without ``program`` it reads every trace in the time range and slows
|
|
54
|
+
as the range grows. Pair it with a program or a shorter ``since``;
|
|
57
55
|
``deployment`` is a deployment id; ``metadata`` is matched by containment (every
|
|
58
56
|
pair must be present); ``tags`` matches any of the given tags. ``since`` /
|
|
59
57
|
``until`` accept an ISO 8601 timestamp, a bare date (``YYYY-MM-DD``), or a
|
|
@@ -71,15 +69,14 @@ def list(
|
|
|
71
69
|
("status", status),
|
|
72
70
|
("q", query),
|
|
73
71
|
("model", model),
|
|
74
|
-
("program_name", program),
|
|
75
72
|
("module_class", module),
|
|
76
73
|
("deployment_id", deployment),
|
|
77
74
|
("tags", tags),
|
|
78
75
|
):
|
|
79
76
|
if val is not None:
|
|
80
77
|
body[wire_key] = val
|
|
81
|
-
if
|
|
82
|
-
body.update(program_filter(
|
|
78
|
+
if program is not None:
|
|
79
|
+
body.update(program_filter(program))
|
|
83
80
|
if metadata is not None:
|
|
84
81
|
body["metadata"] = dict(metadata)
|
|
85
82
|
if since is not None:
|
|
@@ -92,14 +89,14 @@ def list(
|
|
|
92
89
|
def program_filter(value: str) -> dict[str, str]:
|
|
93
90
|
"""The search-body key for one program filter value.
|
|
94
91
|
|
|
95
|
-
A program id (``p_…``) goes in ``program_id`` and
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
A program id (``p_…``) goes in ``program_id`` and anything else is a program
|
|
93
|
+
name, since no name starts with ``p_``. One helper for the CLI and the SDK,
|
|
94
|
+
so the two cannot split.
|
|
98
95
|
"""
|
|
99
96
|
text = value.strip()
|
|
100
97
|
if identity.is_program_id(text):
|
|
101
98
|
return {"program_id": text}
|
|
102
|
-
return {"
|
|
99
|
+
return {"program_name": value}
|
|
103
100
|
|
|
104
101
|
|
|
105
102
|
def show(trace_id: str, *, api_key: str | None = None, endpoint: str | None = None) -> dict[str, Any]:
|
|
@@ -15,7 +15,7 @@ import datetime
|
|
|
15
15
|
import os
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
|
-
from . import _http, configuration
|
|
18
|
+
from . import _http, configuration, programs
|
|
19
19
|
|
|
20
20
|
DEFAULT_ENDPOINT = "https://platform.cmpnd.ai"
|
|
21
21
|
|
|
@@ -30,22 +30,24 @@ def default_name() -> str:
|
|
|
30
30
|
def create(
|
|
31
31
|
program: str, *, name: str | None = None, api_key: str | None = None, endpoint: str | None = None
|
|
32
32
|
) -> dict[str, Any]:
|
|
33
|
-
"""Create a
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
"""Create a valset owned by ``program``, a program name or ``p_`` id. ``name`` is auto-assigned when omitted."""
|
|
34
|
+
client = _client(api_key, endpoint)
|
|
35
|
+
payload = {"program": programs.program_id_for(client, program), "name": name or default_name()}
|
|
36
|
+
return client.post("/api/v1/valsets", json=payload)
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def list(
|
|
39
40
|
program: str, *, limit: int = 50, offset: int = 0, api_key: str | None = None, endpoint: str | None = None
|
|
40
41
|
) -> dict[str, Any]:
|
|
41
|
-
"""List a program
|
|
42
|
+
"""List the valsets of ``program``, a program name or ``p_`` id, newest first.
|
|
42
43
|
|
|
43
44
|
Returns ``{"valsets": [...], "total": N, "limit", "offset", "has_more"}``.
|
|
44
45
|
``limit`` / ``offset`` page the full set — the server clamps ``limit`` to at
|
|
45
46
|
most 200.
|
|
46
47
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
client = _client(api_key, endpoint)
|
|
49
|
+
params = {"program": programs.program_id_for(client, program), "limit": str(limit), "offset": str(offset)}
|
|
50
|
+
return client.get("/api/v1/valsets", params=params)
|
|
49
51
|
|
|
50
52
|
|
|
51
53
|
def get(valset_id: str, *, api_key: str | None = None, endpoint: str | None = None) -> dict[str, Any]:
|
|
@@ -39,6 +39,27 @@ def _declared_program_name() -> str | None:
|
|
|
39
39
|
return name if isinstance(name, str) else None
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
# Set once the process has warned that a trace names no program, so the warning
|
|
43
|
+
# appears once per process, whichever callback records the trace.
|
|
44
|
+
_warned_unattributed = False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _warn_once_if_unattributed(trace: models.TraceBuilder) -> None:
|
|
48
|
+
"""Warn the first time a trace in this process names neither a program name nor a program id.
|
|
49
|
+
|
|
50
|
+
Such a trace belongs to no program, and the platform files it under Unattributed.
|
|
51
|
+
"""
|
|
52
|
+
global _warned_unattributed
|
|
53
|
+
if _warned_unattributed or trace.declared_name is not None or trace.program_id is not None:
|
|
54
|
+
return
|
|
55
|
+
_warned_unattributed = True
|
|
56
|
+
logger.warning(
|
|
57
|
+
"cmpnd: this trace names no program, so the platform files it under Unattributed. Give the program a "
|
|
58
|
+
"name with @cmpnd.program(name) and run it through cmpnd.Client, and its traces go to that program. "
|
|
59
|
+
"This warning appears once per process."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
42
63
|
def trace_id(prediction: Any) -> str | None:
|
|
43
64
|
"""Return the cmpnd trace id for a prediction returned by an instrumented program.
|
|
44
65
|
|
|
@@ -1193,6 +1214,10 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1193
1214
|
module_class=instance.__class__.__name__,
|
|
1194
1215
|
)
|
|
1195
1216
|
self._stamp(trace)
|
|
1217
|
+
# A root module call is where a program runs, so it is where an unnamed one
|
|
1218
|
+
# is caught. GEPA's own LM calls (_emit_optimization_lm_trace) belong to the
|
|
1219
|
+
# optimization run, which names the program, so they never warn.
|
|
1220
|
+
_warn_once_if_unattributed(trace)
|
|
1196
1221
|
|
|
1197
1222
|
# Attach the run ids this trace starts under, from this callback's own
|
|
1198
1223
|
# state right now — not at finalize (on_module_end), where the
|
|
@@ -24,7 +24,6 @@ from .commands import (
|
|
|
24
24
|
org,
|
|
25
25
|
programs,
|
|
26
26
|
provider_credentials,
|
|
27
|
-
signatures,
|
|
28
27
|
tool_servers,
|
|
29
28
|
top,
|
|
30
29
|
traces,
|
|
@@ -164,7 +163,6 @@ def main() -> None:
|
|
|
164
163
|
# gates each read on scope, so a caller without it gets a 403, not a
|
|
165
164
|
# hidden command.
|
|
166
165
|
programs.register(subparsers)
|
|
167
|
-
signatures.register(subparsers)
|
|
168
166
|
traces.register(subparsers)
|
|
169
167
|
evals.register(subparsers)
|
|
170
168
|
datasets.register(subparsers)
|
|
@@ -56,7 +56,8 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
56
56
|
scope.add_argument(
|
|
57
57
|
"program",
|
|
58
58
|
nargs="?",
|
|
59
|
-
|
|
59
|
+
metavar="PROGRAM",
|
|
60
|
+
help="Program name or p_ id",
|
|
60
61
|
)
|
|
61
62
|
scope.add_argument("--trace", dest="trace_id", metavar="TRACE_ID", help="A single trace's assessments")
|
|
62
63
|
pagination.add_args(ls, with_page_aliases=False, default_limit=50)
|
|
@@ -88,7 +89,15 @@ def handle_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
91
|
-
|
|
92
|
+
if args.trace_id:
|
|
93
|
+
scope = {"trace_id": args.trace_id}
|
|
94
|
+
else:
|
|
95
|
+
from . import programs
|
|
96
|
+
|
|
97
|
+
program_id, _ = programs.resolve(
|
|
98
|
+
client, args.program, old_server_remedy="Upgrade the server to name a program."
|
|
99
|
+
)
|
|
100
|
+
scope = {"program": program_id}
|
|
92
101
|
params = {**scope, **pagination.wire(args, "limit_offset", as_str=True)}
|
|
93
102
|
return client.get("/api/v1/assessments", params=params)
|
|
94
103
|
|
|
@@ -50,7 +50,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
50
50
|
# `evals compare` is deliberately absent. The route requires a
|
|
51
51
|
# `dataset_hash`, and no eval read returns one: neither the list nor the
|
|
52
52
|
# detail projection carries it, and nor does the compare response itself.
|
|
53
|
-
# The only surface that emits one is `
|
|
53
|
+
# The only surface that emits one is `programs evals`, so the command
|
|
54
54
|
# would ship with a required argument its own noun cannot produce. It goes
|
|
55
55
|
# in once the projection carries the hash.
|
|
56
56
|
|
|
@@ -13,8 +13,8 @@ from .. import api_client, pagination
|
|
|
13
13
|
from . import provider_credentials
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
# `show`, `traces`, `stats` and `
|
|
17
|
-
# Trash commands do. The `programs optimized` group and `select` take a compiled
|
|
16
|
+
# `show`, `traces`, `stats`, `usage`, `examples`, `evals` and `history` take a
|
|
17
|
+
# program name or a p_ id, as the Trash commands do. The `programs optimized` group and `select` take a compiled
|
|
18
18
|
# program hash, the value `programs optimized list` prints as `program_hash`.
|
|
19
19
|
# The routes resolve that hash against optimization runs, so a program
|
|
20
20
|
# signature hash 404s there. A compiled hash and a program name have the same
|
|
@@ -129,7 +129,7 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
129
129
|
help="List a program's traces, newest first, over the widest window trace search reads",
|
|
130
130
|
description=(
|
|
131
131
|
"List PROGRAM's traces, newest first, through trace search over its widest window, 365 days. "
|
|
132
|
-
"`cmpnd traces list --
|
|
132
|
+
"`cmpnd traces list --program PROGRAM --since DATE` sets the window itself. --limit is at most 500."
|
|
133
133
|
),
|
|
134
134
|
)
|
|
135
135
|
tr.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
@@ -145,6 +145,23 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
145
145
|
_add_usage_window(usage)
|
|
146
146
|
usage.set_defaults(func=handle_usage)
|
|
147
147
|
|
|
148
|
+
examples = sub.add_parser("examples", help="List a program's recorded input/output examples, newest first")
|
|
149
|
+
examples.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
150
|
+
pagination.add_args(examples, with_page_aliases=False, default_limit=25)
|
|
151
|
+
examples.set_defaults(func=handle_examples)
|
|
152
|
+
|
|
153
|
+
evals = sub.add_parser("evals", help="List the eval runs that touched a program, newest first")
|
|
154
|
+
evals.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
155
|
+
# This route caps rows and has no offset of any spelling, so it gets a bare
|
|
156
|
+
# --limit rather than the shared pagination pair: an --offset the server
|
|
157
|
+
# drops would read as a page the caller never received.
|
|
158
|
+
evals.add_argument("--limit", type=int, default=10, help="Max eval runs, newest first (default %(default)s)")
|
|
159
|
+
evals.set_defaults(func=handle_evals)
|
|
160
|
+
|
|
161
|
+
history = sub.add_parser("history", help="Show a program's distinct prompt instructions over time")
|
|
162
|
+
history.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
|
|
163
|
+
history.set_defaults(func=handle_history)
|
|
164
|
+
|
|
148
165
|
# The registry is the programs an SDK client registered, as distinct from
|
|
149
166
|
# the optimized programs `programs list` aggregates out of runs — different
|
|
150
167
|
# records, so they nest rather than share a flat verb.
|
|
@@ -310,15 +327,20 @@ _RECORDED_PROGRAMS_CAP = 200
|
|
|
310
327
|
def _list_recorded(client: api_client.ApiClient, params: dict[str, str]) -> dict[str, Any]:
|
|
311
328
|
"""List the programs a server from before the program list recorded traces for, paged here.
|
|
312
329
|
|
|
313
|
-
GET /api/v1/modules answers them in one array,
|
|
314
|
-
|
|
315
|
-
|
|
330
|
+
GET /api/v1/modules answers them in one array, up to its cap. A program with no traces yet is not in it. A
|
|
331
|
+
full array may be missing programs past the cap, so the answer then has no total and says it is incomplete.
|
|
332
|
+
Each row keeps what the server recorded except its signature hash, which names no program and which no
|
|
333
|
+
command takes.
|
|
316
334
|
"""
|
|
317
|
-
|
|
318
|
-
if not isinstance(
|
|
335
|
+
answered = client.get("/api/v1/modules")
|
|
336
|
+
if not isinstance(answered, list):
|
|
319
337
|
raise api_client.CliError(
|
|
320
338
|
"this cmpnd server answered neither the program list nor the list of recorded programs"
|
|
321
339
|
)
|
|
340
|
+
rows = [
|
|
341
|
+
{key: value for key, value in row.items() if key != "program_signature_hash"} if isinstance(row, dict) else row
|
|
342
|
+
for row in answered
|
|
343
|
+
]
|
|
322
344
|
complete = len(rows) < _RECORDED_PROGRAMS_CAP
|
|
323
345
|
note = "note: this cmpnd server predates the program list, so this lists the programs it recorded traces for."
|
|
324
346
|
if not complete:
|
|
@@ -376,9 +398,15 @@ def _trash_remedy() -> str:
|
|
|
376
398
|
return "Manage its Trash in the web app, at /ui/account/trash, or upgrade the server."
|
|
377
399
|
|
|
378
400
|
|
|
401
|
+
# The reads that have a `programs optimized` counterpart taking a compiled program hash.
|
|
402
|
+
_OPTIMIZED_VERBS = frozenset({"show", "traces", "stats", "usage"})
|
|
403
|
+
|
|
404
|
+
|
|
379
405
|
def _read_remedy(verb: str) -> str:
|
|
380
406
|
from . import top
|
|
381
407
|
|
|
408
|
+
if verb not in _OPTIMIZED_VERBS:
|
|
409
|
+
return f"Upgrade the server to read a program's {verb} by its name or p_ id."
|
|
382
410
|
return (
|
|
383
411
|
f"`{top.hint_programs('optimized', verb, 'HASH')}` reads a compiled program hash there, and "
|
|
384
412
|
f"`{top.hint_programs('list')}` lists the programs it recorded traces for."
|
|
@@ -389,17 +417,15 @@ def _resolve_for_read(client: api_client.ApiClient, ref: str, verb: str, *, tras
|
|
|
389
417
|
"""Return the p_ id of the program ``ref`` names, for a read of its activity.
|
|
390
418
|
|
|
391
419
|
A program in the Trash has no activity to read, so it is refused unless
|
|
392
|
-
``trashed_ok``.
|
|
393
|
-
|
|
420
|
+
``trashed_ok``. For a verb with a ``programs optimized`` counterpart, an
|
|
421
|
+
unknown name may be a compiled program hash, which that command takes.
|
|
394
422
|
"""
|
|
395
423
|
from . import top
|
|
396
424
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
ref
|
|
400
|
-
|
|
401
|
-
old_server_remedy=_read_remedy(verb),
|
|
402
|
-
)
|
|
425
|
+
unknown_hint = ""
|
|
426
|
+
if verb in _OPTIMIZED_VERBS:
|
|
427
|
+
unknown_hint = f"A compiled program hash goes to `{top.hint_programs('optimized', verb, ref)}`."
|
|
428
|
+
program_id, trashed = resolve(client, ref, unknown_hint=unknown_hint, old_server_remedy=_read_remedy(verb))
|
|
403
429
|
if trashed and not trashed_ok:
|
|
404
430
|
raise api_client.CliError(
|
|
405
431
|
f"{ref} is in the Trash, so it has no activity to read. "
|
|
@@ -451,6 +477,23 @@ def handle_usage(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
451
477
|
return _read(client, "usage", f"/api/v1/programs/{program_id}/usage-history", _usage_params(args))
|
|
452
478
|
|
|
453
479
|
|
|
480
|
+
# The per-program record reads live under /api/v1/signatures/{program}, which takes the program's p_ id.
|
|
481
|
+
def handle_examples(client: api_client.ApiClient, args: Any) -> Any:
|
|
482
|
+
params = pagination.wire(args, "per_page", as_str=True)
|
|
483
|
+
program_id = _resolve_for_read(client, args.program, "examples")
|
|
484
|
+
return _read(client, "examples", f"/api/v1/signatures/{program_id}/examples", params)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def handle_evals(client: api_client.ApiClient, args: Any) -> Any:
|
|
488
|
+
program_id = _resolve_for_read(client, args.program, "evals")
|
|
489
|
+
return _read(client, "evals", f"/api/v1/signatures/{program_id}/evals", {"limit": str(args.limit)})
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def handle_history(client: api_client.ApiClient, args: Any) -> Any:
|
|
493
|
+
program_id = _resolve_for_read(client, args.program, "history")
|
|
494
|
+
return _read(client, "history", f"/api/v1/signatures/{program_id}/history")
|
|
495
|
+
|
|
496
|
+
|
|
454
497
|
def handle_optimized_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
455
498
|
return client.get(f"/api/v1/programs/{args.hash}")
|
|
456
499
|
|