cmpnd 0.72.0__tar.gz → 0.74.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.72.0 → cmpnd-0.74.0}/PKG-INFO +9 -6
- {cmpnd-0.72.0 → cmpnd-0.74.0}/README.md +8 -5
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_gepa_patch.py +12 -2
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/callback.py +36 -21
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/admin.py +18 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/verbs.py +2 -1
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/context.py +28 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/eval_handler.py +22 -9
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/models.py +9 -2
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimizers/gepa.py +9 -1
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimizers/tracker.py +31 -1
- cmpnd-0.74.0/cmpnd/program_naming.py +86 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/cli.md +17 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/client.md +6 -3
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/sdk-instrumentation.md +64 -18
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/exporter_http.py +74 -45
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/program_names.py +26 -7
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/protocols.py +5 -4
- {cmpnd-0.72.0 → cmpnd-0.74.0}/pyproject.toml +1 -1
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_admin.py +18 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_client_program_names.py +130 -26
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_eval_program_id.py +74 -10
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_exporter_program_names.py +116 -15
- cmpnd-0.74.0/tests/test_optimization_program_names.py +363 -0
- cmpnd-0.74.0/tests/test_program_naming.py +166 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/uv.lock +1 -1
- {cmpnd-0.72.0 → cmpnd-0.74.0}/.gitignore +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/AGENTS.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/CLAUDE.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/client.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/references.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/README.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/drain.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_callback.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_client.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_context.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_eval.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_execute.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_integration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_models.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_retry.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.72.0 → cmpnd-0.74.0}/tests/test_xxh64.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.74.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>
|
|
@@ -157,9 +157,12 @@ dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
|
157
157
|
dspy.ChainOfThought("question -> answer")(question="What is DSPy?")
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
Each program shape gets a program of its own, under a name the SDK generates
|
|
161
|
+
from its class and signature, such as `ChainOfThought-17e1f4089a24`. These
|
|
162
|
+
programs do not count toward your plan's program limit. The first such trace in
|
|
163
|
+
a process logs a warning that names the generated name and shows how to give the
|
|
164
|
+
program a real one: `@cmpnd.program(name)` or `cmpnd.Program(name, module=..., lm=...)`,
|
|
165
|
+
run through `cmpnd.Client`. Calling a `cmpnd.Program` directly while `auto_instrument()` is
|
|
163
166
|
on raises a `TypeError` that points to the client. The API reference below
|
|
164
167
|
lists the options of `cmpnd.configure()`.
|
|
165
168
|
|
|
@@ -640,8 +643,8 @@ captured input and output size limits, 65,536 bytes each by default.
|
|
|
640
643
|
|
|
641
644
|
### `cmpnd.auto_instrument()`
|
|
642
645
|
|
|
643
|
-
Registers the callback with DSPy, so every DSPy call records a trace.
|
|
644
|
-
|
|
646
|
+
Registers the callback with DSPy, so every DSPy call records a trace. Each
|
|
647
|
+
trace goes to a program under a name the SDK generates for its root module.
|
|
645
648
|
|
|
646
649
|
### `cmpnd.Client()`
|
|
647
650
|
|
|
@@ -106,9 +106,12 @@ dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
|
|
|
106
106
|
dspy.ChainOfThought("question -> answer")(question="What is DSPy?")
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
Each program shape gets a program of its own, under a name the SDK generates
|
|
110
|
+
from its class and signature, such as `ChainOfThought-17e1f4089a24`. These
|
|
111
|
+
programs do not count toward your plan's program limit. The first such trace in
|
|
112
|
+
a process logs a warning that names the generated name and shows how to give the
|
|
113
|
+
program a real one: `@cmpnd.program(name)` or `cmpnd.Program(name, module=..., lm=...)`,
|
|
114
|
+
run through `cmpnd.Client`. Calling a `cmpnd.Program` directly while `auto_instrument()` is
|
|
112
115
|
on raises a `TypeError` that points to the client. The API reference below
|
|
113
116
|
lists the options of `cmpnd.configure()`.
|
|
114
117
|
|
|
@@ -589,8 +592,8 @@ captured input and output size limits, 65,536 bytes each by default.
|
|
|
589
592
|
|
|
590
593
|
### `cmpnd.auto_instrument()`
|
|
591
594
|
|
|
592
|
-
Registers the callback with DSPy, so every DSPy call records a trace.
|
|
593
|
-
|
|
595
|
+
Registers the callback with DSPy, so every DSPy call records a trace. Each
|
|
596
|
+
trace goes to a program under a name the SDK generates for its root module.
|
|
594
597
|
|
|
595
598
|
### `cmpnd.Client()`
|
|
596
599
|
|
|
@@ -58,13 +58,19 @@ def _run_tracked_compile(
|
|
|
58
58
|
|
|
59
59
|
``client_config`` scopes the run record to a client: the record takes that
|
|
60
60
|
config's project tags and program id as they are, absent ones included, so a
|
|
61
|
-
client that names no program sends none rather than the global config's.
|
|
62
|
-
|
|
61
|
+
client that names no program sends none rather than the global config's. A client
|
|
62
|
+
always runs a named program, so its run never takes a generated name, even when the
|
|
63
|
+
name did not resolve. The global path passes no client config and keeps the
|
|
64
|
+
tracker's global snapshot. With no program id there, the run takes the name the SDK
|
|
65
|
+
generates for the student, the name its rollouts' traces carry, unless the call
|
|
66
|
+
declares a name.
|
|
63
67
|
``has_callbacks`` is passed by the patch (resolved once at patch time); a
|
|
64
68
|
client leaves it None and it is resolved here.
|
|
65
69
|
"""
|
|
66
70
|
import dspy
|
|
67
71
|
|
|
72
|
+
from . import callback as dspy_callback
|
|
73
|
+
from . import program_naming
|
|
68
74
|
from .optimizers import gepa_callback, tracker
|
|
69
75
|
|
|
70
76
|
if has_callbacks is None:
|
|
@@ -127,6 +133,10 @@ def _run_tracked_compile(
|
|
|
127
133
|
max_metric_calls=max_metric_calls,
|
|
128
134
|
)
|
|
129
135
|
|
|
136
|
+
# `enrich_from_program` hashed the student, so its generated name reuses that hash.
|
|
137
|
+
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, trk.program_signature_hash)
|
|
139
|
+
|
|
130
140
|
restore_attribution = callback.start_trace_attribution()
|
|
131
141
|
|
|
132
142
|
try:
|
|
@@ -9,7 +9,7 @@ from datetime import datetime, timezone
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
from uuid import UUID, uuid4
|
|
11
11
|
|
|
12
|
-
from . import configuration, context, eval_handler, exporter, helpers, identity, models, references
|
|
12
|
+
from . import configuration, context, eval_handler, exporter, helpers, identity, models, program_naming, references
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
@@ -39,24 +39,23 @@ def _declared_program_name() -> str | None:
|
|
|
39
39
|
return name if isinstance(name, str) else None
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
# Set once the process has warned that a
|
|
43
|
-
#
|
|
44
|
-
|
|
42
|
+
# Set once the process has warned that a program has no name, so the warning appears
|
|
43
|
+
# once per process, whichever callback records the trace.
|
|
44
|
+
_warned_unnamed = False
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
"""Warn the first time a trace in this process
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"""
|
|
52
|
-
global _warned_unattributed
|
|
53
|
-
if _warned_unattributed or trace.declared_name is not None or trace.program_id is not None:
|
|
47
|
+
def _warn_once_about_generated_name(trace: models.TraceBuilder) -> None:
|
|
48
|
+
"""Warn the first time a trace in this process goes to a program under a generated name."""
|
|
49
|
+
global _warned_unnamed
|
|
50
|
+
if _warned_unnamed or trace.generated_name is None:
|
|
54
51
|
return
|
|
55
|
-
|
|
52
|
+
_warned_unnamed = True
|
|
56
53
|
logger.warning(
|
|
57
|
-
"cmpnd: this
|
|
58
|
-
"
|
|
59
|
-
"
|
|
54
|
+
"cmpnd: this program has no name, so its traces go to the program named %r, a name cmpnd generated from "
|
|
55
|
+
"its class and its signature. Name the program with @cmpnd.program(name) or "
|
|
56
|
+
"cmpnd.Program(name, module=..., lm=...) and run it through a cmpnd.Client, and its traces go to the program "
|
|
57
|
+
"with that name. This warning appears once per process.",
|
|
58
|
+
trace.generated_name,
|
|
60
59
|
)
|
|
61
60
|
|
|
62
61
|
|
|
@@ -711,6 +710,7 @@ class _CallbackBase:
|
|
|
711
710
|
A trace from a call that declares a program name carries the name and no id,
|
|
712
711
|
and the exporter resolves the name. The declared name wins over a program id the
|
|
713
712
|
client's config read from ``CMPND_PROGRAM_ID``, and the callback warns once that it did.
|
|
713
|
+
A trace that gets neither gets a generated name in `_create_trace`.
|
|
714
714
|
"""
|
|
715
715
|
builder.project_tags = self._resolve_project_tags()
|
|
716
716
|
if isinstance(builder, models.TraceBuilder):
|
|
@@ -1214,10 +1214,6 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1214
1214
|
module_class=instance.__class__.__name__,
|
|
1215
1215
|
)
|
|
1216
1216
|
self._stamp(trace)
|
|
1217
|
-
# A root module call is where a program runs, so it is where an unnamed one
|
|
1218
|
-
# is caught. GEPA's own LM calls (_emit_optimization_lm_trace) belong to the
|
|
1219
|
-
# optimization run, which names the program, so they never warn.
|
|
1220
|
-
_warn_once_if_unattributed(trace)
|
|
1221
1217
|
|
|
1222
1218
|
# Attach the run ids this trace starts under, from this callback's own
|
|
1223
1219
|
# state right now — not at finalize (on_module_end), where the
|
|
@@ -1261,6 +1257,9 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1261
1257
|
elif hasattr(instance, "predict") and hasattr(instance.predict, "signature"):
|
|
1262
1258
|
signature = instance.predict.signature
|
|
1263
1259
|
|
|
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
|
|
1264
1263
|
if signature:
|
|
1265
1264
|
# Single-signature module (Predict, ChainOfThought, etc.)
|
|
1266
1265
|
sig_name = getattr(signature, "__name__", None)
|
|
@@ -1288,7 +1287,8 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1288
1287
|
identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
|
|
1289
1288
|
]
|
|
1290
1289
|
|
|
1291
|
-
|
|
1290
|
+
program_hash = identity.compute_program_hash(instance)
|
|
1291
|
+
trace.program_signature_hash = program_hash or 0
|
|
1292
1292
|
trace.signature_summary = identity.build_signature_summary(signature)
|
|
1293
1293
|
else:
|
|
1294
1294
|
# Multi-child module: collect Predict children via DSPy's own walker
|
|
@@ -1303,7 +1303,8 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1303
1303
|
|
|
1304
1304
|
if children:
|
|
1305
1305
|
trace.signature_name = instance.__class__.__name__
|
|
1306
|
-
|
|
1306
|
+
program_hash = identity.compute_program_hash(instance)
|
|
1307
|
+
trace.program_signature_hash = program_hash or 0
|
|
1307
1308
|
|
|
1308
1309
|
# Build component_signatures for grouped field display
|
|
1309
1310
|
components = []
|
|
@@ -1332,6 +1333,16 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1332
1333
|
components.append(comp)
|
|
1333
1334
|
trace.component_signatures = components
|
|
1334
1335
|
|
|
1336
|
+
# A trace that declares neither an id nor a name takes the program of the
|
|
1337
|
+
# optimization run it belongs to, so a run's judge modules land in the program it
|
|
1338
|
+
# optimizes. Otherwise a root module call is where a program runs, so it is where
|
|
1339
|
+
# an unnamed one gets its generated name. A span never names a program.
|
|
1340
|
+
if trace.program_id is None and trace.declared_name is None:
|
|
1341
|
+
trace.program_id = context.optimization_program_id(trace.optimization_run_id)
|
|
1342
|
+
if trace.program_id is None and trace.declared_name is None:
|
|
1343
|
+
trace.generated_name = program_naming.generated_program_name(instance, program_hash)
|
|
1344
|
+
_warn_once_about_generated_name(trace)
|
|
1345
|
+
|
|
1335
1346
|
# Detect if this is a compiled program (from optimization)
|
|
1336
1347
|
if hasattr(instance, "_optimization_run_id"):
|
|
1337
1348
|
trace.compiled_from_run_id = str(instance._optimization_run_id)
|
|
@@ -1661,6 +1672,10 @@ class _LMMixin(_CallbackBase):
|
|
|
1661
1672
|
module_class="GEPA",
|
|
1662
1673
|
)
|
|
1663
1674
|
self._stamp(trace)
|
|
1675
|
+
# The call has no root module to name a program after, so a trace that declares
|
|
1676
|
+
# neither an id nor a name takes the program the run optimizes.
|
|
1677
|
+
if trace.program_id is None and trace.declared_name is None:
|
|
1678
|
+
trace.program_id = context.optimization_program_id(run_id)
|
|
1664
1679
|
# A metric-phase call can start while DSPy's Evaluate span is still on the
|
|
1665
1680
|
# context stack, so on_lm_start stamped this span with a parent_span_id for
|
|
1666
1681
|
# a span that isn't in this standalone one-span trace. This span IS the
|
|
@@ -121,6 +121,12 @@ def _register_org(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
121
121
|
elect_owner.add_argument("user_id", help="User id to make owner")
|
|
122
122
|
elect_owner.set_defaults(func=handle_org_elect_owner)
|
|
123
123
|
|
|
124
|
+
usage = osub.add_parser(
|
|
125
|
+
"usage", help="Show the org's limits in use and its activity over 24 hours, 7 days and 30 days"
|
|
126
|
+
)
|
|
127
|
+
_org_id_arg(usage)
|
|
128
|
+
usage.set_defaults(func=handle_org_usage)
|
|
129
|
+
|
|
124
130
|
plan = osub.add_parser("plan", help="An org's plan and its limits")
|
|
125
131
|
plsub = plan.add_subparsers(dest="org_plan_action")
|
|
126
132
|
plan_show = plsub.add_parser("show", help="Show the org's plan and every limit as it resolves for the org")
|
|
@@ -162,6 +168,10 @@ def _register_user(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
162
168
|
_user_id_arg(show)
|
|
163
169
|
show.set_defaults(func=handle_user_show)
|
|
164
170
|
|
|
171
|
+
activity = usub.add_parser("activity", help="Show a user's milestones, memberships and recorded changes")
|
|
172
|
+
_user_id_arg(activity)
|
|
173
|
+
activity.set_defaults(func=handle_user_activity)
|
|
174
|
+
|
|
165
175
|
deprecation.alias(usub, "get", "show", func=handle_user_show, configure=_user_id_arg)
|
|
166
176
|
|
|
167
177
|
create = usub.add_parser("create", help="Provision a user")
|
|
@@ -349,6 +359,10 @@ def handle_org_elect_owner(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
349
359
|
return {"elected": args.user_id, "github_login": args.github_login}
|
|
350
360
|
|
|
351
361
|
|
|
362
|
+
def handle_org_usage(client: api_client.ApiClient, args: Any) -> Any:
|
|
363
|
+
return client.get(f"/api/v1/admin/organizations/{args.org_id}/usage")
|
|
364
|
+
|
|
365
|
+
|
|
352
366
|
def handle_org_plan_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
353
367
|
return client.get(f"/api/v1/admin/organizations/{args.org_id}/plan")
|
|
354
368
|
|
|
@@ -424,6 +438,10 @@ def handle_user_show(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
424
438
|
return client.get(f"/api/v1/admin/users/{args.id}")
|
|
425
439
|
|
|
426
440
|
|
|
441
|
+
def handle_user_activity(client: api_client.ApiClient, args: Any) -> Any:
|
|
442
|
+
return client.get(f"/api/v1/admin/users/{args.id}/activity")
|
|
443
|
+
|
|
444
|
+
|
|
427
445
|
def handle_user_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
428
446
|
# email/name/org_id/is_admin are always sent; password is only included
|
|
429
447
|
# when supplied so the server keeps its own default (e.g. no local login).
|
|
@@ -192,7 +192,8 @@ RELATION_NOUNS: dict[str, str] = {
|
|
|
192
192
|
"registry": "the registered programs, as distinct from the organization's programs",
|
|
193
193
|
"optimized": "the compiled programs optimization runs produced, as distinct from the organization's programs",
|
|
194
194
|
"stats": "a program's execution statistics",
|
|
195
|
-
"usage": "a program's usage over time",
|
|
195
|
+
"usage": "a program's usage over time, or an organization's limits in use and recent activity",
|
|
196
|
+
"activity": "a user's account milestones, memberships and recorded changes",
|
|
196
197
|
"openapi": "the OpenAPI document a deployment serves",
|
|
197
198
|
"history": "a program's distinct prompt instructions over time",
|
|
198
199
|
"evals": "the eval runs recorded against a program",
|
|
@@ -79,6 +79,33 @@ def set_current_optimization_run_id(optimization_run_id: str | None) -> None:
|
|
|
79
79
|
_current_optimization_run_id.set(optimization_run_id)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
# The program each optimization run this process started optimizes, by run id. A trace
|
|
83
|
+
# linked to a run that names no program of its own takes its run's, so everything a run
|
|
84
|
+
# emits is reachable through that program: GEPA's reflection and judge calls, a judge
|
|
85
|
+
# module's trace, the rollouts. Keyed by run id rather than held in a context variable
|
|
86
|
+
# because the run id reaches a trace two ways (the callback's field and a context
|
|
87
|
+
# variable, for evaluator threads), and whichever way a trace learns its run, this
|
|
88
|
+
# answers for it. One entry per run, kept for the life of the process: a trace may still
|
|
89
|
+
# be built after its run's record settles, and the map holds two short strings a run.
|
|
90
|
+
_optimization_program_ids: dict[str, str] = {}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def set_optimization_program_id(optimization_run_id: str, program_id: str) -> None:
|
|
94
|
+
"""Record the program an optimization run optimizes, once the run has both ids."""
|
|
95
|
+
_optimization_program_ids[optimization_run_id] = program_id
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def optimization_program_id(optimization_run_id: str | None) -> str | None:
|
|
99
|
+
"""The program the optimization run ``optimization_run_id`` optimizes, or None.
|
|
100
|
+
|
|
101
|
+
None for no run, and for a run that names no program, as one whose generated name
|
|
102
|
+
serve could not answer for does.
|
|
103
|
+
"""
|
|
104
|
+
if optimization_run_id is None:
|
|
105
|
+
return None
|
|
106
|
+
return _optimization_program_ids.get(optimization_run_id)
|
|
107
|
+
|
|
108
|
+
|
|
82
109
|
def create_child_span(
|
|
83
110
|
name: str, span_type: models.SpanType, *, root: bool = False, **kwargs: Any
|
|
84
111
|
) -> models.SpanBuilder:
|
|
@@ -121,3 +148,4 @@ def clear_context() -> None:
|
|
|
121
148
|
_span_stack.set(())
|
|
122
149
|
_current_eval_run.set(None)
|
|
123
150
|
_current_optimization_run_id.set(None)
|
|
151
|
+
_optimization_program_ids.clear()
|
|
@@ -10,7 +10,7 @@ import logging
|
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from . import context, helpers, identity, models
|
|
13
|
+
from . import context, helpers, identity, models, program_naming
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -177,7 +177,9 @@ class EvalHandler:
|
|
|
177
177
|
eval_run._dataset_exists = None # Will include examples as fallback
|
|
178
178
|
|
|
179
179
|
exp = self._callback._current_exporter()
|
|
180
|
-
eval_run.program_id = self._program_id(
|
|
180
|
+
eval_run.program_id = self._program_id(
|
|
181
|
+
exp, program, eval_run.program_signature_hash, eval_run.optimization_run_id
|
|
182
|
+
)
|
|
181
183
|
|
|
182
184
|
logger.info(
|
|
183
185
|
f"EvalRun created: program_id={eval_run.program_id}, "
|
|
@@ -214,20 +216,31 @@ class EvalHandler:
|
|
|
214
216
|
self._callback._call_id_to_span[call_id] = span
|
|
215
217
|
context.push_span(span)
|
|
216
218
|
|
|
217
|
-
def _program_id(
|
|
219
|
+
def _program_id(
|
|
220
|
+
self, exp: Any, program: Any, program_hash: int | None, optimization_run_id: str | None
|
|
221
|
+
) -> str | None:
|
|
218
222
|
"""The program id the eval run carries, resolved the way a trace's is.
|
|
219
223
|
|
|
220
|
-
A program name the call declares wins over the callback's program id.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
A program name the call declares wins over the callback's program id. A run
|
|
225
|
+
that has neither takes the program of the optimization run it belongs to, else
|
|
226
|
+
the name the SDK generates for the evaluated program, the name its traces carry.
|
|
227
|
+
The exporter resolves either name now, on the caller's thread, because the run's
|
|
228
|
+
start is sent synchronously and must carry the id. The exporter keeps the answer
|
|
229
|
+
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. ``program_hash`` is the
|
|
231
|
+
evaluated program's signature hash, already computed for the run, so naming the
|
|
232
|
+
program does not reflect it again.
|
|
225
233
|
"""
|
|
226
234
|
from . import callback
|
|
227
235
|
|
|
228
236
|
name = callback._declared_program_name()
|
|
229
237
|
if name is None:
|
|
230
|
-
|
|
238
|
+
program_id = self._callback._resolve_program_id() or context.optimization_program_id(optimization_run_id)
|
|
239
|
+
if program_id is not None or program is None:
|
|
240
|
+
return program_id
|
|
241
|
+
return program_naming.resolve_generated_name(
|
|
242
|
+
exp, program_naming.generated_program_name(program, program_hash)
|
|
243
|
+
)
|
|
231
244
|
# Read with getattr because the member is optional: an in-memory collector
|
|
232
245
|
# that resolves no names has none.
|
|
233
246
|
resolve = getattr(exp, "resolve_program_name", None)
|
|
@@ -967,6 +967,10 @@ class TraceBuilder:
|
|
|
967
967
|
# exporter resolves it to a program id and writes that into ``program_id`` on the
|
|
968
968
|
# bodies it sends, unless the trace already carries an id.
|
|
969
969
|
declared_name: str | None = None
|
|
970
|
+
# The name the SDK generated for the trace's program, when the trace declares neither
|
|
971
|
+
# an id nor a name (`program_naming`). The exporter resolves it as it resolves a
|
|
972
|
+
# declared name, and tells serve that the SDK generated it.
|
|
973
|
+
generated_name: str | None = None
|
|
970
974
|
|
|
971
975
|
# Optimization lineage - tracks which optimization produced this program
|
|
972
976
|
compiled_from_run_id: str | None = None
|
|
@@ -1275,14 +1279,17 @@ class EvalRunBuilder:
|
|
|
1275
1279
|
|
|
1276
1280
|
# Eval Identity (defines comparability)
|
|
1277
1281
|
# The program the run evaluates: the id serve gave the program name the call
|
|
1278
|
-
# declared,
|
|
1282
|
+
# declared, else the client's program id, else the id serve gave the name the SDK
|
|
1283
|
+
# generated for the evaluated module. None when the run has no program.
|
|
1279
1284
|
program_id: str | None = None
|
|
1280
1285
|
# The evaluated module's class, such as "ReAct".
|
|
1281
1286
|
module_class: str | None = None
|
|
1282
1287
|
metric_name: str | None = None
|
|
1283
1288
|
dataset_hash: str | None = None
|
|
1284
1289
|
dataset_size: int = 0
|
|
1285
|
-
|
|
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
|
|
1286
1293
|
signature_summary: str | None = None # Human-readable: "input1, input2 -> output1"
|
|
1287
1294
|
|
|
1288
1295
|
# Model Configuration
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
from typing import TYPE_CHECKING, Any
|
|
7
7
|
|
|
8
|
-
from .. import _gepa_patch, identity
|
|
8
|
+
from .. import _gepa_patch, callback, identity, program_naming
|
|
9
9
|
from . import gepa_callback, tracker
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
@@ -166,6 +166,14 @@ class TrackedGEPA:
|
|
|
166
166
|
# program's `dspy.Flex` components itself.
|
|
167
167
|
self._tracker.component_types = tracker.code_component_types(student)
|
|
168
168
|
|
|
169
|
+
# A run that declares neither a program id nor a program name takes the name the
|
|
170
|
+
# SDK generates for the student, the name its rollouts' traces carry, and
|
|
171
|
+
# resolves it when the run starts.
|
|
172
|
+
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
|
+
)
|
|
176
|
+
|
|
169
177
|
# Store both sets for dataset payload building
|
|
170
178
|
self._tracker._trainset = list(trainset)
|
|
171
179
|
self._tracker._valset = list(effective_valset)
|
|
@@ -11,7 +11,7 @@ from datetime import datetime, timezone
|
|
|
11
11
|
from typing import TYPE_CHECKING, Any
|
|
12
12
|
from uuid import UUID
|
|
13
13
|
|
|
14
|
-
from .. import configuration, identity, models
|
|
14
|
+
from .. import configuration, context, identity, models, program_naming
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from .. import exporter_http
|
|
@@ -287,6 +287,10 @@ class OptimizationTracker:
|
|
|
287
287
|
# The program the run optimizes, snapshotted like project_tags. None
|
|
288
288
|
# declares none, and the key is then left off the run upsert.
|
|
289
289
|
self.program_id: str | None = configuration._current_program_id()
|
|
290
|
+
# The name the SDK generated for the program, for a run that declares neither a
|
|
291
|
+
# program id nor a program name (`program_naming`). The run resolves it when it
|
|
292
|
+
# is first sent, on the caller's thread, and carries the id serve gives.
|
|
293
|
+
self.generated_program_name: str | None = None
|
|
290
294
|
# A client's optimize_program sets this to its own pooled transport, so the
|
|
291
295
|
# run record is published on the client's account (its endpoint + key)
|
|
292
296
|
# rather than the process-global exporter. None means the global exporter.
|
|
@@ -842,6 +846,7 @@ class OptimizationTracker:
|
|
|
842
846
|
if not exp:
|
|
843
847
|
logger.debug("No exporter available, optimization start not exported")
|
|
844
848
|
return
|
|
849
|
+
self._resolve_generated_program(exp)
|
|
845
850
|
|
|
846
851
|
# A GEPA job resumed from the same log_dir is one continuous
|
|
847
852
|
# run. If a prior run left a checkpoint + our run_id sidecar, re-adopt
|
|
@@ -853,6 +858,7 @@ class OptimizationTracker:
|
|
|
853
858
|
self.optimization_run_id, prior_start = readopted
|
|
854
859
|
if prior_start is not None:
|
|
855
860
|
self.start_time = prior_start
|
|
861
|
+
self._publish_program()
|
|
856
862
|
logger.info(f"Resuming optimization run: run_id={self.optimization_run_id} (log_dir={log_dir})")
|
|
857
863
|
return
|
|
858
864
|
|
|
@@ -861,6 +867,7 @@ class OptimizationTracker:
|
|
|
861
867
|
assigned = exp.export_optimization_start(self)
|
|
862
868
|
if assigned:
|
|
863
869
|
self.optimization_run_id = assigned
|
|
870
|
+
self._publish_program()
|
|
864
871
|
# Persist the id so a later resume of the same log_dir re-adopts it.
|
|
865
872
|
if log_dir and self.optimization_run_id:
|
|
866
873
|
resume.write_run_id(log_dir, self.optimization_run_id, self.program_signature_hash, self.start_time)
|
|
@@ -1089,6 +1096,28 @@ class OptimizationTracker:
|
|
|
1089
1096
|
return
|
|
1090
1097
|
self._export_update()
|
|
1091
1098
|
|
|
1099
|
+
def _resolve_generated_program(self, exp: Any) -> None:
|
|
1100
|
+
"""Give a run that names no program the id of its generated name, before it is sent.
|
|
1101
|
+
|
|
1102
|
+
Resolved now, on the caller's thread, because the run's first record goes out
|
|
1103
|
+
synchronously and must carry its program id. The exporter keeps the answer for
|
|
1104
|
+
the process, so the run's rollouts, whose traces carry the same name, cost no
|
|
1105
|
+
second request. A name serve refuses or cannot answer leaves the run without an
|
|
1106
|
+
id.
|
|
1107
|
+
"""
|
|
1108
|
+
if self.program_id is None and self.generated_program_name is not None:
|
|
1109
|
+
self.program_id = program_naming.resolve_generated_name(exp, self.generated_program_name)
|
|
1110
|
+
|
|
1111
|
+
def _publish_program(self) -> None:
|
|
1112
|
+
"""Let the traces this run emits find its program by its run id.
|
|
1113
|
+
|
|
1114
|
+
A trace with no program of its own, such as a reflection or judge call, or a
|
|
1115
|
+
judge module's trace, takes the run's program (`context.optimization_program_id`).
|
|
1116
|
+
The id is the one this run resolved, so publishing it costs no request.
|
|
1117
|
+
"""
|
|
1118
|
+
if self.optimization_run_id and self.program_id is not None:
|
|
1119
|
+
context.set_optimization_program_id(self.optimization_run_id, self.program_id)
|
|
1120
|
+
|
|
1092
1121
|
def _export_update(self) -> None:
|
|
1093
1122
|
"""Update existing optimization run record."""
|
|
1094
1123
|
exp = self._active_exporter()
|
|
@@ -1113,6 +1142,7 @@ class OptimizationTracker:
|
|
|
1113
1142
|
if not exp:
|
|
1114
1143
|
logger.warning("No exporter available, optimization run not exported")
|
|
1115
1144
|
return
|
|
1145
|
+
self._resolve_generated_program(exp)
|
|
1116
1146
|
# Guarded exactly as start() guards it: the exporters that never reach
|
|
1117
1147
|
# serve return None here, so an unconditional assignment would write None
|
|
1118
1148
|
# over a real id the moment one of them meets a run that already has one.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""The names the SDK gives programs their users never named.
|
|
2
|
+
|
|
3
|
+
A trace, an eval run or an optimization run takes its program from the id its config
|
|
4
|
+
declares, else the name its call declares, else a name this module generates for its
|
|
5
|
+
root module. The SDK resolves a generated name to a program id through the same route
|
|
6
|
+
as a declared one, ``PUT /api/v1/programs/by-name/{name}``, and says that it generated
|
|
7
|
+
the name, so the program does not count toward the plan's program limit.
|
|
8
|
+
|
|
9
|
+
Serve gives a generated name no meaning. It stores and resolves it like any other
|
|
10
|
+
program name, and never parses it. So the derivation belongs to the SDK alone, and
|
|
11
|
+
``generated_program_name`` is the only function that knows it. A later SDK may derive
|
|
12
|
+
names some other way, such as from where the program was constructed, without any
|
|
13
|
+
change to serve. Programs then fork once, because the new name names no program yet.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
import unicodedata
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from . import identity
|
|
23
|
+
|
|
24
|
+
# A program name is at most 64 characters. The suffix takes 13 of them, "-" and twelve
|
|
25
|
+
# hex digits, and the class takes the rest.
|
|
26
|
+
_MAX_NAME_LENGTH = 64
|
|
27
|
+
_SUFFIX_DIGITS = 12
|
|
28
|
+
_MAX_CLASS_LENGTH = _MAX_NAME_LENGTH - 1 - _SUFFIX_DIGITS
|
|
29
|
+
# A class whose name leaves nothing a program name may hold.
|
|
30
|
+
_FALLBACK_CLASS = "Module"
|
|
31
|
+
|
|
32
|
+
_NOT_IN_A_NAME = re.compile(r"[^A-Za-z0-9._-]+")
|
|
33
|
+
_NAME_PUNCTUATION = "._-"
|
|
34
|
+
_RESERVED_PREFIX = "p_"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def generated_program_name(module: Any, signature_hash: int | None = None) -> str:
|
|
38
|
+
"""The name the SDK gives the program ``module`` runs as, when its user named none.
|
|
39
|
+
|
|
40
|
+
The name is the module's class and twelve hex digits of its signature hash, such as
|
|
41
|
+
``ChainOfThought-17e1f4089a24``. Programs of one shape get one name in every process,
|
|
42
|
+
so their traces land in one program, and programs of different shapes get different
|
|
43
|
+
names. An optimizer's new instructions and demos keep the shape, so they keep the
|
|
44
|
+
name. The name always fits the program-name syntax, whatever the class is called.
|
|
45
|
+
|
|
46
|
+
A caller that already holds ``identity.compute_program_hash(module)`` passes it as
|
|
47
|
+
``signature_hash``, so the module is reflected once. Without it, this function
|
|
48
|
+
computes the hash.
|
|
49
|
+
"""
|
|
50
|
+
if signature_hash is None:
|
|
51
|
+
signature_hash = identity.compute_program_hash(module)
|
|
52
|
+
signature_hash = signature_hash or 0
|
|
53
|
+
suffix = f"{signature_hash & 0xFFFF_FFFF_FFFF_FFFF:016x}"[:_SUFFIX_DIGITS]
|
|
54
|
+
return f"{_class_part(module.__class__.__name__)}-{suffix}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _class_part(class_name: str) -> str:
|
|
58
|
+
"""``class_name`` as the start of a program name: ASCII, no reserved prefix, short enough."""
|
|
59
|
+
ascii_name = unicodedata.normalize("NFKD", class_name).encode("ascii", "ignore").decode("ascii")
|
|
60
|
+
part = _NOT_IN_A_NAME.sub("_", ascii_name)
|
|
61
|
+
# A name starts with a letter or digit and never with "p_", in any case.
|
|
62
|
+
while True:
|
|
63
|
+
part = part.lstrip(_NAME_PUNCTUATION)
|
|
64
|
+
if not part.lower().startswith(_RESERVED_PREFIX):
|
|
65
|
+
break
|
|
66
|
+
part = part[len(_RESERVED_PREFIX) :]
|
|
67
|
+
part = part[:_MAX_CLASS_LENGTH].rstrip(_NAME_PUNCTUATION)
|
|
68
|
+
return part or _FALLBACK_CLASS
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def resolve_generated_name(exporter: Any, name: str) -> str | None:
|
|
72
|
+
"""The program id for a generated name, resolved now on the caller's thread.
|
|
73
|
+
|
|
74
|
+
For a run record that is sent synchronously and must carry its program id then, as
|
|
75
|
+
an eval run's start and an optimization run's start are. The exporter keeps the
|
|
76
|
+
answer for the process, so a name its traces already resolved costs no request.
|
|
77
|
+
Returns None when the exporter resolves no names, as a worker's in-memory collector
|
|
78
|
+
does, or when serve refused the name or could not be reached.
|
|
79
|
+
"""
|
|
80
|
+
# Read with getattr because the member is optional: an exporter that resolves no
|
|
81
|
+
# names has none.
|
|
82
|
+
resolve = getattr(exporter, "resolve_program_name", None)
|
|
83
|
+
if not callable(resolve):
|
|
84
|
+
return None
|
|
85
|
+
program_id = resolve(name, generated=True)
|
|
86
|
+
return program_id if isinstance(program_id, str) else None
|
|
@@ -890,6 +890,23 @@ and a value of `none` lifts the plan's limit for that org.
|
|
|
890
890
|
`cmpnd admin org overrides delete ORG_ID NAME` deletes the override, so the org
|
|
891
891
|
follows its plan again.
|
|
892
892
|
|
|
893
|
+
`cmpnd admin org usage ORG_ID` shows what each limit bounds now, in `in_use`,
|
|
894
|
+
and the org's activity in the last 24 hours, 7 days and 30 days, in `windows`.
|
|
895
|
+
Each window counts traces, model calls and tokens, deployment runs by status,
|
|
896
|
+
and hosted optimizations started and cancelled with their compute. `since` is
|
|
897
|
+
the start of the window's first hour. `traces` or `model_usage` is `null` when
|
|
898
|
+
the server has no published count for the window yet.
|
|
899
|
+
|
|
900
|
+
`cmpnd admin user activity USER_ID` shows a user's milestones, every org they
|
|
901
|
+
belong to, and the newest 50 authority changes recorded about them, such as
|
|
902
|
+
approvals, ownership grants, invites and leaves. A milestone that has not
|
|
903
|
+
happened is `null`. `first_approved_at` is the first approval still on record: an admin-created
|
|
904
|
+
user, an invite redemption and an approved founder write no approval event, so
|
|
905
|
+
their date survives only while a membership keeps it. `oldest_key_at` and
|
|
906
|
+
`last_key_used_at` cover only the keys
|
|
907
|
+
the user still holds, because leaving an org deletes the user's keys there.
|
|
908
|
+
Sign-ins are not recorded, and deployment keys do not count as the user's keys.
|
|
909
|
+
|
|
893
910
|
`cmpnd admin plans list` shows every plan's limits and feature flags.
|
|
894
911
|
`cmpnd admin plans limits update PLAN NAME VALUE` and `limits delete PLAN NAME`
|
|
895
912
|
change a plan's default for every org on it. `cmpnd admin plans flags update
|
|
@@ -122,9 +122,12 @@ qa = CapitalQA(lm=dspy.LM("openai/gpt-5-mini"))
|
|
|
122
122
|
## Sketch mode
|
|
123
123
|
|
|
124
124
|
`auto_instrument()` is sketch mode. It traces every DSPy call and declares no
|
|
125
|
-
program names.
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
program names. Each trace goes to a program under a name the SDK generates from
|
|
126
|
+
its root module's class and signature, such as `ChainOfThought-17e1f4089a24`, so
|
|
127
|
+
each program shape lands in a program of its own. Such a program does not count
|
|
128
|
+
toward the plan's program limit. The first such trace in a process logs a warning
|
|
129
|
+
that names the generated name and points to `@cmpnd.program(name)`,
|
|
130
|
+
`cmpnd.Program(name, module=..., lm=...)` and `cmpnd.Client`. A named program refuses sketch mode: calling a `cmpnd.Program`
|
|
128
131
|
directly while `auto_instrument()` is on raises a `TypeError` that points to
|
|
129
132
|
`cmpnd.Client`. The refusal applies at any depth, so a program nested inside a
|
|
130
133
|
plain module called under `auto_instrument()` raises it too. A program run
|