cmpnd 0.61.0__tar.gz → 0.63.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.61.0 → cmpnd-0.63.0}/PKG-INFO +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_traces.py +7 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/callback.py +2 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/org.py +71 -4
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/top.py +19 -6
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/traces.py +40 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/shell.py +6 -4
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/trace_render.py +29 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/models.py +9 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/cli.md +34 -7
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/reading-traces.md +3 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/sdk-instrumentation.md +7 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/trace_facts.py +8 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/pyproject.toml +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/capture_review_run.py +5 -5
- cmpnd-0.63.0/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/seed_review_run.py +5 -5
- cmpnd-0.63.0/tests/e2e/target_capabilities.py +58 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_composite_module.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_execute.py +0 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_pandas.py +6 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_programs.py +5 -0
- cmpnd-0.63.0/tests/e2e/test_optimize_authored_metric.py +78 -0
- cmpnd-0.63.0/tests/e2e/test_optimize_custom_proposer.py +158 -0
- cmpnd-0.63.0/tests/e2e/test_optimize_failures.py +69 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_server_owned_completion.py +7 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_parallel_export.py +2 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_project_attribution.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/rlm_shape.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_trace_streaming.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_traces.py +4 -4
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/span_reference_fixture.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/_provider.py +19 -25
- cmpnd-0.63.0/tests/stress/test_bedrock_converse.py +127 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_multi_client_stress.py +15 -5
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_callback.py +4 -4
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_org.py +160 -4
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_shell.py +3 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_traces.py +2 -2
- cmpnd-0.63.0/tests/test_cli/test_traces_program_and_module.py +274 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_traces_search_wire.py +4 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_client.py +2 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_client_program_names.py +2 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_context_propagation.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_eval_example_attribution.py +6 -6
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_eval_trace_grouping.py +3 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_exporter.py +3 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_exporter_program_names.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_exporter_queue.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_exporter_real.py +3 -3
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_ingest_conformance.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_integration.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_models.py +6 -6
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_multi_client_stress_readiness.py +6 -5
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_program_identity.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_project_payload.py +7 -7
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_repro_reactv2_bugs.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_run_attribution_paths.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_schemas.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_trace_fact_wire_contract.py +1 -1
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_trace_facts.py +8 -8
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_trace_render.py +3 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_truncation.py +2 -2
- {cmpnd-0.61.0 → cmpnd-0.63.0}/uv.lock +1 -1
- cmpnd-0.61.0/scripts/fixtures/review_run.json.gz +0 -0
- cmpnd-0.61.0/tests/e2e/target_capabilities.py +0 -70
- cmpnd-0.61.0/tests/e2e/test_optimize_failures.py +0 -136
- {cmpnd-0.61.0 → cmpnd-0.63.0}/.gitignore +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/AGENTS.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/CLAUDE.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/README.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/client.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/context.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/references.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/client.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/README.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/drain.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_context.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_eval.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_execute.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_identity.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_retry.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.61.0 → cmpnd-0.63.0}/tests/test_xxh64.py +0 -0
|
@@ -4,7 +4,7 @@ The implementation of ``list`` and ``show`` over the shared ``_http`` transport.
|
|
|
4
4
|
:class:`cmpnd.Client`'s ``list_traces`` / ``get_trace`` call these directly; the
|
|
5
5
|
deprecated :mod:`cmpnd.traces` shim wraps them with a warning. The filter and
|
|
6
6
|
verb names match the ``cmpnd traces`` CLI vocabulary — ``list`` filtered by
|
|
7
|
-
``query`` / ``program`` / ``hash`` / ``deployment`` / ``since`` / ``until`` /
|
|
7
|
+
``query`` / ``program`` / ``module`` / ``hash`` / ``deployment`` / ``since`` / ``until`` /
|
|
8
8
|
``all_history``, and ``show`` for one trace — so the surfaces do not drift.
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -28,6 +28,7 @@ def list(
|
|
|
28
28
|
query: str | None = None,
|
|
29
29
|
model: str | None = None,
|
|
30
30
|
program: str | None = None,
|
|
31
|
+
module: str | None = None,
|
|
31
32
|
hash: str | None = None,
|
|
32
33
|
deployment: str | None = None,
|
|
33
34
|
metadata: dict[str, str] | None = None,
|
|
@@ -49,7 +50,10 @@ def list(
|
|
|
49
50
|
|
|
50
51
|
The filters mirror ``cmpnd traces list``: ``query`` searches indexed span text;
|
|
51
52
|
``program`` names the program by name, and ``hash`` by its program id (``p_…``)
|
|
52
|
-
or a legacy signature hash;
|
|
53
|
+
or a legacy signature hash; ``module`` is the root module's class, matched exactly.
|
|
54
|
+
No index serves the class, so without ``program`` or a program id in ``hash`` it
|
|
55
|
+
reads every trace in the time range and slows as the range grows. Pair it with a program or a shorter
|
|
56
|
+
``since``;
|
|
53
57
|
``deployment`` is a deployment id; ``metadata`` is matched by containment (every
|
|
54
58
|
pair must be present); ``tags`` matches any of the given tags. ``since`` /
|
|
55
59
|
``until`` accept an ISO 8601 timestamp, a bare date (``YYYY-MM-DD``), or a
|
|
@@ -68,6 +72,7 @@ def list(
|
|
|
68
72
|
("q", query),
|
|
69
73
|
("model", model),
|
|
70
74
|
("program_name", program),
|
|
75
|
+
("module_class", module),
|
|
71
76
|
("deployment_id", deployment),
|
|
72
77
|
("tags", tags),
|
|
73
78
|
):
|
|
@@ -1190,7 +1190,7 @@ class _ModuleMixin(_CallbackBase):
|
|
|
1190
1190
|
trace = models.TraceBuilder(
|
|
1191
1191
|
trace_id=uuid4(),
|
|
1192
1192
|
start_time=datetime.now(timezone.utc),
|
|
1193
|
-
|
|
1193
|
+
module_class=instance.__class__.__name__,
|
|
1194
1194
|
)
|
|
1195
1195
|
self._stamp(trace)
|
|
1196
1196
|
|
|
@@ -1633,7 +1633,7 @@ class _LMMixin(_CallbackBase):
|
|
|
1633
1633
|
trace_id=span.trace_id or uuid4(),
|
|
1634
1634
|
start_time=span.start_time,
|
|
1635
1635
|
end_time=span.end_time,
|
|
1636
|
-
|
|
1636
|
+
module_class="GEPA",
|
|
1637
1637
|
)
|
|
1638
1638
|
self._stamp(trace)
|
|
1639
1639
|
# A metric-phase call can start while DSPy's Evaluate span is still on the
|
|
@@ -29,17 +29,21 @@ adds one) and `switch` picks which stored key later commands use, the way the
|
|
|
29
29
|
web app switches a session's active org. `list` reads
|
|
30
30
|
/api/v1/auth/organizations and marks the orgs you hold a key for and the
|
|
31
31
|
active one. `switch` needs no network call when the org's key is already
|
|
32
|
-
stored.
|
|
33
|
-
|
|
32
|
+
stored. When none is stored for an org you are an approved member of, it
|
|
33
|
+
offers to mint one with the current key's scopes (`--mint` skips the
|
|
34
|
+
question), stores it and switches to it. `cmpnd org leave` removes your own
|
|
35
|
+
membership and drops that org's stored key.
|
|
34
36
|
|
|
35
37
|
Always registered (no env gate); the server is the authority.
|
|
36
38
|
"""
|
|
37
39
|
|
|
38
40
|
from __future__ import annotations
|
|
39
41
|
|
|
42
|
+
import socket
|
|
40
43
|
import sys
|
|
41
44
|
import urllib.parse
|
|
42
45
|
from argparse import ArgumentParser, _SubParsersAction
|
|
46
|
+
from datetime import datetime, timezone
|
|
43
47
|
from typing import Any
|
|
44
48
|
|
|
45
49
|
from .. import api_client, credentials
|
|
@@ -91,6 +95,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
91
95
|
|
|
92
96
|
switch = sub.add_parser("switch", help="Use the stored key for another of your organizations")
|
|
93
97
|
switch.add_argument("org", help="Organization slug or id (from `org list`)")
|
|
98
|
+
switch.add_argument(
|
|
99
|
+
"--mint",
|
|
100
|
+
action="store_true",
|
|
101
|
+
help="When no key is stored for the org, mint one with the current key's scopes without asking",
|
|
102
|
+
)
|
|
94
103
|
switch.set_defaults(func=handle_switch, _skip_resolve_config=True)
|
|
95
104
|
|
|
96
105
|
leave = sub.add_parser("leave", help="Leave one of your organizations and drop its stored key")
|
|
@@ -214,12 +223,70 @@ def handle_switch(_unused_client: api_client.ApiClient | None, args: Any) -> Non
|
|
|
214
223
|
api_key, _ = api_client.resolve_config(args)
|
|
215
224
|
except api_client.CliError:
|
|
216
225
|
raise _no_key_stored(f"'{target}'", endpoint, target) from None
|
|
217
|
-
|
|
226
|
+
client = api_client.ApiClient(api_key, endpoint)
|
|
227
|
+
for row in _memberships(client):
|
|
218
228
|
if target in (row.get("id"), row.get("slug")):
|
|
219
|
-
|
|
229
|
+
_mint_and_switch(client, row, target, ask=not getattr(args, "mint", False))
|
|
230
|
+
return
|
|
220
231
|
raise api_client.CliError(f"You are not a member of an organization '{target}' on {endpoint}.")
|
|
221
232
|
|
|
222
233
|
|
|
234
|
+
def _mint_and_switch(client: api_client.ApiClient, row: dict[str, Any], target: str, *, ask: bool) -> None:
|
|
235
|
+
"""Mint a key for a member org that has none stored, store it and switch.
|
|
236
|
+
|
|
237
|
+
The request names no scopes, so serve grants the calling key's own
|
|
238
|
+
non-admin scopes and the new key is never broader than the one in use.
|
|
239
|
+
serve refuses with 400 or 403 when the calling key cannot mint, and the
|
|
240
|
+
browser login is then the way to get the key.
|
|
241
|
+
"""
|
|
242
|
+
endpoint = client._endpoint
|
|
243
|
+
label = str(row.get("name") or row.get("slug") or target)
|
|
244
|
+
if row.get("approved") is False:
|
|
245
|
+
raise api_client.CliError(
|
|
246
|
+
f"Your membership in {label} is not approved yet, so no key can be minted for it. "
|
|
247
|
+
"An owner of the org approves it."
|
|
248
|
+
)
|
|
249
|
+
if ask:
|
|
250
|
+
if not sys.stdin.isatty():
|
|
251
|
+
raise api_client.CliError(
|
|
252
|
+
f"No key is stored for {label} on {endpoint}. Run {top.hint_org_switch(target, mint=True)} "
|
|
253
|
+
f"to mint one with the current key's scopes, or {top.hint_login(target)}."
|
|
254
|
+
)
|
|
255
|
+
try:
|
|
256
|
+
answer = input(f"No key is stored for {label}. Mint one with the current key's scopes? [Y/n] ")
|
|
257
|
+
except EOFError:
|
|
258
|
+
# Ctrl-D at the prompt declines.
|
|
259
|
+
print(file=sys.stderr)
|
|
260
|
+
answer = "n"
|
|
261
|
+
if answer.strip().lower() not in ("", "y", "yes"):
|
|
262
|
+
raise _no_key_stored(label, endpoint, target)
|
|
263
|
+
|
|
264
|
+
hostname = socket.gethostname() or "unknown"
|
|
265
|
+
name = f"CLI: {hostname} {datetime.now(timezone.utc).strftime('%Y-%m-%d')}"
|
|
266
|
+
try:
|
|
267
|
+
created = client.post("/api/v1/org/keys", json={"name": name, "org_id": row.get("id") or target})
|
|
268
|
+
except api_client.CliError as exc:
|
|
269
|
+
if exc.status_code in (400, 403):
|
|
270
|
+
raise api_client.CliError(
|
|
271
|
+
f"The current key could not mint a key for {label}: {exc}. Run {top.hint_login(target)} to store one."
|
|
272
|
+
) from exc
|
|
273
|
+
raise
|
|
274
|
+
token = created.get("token") if isinstance(created, dict) else None
|
|
275
|
+
org_id = created.get("org_id") if isinstance(created, dict) else None
|
|
276
|
+
if not isinstance(token, str) or not token or not isinstance(org_id, str) or not org_id:
|
|
277
|
+
raise api_client.CliError(f"The response did not include a key for {label}. Nothing was stored.")
|
|
278
|
+
name_out = created.get("org_name")
|
|
279
|
+
slug_out = created.get("org_slug")
|
|
280
|
+
credentials.save(
|
|
281
|
+
endpoint,
|
|
282
|
+
token,
|
|
283
|
+
org_id=org_id,
|
|
284
|
+
org_name=name_out if isinstance(name_out, str) else None,
|
|
285
|
+
org_slug=slug_out if isinstance(slug_out, str) else None,
|
|
286
|
+
)
|
|
287
|
+
print(f"Minted a key for {name_out or slug_out or org_id} ({org_id}) and switched to it on {endpoint}")
|
|
288
|
+
|
|
289
|
+
|
|
223
290
|
def _say_active_moved(endpoint: str, moved_to: str | None) -> None:
|
|
224
291
|
if moved_to:
|
|
225
292
|
k = {k.org_id: k for k in credentials.stored_keys(endpoint)}.get(moved_to)
|
|
@@ -18,7 +18,7 @@ from typing import Any, NoReturn
|
|
|
18
18
|
|
|
19
19
|
from ... import _deployments, deployment_runs
|
|
20
20
|
from ...deployment_metadata import DeploymentMetadataError
|
|
21
|
-
from .. import api_client, pagination
|
|
21
|
+
from .. import api_client, pagination, trace_render
|
|
22
22
|
from . import deployments as deployment_commands
|
|
23
23
|
from . import signatures
|
|
24
24
|
|
|
@@ -180,8 +180,8 @@ def hint_login(org: str | None = None) -> str:
|
|
|
180
180
|
return f"`{cmd}` in a terminal, then restart the shell" if _SHELL_MODE else f"`{cmd}`"
|
|
181
181
|
|
|
182
182
|
|
|
183
|
-
def hint_org_switch(org: str) -> str:
|
|
184
|
-
cmd = f"cmpnd org switch {org}"
|
|
183
|
+
def hint_org_switch(org: str, *, mint: bool = False) -> str:
|
|
184
|
+
cmd = f"cmpnd org switch {org}" + (" --mint" if mint else "")
|
|
185
185
|
# Same reason as hint_login: the shell picks its key once, at startup.
|
|
186
186
|
# Both are public, unlike the other hint helpers, because only org.py
|
|
187
187
|
# calls them.
|
|
@@ -338,8 +338,20 @@ def render_deployments_table(deployments: list[dict[str, Any]]) -> str:
|
|
|
338
338
|
return "\n".join(lines)
|
|
339
339
|
|
|
340
340
|
|
|
341
|
+
def trace_program_cells(trace: dict[str, Any]) -> tuple[str, str]:
|
|
342
|
+
"""The PROGRAM and MODULE cells for one trace record.
|
|
343
|
+
|
|
344
|
+
PROGRAM is the name the user gave the program, or a dash when it has none.
|
|
345
|
+
MODULE is the root module's class, or a dash when the trace carries no
|
|
346
|
+
class. `trace_render.program_and_module` reads an older server's shape,
|
|
347
|
+
where `program_name` holds the class.
|
|
348
|
+
"""
|
|
349
|
+
name, module = trace_render.program_and_module(trace)
|
|
350
|
+
return name or "—", module or "—"
|
|
351
|
+
|
|
352
|
+
|
|
341
353
|
def render_traces_table(traces: list[dict[str, Any]]) -> str:
|
|
342
|
-
"""Return a colored TRACE / STATUS / PROGRAM / AGE table as a string.
|
|
354
|
+
"""Return a colored TRACE / STATUS / PROGRAM / MODULE / AGE table as a string.
|
|
343
355
|
|
|
344
356
|
Reused by the shell when typing `d.traces` at the REPL (via
|
|
345
357
|
`Trace._render_table`). Mirrors `render_deployments_table` so the
|
|
@@ -353,13 +365,13 @@ def render_traces_table(traces: list[dict[str, Any]]) -> str:
|
|
|
353
365
|
(
|
|
354
366
|
id_render(t["trace_id"]),
|
|
355
367
|
t.get("status", "—"),
|
|
356
|
-
_truncate(
|
|
368
|
+
*(_truncate(cell, 28) for cell in trace_program_cells(t)),
|
|
357
369
|
_age(t.get("start_time")),
|
|
358
370
|
)
|
|
359
371
|
for t in traces
|
|
360
372
|
]
|
|
361
373
|
|
|
362
|
-
headers = ("TRACE", "STATUS", "PROGRAM", "AGE")
|
|
374
|
+
headers = ("TRACE", "STATUS", "PROGRAM", "MODULE", "AGE")
|
|
363
375
|
widths = [max(len(h), max((len(r[i]) for r in rows), default=0)) for i, h in enumerate(headers)]
|
|
364
376
|
|
|
365
377
|
header_line = " ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
|
|
@@ -370,6 +382,7 @@ def render_traces_table(traces: list[dict[str, Any]]) -> str:
|
|
|
370
382
|
_trace_status(r[1]).ljust(widths[1] + (len(_trace_status(r[1])) - len(r[1]))),
|
|
371
383
|
r[2].ljust(widths[2]),
|
|
372
384
|
r[3].ljust(widths[3]),
|
|
385
|
+
r[4].ljust(widths[4]),
|
|
373
386
|
]
|
|
374
387
|
lines.append(" ".join(cells))
|
|
375
388
|
return "\n".join(lines)
|
|
@@ -28,6 +28,16 @@ def _metadata_pair(value: str) -> tuple[str, str]:
|
|
|
28
28
|
return key, val
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
# The wait after which a class search with no program filter gets a hint.
|
|
32
|
+
MODULE_SLOW_HINT_SECONDS = 5.0
|
|
33
|
+
|
|
34
|
+
# The search-body keys that name a program, whichever spelling the flag took.
|
|
35
|
+
_PROGRAM_FILTER_KEYS = ("program_name", "program_id", "program_signature_hash")
|
|
36
|
+
|
|
37
|
+
# The clock the hint measures with, a seam for tests.
|
|
38
|
+
_monotonic = time.monotonic
|
|
39
|
+
|
|
40
|
+
|
|
31
41
|
def _add_list_args(p: ArgumentParser) -> None:
|
|
32
42
|
p.add_argument("--status", help="Filter by status (ok, error, incomplete, in_progress)")
|
|
33
43
|
p.add_argument(
|
|
@@ -49,7 +59,20 @@ def _add_list_args(p: ArgumentParser) -> None:
|
|
|
49
59
|
help="Filter by a metadata pair your program recorded; repeat for all-of. "
|
|
50
60
|
"This is how you find a trace by your own identifier",
|
|
51
61
|
)
|
|
52
|
-
p.add_argument(
|
|
62
|
+
p.add_argument(
|
|
63
|
+
"--program",
|
|
64
|
+
dest="program_name",
|
|
65
|
+
metavar="NAME",
|
|
66
|
+
help="Filter by the program's name, the one given with cmpnd.Program or `cmpnd programs create`",
|
|
67
|
+
)
|
|
68
|
+
p.add_argument(
|
|
69
|
+
"--module",
|
|
70
|
+
dest="module_class",
|
|
71
|
+
metavar="CLASS",
|
|
72
|
+
help="Filter by the root module's class (exact, e.g. ReAct). No index serves the class: "
|
|
73
|
+
"without --program or --hash it reads every trace in the time range, so it gets slower "
|
|
74
|
+
"as the range grows. Narrow it with --program, --hash or --since",
|
|
75
|
+
)
|
|
53
76
|
p.add_argument(
|
|
54
77
|
"--hash",
|
|
55
78
|
dest="program_signature_hash",
|
|
@@ -189,6 +212,7 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
189
212
|
("q", "q"),
|
|
190
213
|
("model", "model"),
|
|
191
214
|
("program_name", "program_name"),
|
|
215
|
+
("module_class", "module_class"),
|
|
192
216
|
("deployment_id", "deployment_id"),
|
|
193
217
|
("tags", "tags"),
|
|
194
218
|
("start_time_from", "start_time_from"),
|
|
@@ -211,7 +235,22 @@ def handle_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
211
235
|
body["start_time_from"] = _timespec.resolve_search_time(str(body["start_time_from"]))
|
|
212
236
|
if "start_time_to" in body:
|
|
213
237
|
body["start_time_to"] = _timespec.resolve_search_time(str(body["start_time_to"]), end_of_day=True)
|
|
238
|
+
started = _monotonic()
|
|
214
239
|
resp = client.post("/api/v1/traces/search", json=body)
|
|
240
|
+
# No index serves the class, so a class search with no program filter reads
|
|
241
|
+
# every trace in the range. Say what narrows it once the wait is long enough
|
|
242
|
+
# to notice, on stderr so stdout stays the response.
|
|
243
|
+
if (
|
|
244
|
+
"module_class" in body
|
|
245
|
+
and not any(key in body for key in _PROGRAM_FILTER_KEYS)
|
|
246
|
+
and _monotonic() - started > MODULE_SLOW_HINT_SECONDS
|
|
247
|
+
):
|
|
248
|
+
print(
|
|
249
|
+
"note: --module reads every trace in the time range when no program is given. "
|
|
250
|
+
"Consider filtering by program (--program or --hash) or a shorter range (--since) "
|
|
251
|
+
"for faster results.",
|
|
252
|
+
file=sys.stderr,
|
|
253
|
+
)
|
|
215
254
|
|
|
216
255
|
# A window the caller did not ask for has to be visible: read as an org
|
|
217
256
|
# figure, a windowed total is simply wrong, and nothing in the numbers says
|
|
@@ -32,7 +32,7 @@ from pathlib import Path
|
|
|
32
32
|
from types import SimpleNamespace
|
|
33
33
|
from typing import Any, Callable
|
|
34
34
|
|
|
35
|
-
from . import api_client, commands, credentials, verbs
|
|
35
|
+
from . import api_client, commands, credentials, trace_render, verbs
|
|
36
36
|
|
|
37
37
|
# --------------------------------------------------------------------------
|
|
38
38
|
# Config + dismissible-tips marker
|
|
@@ -259,13 +259,15 @@ class Trace(Entity):
|
|
|
259
259
|
super().__init__(client, record)
|
|
260
260
|
self.status_str = record.get("status")
|
|
261
261
|
self.start_time = record.get("start_time")
|
|
262
|
-
|
|
262
|
+
# The program's own name (None when it has none) and the root module's
|
|
263
|
+
# class, read the same way for a server that predates module_class.
|
|
264
|
+
self.program_name, self.module_class = trace_render.program_and_module(record)
|
|
263
265
|
|
|
264
266
|
def __repr__(self) -> str:
|
|
265
267
|
short = self.id.replace("-", "")[:12]
|
|
266
|
-
program =
|
|
268
|
+
program, module = commands.top.trace_program_cells(self._record)
|
|
267
269
|
age = commands.top._age(self.start_time)
|
|
268
|
-
return f"<Trace {short} status={self.status_str} program={program!r} age={age}>"
|
|
270
|
+
return f"<Trace {short} status={self.status_str} program={program!r} module={module!r} age={age}>"
|
|
269
271
|
|
|
270
272
|
def inspect(self) -> dict[str, Any]:
|
|
271
273
|
"""Return the full trace record (with spans)."""
|
|
@@ -267,8 +267,35 @@ def _int(v: Any) -> int:
|
|
|
267
267
|
return 0
|
|
268
268
|
|
|
269
269
|
|
|
270
|
+
def program_and_module(trace: dict[str, Any]) -> tuple[str | None, str | None]:
|
|
271
|
+
"""The program's name and the root module's class a trace record carries.
|
|
272
|
+
|
|
273
|
+
A server that sends ``module_class`` puts the program's own name in
|
|
274
|
+
``program_name``, empty when the program has none. An older server sends
|
|
275
|
+
no ``module_class``, and its ``program_name`` holds the class, so that
|
|
276
|
+
value is read as the class and the program counts as unnamed.
|
|
277
|
+
"""
|
|
278
|
+
if "module_class" in trace:
|
|
279
|
+
return trace.get("program_name") or None, trace.get("module_class") or None
|
|
280
|
+
return None, trace.get("program_name") or None
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def trace_label(trace: dict[str, Any]) -> str:
|
|
284
|
+
"""One label for a trace: the program's name, else its id, else its class, else its signature.
|
|
285
|
+
|
|
286
|
+
The id shows only for a program with no name: a name already identifies it.
|
|
287
|
+
"""
|
|
288
|
+
name, module = program_and_module(trace)
|
|
289
|
+
return name or trace.get("program_id") or module or trace.get("signature_name") or "trace"
|
|
290
|
+
|
|
291
|
+
|
|
270
292
|
def _header(trace: dict[str, Any], span_total: int, color: bool, arrivals: bool = False) -> str:
|
|
271
|
-
|
|
293
|
+
_, module = program_and_module(trace)
|
|
294
|
+
label = trace_label(trace)
|
|
295
|
+
program = _paint(label, "bold", color)
|
|
296
|
+
if module and label != module:
|
|
297
|
+
# The label names the program; the class beside it says what ran.
|
|
298
|
+
program += " " + _paint(module, "dim", color)
|
|
272
299
|
tid = str(trace.get("trace_id") or "")
|
|
273
300
|
status = trace.get("status")
|
|
274
301
|
meta = [
|
|
@@ -278,7 +305,7 @@ def _header(trace: dict[str, Any], span_total: int, color: bool, arrivals: bool
|
|
|
278
305
|
]
|
|
279
306
|
if _int(trace.get("total_tokens")) > 0:
|
|
280
307
|
meta.append(f"{_int(trace.get('total_tokens'))} tokens")
|
|
281
|
-
line1 = (
|
|
308
|
+
line1 = (program + " " + _paint(tid, "grey", color)).rstrip()
|
|
282
309
|
out = line1 + "\n" + " · ".join(meta)
|
|
283
310
|
if arrivals:
|
|
284
311
|
# Absolute landing time on the header (the one place there is room for
|
|
@@ -942,7 +942,9 @@ class TraceBuilder:
|
|
|
942
942
|
status: TraceStatus = TraceStatus.UNSET
|
|
943
943
|
error_message: str | None = None
|
|
944
944
|
|
|
945
|
-
|
|
945
|
+
# The root DSPy module's class. The program's own name is `declared_name`,
|
|
946
|
+
# which the transport resolves to `program_id`.
|
|
947
|
+
module_class: str | None = None
|
|
946
948
|
program_version: str | None = None
|
|
947
949
|
|
|
948
950
|
# Signature info - identifies the program by its inputs/outputs
|
|
@@ -1071,7 +1073,9 @@ class TraceBuilder:
|
|
|
1071
1073
|
"end_time": self.end_time.isoformat() if self.end_time else None,
|
|
1072
1074
|
"status": self.status.value,
|
|
1073
1075
|
"error_message": _strip_unstorable_opt(self.error_message),
|
|
1074
|
-
"
|
|
1076
|
+
"module_class": self.module_class,
|
|
1077
|
+
# Also under the key a serve from before module_class reads (StartedFact.creation_dict).
|
|
1078
|
+
"program_name": self.module_class,
|
|
1075
1079
|
"program_version": self.program_version,
|
|
1076
1080
|
"signature_name": self.signature_name,
|
|
1077
1081
|
"signature_input_fields": self.signature_input_fields,
|
|
@@ -1112,7 +1116,9 @@ class TraceBuilder:
|
|
|
1112
1116
|
return self._with_facts(
|
|
1113
1117
|
{
|
|
1114
1118
|
"start_time": self.start_time.isoformat() if self.start_time else None,
|
|
1115
|
-
"
|
|
1119
|
+
"module_class": self.module_class,
|
|
1120
|
+
# Also under the key a serve from before module_class reads (StartedFact.creation_dict).
|
|
1121
|
+
"program_name": self.module_class,
|
|
1116
1122
|
"program_version": self.program_version,
|
|
1117
1123
|
"signature_name": self.signature_name,
|
|
1118
1124
|
"signature_input_fields": self.signature_input_fields,
|
|
@@ -291,8 +291,9 @@ caller reads it. To find where a program is slow, list recent traces, then
|
|
|
291
291
|
read each one's spans and durations.
|
|
292
292
|
|
|
293
293
|
- `cmpnd traces list [-q TEXT] [--model] [--metadata k=v …] [--status]
|
|
294
|
-
[--program] [--hash ID-OR-HASH] [--deployment]
|
|
295
|
-
[--until] [--all] [--limit N] [--offset N]` lists
|
|
294
|
+
[--program NAME] [--module CLASS] [--hash ID-OR-HASH] [--deployment]
|
|
295
|
+
[--tag …] [--since] [--until] [--all] [--limit N] [--offset N]` lists
|
|
296
|
+
traces, newest first.
|
|
296
297
|
- `--limit N` returns the N most recent, and `--offset N` skips N rows.
|
|
297
298
|
- `--since` and `--until` bound the start time. Each takes ISO 8601, a date
|
|
298
299
|
(`YYYY-MM-DD`, UTC, the start of the day for `--since` and the end for
|
|
@@ -305,6 +306,17 @@ read each one's spans and durations.
|
|
|
305
306
|
must appear.
|
|
306
307
|
- `--metadata` repeats and matches metadata you attached at trace time, so
|
|
307
308
|
you can find a trace by your own identifier.
|
|
309
|
+
- `--program` takes the program's name, the one you gave with
|
|
310
|
+
`cmpnd.Program` or `cmpnd programs create`. The CLI sends it as the search
|
|
311
|
+
body's `program_name`. A name no program answers to is an error that names
|
|
312
|
+
it, not an empty list.
|
|
313
|
+
- `--module` takes the root module's class, such as `ReAct`, and matches it
|
|
314
|
+
exactly. The CLI sends it as `module_class`. No index serves the class.
|
|
315
|
+
Without `--program` or a program id in `--hash` it reads every trace in
|
|
316
|
+
the time range, so it slows as the range grows. When such a search takes
|
|
317
|
+
longer than five seconds, the CLI prints a line on stderr that suggests a
|
|
318
|
+
program filter or a shorter range. Stdout is unchanged. A search that
|
|
319
|
+
times out answers 503 with the same advice.
|
|
308
320
|
- `--hash` takes a program id or a legacy signature hash. The CLI sends an
|
|
309
321
|
id as the search body's `program_id` and a hash as
|
|
310
322
|
`program_signature_hash`. `cmpnd.Client().list_traces(hash=...)` splits
|
|
@@ -312,8 +324,10 @@ read each one's spans and durations.
|
|
|
312
324
|
- `--deployment` takes a deployment ID (`d_...`).
|
|
313
325
|
- `cmpnd traces show <id> [--tree|--timeline|--notebook] [--arrivals]` prints
|
|
314
326
|
one trace. The default is raw JSON with each span's duration and its
|
|
315
|
-
`created_at`. `--tree` and `--timeline` render human views
|
|
316
|
-
|
|
327
|
+
`created_at`. `--tree` and `--timeline` render human views headed by the
|
|
328
|
+
program's name and its root module's class. A program with no name is headed
|
|
329
|
+
by its `p_` id instead, and a trace that names no program by its class
|
|
330
|
+
alone. `--notebook` prints an RLM trace as a Jupyter notebook. With `--tree` or
|
|
317
331
|
`--timeline`, `--arrivals` shows when the server stored each span and how
|
|
318
332
|
far that is from the start time the client reported, which exposes a skewed
|
|
319
333
|
client clock.
|
|
@@ -322,6 +336,14 @@ read each one's spans and durations.
|
|
|
322
336
|
- `cmpnd traces update <id> --metadata k=v …` merges metadata onto a trace,
|
|
323
337
|
and `cmpnd traces delete <id>` deletes a trace and its spans.
|
|
324
338
|
|
|
339
|
+
Each trace row and each `traces show` trace names what ran in three fields.
|
|
340
|
+
`program_name` is the program's name, empty when the program has none or the
|
|
341
|
+
trace names no program. `module_class` is the root DSPy module's class, and
|
|
342
|
+
`program_id` is the program's `p_` id. A server that predates `module_class`
|
|
343
|
+
sends the class in `program_name`. The shell's traces table and the `--tree`
|
|
344
|
+
and `--timeline` headers then show that value as the class and leave the
|
|
345
|
+
program unnamed.
|
|
346
|
+
|
|
325
347
|
`-q` matches text that serve indexes on each span when it stores the span.
|
|
326
348
|
Spans stored before the index existed match nothing until a backfill indexes
|
|
327
349
|
them. An empty result says so, because the list alone cannot tell "nothing
|
|
@@ -416,9 +438,14 @@ mode 0600. An active pointer names the org whose key commands use.
|
|
|
416
438
|
is stored, and `active` marks the org of the key in use. When `--api-key` or
|
|
417
439
|
`CMPND_API_KEY` is set, `active` marks that key's org.
|
|
418
440
|
- `cmpnd org switch <slug-or-id>` makes another org's stored key active, with
|
|
419
|
-
no network call. When no key is stored for
|
|
420
|
-
|
|
421
|
-
|
|
441
|
+
no network call. When no key is stored for an org where you are an approved
|
|
442
|
+
member, it asks whether to mint one with the current key's scopes, stores
|
|
443
|
+
it and switches to it. `--mint` mints without asking, and without a
|
|
444
|
+
terminal to ask in it mints only with `--mint`. The request names no
|
|
445
|
+
scopes, so serve grants the current key's non-admin scopes. When serve
|
|
446
|
+
refuses the mint, the error names the `cmpnd login --org` command to run.
|
|
447
|
+
`switch` refuses while `--api-key` or `CMPND_API_KEY` is set, because
|
|
448
|
+
either one overrides the stored keys.
|
|
422
449
|
- `cmpnd org leave <slug-or-id>` removes your membership in that org and drops
|
|
423
450
|
its stored key. If that key was active, the CLI switches to another stored
|
|
424
451
|
key, preferring your new default org's, or clears the pointer, and says
|
|
@@ -44,8 +44,9 @@ offers only `list`.
|
|
|
44
44
|
| `status` | `--status` | `ok`, `error`, `incomplete` or `in_progress` |
|
|
45
45
|
| `query` | `--query` / `-q` | Full-text search over indexed span text: prompts, completions, tool calls and errors |
|
|
46
46
|
| `model` | `--model` | Exact model name, such as `openai/gpt-6-luna` |
|
|
47
|
-
| `program` | `--program` |
|
|
48
|
-
| `
|
|
47
|
+
| `program` | `--program` | The program's name. A name no program answers to raises an error that names it. |
|
|
48
|
+
| `module` | `--module` | The root module's class, matched exactly. No index serves it, so without `program` or a program id in `hash` it reads every trace in the time range. Pair it with a program or a shorter `since`. |
|
|
49
|
+
| `hash` | `--hash` | A program id (`p_…`), or a legacy signature hash as a string |
|
|
49
50
|
| `deployment` | `--deployment` | Deployment ID (`d_…`) |
|
|
50
51
|
| `metadata` | `--metadata` | A `dict` matched by containment. Every pair must be on the trace. |
|
|
51
52
|
| `tags` | `--tag` | A `list`. A trace matches if it has any of the project tags. |
|
|
@@ -673,6 +673,13 @@ Its first payload carries the optimization id, plus the active eval id for a
|
|
|
673
673
|
metric call. A reflection call outside evaluation has no eval id. Manual
|
|
674
674
|
decorators and `start_span` create spans rather than another automatic trace.
|
|
675
675
|
|
|
676
|
+
A trace's creation fields name the root module's class in `module_class`, at
|
|
677
|
+
the top level and in `started_fact`, and again under `program_name`, the key a
|
|
678
|
+
serve from before `module_class` reads and digests a start from. A standalone
|
|
679
|
+
optimizer LM trace sends `GEPA` there. No trace body carries the program's own name: a declared name
|
|
680
|
+
travels as the program id it resolves to, as "A declared program name resolves
|
|
681
|
+
to an id in the exporter" describes.
|
|
682
|
+
|
|
676
683
|
The callback seals `started_fact` after it sets the root type, before the
|
|
677
684
|
module executes. It keeps that snapshot even when start emission is disabled.
|
|
678
685
|
The completion callback seals one independently identified `completion_fact`
|
|
@@ -80,7 +80,7 @@ class StartedFact:
|
|
|
80
80
|
"""Creation fields that later builder mutation cannot change."""
|
|
81
81
|
|
|
82
82
|
start_time: str | None
|
|
83
|
-
|
|
83
|
+
module_class: str | None
|
|
84
84
|
program_version: str | None
|
|
85
85
|
program_signature_hash: str
|
|
86
86
|
signature_name: str | None
|
|
@@ -104,7 +104,7 @@ class StartedFact:
|
|
|
104
104
|
def from_payload(cls, payload: dict[str, Any]) -> StartedFact:
|
|
105
105
|
return cls(
|
|
106
106
|
start_time=payload.get("start_time"),
|
|
107
|
-
|
|
107
|
+
module_class=payload.get("module_class"),
|
|
108
108
|
program_version=payload.get("program_version"),
|
|
109
109
|
program_signature_hash=str(payload["program_signature_hash"]),
|
|
110
110
|
signature_name=payload.get("signature_name"),
|
|
@@ -125,7 +125,12 @@ class StartedFact:
|
|
|
125
125
|
def creation_dict(self) -> dict[str, Any]:
|
|
126
126
|
payload = {
|
|
127
127
|
"start_time": self.start_time,
|
|
128
|
-
"
|
|
128
|
+
"module_class": self.module_class,
|
|
129
|
+
# A serve from before module_class reads the class only under
|
|
130
|
+
# program_name, and digests a start from that key. Sending both keeps
|
|
131
|
+
# a start it accepted matching its replay to a later serve. Dropped
|
|
132
|
+
# once no such serve runs.
|
|
133
|
+
"program_name": self.module_class,
|
|
129
134
|
"program_version": self.program_version,
|
|
130
135
|
"program_signature_hash": self.program_signature_hash,
|
|
131
136
|
"signature_name": self.signature_name,
|
|
@@ -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.63.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -10,7 +10,7 @@ GEPA optimization the stress suite runs
|
|
|
10
10
|
a weak Bedrock student + a Bedrock reflection LM over the vendored committee
|
|
11
11
|
subset. Capturing a *real* run (rather than fabricating payloads) keeps the seed
|
|
12
12
|
faithful — real prompts, token counts, span trees, and now the GEPA optimizer
|
|
13
|
-
traces (
|
|
13
|
+
traces (module_class "GEPA", `gepa.phase` reflection/metric).
|
|
14
14
|
|
|
15
15
|
Only the four kinds `seed_review_run.py` replays are kept — `optimization` (the
|
|
16
16
|
completed run header), `traces_batch`, `spans`, `stats`; the optimization *start*
|
|
@@ -146,12 +146,12 @@ def main() -> int:
|
|
|
146
146
|
kinds = [it["kind"] for it in captured]
|
|
147
147
|
n_opt = kinds.count("optimization")
|
|
148
148
|
trace_payloads = [t for it in captured if it["kind"] == "traces_batch" for t in it["payload"]]
|
|
149
|
-
|
|
149
|
+
classes = {t.get("module_class") for t in trace_payloads}
|
|
150
150
|
if n_opt != 1:
|
|
151
151
|
print(f"expected exactly 1 completed optimization, captured {n_opt}; not writing", file=sys.stderr)
|
|
152
152
|
return 1
|
|
153
|
-
if "GEPA" not in
|
|
154
|
-
print(f"no GEPA optimizer traces captured (
|
|
153
|
+
if "GEPA" not in classes:
|
|
154
|
+
print(f"no GEPA optimizer traces captured (module classes={classes}); not writing", file=sys.stderr)
|
|
155
155
|
return 1
|
|
156
156
|
|
|
157
157
|
_FIXTURE.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -160,7 +160,7 @@ def main() -> int:
|
|
|
160
160
|
from collections import Counter
|
|
161
161
|
|
|
162
162
|
print(f"wrote {_FIXTURE} — {len(captured)} items {dict(Counter(kinds))}")
|
|
163
|
-
print(f" trace
|
|
163
|
+
print(f" trace module classes: {dict(Counter(t.get('module_class') for t in trace_payloads))}")
|
|
164
164
|
return 0
|
|
165
165
|
|
|
166
166
|
|
|
Binary file
|
|
@@ -258,7 +258,7 @@ _TOOL_KIND_DEMO_FIXTURE = (
|
|
|
258
258
|
# A dspy.ReAct(ExtractCommittee) run (email_body->committee) that reasons, calls
|
|
259
259
|
# a `lookup_disclaimer` tool to pull the "Paid for by" line, then extracts the
|
|
260
260
|
# committee (CMP-634 golden-path). Flat-replay format like the two fixtures
|
|
261
|
-
# above;
|
|
261
|
+
# above; module_class "ReAct" so the sidebar/Type badge reads ReAct. Loaded
|
|
262
262
|
# lazily so a missing file degrades to skipping that seed.
|
|
263
263
|
_REACT_COMMITTEE_FIXTURE = (
|
|
264
264
|
pathlib.Path(__file__).resolve().parents[2] / "serve" / "scripts" / "seed_fixtures" / "react_committee.json"
|
|
@@ -390,7 +390,7 @@ def _notebook_trace(now: datetime.datetime) -> dict:
|
|
|
390
390
|
"end_time": at(total_ns),
|
|
391
391
|
"duration_ns": total_ns,
|
|
392
392
|
"status": "ok",
|
|
393
|
-
"
|
|
393
|
+
"module_class": "RLM", # DSPy module class → programs.module_class → sidebar badge
|
|
394
394
|
"program_version": "0.1.0",
|
|
395
395
|
"program_signature_hash": fx["program_signature_hash"],
|
|
396
396
|
"signature_name": fx["signature_name"],
|
|
@@ -540,7 +540,7 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
|
|
|
540
540
|
"trace_id": trace_id,
|
|
541
541
|
"start_time": at(0),
|
|
542
542
|
"status": "in_progress",
|
|
543
|
-
"
|
|
543
|
+
"module_class": "RLM",
|
|
544
544
|
"program_version": "0.1.0",
|
|
545
545
|
"program_signature_hash": fx["program_signature_hash"],
|
|
546
546
|
"signature_name": fx["signature_name"],
|
|
@@ -559,7 +559,7 @@ def _notebook_trace_in_progress(now: datetime.datetime) -> dict:
|
|
|
559
559
|
def _replay_flat_fixture(
|
|
560
560
|
fixture_path: pathlib.Path,
|
|
561
561
|
now: datetime.datetime,
|
|
562
|
-
|
|
562
|
+
module_class: str,
|
|
563
563
|
project_tags: list[str],
|
|
564
564
|
request_preview: str,
|
|
565
565
|
response_preview: str,
|
|
@@ -609,7 +609,7 @@ def _replay_flat_fixture(
|
|
|
609
609
|
"end_time": at(total_ns),
|
|
610
610
|
"duration_ns": total_ns,
|
|
611
611
|
"status": "ok",
|
|
612
|
-
"
|
|
612
|
+
"module_class": module_class, # DSPy module class → programs.module_class → sidebar badge
|
|
613
613
|
"program_version": "0.1.0",
|
|
614
614
|
"program_signature_hash": root["program_signature_hash"],
|
|
615
615
|
"signature_name": root["signature_name"],
|