cmpnd 0.85.0__tar.gz → 0.85.1__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.85.0 → cmpnd-0.85.1}/PKG-INFO +1 -1
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/sdk-instrumentation.md +12 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/exporter_http.py +68 -7
- {cmpnd-0.85.0 → cmpnd-0.85.1}/pyproject.toml +1 -1
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/seed_review_run.py +19 -3
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_project_attribution.py +0 -73
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_traces_read.py +3 -1
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_trace_search.py +7 -1
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_exporter.py +123 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/uv.lock +1 -1
- {cmpnd-0.85.0 → cmpnd-0.85.1}/.gitignore +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/AGENTS.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/CLAUDE.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/README.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_http.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/authored.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/client.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/configuration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/context.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/datasets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/decorators.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/deployment.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/execution.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/exporter.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/helpers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/imaging.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/models.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimization.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/packaging.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/program_naming.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/references.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/retry.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/cmpnd/traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/cli.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/client.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/custom-proposer.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/docs/reading-traces.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/examples/local_ollama.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/README.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/span_context.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/ingest/pyproject.toml +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/release-gate.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_credential_route.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_fake_clock.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/_traced_run.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/lm15_prompt_fixture.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/lm15_prompts/four_turn_predict.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/lm15_prompts/rlm_six_turns.json +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/__main__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/drain.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/ingest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/sweep.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_health.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/_result.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_client.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_client_floor.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_configuration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_context.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_credential_values.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_datasets.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_decorators.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deploy.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_eval.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_eval_program_id.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_eval_start.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_execute.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_imaging.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_import_weight.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_integration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_lm15_prompt_fixtures.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_lm15_prompt_references.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_models.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_no_signature_hash_on_the_wire.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimization_phase_callback.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_packaging.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_poll_until.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_program_identity.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_program_naming.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_programs_create.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_project_payload.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_retry.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_schemas.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_span_references.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_span_submission.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_span_submission_wire_contract.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_system_one.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_trace_render.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_traces_read.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_truncation.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.85.0 → cmpnd-0.85.1}/tests/test_xxh64.py +0 -0
|
@@ -331,6 +331,18 @@ Each `_send_*` method routes a failed delivery to one of two counters: a `429` (
|
|
|
331
331
|
|
|
332
332
|
Tolerating is never silent. A send that recovered logs a warning naming what it rode through (`rode through 3 transient responses over 12.4s (503=3) to POST /api/v1/traces`), and an exhausted budget raises `ExportRetriesExhausted` with the same count and window in its message. `BatchExporter.failure_summary()` reports the distribution of non-2xx statuses observed, how many transient answers were ridden through, and a sample error body — that's what the stress lane prints when it fails, so a red lane says *what* the wire answered rather than only how many errors there were.
|
|
333
333
|
|
|
334
|
+
Trace delivery failures log at ERROR on `cmpnd.exporter`. The message names the
|
|
335
|
+
operation and the number of records the exporter will not resend. HTTP failures
|
|
336
|
+
include the status, route and serve's string `error`, `error_code`, `upgrade` and
|
|
337
|
+
`field` values when present. The exporter bounds each value and escapes control
|
|
338
|
+
characters. It omits other body fields and non-JSON responses. Exhausted retries
|
|
339
|
+
include the last server message as well as the attempt count and elapsed time.
|
|
340
|
+
|
|
341
|
+
A non-retryable 422 discards the submission. Upgrade the SDK when serve asks for
|
|
342
|
+
it, then emit fresh telemetry. An upgrade cannot recover discarded records.
|
|
343
|
+
Background failures keep the application running and increment the existing
|
|
344
|
+
error counters. They do not raise an exception in the instrumented program.
|
|
345
|
+
|
|
334
346
|
### The retryable-error contract
|
|
335
347
|
|
|
336
348
|
The server tells a client when a failed write is worth re-sending, in three signals carrying one fact:
|
|
@@ -59,6 +59,31 @@ logger = logging.getLogger("cmpnd.exporter")
|
|
|
59
59
|
_GZIP_HEADERS = {"Content-Encoding": "gzip"}
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def _export_error_detail(exc: Exception) -> str:
|
|
63
|
+
"""Keep delivery errors useful in background logs without dumping response bodies."""
|
|
64
|
+
|
|
65
|
+
def text(value: str, limit: int = 300) -> str:
|
|
66
|
+
return json.dumps(value[:limit], ensure_ascii=True)[1:-1]
|
|
67
|
+
|
|
68
|
+
cause = exc.__cause__ if isinstance(exc, ExportRetriesExhausted) else exc
|
|
69
|
+
if not isinstance(cause, httpx.HTTPStatusError):
|
|
70
|
+
return text(str(exc), 600)
|
|
71
|
+
response = cause.response
|
|
72
|
+
detail = f"HTTP {response.status_code} for {cause.request.method} {cause.request.url.path}"
|
|
73
|
+
if cause is not exc:
|
|
74
|
+
detail = f"{text(str(exc), 600)}; {detail}"
|
|
75
|
+
try:
|
|
76
|
+
body = response.json()
|
|
77
|
+
except (ValueError, httpx.ResponseNotRead):
|
|
78
|
+
return detail
|
|
79
|
+
if isinstance(body, dict):
|
|
80
|
+
for key in ("error", "error_code", "upgrade", "field"):
|
|
81
|
+
value = body.get(key)
|
|
82
|
+
if isinstance(value, str) and value:
|
|
83
|
+
detail += f"; {key}={text(value)}"
|
|
84
|
+
return detail
|
|
85
|
+
|
|
86
|
+
|
|
62
87
|
def _send_json(client: httpx.Client, method: str, path: str, payload: Any) -> httpx.Response:
|
|
63
88
|
"""POST/PATCH/PUT ``payload`` as gzipped JSON.
|
|
64
89
|
|
|
@@ -1581,15 +1606,30 @@ class BatchExporter:
|
|
|
1581
1606
|
# drop call for opposite responses from the caller.
|
|
1582
1607
|
self._record_drop("trace", "server_shed", count=len(traces_to_send), log=False)
|
|
1583
1608
|
else:
|
|
1584
|
-
logger.error(
|
|
1609
|
+
logger.error(
|
|
1610
|
+
"Failed to export traces batch. Records will not be resent (count=%s, traces=%s, spans=%s): %s",
|
|
1611
|
+
len(traces_to_send) + total_spans,
|
|
1612
|
+
len(traces_to_send),
|
|
1613
|
+
total_spans,
|
|
1614
|
+
_export_error_detail(e),
|
|
1615
|
+
)
|
|
1585
1616
|
with self._stats_lock:
|
|
1586
1617
|
self._error_count += len(traces_to_send)
|
|
1587
1618
|
except Exception as e:
|
|
1588
|
-
logger.error(
|
|
1619
|
+
logger.error(
|
|
1620
|
+
"Failed to export traces batch. Records will not be resent (count=%s, traces=%s, spans=%s): %s",
|
|
1621
|
+
len(traces_to_send) + total_spans,
|
|
1622
|
+
len(traces_to_send),
|
|
1623
|
+
total_spans,
|
|
1624
|
+
_export_error_detail(e),
|
|
1625
|
+
)
|
|
1589
1626
|
with self._stats_lock:
|
|
1590
1627
|
self._error_count += len(traces_to_send)
|
|
1591
1628
|
|
|
1592
1629
|
def _send_encoded_trace(self, trace: EncodedTraceBody) -> None:
|
|
1630
|
+
# The body stays compressed end to end, so the exporter never learns how
|
|
1631
|
+
# many spans it embeds. A failure names the one trace and says the span
|
|
1632
|
+
# count is unknown, rather than understating the loss as one record.
|
|
1593
1633
|
client = self._require_client()
|
|
1594
1634
|
try:
|
|
1595
1635
|
self._send_retrying(
|
|
@@ -1601,11 +1641,19 @@ class BatchExporter:
|
|
|
1601
1641
|
if e.response.status_code == 429:
|
|
1602
1642
|
self._record_drop("trace", "server_shed", count=1)
|
|
1603
1643
|
else:
|
|
1604
|
-
logger.error(
|
|
1644
|
+
logger.error(
|
|
1645
|
+
"Failed to export encoded trace %s. Records will not be resent (traces=1, spans=unknown): %s",
|
|
1646
|
+
trace.trace_id,
|
|
1647
|
+
_export_error_detail(e),
|
|
1648
|
+
)
|
|
1605
1649
|
with self._stats_lock:
|
|
1606
1650
|
self._error_count += 1
|
|
1607
1651
|
except Exception as e:
|
|
1608
|
-
logger.error(
|
|
1652
|
+
logger.error(
|
|
1653
|
+
"Failed to export encoded trace %s. Records will not be resent (traces=1, spans=unknown): %s",
|
|
1654
|
+
trace.trace_id,
|
|
1655
|
+
_export_error_detail(e),
|
|
1656
|
+
)
|
|
1609
1657
|
with self._stats_lock:
|
|
1610
1658
|
self._error_count += 1
|
|
1611
1659
|
|
|
@@ -1619,7 +1667,11 @@ class BatchExporter:
|
|
|
1619
1667
|
self._exported_count += 1
|
|
1620
1668
|
logger.debug(f"Started trace {trace_id}")
|
|
1621
1669
|
except Exception as e:
|
|
1622
|
-
logger.error(
|
|
1670
|
+
logger.error(
|
|
1671
|
+
"Failed to start trace %s. Records will not be resent (count=1): %s",
|
|
1672
|
+
trace_id,
|
|
1673
|
+
_export_error_detail(e),
|
|
1674
|
+
)
|
|
1623
1675
|
with self._stats_lock:
|
|
1624
1676
|
self._error_count += 1
|
|
1625
1677
|
|
|
@@ -1651,7 +1703,12 @@ class BatchExporter:
|
|
|
1651
1703
|
self._exported_count += len(span_list)
|
|
1652
1704
|
logger.debug(f"Exported {len(span_list)} spans for trace {trace_id}")
|
|
1653
1705
|
except Exception as e:
|
|
1654
|
-
logger.error(
|
|
1706
|
+
logger.error(
|
|
1707
|
+
"Failed to export spans for trace %s. Records will not be resent (count=%s): %s",
|
|
1708
|
+
trace_id,
|
|
1709
|
+
len(span_list),
|
|
1710
|
+
_export_error_detail(e),
|
|
1711
|
+
)
|
|
1655
1712
|
with self._stats_lock:
|
|
1656
1713
|
self._error_count += len(span_list)
|
|
1657
1714
|
|
|
@@ -1667,7 +1724,11 @@ class BatchExporter:
|
|
|
1667
1724
|
self._exported_count += 1
|
|
1668
1725
|
logger.debug(f"Finalized trace {trace_id}")
|
|
1669
1726
|
except Exception as e:
|
|
1670
|
-
logger.error(
|
|
1727
|
+
logger.error(
|
|
1728
|
+
"Failed to finalize trace %s. Records will not be resent (count=1): %s",
|
|
1729
|
+
trace_id,
|
|
1730
|
+
_export_error_detail(e),
|
|
1731
|
+
)
|
|
1671
1732
|
with self._stats_lock:
|
|
1672
1733
|
self._error_count += 1
|
|
1673
1734
|
|
|
@@ -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.85.
|
|
8
|
+
version = "0.85.1"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -46,11 +46,15 @@ import logging
|
|
|
46
46
|
import os
|
|
47
47
|
import pathlib
|
|
48
48
|
import re
|
|
49
|
+
import sys
|
|
49
50
|
import urllib.error
|
|
50
51
|
import urllib.parse
|
|
51
52
|
import urllib.request
|
|
52
53
|
import uuid
|
|
53
54
|
|
|
55
|
+
# The captured fixtures already depend on this repository's serve scripts.
|
|
56
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "serve" / "scripts" / "seed_fixtures"))
|
|
57
|
+
|
|
54
58
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
|
55
59
|
log = logging.getLogger("seed_review_run")
|
|
56
60
|
|
|
@@ -673,6 +677,14 @@ def _ensure_program(endpoint: str, api_key: str, name: str) -> str | None:
|
|
|
673
677
|
|
|
674
678
|
|
|
675
679
|
def _post(endpoint: str, path: str, payload, api_key: str, method: str = "POST"):
|
|
680
|
+
if path.startswith("/api/v1/traces"):
|
|
681
|
+
from submission import prepare_trace_submission
|
|
682
|
+
|
|
683
|
+
if path == "/api/v1/traces/batch":
|
|
684
|
+
for trace in payload:
|
|
685
|
+
prepare_trace_submission(trace)
|
|
686
|
+
elif path.startswith("/api/v1/traces/") and path.endswith("/spans"):
|
|
687
|
+
prepare_trace_submission(payload, path.split("/")[-2])
|
|
676
688
|
body = json.dumps(payload).encode("utf-8")
|
|
677
689
|
req = urllib.request.Request(
|
|
678
690
|
endpoint + path,
|
|
@@ -680,9 +692,13 @@ def _post(endpoint: str, path: str, payload, api_key: str, method: str = "POST")
|
|
|
680
692
|
method=method,
|
|
681
693
|
headers={"Content-Type": "application/json", "X-API-Key": api_key},
|
|
682
694
|
)
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
695
|
+
try:
|
|
696
|
+
with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310 - fixed https endpoint from env
|
|
697
|
+
raw = resp.read()
|
|
698
|
+
return resp.status, (json.loads(raw) if raw else {})
|
|
699
|
+
except urllib.error.HTTPError as exc:
|
|
700
|
+
detail = exc.read().decode("utf-8", "replace")[:400]
|
|
701
|
+
raise RuntimeError(f"{method} {path} returned HTTP {exc.code}: {detail!r}") from exc
|
|
686
702
|
|
|
687
703
|
|
|
688
704
|
def _reserve_optimization(endpoint: str, api_key: str) -> str | None:
|
|
@@ -10,13 +10,11 @@ block so subsequent tests in the same pytest invocation are not affected.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
import datetime
|
|
14
13
|
import uuid
|
|
15
14
|
from collections.abc import Callable
|
|
16
15
|
from typing import Any
|
|
17
16
|
|
|
18
17
|
import dspy
|
|
19
|
-
import httpx
|
|
20
18
|
import pytest
|
|
21
19
|
|
|
22
20
|
import cmpnd
|
|
@@ -121,77 +119,6 @@ def test_project_stamped_on_trace_and_spans(server_client, trace_capture):
|
|
|
121
119
|
_restore_session_config()
|
|
122
120
|
|
|
123
121
|
|
|
124
|
-
def test_streaming_spans_inherit_full_project_tags(server_client):
|
|
125
|
-
"""Spans added via the streaming path (POST /traces/:id/spans) inherit all
|
|
126
|
-
project_tags from the parent trace, not just the first tag.
|
|
127
|
-
|
|
128
|
-
This bypasses the SDK exporter and calls the API directly to simulate
|
|
129
|
-
an old SDK that sends project but not project_tags on spans. Serve must
|
|
130
|
-
preserve both tags from the parent trace.
|
|
131
|
-
"""
|
|
132
|
-
trace_id = str(uuid.uuid4())
|
|
133
|
-
tag_a = f"e2e-stream-a-{uuid.uuid4().hex[:8]}"
|
|
134
|
-
tag_b = f"e2e-stream-b-{uuid.uuid4().hex[:8]}"
|
|
135
|
-
now = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
136
|
-
headers = {
|
|
137
|
-
"X-API-Key": CMPND_E2E_API_KEY or "",
|
|
138
|
-
"Content-Type": "application/json",
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
# 1. Start trace with two tags
|
|
142
|
-
start_resp = httpx.post(
|
|
143
|
-
f"{E2E_ENDPOINT}/api/v1/traces/{trace_id}/start",
|
|
144
|
-
headers=headers,
|
|
145
|
-
json={
|
|
146
|
-
"module_class": "StreamTest",
|
|
147
|
-
"start_time": now,
|
|
148
|
-
"project": tag_a,
|
|
149
|
-
"project_tags": [tag_a, tag_b],
|
|
150
|
-
},
|
|
151
|
-
timeout=30.0,
|
|
152
|
-
)
|
|
153
|
-
assert start_resp.status_code == 202, f"start_trace failed: {start_resp.status_code} {start_resp.text}"
|
|
154
|
-
|
|
155
|
-
# 2. Add a span WITHOUT project_tags — simulates old SDK behavior
|
|
156
|
-
span_id = str(uuid.uuid4())
|
|
157
|
-
spans_resp = httpx.post(
|
|
158
|
-
f"{E2E_ENDPOINT}/api/v1/traces/{trace_id}/spans",
|
|
159
|
-
headers=headers,
|
|
160
|
-
json={
|
|
161
|
-
"spans": [
|
|
162
|
-
{
|
|
163
|
-
"span_id": span_id,
|
|
164
|
-
"trace_id": trace_id,
|
|
165
|
-
"name": "predict",
|
|
166
|
-
"span_type": "predict",
|
|
167
|
-
"start_time": now,
|
|
168
|
-
"end_time": now,
|
|
169
|
-
# project and project_tags intentionally omitted
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
},
|
|
173
|
-
timeout=30.0,
|
|
174
|
-
)
|
|
175
|
-
assert spans_resp.status_code == 200, f"add_spans failed: {spans_resp.status_code} {spans_resp.text}"
|
|
176
|
-
|
|
177
|
-
# 3. Poll for ingestion, then fetch the trace. The trace row and its spans
|
|
178
|
-
# become visible independently and asynchronously. Read the endpoint
|
|
179
|
-
# directly to keep the wait bounded by one retry loop (CMP-843).
|
|
180
|
-
def _spans():
|
|
181
|
-
resp = server_client._client.get(f"/api/v1/traces/{trace_id}")
|
|
182
|
-
return resp.json().get("spans", []) if resp.status_code == 200 else []
|
|
183
|
-
|
|
184
|
-
spans = poll_until(_spans, timeout=60.0, interval=3.0)
|
|
185
|
-
|
|
186
|
-
assert spans, "trace has no spans after streaming add"
|
|
187
|
-
for span in spans:
|
|
188
|
-
span_tags = span.get("project_tags", [])
|
|
189
|
-
assert tag_a in span_tags and tag_b in span_tags, (
|
|
190
|
-
f"span {span.get('span_id')} should inherit both tags from trace, "
|
|
191
|
-
f"got {span_tags!r} — expected {[tag_a, tag_b]!r}"
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
122
|
def test_project_null_when_unset(server_client, trace_capture):
|
|
196
123
|
"""Without configure(project=...), the trace and spans land with NULL project."""
|
|
197
124
|
try:
|
|
@@ -29,7 +29,9 @@ class TestTracesReadE2E:
|
|
|
29
29
|
trace_id = trace_capture.last_trace_id
|
|
30
30
|
assert trace_id is not None, "no trace captured"
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# Trace headers and spans publish independently. Wait until both are visible
|
|
33
|
+
# before checking the SDK's detail read.
|
|
34
|
+
server_client.get_trace(trace_id)
|
|
33
35
|
detail = cmpnd.traces.show(trace_id, api_key=CMPND_E2E_API_KEY, endpoint=E2E_ENDPOINT)
|
|
34
36
|
assert detail["trace"]["trace_id"] == trace_id
|
|
35
37
|
assert isinstance(detail["spans"], list)
|
|
@@ -78,8 +78,11 @@ def test_cross_trace_q_search_finds_the_span_token():
|
|
|
78
78
|
|
|
79
79
|
# One trace, one span whose name carries the unique token. The write path
|
|
80
80
|
# indexes that name in the span transaction, and the read below waits for
|
|
81
|
-
# the projected search collection to publish it.
|
|
81
|
+
# the projected search collection to publish it. Both records are
|
|
82
|
+
# self-contained submissions: each declares its schema version, and the span
|
|
83
|
+
# carries its own tags rather than inheriting the trace's.
|
|
82
84
|
trace = {
|
|
85
|
+
"schema_version": 1,
|
|
83
86
|
"trace_id": trace_id,
|
|
84
87
|
"start_time": _iso(),
|
|
85
88
|
"end_time": _iso(),
|
|
@@ -89,8 +92,11 @@ def test_cross_trace_q_search_finds_the_span_token():
|
|
|
89
92
|
"span_count": 1,
|
|
90
93
|
"spans": [
|
|
91
94
|
{
|
|
95
|
+
"schema_version": 1,
|
|
92
96
|
"span_id": span_id,
|
|
93
97
|
"trace_id": trace_id,
|
|
98
|
+
"project_tags": [tag],
|
|
99
|
+
"tags": {},
|
|
94
100
|
"name": token,
|
|
95
101
|
"span_type": "tool",
|
|
96
102
|
"start_time": _iso(),
|
|
@@ -14,6 +14,7 @@ from uuid import uuid4
|
|
|
14
14
|
|
|
15
15
|
import httpx
|
|
16
16
|
import pytest
|
|
17
|
+
from cmpnd_ingest.bodies import EncodedTraceBody
|
|
17
18
|
|
|
18
19
|
from cmpnd import retry
|
|
19
20
|
from cmpnd.configuration import Config
|
|
@@ -1179,3 +1180,125 @@ def test_failure_summary_is_quiet_when_nothing_failed():
|
|
|
1179
1180
|
exporter._flush_batch([ExportItem(item_type="trace", data=_make_trace(), timestamp=0.0)])
|
|
1180
1181
|
|
|
1181
1182
|
assert "no non-2xx" in exporter.failure_summary()
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
@pytest.mark.parametrize(
|
|
1186
|
+
"kind,count",
|
|
1187
|
+
[("trace", 1), ("trace", 2), ("trace_start", 1), ("span", 2), ("trace_finalize", 1), ("encoded_trace", 1)],
|
|
1188
|
+
)
|
|
1189
|
+
def test_trace_rejection_logs_server_guidance_and_delivery_loss(kind, count, caplog):
|
|
1190
|
+
body = {
|
|
1191
|
+
"error": "schema_version must be 1",
|
|
1192
|
+
"error_code": "trace_submission_contract",
|
|
1193
|
+
"field": "schema_version",
|
|
1194
|
+
"upgrade": "Upgrade with uv add cmpnd --upgrade",
|
|
1195
|
+
"retryable": False,
|
|
1196
|
+
"unrelated": "private response detail",
|
|
1197
|
+
}
|
|
1198
|
+
capture = ContractCapture([(422, body, {})])
|
|
1199
|
+
exporter = _make_seq_exporter(capture)
|
|
1200
|
+
tid = uuid4()
|
|
1201
|
+
batch = []
|
|
1202
|
+
for _ in range(count):
|
|
1203
|
+
data = (
|
|
1204
|
+
_make_span(trace_id=tid)
|
|
1205
|
+
if kind == "span"
|
|
1206
|
+
else EncodedTraceBody(str(tid), "gzip", gzip.compress(b"{}"))
|
|
1207
|
+
if kind == "encoded_trace"
|
|
1208
|
+
else _make_trace()
|
|
1209
|
+
)
|
|
1210
|
+
batch.append(ExportItem(item_type=kind, data=data, timestamp=0.0))
|
|
1211
|
+
try:
|
|
1212
|
+
with caplog.at_level(logging.ERROR, logger="cmpnd.exporter"):
|
|
1213
|
+
exporter._flush_batch(batch)
|
|
1214
|
+
assert len(capture.requests) == 1
|
|
1215
|
+
assert exporter._error_count == count
|
|
1216
|
+
assert exporter._exported_count == 0
|
|
1217
|
+
assert "422" in caplog.text
|
|
1218
|
+
assert "schema_version must be 1" in caplog.text
|
|
1219
|
+
assert "trace_submission_contract" in caplog.text
|
|
1220
|
+
assert "Upgrade with uv add cmpnd --upgrade" in caplog.text
|
|
1221
|
+
if kind == "encoded_trace":
|
|
1222
|
+
# The exporter never inflates an encoded trace, so it cannot count the
|
|
1223
|
+
# spans the trace embeds and must not report the trace as one record.
|
|
1224
|
+
assert "traces=1, spans=unknown" in caplog.text
|
|
1225
|
+
assert "count=" not in caplog.text
|
|
1226
|
+
else:
|
|
1227
|
+
assert f"count={count}" in caplog.text
|
|
1228
|
+
assert "Records will not be resent" in caplog.text
|
|
1229
|
+
assert "private response detail" not in caplog.text
|
|
1230
|
+
finally:
|
|
1231
|
+
exporter.shutdown()
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
@pytest.mark.parametrize(
|
|
1235
|
+
"body", [[], {"error": {"secret": "private response detail"}}, {"error": "bad\nforged log\r\x1b[31m" + "x" * 5000}]
|
|
1236
|
+
)
|
|
1237
|
+
def test_trace_rejection_handles_unexpected_or_long_error_bodies(body, caplog):
|
|
1238
|
+
requests = []
|
|
1239
|
+
|
|
1240
|
+
def handle(request):
|
|
1241
|
+
requests.append(request)
|
|
1242
|
+
return httpx.Response(400, json=body)
|
|
1243
|
+
|
|
1244
|
+
exporter = _make_exporter(RequestCapture())
|
|
1245
|
+
exporter._client = httpx.Client(base_url=_make_config().endpoint, transport=httpx.MockTransport(handle))
|
|
1246
|
+
try:
|
|
1247
|
+
with caplog.at_level(logging.ERROR, logger="cmpnd.exporter"):
|
|
1248
|
+
exporter._flush_batch([ExportItem(item_type="trace", data=_make_trace(), timestamp=0.0)])
|
|
1249
|
+
messages = [r.getMessage() for r in caplog.records if r.levelno == logging.ERROR]
|
|
1250
|
+
assert len(requests) == 1
|
|
1251
|
+
assert len(messages) == 1
|
|
1252
|
+
assert "400" in messages[0]
|
|
1253
|
+
assert "count=1" in messages[0]
|
|
1254
|
+
assert len(messages[0]) < 2000
|
|
1255
|
+
assert "private response detail" not in messages[0]
|
|
1256
|
+
assert "\nforged log" not in messages[0]
|
|
1257
|
+
assert "\x1b" not in messages[0]
|
|
1258
|
+
finally:
|
|
1259
|
+
exporter.shutdown()
|
|
1260
|
+
|
|
1261
|
+
|
|
1262
|
+
def test_trace_rejection_handles_non_json_response(caplog):
|
|
1263
|
+
exporter = _make_exporter(RequestCapture())
|
|
1264
|
+
exporter._client = httpx.Client(
|
|
1265
|
+
base_url=_make_config().endpoint,
|
|
1266
|
+
transport=httpx.MockTransport(lambda _: httpx.Response(413, text="<html>private response detail</html>")),
|
|
1267
|
+
)
|
|
1268
|
+
try:
|
|
1269
|
+
with caplog.at_level(logging.ERROR, logger="cmpnd.exporter"):
|
|
1270
|
+
exporter._flush_batch([ExportItem(item_type="trace", data=_make_trace(), timestamp=0.0)])
|
|
1271
|
+
assert "413" in caplog.text
|
|
1272
|
+
assert "count=1" in caplog.text
|
|
1273
|
+
assert "private response detail" not in caplog.text
|
|
1274
|
+
finally:
|
|
1275
|
+
exporter.shutdown()
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
def test_retry_exhaustion_logs_last_server_message(caplog):
|
|
1279
|
+
capture = ContractCapture([(503, {"error": "database temporarily unavailable", "retryable": True}, {})])
|
|
1280
|
+
exporter = _make_seq_exporter(capture)
|
|
1281
|
+
try:
|
|
1282
|
+
with caplog.at_level(logging.ERROR, logger="cmpnd.exporter"):
|
|
1283
|
+
exporter._flush_batch([ExportItem(item_type="trace", data=_make_trace(), timestamp=0.0)])
|
|
1284
|
+
assert len(capture.requests) == retry.MAX_ATTEMPTS
|
|
1285
|
+
assert "database temporarily unavailable" in caplog.text
|
|
1286
|
+
assert "transient responses over" in caplog.text
|
|
1287
|
+
assert "count=1" in caplog.text
|
|
1288
|
+
finally:
|
|
1289
|
+
exporter.shutdown()
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
def test_rejected_full_trace_logs_embedded_span_loss(caplog):
|
|
1293
|
+
capture = ContractCapture([(422, {"error": "invalid span", "retryable": False}, {})])
|
|
1294
|
+
exporter = _make_seq_exporter(capture)
|
|
1295
|
+
payload = _make_trace().to_api_dict()
|
|
1296
|
+
payload["spans"] = [_make_span().to_api_dict(), _make_span().to_api_dict()]
|
|
1297
|
+
try:
|
|
1298
|
+
with caplog.at_level(logging.ERROR, logger="cmpnd.exporter"):
|
|
1299
|
+
exporter._send_traces([payload])
|
|
1300
|
+
assert "count=3, traces=1, spans=2" in caplog.text
|
|
1301
|
+
assert exporter._exported_count == 0
|
|
1302
|
+
assert len(capture.requests) == 1
|
|
1303
|
+
finally:
|
|
1304
|
+
exporter.shutdown()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|