cmpnd 0.42.2__tar.gz → 0.43.1__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.42.2 → cmpnd-0.43.1}/PKG-INFO +2 -2
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/__init__.py +3 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/callback.py +39 -3
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/client.py +61 -10
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/programs.py +64 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/client.md +37 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/sdk-instrumentation.md +7 -1
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/exporter_http.py +16 -2
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/program_names.py +5 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/pyproject.toml +6 -11
- cmpnd-0.43.1/tests/test_client_program_names.py +339 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/uv.lock +36 -23
- {cmpnd-0.42.2 → cmpnd-0.43.1}/.gitignore +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/AGENTS.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/CLAUDE.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/README.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_http.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/authored.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/configuration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/context.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/datasets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/decorators.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/deployment.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/execution.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/exporter.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/helpers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/imaging.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/models.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimization.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/packaging.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/references.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/retry.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/cmpnd/traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/cli.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/custom-proposer.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/docs/reading-traces.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/examples/local_ollama.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/README.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/ingest/pyproject.toml +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/release-stress.sh +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_fake_clock.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/_traced_run.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/__main__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/drain.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/ingest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/sweep.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_health.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/_provider.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/_result.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_authored.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_callback.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_client.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_configuration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_context.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_datasets.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_decorators.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deploy.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_eval.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_eval_start.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_execute.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_exporter.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_imaging.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_import_weight.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_integration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_models.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_packaging.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_poll_until.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_program_identity.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_programs_create.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_project_payload.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_retry.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_schemas.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_span_references.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_trace_render.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_traces_read.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_truncation.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.42.2 → cmpnd-0.43.1}/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.43.1
|
|
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>
|
|
@@ -20,7 +20,6 @@ Requires-Python: >=3.11
|
|
|
20
20
|
Requires-Dist: dspy>=3.1.3
|
|
21
21
|
Requires-Dist: httpx>=0.27.0
|
|
22
22
|
Requires-Dist: ipython>=8.20
|
|
23
|
-
Requires-Dist: openai<2
|
|
24
23
|
Requires-Dist: pydantic>=2.0
|
|
25
24
|
Requires-Dist: typing-extensions>=4.1.0
|
|
26
25
|
Requires-Dist: xxhash>=3.0.0
|
|
@@ -31,6 +30,7 @@ Requires-Dist: gepa; extra == 'dev'
|
|
|
31
30
|
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
32
31
|
Requires-Dist: jsonschema>=4.0; extra == 'dev'
|
|
33
32
|
Requires-Dist: litellm; extra == 'dev'
|
|
33
|
+
Requires-Dist: litellm<1.102; (python_version >= '3.15') and extra == 'dev'
|
|
34
34
|
Requires-Dist: numpy>=2.3; extra == 'dev'
|
|
35
35
|
Requires-Dist: pandas>=2.2; extra == 'dev'
|
|
36
36
|
Requires-Dist: pillow>=10.0; extra == 'dev'
|
|
@@ -60,6 +60,7 @@ from .optimization import (
|
|
|
60
60
|
OptimizeFailed,
|
|
61
61
|
optimize,
|
|
62
62
|
)
|
|
63
|
+
from .programs import Program
|
|
63
64
|
|
|
64
65
|
try:
|
|
65
66
|
__version__ = importlib.metadata.version("cmpnd")
|
|
@@ -100,6 +101,8 @@ __all__ = [
|
|
|
100
101
|
"traces",
|
|
101
102
|
# Program ids (cmpnd.programs.create)
|
|
102
103
|
"programs",
|
|
104
|
+
# A program declared by name
|
|
105
|
+
"Program",
|
|
103
106
|
"authored",
|
|
104
107
|
"ApiError",
|
|
105
108
|
# Types
|
|
@@ -24,6 +24,21 @@ _current_interpreter_tool_frame: contextvars.ContextVar[dict[str, Any] | None] =
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
# The DSPy settings key that carries the name of the program a client is running.
|
|
28
|
+
# ``cmpnd.Client`` sets it with ``dspy.context`` around a call, so it reaches the
|
|
29
|
+
# threads DSPy starts for evaluation and parallel work, and the callback stamps it on
|
|
30
|
+
# the trace it creates for the call. None means the call declares no program.
|
|
31
|
+
DECLARED_PROGRAM_SETTING = "cmpnd_declared_program"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _declared_program_name() -> str | None:
|
|
35
|
+
"""The program name the current call declares, read from DSPy's settings."""
|
|
36
|
+
import dspy
|
|
37
|
+
|
|
38
|
+
name = dspy.settings.get(DECLARED_PROGRAM_SETTING)
|
|
39
|
+
return name if isinstance(name, str) else None
|
|
40
|
+
|
|
41
|
+
|
|
27
42
|
def trace_id(prediction: Any) -> str | None:
|
|
28
43
|
"""Return the cmpnd trace id for a prediction returned by an instrumented program.
|
|
29
44
|
|
|
@@ -502,6 +517,9 @@ class _CallbackBase:
|
|
|
502
517
|
# later cmpnd.configure(). That fixedness is what keeps a client
|
|
503
518
|
# orthogonal to the global config.
|
|
504
519
|
self._own_config = own_config
|
|
520
|
+
# Set once this callback has warned that a declared name replaced its
|
|
521
|
+
# configured program id, so the warning appears once per client.
|
|
522
|
+
self._warned_name_over_id = False
|
|
505
523
|
|
|
506
524
|
# Resolve feature flags: explicit param > config > default
|
|
507
525
|
config = own_config or configuration.get_config()
|
|
@@ -652,12 +670,30 @@ class _CallbackBase:
|
|
|
652
670
|
return configuration._current_program_id()
|
|
653
671
|
|
|
654
672
|
def _stamp(self, builder: Any) -> Any:
|
|
655
|
-
"""Stamp this callback's project tags, and on a trace its program
|
|
673
|
+
"""Stamp this callback's project tags, and on a trace its program, onto a
|
|
656
674
|
builder it just created. A span's program is its trace's, so a span carries
|
|
657
|
-
no
|
|
675
|
+
no program of its own.
|
|
676
|
+
|
|
677
|
+
A trace from a call that declares a program name carries the name and no id,
|
|
678
|
+
and the exporter resolves the name. The declared name wins over a program id
|
|
679
|
+
the client was configured with, and the callback warns once that it did.
|
|
680
|
+
"""
|
|
658
681
|
builder.project_tags = self._resolve_project_tags()
|
|
659
682
|
if isinstance(builder, models.TraceBuilder):
|
|
660
|
-
|
|
683
|
+
program_id = self._resolve_program_id()
|
|
684
|
+
name = _declared_program_name()
|
|
685
|
+
if name is not None:
|
|
686
|
+
if program_id is not None and not self._warned_name_over_id:
|
|
687
|
+
self._warned_name_over_id = True
|
|
688
|
+
logger.warning(
|
|
689
|
+
"cmpnd: traces of program %r go to the program with that name, not to the configured "
|
|
690
|
+
"program id %s.",
|
|
691
|
+
name,
|
|
692
|
+
program_id,
|
|
693
|
+
)
|
|
694
|
+
builder.declared_name = name
|
|
695
|
+
program_id = None
|
|
696
|
+
builder.program_id = program_id
|
|
661
697
|
return builder
|
|
662
698
|
|
|
663
699
|
def _current_exporter(self) -> Any:
|
|
@@ -25,10 +25,11 @@ CLI groups from customers.
|
|
|
25
25
|
|
|
26
26
|
from __future__ import annotations
|
|
27
27
|
|
|
28
|
+
import dataclasses
|
|
28
29
|
import os
|
|
29
30
|
from typing import Any, Literal
|
|
30
31
|
|
|
31
|
-
from . import _assessments, _deployments, _exporter_pool, _traces, _valsets, callback, configuration
|
|
32
|
+
from . import _assessments, _deployments, _exporter_pool, _traces, _valsets, callback, configuration, programs
|
|
32
33
|
from .deployment_metadata import DeploymentSignatureResponse
|
|
33
34
|
|
|
34
35
|
|
|
@@ -105,13 +106,28 @@ class Client:
|
|
|
105
106
|
|
|
106
107
|
# -- scoped runs ------------------------------------------------------
|
|
107
108
|
|
|
109
|
+
def _declared(self, program: Any) -> dict[str, Any]:
|
|
110
|
+
"""The ``dspy.context`` settings that run ``program`` under its declared name.
|
|
111
|
+
|
|
112
|
+
A ``cmpnd.Program`` runs on its own LM and declares its name. Any other module
|
|
113
|
+
declares no name, which also clears a name an enclosing call declared.
|
|
114
|
+
"""
|
|
115
|
+
if isinstance(program, programs.Program):
|
|
116
|
+
return {"lm": program.lm, callback.DECLARED_PROGRAM_SETTING: program.name}
|
|
117
|
+
return {callback.DECLARED_PROGRAM_SETTING: None}
|
|
118
|
+
|
|
108
119
|
def run_program(self, program: Any, **inputs: Any) -> Any:
|
|
109
|
-
"""Run ``program(**inputs)`` traced on this client's account.
|
|
120
|
+
"""Run ``program(**inputs)`` traced on this client's account.
|
|
121
|
+
|
|
122
|
+
``program`` is a ``cmpnd.Program`` or a plain DSPy module. A ``Program`` runs
|
|
123
|
+
on its own LM, and its traces go to the program with its name.
|
|
124
|
+
"""
|
|
110
125
|
self._check_active()
|
|
111
126
|
import dspy
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
module = program.module if isinstance(program, programs.Program) else program
|
|
129
|
+
with dspy.context(callbacks=self._scoped_callbacks(), **self._declared(program)):
|
|
130
|
+
return module(**inputs)
|
|
115
131
|
|
|
116
132
|
def run_optimization(
|
|
117
133
|
self,
|
|
@@ -133,17 +149,33 @@ class Client:
|
|
|
133
149
|
client's own transport, and — when ``auto_instrument`` is also active —
|
|
134
150
|
suppresses the global class patch so the run is recorded once, on the
|
|
135
151
|
client's account, not the global one.
|
|
152
|
+
|
|
153
|
+
``student`` is a ``cmpnd.Program`` or a plain DSPy module. A ``Program`` is
|
|
154
|
+
optimized on its own LM, its rollouts and run record go to the program with
|
|
155
|
+
its name, and the result is a ``Program`` with the same name and LM.
|
|
136
156
|
"""
|
|
137
157
|
self._check_active()
|
|
138
158
|
import dspy
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
module = student.module if isinstance(student, programs.Program) else student
|
|
161
|
+
with dspy.context(callbacks=self._scoped_callbacks(), **self._declared(student)):
|
|
141
162
|
try:
|
|
142
163
|
if isinstance(optimizer, dspy.GEPA):
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
result = self._run_gepa(
|
|
165
|
+
optimizer,
|
|
166
|
+
module,
|
|
167
|
+
trainset=trainset,
|
|
168
|
+
valset=valset,
|
|
169
|
+
kwargs=kwargs,
|
|
170
|
+
declared_name=student.name if isinstance(student, programs.Program) else None,
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
result = optimizer.compile(module, trainset=trainset, valset=valset, **kwargs)
|
|
145
174
|
finally:
|
|
146
175
|
self._callback._clear_optimization_trace_state()
|
|
176
|
+
if isinstance(student, programs.Program):
|
|
177
|
+
return programs.Program(student.name, module=result, lm=student.lm)
|
|
178
|
+
return result
|
|
147
179
|
|
|
148
180
|
# -- data surface (flat verb-object over the client's credentials) -----
|
|
149
181
|
#
|
|
@@ -285,11 +317,30 @@ class Client:
|
|
|
285
317
|
self._check_active()
|
|
286
318
|
return _valsets.delete_entry(entry_id, api_key=self._config.api_key, endpoint=self._config.endpoint)
|
|
287
319
|
|
|
288
|
-
def _run_gepa(
|
|
289
|
-
|
|
320
|
+
def _run_gepa(
|
|
321
|
+
self,
|
|
322
|
+
optimizer: Any,
|
|
323
|
+
student: Any,
|
|
324
|
+
*,
|
|
325
|
+
trainset: Any,
|
|
326
|
+
valset: Any,
|
|
327
|
+
kwargs: dict[str, Any],
|
|
328
|
+
declared_name: str | None = None,
|
|
329
|
+
) -> Any:
|
|
330
|
+
"""Track a GEPA compile on this client's account and suppress the global patch.
|
|
331
|
+
|
|
332
|
+
A declared name is resolved here, on the caller's thread, because the run
|
|
333
|
+
record is sent synchronously when the run starts and must carry its program
|
|
334
|
+
id then. The record goes without an id when the name does not resolve.
|
|
335
|
+
"""
|
|
290
336
|
from . import _exporter_pool, _gepa_patch
|
|
291
337
|
|
|
292
338
|
client_exporter = _exporter_pool.ensure_exporter_started(self._config)
|
|
339
|
+
run_config = self._config
|
|
340
|
+
if declared_name is not None:
|
|
341
|
+
resolve = getattr(client_exporter, "resolve_program_name", None)
|
|
342
|
+
program_id = resolve(declared_name) if callable(resolve) else None
|
|
343
|
+
run_config = dataclasses.replace(self._config, program_id=program_id)
|
|
293
344
|
|
|
294
345
|
def _search() -> Any:
|
|
295
346
|
return optimizer.compile(student, trainset=trainset, valset=valset, **kwargs)
|
|
@@ -302,7 +353,7 @@ class Client:
|
|
|
302
353
|
trainset=trainset,
|
|
303
354
|
valset=valset,
|
|
304
355
|
run_original=_search,
|
|
305
|
-
client_config=
|
|
356
|
+
client_config=run_config,
|
|
306
357
|
exporter_override=client_exporter,
|
|
307
358
|
)
|
|
308
359
|
finally:
|
|
@@ -13,10 +13,17 @@ call that lost its answer leaves a second, empty program behind. That is why
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
|
+
import re
|
|
16
17
|
from typing import Any
|
|
17
18
|
|
|
18
19
|
from . import _http, configuration
|
|
19
20
|
|
|
21
|
+
# A program name is an ASCII letter or digit, then letters, digits, ".", "_" or "-",
|
|
22
|
+
# at most 64 characters. Serve compares names without case and keeps the case typed.
|
|
23
|
+
_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
|
|
24
|
+
# Reserved in any case, so that a name never looks like a program id.
|
|
25
|
+
_RESERVED_PREFIX = "p_"
|
|
26
|
+
|
|
20
27
|
DEFAULT_ENDPOINT = "https://platform.cmpnd.ai"
|
|
21
28
|
|
|
22
29
|
# Self-host release 2026-001 answers a programs create with 405, because it routes
|
|
@@ -87,3 +94,60 @@ def _client(api_key: str | None, endpoint: str | None) -> _http.ApiClient:
|
|
|
87
94
|
raise _http.ApiError("No API key. Pass api_key=, call cmpnd.configure(api_key=...), or set CMPND_API_KEY.")
|
|
88
95
|
resolved_endpoint = endpoint or (cfg.endpoint if cfg else None) or os.getenv("CMPND_ENDPOINT") or DEFAULT_ENDPOINT
|
|
89
96
|
return _http.ApiClient(resolved_key, resolved_endpoint)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def validate_program_name(name: object) -> str:
|
|
100
|
+
"""Return ``name`` when it is a valid program name, and raise ``ValueError`` otherwise.
|
|
101
|
+
|
|
102
|
+
Serve checks the same syntax. Checking it here makes a bad name fail where it is
|
|
103
|
+
declared, with no network call, instead of in the exporter after the program runs.
|
|
104
|
+
"""
|
|
105
|
+
if not isinstance(name, str) or not _NAME.fullmatch(name) or name.lower().startswith(_RESERVED_PREFIX):
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"{name!r} is not a valid program name. A program name is a letter or digit followed by "
|
|
108
|
+
"letters, digits, '.', '_' or '-', at most 64 characters, and does not start with p_."
|
|
109
|
+
)
|
|
110
|
+
return name
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Program:
|
|
114
|
+
"""A DSPy module, the LM it runs on, and the name of the program it is.
|
|
115
|
+
|
|
116
|
+
The name is the program's handle: traces from a ``cmpnd.Client`` run of this
|
|
117
|
+
program go to the program with this name in the caller's organization, which the
|
|
118
|
+
platform creates on first use. Programs in one organization that share a name
|
|
119
|
+
share a history.
|
|
120
|
+
|
|
121
|
+
Declaring a program makes no network call. The SDK asks the platform for the
|
|
122
|
+
program behind the name the first time a client sends one of its traces.
|
|
123
|
+
|
|
124
|
+
>>> qa = cmpnd.Program("capital-qa", module=dspy.Predict("question -> answer"), lm=dspy.LM("openai/gpt-5-mini"))
|
|
125
|
+
>>> with cmpnd.Client() as client:
|
|
126
|
+
... client.run_program(qa, question="What is the capital of France?")
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, name: str, *, module: Any, lm: Any) -> None:
|
|
130
|
+
import dspy
|
|
131
|
+
from dspy.clients import base_lm # pyright: ignore[reportMissingTypeStubs] # dspy re-exports BaseLM untyped
|
|
132
|
+
|
|
133
|
+
self.name = validate_program_name(name)
|
|
134
|
+
if not isinstance(module, dspy.Module):
|
|
135
|
+
raise TypeError(f"A Program needs a DSPy module, not {type(module).__name__}")
|
|
136
|
+
if not isinstance(lm, base_lm.BaseLM):
|
|
137
|
+
raise TypeError(f"A Program needs an LM such as dspy.LM(...), not {type(lm).__name__}")
|
|
138
|
+
self.module = module
|
|
139
|
+
self.lm = lm
|
|
140
|
+
|
|
141
|
+
def __call__(self, **inputs: Any) -> Any:
|
|
142
|
+
"""Run the module on this program's LM, as plain DSPy.
|
|
143
|
+
|
|
144
|
+
A direct call records nothing under the program's name. Run the program
|
|
145
|
+
through ``cmpnd.Client.run_program`` to trace it under its name.
|
|
146
|
+
"""
|
|
147
|
+
import dspy
|
|
148
|
+
|
|
149
|
+
with dspy.context(lm=self.lm):
|
|
150
|
+
return self.module(**inputs)
|
|
151
|
+
|
|
152
|
+
def __repr__(self) -> str:
|
|
153
|
+
return f"Program({self.name!r}, lm={getattr(self.lm, 'model', self.lm)!r})"
|
|
@@ -43,6 +43,43 @@ and run records use the client's own id and never the id given to
|
|
|
43
43
|
`configure()`. A client with no id declares no program, so its runs go out
|
|
44
44
|
without one. Project tags follow the same rule.
|
|
45
45
|
|
|
46
|
+
## Programs declared by name
|
|
47
|
+
|
|
48
|
+
`cmpnd.Program(name, module=..., lm=...)` declares a program by name. It holds a
|
|
49
|
+
DSPy module and the `dspy.LM` it runs on. A client that runs a `Program` traces
|
|
50
|
+
it under its name:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
qa = cmpnd.Program("capital-qa", module=dspy.Predict("question -> answer"), lm=dspy.LM("openai/gpt-5-mini"))
|
|
54
|
+
|
|
55
|
+
with cmpnd.Client() as sdk:
|
|
56
|
+
sdk.run_program(qa, question="What is the capital of France?")
|
|
57
|
+
better = sdk.run_optimization(gepa, qa, trainset=train, valset=val)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **A name is checked where it is declared.** It is an ASCII letter or digit,
|
|
61
|
+
then letters, digits, `.`, `_` or `-`, at most 64 characters, and it does not
|
|
62
|
+
start with `p_` in any case. A bad name raises `ValueError`. Declaring a
|
|
63
|
+
program makes no network call.
|
|
64
|
+
- **The name finds the program.** The exporter asks the platform for the
|
|
65
|
+
program with that name in the caller's organization, which the platform
|
|
66
|
+
creates on first use, and sends the program's id on each trace. Each process
|
|
67
|
+
asks once per name. Two processes that declare one name write to one program.
|
|
68
|
+
- **A `Program` runs on its own LM**, with no global `dspy.configure` needed.
|
|
69
|
+
Calling it directly runs the module on its LM as plain DSPy, and traces
|
|
70
|
+
nothing under its name.
|
|
71
|
+
- **An optimized program keeps its name.** `run_optimization` returns a
|
|
72
|
+
`Program` with the same name and LM and the optimized module. For a
|
|
73
|
+
`dspy.GEPA` optimizer, the client resolves the name before the run starts, so
|
|
74
|
+
the run record carries the program's id.
|
|
75
|
+
- **The name wins over `program_id=`.** A client configured with an id sends a
|
|
76
|
+
declared program's traces to the named program, and warns once.
|
|
77
|
+
- **A serve without program names**, such as self-host release 2026-001, gets
|
|
78
|
+
the traces without an id. The exporter warns once per name.
|
|
79
|
+
|
|
80
|
+
A plain DSPy module passed to `run_program` or `run_optimization` declares no
|
|
81
|
+
name, as before.
|
|
82
|
+
|
|
46
83
|
## Membership
|
|
47
84
|
|
|
48
85
|
A client records a call only when the call runs through one of its methods.
|
|
@@ -771,7 +771,10 @@ the caller's id rides only the steps on the caller's own deployment.
|
|
|
771
771
|
#### A declared program name resolves to an id in the exporter
|
|
772
772
|
|
|
773
773
|
A trace can declare its program by name instead of by id, in `TraceBuilder.declared_name`.
|
|
774
|
-
|
|
774
|
+
A `cmpnd.Client` run of a `cmpnd.Program` sets it: the client puts the name in the DSPy
|
|
775
|
+
settings key `callback.DECLARED_PROGRAM_SETTING` with `dspy.context`, which reaches the
|
|
776
|
+
threads DSPy starts, and `CmpndCallback._stamp` copies it onto the trace and drops any id
|
|
777
|
+
the client was configured with. The name never goes on the wire. The exporter resolves it with
|
|
775
778
|
`PUT /api/v1/programs/by-name/{name}` and writes the id into the trace start and full
|
|
776
779
|
trace bodies, at the top level and in `started_fact`, before it compares or sends them.
|
|
777
780
|
`cmpnd_ingest/program_names.py` keeps the answers, and
|
|
@@ -784,6 +787,9 @@ trace bodies, at the top level and in `started_fact`, before it compares or send
|
|
|
784
787
|
does. The exit flush resolves the names still waiting, with no retries.
|
|
785
788
|
- **An id already on the trace stands.** Inside a worker, a deployment's traces carry
|
|
786
789
|
its id from the start, and no name is resolved.
|
|
790
|
+
- **A run record resolves on the caller's thread.** An optimization run's start is sent
|
|
791
|
+
synchronously, so `Client.run_optimization` calls `BatchExporter.resolve_program_name`
|
|
792
|
+
before a `dspy.GEPA` run starts. It shares the flush's answers.
|
|
787
793
|
- **A refusal settles the name with no id.** Serve answers 404 or 405 when it has no
|
|
788
794
|
program names, as self-host release 2026-001 does, and 409, 422 or 403 for a name it
|
|
789
795
|
will not resolve. The traces go without an id, and the exporter warns once per name.
|
|
@@ -1435,7 +1435,21 @@ class BatchExporter:
|
|
|
1435
1435
|
for trace_id in ended_traces:
|
|
1436
1436
|
self._program_names.trace_ended(trace_id)
|
|
1437
1437
|
|
|
1438
|
-
def
|
|
1438
|
+
def resolve_program_name(self, name: str) -> str | None:
|
|
1439
|
+
"""The program id for a declared name, resolved now on the caller's thread.
|
|
1440
|
+
|
|
1441
|
+
For a record that must carry its program id when it is sent synchronously,
|
|
1442
|
+
such as an optimization run's start. It shares the answers the flush keeps, so
|
|
1443
|
+
a name the flush resolved costs no request here, and the reverse. Returns None
|
|
1444
|
+
when serve refused the name or could not be reached, and the record goes
|
|
1445
|
+
without an id.
|
|
1446
|
+
"""
|
|
1447
|
+
if not self._program_names.unsettled([name]):
|
|
1448
|
+
return self._program_names.program_id(name)
|
|
1449
|
+
self._resolve_program_name(name, retry.TransientTally(), self._sync_http_client())
|
|
1450
|
+
return self._program_names.program_id(name)
|
|
1451
|
+
|
|
1452
|
+
def _resolve_program_name(self, name: str, tally: retry.TransientTally, client: httpx.Client | None = None) -> None:
|
|
1439
1453
|
"""Ask serve for a name's program id, and settle the name unless serve was unreachable.
|
|
1440
1454
|
|
|
1441
1455
|
The request has no body. A 429, a 5xx or an unreachable server is retried on
|
|
@@ -1443,7 +1457,7 @@ class BatchExporter:
|
|
|
1443
1457
|
leaves the name unsettled for the next batch. A refusal settles the name with
|
|
1444
1458
|
no id. Each name warns once.
|
|
1445
1459
|
"""
|
|
1446
|
-
client = self._require_client()
|
|
1460
|
+
client = client if client is not None else self._require_client()
|
|
1447
1461
|
path = "/api/v1/programs/by-name/" + urllib.parse.quote(name, safe="")
|
|
1448
1462
|
try:
|
|
1449
1463
|
response = self._send_retrying(
|
|
@@ -57,6 +57,11 @@ class ProgramNames:
|
|
|
57
57
|
with self._lock:
|
|
58
58
|
self._ids[name] = program_id
|
|
59
59
|
|
|
60
|
+
def program_id(self, name: str) -> str | None:
|
|
61
|
+
"""The id serve gave for a settled name, or None for a refused or unsettled one."""
|
|
62
|
+
with self._lock:
|
|
63
|
+
return self._ids.get(name)
|
|
64
|
+
|
|
60
65
|
def first_warning(self, name: str) -> bool:
|
|
61
66
|
"""True the first time this is asked for a name, so each name warns once."""
|
|
62
67
|
with self._lock:
|
|
@@ -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.43.1"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -29,16 +29,6 @@ classifiers = [
|
|
|
29
29
|
|
|
30
30
|
dependencies = [
|
|
31
31
|
"dspy>=3.1.3",
|
|
32
|
-
# Compatibility cap, not a feature dependency (CMP-814). litellm (pulled in
|
|
33
|
-
# transitively by dspy) 1.97.x declares openai>=2 but its `Message` pydantic
|
|
34
|
-
# model references a type openai's 2.x major bump moved, so `Message.model_rebuild()`
|
|
35
|
-
# raises "not fully defined" on every LM call — including through the fake LM the
|
|
36
|
-
# sdk-test suite drives. With no bound, `uv pip install … dspy==3.3.0` (the CI
|
|
37
|
-
# matrix's install) resolves that broken pair; consumers hit it the same way. The
|
|
38
|
-
# cap forces the last working pair (litellm 1.80.x + openai 1.109.x). No separate
|
|
39
|
-
# litellm pin: this transitively selects a compatible litellm within dspy's range.
|
|
40
|
-
# Lift it once litellm ships a build whose Message is defined against openai 2.x.
|
|
41
|
-
"openai<2",
|
|
42
32
|
# Reached directly by the SDK (cmpnd/optimization.py's poll, cmpnd/datasets.py)
|
|
43
33
|
# and by the ingest transport this wheel bundles (ingest/cmpnd_ingest), whose
|
|
44
34
|
# only non-stdlib import it is.
|
|
@@ -99,6 +89,11 @@ dev = [
|
|
|
99
89
|
# requires-python <3.15. uv ignores that upper bound, so the 3.15 test leg still
|
|
100
90
|
# installs them.
|
|
101
91
|
"litellm",
|
|
92
|
+
# Every litellm release from 1.102.0 imports sre_constants, which Python 3.15
|
|
93
|
+
# removed, so `import litellm` fails there (BerriAI/litellm#46060). This cap
|
|
94
|
+
# applies only to the test install. A plain install on Python 3.15 has no
|
|
95
|
+
# litellm. Delete it once a litellm release imports on Python 3.15.
|
|
96
|
+
"litellm<1.102; python_version >= '3.15'",
|
|
102
97
|
"gepa",
|
|
103
98
|
# litellm's bedrock/ provider imports boto3 in-process, so the client-side
|
|
104
99
|
# Bedrock stress tests (committee twins) need it in the test env.
|