cmpnd 0.27.0__tar.gz → 0.27.2__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.27.0 → cmpnd-0.27.2}/AGENTS.md +2 -2
- {cmpnd-0.27.0 → cmpnd-0.27.2}/PKG-INFO +1 -1
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/__init__.py +4 -1
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_authored_import.py +26 -5
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/authored.py +6 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/execution.py +67 -3
- cmpnd-0.27.2/cmpnd/lm_configuration.py +203 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimization.py +52 -3
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/packaging.py +15 -8
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/sdk-instrumentation.md +44 -1
- {cmpnd-0.27.0 → cmpnd-0.27.2}/pyproject.toml +1 -1
- cmpnd-0.27.2/scripts/release-stress.sh +14 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/_reliability.py +8 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_reliability_classify.py +12 -1
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_rlm_wasm.py +9 -11
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_execute.py +77 -1
- cmpnd-0.27.2/tests/test_lm_configuration.py +286 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_reflector_properties.py +1 -1
- cmpnd-0.27.2/tests/test_release_stress_script.py +14 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/uv.lock +1 -1
- {cmpnd-0.27.0 → cmpnd-0.27.2}/.gitignore +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/CLAUDE.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/README.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_http.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/callback.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/client.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/configuration.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/context.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/datasets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/decorators.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/deployment.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/exporter.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/helpers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/identity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/imaging.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/models.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/retry.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/cmpnd/traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/cli.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/client.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/custom-proposer.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/docs/reading-traces.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/examples/local_ollama.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/README.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/ingest/pyproject.toml +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/_fake_clock.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/__main__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/drain.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/ingest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/sweep.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_health.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/_provider.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/_result.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_authored.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_callback.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_client.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_configuration.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_context.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_datasets.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_decorators.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_deploy.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_eval.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_eval_start.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_exporter.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_identity.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_imaging.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_import_weight.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_integration.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_models.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_packaging.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_poll_until.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_project_payload.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_retry.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_schemas.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_trace_render.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_traces_read.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_truncation.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/tests/test_xxh64.py +0 -0
- {cmpnd-0.27.0 → cmpnd-0.27.2}/todo.md +0 -0
|
@@ -130,7 +130,7 @@ not a copy — see the reasoning at the top of `cmpnd/exporter_http.py`).
|
|
|
130
130
|
|
|
131
131
|
The transport sits under its own directory, with its own `pyproject.toml`
|
|
132
132
|
(`cmpnd-ingest`), so a producer that is not a DSPy program can install it without
|
|
133
|
-
dspy: its entire dependency set is httpx. The
|
|
133
|
+
dspy: its entire dependency set is httpx. The optimizer's search process
|
|
134
134
|
(`search-process/`) is that producer, and a venv unable to supply dspy is
|
|
135
135
|
what enforces its boundary. `ingest/README.md` has the full argument.
|
|
136
136
|
|
|
@@ -158,7 +158,7 @@ What that costs you, before touching either pyproject:
|
|
|
158
158
|
- `cmpnd/callback.py` - DSPy instrumentation, signature extraction
|
|
159
159
|
- `cmpnd/optimizers/tracker.py` - Optimization tracking
|
|
160
160
|
- `cmpnd/packaging.py` - Source ZIP generation for deploy (ported from the retired `deploy/local_cmpnd.py`)
|
|
161
|
-
- `cmpnd/deployment.py` - `deploy()` function:
|
|
161
|
+
- `cmpnd/deployment.py` - `deploy()` function: package the module, create the deployment on serve, PUT the bundle to the upload slot serve provisioned (a presigned S3 PUT when hosted, the worker's own slot on-prem), poll the parser
|
|
162
162
|
|
|
163
163
|
## Reflector ↔ DSPy compatibility
|
|
164
164
|
|
|
@@ -27,8 +27,9 @@ from .context import (
|
|
|
27
27
|
from .datasets import DatasetNotFoundError, VersionNotFoundError, load_dataset
|
|
28
28
|
from .decorators import start_span, trace
|
|
29
29
|
from .deployment import DeployFailed, deploy, deploy_bundle
|
|
30
|
-
from .execution import DeployedProgram, execute
|
|
30
|
+
from .execution import DeployedProgram, RetryableProviderError, execute
|
|
31
31
|
from .exporter import flush_exporter, shutdown_exporter
|
|
32
|
+
from .lm_configuration import configure_lm
|
|
32
33
|
from .mcp_servers import (
|
|
33
34
|
Bearer,
|
|
34
35
|
McpToolReference,
|
|
@@ -57,6 +58,7 @@ _auto_instrument_callback: CmpndCallback | None = None
|
|
|
57
58
|
__all__ = [
|
|
58
59
|
# Configuration
|
|
59
60
|
"configure",
|
|
61
|
+
"configure_lm",
|
|
60
62
|
"get_config",
|
|
61
63
|
"require_config",
|
|
62
64
|
# Main callback
|
|
@@ -93,6 +95,7 @@ __all__ = [
|
|
|
93
95
|
"DeployFailed",
|
|
94
96
|
"execute",
|
|
95
97
|
"DeployedProgram",
|
|
98
|
+
"RetryableProviderError",
|
|
96
99
|
# Optimize
|
|
97
100
|
"optimize",
|
|
98
101
|
"OptimizeError",
|
|
@@ -56,7 +56,9 @@ def _configured(request: dict[str, Any]) -> dict[str, int | None]:
|
|
|
56
56
|
transport while letting the file change the settings it owns, and records the two
|
|
57
57
|
trace limits the create call carries.
|
|
58
58
|
"""
|
|
59
|
-
from . import configuration
|
|
59
|
+
from . import configuration, lm_configuration
|
|
60
|
+
|
|
61
|
+
lm_configuration.begin_authored()
|
|
60
62
|
|
|
61
63
|
original_configure = configuration.configure
|
|
62
64
|
authored_limits: dict[str, int | None] = {"max_input_size": None, "max_output_size": None}
|
|
@@ -115,7 +117,6 @@ def _program_facts(
|
|
|
115
117
|
authored_limits: dict[str, int | None],
|
|
116
118
|
) -> dict[str, Any]:
|
|
117
119
|
"""Everything the deployment create call reads off the imported program."""
|
|
118
|
-
import dspy
|
|
119
120
|
|
|
120
121
|
from . import configuration, deployment, identity, mcp_servers, packaging
|
|
121
122
|
from .authored import ProgramFacts
|
|
@@ -163,7 +164,9 @@ def _program_facts(
|
|
|
163
164
|
"input_field_types": [identity.get_type_string(f.annotation) for f in signature.input_fields.values()],
|
|
164
165
|
"output_field_types": [identity.get_type_string(f.annotation) for f in signature.output_fields.values()],
|
|
165
166
|
}
|
|
166
|
-
|
|
167
|
+
from . import lm_configuration
|
|
168
|
+
|
|
169
|
+
lm = packaging._extract_lm_object(program)
|
|
167
170
|
lm_kwargs = getattr(lm, "kwargs", {}) if lm is not None else {}
|
|
168
171
|
facts = ProgramFacts(
|
|
169
172
|
program_signature_hash=identity.compute_program_hash(program),
|
|
@@ -177,6 +180,7 @@ def _program_facts(
|
|
|
177
180
|
max_output_size=authored_limits["max_output_size"],
|
|
178
181
|
tool_references=tool_references,
|
|
179
182
|
tool_declarations=tool_declarations,
|
|
183
|
+
lm_capabilities=lm_configuration.resolve({"default-lm": lm} if lm is not None else {}, require_all=False),
|
|
180
184
|
**fields,
|
|
181
185
|
)
|
|
182
186
|
return asdict(facts)
|
|
@@ -184,12 +188,20 @@ def _program_facts(
|
|
|
184
188
|
|
|
185
189
|
def _check(request: dict[str, Any]) -> dict[str, Any]:
|
|
186
190
|
"""The program bundle's answer: one import, the entrypoint's facts."""
|
|
191
|
+
from . import lm_configuration, packaging
|
|
192
|
+
|
|
187
193
|
authored_limits = _configured(request)
|
|
188
194
|
with _staged(request) as root:
|
|
189
195
|
manifest = json.loads((root / "manifest.json").read_text())
|
|
190
196
|
module = _import_member(str(manifest["program"]))
|
|
191
197
|
program, kind = _program(module, str(manifest["entrypoint"]))
|
|
192
|
-
|
|
198
|
+
facts = _program_facts(program, kind, authored_limits)
|
|
199
|
+
bindings = dict(getattr(module, "lms", None) or {})
|
|
200
|
+
default_lm = packaging._extract_lm_object(program)
|
|
201
|
+
if default_lm is not None:
|
|
202
|
+
bindings["default-lm"] = default_lm
|
|
203
|
+
facts["lm_capabilities"] = lm_configuration.resolve(bindings)
|
|
204
|
+
return {"facts": facts}
|
|
193
205
|
|
|
194
206
|
|
|
195
207
|
def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -199,7 +211,7 @@ def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
199
211
|
importing it three times would run its module scope three times — which is neither
|
|
200
212
|
what the author's own script does nor what the kernel does.
|
|
201
213
|
"""
|
|
202
|
-
from . import optimization
|
|
214
|
+
from . import lm_configuration, optimization, packaging
|
|
203
215
|
|
|
204
216
|
ask = request["optimize"]
|
|
205
217
|
authored_limits = _configured(request)
|
|
@@ -242,6 +254,7 @@ def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
242
254
|
f"Bind `{program_entrypoint} = ...` to optimize the file's own program, or pass an "
|
|
243
255
|
"existing deployment id to optimize that."
|
|
244
256
|
)
|
|
257
|
+
program = None
|
|
245
258
|
if declared_program is not None:
|
|
246
259
|
program, kind = _program(module, program_entrypoint)
|
|
247
260
|
facts = _program_facts(program, kind, authored_limits)
|
|
@@ -254,6 +267,13 @@ def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
254
267
|
optimization._check_proposer_call(declared_proposer)
|
|
255
268
|
found_proposer = proposer_entrypoint
|
|
256
269
|
|
|
270
|
+
bindings = dict(getattr(module, "lms", None) or {})
|
|
271
|
+
if program is not None:
|
|
272
|
+
default_lm = packaging._extract_lm_object(program)
|
|
273
|
+
if default_lm is not None:
|
|
274
|
+
bindings["default-lm"] = default_lm
|
|
275
|
+
declarations = lm_configuration.resolve(bindings)
|
|
276
|
+
|
|
257
277
|
work = Path(request["work_dir"])
|
|
258
278
|
trainset = _rows(module, "trainset")
|
|
259
279
|
valset = _rows(module, "valset")
|
|
@@ -266,6 +286,7 @@ def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
266
286
|
"program_entrypoint": found_program,
|
|
267
287
|
"proposer_entrypoint": found_proposer,
|
|
268
288
|
"lms": declared_lms,
|
|
289
|
+
"lm_capabilities": declarations,
|
|
269
290
|
"trainset_rows": len(trainset),
|
|
270
291
|
"valset_rows": len(valset),
|
|
271
292
|
}
|
|
@@ -59,6 +59,7 @@ class ProgramFacts:
|
|
|
59
59
|
max_output_size: int | None = None
|
|
60
60
|
tool_references: list[dict[str, Any]] = field(default_factory=list)
|
|
61
61
|
tool_declarations: list[dict[str, Any]] = field(default_factory=list)
|
|
62
|
+
lm_capabilities: dict[str, Any] = field(default_factory=dict)
|
|
62
63
|
|
|
63
64
|
@classmethod
|
|
64
65
|
def minimal(cls) -> ProgramFacts:
|
|
@@ -177,6 +178,9 @@ def _prepare(
|
|
|
177
178
|
}
|
|
178
179
|
bundle = _zip(manifest, members)
|
|
179
180
|
facts = _check_bundle(bundle, api_key=api_key, endpoint=endpoint)
|
|
181
|
+
if facts.lm_capabilities:
|
|
182
|
+
manifest["lm_capabilities"] = facts.lm_capabilities
|
|
183
|
+
bundle = _zip(manifest, members)
|
|
180
184
|
if facts.tool_declarations:
|
|
181
185
|
# The child pinned these declarations while importing this exact source.
|
|
182
186
|
# Serve checks each schema hash against the registry again on create, so
|
|
@@ -350,6 +354,8 @@ def _prepare_optimize(
|
|
|
350
354
|
manifest["proposer_entrypoint"] = facts.proposer_entrypoint
|
|
351
355
|
if program_facts is not None and program_facts.tool_declarations:
|
|
352
356
|
manifest["tools"] = program_facts.tool_declarations
|
|
357
|
+
if found.get("lm_capabilities"):
|
|
358
|
+
manifest["lm_capabilities"] = found["lm_capabilities"]
|
|
353
359
|
program_bundle = _zip(manifest, members) if facts.program_entrypoint else None
|
|
354
360
|
optimization_bundle = _zip(manifest, {**members, **produced})
|
|
355
361
|
return PreparedOptimize(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Execute deployed DSPy programs through the Cmpnd platform.
|
|
2
2
|
|
|
3
|
-
Calls the
|
|
4
|
-
|
|
3
|
+
Calls the serve execute endpoint, which forwards to the worker with scoped
|
|
4
|
+
provider access. End users only send program inputs.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
@@ -23,6 +23,55 @@ if TYPE_CHECKING:
|
|
|
23
23
|
from dspy.primitives.prediction import Prediction
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
class RetryableProviderError(HTTPStatusError):
|
|
27
|
+
"""A provider failure the platform identified as safe to retry.
|
|
28
|
+
|
|
29
|
+
``provider_status`` is absent when the provider failed before returning an HTTP
|
|
30
|
+
response, such as on a connection failure or timeout.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
message: str,
|
|
36
|
+
*,
|
|
37
|
+
request: httpx.Request,
|
|
38
|
+
response: httpx.Response,
|
|
39
|
+
provider_status: int | None,
|
|
40
|
+
attempts: int | None,
|
|
41
|
+
retry_after: int | None,
|
|
42
|
+
) -> None:
|
|
43
|
+
super().__init__(message, request=request, response=response)
|
|
44
|
+
self.provider_status = provider_status
|
|
45
|
+
self.attempts = attempts
|
|
46
|
+
self.retry_after = retry_after
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _nonnegative_int(value: object) -> int | None:
|
|
50
|
+
if isinstance(value, int) and not isinstance(value, bool) and value >= 0:
|
|
51
|
+
return value
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _retryable_provider_failure(
|
|
56
|
+
resp: httpx.Response,
|
|
57
|
+
) -> tuple[int | None, int | None, int | None] | None:
|
|
58
|
+
try:
|
|
59
|
+
body = resp.json()
|
|
60
|
+
except ValueError:
|
|
61
|
+
return None
|
|
62
|
+
if not isinstance(body, dict) or body.get("retryable") is not True:
|
|
63
|
+
return None
|
|
64
|
+
failure = body.get("failure")
|
|
65
|
+
if not isinstance(failure, dict) or failure.get("domain") != "provider":
|
|
66
|
+
return None
|
|
67
|
+
provider_status = _nonnegative_int(failure.get("upstream_status"))
|
|
68
|
+
return (
|
|
69
|
+
provider_status,
|
|
70
|
+
_nonnegative_int(failure.get("attempts")),
|
|
71
|
+
_nonnegative_int(body.get("retry_after")),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
26
75
|
class DeployedProgram:
|
|
27
76
|
"""A deployed DSPy program that can be called like a local module.
|
|
28
77
|
|
|
@@ -159,6 +208,7 @@ def execute(deployment_id, timeout=30, metadata=None, tags=None, **inputs):
|
|
|
159
208
|
TypeError: If metadata or tags is provided and is not a ``dict[str, str]``.
|
|
160
209
|
RuntimeError: If cmpnd is not configured, deployment is not ready,
|
|
161
210
|
or the provider key is missing.
|
|
211
|
+
RetryableProviderError: If the platform reports a retryable provider failure.
|
|
162
212
|
httpx.HTTPStatusError: For other HTTP errors (e.g. 502 executor error).
|
|
163
213
|
"""
|
|
164
214
|
from dspy.primitives.prediction import Prediction
|
|
@@ -200,8 +250,22 @@ def execute(deployment_id, timeout=30, metadata=None, tags=None, **inputs):
|
|
|
200
250
|
# sandbox refusal naming the missing module and the user's own file and line
|
|
201
251
|
# was arriving as a bare "Server error '500 Internal Server Error'".
|
|
202
252
|
detail = _upstream_detail(resp)
|
|
253
|
+
message = f"Execution failed with {resp.status_code} for {resp.request.url}" + (
|
|
254
|
+
f": {detail}" if detail else ""
|
|
255
|
+
)
|
|
256
|
+
provider_failure = _retryable_provider_failure(resp)
|
|
257
|
+
if provider_failure is not None:
|
|
258
|
+
provider_status, attempts, retry_after = provider_failure
|
|
259
|
+
raise RetryableProviderError(
|
|
260
|
+
message,
|
|
261
|
+
request=resp.request,
|
|
262
|
+
response=resp,
|
|
263
|
+
provider_status=provider_status,
|
|
264
|
+
attempts=attempts,
|
|
265
|
+
retry_after=retry_after,
|
|
266
|
+
)
|
|
203
267
|
raise HTTPStatusError(
|
|
204
|
-
|
|
268
|
+
message,
|
|
205
269
|
request=resp.request,
|
|
206
270
|
response=resp,
|
|
207
271
|
)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Hosted LM declarations, resolved by binding name and object identity."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, TypedDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LMConfigurationError(ValueError):
|
|
9
|
+
"""A hosted binding declaration cannot be resolved consistently."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LMCapabilities(TypedDict):
|
|
13
|
+
"""Explicit adapter hints for a hosted model, independent of its provider."""
|
|
14
|
+
|
|
15
|
+
supported_params: list[str]
|
|
16
|
+
supports_response_schema: bool
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _validate_lm_capabilities(value: object) -> dict[str, LMCapabilities]:
|
|
20
|
+
if not isinstance(value, dict):
|
|
21
|
+
raise LMConfigurationError("lm_capabilities must map exact binding names to capability declarations")
|
|
22
|
+
result: dict[str, LMCapabilities] = {}
|
|
23
|
+
for binding, declaration in value.items():
|
|
24
|
+
if not isinstance(binding, str) or not binding or binding != binding.strip():
|
|
25
|
+
raise LMConfigurationError(
|
|
26
|
+
"lm_capabilities binding names must be nonempty strings without surrounding whitespace"
|
|
27
|
+
)
|
|
28
|
+
if not isinstance(declaration, dict):
|
|
29
|
+
raise LMConfigurationError(f"lm_capabilities[{binding!r}] must be a capability declaration")
|
|
30
|
+
unknown = declaration.keys() - {"supported_params", "supports_response_schema"}
|
|
31
|
+
if unknown:
|
|
32
|
+
raise LMConfigurationError(f"lm_capabilities[{binding!r}] has unknown fields: {sorted(unknown, key=str)}")
|
|
33
|
+
params = declaration.get("supported_params", [])
|
|
34
|
+
if not isinstance(params, list) or any(not isinstance(param, str) or not param for param in params):
|
|
35
|
+
raise LMConfigurationError(
|
|
36
|
+
f"lm_capabilities[{binding!r}].supported_params must be a list of nonempty strings"
|
|
37
|
+
)
|
|
38
|
+
schema = declaration.get("supports_response_schema", False)
|
|
39
|
+
if not isinstance(schema, bool):
|
|
40
|
+
raise LMConfigurationError(f"lm_capabilities[{binding!r}].supports_response_schema must be a boolean")
|
|
41
|
+
if schema and "response_format" not in params:
|
|
42
|
+
raise LMConfigurationError(
|
|
43
|
+
f"lm_capabilities[{binding!r}] requires response_format in supported_params for schema output"
|
|
44
|
+
)
|
|
45
|
+
result[binding] = {"supported_params": list(dict.fromkeys(params)), "supports_response_schema": schema}
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_objects: list[Any] = []
|
|
50
|
+
_authored = False
|
|
51
|
+
_names: dict[str, dict[str, Any]] = {}
|
|
52
|
+
_restored: dict[str, dict[str, Any]] = {}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def reset() -> None:
|
|
56
|
+
global _authored
|
|
57
|
+
_authored = False
|
|
58
|
+
_objects.clear()
|
|
59
|
+
_names.clear()
|
|
60
|
+
_restored.clear()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def begin_authored() -> None:
|
|
64
|
+
"""Start the isolated importer or kernel scope before loading authored code."""
|
|
65
|
+
global _authored
|
|
66
|
+
reset()
|
|
67
|
+
_authored = True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _object_declaration(lm: Any) -> Any:
|
|
71
|
+
declaration = getattr(lm, "_cmpnd_lm_capabilities", None)
|
|
72
|
+
if declaration is not None and declaration[0] == id(lm):
|
|
73
|
+
return declaration[1:]
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _name(value: object) -> str:
|
|
78
|
+
if not isinstance(value, str) or not value or value != value.strip():
|
|
79
|
+
raise LMConfigurationError("an LM binding name must be a nonempty string without surrounding whitespace")
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _model(lm: Any) -> str:
|
|
84
|
+
model = lm if isinstance(lm, str) else getattr(lm, "model", None)
|
|
85
|
+
if not isinstance(model, str) or not model:
|
|
86
|
+
raise LMConfigurationError("configure_lm needs an LM object with a nonempty model name")
|
|
87
|
+
return model
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _merge(target: dict[str, Any], name: str, declaration: dict[str, Any]) -> None:
|
|
91
|
+
if name in target and target[name] != declaration:
|
|
92
|
+
raise LMConfigurationError(f"conflicting LM capability declarations for binding {name!r}")
|
|
93
|
+
target[name] = declaration
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def configure_lm(
|
|
97
|
+
lm: Any,
|
|
98
|
+
*,
|
|
99
|
+
supported_params: list[str] | None = None,
|
|
100
|
+
supports_response_schema: bool = False,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Declare capabilities once for an LM object or explicit hosted binding name.
|
|
103
|
+
|
|
104
|
+
Every binding created from the same object shares its declaration. Separate
|
|
105
|
+
objects remain independent, even when their settings match. Packaged
|
|
106
|
+
declarations retain their resolved model and cannot follow a model change.
|
|
107
|
+
"""
|
|
108
|
+
hints = dict(
|
|
109
|
+
_validate_lm_capabilities(
|
|
110
|
+
{
|
|
111
|
+
"binding": {
|
|
112
|
+
"supported_params": supported_params if supported_params is not None else [],
|
|
113
|
+
"supports_response_schema": supports_response_schema,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
)["binding"]
|
|
117
|
+
)
|
|
118
|
+
if isinstance(lm, str):
|
|
119
|
+
if not _authored:
|
|
120
|
+
raise LMConfigurationError(
|
|
121
|
+
"named LM declarations are supported only inside authored programs; pass an LM object"
|
|
122
|
+
)
|
|
123
|
+
_merge(_names, _name(lm), hints)
|
|
124
|
+
return
|
|
125
|
+
model = _model(lm)
|
|
126
|
+
declared = _object_declaration(lm)
|
|
127
|
+
if declared is not None and declared != (model, hints):
|
|
128
|
+
raise LMConfigurationError("conflicting LM capability declarations for the same LM object")
|
|
129
|
+
lm._cmpnd_lm_capabilities = (id(lm), model, hints)
|
|
130
|
+
if _authored and not any(obj is lm for obj in _objects):
|
|
131
|
+
_objects.append(lm)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def restore(declarations: object) -> None:
|
|
135
|
+
"""Load model-pinned declarations from a packaged artifact."""
|
|
136
|
+
if not isinstance(declarations, dict):
|
|
137
|
+
raise LMConfigurationError("lm_capabilities must map binding names to declarations")
|
|
138
|
+
for name, declaration in declarations.items():
|
|
139
|
+
_name(name)
|
|
140
|
+
if not isinstance(declaration, dict):
|
|
141
|
+
raise LMConfigurationError(f"lm_capabilities[{name!r}] must be a capability declaration")
|
|
142
|
+
model = _model(declaration.get("model"))
|
|
143
|
+
hints = _validate_lm_capabilities({name: {k: v for k, v in declaration.items() if k != "model"}})[name]
|
|
144
|
+
_merge(_restored, name, {"model": model, **hints})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def resolve(bindings: dict[str, Any], *, require_all: bool = True) -> dict[str, Any]:
|
|
148
|
+
"""Resolve aliases and names, rejecting conflicts before any LM call.
|
|
149
|
+
|
|
150
|
+
Packaging a program alone may defer named optimization declarations. The
|
|
151
|
+
optimization package resolves the full set and rejects names it cannot bind.
|
|
152
|
+
"""
|
|
153
|
+
result: dict[str, Any] = {}
|
|
154
|
+
for name, lm in bindings.items():
|
|
155
|
+
_name(name)
|
|
156
|
+
if name not in _restored and name not in _names and _object_declaration(lm) is None:
|
|
157
|
+
continue
|
|
158
|
+
model = _model(lm)
|
|
159
|
+
if name in _restored:
|
|
160
|
+
stored = _restored[name]
|
|
161
|
+
if stored["model"] != model:
|
|
162
|
+
raise LMConfigurationError(
|
|
163
|
+
f"model changed for binding {name!r}; configure the replacement with a new declaration"
|
|
164
|
+
)
|
|
165
|
+
_merge(result, name, dict(stored))
|
|
166
|
+
if name in _names:
|
|
167
|
+
_merge(result, name, {"model": model, **_names[name]})
|
|
168
|
+
declaration = _object_declaration(lm)
|
|
169
|
+
if declaration is not None:
|
|
170
|
+
declared_model, hints = declaration
|
|
171
|
+
if declared_model != model:
|
|
172
|
+
raise LMConfigurationError(
|
|
173
|
+
f"model changed for binding {name!r}; configure the replacement with a new declaration"
|
|
174
|
+
)
|
|
175
|
+
_merge(result, name, {"model": model, **hints})
|
|
176
|
+
if require_all:
|
|
177
|
+
unresolved = sorted((_names.keys() | _restored.keys()) - bindings.keys())
|
|
178
|
+
if unresolved:
|
|
179
|
+
raise LMConfigurationError(f"unresolved LM capability binding declarations: {', '.join(unresolved)}")
|
|
180
|
+
if any(not any(obj is lm for lm in bindings.values()) for obj in _objects):
|
|
181
|
+
raise LMConfigurationError(
|
|
182
|
+
"unresolved LM object capability declaration; pass that object as a hosted binding"
|
|
183
|
+
)
|
|
184
|
+
return result
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def resolve_call(identities: dict[str, Any], models: dict[str, Any]) -> dict[str, Any]:
|
|
188
|
+
"""Resolve the objects loaded for one call against its granted model names."""
|
|
189
|
+
declarations = resolve(identities, require_all=False)
|
|
190
|
+
unresolved = _names.keys() - identities.keys() - _restored.keys()
|
|
191
|
+
if unresolved:
|
|
192
|
+
raise LMConfigurationError(f"unresolved LM capability binding declarations: {', '.join(sorted(unresolved))}")
|
|
193
|
+
for obj in _objects:
|
|
194
|
+
if not any(obj is value for value in identities.values()):
|
|
195
|
+
raise LMConfigurationError(
|
|
196
|
+
"unresolved LM object capability declaration; pass that object as a hosted binding"
|
|
197
|
+
)
|
|
198
|
+
for name, model in models.items():
|
|
199
|
+
if name in declarations and declarations[name]["model"] != model:
|
|
200
|
+
raise LMConfigurationError(
|
|
201
|
+
f"model changed for binding {name!r}; configure the replacement with a new declaration"
|
|
202
|
+
)
|
|
203
|
+
return {name: declaration for name, declaration in declarations.items() if name in models}
|
|
@@ -658,6 +658,7 @@ def _build_optimization_bundle(
|
|
|
658
658
|
valset: list[Any],
|
|
659
659
|
metric: Callable[..., Any],
|
|
660
660
|
proposer: Callable[..., Any] | None = None,
|
|
661
|
+
lm_capabilities: dict[str, Any] | None = None,
|
|
661
662
|
) -> bytes:
|
|
662
663
|
"""Build the optimization-bundle ZIP: ``trainset.ndjson`` +
|
|
663
664
|
``valset.ndjson`` (one flattened example per line) + ``metric.py`` (the
|
|
@@ -679,6 +680,13 @@ def _build_optimization_bundle(
|
|
|
679
680
|
"runs in a sandbox from its own source, so there is nothing else to send."
|
|
680
681
|
)
|
|
681
682
|
|
|
683
|
+
if lm_capabilities:
|
|
684
|
+
preamble = "from cmpnd.lm_configuration import restore as _restore_lms\n"
|
|
685
|
+
preamble += f"_restore_lms({lm_capabilities!r})\n"
|
|
686
|
+
metric_source += "\n" + preamble
|
|
687
|
+
if proposer_source is not None:
|
|
688
|
+
proposer_source += "\n" + preamble
|
|
689
|
+
|
|
682
690
|
def _ndjson(rows: list[Any]) -> str:
|
|
683
691
|
return "\n".join(json.dumps(_example_to_dict(r)) for r in rows) + "\n"
|
|
684
692
|
|
|
@@ -919,7 +927,15 @@ def optimize(
|
|
|
919
927
|
|
|
920
928
|
cfg = configuration.require_config()
|
|
921
929
|
|
|
922
|
-
|
|
930
|
+
from . import lm_configuration
|
|
931
|
+
|
|
932
|
+
bindings = {name: entry["model"] for name, entry in (config.get("lms") or {}).items()}
|
|
933
|
+
bindings.update(lms or {})
|
|
934
|
+
default_lm = packaging._extract_lm_object(program)
|
|
935
|
+
if default_lm is not None:
|
|
936
|
+
bindings["default-lm"] = default_lm
|
|
937
|
+
declarations = lm_configuration.resolve(bindings)
|
|
938
|
+
bundle = _build_optimization_bundle(trainset, valset, metric, proposer, declarations)
|
|
923
939
|
|
|
924
940
|
if deployment_id is None:
|
|
925
941
|
deployment_id = str(deployment.deploy(program, metric=metric))
|
|
@@ -1083,9 +1099,18 @@ def optimize_authored(
|
|
|
1083
1099
|
_refuse_conflicting_members(cfg, deployment_id, prepared.members)
|
|
1084
1100
|
facts = _deployment_signature_facts(cfg, deployment_id)
|
|
1085
1101
|
|
|
1102
|
+
from . import lm_configuration
|
|
1103
|
+
|
|
1104
|
+
bindings = {name: entry["model"] for name, entry in (config.get("lms") or {}).items()}
|
|
1105
|
+
bindings.update(lms or {})
|
|
1106
|
+
if facts is not None and facts.litellm_model:
|
|
1107
|
+
bindings["default-lm"] = facts.litellm_model
|
|
1108
|
+
declarations = lm_configuration.resolve(bindings)
|
|
1109
|
+
bundle = _authored_capabilities_bundle(prepared.optimization_bundle, declarations)
|
|
1110
|
+
|
|
1086
1111
|
best_deployment_id, best_score = _run_steps(
|
|
1087
1112
|
cfg,
|
|
1088
|
-
|
|
1113
|
+
bundle,
|
|
1089
1114
|
deployment_id,
|
|
1090
1115
|
optimizer=optimizer,
|
|
1091
1116
|
config=config,
|
|
@@ -1110,6 +1135,28 @@ def optimize_authored(
|
|
|
1110
1135
|
)
|
|
1111
1136
|
|
|
1112
1137
|
|
|
1138
|
+
def _authored_capabilities_bundle(bundle: bytes, declarations: dict[str, Any]) -> bytes:
|
|
1139
|
+
"""Add caller-resolved bindings without changing any authored source bytes."""
|
|
1140
|
+
if not declarations:
|
|
1141
|
+
return bundle
|
|
1142
|
+
from .lm_configuration import LMConfigurationError
|
|
1143
|
+
|
|
1144
|
+
output = io.BytesIO()
|
|
1145
|
+
with zipfile.ZipFile(io.BytesIO(bundle)) as archive:
|
|
1146
|
+
manifest = json.loads(archive.read("manifest.json"))
|
|
1147
|
+
merged = dict(manifest.get("lm_capabilities") or {})
|
|
1148
|
+
for name, declaration in declarations.items():
|
|
1149
|
+
if name in merged and merged[name] != declaration:
|
|
1150
|
+
raise LMConfigurationError(f"conflicting LM capability declarations for binding {name!r}")
|
|
1151
|
+
merged[name] = declaration
|
|
1152
|
+
manifest["lm_capabilities"] = merged
|
|
1153
|
+
with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as updated:
|
|
1154
|
+
for member in archive.infolist():
|
|
1155
|
+
body = json.dumps(manifest).encode() if member.filename == "manifest.json" else archive.read(member)
|
|
1156
|
+
updated.writestr(member, body)
|
|
1157
|
+
return output.getvalue()
|
|
1158
|
+
|
|
1159
|
+
|
|
1113
1160
|
def _deployment_signature_facts(cfg: configuration.Config, deployment_id: str) -> authored.ProgramFacts | None:
|
|
1114
1161
|
"""Read an existing deployment's signature without making metadata a run prerequisite."""
|
|
1115
1162
|
from . import identity
|
|
@@ -1120,7 +1167,8 @@ def _deployment_signature_facts(cfg: configuration.Config, deployment_id: str) -
|
|
|
1120
1167
|
response = client.get(f"{cfg.endpoint}/api/v1/deployments/{deployment_id}", headers=headers)
|
|
1121
1168
|
if not response.is_success:
|
|
1122
1169
|
return None
|
|
1123
|
-
|
|
1170
|
+
deployment_row = response.json()
|
|
1171
|
+
value = deployment_row.get("program_signature_hash")
|
|
1124
1172
|
if value is None or value == "":
|
|
1125
1173
|
return None
|
|
1126
1174
|
# Deployment responses use the 16-digit hex display form. Signature routes
|
|
@@ -1133,6 +1181,7 @@ def _deployment_signature_facts(cfg: configuration.Config, deployment_id: str) -
|
|
|
1133
1181
|
row = response.json()
|
|
1134
1182
|
return authored.ProgramFacts(
|
|
1135
1183
|
program_signature_hash=signature_hash,
|
|
1184
|
+
litellm_model=deployment_row.get("litellm_model"),
|
|
1136
1185
|
signature_name=row.get("signature_name"),
|
|
1137
1186
|
input_fields=row.get("input_fields") or [],
|
|
1138
1187
|
output_fields=row.get("output_fields") or [],
|
|
@@ -34,12 +34,11 @@ if TYPE_CHECKING:
|
|
|
34
34
|
import dspy
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
"""
|
|
37
|
+
def _extract_lm_object(module: dspy.Module) -> dspy.LM | None:
|
|
38
|
+
"""Find the program's LM object without losing its identity.
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
to ``dspy.settings.lm`` if no module-level LM exists.
|
|
40
|
+
Walk the module tree for a non-None ``.lm`` attribute, then fall back to
|
|
41
|
+
``dspy.settings.lm`` if no module-level LM exists.
|
|
43
42
|
"""
|
|
44
43
|
seen: set[int] = set()
|
|
45
44
|
|
|
@@ -62,9 +61,12 @@ def _extract_lm_state(module: dspy.Module) -> dict[str, typing.Any] | None:
|
|
|
62
61
|
lm = _find_lm(module)
|
|
63
62
|
if lm is None:
|
|
64
63
|
lm = dspy.settings.lm
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
return lm
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _extract_lm_state(module: dspy.Module) -> dict[str, typing.Any] | None:
|
|
68
|
+
lm = _extract_lm_object(module)
|
|
69
|
+
return lm.dump_state() if lm is not None and hasattr(lm, "dump_state") else None
|
|
68
70
|
|
|
69
71
|
|
|
70
72
|
class ImportEntry(typing.NamedTuple):
|
|
@@ -1248,7 +1250,12 @@ def generate_source_zip(
|
|
|
1248
1250
|
if metric_source is not None:
|
|
1249
1251
|
metric_filename = "metric_source.py"
|
|
1250
1252
|
|
|
1253
|
+
from . import lm_configuration
|
|
1254
|
+
|
|
1255
|
+
lm = _extract_lm_object(module)
|
|
1256
|
+
declarations = lm_configuration.resolve({"default-lm": lm} if lm is not None else {}, require_all=False)
|
|
1251
1257
|
manifest = {
|
|
1258
|
+
"lm_capabilities": declarations,
|
|
1252
1259
|
"dependency_versions": {
|
|
1253
1260
|
"python": platform.python_version(),
|
|
1254
1261
|
"dspy": dspy.__version__,
|