cmpnd 0.83.0__tar.gz → 0.85.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.85.0}/AGENTS.md +7 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/PKG-INFO +2 -2
- {cmpnd-0.83.0 → cmpnd-0.85.0}/README.md +1 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/callback.py +228 -110
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/configuration.py +3 -3
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/context.py +41 -2
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/helpers.py +6 -4
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/reflection.py +4 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/models.py +166 -6
- cmpnd-0.85.0/cmpnd/references.py +180 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/client.md +2 -2
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/sdk-instrumentation.md +33 -8
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/README.md +20 -3
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/bodies.py +14 -7
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/exporter_http.py +84 -47
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/program_names.py +4 -29
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/protocols.py +19 -1
- cmpnd-0.85.0/ingest/cmpnd_ingest/span_context.py +63 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/trace_facts.py +4 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/pyproject.toml +1 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/test-in-linux.sh +18 -4
- cmpnd-0.85.0/tests/lm15_prompt_fixture.py +350 -0
- cmpnd-0.85.0/tests/lm15_prompts/four_turn_predict.json +851 -0
- cmpnd-0.85.0/tests/lm15_prompts/rlm_six_turns.json +1726 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/rlm_shape.py +4 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/test_rlm_shape.py +3 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/span_reference_fixture.py +9 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_callback.py +6 -2
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_context.py +20 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_credential_kwargs.py +28 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_credential_selection.py +1 -1
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_exporter_program_names.py +7 -4
- cmpnd-0.85.0/tests/test_lm15_prompt_fixtures.py +94 -0
- cmpnd-0.85.0/tests/test_lm15_prompt_references.py +309 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_program_identity.py +2 -2
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_program_naming.py +39 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_reflector_smoke.py +2 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_span_references.py +18 -0
- cmpnd-0.85.0/tests/test_span_submission.py +492 -0
- cmpnd-0.85.0/tests/test_span_submission_wire_contract.py +190 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/uv.lock +1 -1
- cmpnd-0.83.0/cmpnd/references.py +0 -80
- {cmpnd-0.83.0 → cmpnd-0.85.0}/.gitignore +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/CLAUDE.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/program_naming.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/cli.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/drain.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_client.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_eval.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_eval_program_id.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_execute.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_identity.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_integration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_models.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimization_phase_callback.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_retry.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.83.0 → cmpnd-0.85.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.85.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
|
|
@@ -668,18 +687,20 @@ class _CallbackBase:
|
|
|
668
687
|
# repl_history (see _AdapterMixin._thin_rlm_adapter_inputs).
|
|
669
688
|
self._rlm_turn_adapter_spans: set[UUID] = set()
|
|
670
689
|
|
|
671
|
-
# Parent span -> the last adapter ``format`` span under it and
|
|
672
|
-
#
|
|
673
|
-
# _AdapterMixin.
|
|
674
|
-
|
|
690
|
+
# Parent span -> the last adapter ``format`` span under it and the full
|
|
691
|
+
# value of each prompt key it stored whole, for the LM calls that follow
|
|
692
|
+
# to reference. See _AdapterMixin._remember_rendered_prompt and
|
|
693
|
+
# _LMMixin._reference_rendered_prompt.
|
|
694
|
+
self._rendered_prompts: dict[UUID, tuple[models.SpanBuilder, dict[str, Any]]] = {}
|
|
675
695
|
|
|
676
696
|
# Open module span -> id() of its module instance, so an adapter span can
|
|
677
697
|
# name the predictor it renders for. And (trace, predictor) -> the last
|
|
678
|
-
# rendering of each
|
|
679
|
-
#
|
|
680
|
-
# ends. See
|
|
698
|
+
# rendering of each prompt text that serve can rebuild in full, by its
|
|
699
|
+
# path in the format span's outputs, as (the format span storing it, its
|
|
700
|
+
# full text). Dropped when the trace ends. See
|
|
701
|
+
# _AdapterMixin._extend_previous_rendering.
|
|
681
702
|
self._module_instance_by_span: dict[UUID, int] = {}
|
|
682
|
-
self._last_rendering: dict[tuple[str, int],
|
|
703
|
+
self._last_rendering: dict[tuple[str, int], dict[tuple[str | int, ...], tuple[UUID, str]]] = {}
|
|
683
704
|
|
|
684
705
|
# Ensure exporter is running
|
|
685
706
|
self._ensure_exporter()
|
|
@@ -722,19 +743,18 @@ class _CallbackBase:
|
|
|
722
743
|
return configuration._current_program_id()
|
|
723
744
|
|
|
724
745
|
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.
|
|
746
|
+
"""Stamp this callback's trace context before any spans begin.
|
|
728
747
|
|
|
729
748
|
A trace from a call that declares a program name carries the name and no id,
|
|
730
749
|
and the exporter resolves the name. The declared name wins over a program id the
|
|
731
750
|
client's config read from ``CMPND_PROGRAM_ID``, and the callback warns once that it did.
|
|
732
751
|
A trace that gets neither gets a generated name in `_create_trace`.
|
|
733
752
|
"""
|
|
734
|
-
builder.project_tags = self._resolve_project_tags()
|
|
735
753
|
if isinstance(builder, models.TraceBuilder):
|
|
736
|
-
|
|
737
|
-
|
|
754
|
+
builder.project_tags = list(self._resolve_project_tags() or ())
|
|
755
|
+
producer_program_id = dict(self._producer_references).get("program_id")
|
|
756
|
+
program_id = producer_program_id or self._resolve_program_id()
|
|
757
|
+
name = None if producer_program_id else _declared_program_name()
|
|
738
758
|
if name is not None:
|
|
739
759
|
if program_id is not None and not self._warned_name_over_id:
|
|
740
760
|
self._warned_name_over_id = True
|
|
@@ -765,6 +785,18 @@ class _CallbackBase:
|
|
|
765
785
|
return _exporter_pool.ensure_exporter_started(self._own_config)
|
|
766
786
|
return exporter.get_exporter()
|
|
767
787
|
|
|
788
|
+
def _apply_producer_context(self, trace: models.TraceBuilder) -> None:
|
|
789
|
+
for name, value in self._producer_references:
|
|
790
|
+
setattr(trace, name, value)
|
|
791
|
+
if dict(self._producer_references).get("program_id") is not None:
|
|
792
|
+
trace.declared_name = trace.generated_name = None
|
|
793
|
+
if self._producer_projects is not None:
|
|
794
|
+
trace.project_tags = list(self._producer_projects)
|
|
795
|
+
# A run's tags and metadata are more specific than the process defaults
|
|
796
|
+
# already on the trace, so they override them.
|
|
797
|
+
trace.tags.update(self._producer_tags)
|
|
798
|
+
trace.metadata.update(self._producer_metadata)
|
|
799
|
+
|
|
768
800
|
def _ensure_exporter(self) -> Any:
|
|
769
801
|
"""Start this callback's exporter if it is not already running."""
|
|
770
802
|
if self._own_config is not None:
|
|
@@ -780,6 +812,60 @@ class _CallbackBase:
|
|
|
780
812
|
span makes ``to_api_dict`` bound the whole body by the same limits rather
|
|
781
813
|
than by the 64 KiB default.
|
|
782
814
|
"""
|
|
815
|
+
if context.get_current_trace() is None:
|
|
816
|
+
config = self._own_config or configuration.get_config()
|
|
817
|
+
kwargs.setdefault(
|
|
818
|
+
"project_tags",
|
|
819
|
+
list(
|
|
820
|
+
self._producer_projects
|
|
821
|
+
if self._producer_projects is not None
|
|
822
|
+
else self._resolve_project_tags() or ()
|
|
823
|
+
),
|
|
824
|
+
)
|
|
825
|
+
# Most specific wins: process defaults, then the run's tags, then the span's own.
|
|
826
|
+
kwargs["tags"] = {
|
|
827
|
+
**(config.default_tags if config else {}),
|
|
828
|
+
**dict(self._producer_tags),
|
|
829
|
+
**kwargs.get("tags", {}),
|
|
830
|
+
}
|
|
831
|
+
for key, value in self._producer_references:
|
|
832
|
+
kwargs.setdefault(key, value)
|
|
833
|
+
run_id = self._active_optimization_run_id or context.get_current_optimization_run_id()
|
|
834
|
+
kwargs.setdefault("optimization_run_id", run_id)
|
|
835
|
+
kwargs.setdefault("eval_run_id", self._active_eval_run.eval_run_id if self._active_eval_run else None)
|
|
836
|
+
program_id = (
|
|
837
|
+
kwargs.get("program_id") or self._resolve_program_id() or context.optimization_program_id(run_id)
|
|
838
|
+
)
|
|
839
|
+
declared = None if kwargs.get("program_id") else _declared_program_name()
|
|
840
|
+
if declared is not None:
|
|
841
|
+
program_id = None
|
|
842
|
+
kwargs.setdefault("program_id", program_id)
|
|
843
|
+
from cmpnd_ingest.program_names import ProgramName
|
|
844
|
+
from cmpnd_ingest.span_context import ExportContext, SpanContext
|
|
845
|
+
|
|
846
|
+
kwargs.setdefault(
|
|
847
|
+
"export_context",
|
|
848
|
+
ExportContext(
|
|
849
|
+
ProgramName(declared) if declared else None,
|
|
850
|
+
program_id,
|
|
851
|
+
declared is None or program_id is not None,
|
|
852
|
+
),
|
|
853
|
+
)
|
|
854
|
+
phase = self._optimization_phase or (
|
|
855
|
+
("metric" if self._active_eval_run else "reflection") if run_id else None
|
|
856
|
+
)
|
|
857
|
+
if phase is not None:
|
|
858
|
+
kwargs["tags"]["optimization_phase"] = phase
|
|
859
|
+
kwargs.setdefault(
|
|
860
|
+
"span_context",
|
|
861
|
+
SpanContext(
|
|
862
|
+
project_tags=tuple(kwargs["project_tags"]),
|
|
863
|
+
tags=tuple(kwargs["tags"].items()),
|
|
864
|
+
program_id=program_id,
|
|
865
|
+
optimization_run_id=kwargs["optimization_run_id"],
|
|
866
|
+
eval_run_id=kwargs["eval_run_id"],
|
|
867
|
+
),
|
|
868
|
+
)
|
|
783
869
|
return context.create_child_span(
|
|
784
870
|
name,
|
|
785
871
|
span_type,
|
|
@@ -974,7 +1060,7 @@ class _ModuleMixin(_CallbackBase):
|
|
|
974
1060
|
|
|
975
1061
|
# An adapter rendering this module's prompt is only ever referenced by an
|
|
976
1062
|
# LM call under this module, which has ended with it.
|
|
977
|
-
self.
|
|
1063
|
+
self._rendered_prompts.pop(span.span_id, None)
|
|
978
1064
|
self._module_instance_by_span.pop(span.span_id, None)
|
|
979
1065
|
|
|
980
1066
|
# RLM repl_history: append trajectory data from PREDICT children
|
|
@@ -1129,9 +1215,9 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1129
1215
|
|
|
1130
1216
|
def _forget_trace_references(self, trace_id: UUID) -> None:
|
|
1131
1217
|
"""Drop the per-trace state reference encoding keeps, once the trace ends."""
|
|
1132
|
-
for parent, (fmt_span, _) in list(self.
|
|
1218
|
+
for parent, (fmt_span, _) in list(self._rendered_prompts.items()):
|
|
1133
1219
|
if fmt_span.trace_id == trace_id:
|
|
1134
|
-
self.
|
|
1220
|
+
self._rendered_prompts.pop(parent, None)
|
|
1135
1221
|
for key in [k for k in self._last_rendering if k[0] == str(trace_id)]:
|
|
1136
1222
|
self._last_rendering.pop(key, None)
|
|
1137
1223
|
|
|
@@ -1220,7 +1306,7 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1220
1306
|
|
|
1221
1307
|
def _create_trace(self, instance: Any, inputs: dict[str, Any]) -> models.TraceBuilder:
|
|
1222
1308
|
"""Create a new trace for a root module call."""
|
|
1223
|
-
config = configuration.get_config()
|
|
1309
|
+
config = self._own_config or configuration.get_config()
|
|
1224
1310
|
|
|
1225
1311
|
trace = models.TraceBuilder(
|
|
1226
1312
|
trace_id=uuid4(),
|
|
@@ -1364,16 +1450,16 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1364
1450
|
preview = helpers.create_preview(helpers.clean_inputs(inputs, dspy, self._max_input_field_size))
|
|
1365
1451
|
trace.request_preview = preview
|
|
1366
1452
|
|
|
1367
|
-
#
|
|
1453
|
+
# Process default tags first, then the run's own labels over them.
|
|
1368
1454
|
if config and config.default_tags:
|
|
1369
1455
|
trace.tags.update(config.default_tags)
|
|
1456
|
+
self._apply_producer_context(trace)
|
|
1370
1457
|
# A trace opened while this callback traces a phase, or while a local run
|
|
1371
1458
|
# calls its metric, is the metric's or the proposer's, never a rollout, even
|
|
1372
1459
|
# when its module is the program's own class.
|
|
1373
1460
|
phase = self._optimization_phase or context.get_current_optimization_phase()
|
|
1374
1461
|
if phase is not None:
|
|
1375
1462
|
trace.tags["optimization_phase"] = phase
|
|
1376
|
-
|
|
1377
1463
|
return trace
|
|
1378
1464
|
|
|
1379
1465
|
def _extract_usage_from_prediction(self, span: models.SpanBuilder, outputs: Any, dspy: Any) -> None:
|
|
@@ -1456,39 +1542,53 @@ class _LMMixin(_CallbackBase):
|
|
|
1456
1542
|
|
|
1457
1543
|
# Capture prompt/messages if enabled. A credential passed to the call itself, as in
|
|
1458
1544
|
# `lm(prompt, api_key=...)`, is stripped first: every member of the org reads a span.
|
|
1545
|
+
# The rendered prompt becomes a reference before truncation, so the rest of the
|
|
1546
|
+
# inputs keep the whole budget.
|
|
1459
1547
|
if self._capture_prompts:
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
span.set_inputs(cleaned_inputs)
|
|
1548
|
+
referenced = self._reference_rendered_prompt(parent_span, helpers.without_credentials(inputs))
|
|
1549
|
+
span.set_inputs(helpers.clean_inputs(referenced, dspy, self._max_input_field_size))
|
|
1463
1550
|
|
|
1464
1551
|
self._call_id_to_span[call_id] = span
|
|
1465
1552
|
# Store LM instance reference to access history for token usage
|
|
1466
1553
|
self._call_id_to_instance[call_id] = instance
|
|
1467
1554
|
context.push_span(span)
|
|
1468
1555
|
|
|
1469
|
-
def
|
|
1470
|
-
self, parent_span: models.SpanBuilder | None, inputs: dict[str, Any]
|
|
1471
|
-
) ->
|
|
1472
|
-
"""
|
|
1556
|
+
def _reference_rendered_prompt(
|
|
1557
|
+
self, parent_span: models.SpanBuilder | None, inputs: dict[str, Any]
|
|
1558
|
+
) -> dict[str, Any]:
|
|
1559
|
+
"""``inputs`` with the rendered prompt as references to the adapter output it came from.
|
|
1473
1560
|
|
|
1474
1561
|
The adapter's ``format`` span ended just before this call started, under
|
|
1475
|
-
the same parent, and
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1562
|
+
the same parent, and stored the rendered prompt (``references.prompt_keys``).
|
|
1563
|
+
In the list shape, ``messages`` equal to the stored ``result`` becomes a
|
|
1564
|
+
``_same_as`` reference to it. In the typed shape, each of ``prompt.system``
|
|
1565
|
+
and ``prompt.messages`` equal to the stored key becomes a reference to that
|
|
1566
|
+
key, and the rest of the request stays inline. Values are compared in full,
|
|
1567
|
+
before any truncation, and serve resolves the references on read.
|
|
1568
|
+
Anything else, such as messages the adapter changed after rendering or an
|
|
1569
|
+
LM call no adapter rendered for, like an RLM ``llm_query``, keeps a full
|
|
1570
|
+
copy, so a mismatch costs bytes, never correctness. Each LM call the
|
|
1571
|
+
adapter makes from one rendering gets the references.
|
|
1482
1572
|
"""
|
|
1483
|
-
if parent_span is None
|
|
1484
|
-
return
|
|
1485
|
-
remembered = self._rendered_messages.pop(parent_span.span_id, None)
|
|
1573
|
+
remembered = self._rendered_prompts.get(parent_span.span_id) if parent_span is not None else None
|
|
1486
1574
|
if remembered is None:
|
|
1487
|
-
return
|
|
1575
|
+
return inputs
|
|
1488
1576
|
fmt_span, rendered = remembered
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1577
|
+
fmt_id = str(fmt_span.span_id)
|
|
1578
|
+
if "result" in rendered:
|
|
1579
|
+
if "messages" in inputs and models._safe_serialize(inputs["messages"]) == rendered["result"]:
|
|
1580
|
+
return {**inputs, "messages": references.same_as(fmt_id, "outputs", ["result"])}
|
|
1581
|
+
return inputs
|
|
1582
|
+
if inputs.get("prompt") is None:
|
|
1583
|
+
return inputs
|
|
1584
|
+
prompt = models._safe_serialize(inputs["prompt"])
|
|
1585
|
+
if not isinstance(prompt, dict):
|
|
1586
|
+
return inputs
|
|
1587
|
+
referenced = dict(prompt)
|
|
1588
|
+
for key in references.TYPED_PROMPT_KEYS:
|
|
1589
|
+
if key in rendered and key in prompt and prompt[key] == rendered[key]:
|
|
1590
|
+
referenced[key] = references.same_as(fmt_id, "outputs", [key])
|
|
1591
|
+
return inputs if referenced == prompt else {**inputs, "prompt": referenced}
|
|
1492
1592
|
|
|
1493
1593
|
def on_lm_end(
|
|
1494
1594
|
self,
|
|
@@ -1631,7 +1731,8 @@ class _LMMixin(_CallbackBase):
|
|
|
1631
1731
|
# would double-count them in the optimization total.
|
|
1632
1732
|
current_trace = context.get_current_trace()
|
|
1633
1733
|
if current_trace is None and (
|
|
1634
|
-
|
|
1734
|
+
(span.span_context is not None and span.span_context.optimization_run_id is not None)
|
|
1735
|
+
or self._optimization_phase is not None
|
|
1635
1736
|
):
|
|
1636
1737
|
self._emit_optimization_lm_trace(span)
|
|
1637
1738
|
return # handled as a standalone GEPA-process trace
|
|
@@ -1678,11 +1779,12 @@ class _LMMixin(_CallbackBase):
|
|
|
1678
1779
|
instead, and may have no run id: it traces the metric or the proposer of a
|
|
1679
1780
|
run whose id the ingest key the trace is pushed with decides.
|
|
1680
1781
|
"""
|
|
1681
|
-
|
|
1782
|
+
assert span.span_context is not None
|
|
1783
|
+
run_id = span.span_context.optimization_run_id
|
|
1682
1784
|
if not run_id and self._optimization_phase is None:
|
|
1683
1785
|
return
|
|
1684
1786
|
|
|
1685
|
-
phase =
|
|
1787
|
+
phase = dict(span.span_context.tags).get("optimization_phase", "reflection")
|
|
1686
1788
|
span.attributes["gepa.phase"] = phase
|
|
1687
1789
|
span.attributes["optimization.role"] = _OPTIMIZATION_PHASE_ROLES[phase]
|
|
1688
1790
|
|
|
@@ -1692,11 +1794,14 @@ class _LMMixin(_CallbackBase):
|
|
|
1692
1794
|
end_time=span.end_time,
|
|
1693
1795
|
module_class="GEPA",
|
|
1694
1796
|
)
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1797
|
+
trace.program_id = span.span_context.program_id
|
|
1798
|
+
trace.deployment_id = span.span_context.deployment_id
|
|
1799
|
+
trace.deployment_run_id = span.span_context.deployment_run_id
|
|
1800
|
+
trace.compiled_from_run_id = span.span_context.compiled_from_run_id
|
|
1801
|
+
trace.project_tags = list(span.span_context.project_tags)
|
|
1802
|
+
trace.tags = dict(span.span_context.tags)
|
|
1803
|
+
trace.metadata = dict(self._producer_metadata)
|
|
1804
|
+
trace._export_context = span.export_context
|
|
1700
1805
|
# A metric-phase call can start while DSPy's Evaluate span is still on the
|
|
1701
1806
|
# context stack, so on_lm_start stamped this span with a parent_span_id for
|
|
1702
1807
|
# a span that isn't in this standalone one-span trace. This span IS the
|
|
@@ -1707,8 +1812,7 @@ class _LMMixin(_CallbackBase):
|
|
|
1707
1812
|
trace.signature_name = span.model_name or phase
|
|
1708
1813
|
trace.status = span.status
|
|
1709
1814
|
trace.optimization_run_id = run_id
|
|
1710
|
-
|
|
1711
|
-
trace.eval_run_id = self._active_eval_run.eval_run_id
|
|
1815
|
+
trace.eval_run_id = span.span_context.eval_run_id
|
|
1712
1816
|
trace.tags["optimization_phase"] = phase
|
|
1713
1817
|
|
|
1714
1818
|
# Single-span trace: roll the span's usage up onto the trace so the
|
|
@@ -1874,12 +1978,16 @@ class _AdapterMixin(_CallbackBase):
|
|
|
1874
1978
|
if self._capture_outputs and outputs:
|
|
1875
1979
|
rendered = models._safe_serialize(outputs)
|
|
1876
1980
|
if span.span_type == models.SpanType.ADAPTER_FORMAT:
|
|
1877
|
-
#
|
|
1878
|
-
#
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
self.
|
|
1981
|
+
# The outputs as stored: a typed Prompt as its own dict, a list
|
|
1982
|
+
# of messages under "result". Encode against the previous
|
|
1983
|
+
# rendering first, then truncate: only a suffix that alone
|
|
1984
|
+
# overflows the budget is cut.
|
|
1985
|
+
body = rendered if isinstance(rendered, dict) else {"result": rendered}
|
|
1986
|
+
encoded = self._extend_previous_rendering(span, body)
|
|
1987
|
+
stored = encoded if isinstance(rendered, dict) else encoded["result"]
|
|
1988
|
+
span.set_outputs(helpers.truncate_outputs(stored, self._max_output_field_size))
|
|
1989
|
+
self._record_rendering(span, body)
|
|
1990
|
+
self._remember_rendered_prompt(span, body)
|
|
1883
1991
|
else:
|
|
1884
1992
|
span.set_outputs(helpers.truncate_outputs(rendered, self._max_output_field_size))
|
|
1885
1993
|
|
|
@@ -1904,78 +2012,81 @@ class _AdapterMixin(_CallbackBase):
|
|
|
1904
2012
|
predictor = self._module_instance_by_span.get(span.parent_span_id)
|
|
1905
2013
|
return None if predictor is None else (str(span.trace_id), predictor)
|
|
1906
2014
|
|
|
1907
|
-
def _extend_previous_rendering(self, span: models.SpanBuilder,
|
|
1908
|
-
"""``
|
|
2015
|
+
def _extend_previous_rendering(self, span: models.SpanBuilder, body: dict[str, Any]) -> dict[str, Any]:
|
|
2016
|
+
"""``body`` with each prompt text that extends the predictor's last one encoded.
|
|
1909
2017
|
|
|
1910
2018
|
A predictor called once per turn — an RLM's action step, a ReAct step —
|
|
1911
|
-
renders the previous turn's prompt plus what the turn added. For each
|
|
1912
|
-
|
|
1913
|
-
rendering this predictor stored in this trace,
|
|
1914
|
-
``_extends`` envelope over that stored
|
|
1915
|
-
|
|
1916
|
-
|
|
2019
|
+
renders the previous turn's prompt plus what the turn added. For each text
|
|
2020
|
+
of the prompt (``references.prompt_text_leaves``) that begins with the text
|
|
2021
|
+
at the same path in the last rendering this predictor stored in this trace,
|
|
2022
|
+
the text becomes an ``_extends`` envelope over that stored text. In the
|
|
2023
|
+
typed shape this covers the system prompt, which is the same on every turn,
|
|
2024
|
+
as well as each message's text parts. Matching compares the full rendered
|
|
2025
|
+
text and needs no knowledge of the module. The base may itself be an
|
|
2026
|
+
extension, and serve resolves the chain.
|
|
1917
2027
|
"""
|
|
1918
2028
|
key = self._rendering_key(span)
|
|
1919
2029
|
previous = self._last_rendering.get(key) if key else None
|
|
1920
|
-
if not previous
|
|
1921
|
-
return
|
|
1922
|
-
encoded =
|
|
1923
|
-
for
|
|
1924
|
-
base = previous
|
|
1925
|
-
|
|
1926
|
-
if base is None or not isinstance(content, str):
|
|
2030
|
+
if not previous:
|
|
2031
|
+
return body
|
|
2032
|
+
encoded = body
|
|
2033
|
+
for path, text in references.prompt_text_leaves(body):
|
|
2034
|
+
base = previous.get(path)
|
|
2035
|
+
if base is None:
|
|
1927
2036
|
continue
|
|
1928
|
-
base_span_id,
|
|
1929
|
-
env = references.extends(
|
|
1930
|
-
base_content, content, references.ref(str(base_span_id), "outputs", ["result", i, "content"])
|
|
1931
|
-
)
|
|
2037
|
+
base_span_id, base_text = base
|
|
2038
|
+
env = references.extends(base_text, text, references.ref(str(base_span_id), "outputs", list(path)))
|
|
1932
2039
|
if env is not None:
|
|
1933
|
-
encoded
|
|
2040
|
+
encoded = references.with_value_at(encoded, path, env)
|
|
1934
2041
|
return encoded
|
|
1935
2042
|
|
|
1936
|
-
def _record_rendering(self, span: models.SpanBuilder,
|
|
2043
|
+
def _record_rendering(self, span: models.SpanBuilder, body: dict[str, Any]) -> None:
|
|
1937
2044
|
"""Keep this rendering as the base for the predictor's next one.
|
|
1938
2045
|
|
|
1939
|
-
A
|
|
2046
|
+
A text becomes the next base only where its stored form resolves to the
|
|
1940
2047
|
full text: a whole string, or an extension whose suffix survived
|
|
1941
|
-
truncation. Where it was cut, the previous base stays, so the
|
|
1942
|
-
extends a
|
|
2048
|
+
truncation. Where it was cut, the previous base at that path stays, so the
|
|
2049
|
+
next turn extends a text serve can still rebuild.
|
|
1943
2050
|
"""
|
|
1944
2051
|
key = self._rendering_key(span)
|
|
1945
|
-
|
|
1946
|
-
if key is None or not isinstance(rendered, list):
|
|
2052
|
+
if key is None or not references.prompt_keys(body):
|
|
1947
2053
|
return
|
|
1948
|
-
previous = self._last_rendering.get(key) or
|
|
1949
|
-
current:
|
|
1950
|
-
for
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
isinstance(
|
|
1956
|
-
and set(kept_content) == {"_extends", "_prefix_chars", "_suffix"}
|
|
1957
|
-
and isinstance(kept_content["_suffix"], str)
|
|
2054
|
+
previous = self._last_rendering.get(key) or {}
|
|
2055
|
+
current: dict[tuple[str | int, ...], tuple[UUID, str]] = {}
|
|
2056
|
+
for path, text in references.prompt_text_leaves(body):
|
|
2057
|
+
kept = references.value_at(span.outputs, path)
|
|
2058
|
+
resolvable = kept == text or (
|
|
2059
|
+
isinstance(kept, dict)
|
|
2060
|
+
and set(kept) == {"_extends", "_prefix_chars", "_suffix"}
|
|
2061
|
+
and isinstance(kept["_suffix"], str)
|
|
1958
2062
|
)
|
|
1959
|
-
if
|
|
1960
|
-
current
|
|
1961
|
-
|
|
1962
|
-
current
|
|
2063
|
+
if resolvable:
|
|
2064
|
+
current[path] = (span.span_id, text)
|
|
2065
|
+
elif path in previous:
|
|
2066
|
+
current[path] = previous[path]
|
|
1963
2067
|
self._last_rendering[key] = current
|
|
1964
2068
|
|
|
1965
|
-
def
|
|
1966
|
-
"""Keep a ``format`` span's full rendered
|
|
2069
|
+
def _remember_rendered_prompt(self, span: models.SpanBuilder, body: dict[str, Any]) -> None:
|
|
2070
|
+
"""Keep a ``format`` span's full rendered prompt for the LM calls that follow.
|
|
1967
2071
|
|
|
1968
|
-
The adapter hands exactly
|
|
1969
|
-
|
|
1970
|
-
compares
|
|
1971
|
-
equal.
|
|
1972
|
-
|
|
1973
|
-
|
|
2072
|
+
The adapter hands exactly this prompt to the LM, which starts after this
|
|
2073
|
+
span ended, as its sibling under the same parent.
|
|
2074
|
+
``_reference_rendered_prompt`` compares the LM's prompt with it and writes
|
|
2075
|
+
references where they are equal. A prompt key is kept only when its stored
|
|
2076
|
+
value resolves in full: a list truncated into an envelope cannot be walked
|
|
2077
|
+
into, and the LM span then keeps its own copy of that key.
|
|
1974
2078
|
"""
|
|
1975
|
-
|
|
1976
|
-
if span.parent_span_id is None or not isinstance(rendered, list) or not isinstance(stored, list):
|
|
2079
|
+
if span.parent_span_id is None:
|
|
1977
2080
|
return
|
|
1978
|
-
|
|
2081
|
+
kept = {}
|
|
2082
|
+
for key in references.prompt_keys(body):
|
|
2083
|
+
stored = references.value_at(span.outputs, (key,))
|
|
2084
|
+
if stored is not None and references.resolves_in_full(stored):
|
|
2085
|
+
kept[key] = body[key]
|
|
2086
|
+
if kept:
|
|
2087
|
+
self._rendered_prompts[span.parent_span_id] = (span, kept)
|
|
2088
|
+
else:
|
|
2089
|
+
self._rendered_prompts.pop(span.parent_span_id, None)
|
|
1979
2090
|
|
|
1980
2091
|
|
|
1981
2092
|
class _ToolMixin(_CallbackBase):
|
|
@@ -2169,6 +2280,11 @@ class _InterpreterMixin(_CallbackBase):
|
|
|
2169
2280
|
"parent_span": context.get_current_span(),
|
|
2170
2281
|
"start_time": datetime.now(timezone.utc),
|
|
2171
2282
|
}
|
|
2283
|
+
trace = context.get_current_trace()
|
|
2284
|
+
parent = frame["parent_span"]
|
|
2285
|
+
frame["trace_id"] = trace.trace_id if trace else parent.trace_id
|
|
2286
|
+
frame["span_context"] = trace.span_context() if trace else parent.span_context
|
|
2287
|
+
frame["export_context"] = trace.export_context if trace else parent.export_context
|
|
2172
2288
|
self._interpreter_tool_frames[call_id] = frame
|
|
2173
2289
|
# Publish this frame as the one a nested on_tool_start marks, in this
|
|
2174
2290
|
# context only. The token restores the prior frame (nested tool calls) on
|
|
@@ -2209,13 +2325,15 @@ class _InterpreterMixin(_CallbackBase):
|
|
|
2209
2325
|
parent = frame["parent_span"]
|
|
2210
2326
|
span = models.SpanBuilder(
|
|
2211
2327
|
span_id=uuid4(),
|
|
2212
|
-
trace_id=
|
|
2328
|
+
trace_id=frame["trace_id"],
|
|
2213
2329
|
name=f"Tool.{frame['tool_name']}",
|
|
2214
2330
|
span_type=models.SpanType.TOOL,
|
|
2215
2331
|
parent_span_id=parent.span_id if parent else None,
|
|
2216
2332
|
start_time=frame["start_time"],
|
|
2217
2333
|
max_input_size=self._max_input_field_size,
|
|
2218
2334
|
max_output_size=self._max_output_field_size,
|
|
2335
|
+
span_context=frame["span_context"],
|
|
2336
|
+
export_context=frame["export_context"],
|
|
2219
2337
|
)
|
|
2220
2338
|
span.attributes["tool.name"] = str(frame["tool_name"])
|
|
2221
2339
|
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
|