cmpnd 0.46.0__tar.gz → 0.47.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.46.0 → cmpnd-0.47.0}/PKG-INFO +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment.py +4 -5
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimization.py +9 -33
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/packaging.py +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/pyproject.toml +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_programs.py +5 -7
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_server_owned_completion.py +7 -12
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_logs_renders_traces.py +1 -2
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_unhappy_path.py +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy.py +4 -4
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize.py +42 -52
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_upload_auth_headers.py +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/uv.lock +1 -1
- {cmpnd-0.46.0 → cmpnd-0.47.0}/.gitignore +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/AGENTS.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/CLAUDE.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/admin.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/context.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/cli.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/client.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/docs/sdk-instrumentation.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/release-stress.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/drain.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_provider.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_reliability.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/_result.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/mint_bedrock_token.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_deploy_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_multi_client_stress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_client_committee.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_loop.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_optimize_stress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_provider_roles.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_real_provider_twins.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_reliability.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_reliability_classify.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_rlm_wasm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/stress/test_trace_search.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_admin.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_signatures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_context.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_execute.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_integration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_models.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_release_stress_script.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_retry.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.46.0 → cmpnd-0.47.0}/tests/test_xxh64.py +0 -0
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The deploy flow:
|
|
4
4
|
1. Package the module into a source ZIP
|
|
5
|
-
2. POST to the server to create a deployment (returns
|
|
6
|
-
3. PUT the ZIP to that slot
|
|
7
|
-
worker, raw body, no form)
|
|
5
|
+
2. POST to the server to create a deployment (returns an upload slot on serve)
|
|
6
|
+
3. PUT the ZIP to that slot as the raw body
|
|
8
7
|
4. Poll until the parser finishes (status becomes "ready" or "failed")
|
|
9
8
|
5. Return the deployment_id
|
|
10
9
|
"""
|
|
@@ -451,8 +450,8 @@ def deploy(module, metric=None, timeout=300, metadata=None, *, program_id=None):
|
|
|
451
450
|
|
|
452
451
|
deploy_body: dict[str, Any] = {
|
|
453
452
|
"program_signature_hash": identity.format_signature_hash(program_signature_hash),
|
|
454
|
-
# Declare the ZIP's exact length so the server can
|
|
455
|
-
#
|
|
453
|
+
# Declare the ZIP's exact length so the server can reject an oversized
|
|
454
|
+
# deploy before the upload.
|
|
456
455
|
"declared_content_length": len(zip_bytes),
|
|
457
456
|
}
|
|
458
457
|
# Left off when undeclared, so a serve from before program ids reads the body it
|
|
@@ -11,8 +11,8 @@ Flow per :func:`optimize` call:
|
|
|
11
11
|
1. Build the optimization bundle (``trainset.ndjson`` + ``valset.ndjson`` +
|
|
12
12
|
``metric.py``) and, if no ``deployment_id`` was given, :func:`deploy` the
|
|
13
13
|
program to materialize the starting deployment A.
|
|
14
|
-
2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get
|
|
15
|
-
|
|
14
|
+
2. Step 0 — ``data:"upload"``: POST to ``/optimize/{optimizer}`` to get an
|
|
15
|
+
upload slot, PUT the bundle, then poll until the step completes. The
|
|
16
16
|
result becomes a first-class child deployment B.
|
|
17
17
|
3. Steps 1..n — ``data:"reuse"`` on the latest result deployment, advancing the
|
|
18
18
|
seed each step (the key quality lever). Track the best score; stop after
|
|
@@ -1417,14 +1417,12 @@ def _initiate_step(
|
|
|
1417
1417
|
def _upload_bundle(
|
|
1418
1418
|
client: httpx.Client, initiated: dict[str, Any], bundle: bytes, *, endpoint: str, api_key: str
|
|
1419
1419
|
) -> None:
|
|
1420
|
-
"""
|
|
1420
|
+
"""PUT the optimization bundle, raw, to the slot the initiate step returned.
|
|
1421
1421
|
|
|
1422
|
-
Serve answers an ``awaiting_data`` initiate with ``upload_url
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
non-empty ``upload_fields`` is a presigned form, which no current server sends.
|
|
1427
|
-
The bundle then rides as the ``file`` part of a multipart POST.
|
|
1422
|
+
Serve answers an ``awaiting_data`` initiate with an ``upload_url`` on its own
|
|
1423
|
+
bundle route, and authenticates that route with the API key. The key rides
|
|
1424
|
+
the PUT only when ``upload_url`` is on ``endpoint``'s origin
|
|
1425
|
+
(``_http.upload_auth_headers``).
|
|
1428
1426
|
|
|
1429
1427
|
The slot pins its destination, so re-sending the same bytes writes the same
|
|
1430
1428
|
object. That makes this call idempotent, so it rides through a transient answer
|
|
@@ -1433,31 +1431,9 @@ def _upload_bundle(
|
|
|
1433
1431
|
upload_url = initiated.get("upload_url")
|
|
1434
1432
|
if not upload_url:
|
|
1435
1433
|
raise OptimizeError("upload step did not return an upload_url")
|
|
1436
|
-
upload_fields = initiated.get("upload_fields")
|
|
1437
|
-
method = (initiated.get("upload_method") or ("POST" if upload_fields else "PUT")).upper()
|
|
1438
1434
|
what = "upload optimization bundle"
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
if upload_fields:
|
|
1442
|
-
resp = _request(
|
|
1443
|
-
lambda: client.post(
|
|
1444
|
-
upload_url,
|
|
1445
|
-
data=dict(upload_fields),
|
|
1446
|
-
files={"file": ("data.zip", bundle, "application/zip")},
|
|
1447
|
-
headers=auth,
|
|
1448
|
-
),
|
|
1449
|
-
what,
|
|
1450
|
-
)
|
|
1451
|
-
else:
|
|
1452
|
-
resp = _request(
|
|
1453
|
-
lambda: client.request(
|
|
1454
|
-
method,
|
|
1455
|
-
upload_url,
|
|
1456
|
-
content=bundle,
|
|
1457
|
-
headers={"Content-Type": "application/zip", **auth},
|
|
1458
|
-
),
|
|
1459
|
-
what,
|
|
1460
|
-
)
|
|
1435
|
+
headers = {"Content-Type": "application/zip", **_http.upload_auth_headers(upload_url, endpoint, api_key)}
|
|
1436
|
+
resp = _request(lambda: client.put(upload_url, content=bundle, headers=headers), what)
|
|
1461
1437
|
if not resp.is_success:
|
|
1462
1438
|
body = (resp.text or "").strip()[:400]
|
|
1463
1439
|
raise OptimizeError(f"failed to {what}: HTTP {resp.status_code}: {body}")
|
|
@@ -1428,7 +1428,7 @@ def generate_source_zip(
|
|
|
1428
1428
|
) -> bytes:
|
|
1429
1429
|
"""Build a ZIP archive with program source, optional metric, and manifest.
|
|
1430
1430
|
|
|
1431
|
-
The ZIP is
|
|
1431
|
+
The ZIP is the body the deploy API's source upload takes.
|
|
1432
1432
|
"""
|
|
1433
1433
|
import dspy
|
|
1434
1434
|
|
|
@@ -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.47.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -212,11 +212,8 @@ class TestDeployBadImportRejected:
|
|
|
212
212
|
headers = {"X-API-Key": config.api_key, "Content-Type": "application/json"}
|
|
213
213
|
|
|
214
214
|
with httpx.Client(timeout=30.0) as client:
|
|
215
|
-
# Step 1: create deployment
|
|
216
|
-
#
|
|
217
|
-
# length, so it must be declared up front — exactly as cmpnd/deploy.py
|
|
218
|
-
# does, and a deployment names its program. This hand-crafted client
|
|
219
|
-
# mirrors that contract.
|
|
215
|
+
# Step 1: create the deployment, declaring the ZIP's length and
|
|
216
|
+
# naming its program as cmpnd.deploy does.
|
|
220
217
|
create_resp = client.post(
|
|
221
218
|
f"{config.endpoint}/api/v1/deployments",
|
|
222
219
|
json={
|
|
@@ -232,11 +229,12 @@ class TestDeployBadImportRejected:
|
|
|
232
229
|
deployment_id = data["deployment_id"]
|
|
233
230
|
upload_url = data["upload_url"]
|
|
234
231
|
|
|
235
|
-
# Step 2: PUT the bad ZIP to the
|
|
232
|
+
# Step 2: PUT the bad ZIP to the upload slot, which serve
|
|
233
|
+
# authenticates with the API key.
|
|
236
234
|
upload_resp = client.put(
|
|
237
235
|
upload_url,
|
|
238
236
|
content=zip_bytes,
|
|
239
|
-
headers={"Content-Type": "application/zip"},
|
|
237
|
+
headers={"X-API-Key": config.api_key, "Content-Type": "application/zip"},
|
|
240
238
|
)
|
|
241
239
|
upload_resp.raise_for_status()
|
|
242
240
|
|
|
@@ -106,18 +106,13 @@ def _initiate_and_abandon(deployment_id: str, trainset, valset, metric) -> str:
|
|
|
106
106
|
resp.raise_for_status()
|
|
107
107
|
initiated = resp.json()
|
|
108
108
|
|
|
109
|
-
# serve
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
data=dict(upload_fields),
|
|
117
|
-
files={"file": ("data.zip", bundle, "application/zip")},
|
|
118
|
-
)
|
|
119
|
-
else:
|
|
120
|
-
up = client.put(upload_url, content=bundle, headers={"Content-Type": "application/zip"})
|
|
109
|
+
# The slot is serve's bundle route, which takes the raw bundle as a PUT
|
|
110
|
+
# and authenticates it with the API key.
|
|
111
|
+
up = client.put(
|
|
112
|
+
initiated["upload_url"],
|
|
113
|
+
content=bundle,
|
|
114
|
+
headers={"X-API-Key": cfg.api_key, "Content-Type": "application/zip"},
|
|
115
|
+
)
|
|
121
116
|
up.raise_for_status()
|
|
122
117
|
|
|
123
118
|
# Deliberately NO status poll — the server-owned watcher must finish it.
|
|
@@ -64,8 +64,7 @@ def _create_and_upload_real_module(rails: httpx.Client, stub_url: str) -> str:
|
|
|
64
64
|
created = resp.json()
|
|
65
65
|
deployment_id = created["deployment_id"]
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
# — POST presigned (Lambda) or PUT (cmpnd-serve).
|
|
67
|
+
# PUT the real source ZIP to the slot the create response names.
|
|
69
68
|
zip_bytes = generate_source_zip(module, None)
|
|
70
69
|
upload_deploy_zip(created, zip_bytes)
|
|
71
70
|
|
|
@@ -59,7 +59,7 @@ def _create_deployment(rails: httpx.Client, *, signature_hash: int) -> dict:
|
|
|
59
59
|
|
|
60
60
|
def _upload_to_stub(stub_url: str, create_resp: dict, zip_bytes: bytes) -> None:
|
|
61
61
|
"""Mirror the SDK's deploy upload step — a single PUT to the upload slot
|
|
62
|
-
|
|
62
|
+
via the shared conftest helper. ``stub_url`` is unused — kept in
|
|
63
63
|
the signature so existing call-sites don't need editing."""
|
|
64
64
|
del stub_url # historical; the helper reads upload_url from create_resp
|
|
65
65
|
upload_deploy_zip(create_resp, zip_bytes)
|
|
@@ -59,7 +59,7 @@ class TestDeploy:
|
|
|
59
59
|
}
|
|
60
60
|
create_response.raise_for_status = mock.MagicMock()
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# Upload slot response
|
|
63
63
|
upload_response = mock.MagicMock()
|
|
64
64
|
upload_response.status_code = 200
|
|
65
65
|
upload_response.raise_for_status = mock.MagicMock()
|
|
@@ -88,7 +88,7 @@ class TestDeploy:
|
|
|
88
88
|
signature.json.return_value = signature_response
|
|
89
89
|
signature.raise_for_status = mock.MagicMock()
|
|
90
90
|
|
|
91
|
-
# Wire up: POST = create; PUT = upload slot
|
|
91
|
+
# Wire up: POST = create; PUT = upload slot; GET = polls.
|
|
92
92
|
client.post.side_effect = [create_response]
|
|
93
93
|
client.put.side_effect = [upload_response]
|
|
94
94
|
client.get.side_effect = [*poll_responses, signature]
|
|
@@ -178,7 +178,7 @@ class TestDeploy:
|
|
|
178
178
|
|
|
179
179
|
deployment.deploy(dspy.Predict("question -> answer"))
|
|
180
180
|
|
|
181
|
-
#
|
|
181
|
+
# One PUT of the raw ZIP to the upload slot.
|
|
182
182
|
client.post.assert_called_once()
|
|
183
183
|
upload_call = client.put.call_args
|
|
184
184
|
assert "s3.amazonaws.com" in upload_call.args[0]
|
|
@@ -194,7 +194,7 @@ class TestDeploy:
|
|
|
194
194
|
|
|
195
195
|
deployment.deploy(dspy.Predict("question -> answer"))
|
|
196
196
|
|
|
197
|
-
# The create body declares the ZIP length for the size cap
|
|
197
|
+
# The create body declares the ZIP length for the size cap.
|
|
198
198
|
create_call = client.post.call_args_list[0]
|
|
199
199
|
body = create_call.kwargs["json"]
|
|
200
200
|
assert isinstance(body["declared_content_length"], int)
|
|
@@ -127,14 +127,13 @@ def _one_completed_step(mock_httpx):
|
|
|
127
127
|
{
|
|
128
128
|
"optimization_id": "opt-0",
|
|
129
129
|
"status": "awaiting_data",
|
|
130
|
-
"upload_url": "https://
|
|
131
|
-
"upload_method": "
|
|
132
|
-
"upload_fields": {"key": "k"},
|
|
130
|
+
"upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
|
|
131
|
+
"upload_method": "PUT",
|
|
133
132
|
},
|
|
134
133
|
status_code=201,
|
|
135
134
|
),
|
|
136
|
-
_resp(status_code=204),
|
|
137
135
|
]
|
|
136
|
+
client.put.side_effect = [_resp(status_code=204)]
|
|
138
137
|
client.get.side_effect = [
|
|
139
138
|
_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
|
|
140
139
|
]
|
|
@@ -348,14 +347,14 @@ class TestLoop:
|
|
|
348
347
|
{
|
|
349
348
|
"optimization_id": "opt-0",
|
|
350
349
|
"status": "awaiting_data",
|
|
351
|
-
"upload_url": "https://
|
|
352
|
-
"
|
|
350
|
+
"upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
|
|
351
|
+
"upload_method": "PUT",
|
|
353
352
|
},
|
|
354
353
|
status_code=201,
|
|
355
354
|
),
|
|
356
|
-
_resp(is_success=True, status_code=204), # S3 upload
|
|
357
355
|
_resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
|
|
358
356
|
]
|
|
357
|
+
client.put.side_effect = [_resp(status_code=204)]
|
|
359
358
|
client.get.side_effect = [
|
|
360
359
|
_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
|
|
361
360
|
_resp({"status": "completed", "best_score": 0.6, "result_deployment_id": "dep-C"}),
|
|
@@ -379,13 +378,13 @@ class TestLoop:
|
|
|
379
378
|
assert init0.kwargs["json"]["data"] == "upload"
|
|
380
379
|
assert init0.kwargs["json"]["config"]["seed"] == 0
|
|
381
380
|
|
|
382
|
-
# The bundle is
|
|
383
|
-
upload = client.
|
|
384
|
-
assert upload.args[0] == "https://
|
|
385
|
-
assert
|
|
381
|
+
# The bundle is PUT exactly once, raw, to the slot serve answered.
|
|
382
|
+
(upload,) = client.put.call_args_list
|
|
383
|
+
assert upload.args[0] == "https://serve.example/api/v1/optimizations/opt-0/bundle"
|
|
384
|
+
assert upload.kwargs["content"]
|
|
386
385
|
|
|
387
386
|
# Step 1 REUSEs the result deployment (dep-B) with seed advanced to 1.
|
|
388
|
-
init1 = client.post.call_args_list[
|
|
387
|
+
init1 = client.post.call_args_list[1]
|
|
389
388
|
assert "/deployments/dep-B/optimize/gepa" in init1.args[0]
|
|
390
389
|
assert init1.kwargs["json"]["data"] == "reuse"
|
|
391
390
|
assert init1.kwargs["json"]["config"]["seed"] == 1
|
|
@@ -395,17 +394,14 @@ class TestLoop:
|
|
|
395
394
|
assert result.score == 0.6
|
|
396
395
|
assert callable(result)
|
|
397
396
|
|
|
398
|
-
def
|
|
399
|
-
"""
|
|
400
|
-
|
|
401
|
-
go out as a raw PUT to the slot URL — not a multipart POST — so the one
|
|
402
|
-
SDK upload path serves both the worker (PUT) and the Lambda (presigned
|
|
403
|
-
POST) workers (the CMP-250 × CMP-254 reconciliation)."""
|
|
397
|
+
def test_upload_is_a_raw_put_to_the_slot(self, mock_httpx):
|
|
398
|
+
"""Serve answers awaiting_data with one PUT slot. The bundle goes out
|
|
399
|
+
as the raw PUT body to that URL, never as a multipart form."""
|
|
404
400
|
client = _client(mock_httpx)
|
|
405
401
|
train, val = _examples()
|
|
406
402
|
|
|
407
403
|
# Only the two initiate calls go through client.post; the bundle upload
|
|
408
|
-
# is a PUT, so it lands on client.
|
|
404
|
+
# is a PUT, so it lands on client.put and must not consume a post.
|
|
409
405
|
client.post.side_effect = [
|
|
410
406
|
_resp(
|
|
411
407
|
{
|
|
@@ -413,13 +409,12 @@ class TestLoop:
|
|
|
413
409
|
"status": "awaiting_data",
|
|
414
410
|
"upload_url": "http://worker:8082/uploads/opt-0",
|
|
415
411
|
"upload_method": "PUT",
|
|
416
|
-
"upload_fields": {},
|
|
417
412
|
},
|
|
418
413
|
status_code=201,
|
|
419
414
|
),
|
|
420
415
|
_resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
|
|
421
416
|
]
|
|
422
|
-
client.
|
|
417
|
+
client.put.side_effect = [_resp(is_success=True, status_code=202)]
|
|
423
418
|
client.get.side_effect = [
|
|
424
419
|
_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
|
|
425
420
|
_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-C"}),
|
|
@@ -439,8 +434,8 @@ class TestLoop:
|
|
|
439
434
|
)
|
|
440
435
|
|
|
441
436
|
# The bundle was PUT (raw bytes) to the worker's slot, never POSTed.
|
|
442
|
-
put = client.
|
|
443
|
-
assert put.args == ("
|
|
437
|
+
put = client.put.call_args
|
|
438
|
+
assert put.args == ("http://worker:8082/uploads/opt-0",)
|
|
444
439
|
assert put.kwargs["content"] # raw zip bytes, not a multipart `files=`
|
|
445
440
|
assert put.kwargs["headers"]["Content-Type"] == "application/zip"
|
|
446
441
|
# The two client.post calls are both initiate calls (no upload among them).
|
|
@@ -456,15 +451,14 @@ class TestLoop:
|
|
|
456
451
|
{
|
|
457
452
|
"optimization_id": "opt-0",
|
|
458
453
|
"status": "awaiting_data",
|
|
459
|
-
"upload_url": "https://
|
|
460
|
-
"upload_method": "
|
|
461
|
-
"upload_fields": {"key": "k"},
|
|
454
|
+
"upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
|
|
455
|
+
"upload_method": "PUT",
|
|
462
456
|
},
|
|
463
457
|
status_code=201,
|
|
464
458
|
),
|
|
465
|
-
_resp(status_code=204),
|
|
466
459
|
_resp({"optimization_id": "opt-1", "status": "running"}, status_code=201),
|
|
467
460
|
]
|
|
461
|
+
client.put.side_effect = [_resp(status_code=204)]
|
|
468
462
|
# Both steps score the same → no improvement on step 1.
|
|
469
463
|
client.get.side_effect = [
|
|
470
464
|
_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"}),
|
|
@@ -499,14 +493,13 @@ class TestLoop:
|
|
|
499
493
|
{
|
|
500
494
|
"optimization_id": "opt-0",
|
|
501
495
|
"status": "awaiting_data",
|
|
502
|
-
"upload_url": "https://
|
|
503
|
-
"upload_method": "
|
|
504
|
-
"upload_fields": {"key": "k"},
|
|
496
|
+
"upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
|
|
497
|
+
"upload_method": "PUT",
|
|
505
498
|
},
|
|
506
499
|
status_code=201,
|
|
507
500
|
),
|
|
508
|
-
_resp(status_code=204),
|
|
509
501
|
]
|
|
502
|
+
client.put.side_effect = [_resp(status_code=204)]
|
|
510
503
|
client.get.side_effect = [
|
|
511
504
|
_resp({"status": "completed", "best_score": 0.7, "result_deployment_id": "dep-B"}),
|
|
512
505
|
]
|
|
@@ -545,14 +538,13 @@ class TestFailures:
|
|
|
545
538
|
{
|
|
546
539
|
"optimization_id": "opt-0",
|
|
547
540
|
"status": "awaiting_data",
|
|
548
|
-
"upload_url": "https://
|
|
549
|
-
"upload_method": "
|
|
550
|
-
"upload_fields": {"key": "k"},
|
|
541
|
+
"upload_url": "https://serve.example/api/v1/optimizations/opt-0/bundle",
|
|
542
|
+
"upload_method": "PUT",
|
|
551
543
|
},
|
|
552
544
|
status_code=201,
|
|
553
545
|
),
|
|
554
|
-
_resp(status_code=204),
|
|
555
546
|
]
|
|
547
|
+
client.put.side_effect = [_resp(status_code=204)]
|
|
556
548
|
client.get.side_effect = [
|
|
557
549
|
_resp({"status": "failed", "error": "trainset row 1 missing label field(s): ['answer']"}),
|
|
558
550
|
]
|
|
@@ -633,8 +625,8 @@ class TestNetworkErrorsWrapped:
|
|
|
633
625
|
|
|
634
626
|
def test_upload_bundle_wraps_network_error(self):
|
|
635
627
|
client = mock.MagicMock()
|
|
636
|
-
client.
|
|
637
|
-
initiated = {"upload_url": "
|
|
628
|
+
client.put.side_effect = httpx.WriteTimeout("slow upload")
|
|
629
|
+
initiated = {"upload_url": "http://x/api/v1/optimizations/opt-0/bundle", "upload_method": "PUT"}
|
|
638
630
|
# The upload re-sends the same bytes to the same pinned slot, so a
|
|
639
631
|
# transport blip is retried; the wrapped error surfaces once the budget
|
|
640
632
|
# is spent.
|
|
@@ -951,13 +943,13 @@ class TestInitiateAndUploadTolerance:
|
|
|
951
943
|
|
|
952
944
|
def test_upload_retries_a_retryable_response(self):
|
|
953
945
|
client = mock.MagicMock()
|
|
954
|
-
client.
|
|
955
|
-
initiated = {"upload_url": "http://worker:8082/uploads/opt-0"
|
|
946
|
+
client.put.side_effect = [_transient(), _resp(status_code=204)]
|
|
947
|
+
initiated = {"upload_url": "http://worker:8082/uploads/opt-0"}
|
|
956
948
|
|
|
957
949
|
with _fake_clock():
|
|
958
950
|
optimization._upload_bundle(client, initiated, b"zip-bytes", endpoint="http://x", api_key="k")
|
|
959
951
|
|
|
960
|
-
assert client.
|
|
952
|
+
assert client.put.call_count == 2
|
|
961
953
|
|
|
962
954
|
|
|
963
955
|
class TestUploadAuthentication:
|
|
@@ -982,7 +974,7 @@ class TestUploadAuthentication:
|
|
|
982
974
|
return sent
|
|
983
975
|
|
|
984
976
|
def test_upload_to_the_endpoint_carries_the_api_key(self):
|
|
985
|
-
(upload,) = self._upload({"upload_url": self._OWN_SLOT, "upload_method": "PUT"
|
|
977
|
+
(upload,) = self._upload({"upload_url": self._OWN_SLOT, "upload_method": "PUT"})
|
|
986
978
|
|
|
987
979
|
assert upload.method == "PUT"
|
|
988
980
|
assert str(upload.url) == self._OWN_SLOT
|
|
@@ -991,20 +983,19 @@ class TestUploadAuthentication:
|
|
|
991
983
|
assert upload.content == b"zip-bytes"
|
|
992
984
|
|
|
993
985
|
def test_upload_to_another_origin_carries_no_api_key(self):
|
|
994
|
-
(upload,) = self._upload(
|
|
995
|
-
{"upload_url": "http://worker:8082/uploads/opt-0", "upload_method": "PUT", "upload_fields": {}}
|
|
996
|
-
)
|
|
986
|
+
(upload,) = self._upload({"upload_url": "http://worker:8082/uploads/opt-0", "upload_method": "PUT"})
|
|
997
987
|
|
|
998
988
|
assert "X-API-Key" not in upload.headers
|
|
999
989
|
assert upload.headers["Content-Type"] == "application/zip"
|
|
1000
990
|
|
|
1001
|
-
def
|
|
991
|
+
def test_upload_is_a_raw_put_whatever_form_the_answer_names(self):
|
|
1002
992
|
(upload,) = self._upload(
|
|
1003
|
-
{"upload_url":
|
|
993
|
+
{"upload_url": self._OWN_SLOT, "upload_method": "POST", "upload_fields": {"key": "programs/x/data.zip"}}
|
|
1004
994
|
)
|
|
1005
995
|
|
|
1006
|
-
assert upload.method == "
|
|
1007
|
-
assert "
|
|
996
|
+
assert upload.method == "PUT"
|
|
997
|
+
assert upload.headers["Content-Type"] == "application/zip"
|
|
998
|
+
assert upload.content == b"zip-bytes"
|
|
1008
999
|
|
|
1009
1000
|
def test_optimize_hands_the_configured_endpoint_and_key_to_the_upload(self, mock_httpx):
|
|
1010
1001
|
client = _client(mock_httpx)
|
|
@@ -1016,12 +1007,11 @@ class TestUploadAuthentication:
|
|
|
1016
1007
|
"status": "awaiting_data",
|
|
1017
1008
|
"upload_url": self._OWN_SLOT,
|
|
1018
1009
|
"upload_method": "PUT",
|
|
1019
|
-
"upload_fields": {},
|
|
1020
1010
|
},
|
|
1021
1011
|
status_code=201,
|
|
1022
1012
|
),
|
|
1023
1013
|
]
|
|
1024
|
-
client.
|
|
1014
|
+
client.put.side_effect = [_resp(status_code=200)]
|
|
1025
1015
|
client.get.side_effect = [_resp({"status": "completed", "best_score": 0.5, "result_deployment_id": "dep-B"})]
|
|
1026
1016
|
|
|
1027
1017
|
with _no_sleep():
|
|
@@ -1036,7 +1026,7 @@ class TestUploadAuthentication:
|
|
|
1036
1026
|
max_steps=1,
|
|
1037
1027
|
)
|
|
1038
1028
|
|
|
1039
|
-
put = client.
|
|
1040
|
-
assert put.args == (
|
|
1029
|
+
put = client.put.call_args
|
|
1030
|
+
assert put.args == (self._OWN_SLOT,)
|
|
1041
1031
|
assert put.kwargs["headers"]["X-API-Key"] == "ct_test_key"
|
|
1042
1032
|
assert put.kwargs["headers"]["Content-Type"] == "application/zip"
|
|
@@ -27,7 +27,7 @@ _KEY = "ct_test_key"
|
|
|
27
27
|
("http://localhost/api/v1/optimizations/op_abc/bundle", "http://localhost:80"),
|
|
28
28
|
# Scheme and host compare case-insensitively.
|
|
29
29
|
("HTTPS://App.Cmpnd.AI/api/v1/deployments/d_abc/source", _ENDPOINT),
|
|
30
|
-
("http://localhost:3000/api/v1/
|
|
30
|
+
("http://localhost:3000/api/v1/deployments/d_abc/source", "http://localhost:3000"),
|
|
31
31
|
],
|
|
32
32
|
)
|
|
33
33
|
def test_sends_the_key_to_the_endpoint_origin(upload_url: str, endpoint: str) -> None:
|
|
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
|