cmpnd 0.7.0__tar.gz → 0.7.2__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.7.0 → cmpnd-0.7.2}/PKG-INFO +2 -1
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/_gepa_patch.py +9 -2
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/exporter_http.py +74 -10
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimizers/gepa.py +6 -2
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimizers/gepa_callback.py +1 -1
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimizers/tracker.py +11 -4
- {cmpnd-0.7.0 → cmpnd-0.7.2}/docs/sdk-instrumentation.md +4 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/pyproject.toml +38 -1
- {cmpnd-0.7.0 → cmpnd-0.7.2}/scripts/seed_review_run.py +290 -7
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/committee_task.py +19 -4
- cmpnd-0.7.2/tests/stress/_provider.py +276 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_deploy_programs.py +38 -8
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_multi_client_stress.py +15 -3
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_optimize_loop.py +8 -1
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_optimize_loop_wasm.py +71 -24
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_optimize_stress.py +6 -1
- cmpnd-0.7.2/tests/stress/test_provider_roles.py +221 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_real_provider_twins.py +19 -2
- cmpnd-0.7.2/tests/stress/test_rlm_wasm.py +411 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_clustering/conftest.py +2 -9
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_clustering/test_edge_cases.py +2 -7
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_clustering/test_hash_consistency.py +4 -11
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_clustering/test_type_conversion.py +3 -8
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_deploy.py +50 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_exporter.py +82 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_optimize_terminal_failure.py +78 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_seed_review_run.py +10 -4
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_tracked_gepa.py +5 -1
- {cmpnd-0.7.0 → cmpnd-0.7.2}/uv.lock +58 -94
- cmpnd-0.7.0/tests/stress/_provider.py +0 -91
- {cmpnd-0.7.0 → cmpnd-0.7.2}/.gitignore +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/CLAUDE.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/README.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/callback.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/configuration.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/context.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/datasets.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/decorators.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/deployment.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/execution.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/exporter.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/helpers.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/identity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/models.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimization.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/cmpnd/packaging.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/examples/local_ollama.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/conftest.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/conftest.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_health.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_callback.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_configuration.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_context.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_datasets.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_decorators.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_eval.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_eval_start.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_execute.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_identity.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_import_weight.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_integration.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_models.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_optimize.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_packaging.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_project_payload.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_schemas.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_stats_export.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_trace_render.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_truncation.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/tests/test_xxh64.py +0 -0
- {cmpnd-0.7.0 → cmpnd-0.7.2}/todo.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmpnd
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: DSPy observability and deployment SDK for cmpnd
|
|
5
5
|
Project-URL: Homepage, https://cmpnd.ai
|
|
6
6
|
Author-email: cmpnd <hello@cmpnd.ai>
|
|
@@ -27,6 +27,7 @@ Requires-Dist: basedpyright>=1.39; extra == 'dev'
|
|
|
27
27
|
Requires-Dist: boto3>=1.34; extra == 'dev'
|
|
28
28
|
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
29
29
|
Requires-Dist: jsonschema>=4.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: numpy>=1.26; extra == 'dev'
|
|
30
31
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
31
32
|
Requires-Dist: pytest-rerunfailures>=14.0; extra == 'dev'
|
|
32
33
|
Requires-Dist: pytest-xdist>=3.0; extra == 'dev'
|
|
@@ -64,7 +64,7 @@ def patch_gepa() -> None:
|
|
|
64
64
|
# This ensures each optimization gets its own run_id, even when
|
|
65
65
|
# the same GEPA instance is reused (e.g., optimizing an already-optimized program).
|
|
66
66
|
trk = tracker.OptimizationTracker(
|
|
67
|
-
optimizer_type="
|
|
67
|
+
optimizer_type="gepa",
|
|
68
68
|
program_name="",
|
|
69
69
|
)
|
|
70
70
|
callback = gepa_callback.CmpndGEPACallback(trk, defer_finalize=True)
|
|
@@ -157,7 +157,14 @@ def patch_gepa() -> None:
|
|
|
157
157
|
|
|
158
158
|
return result
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
# `BaseException`, because the invariant does not get to have an exception: a
|
|
161
|
+
# run that ends by ANY route must transition the row start() sent, or the
|
|
162
|
+
# program page shows a perpetual in-progress optimization. A backend that
|
|
163
|
+
# rejected a model request raises a failure that deliberately does NOT derive
|
|
164
|
+
# from `Exception` — that is the only way past gepa's and dspy's own blanket
|
|
165
|
+
# `except Exception` clauses — so narrowing this one reopens exactly the bug
|
|
166
|
+
# this block exists for. Re-raised either way, so widening swallows nothing.
|
|
167
|
+
except BaseException as e:
|
|
161
168
|
if callback is not None: # pyright: ignore[reportUnnecessaryComparison]
|
|
162
169
|
# Flush a terminal 'failed', not just in-memory state — else the
|
|
163
170
|
# running row start() sent lingers forever.
|
|
@@ -55,6 +55,28 @@ def _send_json(client: httpx.Client, method: str, path: str, payload: Any) -> ht
|
|
|
55
55
|
return client.request(method, path, content=body, headers=_GZIP_HEADERS)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
# Gateway/overload statuses a data POST should retry rather than count as a hard
|
|
59
|
+
# export error: the backend (or the proxy in front of it) is transiently busy,
|
|
60
|
+
# not rejecting the payload. A retry is safe because serve dedups on ingest —
|
|
61
|
+
# traces by (org_id, trace_id, start_time) and spans by the spans_dedup index —
|
|
62
|
+
# so a re-POST of a request that actually landed can't double-insert.
|
|
63
|
+
_RETRYABLE_STATUS = frozenset({502, 503, 504})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _is_retryable_export_error(exc: BaseException) -> bool:
|
|
67
|
+
"""Whether a failed data send is a transient delivery blip worth retrying.
|
|
68
|
+
|
|
69
|
+
True for gateway/overload statuses (502/503/504) and httpx transport/timeout
|
|
70
|
+
errors (connect/read/pool timeouts, connection refused/reset). False for
|
|
71
|
+
everything else — 4xx (client error, a retry won't help), 429 (backpressure,
|
|
72
|
+
the caller records it as *dropped* not errored), and 500 (the platform's to
|
|
73
|
+
answer for, mirroring the is_external_outage boundary). Non-httpx exceptions
|
|
74
|
+
are not retried (a bug in our own build path shouldn't loop)."""
|
|
75
|
+
if isinstance(exc, httpx.HTTPStatusError):
|
|
76
|
+
return exc.response.status_code in _RETRYABLE_STATUS
|
|
77
|
+
return isinstance(exc, (httpx.TimeoutException, httpx.TransportError))
|
|
78
|
+
|
|
79
|
+
|
|
58
80
|
@dataclass
|
|
59
81
|
class ExportItem:
|
|
60
82
|
"""Item in the export queue."""
|
|
@@ -115,6 +137,14 @@ class BatchExporter:
|
|
|
115
137
|
self._start_post_attempts = 3
|
|
116
138
|
self._start_post_backoff = 0.5
|
|
117
139
|
|
|
140
|
+
# Bounded retry for the best-effort data POSTs (traces/spans/stats/…):
|
|
141
|
+
# a transient gateway/overload or transport blip retries with backoff
|
|
142
|
+
# instead of immediately counting a hard export error. Idempotent on the
|
|
143
|
+
# serve side (see _is_retryable_export_error). Instance attrs so tests
|
|
144
|
+
# can zero the backoff. Distinct from the start-post budget above.
|
|
145
|
+
self._data_post_attempts = 3
|
|
146
|
+
self._data_post_backoff = 0.5
|
|
147
|
+
|
|
118
148
|
# Guard against registering multiple exit handlers on repeated start()
|
|
119
149
|
self._atexit_registered = False
|
|
120
150
|
|
|
@@ -440,6 +470,34 @@ class BatchExporter:
|
|
|
440
470
|
raise RuntimeError("BackgroundExporter has no HTTP client (not started or already shut down)")
|
|
441
471
|
return self._client
|
|
442
472
|
|
|
473
|
+
def _send_json_retrying(self, client: httpx.Client, method: str, path: str, payload: Any) -> httpx.Response:
|
|
474
|
+
"""`_send_json` + `raise_for_status`, retrying transient delivery blips.
|
|
475
|
+
|
|
476
|
+
Retries `_data_post_attempts` times with exponential backoff on a
|
|
477
|
+
retryable failure (502/503/504 or a transport/timeout error — see
|
|
478
|
+
`_is_retryable_export_error`), then re-raises. A non-retryable failure
|
|
479
|
+
(4xx, 429, 500, or a 2xx that somehow raised) propagates on the first
|
|
480
|
+
attempt, so the caller's existing except blocks still record it (429 →
|
|
481
|
+
dropped, everything else → error) exactly as before — the only change
|
|
482
|
+
is that a transient blip now gets a few tries before it counts."""
|
|
483
|
+
last_exc: Exception | None = None
|
|
484
|
+
for attempt in range(self._data_post_attempts):
|
|
485
|
+
try:
|
|
486
|
+
response = _send_json(client, method, path, payload)
|
|
487
|
+
response.raise_for_status()
|
|
488
|
+
return response
|
|
489
|
+
except Exception as e:
|
|
490
|
+
last_exc = e
|
|
491
|
+
if _is_retryable_export_error(e) and attempt + 1 < self._data_post_attempts:
|
|
492
|
+
logger.debug(
|
|
493
|
+
"cmpnd: retrying %s %s after transient error (attempt %d): %s", method, path, attempt + 1, e
|
|
494
|
+
)
|
|
495
|
+
time.sleep(self._data_post_backoff * (2**attempt))
|
|
496
|
+
continue
|
|
497
|
+
raise
|
|
498
|
+
assert last_exc is not None # unreachable: the loop either returns or raises
|
|
499
|
+
raise last_exc
|
|
500
|
+
|
|
443
501
|
def _flush_batch(self, batch: list[ExportItem]) -> None:
|
|
444
502
|
"""Send a batch of items to the backend with parallel dispatch."""
|
|
445
503
|
if not batch or not self._client:
|
|
@@ -600,9 +658,9 @@ class BatchExporter:
|
|
|
600
658
|
total_spans = sum(len(t.get("spans", [])) for t in traces_to_send)
|
|
601
659
|
try:
|
|
602
660
|
if len(traces_to_send) == 1:
|
|
603
|
-
response =
|
|
661
|
+
response = self._send_json_retrying(client, "POST", "/api/v1/traces", traces_to_send[0])
|
|
604
662
|
else:
|
|
605
|
-
response =
|
|
663
|
+
response = self._send_json_retrying(client, "POST", "/api/v1/traces/batch", traces_to_send)
|
|
606
664
|
response.raise_for_status()
|
|
607
665
|
with self._stats_lock:
|
|
608
666
|
self._exported_count += len(traces_to_send) + total_spans
|
|
@@ -627,7 +685,7 @@ class BatchExporter:
|
|
|
627
685
|
client = self._require_client()
|
|
628
686
|
for trace_id, start_dict in trace_starts:
|
|
629
687
|
try:
|
|
630
|
-
response =
|
|
688
|
+
response = self._send_json_retrying(client, "POST", f"/api/v1/traces/{trace_id}/start", start_dict)
|
|
631
689
|
response.raise_for_status()
|
|
632
690
|
with self._stats_lock:
|
|
633
691
|
self._exported_count += 1
|
|
@@ -641,7 +699,9 @@ class BatchExporter:
|
|
|
641
699
|
client = self._require_client()
|
|
642
700
|
for trace_id, span_list in spans_by_trace.items():
|
|
643
701
|
try:
|
|
644
|
-
response =
|
|
702
|
+
response = self._send_json_retrying(
|
|
703
|
+
client, "POST", f"/api/v1/traces/{trace_id}/spans", {"spans": span_list}
|
|
704
|
+
)
|
|
645
705
|
response.raise_for_status()
|
|
646
706
|
with self._stats_lock:
|
|
647
707
|
self._exported_count += len(span_list)
|
|
@@ -655,7 +715,9 @@ class BatchExporter:
|
|
|
655
715
|
client = self._require_client()
|
|
656
716
|
for trace_id, finalize_dict in trace_finalizes:
|
|
657
717
|
try:
|
|
658
|
-
response =
|
|
718
|
+
response = self._send_json_retrying(
|
|
719
|
+
client, "POST", f"/api/v1/traces/{trace_id}/finalize", finalize_dict
|
|
720
|
+
)
|
|
659
721
|
response.raise_for_status()
|
|
660
722
|
with self._stats_lock:
|
|
661
723
|
self._exported_count += 1
|
|
@@ -668,7 +730,7 @@ class BatchExporter:
|
|
|
668
730
|
def _send_stats(self, stats_to_send: list[dict[str, Any]]) -> None:
|
|
669
731
|
client = self._require_client()
|
|
670
732
|
try:
|
|
671
|
-
response =
|
|
733
|
+
response = self._send_json_retrying(client, "POST", "/api/v1/traces/stats", stats_to_send)
|
|
672
734
|
response.raise_for_status()
|
|
673
735
|
with self._stats_lock:
|
|
674
736
|
self._exported_count += len(stats_to_send)
|
|
@@ -686,7 +748,7 @@ class BatchExporter:
|
|
|
686
748
|
eval_run_id = str(eval_run_dict.get("eval_run_id"))
|
|
687
749
|
num_examples = len(eval_run_dict.get("examples", []))
|
|
688
750
|
try:
|
|
689
|
-
response =
|
|
751
|
+
response = self._send_json_retrying(client, "PATCH", f"/api/v1/evals/{eval_run_id}", eval_run_dict)
|
|
690
752
|
response.raise_for_status()
|
|
691
753
|
with self._stats_lock:
|
|
692
754
|
self._exported_count += 1 + num_examples
|
|
@@ -703,7 +765,7 @@ class BatchExporter:
|
|
|
703
765
|
num_iterations = len(opt_run_dict.get("iterations", []))
|
|
704
766
|
num_candidates = len(opt_run_dict.get("candidates", []))
|
|
705
767
|
try:
|
|
706
|
-
response =
|
|
768
|
+
response = self._send_json_retrying(client, "POST", "/api/v1/optimizations", opt_run_dict)
|
|
707
769
|
response.raise_for_status()
|
|
708
770
|
with self._stats_lock:
|
|
709
771
|
self._exported_count += 1
|
|
@@ -722,7 +784,9 @@ class BatchExporter:
|
|
|
722
784
|
client = self._require_client()
|
|
723
785
|
for opt_run_id, progress_dict, trk in optimization_progress_to_send:
|
|
724
786
|
try:
|
|
725
|
-
response =
|
|
787
|
+
response = self._send_json_retrying(
|
|
788
|
+
client, "PATCH", f"/api/v1/optimizations/{opt_run_id}/progress", progress_dict
|
|
789
|
+
)
|
|
726
790
|
response.raise_for_status()
|
|
727
791
|
# Delivery-acknowledged high-water: only advance past these
|
|
728
792
|
# iteration_points now that the PATCH succeeded (2xx, no
|
|
@@ -745,7 +809,7 @@ class BatchExporter:
|
|
|
745
809
|
num_iterations = len(opt_run_dict.get("iterations", []))
|
|
746
810
|
num_candidates = len(opt_run_dict.get("candidates", []))
|
|
747
811
|
try:
|
|
748
|
-
response =
|
|
812
|
+
response = self._send_json_retrying(client, "PUT", f"/api/v1/optimizations/{opt_run_id}", opt_run_dict)
|
|
749
813
|
response.raise_for_status()
|
|
750
814
|
with self._stats_lock:
|
|
751
815
|
self._exported_count += 1
|
|
@@ -152,7 +152,7 @@ class TrackedGEPA:
|
|
|
152
152
|
|
|
153
153
|
# Initialize optimization tracking
|
|
154
154
|
self._tracker = tracker.OptimizationTracker(
|
|
155
|
-
optimizer_type="
|
|
155
|
+
optimizer_type="gepa",
|
|
156
156
|
program_name=student.__class__.__name__,
|
|
157
157
|
config=config,
|
|
158
158
|
model_name=model_name,
|
|
@@ -218,7 +218,11 @@ class TrackedGEPA:
|
|
|
218
218
|
|
|
219
219
|
return result
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
# `BaseException` for the reason `_gepa_patch._patched_compile` documents: the
|
|
222
|
+
# row must transition however the run ends, and a rejected model request
|
|
223
|
+
# reaches here as something that is not an `Exception`, by design. Re-raised
|
|
224
|
+
# either way.
|
|
225
|
+
except BaseException as e:
|
|
222
226
|
if self._tracker:
|
|
223
227
|
# Flush a terminal 'failed', not just in-memory state — else the
|
|
224
228
|
# running row start() sent lingers forever.
|
|
@@ -5,7 +5,7 @@ monkey-patching or wrapper classes. Replaces the monkey-patched
|
|
|
5
5
|
InstrumentedDspyAdapter approach used by TrackedGEPA.
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
|
-
tracker = OptimizationTracker(optimizer_type="
|
|
8
|
+
tracker = OptimizationTracker(optimizer_type="gepa", ...)
|
|
9
9
|
callback = CmpndGEPACallback(tracker)
|
|
10
10
|
gepa = dspy.GEPA(gepa_kwargs={"callbacks": [callback]})
|
|
11
11
|
"""
|
|
@@ -199,7 +199,7 @@ class OptimizationTracker:
|
|
|
199
199
|
|
|
200
200
|
Usage:
|
|
201
201
|
tracker = OptimizationTracker(
|
|
202
|
-
optimizer_type="
|
|
202
|
+
optimizer_type="gepa",
|
|
203
203
|
program_name="MyProgram",
|
|
204
204
|
config={"auto": "light", "seed": 42}
|
|
205
205
|
)
|
|
@@ -634,14 +634,21 @@ class OptimizationTracker:
|
|
|
634
634
|
except Exception as e:
|
|
635
635
|
logger.warning(f"Failed to capture compiled program state: {e}")
|
|
636
636
|
|
|
637
|
-
def set_error(self, exception:
|
|
638
|
-
"""Mark the optimization run as failed (in-memory only).
|
|
637
|
+
def set_error(self, exception: BaseException) -> None:
|
|
638
|
+
"""Mark the optimization run as failed (in-memory only).
|
|
639
|
+
|
|
640
|
+
``BaseException`` and not ``Exception``: a backend that rejected a model request
|
|
641
|
+
reports it as a failure that deliberately does not derive from ``Exception``,
|
|
642
|
+
since that is the only thing gepa's and dspy's blanket ``except Exception``
|
|
643
|
+
clauses do not swallow. A run that ended is a run that has to be recorded as
|
|
644
|
+
ended, whichever base class carried the reason.
|
|
645
|
+
"""
|
|
639
646
|
self.status = "failed"
|
|
640
647
|
self.error_message = str(exception)
|
|
641
648
|
self.end_time = datetime.now(timezone.utc)
|
|
642
649
|
logger.error(f"Optimization failed: {exception}")
|
|
643
650
|
|
|
644
|
-
def finalize_error(self, exception:
|
|
651
|
+
def finalize_error(self, exception: BaseException) -> None:
|
|
645
652
|
"""Mark the run failed AND flush the terminal 'failed' to the backend.
|
|
646
653
|
|
|
647
654
|
The success path is finalize(); on an optimizer exception the run must
|
|
@@ -154,6 +154,10 @@ Override any of these by passing kwargs to `cmpnd.configure(...)`.
|
|
|
154
154
|
- `_flush_batch` at `exporter_http.py:443` sorts items by type (`trace`, `span`, `trace_start`, `trace_finalize`, `trace_stats`, `eval_run`, `optimization_run`, various optimization progress records), buffers loose spans keyed by `trace_id` so they can be attached to their parent trace when it arrives, and submits each type's `_send_*` call to the thread pool in parallel.
|
|
155
155
|
- `shutdown()` at `exporter_http.py:163` sets the shutdown event, joins the worker thread, drains any leftover queue items, then shuts down the executor and HTTP client. It is safe to call multiple times.
|
|
156
156
|
|
|
157
|
+
### Delivery errors, dropping, and transient retry
|
|
158
|
+
|
|
159
|
+
Each `_send_*` method routes a failed delivery to one of two counters: a `429` (server buffer full) increments `_dropped_count` and is *not* retried (it's backpressure — the caller intentionally sheds the load), while any other non-2xx or transport failure increments `_error_count`. The data POSTs go through `_send_json_retrying` (`exporter_http.py`), which retries a **transient** failure — a `502`/`503`/`504` gateway/overload status, or an httpx transport/timeout error — up to `_data_post_attempts` (3) times with exponential backoff before it counts, so a momentary blip against a busy backend doesn't register as a hard error. It is *not* retried on `4xx` (client error), `429` (dropped, above), or `500` (the platform's to answer for — the same boundary as the stress harness's `is_external_outage`). Retrying is safe because serve dedups ingest — traces on `(org_id, trace_id, start_time)`, spans on the `spans_dedup` index — so a re-POST of a request that actually landed can't double-insert. The blocking run-*start* POSTs keep their own separate retry budget (`_start_post_attempts`), since they must return a server-assigned id synchronously.
|
|
160
|
+
|
|
157
161
|
### Loose spans vs bundled traces
|
|
158
162
|
|
|
159
163
|
A span can arrive at `_flush_batch` before its parent trace has been exported — this happens whenever the exporter ticks in the middle of a DSPy call. The exporter holds those loose spans in `self._pending_spans` (`exporter_http.py:122`) keyed by `trace_id`, and when the trace finally arrives it attaches the buffered spans before shipping. Spans for which the trace never arrives get flushed as a bare spans batch via `/api/v1/traces/:id/spans` — see `_send_spans` at `exporter_http.py:637`.
|
|
@@ -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.7.
|
|
8
|
+
version = "0.7.2"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -58,6 +58,10 @@ dev = [
|
|
|
58
58
|
# litellm's bedrock/ provider imports boto3 in-process, so the client-side
|
|
59
59
|
# Bedrock stress tests (committee twins) need it in the test env.
|
|
60
60
|
"boto3>=1.34",
|
|
61
|
+
# test_reflector_smoke.py builds numpy fixtures. dspy <=3.2.x required numpy
|
|
62
|
+
# unconditionally so it rode in transitively; dspy 3.3.0 moved numpy behind a
|
|
63
|
+
# `numpy` extra, so declare it directly as the test dependency it always was.
|
|
64
|
+
"numpy>=1.26",
|
|
61
65
|
]
|
|
62
66
|
# Deps for `deploy/deploy/stub/`, the local FastAPI server that emulates
|
|
63
67
|
# the AWS deploy stack. The stub itself ships in the cmpnd-deploy
|
|
@@ -92,6 +96,39 @@ build-backend = "hatchling.build"
|
|
|
92
96
|
[tool.hatch.build.targets.wheel]
|
|
93
97
|
packages = ["cmpnd"]
|
|
94
98
|
|
|
99
|
+
[tool.pytest.ini_options]
|
|
100
|
+
# Deliberately minimal: `markers` and `--strict-markers`, nothing else. Setting
|
|
101
|
+
# `testpaths` or a broader `addopts` here would change how every existing
|
|
102
|
+
# invocation resolves (CI lanes, `uv run pytest tests/ -k "not e2e"`, a bare
|
|
103
|
+
# `pytest` from a subdirectory), and this file's job is only to make the marker
|
|
104
|
+
# vocabulary explicit.
|
|
105
|
+
#
|
|
106
|
+
# --strict-markers turns a typo'd marker into an error instead of a silent
|
|
107
|
+
# no-op. That matters most for `forall_workers`: it SELECTS the merge-queue's
|
|
108
|
+
# wasm lane, so a misspelling there would quietly select nothing and report a
|
|
109
|
+
# green lane that ran zero tests. `asyncio` and `flaky` are registered by
|
|
110
|
+
# pytest-asyncio / pytest-rerunfailures, so they are not listed here.
|
|
111
|
+
addopts = "--strict-markers"
|
|
112
|
+
markers = [
|
|
113
|
+
# Tests that must hold for EVERY worker backend — i.e. that provision,
|
|
114
|
+
# execute or optimize through a data plane, and so can distinguish the
|
|
115
|
+
# Lambda plane from the wasm plane from the on-prem worker. This is the
|
|
116
|
+
# selector for the per-plane stress lanes: the set worth running against a
|
|
117
|
+
# second worker is exactly the set claimed to hold for all of them.
|
|
118
|
+
#
|
|
119
|
+
# A stress test that deploys or executes gets this marker. Its absence on
|
|
120
|
+
# such a test is a bug; its PRESENCE on a test no worker can affect (one
|
|
121
|
+
# that only exercises client-side tracing, or serve's own health) is worse,
|
|
122
|
+
# because it costs a second real-provider run to prove nothing.
|
|
123
|
+
# See docs/plans/2026-07-29-wasm-stress-lane.md.
|
|
124
|
+
"forall_workers: must hold for every worker backend (reaches a data plane)",
|
|
125
|
+
# Pre-existing vocabulary, registered here because --strict-markers now
|
|
126
|
+
# requires it — not introduced by this block.
|
|
127
|
+
"parity: CLI dual-front-end parity (argparse vs. interactive shell)",
|
|
128
|
+
"e2e: needs a live platform; excluded from the fast lane via -k 'not e2e'",
|
|
129
|
+
"integration: crosses a component boundary (SDK ↔ serve ↔ worker)",
|
|
130
|
+
]
|
|
131
|
+
|
|
95
132
|
[tool.ruff]
|
|
96
133
|
line-length = 120
|
|
97
134
|
target-version = "py310"
|