cmpnd 0.83.0__tar.gz → 0.84.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.83.0 → cmpnd-0.84.0}/AGENTS.md +7 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/PKG-INFO +2 -2
- {cmpnd-0.83.0 → cmpnd-0.84.0}/README.md +1 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/callback.py +116 -21
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/configuration.py +3 -3
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/context.py +41 -2
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/models.py +130 -6
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/client.md +2 -2
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/sdk-instrumentation.md +26 -5
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/README.md +20 -3
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/bodies.py +14 -7
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/exporter_http.py +84 -47
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/program_names.py +4 -29
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/protocols.py +19 -1
- cmpnd-0.84.0/ingest/cmpnd_ingest/span_context.py +63 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/trace_facts.py +4 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/pyproject.toml +1 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/test-in-linux.sh +18 -4
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/rlm_shape.py +4 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/test_rlm_shape.py +3 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/span_reference_fixture.py +9 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_callback.py +6 -2
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_context.py +20 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_kwargs.py +1 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_selection.py +1 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_program_names.py +7 -4
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_identity.py +2 -2
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_span_references.py +18 -0
- cmpnd-0.84.0/tests/test_span_submission.py +492 -0
- cmpnd-0.84.0/tests/test_span_submission_wire_contract.py +190 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/uv.lock +1 -1
- {cmpnd-0.83.0 → cmpnd-0.84.0}/.gitignore +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/CLAUDE.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/program_naming.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/references.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/cli.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/drain.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_program_id.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_execute.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_integration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_models.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_phase_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_program_naming.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.84.0}/tests/test_xxh64.py +0 -0
|
@@ -137,7 +137,13 @@ still install against the `dspy>=…` floor in `dependencies`.
|
|
|
137
137
|
- `scripts/install-test-env.sh <python> <dspy>` builds `.venv` from one group's
|
|
138
138
|
locked tree, as each `sdk-test` leg in `ci-sdk.yml` does. From the repo root,
|
|
139
139
|
run `just sdk-test-env 3.13 3.3.0`, then, from `python-sdk/`,
|
|
140
|
-
`.venv/bin/python -m pytest tests/ -k "not e2e"`.
|
|
140
|
+
`.venv/bin/python -m pytest tests/ -k "not e2e"`. The root recipe
|
|
141
|
+
`SDK_TEST_PYTHON_VERSION=3.13 SDK_TEST_DSPY_VERSION=3.4.0 just sdk-test-linux`
|
|
142
|
+
runs the same locked environment in Linux. `SDK_TEST_LINUX_IMAGE` selects a
|
|
143
|
+
pinned CI builder when reproducing a job. The helper copies the worker pins
|
|
144
|
+
and truncation vectors and mounts Git metadata read-only for revision tests.
|
|
145
|
+
The revision tests require Git, which the CI builder provides.
|
|
146
|
+
`uv sync --extra dev` in
|
|
141
147
|
`python-sdk/` returns to the default group.
|
|
142
148
|
- `--latest` resolves every package fresh, as a user's install today would.
|
|
143
149
|
With the DSPy version `newest` it pins no DSPy either. The daily
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.84.0
|
|
4
4
|
Summary: DSPy observability and deployment SDK for cmpnd
|
|
5
5
|
Project-URL: Homepage, https://cmpnd.ai
|
|
6
6
|
Author-email: cmpnd <hello@cmpnd.ai>
|
|
@@ -685,7 +685,7 @@ cmpnd.configure(
|
|
|
685
685
|
flush_interval_seconds=5.0, # optional: seconds between flushes
|
|
686
686
|
capture_inputs=True, # optional: record span inputs
|
|
687
687
|
capture_outputs=True, # optional: record span outputs
|
|
688
|
-
default_tags={"env": "prod"}, # optional: tags on every trace
|
|
688
|
+
default_tags={"env": "prod"}, # optional: tags on every trace; a run's own tags override them
|
|
689
689
|
)
|
|
690
690
|
```
|
|
691
691
|
|
|
@@ -634,7 +634,7 @@ cmpnd.configure(
|
|
|
634
634
|
flush_interval_seconds=5.0, # optional: seconds between flushes
|
|
635
635
|
capture_inputs=True, # optional: record span inputs
|
|
636
636
|
capture_outputs=True, # optional: record span outputs
|
|
637
|
-
default_tags={"env": "prod"}, # optional: tags on every trace
|
|
637
|
+
default_tags={"env": "prod"}, # optional: tags on every trace; a run's own tags override them
|
|
638
638
|
)
|
|
639
639
|
```
|
|
640
640
|
|
|
@@ -538,6 +538,7 @@ class _CallbackBase:
|
|
|
538
538
|
stream_trace_starts: bool | None = None,
|
|
539
539
|
own_config: configuration.Config | None = None,
|
|
540
540
|
optimization_phase: str | None = None,
|
|
541
|
+
producer_context: dict[str, Any] | None = None,
|
|
541
542
|
) -> None:
|
|
542
543
|
# A client passes its own config here; the auto_instrument callback leaves
|
|
543
544
|
# it None and reads the process-global config live, so a reconfigure()
|
|
@@ -547,6 +548,24 @@ class _CallbackBase:
|
|
|
547
548
|
# later cmpnd.configure(). That fixedness is what keeps a client
|
|
548
549
|
# orthogonal to the global config.
|
|
549
550
|
self._own_config = own_config
|
|
551
|
+
producer_context = producer_context or {}
|
|
552
|
+
self._producer_references = tuple(
|
|
553
|
+
(name, producer_context[name])
|
|
554
|
+
for name in (
|
|
555
|
+
"program_id",
|
|
556
|
+
"deployment_id",
|
|
557
|
+
"deployment_run_id",
|
|
558
|
+
"optimization_run_id",
|
|
559
|
+
"eval_run_id",
|
|
560
|
+
"compiled_from_run_id",
|
|
561
|
+
)
|
|
562
|
+
if producer_context.get(name) is not None
|
|
563
|
+
)
|
|
564
|
+
self._producer_tags = tuple((producer_context.get("tags") or {}).items())
|
|
565
|
+
self._producer_metadata = tuple((producer_context.get("metadata") or {}).items())
|
|
566
|
+
self._producer_projects = (
|
|
567
|
+
tuple(producer_context["project_tags"]) if "project_tags" in producer_context else None
|
|
568
|
+
)
|
|
550
569
|
# The optimization phase this callback traces for a run it does not own: an
|
|
551
570
|
# executor that scores a hosted rollout with the author's metric ("metric") or runs
|
|
552
571
|
# the author's GEPA proposer ("reflection"). Such a process knows the program the
|
|
@@ -621,7 +640,7 @@ class _CallbackBase:
|
|
|
621
640
|
|
|
622
641
|
# Active optimization run ID - stored on instance for same thread-safety reason.
|
|
623
642
|
# Prefixed text id (`op_…`), minted via cmpnd.identity.
|
|
624
|
-
self._active_optimization_run_id: str | None =
|
|
643
|
+
self._active_optimization_run_id: str | None = dict(self._producer_references).get("optimization_run_id")
|
|
625
644
|
|
|
626
645
|
# Token usage from LM calls without a trace context (e.g., GEPA reflection calls)
|
|
627
646
|
# Accumulated during optimization and included in the finalization payload
|
|
@@ -722,19 +741,18 @@ class _CallbackBase:
|
|
|
722
741
|
return configuration._current_program_id()
|
|
723
742
|
|
|
724
743
|
def _stamp(self, builder: Any) -> Any:
|
|
725
|
-
"""Stamp this callback's
|
|
726
|
-
builder it just created. A span's program is its trace's, so a span carries
|
|
727
|
-
no program of its own.
|
|
744
|
+
"""Stamp this callback's trace context before any spans begin.
|
|
728
745
|
|
|
729
746
|
A trace from a call that declares a program name carries the name and no id,
|
|
730
747
|
and the exporter resolves the name. The declared name wins over a program id the
|
|
731
748
|
client's config read from ``CMPND_PROGRAM_ID``, and the callback warns once that it did.
|
|
732
749
|
A trace that gets neither gets a generated name in `_create_trace`.
|
|
733
750
|
"""
|
|
734
|
-
builder.project_tags = self._resolve_project_tags()
|
|
735
751
|
if isinstance(builder, models.TraceBuilder):
|
|
736
|
-
|
|
737
|
-
|
|
752
|
+
builder.project_tags = list(self._resolve_project_tags() or ())
|
|
753
|
+
producer_program_id = dict(self._producer_references).get("program_id")
|
|
754
|
+
program_id = producer_program_id or self._resolve_program_id()
|
|
755
|
+
name = None if producer_program_id else _declared_program_name()
|
|
738
756
|
if name is not None:
|
|
739
757
|
if program_id is not None and not self._warned_name_over_id:
|
|
740
758
|
self._warned_name_over_id = True
|
|
@@ -765,6 +783,18 @@ class _CallbackBase:
|
|
|
765
783
|
return _exporter_pool.ensure_exporter_started(self._own_config)
|
|
766
784
|
return exporter.get_exporter()
|
|
767
785
|
|
|
786
|
+
def _apply_producer_context(self, trace: models.TraceBuilder) -> None:
|
|
787
|
+
for name, value in self._producer_references:
|
|
788
|
+
setattr(trace, name, value)
|
|
789
|
+
if dict(self._producer_references).get("program_id") is not None:
|
|
790
|
+
trace.declared_name = trace.generated_name = None
|
|
791
|
+
if self._producer_projects is not None:
|
|
792
|
+
trace.project_tags = list(self._producer_projects)
|
|
793
|
+
# A run's tags and metadata are more specific than the process defaults
|
|
794
|
+
# already on the trace, so they override them.
|
|
795
|
+
trace.tags.update(self._producer_tags)
|
|
796
|
+
trace.metadata.update(self._producer_metadata)
|
|
797
|
+
|
|
768
798
|
def _ensure_exporter(self) -> Any:
|
|
769
799
|
"""Start this callback's exporter if it is not already running."""
|
|
770
800
|
if self._own_config is not None:
|
|
@@ -780,6 +810,60 @@ class _CallbackBase:
|
|
|
780
810
|
span makes ``to_api_dict`` bound the whole body by the same limits rather
|
|
781
811
|
than by the 64 KiB default.
|
|
782
812
|
"""
|
|
813
|
+
if context.get_current_trace() is None:
|
|
814
|
+
config = self._own_config or configuration.get_config()
|
|
815
|
+
kwargs.setdefault(
|
|
816
|
+
"project_tags",
|
|
817
|
+
list(
|
|
818
|
+
self._producer_projects
|
|
819
|
+
if self._producer_projects is not None
|
|
820
|
+
else self._resolve_project_tags() or ()
|
|
821
|
+
),
|
|
822
|
+
)
|
|
823
|
+
# Most specific wins: process defaults, then the run's tags, then the span's own.
|
|
824
|
+
kwargs["tags"] = {
|
|
825
|
+
**(config.default_tags if config else {}),
|
|
826
|
+
**dict(self._producer_tags),
|
|
827
|
+
**kwargs.get("tags", {}),
|
|
828
|
+
}
|
|
829
|
+
for key, value in self._producer_references:
|
|
830
|
+
kwargs.setdefault(key, value)
|
|
831
|
+
run_id = self._active_optimization_run_id or context.get_current_optimization_run_id()
|
|
832
|
+
kwargs.setdefault("optimization_run_id", run_id)
|
|
833
|
+
kwargs.setdefault("eval_run_id", self._active_eval_run.eval_run_id if self._active_eval_run else None)
|
|
834
|
+
program_id = (
|
|
835
|
+
kwargs.get("program_id") or self._resolve_program_id() or context.optimization_program_id(run_id)
|
|
836
|
+
)
|
|
837
|
+
declared = None if kwargs.get("program_id") else _declared_program_name()
|
|
838
|
+
if declared is not None:
|
|
839
|
+
program_id = None
|
|
840
|
+
kwargs.setdefault("program_id", program_id)
|
|
841
|
+
from cmpnd_ingest.program_names import ProgramName
|
|
842
|
+
from cmpnd_ingest.span_context import ExportContext, SpanContext
|
|
843
|
+
|
|
844
|
+
kwargs.setdefault(
|
|
845
|
+
"export_context",
|
|
846
|
+
ExportContext(
|
|
847
|
+
ProgramName(declared) if declared else None,
|
|
848
|
+
program_id,
|
|
849
|
+
declared is None or program_id is not None,
|
|
850
|
+
),
|
|
851
|
+
)
|
|
852
|
+
phase = self._optimization_phase or (
|
|
853
|
+
("metric" if self._active_eval_run else "reflection") if run_id else None
|
|
854
|
+
)
|
|
855
|
+
if phase is not None:
|
|
856
|
+
kwargs["tags"]["optimization_phase"] = phase
|
|
857
|
+
kwargs.setdefault(
|
|
858
|
+
"span_context",
|
|
859
|
+
SpanContext(
|
|
860
|
+
project_tags=tuple(kwargs["project_tags"]),
|
|
861
|
+
tags=tuple(kwargs["tags"].items()),
|
|
862
|
+
program_id=program_id,
|
|
863
|
+
optimization_run_id=kwargs["optimization_run_id"],
|
|
864
|
+
eval_run_id=kwargs["eval_run_id"],
|
|
865
|
+
),
|
|
866
|
+
)
|
|
783
867
|
return context.create_child_span(
|
|
784
868
|
name,
|
|
785
869
|
span_type,
|
|
@@ -1220,7 +1304,7 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1220
1304
|
|
|
1221
1305
|
def _create_trace(self, instance: Any, inputs: dict[str, Any]) -> models.TraceBuilder:
|
|
1222
1306
|
"""Create a new trace for a root module call."""
|
|
1223
|
-
config = configuration.get_config()
|
|
1307
|
+
config = self._own_config or configuration.get_config()
|
|
1224
1308
|
|
|
1225
1309
|
trace = models.TraceBuilder(
|
|
1226
1310
|
trace_id=uuid4(),
|
|
@@ -1364,16 +1448,16 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1364
1448
|
preview = helpers.create_preview(helpers.clean_inputs(inputs, dspy, self._max_input_field_size))
|
|
1365
1449
|
trace.request_preview = preview
|
|
1366
1450
|
|
|
1367
|
-
#
|
|
1451
|
+
# Process default tags first, then the run's own labels over them.
|
|
1368
1452
|
if config and config.default_tags:
|
|
1369
1453
|
trace.tags.update(config.default_tags)
|
|
1454
|
+
self._apply_producer_context(trace)
|
|
1370
1455
|
# A trace opened while this callback traces a phase, or while a local run
|
|
1371
1456
|
# calls its metric, is the metric's or the proposer's, never a rollout, even
|
|
1372
1457
|
# when its module is the program's own class.
|
|
1373
1458
|
phase = self._optimization_phase or context.get_current_optimization_phase()
|
|
1374
1459
|
if phase is not None:
|
|
1375
1460
|
trace.tags["optimization_phase"] = phase
|
|
1376
|
-
|
|
1377
1461
|
return trace
|
|
1378
1462
|
|
|
1379
1463
|
def _extract_usage_from_prediction(self, span: models.SpanBuilder, outputs: Any, dspy: Any) -> None:
|
|
@@ -1631,7 +1715,8 @@ class _LMMixin(_CallbackBase):
|
|
|
1631
1715
|
# would double-count them in the optimization total.
|
|
1632
1716
|
current_trace = context.get_current_trace()
|
|
1633
1717
|
if current_trace is None and (
|
|
1634
|
-
|
|
1718
|
+
(span.span_context is not None and span.span_context.optimization_run_id is not None)
|
|
1719
|
+
or self._optimization_phase is not None
|
|
1635
1720
|
):
|
|
1636
1721
|
self._emit_optimization_lm_trace(span)
|
|
1637
1722
|
return # handled as a standalone GEPA-process trace
|
|
@@ -1678,11 +1763,12 @@ class _LMMixin(_CallbackBase):
|
|
|
1678
1763
|
instead, and may have no run id: it traces the metric or the proposer of a
|
|
1679
1764
|
run whose id the ingest key the trace is pushed with decides.
|
|
1680
1765
|
"""
|
|
1681
|
-
|
|
1766
|
+
assert span.span_context is not None
|
|
1767
|
+
run_id = span.span_context.optimization_run_id
|
|
1682
1768
|
if not run_id and self._optimization_phase is None:
|
|
1683
1769
|
return
|
|
1684
1770
|
|
|
1685
|
-
phase =
|
|
1771
|
+
phase = dict(span.span_context.tags).get("optimization_phase", "reflection")
|
|
1686
1772
|
span.attributes["gepa.phase"] = phase
|
|
1687
1773
|
span.attributes["optimization.role"] = _OPTIMIZATION_PHASE_ROLES[phase]
|
|
1688
1774
|
|
|
@@ -1692,11 +1778,14 @@ class _LMMixin(_CallbackBase):
|
|
|
1692
1778
|
end_time=span.end_time,
|
|
1693
1779
|
module_class="GEPA",
|
|
1694
1780
|
)
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1781
|
+
trace.program_id = span.span_context.program_id
|
|
1782
|
+
trace.deployment_id = span.span_context.deployment_id
|
|
1783
|
+
trace.deployment_run_id = span.span_context.deployment_run_id
|
|
1784
|
+
trace.compiled_from_run_id = span.span_context.compiled_from_run_id
|
|
1785
|
+
trace.project_tags = list(span.span_context.project_tags)
|
|
1786
|
+
trace.tags = dict(span.span_context.tags)
|
|
1787
|
+
trace.metadata = dict(self._producer_metadata)
|
|
1788
|
+
trace._export_context = span.export_context
|
|
1700
1789
|
# A metric-phase call can start while DSPy's Evaluate span is still on the
|
|
1701
1790
|
# context stack, so on_lm_start stamped this span with a parent_span_id for
|
|
1702
1791
|
# a span that isn't in this standalone one-span trace. This span IS the
|
|
@@ -1707,8 +1796,7 @@ class _LMMixin(_CallbackBase):
|
|
|
1707
1796
|
trace.signature_name = span.model_name or phase
|
|
1708
1797
|
trace.status = span.status
|
|
1709
1798
|
trace.optimization_run_id = run_id
|
|
1710
|
-
|
|
1711
|
-
trace.eval_run_id = self._active_eval_run.eval_run_id
|
|
1799
|
+
trace.eval_run_id = span.span_context.eval_run_id
|
|
1712
1800
|
trace.tags["optimization_phase"] = phase
|
|
1713
1801
|
|
|
1714
1802
|
# Single-span trace: roll the span's usage up onto the trace so the
|
|
@@ -2169,6 +2257,11 @@ class _InterpreterMixin(_CallbackBase):
|
|
|
2169
2257
|
"parent_span": context.get_current_span(),
|
|
2170
2258
|
"start_time": datetime.now(timezone.utc),
|
|
2171
2259
|
}
|
|
2260
|
+
trace = context.get_current_trace()
|
|
2261
|
+
parent = frame["parent_span"]
|
|
2262
|
+
frame["trace_id"] = trace.trace_id if trace else parent.trace_id
|
|
2263
|
+
frame["span_context"] = trace.span_context() if trace else parent.span_context
|
|
2264
|
+
frame["export_context"] = trace.export_context if trace else parent.export_context
|
|
2172
2265
|
self._interpreter_tool_frames[call_id] = frame
|
|
2173
2266
|
# Publish this frame as the one a nested on_tool_start marks, in this
|
|
2174
2267
|
# context only. The token restores the prior frame (nested tool calls) on
|
|
@@ -2209,13 +2302,15 @@ class _InterpreterMixin(_CallbackBase):
|
|
|
2209
2302
|
parent = frame["parent_span"]
|
|
2210
2303
|
span = models.SpanBuilder(
|
|
2211
2304
|
span_id=uuid4(),
|
|
2212
|
-
trace_id=
|
|
2305
|
+
trace_id=frame["trace_id"],
|
|
2213
2306
|
name=f"Tool.{frame['tool_name']}",
|
|
2214
2307
|
span_type=models.SpanType.TOOL,
|
|
2215
2308
|
parent_span_id=parent.span_id if parent else None,
|
|
2216
2309
|
start_time=frame["start_time"],
|
|
2217
2310
|
max_input_size=self._max_input_field_size,
|
|
2218
2311
|
max_output_size=self._max_output_field_size,
|
|
2312
|
+
span_context=frame["span_context"],
|
|
2313
|
+
export_context=frame["export_context"],
|
|
2219
2314
|
)
|
|
2220
2315
|
span.attributes["tool.name"] = str(frame["tool_name"])
|
|
2221
2316
|
span.attributes["tool.synthesized"] = "true"
|
|
@@ -257,11 +257,11 @@ def require_config() -> Config:
|
|
|
257
257
|
def _current_project_tags() -> list[str] | None: # pyright: ignore[reportUnusedFunction] # used cross-module as a dataclass default_factory
|
|
258
258
|
"""Read the currently-configured project tags, or None if unconfigured.
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
Builders copy these values when execution begins, so later configuration
|
|
261
|
+
changes affect only future traces and spans.
|
|
262
262
|
"""
|
|
263
263
|
cfg = get_config()
|
|
264
|
-
return cfg.project_tags if cfg else None
|
|
264
|
+
return list(cfg.project_tags) if cfg and cfg.project_tags is not None else None
|
|
265
265
|
|
|
266
266
|
|
|
267
267
|
def _current_program_id() -> str | None: # pyright: ignore[reportUnusedFunction] # used cross-module as a dataclass default_factory
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
from collections.abc import Generator
|
|
6
7
|
from contextlib import contextmanager
|
|
7
|
-
from contextvars import ContextVar
|
|
8
|
+
from contextvars import ContextVar, Token
|
|
8
9
|
from datetime import datetime, timezone
|
|
9
|
-
from
|
|
10
|
+
from types import TracebackType
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
10
12
|
from uuid import uuid4
|
|
11
13
|
|
|
12
14
|
if TYPE_CHECKING:
|
|
@@ -14,6 +16,42 @@ if TYPE_CHECKING:
|
|
|
14
16
|
|
|
15
17
|
# Current trace context
|
|
16
18
|
_current_trace: ContextVar[models.TraceBuilder | None] = ContextVar("cmpnd_current_trace", default=None)
|
|
19
|
+
_producer_context: ContextVar[str | None] = ContextVar("cmpnd_producer_context", default=None)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def producer_context(encoded: str | None) -> _ProducerContext:
|
|
23
|
+
"""Bind one immutable producer snapshot without changing process configuration."""
|
|
24
|
+
return _ProducerContext(encoded)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _ProducerContext:
|
|
28
|
+
# Generator context managers assign exception.__traceback__ while unwinding.
|
|
29
|
+
# A worker's frozen WIT error must cross this boundary unchanged.
|
|
30
|
+
def __init__(self, encoded: str | None) -> None:
|
|
31
|
+
self._encoded = encoded
|
|
32
|
+
self._token: Token[str | None] | None = None
|
|
33
|
+
|
|
34
|
+
def __enter__(self) -> None:
|
|
35
|
+
if self._token is not None:
|
|
36
|
+
raise RuntimeError("producer context is already active")
|
|
37
|
+
if self._encoded is not None and not isinstance(json.loads(self._encoded), dict):
|
|
38
|
+
raise TypeError("producer context must be a JSON object")
|
|
39
|
+
self._token = _producer_context.set(self._encoded)
|
|
40
|
+
|
|
41
|
+
def __exit__(
|
|
42
|
+
self, _kind: type[BaseException] | None, _error: BaseException | None, _traceback: TracebackType | None
|
|
43
|
+
) -> Literal[False]:
|
|
44
|
+
if self._token is not None:
|
|
45
|
+
_producer_context.reset(self._token)
|
|
46
|
+
self._token = None
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _current_producer_context() -> dict[str, Any]: # pyright: ignore[reportUnusedFunction] # read by span builders
|
|
51
|
+
"""Return a fresh view so a builder cannot mutate another span's context."""
|
|
52
|
+
encoded = _producer_context.get()
|
|
53
|
+
return json.loads(encoded) if encoded else {}
|
|
54
|
+
|
|
17
55
|
|
|
18
56
|
# Stack of active spans (for nested spans). A tuple, replaced on every push and
|
|
19
57
|
# pop and never mutated: a context copy (``contextvars.copy_context()``, an
|
|
@@ -168,6 +206,7 @@ def create_child_span(
|
|
|
168
206
|
def clear_context() -> None:
|
|
169
207
|
"""Clear all trace/span context. Useful for testing."""
|
|
170
208
|
_current_trace.set(None)
|
|
209
|
+
_producer_context.set(None)
|
|
171
210
|
_span_stack.set(())
|
|
172
211
|
_current_eval_run.set(None)
|
|
173
212
|
_current_optimization_run_id.set(None)
|
|
@@ -11,6 +11,9 @@ from enum import Enum
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
from uuid import UUID, uuid4
|
|
13
13
|
|
|
14
|
+
from cmpnd_ingest.bodies import SpanBody
|
|
15
|
+
from cmpnd_ingest.program_names import ProgramName
|
|
16
|
+
from cmpnd_ingest.span_context import ExportContext, SpanContext
|
|
14
17
|
from cmpnd_ingest.trace_facts import CompletionFact, MetadataFact, StartedFact, StringMapChange
|
|
15
18
|
|
|
16
19
|
from . import imaging
|
|
@@ -736,9 +739,9 @@ class SpanBuilder:
|
|
|
736
739
|
name: str
|
|
737
740
|
span_type: SpanType = SpanType.UNKNOWN
|
|
738
741
|
parent_span_id: UUID | None = None
|
|
739
|
-
trace_id: UUID | None =
|
|
742
|
+
trace_id: UUID | None = field(default_factory=uuid4)
|
|
740
743
|
|
|
741
|
-
start_time: datetime | None =
|
|
744
|
+
start_time: datetime | None = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
742
745
|
end_time: datetime | None = None
|
|
743
746
|
|
|
744
747
|
status: TraceStatus = TraceStatus.UNSET
|
|
@@ -817,7 +820,73 @@ class SpanBuilder:
|
|
|
817
820
|
max_output_size: int | None = None
|
|
818
821
|
|
|
819
822
|
# Snapshot project_tags at construction time to avoid reconfigure races in async export
|
|
820
|
-
project_tags: list[str] | None =
|
|
823
|
+
project_tags: list[str] | None = None
|
|
824
|
+
tags: dict[str, str] = field(default_factory=dict)
|
|
825
|
+
program_id: str | None = None
|
|
826
|
+
deployment_id: str | None = None
|
|
827
|
+
deployment_run_id: str | None = None
|
|
828
|
+
optimization_run_id: str | None = None
|
|
829
|
+
eval_run_id: str | None = None
|
|
830
|
+
compiled_from_run_id: str | None = None
|
|
831
|
+
span_context: SpanContext | None = field(default=None, repr=False)
|
|
832
|
+
export_context: ExportContext | None = field(default=None, repr=False)
|
|
833
|
+
_submitted_body: SpanBody | None = field(default=None, init=False, repr=False)
|
|
834
|
+
|
|
835
|
+
def __post_init__(self) -> None:
|
|
836
|
+
from . import context
|
|
837
|
+
|
|
838
|
+
if self.trace_id is None:
|
|
839
|
+
self.trace_id = uuid4()
|
|
840
|
+
if self.start_time is None:
|
|
841
|
+
self.start_time = datetime.now(timezone.utc)
|
|
842
|
+
trace = context.get_current_trace()
|
|
843
|
+
if trace is not None and self.trace_id == trace.trace_id:
|
|
844
|
+
inherited = trace.span_context()
|
|
845
|
+
if self.export_context is None:
|
|
846
|
+
self.export_context = trace.export_context
|
|
847
|
+
else:
|
|
848
|
+
config = configuration.get_config()
|
|
849
|
+
producer = context._current_producer_context()
|
|
850
|
+
optimization_run_id = producer.get("optimization_run_id") or context.get_current_optimization_run_id()
|
|
851
|
+
eval_run = context.get_current_eval_run()
|
|
852
|
+
inherited = SpanContext(
|
|
853
|
+
project_tags=tuple(producer["project_tags"])
|
|
854
|
+
if "project_tags" in producer
|
|
855
|
+
else (tuple(config.project_tags or ()) if config else ()),
|
|
856
|
+
# A run's tags are more specific than the process defaults, so they win.
|
|
857
|
+
tags=tuple({**(config.default_tags if config else {}), **producer.get("tags", {})}.items()),
|
|
858
|
+
program_id=producer.get("program_id")
|
|
859
|
+
or (config.program_id if config else None)
|
|
860
|
+
or context.optimization_program_id(optimization_run_id),
|
|
861
|
+
deployment_id=producer.get("deployment_id"),
|
|
862
|
+
deployment_run_id=producer.get("deployment_run_id"),
|
|
863
|
+
optimization_run_id=optimization_run_id,
|
|
864
|
+
eval_run_id=producer.get("eval_run_id") or (eval_run.eval_run_id if eval_run else None),
|
|
865
|
+
compiled_from_run_id=producer.get("compiled_from_run_id"),
|
|
866
|
+
)
|
|
867
|
+
inherited = self.span_context or inherited
|
|
868
|
+
refs = {
|
|
869
|
+
name: getattr(self, name) if getattr(self, name) is not None else getattr(inherited, name)
|
|
870
|
+
for name in (
|
|
871
|
+
"program_id",
|
|
872
|
+
"deployment_id",
|
|
873
|
+
"deployment_run_id",
|
|
874
|
+
"optimization_run_id",
|
|
875
|
+
"eval_run_id",
|
|
876
|
+
"compiled_from_run_id",
|
|
877
|
+
)
|
|
878
|
+
}
|
|
879
|
+
self.span_context = SpanContext(
|
|
880
|
+
project_tags=tuple(self.project_tags) if self.project_tags is not None else inherited.project_tags,
|
|
881
|
+
tags=tuple({**dict(inherited.tags), **self.tags}.items()),
|
|
882
|
+
**refs,
|
|
883
|
+
)
|
|
884
|
+
self.project_tags = list(self.span_context.project_tags)
|
|
885
|
+
self.tags = dict(self.span_context.tags)
|
|
886
|
+
if self.export_context is None:
|
|
887
|
+
self.export_context = ExportContext(program_id=self.span_context.program_id, resolved=True)
|
|
888
|
+
if self.span_type == SpanType.LM_CALL and self.lm_usage is None:
|
|
889
|
+
self.lm_usage = LMUsageObservationV1()
|
|
821
890
|
|
|
822
891
|
def set_inputs(self, inputs: dict[str, Any]) -> None:
|
|
823
892
|
"""Set span inputs."""
|
|
@@ -882,7 +951,13 @@ class SpanBuilder:
|
|
|
882
951
|
def to_api_dict(self) -> dict[str, Any]:
|
|
883
952
|
"""Convert to API-compatible dictionary."""
|
|
884
953
|
max_in, max_out = self._limits()
|
|
954
|
+
if self.span_type == SpanType.LM_CALL:
|
|
955
|
+
if self.lm_usage is None:
|
|
956
|
+
self.lm_usage = LMUsageObservationV1()
|
|
957
|
+
if self.lm_usage.observed_at is None:
|
|
958
|
+
self.lm_usage.observed_at = self.end_time or self.start_time
|
|
885
959
|
return {
|
|
960
|
+
"schema_version": 1,
|
|
886
961
|
"span_id": str(self.span_id),
|
|
887
962
|
"trace_id": str(self.trace_id) if self.trace_id else None,
|
|
888
963
|
"parent_span_id": str(self.parent_span_id) if self.parent_span_id else None,
|
|
@@ -923,10 +998,18 @@ class SpanBuilder:
|
|
|
923
998
|
"attributes": _safe_serialize(self._build_attributes(max_out)),
|
|
924
999
|
"events": truncate_field(_safe_serialize(self.events), max_out),
|
|
925
1000
|
"adapter_type": self.adapter_type,
|
|
926
|
-
"project": self.project_tags[0]
|
|
927
|
-
|
|
1001
|
+
"project": self.span_context.project_tags[0]
|
|
1002
|
+
if self.span_context and self.span_context.project_tags
|
|
1003
|
+
else None,
|
|
1004
|
+
**(_safe_serialize(self.span_context.to_dict()) if self.span_context else {}),
|
|
928
1005
|
}
|
|
929
1006
|
|
|
1007
|
+
def seal_for_export(self) -> SpanBody:
|
|
1008
|
+
"""Freeze the first completed body for every route that later carries it."""
|
|
1009
|
+
if self._submitted_body is None:
|
|
1010
|
+
self._submitted_body = SpanBody(self.to_api_dict())
|
|
1011
|
+
return self._submitted_body
|
|
1012
|
+
|
|
930
1013
|
|
|
931
1014
|
@dataclass
|
|
932
1015
|
class TraceBuilder:
|
|
@@ -958,6 +1041,8 @@ class TraceBuilder:
|
|
|
958
1041
|
# the configuration at construction like project_tags. None declares none,
|
|
959
1042
|
# and the key is then left off the wire rather than sent as null.
|
|
960
1043
|
program_id: str | None = field(default_factory=configuration._current_program_id)
|
|
1044
|
+
deployment_id: str | None = None
|
|
1045
|
+
deployment_run_id: str | None = None
|
|
961
1046
|
# The program name the trace declares, or None. It never goes on the wire: the
|
|
962
1047
|
# exporter resolves it to a program id and writes that into ``program_id`` on the
|
|
963
1048
|
# bodies it sends, unless the trace already carries an id.
|
|
@@ -1010,10 +1095,41 @@ class TraceBuilder:
|
|
|
1010
1095
|
_sealed_tags: dict[str, str] = field(default_factory=dict, init=False, repr=False)
|
|
1011
1096
|
_sealed_metadata: dict[str, str] = field(default_factory=dict, init=False, repr=False)
|
|
1012
1097
|
_sealed_project_tags: tuple[str, ...] = field(default=(), init=False, repr=False)
|
|
1098
|
+
_export_context: ExportContext | None = field(default=None, init=False, repr=False)
|
|
1099
|
+
|
|
1100
|
+
@property
|
|
1101
|
+
def export_context(self) -> ExportContext:
|
|
1102
|
+
"""One resolution decision shared by the trace and every queued span."""
|
|
1103
|
+
if self._export_context is None:
|
|
1104
|
+
name = (
|
|
1105
|
+
ProgramName(self.declared_name)
|
|
1106
|
+
if self.declared_name
|
|
1107
|
+
else (ProgramName(self.generated_name, generated=True) if self.generated_name else None)
|
|
1108
|
+
)
|
|
1109
|
+
self._export_context = ExportContext(name, self.program_id, self.program_id is not None or name is None)
|
|
1110
|
+
return self._export_context
|
|
1111
|
+
|
|
1112
|
+
@property
|
|
1113
|
+
def span_export_contexts(self) -> tuple[ExportContext | None, ...]:
|
|
1114
|
+
return tuple(span.export_context for span in self._batch_spans)
|
|
1115
|
+
|
|
1116
|
+
def span_context(self) -> SpanContext:
|
|
1117
|
+
"""Snapshot the context for a span beginning now."""
|
|
1118
|
+
return SpanContext(
|
|
1119
|
+
project_tags=tuple(self.project_tags or ()),
|
|
1120
|
+
tags=tuple(self.tags.items()),
|
|
1121
|
+
program_id=self.program_id,
|
|
1122
|
+
deployment_id=self.deployment_id,
|
|
1123
|
+
deployment_run_id=self.deployment_run_id,
|
|
1124
|
+
optimization_run_id=self.optimization_run_id,
|
|
1125
|
+
eval_run_id=self.eval_run_id,
|
|
1126
|
+
compiled_from_run_id=self.compiled_from_run_id,
|
|
1127
|
+
)
|
|
1013
1128
|
|
|
1014
1129
|
def seal_start(self) -> None:
|
|
1015
1130
|
"""Retain the first creation snapshot and its independent annotations."""
|
|
1016
1131
|
if self._started_fact is None:
|
|
1132
|
+
self.export_context
|
|
1017
1133
|
self._started_fact = StartedFact.from_payload(self.to_start_dict())
|
|
1018
1134
|
self.seal_metadata()
|
|
1019
1135
|
|
|
@@ -1052,8 +1168,16 @@ class TraceBuilder:
|
|
|
1052
1168
|
self.seal_metadata()
|
|
1053
1169
|
|
|
1054
1170
|
def _with_facts(self, payload: dict[str, Any], *, completion: bool = False, started: bool = True) -> dict[str, Any]:
|
|
1171
|
+
payload["schema_version"] = 1
|
|
1172
|
+
if self.deployment_id is not None:
|
|
1173
|
+
payload["deployment_id"] = self.deployment_id
|
|
1174
|
+
if self.deployment_run_id is not None:
|
|
1175
|
+
payload["deployment_run_id"] = self.deployment_run_id
|
|
1055
1176
|
if self._started_fact is not None:
|
|
1056
1177
|
creation = self._started_fact.creation_dict()
|
|
1178
|
+
for name in ("program_id", "deployment_id", "deployment_run_id"):
|
|
1179
|
+
if name not in creation:
|
|
1180
|
+
payload.pop(name, None)
|
|
1057
1181
|
payload.update(creation if started else {key: value for key, value in creation.items() if key in payload})
|
|
1058
1182
|
if started:
|
|
1059
1183
|
payload["started_fact"] = self._started_fact.to_dict()
|
|
@@ -1099,7 +1223,7 @@ class TraceBuilder:
|
|
|
1099
1223
|
"metadata": _safe_serialize(self.metadata),
|
|
1100
1224
|
"project": self.project_tags[0] if self.project_tags else None,
|
|
1101
1225
|
"project_tags": self.project_tags if self.project_tags else [],
|
|
1102
|
-
"spans": [s.to_api_dict() for s in self._batch_spans] if self._batch_spans else [],
|
|
1226
|
+
"spans": [s.seal_for_export().to_api_dict() for s in self._batch_spans] if self._batch_spans else [],
|
|
1103
1227
|
**self._declared_program(),
|
|
1104
1228
|
},
|
|
1105
1229
|
completion=True,
|
|
@@ -151,8 +151,8 @@ endpoint. None of these reads the global config:
|
|
|
151
151
|
metadata=None, tags=None, lm=None, **inputs)` runs one deployment and returns
|
|
152
152
|
a `dspy.Prediction`. `deployment` is a `DeployedProgram` or a deployment id.
|
|
153
153
|
`entrypoint` picks the module to run inside a deployment that holds more than
|
|
154
|
-
one. `metadata` and `tags` are `dict[str, str]` values the
|
|
155
|
-
|
|
154
|
+
one. `metadata` and `tags` are `dict[str, str]` values the run's traces and
|
|
155
|
+
spans carry, overriding the worker process's `default_tags`. The run spends the credential its deployment
|
|
156
156
|
recorded. A `program_name` among the inputs raises `TypeError` and names
|
|
157
157
|
`entrypoint=`.
|
|
158
158
|
- `sdk.submit_deployment_run`, `list_deployment_runs`, `get_deployment_run`,
|