cmpnd 0.46.0__tar.gz → 0.47.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.46.0 → cmpnd-0.47.1}/PKG-INFO +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/callback.py +5 -5
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/deployment.py +4 -5
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/helpers.py +52 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimization.py +9 -33
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/packaging.py +8 -24
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/sdk-instrumentation.md +2 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/pyproject.toml +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_deploy_programs.py +5 -7
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimize_server_owned_completion.py +7 -12
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/test_logs_renders_traces.py +1 -2
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/test_unhappy_path.py +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/_provider.py +2 -2
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_callback.py +45 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deploy.py +6 -6
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize.py +42 -52
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_packaging_program_source.py +35 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_upload_auth_headers.py +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.1}/uv.lock +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.1}/.gitignore +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/AGENTS.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/CLAUDE.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/authored.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/context.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/decorators.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/execution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/exporter.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/imaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/cmpnd/traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/cli.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/client.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/custom-proposer.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/docs/reading-traces.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/examples/local_ollama.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/ingest/pyproject.toml +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/release-stress.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_fake_clock.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/_traced_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/__main__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/drain.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/ingest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/sweep.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_health.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/_result.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_authored.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_client_floor.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_context.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_decorators.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_eval.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_eval_start.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_execute.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_exporter.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_imaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_import_weight.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_integration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_packaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_poll_until.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_program_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_programs_create.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_project_payload.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_schemas.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_span_references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_system_one.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_trace_render.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_traces_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_truncation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.1}/tests/test_xxh64.py +0 -0
|
@@ -1372,10 +1372,10 @@ class _LMMixin(_CallbackBase):
|
|
|
1372
1372
|
if self._active_optimization_run_id is not None or context.get_current_optimization_run_id() is not None:
|
|
1373
1373
|
span.attributes["optimization.role"] = "rollout"
|
|
1374
1374
|
|
|
1375
|
-
#
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1375
|
+
# The usage fields read one named value each out of the raw kwargs. The
|
|
1376
|
+
# span attributes copy only what helpers.SAFE_LM_KWARGS names, because
|
|
1377
|
+
# the kwargs also hold provider credentials.
|
|
1378
|
+
lm_kwargs = getattr(instance, "kwargs", None) or {}
|
|
1379
1379
|
span.lm_usage = models.LMUsageObservationV1(
|
|
1380
1380
|
sdk_call_id=call_id,
|
|
1381
1381
|
model_requested=span.model_name,
|
|
@@ -1387,7 +1387,7 @@ class _LMMixin(_CallbackBase):
|
|
|
1387
1387
|
{
|
|
1388
1388
|
"lm.model_type": getattr(instance, "model_type", "unknown"),
|
|
1389
1389
|
"lm.cache": str(getattr(instance, "cache", False)),
|
|
1390
|
-
**{f"lm.{k}": str(v) for k, v in lm_kwargs.items()},
|
|
1390
|
+
**{f"lm.{k}": str(v) for k, v in helpers.safe_lm_kwargs(lm_kwargs).items()},
|
|
1391
1391
|
}
|
|
1392
1392
|
)
|
|
1393
1393
|
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The deploy flow:
|
|
4
4
|
1. Package the module into a source ZIP
|
|
5
|
-
2. POST to the server to create a deployment (returns
|
|
6
|
-
3. PUT the ZIP to that slot
|
|
7
|
-
worker, raw body, no form)
|
|
5
|
+
2. POST to the server to create a deployment (returns an upload slot on serve)
|
|
6
|
+
3. PUT the ZIP to that slot as the raw body
|
|
8
7
|
4. Poll until the parser finishes (status becomes "ready" or "failed")
|
|
9
8
|
5. Return the deployment_id
|
|
10
9
|
"""
|
|
@@ -451,8 +450,8 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
|
451
450
|
|
|
452
451
|
deploy_body: dict[str, Any] = {
|
|
453
452
|
"program_signature_hash": identity.format_signature_hash(program_signature_hash),
|
|
454
|
-
# Declare the ZIP's exact length so the server can
|
|
455
|
-
#
|
|
453
|
+
# Declare the ZIP's exact length so the server can reject an oversized
|
|
454
|
+
# deploy before the upload.
|
|
456
455
|
"declared_content_length": len(zip_bytes),
|
|
457
456
|
}
|
|
458
457
|
# Left off when undeclared, so a serve from before program ids reads the body it
|
|
@@ -193,3 +193,55 @@ def infer_provider(instance: Any) -> str:
|
|
|
193
193
|
return "together"
|
|
194
194
|
else:
|
|
195
195
|
return "unknown"
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# The LM kwargs the SDK may copy off an LM: into an ``lm.*`` span attribute,
|
|
199
|
+
# which every member of the org can read, and into the ``dspy.LM(...)`` line of
|
|
200
|
+
# deployed source. An allow-list, because an LM's kwargs carry provider
|
|
201
|
+
# credentials under names no deny-list keeps up with, such as
|
|
202
|
+
# ``aws_secret_access_key``, ``azure_ad_token``, ``vertex_credentials`` and
|
|
203
|
+
# ``extra_headers``, and ``dspy.LM.dump_state()`` drops only ``api_key``. A
|
|
204
|
+
# worker spends the deployment's sealed credential, so neither copy needs one.
|
|
205
|
+
#
|
|
206
|
+
# It holds what a provider is asked for (a superset of
|
|
207
|
+
# ``optimization.SAMPLING_KEYS``) and the ``dspy.LM`` settings that shape a
|
|
208
|
+
# call, and nothing that names a destination: ``api_base`` and ``base_url``
|
|
209
|
+
# stay out.
|
|
210
|
+
SAFE_LM_KWARGS: frozenset[str] = frozenset(
|
|
211
|
+
{
|
|
212
|
+
# Sampling and response shape.
|
|
213
|
+
"temperature",
|
|
214
|
+
"max_tokens",
|
|
215
|
+
"max_completion_tokens",
|
|
216
|
+
"top_p",
|
|
217
|
+
"top_k",
|
|
218
|
+
"n",
|
|
219
|
+
"stop",
|
|
220
|
+
"presence_penalty",
|
|
221
|
+
"frequency_penalty",
|
|
222
|
+
"seed",
|
|
223
|
+
"logprobs",
|
|
224
|
+
"top_logprobs",
|
|
225
|
+
"reasoning_effort",
|
|
226
|
+
"thinking",
|
|
227
|
+
"verbosity",
|
|
228
|
+
"response_format",
|
|
229
|
+
"tools",
|
|
230
|
+
"tool_choice",
|
|
231
|
+
"parallel_tool_calls",
|
|
232
|
+
"service_tier",
|
|
233
|
+
"cache_control_injection_points",
|
|
234
|
+
# dspy.LM call settings.
|
|
235
|
+
"cache",
|
|
236
|
+
"num_retries",
|
|
237
|
+
"use_developer_role",
|
|
238
|
+
"rollout_id",
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def safe_lm_kwargs(kwargs: Any) -> dict[str, Any]:
|
|
244
|
+
"""The entries of an LM's kwargs or ``dump_state()`` that :data:`SAFE_LM_KWARGS` names."""
|
|
245
|
+
if not isinstance(kwargs, dict):
|
|
246
|
+
return {}
|
|
247
|
+
return {key: value for key, value in kwargs.items() if key in SAFE_LM_KWARGS}
|
|
@@ -11,8 +11,8 @@ Flow per :func:`optimize` call:
|
|
|
11
11
|
1. Build the optimization bundle (``trainset.ndjson`` + ``valset.ndjson`` +
|
|
12
12
|
``metric.py``) and, if no ``deployment_id`` was given, :func:`deploy` the
|
|
13
13
|
program to materialize the starting deployment A.
|
|
14
|
-
2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get
|
|
15
|
-
|
|
14
|
+
2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get an
|
|
15
|
+
upload slot, PUT the bundle, then poll until the step completes. The
|
|
16
16
|
result becomes a first-class child deployment B.
|
|
17
17
|
3. Steps 1..n — ``data:"reuse"`` on the latest result deployment, advancing the
|
|
18
18
|
seed each step (the key quality lever). Track the best score; stop after
|
|
@@ -1417,14 +1417,12 @@ def _initiate_step(
|
|
|
1417
1417
|
def _upload_bundle(
|
|
1418
1418
|
client: httpx.Client, initiated: dict[str, Any], bundle: bytes, *, endpoint: str, api_key: str
|
|
1419
1419
|
) -> None:
|
|
1420
|
-
"""
|
|
1420
|
+
"""PUT the optimization bundle, raw, to the slot the initiate step returned.
|
|
1421
1421
|
|
|
1422
|
-
Serve answers an ``awaiting_data`` initiate with ``upload_url
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
non-empty ``upload_fields`` is a presigned form, which no current server sends.
|
|
1427
|
-
The bundle then rides as the ``file`` part of a multipart POST.
|
|
1422
|
+
Serve answers an ``awaiting_data`` initiate with an ``upload_url`` on its own
|
|
1423
|
+
bundle route, and authenticates that route with the API key. The key rides
|
|
1424
|
+
the PUT only when ``upload_url`` is on ``endpoint``'s origin
|
|
1425
|
+
(``_http.upload_auth_headers``).
|
|
1428
1426
|
|
|
1429
1427
|
The slot pins its destination, so re-sending the same bytes writes the same
|
|
1430
1428
|
object. That makes this call idempotent, so it rides through a transient answer
|
|
@@ -1433,31 +1431,9 @@ def _upload_bundle(
|
|
|
1433
1431
|
upload_url = initiated.get("upload_url")
|
|
1434
1432
|
if not upload_url:
|
|
1435
1433
|
raise OptimizeError("upload step did not return an upload_url")
|
|
1436
|
-
upload_fields = initiated.get("upload_fields")
|
|
1437
|
-
method = (initiated.get("upload_method") or ("POST" if upload_fields else "PUT")).upper()
|
|
1438
1434
|
what = "upload optimization bundle"
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
if upload_fields:
|
|
1442
|
-
resp = _request(
|
|
1443
|
-
lambda: client.post(
|
|
1444
|
-
upload_url,
|
|
1445
|
-
data=dict(upload_fields),
|
|
1446
|
-
files={"file": ("data.zip", bundle, "application/zip")},
|
|
1447
|
-
headers=auth,
|
|
1448
|
-
),
|
|
1449
|
-
what,
|
|
1450
|
-
)
|
|
1451
|
-
else:
|
|
1452
|
-
resp = _request(
|
|
1453
|
-
lambda: client.request(
|
|
1454
|
-
method,
|
|
1455
|
-
upload_url,
|
|
1456
|
-
content=bundle,
|
|
1457
|
-
headers={"Content-Type": "application/zip", **auth},
|
|
1458
|
-
),
|
|
1459
|
-
what,
|
|
1460
|
-
)
|
|
1435
|
+
headers = {"Content-Type": "application/zip", **_http.upload_auth_headers(upload_url, endpoint, api_key)}
|
|
1436
|
+
resp = _request(lambda: client.put(upload_url, content=bundle, headers=headers), what)
|
|
1461
1437
|
if not resp.is_success:
|
|
1462
1438
|
body = (resp.text or "").strip()[:400]
|
|
1463
1439
|
raise OptimizeError(f"failed to {what}: HTTP {resp.status_code}: {body}")
|
|
@@ -30,6 +30,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
30
30
|
|
|
31
31
|
import typing_extensions
|
|
32
32
|
|
|
33
|
+
from . import helpers
|
|
33
34
|
from ._runtime_tools import is_runtime_tool
|
|
34
35
|
|
|
35
36
|
if TYPE_CHECKING:
|
|
@@ -331,17 +332,6 @@ def _get_module_signature(module: dspy.Module) -> dspy.Signature | None:
|
|
|
331
332
|
return None
|
|
332
333
|
|
|
333
334
|
|
|
334
|
-
# Keys to omit when generating a dspy.LM() call
|
|
335
|
-
_LM_SKIP_KEYS: set[str] = {
|
|
336
|
-
"model",
|
|
337
|
-
"model_type",
|
|
338
|
-
"api_key",
|
|
339
|
-
"api_base",
|
|
340
|
-
"base_url",
|
|
341
|
-
"model_list",
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
|
|
345
335
|
def _is_user_type(cls: type) -> bool:
|
|
346
336
|
"""True if cls is defined by user code, not stdlib/dspy/third-party."""
|
|
347
337
|
mod = getattr(cls, "__module__", "") or ""
|
|
@@ -894,18 +884,12 @@ def generate_program_source(
|
|
|
894
884
|
lm_state = _extract_lm_state(module)
|
|
895
885
|
if lm_state is not None:
|
|
896
886
|
model = lm_state.get("model", "")
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
# argument (a 502 at execute on the dspy versions that emit it).
|
|
904
|
-
# LM/litellm kwargs are never underscore-prefixed, so the prefix is
|
|
905
|
-
# a safe discriminator.
|
|
906
|
-
if key in _LM_SKIP_KEYS or key.startswith("_"):
|
|
907
|
-
continue
|
|
908
|
-
kwargs_parts.append(f"{key}={value!r}")
|
|
887
|
+
# dump_state() keeps every constructor kwarg but api_key, including
|
|
888
|
+
# cloud credentials, the destination (api_base), and dspy-internal
|
|
889
|
+
# markers such as `_dspy_lm_class` that litellm rejects as unknown
|
|
890
|
+
# arguments. The deployed LM names only what helpers.SAFE_LM_KWARGS
|
|
891
|
+
# allows. The worker supplies the credential and the destination.
|
|
892
|
+
kwargs_parts = [f"{key}={value!r}" for key, value in helpers.safe_lm_kwargs(lm_state).items()]
|
|
909
893
|
kwargs_str = ", ".join(kwargs_parts)
|
|
910
894
|
if kwargs_str:
|
|
911
895
|
lines.append(f'lm = dspy.LM("{model}", {kwargs_str})')
|
|
@@ -1428,7 +1412,7 @@ def generate_source_zip(
|
|
|
1428
1412
|
) -> bytes:
|
|
1429
1413
|
"""Build a ZIP archive with program source, optional metric, and manifest.
|
|
1430
1414
|
|
|
1431
|
-
The ZIP is
|
|
1415
|
+
The ZIP is the body the deploy API's source upload takes.
|
|
1432
1416
|
"""
|
|
1433
1417
|
import dspy
|
|
1434
1418
|
|
|
@@ -846,6 +846,8 @@ The callback mints `source_observation_id` when the LM call starts and records `
|
|
|
846
846
|
|
|
847
847
|
The LM span's `lm.finish_reason` attribute records why the model stopped, in the chat vocabulary serve's truncation warning reads. Each response shape spells it differently. Chat puts it on the first choice and DSPy 3.3's typed `LMResponse` on the first output. The lm15 `Response` a DSPy 3.4 engine returns carries it at the top level and names a tool-call stop `tool_call`, which the SDK records as `tool_calls`. The Responses API reports a `status`, and its `max_output_tokens` cutoff lands as `length`. `_finish_reason` in `python-sdk/cmpnd/callback.py` holds the mapping.
|
|
848
848
|
|
|
849
|
+
The LM span's other `lm.*` attributes copy only the LM kwargs that `SAFE_LM_KWARGS` in `python-sdk/cmpnd/helpers.py` names. These are the sampling and response-shape settings, such as `temperature` and `reasoning_effort`, and dspy.LM's own call settings, such as `num_retries`. Every member of the org can read a span. An LM's kwargs can also carry provider credentials under names no deny-list keeps up with, such as `aws_secret_access_key`, `azure_ad_token`, `vertex_credentials` and `extra_headers`. So the list names what may be copied, and a kwarg it does not name stays out. It also leaves out `api_base` and `base_url`, which name a destination. The `dspy.LM(...)` line in a programmatic deploy's generated source goes through the same list, because `dspy.LM.dump_state()` drops only `api_key`. The worker supplies the deployment's credential and destination itself.
|
|
850
|
+
|
|
849
851
|
`provider_cost` stays `None`. DSPy and LiteLLM can attach a calculated cost to history, but that value is not provider evidence. The SDK will populate this field only when an adapter can prove that the provider supplied both the amount and its currency.
|
|
850
852
|
|
|
851
853
|
## Field truncation (span inputs/outputs)
|
|
@@ -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.47.1"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -212,11 +212,8 @@ class TestDeployBadImportRejected:
|
|
|
212
212
|
headers = {"X-API-Key": config.api_key, "Content-Type": "application/json"}
|
|
213
213
|
|
|
214
214
|
with httpx.Client(timeout=30.0) as client:
|
|
215
|
-
# Step 1: create deployment
|
|
216
|
-
#
|
|
217
|
-
# length, so it must be declared up front — exactly as cmpnd/deploy.py
|
|
218
|
-
# does, and a deployment names its program. This hand-crafted client
|
|
219
|
-
# mirrors that contract.
|
|
215
|
+
# Step 1: create the deployment, declaring the ZIP's length and
|
|
216
|
+
# naming its program as cmpnd.deploy does.
|
|
220
217
|
create_resp = client.post(
|
|
221
218
|
f"{config.endpoint}/api/v1/deployments",
|
|
222
219
|
json={
|
|
@@ -232,11 +229,12 @@ class TestDeployBadImportRejected:
|
|
|
232
229
|
deployment_id = data["deployment_id"]
|
|
233
230
|
upload_url = data["upload_url"]
|
|
234
231
|
|
|
235
|
-
# Step 2: PUT the bad ZIP to the
|
|
232
|
+
# Step 2: PUT the bad ZIP to the upload slot, which serve
|
|
233
|
+
# authenticates with the API key.
|
|
236
234
|
upload_resp = client.put(
|
|
237
235
|
upload_url,
|
|
238
236
|
content=zip_bytes,
|
|
239
|
-
headers={"Content-Type": "application/zip"},
|
|
237
|
+
headers={"X-API-Key": config.api_key, "Content-Type": "application/zip"},
|
|
240
238
|
)
|
|
241
239
|
upload_resp.raise_for_status()
|
|
242
240
|
|
|
@@ -106,18 +106,13 @@ def _initiate_and_abandon(deployment_id: str, trainset, valset, metric) -> str:
|
|
|
106
106
|
resp.raise_for_status()
|
|
107
107
|
initiated = resp.json()
|
|
108
108
|
|
|
109
|
-
# serve
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
data=dict(upload_fields),
|
|
117
|
-
files={"file": ("data.zip", bundle, "application/zip")},
|
|
118
|
-
)
|
|
119
|
-
else:
|
|
120
|
-
up = client.put(upload_url, content=bundle, headers={"Content-Type": "application/zip"})
|
|
109
|
+
# The slot is serve's bundle route, which takes the raw bundle as a PUT
|
|
110
|
+
# and authenticates it with the API key.
|
|
111
|
+
up = client.put(
|
|
112
|
+
initiated["upload_url"],
|
|
113
|
+
content=bundle,
|
|
114
|
+
headers={"X-API-Key": cfg.api_key, "Content-Type": "application/zip"},
|
|
115
|
+
)
|
|
121
116
|
up.raise_for_status()
|
|
122
117
|
|
|
123
118
|
# Deliberately NO status poll — the server-owned watcher must finish it.
|
|
@@ -64,8 +64,7 @@ def _create_and_upload_real_module(rails: httpx.Client, stub_url: str) -> str:
|
|
|
64
64
|
created = resp.json()
|
|
65
65
|
deployment_id = created["deployment_id"]
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
# — POST presigned (Lambda) or PUT (cmpnd-serve).
|
|
67
|
+
# PUT the real source ZIP to the slot the create response names.
|
|
69
68
|
zip_bytes = generate_source_zip(module, None)
|
|
70
69
|
upload_deploy_zip(created, zip_bytes)
|
|
71
70
|
|
|
@@ -59,7 +59,7 @@ def _create_deployment(rails: httpx.Client, *, signature_hash: int) -> dict:
|
|
|
59
59
|
|
|
60
60
|
def _upload_to_stub(stub_url: str, create_resp: dict, zip_bytes: bytes) -> None:
|
|
61
61
|
"""Mirror the SDK's deploy upload step — a single PUT to the upload slot
|
|
62
|
-
|
|
62
|
+
via the shared conftest helper. ``stub_url`` is unused — kept in
|
|
63
63
|
the signature so existing call-sites don't need editing."""
|
|
64
64
|
del stub_url # historical; the helper reads upload_url from create_resp
|
|
65
65
|
upload_deploy_zip(create_resp, zip_bytes)
|
|
@@ -198,8 +198,8 @@ def deployed_lm_kwargs(**overrides: object) -> dict[str, object]:
|
|
|
198
198
|
that is where the destination is decided: `deploy` reads the model, the key and the
|
|
199
199
|
``api_base`` off `dspy.settings.lm` and forwards them so the server can resolve a
|
|
200
200
|
provider. Setting the LM on the *module* does not do it — the packaged program's own
|
|
201
|
-
LM line comes from there, but `api_base` is stripped out of it (it is in
|
|
202
|
-
`cmpnd.
|
|
201
|
+
LM line comes from there, but `api_base` is stripped out of it (it is not in
|
|
202
|
+
`cmpnd.helpers.SAFE_LM_KWARGS`), so a worker pointed by nothing else answers from
|
|
203
203
|
whatever host it can resolve. Hence `with dspy.context(lm=…): cmpnd.deploy(…)` at the
|
|
204
204
|
call sites here.
|
|
205
205
|
"""
|
|
@@ -433,6 +433,51 @@ class TestCallbackLMHandlers:
|
|
|
433
433
|
|
|
434
434
|
shutdown_exporter()
|
|
435
435
|
|
|
436
|
+
def test_on_lm_start_records_only_allow_listed_lm_kwargs(self):
|
|
437
|
+
"""A provider credential set on the LM never reaches the span.
|
|
438
|
+
|
|
439
|
+
The span ships to serve, where every org member can read it, so its
|
|
440
|
+
``lm.*`` attributes name what is known to be safe rather than filtering
|
|
441
|
+
out what is known to be secret.
|
|
442
|
+
"""
|
|
443
|
+
import json
|
|
444
|
+
|
|
445
|
+
import dspy
|
|
446
|
+
|
|
447
|
+
from cmpnd.callback import CmpndCallback
|
|
448
|
+
|
|
449
|
+
with mock.patch("cmpnd.exporter.ensure_exporter_started", autospec=True):
|
|
450
|
+
callback = CmpndCallback()
|
|
451
|
+
lm = dspy.LM(
|
|
452
|
+
"bedrock/anthropic.claude-3-haiku",
|
|
453
|
+
temperature=0.2,
|
|
454
|
+
max_tokens=64,
|
|
455
|
+
api_key="secret-api-key",
|
|
456
|
+
api_base="https://secret-host.invalid",
|
|
457
|
+
aws_access_key_id="secret-access-key-id",
|
|
458
|
+
aws_secret_access_key="secret-access-key",
|
|
459
|
+
aws_session_token="secret-session-token",
|
|
460
|
+
azure_ad_token="secret-azure-token",
|
|
461
|
+
vertex_credentials='{"private_key": "secret-vertex"}',
|
|
462
|
+
extra_headers={"Authorization": "Bearer secret-header"},
|
|
463
|
+
unknown_future_kwarg="secret-unknown",
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
with mock.patch("cmpnd.exporter.get_exporter", autospec=True, return_value=None):
|
|
467
|
+
callback.on_lm_start(call_id="lm_call_1", instance=lm, inputs={"messages": []})
|
|
468
|
+
|
|
469
|
+
span = get_current_span()
|
|
470
|
+
assert span is not None
|
|
471
|
+
assert span.attributes["lm.temperature"] == "0.2"
|
|
472
|
+
assert span.attributes["lm.max_tokens"] == "64"
|
|
473
|
+
assert "secret" not in json.dumps(span.to_api_dict(), default=str)
|
|
474
|
+
|
|
475
|
+
def test_safe_lm_kwargs_cover_the_sampling_an_optimization_forwards(self):
|
|
476
|
+
"""A sampling key an optimization may forward to a provider is one a span may show."""
|
|
477
|
+
from cmpnd import helpers, optimization
|
|
478
|
+
|
|
479
|
+
assert set(optimization.SAMPLING_KEYS) <= helpers.SAFE_LM_KWARGS
|
|
480
|
+
|
|
436
481
|
@pytest.mark.parametrize("optimization_source", [None, "context", "callback"])
|
|
437
482
|
def test_on_lm_start_creates_span(self, optimization_source):
|
|
438
483
|
"""Test on_lm_start creates LM_CALL span."""
|
|
@@ -59,7 +59,7 @@ class TestDeploy:
|
|
|
59
59
|
}
|
|
60
60
|
create_response.raise_for_status = mock.MagicMock()
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# Upload slot response
|
|
63
63
|
upload_response = mock.MagicMock()
|
|
64
64
|
upload_response.status_code = 200
|
|
65
65
|
upload_response.raise_for_status = mock.MagicMock()
|
|
@@ -88,7 +88,7 @@ class TestDeploy:
|
|
|
88
88
|
signature.json.return_value = signature_response
|
|
89
89
|
signature.raise_for_status = mock.MagicMock()
|
|
90
90
|
|
|
91
|
-
# Wire up: POST = create; PUT = upload slot
|
|
91
|
+
# Wire up: POST = create; PUT = upload slot; GET = polls.
|
|
92
92
|
client.post.side_effect = [create_response]
|
|
93
93
|
client.put.side_effect = [upload_response]
|
|
94
94
|
client.get.side_effect = [*poll_responses, signature]
|
|
@@ -178,7 +178,7 @@ class TestDeploy:
|
|
|
178
178
|
|
|
179
179
|
deployment.deploy(dspy.Predict("question -> answer"))
|
|
180
180
|
|
|
181
|
-
#
|
|
181
|
+
# One PUT of the raw ZIP to the upload slot.
|
|
182
182
|
client.post.assert_called_once()
|
|
183
183
|
upload_call = client.put.call_args
|
|
184
184
|
assert "s3.amazonaws.com" in upload_call.args[0]
|
|
@@ -194,7 +194,7 @@ class TestDeploy:
|
|
|
194
194
|
|
|
195
195
|
deployment.deploy(dspy.Predict("question -> answer"))
|
|
196
196
|
|
|
197
|
-
# The create body declares the ZIP length for the size cap
|
|
197
|
+
# The create body declares the ZIP length for the size cap.
|
|
198
198
|
create_call = client.post.call_args_list[0]
|
|
199
199
|
body = create_call.kwargs["json"]
|
|
200
200
|
assert isinstance(body["declared_content_length"], int)
|
|
@@ -528,8 +528,8 @@ class TestLMDestinationForwarding:
|
|
|
528
528
|
|
|
529
529
|
A worker calls the model itself, so `deploy()` forwards both halves of the
|
|
530
530
|
destination — `litellm_model` and, when the LM carries one, `lm_api_host` from its
|
|
531
|
-
``api_base``. The host cannot ride the packaged program instead: `api_base` is in
|
|
532
|
-
`
|
|
531
|
+
``api_base``. The host cannot ride the packaged program instead: `api_base` is not in
|
|
532
|
+
`helpers.SAFE_LM_KWARGS`, so the generated source never mentions it.
|
|
533
533
|
|
|
534
534
|
Both come off ``dspy.settings.lm`` **as of the deploy call**, which is the property a
|
|
535
535
|
test that deploys under a different LM than it calls in-process depends on
|