cmpnd 0.44.0__tar.gz → 0.46.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cmpnd-0.44.0 → cmpnd-0.46.0}/PKG-INFO +1 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/__init__.py +6 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_authored_import.py +2 -4
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_http.py +45 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/callback.py +6 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/dataset_sync.py +2 -4
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/datasets.py +2 -4
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/deployment.py +20 -8
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/deployment_runs.py +1 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/lm_configuration.py +68 -4
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/mcp_servers.py +2 -3
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimization.py +35 -21
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/cli.md +10 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/sdk-instrumentation.md +30 -0
- cmpnd-0.46.0/ingest/cmpnd_ingest/client_floor.py +101 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/exporter_http.py +4 -3
- {cmpnd-0.44.0 → cmpnd-0.46.0}/pyproject.toml +1 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/conftest.py +13 -12
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/hosted_optimize_probe.py +58 -0
- cmpnd-0.46.0/tests/test_client_floor.py +181 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_dataset_sync.py +5 -5
- cmpnd-0.46.0/tests/test_decision_client_spans.py +144 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deploy.py +48 -4
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize.py +93 -5
- cmpnd-0.46.0/tests/test_system_one.py +106 -0
- cmpnd-0.46.0/tests/test_upload_auth_headers.py +59 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/uv.lock +1 -1
- {cmpnd-0.44.0 → cmpnd-0.46.0}/.gitignore +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/AGENTS.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/CLAUDE.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/README.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/client.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/context.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/models.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/references.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/client.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/README.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/conftest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/drain.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_authored.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_callback.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_client.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_context.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_eval.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_execute.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_integration.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_models.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_retry.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.44.0 → cmpnd-0.46.0}/tests/test_xxh64.py +0 -0
|
@@ -15,6 +15,8 @@ Usage:
|
|
|
15
15
|
import importlib.metadata
|
|
16
16
|
from typing import TYPE_CHECKING
|
|
17
17
|
|
|
18
|
+
from cmpnd_ingest.client_floor import UpgradeRequiredWarning
|
|
19
|
+
|
|
18
20
|
from . import authored, programs, traces
|
|
19
21
|
from ._http import ApiError
|
|
20
22
|
from .callback import CmpndCallback, trace_id
|
|
@@ -46,7 +48,7 @@ from .deployment_runs import (
|
|
|
46
48
|
)
|
|
47
49
|
from .execution import DeployedProgram, RetryableProviderError, deployment_run_id, execute
|
|
48
50
|
from .exporter import flush_exporter, shutdown_exporter
|
|
49
|
-
from .lm_configuration import configure_lm
|
|
51
|
+
from .lm_configuration import SystemOne, configure_lm
|
|
50
52
|
from .mcp_servers import (
|
|
51
53
|
Bearer,
|
|
52
54
|
McpToolReference,
|
|
@@ -80,6 +82,7 @@ __all__ = [
|
|
|
80
82
|
# Configuration
|
|
81
83
|
"configure",
|
|
82
84
|
"configure_lm",
|
|
85
|
+
"SystemOne",
|
|
83
86
|
"get_config",
|
|
84
87
|
"require_config",
|
|
85
88
|
# Main callback
|
|
@@ -110,6 +113,8 @@ __all__ = [
|
|
|
110
113
|
"program",
|
|
111
114
|
"authored",
|
|
112
115
|
"ApiError",
|
|
116
|
+
# Raised once when the server no longer supports this release
|
|
117
|
+
"UpgradeRequiredWarning",
|
|
113
118
|
# Types
|
|
114
119
|
"SpanType",
|
|
115
120
|
"TraceStatus",
|
|
@@ -118,16 +118,14 @@ def _program_facts(
|
|
|
118
118
|
) -> dict[str, Any]:
|
|
119
119
|
"""Everything the deployment create call reads off the imported program."""
|
|
120
120
|
|
|
121
|
-
from . import configuration, deployment, identity, mcp_servers, packaging
|
|
121
|
+
from . import _http, configuration, deployment, identity, mcp_servers, packaging
|
|
122
122
|
from .authored import ProgramFacts
|
|
123
123
|
|
|
124
124
|
references = deployment._mcp_tool_references(program)
|
|
125
125
|
pinned: dict[tuple[str, str], Any] = {}
|
|
126
126
|
if references:
|
|
127
|
-
import httpx
|
|
128
|
-
|
|
129
127
|
config = configuration.require_config()
|
|
130
|
-
with
|
|
128
|
+
with _http.new_client(timeout=30) as client:
|
|
131
129
|
pinned = deployment._resolve_mcp_tool_references(client, config, references)
|
|
132
130
|
tool_references = [
|
|
133
131
|
{
|
|
@@ -13,8 +13,10 @@ from __future__ import annotations
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
15
|
from typing import Any
|
|
16
|
+
from urllib.parse import urlsplit
|
|
16
17
|
|
|
17
18
|
import httpx
|
|
19
|
+
from cmpnd_ingest import client_floor
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class ApiError(Exception):
|
|
@@ -76,6 +78,48 @@ def server_detail(response: httpx.Response) -> str:
|
|
|
76
78
|
return (response.text or "").strip()[:2000]
|
|
77
79
|
|
|
78
80
|
|
|
81
|
+
def new_client(**kwargs: Any) -> httpx.Client:
|
|
82
|
+
"""An ``httpx.Client`` that warns when the server no longer supports this
|
|
83
|
+
release (:mod:`cmpnd_ingest.client_floor`).
|
|
84
|
+
|
|
85
|
+
Every client the SDK builds comes from here, so every answer the platform
|
|
86
|
+
sends gets the check. A caller's own ``event_hooks`` run after it.
|
|
87
|
+
"""
|
|
88
|
+
hooks = client_floor.event_hooks()
|
|
89
|
+
for event, callers in (kwargs.pop("event_hooks", None) or {}).items():
|
|
90
|
+
hooks.setdefault(event, []).extend(callers)
|
|
91
|
+
return httpx.Client(event_hooks=hooks, **kwargs)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
_DEFAULT_PORTS = {"http": 80, "https": 443}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _origin(url: str) -> tuple[str, str, int] | None:
|
|
98
|
+
"""A URL's (scheme, host, port), or None when it cannot be read as one."""
|
|
99
|
+
try:
|
|
100
|
+
parts = urlsplit(url)
|
|
101
|
+
port = parts.port
|
|
102
|
+
except ValueError:
|
|
103
|
+
return None
|
|
104
|
+
scheme = parts.scheme.lower()
|
|
105
|
+
if scheme not in _DEFAULT_PORTS or not parts.hostname:
|
|
106
|
+
return None
|
|
107
|
+
return scheme, parts.hostname.lower(), port if port is not None else _DEFAULT_PORTS[scheme]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def upload_auth_headers(upload_url: str, endpoint: str, api_key: str) -> dict[str, str]:
|
|
111
|
+
"""The authentication header for a PUT to an ``upload_url`` serve advertised.
|
|
112
|
+
|
|
113
|
+
Serve authenticates its upload routes with the API key. The SDK sends the URL
|
|
114
|
+
as given, so the key goes only to a URL on the configured endpoint's origin:
|
|
115
|
+
the same scheme, host and port. Any other origin gets no key.
|
|
116
|
+
"""
|
|
117
|
+
own = _origin(endpoint)
|
|
118
|
+
if not api_key or own is None or _origin(upload_url) != own:
|
|
119
|
+
return {}
|
|
120
|
+
return {"X-API-Key": api_key}
|
|
121
|
+
|
|
122
|
+
|
|
79
123
|
class ApiClient:
|
|
80
124
|
"""Thin HTTP client for the cmpnd backend API."""
|
|
81
125
|
|
|
@@ -150,7 +194,7 @@ class ApiClient:
|
|
|
150
194
|
url = f"{self._endpoint}{path}"
|
|
151
195
|
timeout = self.configured_timeout()
|
|
152
196
|
try:
|
|
153
|
-
with
|
|
197
|
+
with new_client(
|
|
154
198
|
headers={"X-API-Key": self._api_key, "Content-Type": "application/json"},
|
|
155
199
|
timeout=timeout,
|
|
156
200
|
) as client:
|
|
@@ -383,6 +383,10 @@ def _lm15_cache_hit(entry: Any, response: Any) -> bool | None:
|
|
|
383
383
|
|
|
384
384
|
def _usage_dialect(instance: Any, usage: Any) -> str | None:
|
|
385
385
|
"""Name the response surface only when its shape establishes one."""
|
|
386
|
+
# A decision client (DSPy's `TypeSafe`) speaks System One. Its usage names chat's token
|
|
387
|
+
# fields, so the shape alone would misname it. DSPy dispatches on the same flag.
|
|
388
|
+
if getattr(instance, "supports_decision_requests", False) is True:
|
|
389
|
+
return "system_one"
|
|
386
390
|
model_type = _text_field(instance, "model_type")
|
|
387
391
|
if model_type == "responses":
|
|
388
392
|
return "responses"
|
|
@@ -1489,7 +1493,8 @@ class _LMMixin(_CallbackBase):
|
|
|
1489
1493
|
observation.usage_dialect = _usage_dialect(instance, usage)
|
|
1490
1494
|
if observation.usage_dialect in {"chat_completions", "responses"}:
|
|
1491
1495
|
observation.prompt_inclusion = "includes_cache_reads"
|
|
1492
|
-
elif observation.usage_dialect
|
|
1496
|
+
elif observation.usage_dialect in {"converse", "system_one"}:
|
|
1497
|
+
# System One serves nothing from a cache, so its prompt is all fresh input.
|
|
1493
1498
|
observation.prompt_inclusion = "excludes_cache_reads"
|
|
1494
1499
|
|
|
1495
1500
|
if usage_reported and usage is not None:
|
|
@@ -10,9 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import logging
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
from . import configuration, identity, models
|
|
13
|
+
from . import _http, configuration, identity, models
|
|
16
14
|
|
|
17
15
|
logger = logging.getLogger(__name__)
|
|
18
16
|
|
|
@@ -37,7 +35,7 @@ def check_dataset_exists(dataset_hash: str) -> bool:
|
|
|
37
35
|
url = f"{config.endpoint}/api/v1/datasets/check/{dataset_hash}"
|
|
38
36
|
|
|
39
37
|
try:
|
|
40
|
-
with
|
|
38
|
+
with _http.new_client(timeout=5.0) as client:
|
|
41
39
|
response = client.get(
|
|
42
40
|
url,
|
|
43
41
|
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
@@ -8,9 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
from . import configuration
|
|
11
|
+
from . import _http, configuration
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
class DatasetNotFoundError(Exception):
|
|
@@ -114,7 +112,7 @@ def _fetch_dataset(
|
|
|
114
112
|
params["version"] = str(version)
|
|
115
113
|
|
|
116
114
|
# Make request to backend
|
|
117
|
-
with
|
|
115
|
+
with _http.new_client(timeout=30.0) as client:
|
|
118
116
|
response = client.get(
|
|
119
117
|
url,
|
|
120
118
|
params=params,
|
|
@@ -17,7 +17,18 @@ from typing import Any
|
|
|
17
17
|
|
|
18
18
|
import httpx
|
|
19
19
|
|
|
20
|
-
from . import
|
|
20
|
+
from . import (
|
|
21
|
+
_http,
|
|
22
|
+
authored,
|
|
23
|
+
configuration,
|
|
24
|
+
deployment_metadata,
|
|
25
|
+
execution,
|
|
26
|
+
identity,
|
|
27
|
+
mcp_servers,
|
|
28
|
+
models,
|
|
29
|
+
packaging,
|
|
30
|
+
retry,
|
|
31
|
+
)
|
|
21
32
|
|
|
22
33
|
logger = logging.getLogger(__name__)
|
|
23
34
|
|
|
@@ -427,7 +438,7 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
|
427
438
|
references = _mcp_tool_references(module)
|
|
428
439
|
pinned_mcp_tools: dict[tuple[str, str], Any] = {}
|
|
429
440
|
if references:
|
|
430
|
-
with
|
|
441
|
+
with _http.new_client(timeout=httpx.Timeout(_TIMEOUT_SECONDS, connect=_CONNECT_TIMEOUT_SECONDS)) as client:
|
|
431
442
|
pinned_mcp_tools = _resolve_mcp_tool_references(client, config, references)
|
|
432
443
|
|
|
433
444
|
# Package the module
|
|
@@ -539,7 +550,7 @@ def _deploy_prepared(
|
|
|
539
550
|
"""Create, upload, and poll one fully prepared source bundle."""
|
|
540
551
|
headers = {"X-API-Key": config.api_key, "Content-Type": "application/json"}
|
|
541
552
|
|
|
542
|
-
with
|
|
553
|
+
with _http.new_client(timeout=httpx.Timeout(_TIMEOUT_SECONDS, connect=_CONNECT_TIMEOUT_SECONDS)) as client:
|
|
543
554
|
created = _send_retrying(
|
|
544
555
|
lambda: client.post(
|
|
545
556
|
f"{config.endpoint}/api/v1/deployments",
|
|
@@ -568,12 +579,13 @@ def _deploy_prepared(
|
|
|
568
579
|
)
|
|
569
580
|
for warning in answer.get("warnings") or []:
|
|
570
581
|
logger.warning("deploy: %s", warning)
|
|
582
|
+
upload_url = answer["upload_url"]
|
|
583
|
+
upload_headers = {
|
|
584
|
+
"Content-Type": "application/zip",
|
|
585
|
+
**_http.upload_auth_headers(upload_url, config.endpoint, config.api_key),
|
|
586
|
+
}
|
|
571
587
|
uploaded = _send_retrying(
|
|
572
|
-
lambda: client.put(
|
|
573
|
-
answer["upload_url"],
|
|
574
|
-
content=zip_bytes,
|
|
575
|
-
headers={"Content-Type": "application/zip"},
|
|
576
|
-
),
|
|
588
|
+
lambda: client.put(upload_url, content=zip_bytes, headers=upload_headers),
|
|
577
589
|
"upload deployment ZIP",
|
|
578
590
|
may_resend_with_no_answer=lambda _: True,
|
|
579
591
|
may_resend_after_answer=_repeatable_answer,
|
|
@@ -327,7 +327,7 @@ class _RunClient:
|
|
|
327
327
|
self.retry_after = 0.0
|
|
328
328
|
|
|
329
329
|
def __enter__(self) -> _RunClient:
|
|
330
|
-
self.client =
|
|
330
|
+
self.client = _http.new_client(headers={"X-API-Key": self.api_key})
|
|
331
331
|
return self
|
|
332
332
|
|
|
333
333
|
def __exit__(self, *_: object) -> None:
|
|
@@ -9,11 +9,25 @@ class LMConfigurationError(ValueError):
|
|
|
9
9
|
"""A hosted binding declaration cannot be resolved consistently."""
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class LMCapabilities(TypedDict):
|
|
13
|
-
"""Explicit adapter hints for a hosted model, independent of its provider.
|
|
12
|
+
class LMCapabilities(TypedDict, total=False):
|
|
13
|
+
"""Explicit adapter hints for a hosted model, independent of its provider.
|
|
14
|
+
|
|
15
|
+
``dialect`` is present only for a System One binding, which :class:`SystemOne`
|
|
16
|
+
writes. Absent, the binding is a chat model.
|
|
17
|
+
"""
|
|
14
18
|
|
|
15
19
|
supported_params: list[str]
|
|
16
20
|
supports_response_schema: bool
|
|
21
|
+
dialect: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# The wire protocol a binding's model speaks, named for the protocol and never for a
|
|
25
|
+
# provider or a client library: a chat model answers chat completions, and a System One
|
|
26
|
+
# model (TypeSafe's System One protocol, which other providers serve too) answers a set
|
|
27
|
+
# of questions about a state.
|
|
28
|
+
CHAT_COMPLETIONS = "chat_completions"
|
|
29
|
+
SYSTEM_ONE = "system_one"
|
|
30
|
+
_DIALECTS = frozenset({CHAT_COMPLETIONS, SYSTEM_ONE})
|
|
17
31
|
|
|
18
32
|
|
|
19
33
|
def _validate_lm_capabilities(value: object) -> dict[str, LMCapabilities]:
|
|
@@ -27,7 +41,7 @@ def _validate_lm_capabilities(value: object) -> dict[str, LMCapabilities]:
|
|
|
27
41
|
)
|
|
28
42
|
if not isinstance(declaration, dict):
|
|
29
43
|
raise LMConfigurationError(f"lm_capabilities[{binding!r}] must be a capability declaration")
|
|
30
|
-
unknown = declaration.keys() - {"supported_params", "supports_response_schema"}
|
|
44
|
+
unknown = declaration.keys() - {"supported_params", "supports_response_schema", "dialect"}
|
|
31
45
|
if unknown:
|
|
32
46
|
raise LMConfigurationError(f"lm_capabilities[{binding!r}] has unknown fields: {sorted(unknown, key=str)}")
|
|
33
47
|
params = declaration.get("supported_params", [])
|
|
@@ -38,11 +52,24 @@ def _validate_lm_capabilities(value: object) -> dict[str, LMCapabilities]:
|
|
|
38
52
|
schema = declaration.get("supports_response_schema", False)
|
|
39
53
|
if not isinstance(schema, bool):
|
|
40
54
|
raise LMConfigurationError(f"lm_capabilities[{binding!r}].supports_response_schema must be a boolean")
|
|
55
|
+
dialect = declaration.get("dialect", CHAT_COMPLETIONS)
|
|
56
|
+
if dialect not in _DIALECTS:
|
|
57
|
+
raise LMConfigurationError(
|
|
58
|
+
f"lm_capabilities[{binding!r}].dialect must be one of {sorted(_DIALECTS)} (got {dialect!r})"
|
|
59
|
+
)
|
|
60
|
+
if dialect == SYSTEM_ONE and (params or schema):
|
|
61
|
+
raise LMConfigurationError(
|
|
62
|
+
f"lm_capabilities[{binding!r}] is a System One binding, which takes no sampling "
|
|
63
|
+
"parameters and no response schema"
|
|
64
|
+
)
|
|
41
65
|
if schema and "response_format" not in params:
|
|
42
66
|
raise LMConfigurationError(
|
|
43
67
|
f"lm_capabilities[{binding!r}] requires response_format in supported_params for schema output"
|
|
44
68
|
)
|
|
45
|
-
|
|
69
|
+
hints: LMCapabilities = {"supported_params": list(dict.fromkeys(params)), "supports_response_schema": schema}
|
|
70
|
+
if dialect == SYSTEM_ONE:
|
|
71
|
+
hints["dialect"] = SYSTEM_ONE
|
|
72
|
+
result[binding] = hints
|
|
46
73
|
return result
|
|
47
74
|
|
|
48
75
|
|
|
@@ -122,6 +149,11 @@ def configure_lm(
|
|
|
122
149
|
)
|
|
123
150
|
_merge(_names, _name(lm), hints)
|
|
124
151
|
return
|
|
152
|
+
_declare(lm, hints)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _declare(lm: Any, hints: dict[str, Any]) -> None:
|
|
156
|
+
"""Attach a declaration to one LM object, shared by every binding made from it."""
|
|
125
157
|
model = _model(lm)
|
|
126
158
|
declared = _object_declaration(lm)
|
|
127
159
|
if declared is not None and declared != (model, hints):
|
|
@@ -131,6 +163,38 @@ def configure_lm(
|
|
|
131
163
|
_objects.append(lm)
|
|
132
164
|
|
|
133
165
|
|
|
166
|
+
class SystemOne:
|
|
167
|
+
"""A System One decision model, for a run's ``lms``.
|
|
168
|
+
|
|
169
|
+
A System One model answers a set of questions about a state instead of continuing a
|
|
170
|
+
conversation: TypeSafe's Jev, or another endpoint serving the same schema. A hosted
|
|
171
|
+
judge built on ``dspy.experimental.TypeSafe`` runs against the run's System One
|
|
172
|
+
binding without naming a key or a URL::
|
|
173
|
+
|
|
174
|
+
cmpnd.optimize(..., lms={"judge": cmpnd.SystemOne("typesafe/jev-latest")})
|
|
175
|
+
|
|
176
|
+
``model`` is a platform model id: the prefix names the organization's sealed
|
|
177
|
+
credential, and the rest is the model at that credential's endpoint. Writing the
|
|
178
|
+
binding this way is the only thing that makes it a System One binding; a model name
|
|
179
|
+
on its own never does.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self, model: object):
|
|
183
|
+
if not isinstance(model, str) or not model.strip():
|
|
184
|
+
raise ValueError(f"cmpnd.SystemOne needs a model id such as 'typesafe/jev-latest' (got {model!r})")
|
|
185
|
+
credential, _, name = model.strip().partition("/")
|
|
186
|
+
if not credential or not name:
|
|
187
|
+
raise ValueError(
|
|
188
|
+
f"cmpnd.SystemOne({model!r}) names no credential: write the model as "
|
|
189
|
+
"'<credential>/<model>', for example 'typesafe/jev-latest'"
|
|
190
|
+
)
|
|
191
|
+
self.model = model.strip()
|
|
192
|
+
_declare(self, {"supported_params": [], "supports_response_schema": False, "dialect": SYSTEM_ONE})
|
|
193
|
+
|
|
194
|
+
def __repr__(self) -> str:
|
|
195
|
+
return f"cmpnd.SystemOne({self.model!r})"
|
|
196
|
+
|
|
197
|
+
|
|
134
198
|
def restore(declarations: object) -> None:
|
|
135
199
|
"""Load model-pinned declarations from a packaged artifact."""
|
|
136
200
|
if not isinstance(declarations, dict):
|
|
@@ -669,15 +669,14 @@ def _registered_tools(url: str, timeout_s: float) -> list[PinnedTool] | None | o
|
|
|
669
669
|
the registry route, so notebook use keeps its existing direct connection.
|
|
670
670
|
``None`` means the configured registry answered and does not know this server.
|
|
671
671
|
"""
|
|
672
|
-
from . import configuration
|
|
672
|
+
from . import _http, configuration
|
|
673
673
|
|
|
674
674
|
config = configuration.get_config()
|
|
675
675
|
if config is None:
|
|
676
676
|
return _NO_PLATFORM_REGISTRY
|
|
677
|
-
import httpx
|
|
678
677
|
|
|
679
678
|
headers = {"X-API-Key": config.api_key}
|
|
680
|
-
with
|
|
679
|
+
with _http.new_client(timeout=timeout_s) as client:
|
|
681
680
|
listing = client.get(f"{config.endpoint}/api/v1/org/tool-servers", headers=headers)
|
|
682
681
|
if listing.status_code == 404:
|
|
683
682
|
return _NO_PLATFORM_REGISTRY
|
|
@@ -462,6 +462,23 @@ def _lms_config(config: dict[str, Any], lms: Any) -> None:
|
|
|
462
462
|
if isinstance(lm, str):
|
|
463
463
|
declared[name] = {"model": lm}
|
|
464
464
|
continue
|
|
465
|
+
from .lm_configuration import SystemOne
|
|
466
|
+
|
|
467
|
+
if isinstance(lm, SystemOne):
|
|
468
|
+
# A System One model takes no sampling, and its declaration (which says it
|
|
469
|
+
# is one) travels in the bundle rather than on the wire.
|
|
470
|
+
declared[name] = {"model": lm.model}
|
|
471
|
+
continue
|
|
472
|
+
if getattr(lm, "supports_decision_requests", False):
|
|
473
|
+
# dspy.experimental.TypeSafe, or a client like it: it carries a key and a URL
|
|
474
|
+
# rather than the name of a sealed credential, so the platform has nothing to
|
|
475
|
+
# resolve. Duck-typed so this module never imports dspy at scope.
|
|
476
|
+
raise ValueError(
|
|
477
|
+
f"{subject} is a {type(lm).__name__} client, which names a key and a URL rather "
|
|
478
|
+
f"than a credential. Bind the model with cmpnd.SystemOne, for example "
|
|
479
|
+
f"lms={{{name!r}: cmpnd.SystemOne('typesafe/jev-latest')}}; a judge that builds "
|
|
480
|
+
f"its own {type(lm).__name__} runs against that binding when hosted"
|
|
481
|
+
)
|
|
465
482
|
model = _lm_model(lm, subject)
|
|
466
483
|
_refuse_credential_on(lm, model, subject)
|
|
467
484
|
entry: dict[str, Any] = {"model": model}
|
|
@@ -991,7 +1008,7 @@ def _run_steps(
|
|
|
991
1008
|
current_deployment_id = deployment_id
|
|
992
1009
|
non_improving = 0
|
|
993
1010
|
|
|
994
|
-
with
|
|
1011
|
+
with _http.new_client(timeout=_HTTP_TIMEOUT_SECONDS) as client:
|
|
995
1012
|
for step in range(max_steps):
|
|
996
1013
|
data = "upload" if step == 0 else "reuse"
|
|
997
1014
|
step_config = dict(config)
|
|
@@ -1014,7 +1031,7 @@ def _run_steps(
|
|
|
1014
1031
|
optimization_id = initiated["optimization_id"]
|
|
1015
1032
|
|
|
1016
1033
|
if data == "upload":
|
|
1017
|
-
_upload_bundle(client, initiated, bundle)
|
|
1034
|
+
_upload_bundle(client, initiated, bundle, endpoint=cfg.endpoint, api_key=cfg.api_key)
|
|
1018
1035
|
|
|
1019
1036
|
status = _poll_until_terminal(
|
|
1020
1037
|
client,
|
|
@@ -1226,7 +1243,7 @@ def _refuse_conflicting_members(
|
|
|
1226
1243
|
headers = {"X-API-Key": cfg.api_key}
|
|
1227
1244
|
conflicting: list[str] = []
|
|
1228
1245
|
try:
|
|
1229
|
-
with
|
|
1246
|
+
with _http.new_client(timeout=_HTTP_TIMEOUT_SECONDS) as client:
|
|
1230
1247
|
listing = client.get(f"{cfg.endpoint}/api/v1/deployments/{deployment_id}/source", headers=headers)
|
|
1231
1248
|
if not listing.is_success:
|
|
1232
1249
|
return
|
|
@@ -1397,26 +1414,21 @@ def _initiate_step(
|
|
|
1397
1414
|
)
|
|
1398
1415
|
|
|
1399
1416
|
|
|
1400
|
-
def _upload_bundle(
|
|
1417
|
+
def _upload_bundle(
|
|
1418
|
+
client: httpx.Client, initiated: dict[str, Any], bundle: bytes, *, endpoint: str, api_key: str
|
|
1419
|
+
) -> None:
|
|
1401
1420
|
"""Upload the optimization bundle to the slot the initiate step returned.
|
|
1402
1421
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
- **PUT** (the on-prem worker-sandbox): no form fields — the URL *is* the
|
|
1410
|
-
slot. The bundle is the raw PUT body.
|
|
1411
|
-
|
|
1412
|
-
serve normalizes the awaiting_data response so both keys are always present
|
|
1413
|
-
(``upload_method`` defaulting to PUT, ``upload_fields`` to ``{}``), so the
|
|
1414
|
-
branch here is just "are there presigned fields to post."
|
|
1422
|
+
Serve answers an ``awaiting_data`` initiate with ``upload_url``, ``upload_method:
|
|
1423
|
+
"PUT"`` and empty ``upload_fields``, and the bundle goes out as the raw PUT body.
|
|
1424
|
+
Serve authenticates that route with the API key, which rides the PUT only when
|
|
1425
|
+
``upload_url`` is on ``endpoint``'s origin (``_http.upload_auth_headers``). A
|
|
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.
|
|
1415
1428
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
transport blip alike.
|
|
1429
|
+
The slot pins its destination, so re-sending the same bytes writes the same
|
|
1430
|
+
object. That makes this call idempotent, so it rides through a transient answer
|
|
1431
|
+
and a transport blip alike.
|
|
1420
1432
|
"""
|
|
1421
1433
|
upload_url = initiated.get("upload_url")
|
|
1422
1434
|
if not upload_url:
|
|
@@ -1424,6 +1436,7 @@ def _upload_bundle(client: httpx.Client, initiated: dict[str, Any], bundle: byte
|
|
|
1424
1436
|
upload_fields = initiated.get("upload_fields")
|
|
1425
1437
|
method = (initiated.get("upload_method") or ("POST" if upload_fields else "PUT")).upper()
|
|
1426
1438
|
what = "upload optimization bundle"
|
|
1439
|
+
auth = _http.upload_auth_headers(upload_url, endpoint, api_key)
|
|
1427
1440
|
|
|
1428
1441
|
if upload_fields:
|
|
1429
1442
|
resp = _request(
|
|
@@ -1431,6 +1444,7 @@ def _upload_bundle(client: httpx.Client, initiated: dict[str, Any], bundle: byte
|
|
|
1431
1444
|
upload_url,
|
|
1432
1445
|
data=dict(upload_fields),
|
|
1433
1446
|
files={"file": ("data.zip", bundle, "application/zip")},
|
|
1447
|
+
headers=auth,
|
|
1434
1448
|
),
|
|
1435
1449
|
what,
|
|
1436
1450
|
)
|
|
@@ -1440,7 +1454,7 @@ def _upload_bundle(client: httpx.Client, initiated: dict[str, Any], bundle: byte
|
|
|
1440
1454
|
method,
|
|
1441
1455
|
upload_url,
|
|
1442
1456
|
content=bundle,
|
|
1443
|
-
headers={"Content-Type": "application/zip"},
|
|
1457
|
+
headers={"Content-Type": "application/zip", **auth},
|
|
1444
1458
|
),
|
|
1445
1459
|
what,
|
|
1446
1460
|
)
|
|
@@ -32,6 +32,16 @@ retries, because a second send would create a second program.
|
|
|
32
32
|
`cmpnd.programs.create(name=...)` makes the same call from Python and returns
|
|
33
33
|
the row.
|
|
34
34
|
|
|
35
|
+
## When the server no longer supports your release
|
|
36
|
+
|
|
37
|
+
Every answer from the platform names the oldest `cmpnd` release it supports.
|
|
38
|
+
When yours is older, the CLI and the SDK warn once per process and name the
|
|
39
|
+
upgrade, `pip install -U cmpnd`. The server still answers, so an older release
|
|
40
|
+
keeps working wherever nothing it relies on has changed. To stay on an older
|
|
41
|
+
release without the warning, set `CMPND_NO_UPGRADE_WARNING=1`. In code,
|
|
42
|
+
`warnings.filterwarnings("ignore", category=cmpnd.UpgradeRequiredWarning)`
|
|
43
|
+
does the same.
|
|
44
|
+
|
|
35
45
|
## Deploy flow (Docker-style)
|
|
36
46
|
|
|
37
47
|
These commands print human-readable output by default. Each takes `--json` for
|
|
@@ -73,6 +73,27 @@ endpoint accept JSON schemas. Undeclared bindings keep prompt-only JSON. These
|
|
|
73
73
|
hints do not add a format the provider cannot accept or change local DSPy's
|
|
74
74
|
capability lookup.
|
|
75
75
|
|
|
76
|
+
### System One judges
|
|
77
|
+
|
|
78
|
+
A System One model answers a set of questions about a state instead of continuing
|
|
79
|
+
a conversation. TypeSafe's Jev is one, and other endpoints serve the same schema.
|
|
80
|
+
Declare one for a run with `cmpnd.SystemOne`:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
cmpnd.optimize(..., lms={"judge": cmpnd.SystemOne("typesafe/jev-latest")})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The prefix names the organization's sealed credential and the rest names the
|
|
87
|
+
model at that credential's endpoint. Writing the binding this way is the only
|
|
88
|
+
thing that makes it a System One binding; a model name never does. The binding
|
|
89
|
+
carries no sampling, and its declaration travels in the bundle like any other.
|
|
90
|
+
|
|
91
|
+
Hosted, a judge built on `dspy.experimental.TypeSafe` runs unchanged. The kernel
|
|
92
|
+
replaces `TypeSafe` with a client that spends the run's System One binding, so
|
|
93
|
+
the judge's own model, key and base URL are not used. With several System One
|
|
94
|
+
bindings, the judge picks one through `lms[name]`. A `TypeSafe` object passed in
|
|
95
|
+
`lms` is refused, because it names a key and a URL rather than a credential.
|
|
96
|
+
|
|
76
97
|
## The callback
|
|
77
98
|
|
|
78
99
|
`python-sdk/cmpnd/callback.py` implements `CmpndCallback`, which satisfies `dspy.utils.callback.BaseCallback` without inheriting it — `callback.py` must import on the standard library alone (`tests/test_import_weight.py`), so it cannot import dspy to get a base class. DSPy calls its hook methods synchronously on whatever thread is executing the module. The callback owns no long-lived state beyond a per-call span builder map; everything trace/span related flows through context vars (see next section).
|
|
@@ -285,6 +306,10 @@ Two callers implement the waiting:
|
|
|
285
306
|
- **`exporter_http._send_json_retrying`** waits on the shutdown event rather than sleeping, so a backoff can't hold the process open at exit (and the post-shutdown drain gets no retries at all, deliberately).
|
|
286
307
|
- **`optimization._request`** clips every wait by the caller's `timeout` deadline, which stays the outer bound of `optimize()`. See [the optimize loop](#the-optimize-loop-and-transient-tolerance) below.
|
|
287
308
|
|
|
309
|
+
### The server says when this release is too old
|
|
310
|
+
|
|
311
|
+
Every API answer names the oldest cmpnd release the server supports, in the `X-Cmpnd-Minimum-Client-Version` header. `cmpnd_ingest/client_floor.py` reads it in an httpx response hook. When the installed `cmpnd` is older, it raises an `UpgradeRequiredWarning` once per process that names both versions and the command `pip install -U cmpnd`. The server refuses nothing for a client's version, so a call below the floor still goes through, and the warning appears beside whatever it does. Every client the SDK builds comes from `cmpnd._http.new_client`, which installs the hook, and the exporter installs it in `_client_kwargs`. `tests/test_client_floor.py` fails on an `httpx.Client` built any other way in `cmpnd/`. An install of `cmpnd_ingest` without `cmpnd` has no version to compare and says nothing. `CMPND_NO_UPGRADE_WARNING=1` silences the warning for a user who stays on an older release on purpose, and the warning names that switch. `cmpnd.UpgradeRequiredWarning` is public, so a `warnings` filter silences it in code.
|
|
312
|
+
|
|
288
313
|
### The optimize loop and transient tolerance
|
|
289
314
|
|
|
290
315
|
`optimization._poll_until_terminal` is what a caller blocks on for the length of a run, so one transient answer or one reset connection must not destroy a healthy run. It rides through transient answers on the shared budget, resetting after each successful poll, and never extends past the caller's `timeout` (default 600s per step) — a backoff wait is clipped by the deadline rather than overshooting it.
|
|
@@ -333,6 +358,11 @@ done.
|
|
|
333
358
|
| `PUT upload_url` (the ZIP) | retried — the slot pins the key and the bytes are fixed, so a re-send overwrites the same object | retried, for the same reason |
|
|
334
359
|
| `GET /api/v1/deployments/{id}` (poll) | retried on the next tick — a read is repeatable | retried on the next tick |
|
|
335
360
|
|
|
361
|
+
Serve authenticates its upload routes with the API key. The deploy upload and
|
|
362
|
+
the optimization bundle upload send `X-API-Key` when `upload_url` has the
|
|
363
|
+
configured endpoint's scheme, host and port (`_http.upload_auth_headers`). A URL
|
|
364
|
+
on any other origin gets no key.
|
|
365
|
+
|
|
336
366
|
After the poll reports ready, the SDK reads
|
|
337
367
|
`GET /api/v1/deployments/{id}/signature`. This bounded read returns persisted
|
|
338
368
|
artifact metadata and never asks a worker or analytics store. A failed metadata
|