cmpnd 0.26.0__tar.gz → 0.27.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.26.0 → cmpnd-0.27.0}/PKG-INFO +1 -1
- cmpnd-0.27.0/cmpnd/_authored_import.py +355 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/authored.py +259 -20
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/top.py +85 -76
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/shell.py +11 -4
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimization.py +243 -13
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/cli.md +49 -30
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/sdk-instrumentation.md +6 -2
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/exporter_http.py +23 -25
- {cmpnd-0.26.0 → cmpnd-0.27.0}/pyproject.toml +1 -1
- cmpnd-0.27.0/tests/integration/authored_optimize/judge.py +46 -0
- cmpnd-0.27.0/tests/integration/authored_optimize/optimize_hosted.py +98 -0
- cmpnd-0.27.0/tests/test_authored_bundle_collisions.py +61 -0
- cmpnd-0.27.0/tests/test_authored_metric_contract.py +106 -0
- cmpnd-0.27.0/tests/test_authored_named_models.py +188 -0
- cmpnd-0.27.0/tests/test_authored_optimize.py +407 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_top.py +86 -25
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter.py +79 -10
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter_real.py +23 -12
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ingest_second_producer.py +5 -6
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_integration.py +9 -15
- cmpnd-0.27.0/tests/test_span_sent_once.py +178 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/uv.lock +1 -1
- cmpnd-0.26.0/cmpnd/_authored_import.py +0 -201
- cmpnd-0.26.0/cmpnd/cli/snippet.py +0 -52
- {cmpnd-0.26.0 → cmpnd-0.27.0}/.gitignore +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/AGENTS.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/CLAUDE.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/README.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/client.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/context.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/models.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/client.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/README.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/drain.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_authored.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_callback.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_client.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_context.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_execute.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_identity.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_models.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_retry.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/tests/test_xxh64.py +0 -0
- {cmpnd-0.26.0 → cmpnd-0.27.0}/todo.md +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""Isolated import checker for authored deployment bundles."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import importlib
|
|
7
|
+
import inspect
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
import traceback
|
|
12
|
+
import zipfile
|
|
13
|
+
from dataclasses import asdict
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _program(module: Any, entrypoint: str) -> tuple[Any, str]:
|
|
19
|
+
import dspy
|
|
20
|
+
|
|
21
|
+
if not hasattr(module, entrypoint):
|
|
22
|
+
raise ValueError(f"entrypoint {entrypoint!r} is not defined by the authored program")
|
|
23
|
+
value = getattr(module, entrypoint)
|
|
24
|
+
if isinstance(value, dspy.Module):
|
|
25
|
+
return value, "instance"
|
|
26
|
+
if inspect.isclass(value) and issubclass(value, dspy.Module):
|
|
27
|
+
_require_zero_arguments(value, f"entrypoint class {entrypoint!r}")
|
|
28
|
+
return value(), "class"
|
|
29
|
+
if callable(value):
|
|
30
|
+
_require_zero_arguments(value, f"entrypoint factory {entrypoint!r}")
|
|
31
|
+
built = value()
|
|
32
|
+
if isinstance(built, dspy.Module):
|
|
33
|
+
return built, "factory"
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"entrypoint {entrypoint!r} must be a dspy.Module instance, a dspy.Module class, or a zero-argument factory"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _require_zero_arguments(value: Any, label: str) -> None:
|
|
40
|
+
"""Refuse required parameters without mislabelling errors inside the call."""
|
|
41
|
+
try:
|
|
42
|
+
signature = inspect.signature(value)
|
|
43
|
+
except (TypeError, ValueError):
|
|
44
|
+
return
|
|
45
|
+
try:
|
|
46
|
+
signature.bind()
|
|
47
|
+
except TypeError as exc:
|
|
48
|
+
raise ValueError(f"{label} must accept no arguments: {exc}") from exc
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _configured(request: dict[str, Any]) -> dict[str, int | None]:
|
|
52
|
+
"""Give the check its own platform configuration, and watch what the file asks for.
|
|
53
|
+
|
|
54
|
+
The child has no environment, so an authored ``cmpnd.configure(max_input_size=…)``
|
|
55
|
+
would otherwise have nothing to configure against. The wrapper keeps the check's own
|
|
56
|
+
transport while letting the file change the settings it owns, and records the two
|
|
57
|
+
trace limits the create call carries.
|
|
58
|
+
"""
|
|
59
|
+
from . import configuration
|
|
60
|
+
|
|
61
|
+
original_configure = configuration.configure
|
|
62
|
+
authored_limits: dict[str, int | None] = {"max_input_size": None, "max_output_size": None}
|
|
63
|
+
if not request.get("api_key"):
|
|
64
|
+
return authored_limits
|
|
65
|
+
original_configure(api_key=request["api_key"], endpoint=request.get("endpoint"))
|
|
66
|
+
|
|
67
|
+
def source_configure(api_key=None, endpoint=None, *args, **kwargs):
|
|
68
|
+
for name in authored_limits:
|
|
69
|
+
value = kwargs.get(name)
|
|
70
|
+
authored_limits[name] = value if isinstance(value, int) else None
|
|
71
|
+
return original_configure(
|
|
72
|
+
api_key=api_key or request["api_key"],
|
|
73
|
+
endpoint=endpoint or request.get("endpoint"),
|
|
74
|
+
*args,
|
|
75
|
+
**kwargs,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# The check has its own platform configuration but exposes no secret in the child
|
|
79
|
+
# environment. Serve resolves any tool binding again on create, so nothing the file
|
|
80
|
+
# configures here decides what the run may reach.
|
|
81
|
+
configuration.configure = source_configure
|
|
82
|
+
setattr(sys.modules["cmpnd"], "configure", source_configure)
|
|
83
|
+
return authored_limits
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@contextlib.contextmanager
|
|
87
|
+
def _staged(request: dict[str, Any]):
|
|
88
|
+
"""Extract the exact staged ZIP and put its root first on ``sys.path``.
|
|
89
|
+
|
|
90
|
+
The root stays on the path for the whole block rather than only for the first
|
|
91
|
+
import: an authored file's sibling may be imported lazily inside a function the
|
|
92
|
+
checks below call, and a path removed at the end of the entry import would make that
|
|
93
|
+
fail here and work in the kernel, which mounts the whole tree for the run's life.
|
|
94
|
+
"""
|
|
95
|
+
with tempfile.TemporaryDirectory(prefix="cmpnd-authored-") as directory:
|
|
96
|
+
root = Path(directory)
|
|
97
|
+
with zipfile.ZipFile(request["bundle"]) as archive:
|
|
98
|
+
archive.extractall(root)
|
|
99
|
+
sys.path.insert(0, str(root))
|
|
100
|
+
try:
|
|
101
|
+
yield root
|
|
102
|
+
finally:
|
|
103
|
+
if str(root) in sys.path:
|
|
104
|
+
sys.path.remove(str(root))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _import_member(member: str) -> Any:
|
|
108
|
+
"""Import one archive member as the module the kernel will import it as."""
|
|
109
|
+
return importlib.import_module(str(member).removesuffix(".py").replace("/", "."))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _program_facts(
|
|
113
|
+
program: Any,
|
|
114
|
+
kind: str,
|
|
115
|
+
authored_limits: dict[str, int | None],
|
|
116
|
+
) -> dict[str, Any]:
|
|
117
|
+
"""Everything the deployment create call reads off the imported program."""
|
|
118
|
+
import dspy
|
|
119
|
+
|
|
120
|
+
from . import configuration, deployment, identity, mcp_servers, packaging
|
|
121
|
+
from .authored import ProgramFacts
|
|
122
|
+
|
|
123
|
+
references = deployment._mcp_tool_references(program)
|
|
124
|
+
deployment._refuse_mcp_tools_outside_rlm(program, references)
|
|
125
|
+
pinned: dict[tuple[str, str], Any] = {}
|
|
126
|
+
if references:
|
|
127
|
+
import httpx
|
|
128
|
+
|
|
129
|
+
config = configuration.require_config()
|
|
130
|
+
with httpx.Client(timeout=30) as client:
|
|
131
|
+
pinned = deployment._resolve_mcp_tool_references(client, config, references)
|
|
132
|
+
tool_references = [
|
|
133
|
+
{
|
|
134
|
+
# The child already checked an explicit credential name against the
|
|
135
|
+
# registered server binding. Serve resolves that binding again and
|
|
136
|
+
# never accepts a client-selected credential name in this declaration.
|
|
137
|
+
"server_url": item.server_url,
|
|
138
|
+
"tool": item.tool_name,
|
|
139
|
+
"schema_hash": item.schema_hash,
|
|
140
|
+
"parameters": mcp_servers.synthesized_signature(item).parameter_names,
|
|
141
|
+
}
|
|
142
|
+
for item in pinned.values()
|
|
143
|
+
]
|
|
144
|
+
tool_declarations = [
|
|
145
|
+
{
|
|
146
|
+
"server_url": item.server_url,
|
|
147
|
+
"name": item.tool_name,
|
|
148
|
+
"schema_hash": item.schema_hash,
|
|
149
|
+
"input_schema": item.input_schema,
|
|
150
|
+
"description": item.description,
|
|
151
|
+
"parameters": mcp_servers.synthesized_signature(item).parameter_names,
|
|
152
|
+
}
|
|
153
|
+
for item in pinned.values()
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
signature = packaging._get_module_signature(program)
|
|
157
|
+
fields: dict[str, Any] = {}
|
|
158
|
+
if signature is not None:
|
|
159
|
+
fields = {
|
|
160
|
+
"signature_name": packaging._signature_to_string(signature),
|
|
161
|
+
"input_fields": list(signature.input_fields),
|
|
162
|
+
"output_fields": list(signature.output_fields),
|
|
163
|
+
"input_field_types": [identity.get_type_string(f.annotation) for f in signature.input_fields.values()],
|
|
164
|
+
"output_field_types": [identity.get_type_string(f.annotation) for f in signature.output_fields.values()],
|
|
165
|
+
}
|
|
166
|
+
lm = dspy.settings.lm
|
|
167
|
+
lm_kwargs = getattr(lm, "kwargs", {}) if lm is not None else {}
|
|
168
|
+
facts = ProgramFacts(
|
|
169
|
+
program_signature_hash=identity.compute_program_hash(program),
|
|
170
|
+
entrypoint_kind=kind,
|
|
171
|
+
module_class=type(program).__name__,
|
|
172
|
+
litellm_model=getattr(lm, "model", None),
|
|
173
|
+
temperature=lm_kwargs.get("temperature"),
|
|
174
|
+
max_tokens=lm_kwargs.get("max_tokens"),
|
|
175
|
+
thinking_effort=lm_kwargs.get("thinking_effort"),
|
|
176
|
+
max_input_size=authored_limits["max_input_size"],
|
|
177
|
+
max_output_size=authored_limits["max_output_size"],
|
|
178
|
+
tool_references=tool_references,
|
|
179
|
+
tool_declarations=tool_declarations,
|
|
180
|
+
**fields,
|
|
181
|
+
)
|
|
182
|
+
return asdict(facts)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _check(request: dict[str, Any]) -> dict[str, Any]:
|
|
186
|
+
"""The program bundle's answer: one import, the entrypoint's facts."""
|
|
187
|
+
authored_limits = _configured(request)
|
|
188
|
+
with _staged(request) as root:
|
|
189
|
+
manifest = json.loads((root / "manifest.json").read_text())
|
|
190
|
+
module = _import_member(str(manifest["program"]))
|
|
191
|
+
program, kind = _program(module, str(manifest["entrypoint"]))
|
|
192
|
+
return {"facts": _program_facts(program, kind, authored_limits)}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _check_optimize(request: dict[str, Any]) -> dict[str, Any]:
|
|
196
|
+
"""The optimize file's answer: one import, every entrypoint the run needs.
|
|
197
|
+
|
|
198
|
+
One import for all of it, rather than one per callable: the file is one module, and
|
|
199
|
+
importing it three times would run its module scope three times — which is neither
|
|
200
|
+
what the author's own script does nor what the kernel does.
|
|
201
|
+
"""
|
|
202
|
+
from . import optimization
|
|
203
|
+
|
|
204
|
+
ask = request["optimize"]
|
|
205
|
+
authored_limits = _configured(request)
|
|
206
|
+
with _staged(request):
|
|
207
|
+
module = _import_member(str(ask["entry"]))
|
|
208
|
+
declared_lms: dict[str, Any] | None = None
|
|
209
|
+
if hasattr(module, "lms"):
|
|
210
|
+
lms_config: dict[str, Any] = {}
|
|
211
|
+
optimization._lms_config(lms_config, getattr(module, "lms"))
|
|
212
|
+
declared_lms = lms_config.get("lms", {})
|
|
213
|
+
metric_entrypoint = str(ask["metric_entrypoint"])
|
|
214
|
+
metric = getattr(module, metric_entrypoint, None)
|
|
215
|
+
if metric is None:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
f"the optimize file binds no {metric_entrypoint!r}. An optimization scores every rollout "
|
|
218
|
+
f"with it, so define `def {metric_entrypoint}(gold, pred, trace, pred_name, pred_trace)` "
|
|
219
|
+
"returning dspy.Prediction(score=, feedback=)."
|
|
220
|
+
)
|
|
221
|
+
if not callable(metric):
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"{metric_entrypoint!r} in the optimize file is a {type(metric).__name__}, not a callable, "
|
|
224
|
+
"so nothing can score a rollout with it."
|
|
225
|
+
)
|
|
226
|
+
optimization._check_metric_call(metric, str(ask["optimizer"]))
|
|
227
|
+
|
|
228
|
+
program_entrypoint = str(ask["program_entrypoint"])
|
|
229
|
+
declared_program = getattr(module, program_entrypoint, None)
|
|
230
|
+
facts: dict[str, Any] | None = None
|
|
231
|
+
found_program: str | None = None
|
|
232
|
+
if declared_program is not None and not ask["program_allowed"]:
|
|
233
|
+
raise ValueError(
|
|
234
|
+
f"the optimize file binds {program_entrypoint!r} and a deployment id was given as well. "
|
|
235
|
+
"Those are two answers to which program this run optimizes, so neither is taken: remove "
|
|
236
|
+
f"the deployment id to optimize the file's own {program_entrypoint!r}, or remove "
|
|
237
|
+
f"{program_entrypoint!r} to optimize the deployment."
|
|
238
|
+
)
|
|
239
|
+
if declared_program is None and ask["program_allowed"]:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
f"the optimize file binds no {program_entrypoint!r}, and no deployment id names one. "
|
|
242
|
+
f"Bind `{program_entrypoint} = ...` to optimize the file's own program, or pass an "
|
|
243
|
+
"existing deployment id to optimize that."
|
|
244
|
+
)
|
|
245
|
+
if declared_program is not None:
|
|
246
|
+
program, kind = _program(module, program_entrypoint)
|
|
247
|
+
facts = _program_facts(program, kind, authored_limits)
|
|
248
|
+
found_program = program_entrypoint
|
|
249
|
+
|
|
250
|
+
proposer_entrypoint = str(ask["proposer_entrypoint"])
|
|
251
|
+
declared_proposer = getattr(module, proposer_entrypoint, None)
|
|
252
|
+
found_proposer: str | None = None
|
|
253
|
+
if declared_proposer is not None:
|
|
254
|
+
optimization._check_proposer_call(declared_proposer)
|
|
255
|
+
found_proposer = proposer_entrypoint
|
|
256
|
+
|
|
257
|
+
work = Path(request["work_dir"])
|
|
258
|
+
trainset = _rows(module, "trainset")
|
|
259
|
+
valset = _rows(module, "valset")
|
|
260
|
+
(work / "trainset.ndjson").write_text(_ndjson(trainset))
|
|
261
|
+
(work / "valset.ndjson").write_text(_ndjson(valset))
|
|
262
|
+
|
|
263
|
+
answer: dict[str, Any] = {
|
|
264
|
+
"optimize": {
|
|
265
|
+
"metric_entrypoint": metric_entrypoint,
|
|
266
|
+
"program_entrypoint": found_program,
|
|
267
|
+
"proposer_entrypoint": found_proposer,
|
|
268
|
+
"lms": declared_lms,
|
|
269
|
+
"trainset_rows": len(trainset),
|
|
270
|
+
"valset_rows": len(valset),
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if facts is not None:
|
|
274
|
+
answer["facts"] = facts
|
|
275
|
+
return answer
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _rows(module: Any, name: str) -> list[Any]:
|
|
279
|
+
"""One example set the file built, refused here rather than at the first rollout."""
|
|
280
|
+
rows = getattr(module, name, None)
|
|
281
|
+
if rows is None:
|
|
282
|
+
raise ValueError(
|
|
283
|
+
f"the optimize file binds no {name!r}. An optimization needs both `trainset` and `valset` "
|
|
284
|
+
"as lists of dspy.Example."
|
|
285
|
+
)
|
|
286
|
+
if not isinstance(rows, (list, tuple)) or not rows:
|
|
287
|
+
raise ValueError(
|
|
288
|
+
f"{name!r} in the optimize file is {type(rows).__name__} and must be a non-empty list of "
|
|
289
|
+
"dspy.Example — an empty one leaves the search nothing to score."
|
|
290
|
+
)
|
|
291
|
+
return list(rows)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _ndjson(rows: list[Any]) -> str:
|
|
295
|
+
"""The dataset as the optimizer reads it: one flattened example per line.
|
|
296
|
+
|
|
297
|
+
Serialized HERE, in the process that imported the file, because that is the only
|
|
298
|
+
process that ever holds the Python objects. Authored source is for code the kernel
|
|
299
|
+
imports; a dataset is data the optimizer reads, and this is the shape it reads.
|
|
300
|
+
"""
|
|
301
|
+
from . import optimization
|
|
302
|
+
|
|
303
|
+
return "\n".join(json.dumps(optimization._example_to_dict(row)) for row in rows) + "\n"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def main() -> int:
|
|
307
|
+
try:
|
|
308
|
+
request = json.loads(sys.stdin.read())
|
|
309
|
+
with contextlib.redirect_stdout(sys.stderr):
|
|
310
|
+
# Anything the imported file prints goes to stderr: this process answers one
|
|
311
|
+
# JSON document on stdout, and a module-scope `print` would otherwise be read
|
|
312
|
+
# as part of it.
|
|
313
|
+
answer = _check_optimize(request) if request.get("optimize") else _check(request)
|
|
314
|
+
print(json.dumps(answer))
|
|
315
|
+
return 0
|
|
316
|
+
except ModuleNotFoundError as exc:
|
|
317
|
+
print(
|
|
318
|
+
json.dumps(
|
|
319
|
+
{
|
|
320
|
+
"error": (
|
|
321
|
+
f"the authored source imports {exc.name!r}, which the local import check cannot find. "
|
|
322
|
+
"If it is a sibling module, add its file or directory with --include; otherwise install "
|
|
323
|
+
"the dependency locally and ensure the worker supports it."
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
except KeyError as exc:
|
|
329
|
+
print(
|
|
330
|
+
json.dumps(
|
|
331
|
+
{
|
|
332
|
+
"error": (
|
|
333
|
+
f"authored source import failed with KeyError: {exc}. The check runs without your "
|
|
334
|
+
"environment because the worker has none. Do not read provider or MCP credentials at "
|
|
335
|
+
"module scope. Name the model without its key and use cmpnd.mcp_tools(url) for a "
|
|
336
|
+
"registered server.\n" + _concise_traceback()
|
|
337
|
+
)
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
except BaseException as exc:
|
|
342
|
+
print(
|
|
343
|
+
json.dumps(
|
|
344
|
+
{"error": (f"authored source import failed with {type(exc).__name__}: {exc}\n" + _concise_traceback())}
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
return 1
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _concise_traceback() -> str:
|
|
351
|
+
return "".join(traceback.format_exc(limit=6)).strip()
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
if __name__ == "__main__":
|
|
355
|
+
raise SystemExit(main())
|