cmpnd 0.74.0__tar.gz → 0.75.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.74.0 → cmpnd-0.75.0}/PKG-INFO +1 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_gepa_patch.py +1 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/callback.py +2 -14
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/eval_handler.py +5 -19
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/identity.py +0 -17
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/models.py +1 -15
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/gepa.py +1 -8
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/resume.py +12 -11
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/tracker.py +3 -14
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/program_naming.py +4 -7
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/sdk-instrumentation.md +14 -11
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/trace_facts.py +0 -6
- {cmpnd-0.74.0 → cmpnd-0.75.0}/pyproject.toml +1 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/seed_review_run.py +2 -4
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/conftest.py +14 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_composite_module.py +4 -4
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_module_tracing.py +2 -3
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimization_lineage.py +0 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_review_committee.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_save_load.py +28 -33
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_trace_structure.py +21 -20
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/rlm_shape.py +0 -8
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_trace_streaming.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_multi_client_stress.py +0 -4
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_callback.py +20 -9
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client_program_names.py +12 -7
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval.py +4 -6
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_program_id.py +2 -9
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter.py +1 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_program_names.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_queue.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exporter_real.py +2 -3
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_callback.py +16 -12
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_resume.py +15 -12
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_identity.py +0 -21
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ingest_conformance.py +8 -18
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_models.py +17 -7
- cmpnd-0.75.0/tests/test_no_signature_hash_on_the_wire.py +188 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_identity.py +3 -4
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_naming.py +8 -8
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_repro_reactv2_bugs.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reversible_failures.py +1 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_schemas.py +0 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_fact_wire_contract.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_facts.py +0 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_tracker_enrich.py +3 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_xxh64.py +0 -2
- {cmpnd-0.74.0 → cmpnd-0.75.0}/uv.lock +1 -1
- {cmpnd-0.74.0 → cmpnd-0.75.0}/.gitignore +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/AGENTS.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/CLAUDE.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/README.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/client.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/context.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/references.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/cli.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/client.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/README.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/conftest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/drain.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_context.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_execute.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_integration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_retry.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.74.0 → cmpnd-0.75.0}/tests/test_valsets_api.py +0 -0
|
@@ -133,9 +133,8 @@ def _run_tracked_compile(
|
|
|
133
133
|
max_metric_calls=max_metric_calls,
|
|
134
134
|
)
|
|
135
135
|
|
|
136
|
-
# `enrich_from_program` hashed the student, so its generated name reuses that hash.
|
|
137
136
|
if client_config is None and trk.program_id is None and dspy_callback._declared_program_name() is None:
|
|
138
|
-
trk.generated_program_name = program_naming.generated_program_name(student
|
|
137
|
+
trk.generated_program_name = program_naming.generated_program_name(student)
|
|
139
138
|
|
|
140
139
|
restore_attribution = callback.start_trace_attribution()
|
|
141
140
|
|
|
@@ -1188,16 +1188,11 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1188
1188
|
identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
|
|
1189
1189
|
]
|
|
1190
1190
|
|
|
1191
|
-
span.program_signature_hash = identity.compute_program_hash(instance) or 0
|
|
1192
|
-
|
|
1193
1191
|
# For ChainOfThought, also capture extended signature
|
|
1194
1192
|
if hasattr(instance, "extended_signature"):
|
|
1195
1193
|
ext_sig = instance.extended_signature
|
|
1196
1194
|
span.attributes["extended_signature"] = getattr(ext_sig, "signature", str(ext_sig))
|
|
1197
1195
|
|
|
1198
|
-
else:
|
|
1199
|
-
span.program_signature_hash = identity.compute_program_hash(instance) or 0
|
|
1200
|
-
|
|
1201
1196
|
# Capture demo count and contents if available
|
|
1202
1197
|
demos = getattr(instance, "demos", None)
|
|
1203
1198
|
if demos:
|
|
@@ -1257,9 +1252,6 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1257
1252
|
elif hasattr(instance, "predict") and hasattr(instance.predict, "signature"):
|
|
1258
1253
|
signature = instance.predict.signature
|
|
1259
1254
|
|
|
1260
|
-
# The root module's signature hash, once a branch below computes it. An unnamed
|
|
1261
|
-
# program's generated name reuses it rather than reflecting the module again.
|
|
1262
|
-
program_hash: int | None = None
|
|
1263
1255
|
if signature:
|
|
1264
1256
|
# Single-signature module (Predict, ChainOfThought, etc.)
|
|
1265
1257
|
sig_name = getattr(signature, "__name__", None)
|
|
@@ -1287,12 +1279,10 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1287
1279
|
identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
|
|
1288
1280
|
]
|
|
1289
1281
|
|
|
1290
|
-
program_hash = identity.compute_program_hash(instance)
|
|
1291
|
-
trace.program_signature_hash = program_hash or 0
|
|
1292
1282
|
trace.signature_summary = identity.build_signature_summary(signature)
|
|
1293
1283
|
else:
|
|
1294
1284
|
# Multi-child module: collect Predict children via DSPy's own walker
|
|
1295
|
-
# for metadata
|
|
1285
|
+
# for metadata.
|
|
1296
1286
|
dspy_mod, _ = _get_dspy()
|
|
1297
1287
|
children: list[tuple[str, Any]] = []
|
|
1298
1288
|
if dspy_mod and hasattr(instance, "named_predictors"):
|
|
@@ -1303,8 +1293,6 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1303
1293
|
|
|
1304
1294
|
if children:
|
|
1305
1295
|
trace.signature_name = instance.__class__.__name__
|
|
1306
|
-
program_hash = identity.compute_program_hash(instance)
|
|
1307
|
-
trace.program_signature_hash = program_hash or 0
|
|
1308
1296
|
|
|
1309
1297
|
# Build component_signatures for grouped field display
|
|
1310
1298
|
components = []
|
|
@@ -1340,7 +1328,7 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1340
1328
|
if trace.program_id is None and trace.declared_name is None:
|
|
1341
1329
|
trace.program_id = context.optimization_program_id(trace.optimization_run_id)
|
|
1342
1330
|
if trace.program_id is None and trace.declared_name is None:
|
|
1343
|
-
trace.generated_name = program_naming.generated_program_name(instance
|
|
1331
|
+
trace.generated_name = program_naming.generated_program_name(instance)
|
|
1344
1332
|
_warn_once_about_generated_name(trace)
|
|
1345
1333
|
|
|
1346
1334
|
# Detect if this is a compiled program (from optimization)
|
|
@@ -87,16 +87,10 @@ class EvalHandler:
|
|
|
87
87
|
if program:
|
|
88
88
|
eval_run.module_class = program.__class__.__name__
|
|
89
89
|
|
|
90
|
-
#
|
|
91
|
-
# discriminates correctly; signature_summary is still derived from
|
|
92
|
-
# the root signature for human display.
|
|
93
|
-
program_hash = identity.compute_program_hash(program)
|
|
94
|
-
if program_hash is not None:
|
|
95
|
-
eval_run.program_signature_hash = program_hash
|
|
90
|
+
# The summary comes from the root signature, for human display.
|
|
96
91
|
signature = identity.get_program_signature(program)
|
|
97
92
|
if signature:
|
|
98
93
|
eval_run.signature_summary = identity.build_signature_summary(signature)
|
|
99
|
-
logger.debug(f"Eval run program_signature_hash: {eval_run.program_signature_hash}")
|
|
100
94
|
|
|
101
95
|
# Extract input field names for filtering example inputs in on_evaluate_end
|
|
102
96
|
if hasattr(signature, "input_fields"):
|
|
@@ -177,9 +171,7 @@ class EvalHandler:
|
|
|
177
171
|
eval_run._dataset_exists = None # Will include examples as fallback
|
|
178
172
|
|
|
179
173
|
exp = self._callback._current_exporter()
|
|
180
|
-
eval_run.program_id = self._program_id(
|
|
181
|
-
exp, program, eval_run.program_signature_hash, eval_run.optimization_run_id
|
|
182
|
-
)
|
|
174
|
+
eval_run.program_id = self._program_id(exp, program, eval_run.optimization_run_id)
|
|
183
175
|
|
|
184
176
|
logger.info(
|
|
185
177
|
f"EvalRun created: program_id={eval_run.program_id}, "
|
|
@@ -216,9 +208,7 @@ class EvalHandler:
|
|
|
216
208
|
self._callback._call_id_to_span[call_id] = span
|
|
217
209
|
context.push_span(span)
|
|
218
210
|
|
|
219
|
-
def _program_id(
|
|
220
|
-
self, exp: Any, program: Any, program_hash: int | None, optimization_run_id: str | None
|
|
221
|
-
) -> str | None:
|
|
211
|
+
def _program_id(self, exp: Any, program: Any, optimization_run_id: str | None) -> str | None:
|
|
222
212
|
"""The program id the eval run carries, resolved the way a trace's is.
|
|
223
213
|
|
|
224
214
|
A program name the call declares wins over the callback's program id. A run
|
|
@@ -227,9 +217,7 @@ class EvalHandler:
|
|
|
227
217
|
The exporter resolves either name now, on the caller's thread, because the run's
|
|
228
218
|
start is sent synchronously and must carry the id. The exporter keeps the answer
|
|
229
219
|
for the process, so a name its traces already resolved costs no request. A name
|
|
230
|
-
the exporter cannot resolve leaves the run without an id.
|
|
231
|
-
evaluated program's signature hash, already computed for the run, so naming the
|
|
232
|
-
program does not reflect it again.
|
|
220
|
+
the exporter cannot resolve leaves the run without an id.
|
|
233
221
|
"""
|
|
234
222
|
from . import callback
|
|
235
223
|
|
|
@@ -238,9 +226,7 @@ class EvalHandler:
|
|
|
238
226
|
program_id = self._callback._resolve_program_id() or context.optimization_program_id(optimization_run_id)
|
|
239
227
|
if program_id is not None or program is None:
|
|
240
228
|
return program_id
|
|
241
|
-
return program_naming.resolve_generated_name(
|
|
242
|
-
exp, program_naming.generated_program_name(program, program_hash)
|
|
243
|
-
)
|
|
229
|
+
return program_naming.resolve_generated_name(exp, program_naming.generated_program_name(program))
|
|
244
230
|
# Read with getattr because the member is optional: an in-memory collector
|
|
245
231
|
# that resolves no names has none.
|
|
246
232
|
resolve = getattr(exp, "resolve_program_name", None)
|
|
@@ -97,23 +97,6 @@ def _unsigned_to_signed_int64(value: int) -> int: # pyright: ignore[reportUnuse
|
|
|
97
97
|
return masked - 0x10000000000000000 if masked >= 0x8000000000000000 else masked
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
def format_signature_hash(value: int | None) -> str | None:
|
|
101
|
-
"""Render a ``program_signature_hash`` for the JSON wire as a quoted decimal
|
|
102
|
-
string (e.g. ``"-935109221252960526"``), or ``None`` when absent.
|
|
103
|
-
|
|
104
|
-
The hash is a signed int64 (xxhash64). Emitting it as a JSON *number* is
|
|
105
|
-
unsafe: any consumer that decodes through an untyped path floats it and
|
|
106
|
-
loses precision above 2^53 (Go ``any``/``map[string]any``, JavaScript
|
|
107
|
-
natively). Carrying it as a string makes the safe representation automatic
|
|
108
|
-
end to end. Storage stays signed int64; only the wire value is a
|
|
109
|
-
string. ``None`` stays ``None`` (nullable optimization hash); the trace/span
|
|
110
|
-
``0`` sentinel renders as ``"0"``.
|
|
111
|
-
"""
|
|
112
|
-
if value is None:
|
|
113
|
-
return None
|
|
114
|
-
return str(value)
|
|
115
|
-
|
|
116
|
-
|
|
117
100
|
# A program id is server-issued: ``p_`` and twelve base62 characters, the shape
|
|
118
101
|
# every platform id shares. The SDK never mints one; it carries the id serve gives
|
|
119
102
|
# a program name, or the id a deployment's worker passes to the kernel.
|
|
@@ -13,7 +13,7 @@ from uuid import UUID, uuid4
|
|
|
13
13
|
|
|
14
14
|
from cmpnd_ingest.trace_facts import CompletionFact, MetadataFact, StartedFact, StringMapChange
|
|
15
15
|
|
|
16
|
-
from . import
|
|
16
|
+
from . import imaging
|
|
17
17
|
|
|
18
18
|
# U+0000 (NUL) and lone UTF-16 surrogates are valid JSON, and valid in a Python
|
|
19
19
|
# str, but the server cannot store either: a JSON column rejects both, and a NUL
|
|
@@ -758,8 +758,6 @@ class SpanBuilder:
|
|
|
758
758
|
# Each element is None for primitives, or a schema dict for complex types
|
|
759
759
|
signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
|
|
760
760
|
signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
|
|
761
|
-
# Hash of input/output fields+types for program identity (xxhash64, signed)
|
|
762
|
-
program_signature_hash: int = 0
|
|
763
761
|
|
|
764
762
|
# DSPy-specific: Module info
|
|
765
763
|
module_type: str | None = None
|
|
@@ -905,7 +903,6 @@ class SpanBuilder:
|
|
|
905
903
|
"signature_output_field_descs": self.signature_output_field_descs,
|
|
906
904
|
"signature_input_field_schemas": self.signature_input_field_schemas,
|
|
907
905
|
"signature_output_field_schemas": self.signature_output_field_schemas,
|
|
908
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
909
906
|
"module_type": self.module_type,
|
|
910
907
|
"module_name": self.module_name,
|
|
911
908
|
"model_name": self.model_name,
|
|
@@ -945,7 +942,6 @@ class TraceBuilder:
|
|
|
945
942
|
# The root DSPy module's class. The program's own name is `declared_name`,
|
|
946
943
|
# which the transport resolves to `program_id`.
|
|
947
944
|
module_class: str | None = None
|
|
948
|
-
program_version: str | None = None
|
|
949
945
|
|
|
950
946
|
# Signature info - identifies the program by its inputs/outputs
|
|
951
947
|
signature_name: str | None = None
|
|
@@ -956,7 +952,6 @@ class TraceBuilder:
|
|
|
956
952
|
# Schemas for complex types (Pydantic models, dataclasses)
|
|
957
953
|
signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
|
|
958
954
|
signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
|
|
959
|
-
program_signature_hash: int = 0 # Hash of input/output fields+types (legacy program key)
|
|
960
955
|
signature_summary: str | None = None # Human-readable: "input1, input2 -> output1, output2"
|
|
961
956
|
component_signatures: list[dict[str, Any]] = field(default_factory=list)
|
|
962
957
|
# The server-issued program id the trace declares (`p_…`), snapshotted from
|
|
@@ -1080,7 +1075,6 @@ class TraceBuilder:
|
|
|
1080
1075
|
"module_class": self.module_class,
|
|
1081
1076
|
# Also under the key a serve from before module_class reads (StartedFact.creation_dict).
|
|
1082
1077
|
"program_name": self.module_class,
|
|
1083
|
-
"program_version": self.program_version,
|
|
1084
1078
|
"signature_name": self.signature_name,
|
|
1085
1079
|
"signature_input_fields": self.signature_input_fields,
|
|
1086
1080
|
"signature_output_fields": self.signature_output_fields,
|
|
@@ -1088,7 +1082,6 @@ class TraceBuilder:
|
|
|
1088
1082
|
"signature_output_field_types": self.signature_output_field_types,
|
|
1089
1083
|
"signature_input_field_schemas": self.signature_input_field_schemas,
|
|
1090
1084
|
"signature_output_field_schemas": self.signature_output_field_schemas,
|
|
1091
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
1092
1085
|
"signature_summary": self.signature_summary,
|
|
1093
1086
|
# The server forwards this blob verbatim into a jsonb column, and its
|
|
1094
1087
|
# program registration is best-effort (a rejection is only logged), so
|
|
@@ -1123,13 +1116,11 @@ class TraceBuilder:
|
|
|
1123
1116
|
"module_class": self.module_class,
|
|
1124
1117
|
# Also under the key a serve from before module_class reads (StartedFact.creation_dict).
|
|
1125
1118
|
"program_name": self.module_class,
|
|
1126
|
-
"program_version": self.program_version,
|
|
1127
1119
|
"signature_name": self.signature_name,
|
|
1128
1120
|
"signature_input_fields": self.signature_input_fields,
|
|
1129
1121
|
"signature_output_fields": self.signature_output_fields,
|
|
1130
1122
|
"signature_input_field_types": self.signature_input_field_types,
|
|
1131
1123
|
"signature_output_field_types": self.signature_output_field_types,
|
|
1132
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
1133
1124
|
"signature_summary": self.signature_summary,
|
|
1134
1125
|
"component_signatures": _safe_serialize(self.component_signatures),
|
|
1135
1126
|
"root_span_type": self.root_span_type,
|
|
@@ -1287,9 +1278,6 @@ class EvalRunBuilder:
|
|
|
1287
1278
|
metric_name: str | None = None
|
|
1288
1279
|
dataset_hash: str | None = None
|
|
1289
1280
|
dataset_size: int = 0
|
|
1290
|
-
# The evaluated module's signature hash (legacy program key). None when the run has
|
|
1291
|
-
# no module, and then sent as null.
|
|
1292
|
-
program_signature_hash: int | None = None
|
|
1293
1281
|
signature_summary: str | None = None # Human-readable: "input1, input2 -> output1"
|
|
1294
1282
|
|
|
1295
1283
|
# Model Configuration
|
|
@@ -1411,7 +1399,6 @@ class EvalRunBuilder:
|
|
|
1411
1399
|
"metric_name": self.metric_name or "",
|
|
1412
1400
|
"dataset_hash": self.dataset_hash or "",
|
|
1413
1401
|
"dataset_size": self.dataset_size,
|
|
1414
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
1415
1402
|
"signature_summary": self.signature_summary,
|
|
1416
1403
|
"model_name": self.model_name,
|
|
1417
1404
|
"model_provider": self.model_provider,
|
|
@@ -1432,7 +1419,6 @@ class EvalRunBuilder:
|
|
|
1432
1419
|
"metric_name": self.metric_name or "",
|
|
1433
1420
|
"dataset_hash": self.dataset_hash or "",
|
|
1434
1421
|
"dataset_size": self.dataset_size,
|
|
1435
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
1436
1422
|
"signature_summary": self.signature_summary,
|
|
1437
1423
|
"model_name": self.model_name,
|
|
1438
1424
|
"model_provider": self.model_provider,
|
|
@@ -146,10 +146,6 @@ class TrackedGEPA:
|
|
|
146
146
|
|
|
147
147
|
component_signature_hash = identity.compute_signature_hash(config.get("components", []))
|
|
148
148
|
|
|
149
|
-
# Compute program_signature_hash via the IR reflector — same encoder
|
|
150
|
-
# the trace path uses, so optimization_runs and traces join on identity.
|
|
151
|
-
program_signature_hash = identity.compute_program_hash(student) or 0
|
|
152
|
-
|
|
153
149
|
# Initialize optimization tracking
|
|
154
150
|
self._tracker = tracker.OptimizationTracker(
|
|
155
151
|
optimizer_type="gepa",
|
|
@@ -159,7 +155,6 @@ class TrackedGEPA:
|
|
|
159
155
|
reflection_model_name=reflection_model_name,
|
|
160
156
|
valset_hash=valset_hash,
|
|
161
157
|
component_signature_hash=component_signature_hash,
|
|
162
|
-
program_signature_hash=program_signature_hash,
|
|
163
158
|
)
|
|
164
159
|
|
|
165
160
|
# This path builds its tracker without `enrich_from_program`, so it types the
|
|
@@ -170,9 +165,7 @@ class TrackedGEPA:
|
|
|
170
165
|
# SDK generates for the student, the name its rollouts' traces carry, and
|
|
171
166
|
# resolves it when the run starts.
|
|
172
167
|
if self._tracker.program_id is None and callback._declared_program_name() is None:
|
|
173
|
-
self._tracker.generated_program_name = program_naming.generated_program_name(
|
|
174
|
-
student, program_signature_hash
|
|
175
|
-
)
|
|
168
|
+
self._tracker.generated_program_name = program_naming.generated_program_name(student)
|
|
176
169
|
|
|
177
170
|
# Store both sets for dataset payload building
|
|
178
171
|
self._tracker._trainset = list(trainset)
|
|
@@ -30,7 +30,7 @@ def sidecar_path(log_dir: str) -> str:
|
|
|
30
30
|
return os.path.join(log_dir, _SIDECAR)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def write_run_id(log_dir: str, run_id: str,
|
|
33
|
+
def write_run_id(log_dir: str, run_id: str, program_id: str | None, start_time: datetime | None) -> None:
|
|
34
34
|
"""Persist the server-assigned ``run_id`` into ``log_dir`` so a later resume
|
|
35
35
|
can re-adopt it. Called at ``start()``, which runs before GEPA creates its
|
|
36
36
|
``run_dir`` — so create the directory if it isn't there yet. Best-effort: a
|
|
@@ -38,7 +38,7 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
|
|
|
38
38
|
"""
|
|
39
39
|
payload = {
|
|
40
40
|
"run_id": run_id,
|
|
41
|
-
"
|
|
41
|
+
"program_id": program_id,
|
|
42
42
|
"start_time": start_time.isoformat() if start_time is not None else None,
|
|
43
43
|
}
|
|
44
44
|
try:
|
|
@@ -49,15 +49,16 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
|
|
|
49
49
|
logger.warning("could not write resume sidecar in %s: %s", log_dir, e)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def read_resumable(log_dir: str,
|
|
52
|
+
def read_resumable(log_dir: str, program_id: str | None) -> tuple[str, datetime | None] | None:
|
|
53
53
|
"""Return ``(run_id, start_time)`` to re-adopt, or ``None`` if this is not a
|
|
54
54
|
resumable job.
|
|
55
55
|
|
|
56
56
|
Resumable requires: GEPA's ``gepa_state.bin`` checkpoint present (else GEPA
|
|
57
57
|
itself starts fresh) AND a readable sidecar carrying a ``run_id``. A definite
|
|
58
|
-
``
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
``program_id`` mismatch (a different program reusing the same ``log_dir``) is
|
|
59
|
+
rejected so it can't hijack the id. A missing program id on either side errs
|
|
60
|
+
toward stitching, since ``log_dir`` is the strong identity signal. A sidecar an
|
|
61
|
+
older SDK wrote names no program id, so it stitches.
|
|
61
62
|
"""
|
|
62
63
|
if not os.path.exists(os.path.join(log_dir, _CHECKPOINT)):
|
|
63
64
|
return None
|
|
@@ -71,13 +72,13 @@ def read_resumable(log_dir: str, program_signature_hash: int | None) -> tuple[st
|
|
|
71
72
|
if not run_id:
|
|
72
73
|
return None
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
if
|
|
75
|
+
prior_program = data.get("program_id")
|
|
76
|
+
if prior_program is not None and program_id is not None and prior_program != program_id:
|
|
76
77
|
logger.warning(
|
|
77
|
-
"resume sidecar in %s is for a different program (
|
|
78
|
+
"resume sidecar in %s is for a different program (%s != %s); not re-adopting",
|
|
78
79
|
log_dir,
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
prior_program,
|
|
81
|
+
program_id,
|
|
81
82
|
)
|
|
82
83
|
return None
|
|
83
84
|
|
|
@@ -274,7 +274,6 @@ class OptimizationTracker:
|
|
|
274
274
|
reflection_model_name: str = "",
|
|
275
275
|
valset_hash: str = "",
|
|
276
276
|
component_signature_hash: str = "",
|
|
277
|
-
program_signature_hash: int = 0,
|
|
278
277
|
):
|
|
279
278
|
# The SDK no longer mints this id. It stays None until start()
|
|
280
279
|
# runs the synchronous start POST and adopts the id assigned by the
|
|
@@ -303,8 +302,6 @@ class OptimizationTracker:
|
|
|
303
302
|
self.trainset_hash = "" # individual trainset hash
|
|
304
303
|
self.valset_individual_hash = "" # individual valset hash
|
|
305
304
|
self.component_signature_hash = component_signature_hash # SHA256[:16] for model sweep grouping
|
|
306
|
-
# Program signature hash - matches trace format (64-bit int from input/output fields+types)
|
|
307
|
-
self.program_signature_hash = program_signature_hash
|
|
308
305
|
self.start_time = datetime.now(timezone.utc)
|
|
309
306
|
|
|
310
307
|
# Collected data
|
|
@@ -751,7 +748,7 @@ class OptimizationTracker:
|
|
|
751
748
|
log_dir = self.config.get("log_dir") if self.config else None
|
|
752
749
|
if not log_dir or not self.optimization_run_id:
|
|
753
750
|
return False
|
|
754
|
-
readopted = resume.read_resumable(log_dir, self.
|
|
751
|
+
readopted = resume.read_resumable(log_dir, self.program_id)
|
|
755
752
|
return readopted is not None and readopted[0] == self.optimization_run_id
|
|
756
753
|
|
|
757
754
|
def finalize_error(self, exception: BaseException) -> None:
|
|
@@ -853,7 +850,7 @@ class OptimizationTracker:
|
|
|
853
850
|
# that id (and its original start_time, so duration stays cumulative)
|
|
854
851
|
# instead of minting a fresh row. Jobs without a log_dir stay per-run.
|
|
855
852
|
log_dir = self.config.get("log_dir") if self.config else None
|
|
856
|
-
readopted = resume.read_resumable(log_dir, self.
|
|
853
|
+
readopted = resume.read_resumable(log_dir, self.program_id) if log_dir else None
|
|
857
854
|
if readopted is not None:
|
|
858
855
|
self.optimization_run_id, prior_start = readopted
|
|
859
856
|
if prior_start is not None:
|
|
@@ -870,7 +867,7 @@ class OptimizationTracker:
|
|
|
870
867
|
self._publish_program()
|
|
871
868
|
# Persist the id so a later resume of the same log_dir re-adopts it.
|
|
872
869
|
if log_dir and self.optimization_run_id:
|
|
873
|
-
resume.write_run_id(log_dir, self.optimization_run_id, self.
|
|
870
|
+
resume.write_run_id(log_dir, self.optimization_run_id, self.program_id, self.start_time)
|
|
874
871
|
logger.info(
|
|
875
872
|
f"Started optimization tracking: run_id={self.optimization_run_id}, "
|
|
876
873
|
f"max_metric_calls={self.max_metric_calls}"
|
|
@@ -1169,7 +1166,6 @@ class OptimizationTracker:
|
|
|
1169
1166
|
"reflection_model_name": self.reflection_model_name,
|
|
1170
1167
|
"valset_hash": self.valset_hash,
|
|
1171
1168
|
"component_signature_hash": self.component_signature_hash,
|
|
1172
|
-
"program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
|
|
1173
1169
|
"start_time": self.start_time.isoformat() if self.start_time else None,
|
|
1174
1170
|
"end_time": self.end_time.isoformat() if self.end_time else None,
|
|
1175
1171
|
"duration_ms": duration_ms,
|
|
@@ -1435,13 +1431,6 @@ def enrich_from_program(
|
|
|
1435
1431
|
trk.component_signature_hash = identity.compute_signature_hash(components)
|
|
1436
1432
|
trk.component_types = code_component_types(student)
|
|
1437
1433
|
|
|
1438
|
-
# Program signature hash from the live module (routes through the IR
|
|
1439
|
-
# reflector so Predict vs ChainOfThought discriminates — don't pass a bare
|
|
1440
|
-
# signature).
|
|
1441
|
-
program_hash = identity.compute_program_hash(student)
|
|
1442
|
-
if program_hash is not None:
|
|
1443
|
-
trk.program_signature_hash = program_hash
|
|
1444
|
-
|
|
1445
1434
|
# Model names.
|
|
1446
1435
|
if lm is not None and getattr(lm, "model", None):
|
|
1447
1436
|
trk.model_name = lm.model or ""
|
|
@@ -34,7 +34,7 @@ _NAME_PUNCTUATION = "._-"
|
|
|
34
34
|
_RESERVED_PREFIX = "p_"
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def generated_program_name(module: Any
|
|
37
|
+
def generated_program_name(module: Any) -> str:
|
|
38
38
|
"""The name the SDK gives the program ``module`` runs as, when its user named none.
|
|
39
39
|
|
|
40
40
|
The name is the module's class and twelve hex digits of its signature hash, such as
|
|
@@ -43,13 +43,10 @@ def generated_program_name(module: Any, signature_hash: int | None = None) -> st
|
|
|
43
43
|
names. An optimizer's new instructions and demos keep the shape, so they keep the
|
|
44
44
|
name. The name always fits the program-name syntax, whatever the class is called.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
computes the hash.
|
|
46
|
+
The SDK sends the signature hash nowhere else, so naming the program is the only
|
|
47
|
+
place that reflects the module for it.
|
|
49
48
|
"""
|
|
50
|
-
|
|
51
|
-
signature_hash = identity.compute_program_hash(module)
|
|
52
|
-
signature_hash = signature_hash or 0
|
|
49
|
+
signature_hash = identity.compute_program_hash(module) or 0
|
|
53
50
|
suffix = f"{signature_hash & 0xFFFF_FFFF_FFFF_FFFF:016x}"[:_SUFFIX_DIGITS]
|
|
54
51
|
return f"{_class_part(module.__class__.__name__)}-{suffix}"
|
|
55
52
|
|
|
@@ -139,14 +139,16 @@ and every other name still raises. `TestDspyHookCoverage` in
|
|
|
139
139
|
`tests/test_callback.py` asserts the coverage against whatever DSPy is installed,
|
|
140
140
|
so the next release that adds a hook is caught by CI rather than by a user's logs.
|
|
141
141
|
|
|
142
|
-
### Signature extraction and
|
|
142
|
+
### Signature extraction and the program signature hash
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
No record the SDK sends carries the program signature hash. A trace, an eval run and an
|
|
145
|
+
optimization run name their program by `program_id`.
|
|
146
|
+
[`CLUSTERING.md`](../../docs/CLUSTERING.md) explains how the hash is computed.
|
|
145
147
|
|
|
146
|
-
- **On a span** — `_capture_module_attributes` in `callback.py
|
|
147
|
-
- **On a trace** — `_create_trace` in `callback.py
|
|
148
|
+
- **On a span** — `_capture_module_attributes` in `callback.py` walks `instance.signature.input_fields` / `output_fields` and records names, types, descriptions and schemas. It reflects nothing, because a span never names a program.
|
|
149
|
+
- **On a trace** — `_create_trace` in `callback.py` records the root signature, or for a composite module the `dspy.Predict` children's signatures as `component_signatures`. A root module call that names no program reflects the module once, for the name the SDK generates (`program_naming.generated_program_name`). The hash goes into that name and nowhere else.
|
|
148
150
|
|
|
149
|
-
|
|
151
|
+
`python-sdk/cmpnd/identity.py` is the home of every hash the SDK computes.
|
|
150
152
|
|
|
151
153
|
---
|
|
152
154
|
|
|
@@ -690,7 +692,10 @@ the top level and in `started_fact`, and again under `program_name`, the key a
|
|
|
690
692
|
serve from before `module_class` reads and digests a start from. A standalone
|
|
691
693
|
optimizer LM trace sends `GEPA` there. No trace body carries the program's own name: a declared name
|
|
692
694
|
travels as the program id it resolves to, as "A declared program name resolves
|
|
693
|
-
to an id in the exporter" describes.
|
|
695
|
+
to an id in the exporter" describes. No trace body carries a version either. A
|
|
696
|
+
trace serve executed names its version by the `deployment_id` serve stamps on
|
|
697
|
+
it, and `TraceBuilder` has no `program_version` field. A program that wants to
|
|
698
|
+
record a version of its own, such as a git SHA, adds it as a project tag.
|
|
694
699
|
|
|
695
700
|
The callback seals `started_fact` after it sets the root type, before the
|
|
696
701
|
module executes. It keeps that snapshot even when start emission is disabled.
|
|
@@ -735,7 +740,7 @@ What it captures beyond the normal span flow:
|
|
|
735
740
|
- An `EvalRunBuilder` (constructed at `eval_handler.py:79`) that groups all per-example traces under one eval ID.
|
|
736
741
|
- The metric name (via `get_metric_name` from `python-sdk/cmpnd/helpers.py`) and inferred provider.
|
|
737
742
|
- Links to the enclosing optimization run when one exists (`eval_handler.py:85`), so eval runs triggered inside `GEPA.compile` roll up under the optimization in the UI.
|
|
738
|
-
- The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads.
|
|
743
|
+
- The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads. The run sends no `program_signature_hash`.
|
|
739
744
|
- The exporter it publishes through: the callback's own (`_current_exporter`), so a client's eval runs go to the client's account.
|
|
740
745
|
- The dataset — on `on_evaluate_start` it triggers the `dataset_sync` pre-flight described above.
|
|
741
746
|
|
|
@@ -761,7 +766,6 @@ The trace payload is built in `python-sdk/cmpnd/models.py` by `TraceBuilder.to_a
|
|
|
761
766
|
"trace_id": "...",
|
|
762
767
|
"parent_span_id": "...",
|
|
763
768
|
"span_type": "predict",
|
|
764
|
-
"signature_hash": -3847510293847, # signed Int64
|
|
765
769
|
"signature_input_fields": ["question"],
|
|
766
770
|
"signature_output_fields": ["answer"],
|
|
767
771
|
"model_name": "gpt-4o-mini",
|
|
@@ -813,9 +817,8 @@ name form. A program name wins over the config's id. `configure()`
|
|
|
813
817
|
checks the id's shape and raises `ValueError` on a bad one, and a `Config` built directly
|
|
814
818
|
is checked the same way.
|
|
815
819
|
|
|
816
|
-
The id rides every body that can declare a program.
|
|
817
|
-
|
|
818
|
-
hash beside the id:
|
|
820
|
+
The id rides every body that can declare a program. No body carries a
|
|
821
|
+
`program_signature_hash`:
|
|
819
822
|
|
|
820
823
|
| Body | Where | Built by |
|
|
821
824
|
|---|---|---|
|
|
@@ -81,8 +81,6 @@ class StartedFact:
|
|
|
81
81
|
|
|
82
82
|
start_time: str | None
|
|
83
83
|
module_class: str | None
|
|
84
|
-
program_version: str | None
|
|
85
|
-
program_signature_hash: str
|
|
86
84
|
signature_name: str | None
|
|
87
85
|
signature_summary: str | None
|
|
88
86
|
signature_input_fields: tuple[str, ...]
|
|
@@ -105,8 +103,6 @@ class StartedFact:
|
|
|
105
103
|
return cls(
|
|
106
104
|
start_time=payload.get("start_time"),
|
|
107
105
|
module_class=payload.get("module_class"),
|
|
108
|
-
program_version=payload.get("program_version"),
|
|
109
|
-
program_signature_hash=str(payload["program_signature_hash"]),
|
|
110
106
|
signature_name=payload.get("signature_name"),
|
|
111
107
|
signature_summary=payload.get("signature_summary"),
|
|
112
108
|
signature_input_fields=tuple(payload.get("signature_input_fields") or ()),
|
|
@@ -131,8 +127,6 @@ class StartedFact:
|
|
|
131
127
|
# a start it accepted matching its replay to a later serve. Dropped
|
|
132
128
|
# once no such serve runs.
|
|
133
129
|
"program_name": self.module_class,
|
|
134
|
-
"program_version": self.program_version,
|
|
135
|
-
"program_signature_hash": self.program_signature_hash,
|
|
136
130
|
"signature_name": self.signature_name,
|
|
137
131
|
"signature_summary": self.signature_summary,
|
|
138
132
|
"signature_input_fields": list(self.signature_input_fields),
|
|
@@ -5,7 +5,7 @@ name = "cmpnd"
|
|
|
5
5
|
# it's new) — no git tags. Bump with `uv version --bump {minor,patch}` or by
|
|
6
6
|
# hand. Runtime `cmpnd.__version__` reads installed metadata, so it tracks
|
|
7
7
|
# this automatically. See docs/plans/2026-07-02-sdk-pypi-tag-free-publish.md.
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.75.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -28,7 +28,8 @@ shifted so the run lands at "now".
|
|
|
28
28
|
Every seeded program is named. The seeder resolves each name to its `p_` id
|
|
29
29
|
through `PUT /api/v1/programs/by-name/{name}` and declares that id on the
|
|
30
30
|
program's traces, runs and deployments, and pins by it. The replayed payloads
|
|
31
|
-
keep the `program_signature_hash`
|
|
31
|
+
keep the `program_signature_hash` an older SDK captured. Serve still accepts one,
|
|
32
|
+
and a program is never found by it.
|
|
32
33
|
|
|
33
34
|
Non-gating: any failure logs and exits 0 (the review pipeline must not break on it).
|
|
34
35
|
|
|
@@ -396,7 +397,6 @@ def _notebook_trace(now: datetime.datetime) -> dict:
|
|
|
396
397
|
"duration_ns": total_ns,
|
|
397
398
|
"status": "ok",
|
|
398
399
|
"module_class": "RLM", # DSPy module class → programs.module_class → sidebar badge
|
|
399
|
-
"program_version": "0.1.0",
|
|
400
400
|
"program_signature_hash": fx["program_signature_hash"],
|
|
401
401
|
"signature_name": fx["signature_name"],
|
|
402
402
|
"signature_input_fields": fx["input_fields"],
|
|
@@ -546,7 +546,6 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
|
|
|
546
546
|
"start_time": at(0),
|
|
547
547
|
"status": "in_progress",
|
|
548
548
|
"module_class": "RLM",
|
|
549
|
-
"program_version": "0.1.0",
|
|
550
549
|
"program_signature_hash": fx["program_signature_hash"],
|
|
551
550
|
"signature_name": fx["signature_name"],
|
|
552
551
|
"signature_input_fields": fx["input_fields"],
|
|
@@ -615,7 +614,6 @@ def _replay_flat_fixture(
|
|
|
615
614
|
"duration_ns": total_ns,
|
|
616
615
|
"status": "ok",
|
|
617
616
|
"module_class": module_class, # DSPy module class → programs.module_class → sidebar badge
|
|
618
|
-
"program_version": "0.1.0",
|
|
619
617
|
"program_signature_hash": root["program_signature_hash"],
|
|
620
618
|
"signature_name": root["signature_name"],
|
|
621
619
|
"signature_input_fields": root["signature_input_fields"],
|
|
@@ -119,6 +119,20 @@ def flush_and_wait(seconds: float = 3.0) -> None:
|
|
|
119
119
|
time.sleep(seconds)
|
|
120
120
|
|
|
121
121
|
|
|
122
|
+
def generated_program_id(module: Any) -> str:
|
|
123
|
+
"""The program id serve gives the name the SDK generates for ``module``.
|
|
124
|
+
|
|
125
|
+
An unnamed program's traces carry this id, so it is the program identity a test
|
|
126
|
+
compares. The name resolves to the same id on every call.
|
|
127
|
+
"""
|
|
128
|
+
from cmpnd import program_naming
|
|
129
|
+
|
|
130
|
+
exporter = ensure_exporter_started()
|
|
131
|
+
program_id = program_naming.resolve_generated_name(exporter, program_naming.generated_program_name(module))
|
|
132
|
+
assert program_id is not None, "serve did not resolve the generated program name"
|
|
133
|
+
return program_id
|
|
134
|
+
|
|
135
|
+
|
|
122
136
|
# ---------------------------------------------------------------------------
|
|
123
137
|
# ServerClient — thin wrapper around the serve API
|
|
124
138
|
# ---------------------------------------------------------------------------
|
|
@@ -7,14 +7,14 @@ import json
|
|
|
7
7
|
import dspy
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
-
from tests.e2e.conftest import flush_and_wait
|
|
10
|
+
from tests.e2e.conftest import flush_and_wait, generated_program_id
|
|
11
11
|
|
|
12
12
|
pytestmark = pytest.mark.e2e
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TestCompositeModuleIdentity:
|
|
16
|
-
def
|
|
17
|
-
"""A custom Module with Predict children
|
|
16
|
+
def test_custom_module_trace_names_its_program(self, server_client, trace_capture):
|
|
17
|
+
"""A custom Module with Predict children produces a trace that names its program."""
|
|
18
18
|
|
|
19
19
|
class ArticleWriter(dspy.Module):
|
|
20
20
|
def __init__(self):
|
|
@@ -42,7 +42,7 @@ class TestCompositeModuleIdentity:
|
|
|
42
42
|
data = server_client.get_trace(trace_id)
|
|
43
43
|
trace = data["trace"]
|
|
44
44
|
|
|
45
|
-
assert trace["
|
|
45
|
+
assert trace["program_id"] == generated_program_id(writer), "Custom module should name its program"
|
|
46
46
|
assert trace["module_class"] == "ArticleWriter"
|
|
47
47
|
|
|
48
48
|
def test_custom_module_has_component_signatures(self, server_client, trace_capture):
|
|
@@ -35,9 +35,8 @@ class TestPredictBasic:
|
|
|
35
35
|
assert span_types & {"predict", "module"}, f"Expected predict or module span, got {span_types}"
|
|
36
36
|
assert "lm_call" in span_types, f"Expected lm_call span, got {span_types}"
|
|
37
37
|
|
|
38
|
-
#
|
|
39
|
-
assert trace.get("
|
|
40
|
-
assert trace["program_signature_hash"] != 0
|
|
38
|
+
# The trace names its program by id
|
|
39
|
+
assert trace.get("program_id") is not None
|
|
41
40
|
|
|
42
41
|
# total_tokens > 0
|
|
43
42
|
assert trace.get("total_tokens", 0) > 0
|