synth-ai 0.22.2.dev688__tar.gz → 0.22.2.dev689__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.
- {synth_ai-0.22.2.dev688/synth_ai.egg-info → synth_ai-0.22.2.dev689}/PKG-INFO +1 -1
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/pyproject.toml +1 -1
- synth_ai-0.22.2.dev689/synth_ai/core/http/retry.py +302 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/transport.py +81 -12
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/client.py +31 -9
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689/synth_ai.egg-info}/PKG-INFO +1 -1
- synth_ai-0.22.2.dev688/synth_ai/core/http/retry.py +0 -135
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/LICENSE +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/MANIFEST.in +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/index-v1.json +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/messaging-v1.json +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/research-v1.json +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/setup.cfg +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/setup.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/__main__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/AGENTS.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/__main__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/index.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/main.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_image_releases.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_projects.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/research_launch.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/client.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/credentials.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/json_value.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/pagination.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/errors.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/async_transport.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/request.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/streaming.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/local_files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/research/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/env.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/log_filter.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/paths.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/urls.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__main__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/objective_tools.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/registry.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/request_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/server.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/approvals.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/artifacts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/datasets.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/exports.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factory_results.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/image_releases.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index_qa.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/integrations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/intern_program.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/launch_schemas.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/local_files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/logs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/outputs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/policy_schemas.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/progress.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/project_data.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/projects.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/prs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/readiness.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/repos.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/research_intern.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resource_reads.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resources.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/runs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/scientific_records.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/trained_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/usage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/visuals.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/workspace_inputs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/pools.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/py.typed +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/AGENT_SETUP_PROMPT.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/agent_policy.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/artifacts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalog.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalogue.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/classification.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/client.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contributions.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/errors.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/forge_notices.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/intake.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/lifecycle.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/local_files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/manifest.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/oauth.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/package.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_corpus.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_search.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_checks.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_preflight.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_reviews.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/build.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/capture.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/release.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research_intake.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/retry.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/rights.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scientific_provenance.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scope_errors.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scopes.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/search.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/submission.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/timeouts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/transfer.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/upload_session.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/usage_accounting.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/value.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/wallet_search.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/client.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/client.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/pagination.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/crypto.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/env.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/urls.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/account.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_factories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_swarms.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/artifacts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/auth.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/client.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/api.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/operations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/packaging.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_activity_wire.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_environment_wire.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_transcript_wire.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_wire.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity_items.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/actor_images.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/billing.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/canonical_usage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/checkpoints.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployment_claims.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/common.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dataset_revisions.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/deliveries.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dev_environment_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/economics.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environment_manifest.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/experiment_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_lenses.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_operations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_role_receipts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/failure.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/contracts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/evaluation.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/exports.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/operations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/records.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/resources.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/schema.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge_citations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge_uploads.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/image_releases.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_authority.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_blockers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_grants.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_program.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_resources.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_usage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/limit_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/local_execution_profile.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_attachment.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_result_outcome.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/operator_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_data.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_runtime.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/projects.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/promotions.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/research_intern.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_reads.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_settlement.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_authority.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_control.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_diagnostics.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_events.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_execution.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_launch.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_observability.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_state.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_timeline.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_image.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_intent.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream_snapshot.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_citations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_integrity.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_policy_data.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_harnesses.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_kinds.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_branching.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_credential_providers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_environment_kinds.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_evidence_obligations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_funding_sources.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_horizons.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_host_kinds.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_inference_providers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_network_topology.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_providers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_resource_kinds.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_roles.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_run_policy.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runbooks.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runnable_project.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runtime_kinds.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_tool_providers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_work_modes.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/status.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_controls.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_history.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_rollouts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_tasks.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarms.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/tag.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/traces.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/transcript.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/types.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/usage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/visuals.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models_README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/work_products.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/workspaces.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/economics.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/efforts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/errors.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/events.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/experiments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/facade.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factory_storage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/image_releases.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_authority.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_blockers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_program.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/knowledge.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/operations.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_data.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_deliveries.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_workspaces.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/projects.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/public.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/readouts.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/research_intern.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/resource_reads.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/scientific_records.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/secrets.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/README.md +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_base.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_client_helpers.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_run_authority_mixin.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/approvals.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/billing.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cloud_deployments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/config.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cost.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/datasets.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/dev_environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/directory_inputs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/environments.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/exports.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factory_evidence.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/files.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/github.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/image_releases.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/images.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/logs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/outputs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/progress.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/project.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/projects.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/prs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/readiness.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/repositories.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/runs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/secrets.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/setup.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/tag.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/trained_models.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/transport.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/usage.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/work_products.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/workspace_inputs.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/surface_parity.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/swarms.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/traces.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/__init__.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/http.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/pagination.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/version.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/visuals.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/wiki.py +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/SOURCES.txt +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/dependency_links.txt +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/entry_points.txt +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/requires.txt +0 -0
- {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "synth-ai"
|
|
3
|
-
version = "0.22.2.
|
|
3
|
+
version = "0.22.2.dev689"
|
|
4
4
|
description = "Python SDK, CLI and MCP servers for Synth Index search and Swarms"
|
|
5
5
|
keywords = ["synth", "index", "search", "citations", "research", "swarms", "mcp", "sdk"]
|
|
6
6
|
authors = [{name = "Synth AI", email = "josh@usesynth.ai"}]
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Classified retry policy shared by sync and async transports.
|
|
2
|
+
|
|
3
|
+
# See: testing/specifications/sdk/core_research_migration.md
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
import random
|
|
10
|
+
from collections.abc import Mapping
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from synth_ai.core.errors import (
|
|
14
|
+
ContractMismatchError,
|
|
15
|
+
RetryDirective,
|
|
16
|
+
RuntimeUnavailableError,
|
|
17
|
+
SynthError,
|
|
18
|
+
SynthErrorCategory,
|
|
19
|
+
SynthErrorCode,
|
|
20
|
+
SynthFailure,
|
|
21
|
+
)
|
|
22
|
+
from synth_ai.core.http.request import HttpMethod, HttpRequest, OperationMetadata
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class RetryPolicy:
|
|
27
|
+
attempts_max: int = 3
|
|
28
|
+
delay_seconds_initial: float = 0.5
|
|
29
|
+
delay_seconds_max: float = 5.0
|
|
30
|
+
retry_statuses: frozenset[int] = frozenset({408, 409, 429, 500, 502, 503, 504})
|
|
31
|
+
|
|
32
|
+
def __post_init__(self) -> None:
|
|
33
|
+
if (
|
|
34
|
+
isinstance(self.attempts_max, bool)
|
|
35
|
+
or not isinstance(self.attempts_max, int)
|
|
36
|
+
or not 1 <= self.attempts_max <= 100
|
|
37
|
+
):
|
|
38
|
+
raise ValueError("attempts_max must be an integer from 1 through 100")
|
|
39
|
+
for delay in (self.delay_seconds_initial, self.delay_seconds_max):
|
|
40
|
+
if (
|
|
41
|
+
isinstance(delay, bool)
|
|
42
|
+
or not isinstance(delay, (int, float))
|
|
43
|
+
or not math.isfinite(delay)
|
|
44
|
+
):
|
|
45
|
+
raise ValueError("retry delays must be finite numbers")
|
|
46
|
+
if self.delay_seconds_initial < 0 or self.delay_seconds_max < 0:
|
|
47
|
+
raise ValueError("retry delays must be non-negative")
|
|
48
|
+
if self.delay_seconds_initial > self.delay_seconds_max:
|
|
49
|
+
raise ValueError("delay_seconds_initial cannot exceed delay_seconds_max")
|
|
50
|
+
|
|
51
|
+
def permits(
|
|
52
|
+
self,
|
|
53
|
+
operation: OperationMetadata,
|
|
54
|
+
*,
|
|
55
|
+
idempotency_key: str | None,
|
|
56
|
+
) -> bool:
|
|
57
|
+
"""Retry only metadata-declared idempotent operations.
|
|
58
|
+
|
|
59
|
+
Unsafe methods additionally require an idempotency key.
|
|
60
|
+
"""
|
|
61
|
+
if not operation.idempotent:
|
|
62
|
+
return False
|
|
63
|
+
if operation.method is HttpMethod.GET:
|
|
64
|
+
return True
|
|
65
|
+
return bool(idempotency_key)
|
|
66
|
+
|
|
67
|
+
def delay_seconds(
|
|
68
|
+
self,
|
|
69
|
+
attempt_index: int,
|
|
70
|
+
*,
|
|
71
|
+
retry_after_seconds: float | None,
|
|
72
|
+
) -> float:
|
|
73
|
+
"""Deterministic exponential backoff capped by policy and Retry-After."""
|
|
74
|
+
if attempt_index < 0:
|
|
75
|
+
raise ValueError("attempt_index must be non-negative")
|
|
76
|
+
exponential = min(
|
|
77
|
+
self.delay_seconds_max,
|
|
78
|
+
self.delay_seconds_initial * (2**attempt_index),
|
|
79
|
+
)
|
|
80
|
+
if retry_after_seconds is None:
|
|
81
|
+
return exponential
|
|
82
|
+
if not math.isfinite(retry_after_seconds) or retry_after_seconds < 0:
|
|
83
|
+
raise ValueError("retry_after_seconds must be non-negative")
|
|
84
|
+
return min(self.delay_seconds_max, max(exponential, retry_after_seconds))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def idempotency_key_from_request(request: HttpRequest) -> str | None:
|
|
88
|
+
"""Extract an idempotency key from headers or JSON body fields."""
|
|
89
|
+
values = [value for name, value in request.headers.items() if name.lower() == "idempotency-key"]
|
|
90
|
+
if isinstance(request.body, dict):
|
|
91
|
+
values.extend(
|
|
92
|
+
request.body[name]
|
|
93
|
+
for name in ("idempotency_key", "idempotency_key_run_create")
|
|
94
|
+
if name in request.body
|
|
95
|
+
)
|
|
96
|
+
identities = set()
|
|
97
|
+
for value in values:
|
|
98
|
+
if not isinstance(value, str) or not value.strip():
|
|
99
|
+
raise ValueError("idempotency identity must be a non-empty string")
|
|
100
|
+
identities.add(value)
|
|
101
|
+
if len(identities) > 1:
|
|
102
|
+
raise ValueError("request contains conflicting idempotency identities")
|
|
103
|
+
return next(iter(identities), None)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def should_retry_failure(
|
|
107
|
+
policy: RetryPolicy,
|
|
108
|
+
request: HttpRequest,
|
|
109
|
+
error: BaseException,
|
|
110
|
+
) -> bool:
|
|
111
|
+
"""Decide whether one failed execute attempt may be retried."""
|
|
112
|
+
if isinstance(error, (ContractMismatchError, RuntimeUnavailableError)):
|
|
113
|
+
return False
|
|
114
|
+
if not policy.permits(
|
|
115
|
+
request.operation,
|
|
116
|
+
idempotency_key=idempotency_key_from_request(request),
|
|
117
|
+
):
|
|
118
|
+
return False
|
|
119
|
+
if isinstance(error, SynthError):
|
|
120
|
+
if error.failure is not None:
|
|
121
|
+
# The first-class directive is authoritative, including an explicit false.
|
|
122
|
+
return error.failure.retry.retryable
|
|
123
|
+
if error.retryable:
|
|
124
|
+
return True
|
|
125
|
+
status = None
|
|
126
|
+
if error.failure is not None:
|
|
127
|
+
status = error.failure.status
|
|
128
|
+
if status is None and hasattr(error, "status"):
|
|
129
|
+
status = error.status
|
|
130
|
+
return isinstance(status, int) and status in policy.retry_statuses
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def retry_after_from_error(error: BaseException) -> float | None:
|
|
135
|
+
if isinstance(error, SynthError):
|
|
136
|
+
return error.retry_after_seconds
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Backend ``core/data/db/backpressure_classification.py::DB_BACKPRESSURE_REASON_CODE``,
|
|
141
|
+
# emitted as ``error_code`` by ``core/data/db/admission.py::db_backpressure_payload``.
|
|
142
|
+
DB_BACKPRESSURE_ERROR_CODE = "db_backpressure"
|
|
143
|
+
DB_BACKPRESSURE_RETRY_EXHAUSTED_CODE = "db_backpressure_retry_exhausted"
|
|
144
|
+
_REPLAY_SAFE_METHODS = frozenset({"GET", "HEAD", "OPTIONS"})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass(frozen=True, slots=True)
|
|
148
|
+
class DbBackpressureRefusal:
|
|
149
|
+
"""One typed DB backpressure refusal decoded from a backend error body."""
|
|
150
|
+
|
|
151
|
+
status: int
|
|
152
|
+
error_code: str
|
|
153
|
+
component: str | None
|
|
154
|
+
work_class: str | None
|
|
155
|
+
retry_after_seconds: float | None
|
|
156
|
+
operator_action: str | None
|
|
157
|
+
message: str
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@dataclass(frozen=True, slots=True)
|
|
161
|
+
class DbBackpressureRetryPolicy:
|
|
162
|
+
"""Bounds for replaying a request the backend shed before starting DB work.
|
|
163
|
+
|
|
164
|
+
Attempts and total sleep are both bounded; the server's ``retry_after`` is
|
|
165
|
+
honored up to ``retry_after_cap_seconds`` and only ever lengthened by jitter.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
attempts_max: int = 5
|
|
169
|
+
total_wait_seconds_max: float = 120.0
|
|
170
|
+
retry_after_cap_seconds: float = 30.0
|
|
171
|
+
delay_seconds_initial: float = 1.0
|
|
172
|
+
jitter_ratio: float = 0.25
|
|
173
|
+
|
|
174
|
+
def __post_init__(self) -> None:
|
|
175
|
+
if (
|
|
176
|
+
isinstance(self.attempts_max, bool)
|
|
177
|
+
or not isinstance(self.attempts_max, int)
|
|
178
|
+
or not 1 <= self.attempts_max <= 20
|
|
179
|
+
):
|
|
180
|
+
raise ValueError("attempts_max must be an integer from 1 through 20")
|
|
181
|
+
for name in (
|
|
182
|
+
"total_wait_seconds_max",
|
|
183
|
+
"retry_after_cap_seconds",
|
|
184
|
+
"delay_seconds_initial",
|
|
185
|
+
"jitter_ratio",
|
|
186
|
+
):
|
|
187
|
+
value = getattr(self, name)
|
|
188
|
+
if (
|
|
189
|
+
isinstance(value, bool)
|
|
190
|
+
or not isinstance(value, (int, float))
|
|
191
|
+
or not math.isfinite(value)
|
|
192
|
+
or value < 0
|
|
193
|
+
):
|
|
194
|
+
raise ValueError(f"{name} must be a finite non-negative number")
|
|
195
|
+
if self.jitter_ratio > 1:
|
|
196
|
+
raise ValueError("jitter_ratio cannot exceed 1")
|
|
197
|
+
|
|
198
|
+
def next_delay_seconds(
|
|
199
|
+
self,
|
|
200
|
+
*,
|
|
201
|
+
attempts_made: int,
|
|
202
|
+
waited_seconds: float,
|
|
203
|
+
refusal: DbBackpressureRefusal,
|
|
204
|
+
rng: random.Random,
|
|
205
|
+
) -> float | None:
|
|
206
|
+
"""Return the sleep before the next attempt, or None when bounds are spent."""
|
|
207
|
+
if attempts_made >= self.attempts_max:
|
|
208
|
+
return None
|
|
209
|
+
if refusal.retry_after_seconds is not None:
|
|
210
|
+
base = min(self.retry_after_cap_seconds, refusal.retry_after_seconds)
|
|
211
|
+
else:
|
|
212
|
+
base = min(
|
|
213
|
+
self.retry_after_cap_seconds,
|
|
214
|
+
self.delay_seconds_initial * (2 ** (attempts_made - 1)),
|
|
215
|
+
)
|
|
216
|
+
delay = base * (1.0 + rng.uniform(0.0, self.jitter_ratio))
|
|
217
|
+
if waited_seconds + delay > self.total_wait_seconds_max:
|
|
218
|
+
return None
|
|
219
|
+
return delay
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def request_permits_replay(method: str, headers: Mapping[str, str]) -> bool:
|
|
223
|
+
"""A request may be replayed only when safe by method or carrying an idempotency key."""
|
|
224
|
+
if method.upper() in _REPLAY_SAFE_METHODS:
|
|
225
|
+
return True
|
|
226
|
+
return any(
|
|
227
|
+
name.lower() == "idempotency-key" and bool(str(value).strip())
|
|
228
|
+
for name, value in headers.items()
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class DbBackpressureRetryExhaustedError(SynthError):
|
|
233
|
+
"""The backend kept shedding DB work past the retry bounds.
|
|
234
|
+
|
|
235
|
+
Carries the last refusal's ``component``, ``work_class`` and ``retry_after``.
|
|
236
|
+
Not retryable again: callers already spent the bounded budget.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
*,
|
|
242
|
+
method: str,
|
|
243
|
+
path: str,
|
|
244
|
+
operation_id: str | None,
|
|
245
|
+
refusal: DbBackpressureRefusal,
|
|
246
|
+
attempts: int,
|
|
247
|
+
waited_seconds: float,
|
|
248
|
+
) -> None:
|
|
249
|
+
self.method = method
|
|
250
|
+
self.path = path
|
|
251
|
+
self.refusal = refusal
|
|
252
|
+
self.attempts = attempts
|
|
253
|
+
self.waited_seconds = waited_seconds
|
|
254
|
+
detail = (
|
|
255
|
+
f"{method} {path}: backend refused DB work ({refusal.error_code}) on "
|
|
256
|
+
f"{attempts} attempts over {waited_seconds:.1f}s; last component="
|
|
257
|
+
f"{refusal.component} work_class={refusal.work_class} "
|
|
258
|
+
f"retry_after={refusal.retry_after_seconds} status={refusal.status} "
|
|
259
|
+
f"operator_action={refusal.operator_action}"
|
|
260
|
+
)
|
|
261
|
+
super().__init__(
|
|
262
|
+
detail,
|
|
263
|
+
failure=SynthFailure(
|
|
264
|
+
code=SynthErrorCode(DB_BACKPRESSURE_RETRY_EXHAUSTED_CODE),
|
|
265
|
+
category=SynthErrorCategory.TRANSIENT_SERVICE,
|
|
266
|
+
operation=operation_id,
|
|
267
|
+
request_id=None,
|
|
268
|
+
correlation_id=None,
|
|
269
|
+
retry=RetryDirective(
|
|
270
|
+
retryable=False,
|
|
271
|
+
retry_after_seconds=refusal.retry_after_seconds,
|
|
272
|
+
),
|
|
273
|
+
status=refusal.status,
|
|
274
|
+
detail=detail,
|
|
275
|
+
reason=refusal.component,
|
|
276
|
+
),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def component(self) -> str | None:
|
|
281
|
+
return self.refusal.component
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def work_class(self) -> str | None:
|
|
285
|
+
return self.refusal.work_class
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def retry_after_seconds(self) -> float | None:
|
|
289
|
+
return self.refusal.retry_after_seconds
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
__all__ = [
|
|
293
|
+
"DB_BACKPRESSURE_ERROR_CODE",
|
|
294
|
+
"DbBackpressureRefusal",
|
|
295
|
+
"DbBackpressureRetryExhaustedError",
|
|
296
|
+
"DbBackpressureRetryPolicy",
|
|
297
|
+
"request_permits_replay",
|
|
298
|
+
"RetryPolicy",
|
|
299
|
+
"idempotency_key_from_request",
|
|
300
|
+
"retry_after_from_error",
|
|
301
|
+
"should_retry_failure",
|
|
302
|
+
]
|
|
@@ -7,6 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import math
|
|
10
|
+
import random
|
|
10
11
|
import time
|
|
11
12
|
from collections.abc import Callable, Iterator, Mapping
|
|
12
13
|
from dataclasses import dataclass, field
|
|
@@ -37,7 +38,12 @@ from synth_ai.core.errors import (
|
|
|
37
38
|
)
|
|
38
39
|
from synth_ai.core.http.request import HttpRequest
|
|
39
40
|
from synth_ai.core.http.retry import (
|
|
41
|
+
DB_BACKPRESSURE_ERROR_CODE,
|
|
42
|
+
DbBackpressureRefusal,
|
|
43
|
+
DbBackpressureRetryExhaustedError,
|
|
44
|
+
DbBackpressureRetryPolicy,
|
|
40
45
|
RetryPolicy,
|
|
46
|
+
request_permits_replay,
|
|
41
47
|
retry_after_from_error,
|
|
42
48
|
should_retry_failure,
|
|
43
49
|
)
|
|
@@ -206,6 +212,37 @@ SEARCH_ID_HEADER = "x-synth-search-id"
|
|
|
206
212
|
SEARCH_RESOURCE_KIND = "index_search"
|
|
207
213
|
|
|
208
214
|
|
|
215
|
+
def db_backpressure_refusal(response: httpx.Response) -> DbBackpressureRefusal | None:
|
|
216
|
+
"""Decode the backend's typed DB backpressure refusal, or None for any other error."""
|
|
217
|
+
if response.status_code not in {429, 503}:
|
|
218
|
+
return None
|
|
219
|
+
try:
|
|
220
|
+
decoded = response.json()
|
|
221
|
+
except (json.JSONDecodeError, ValueError):
|
|
222
|
+
return None
|
|
223
|
+
if not isinstance(decoded, dict):
|
|
224
|
+
return None
|
|
225
|
+
detail = decoded.get("detail")
|
|
226
|
+
body = detail if isinstance(detail, dict) else decoded
|
|
227
|
+
if DB_BACKPRESSURE_ERROR_CODE not in (body.get("error_code"), body.get("failure_class")):
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def text(key: str) -> str | None:
|
|
231
|
+
value = body.get(key)
|
|
232
|
+
return value if isinstance(value, str) and value.strip() else None
|
|
233
|
+
|
|
234
|
+
retry_after = _retry_after_seconds(response, sources=(cast(JsonObject, body),))
|
|
235
|
+
return DbBackpressureRefusal(
|
|
236
|
+
status=response.status_code,
|
|
237
|
+
error_code=DB_BACKPRESSURE_ERROR_CODE,
|
|
238
|
+
component=text("component"),
|
|
239
|
+
work_class=text("work_class"),
|
|
240
|
+
retry_after_seconds=retry_after,
|
|
241
|
+
operator_action=text("operator_action"),
|
|
242
|
+
message=text("message") or "database work rejected before starting",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
209
246
|
def _created_search(
|
|
210
247
|
response: httpx.Response, sources: tuple[JsonObject, ...]
|
|
211
248
|
) -> ResourceRef | None:
|
|
@@ -394,6 +431,12 @@ class HttpTransport:
|
|
|
394
431
|
error_handler: ErrorHandler = raise_http_error
|
|
395
432
|
exception_handler: ExceptionHandler = raise_transport_exception
|
|
396
433
|
decode_error_handler: DecodeErrorHandler = raise_json_decode_error
|
|
434
|
+
db_backpressure_policy: DbBackpressureRetryPolicy = field(
|
|
435
|
+
default_factory=DbBackpressureRetryPolicy
|
|
436
|
+
)
|
|
437
|
+
sleep: Callable[[float], None] = time.sleep
|
|
438
|
+
monotonic: Callable[[], float] = time.monotonic
|
|
439
|
+
rng: random.Random = field(default_factory=random.Random, repr=False)
|
|
397
440
|
client: httpx.Client = field(init=False, repr=False)
|
|
398
441
|
|
|
399
442
|
def __post_init__(self) -> None:
|
|
@@ -420,19 +463,44 @@ class HttpTransport:
|
|
|
420
463
|
timeout_seconds: float | None = None,
|
|
421
464
|
operation_id: str | None = None,
|
|
422
465
|
) -> JsonValue:
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
466
|
+
replay_safe = request_permits_replay(method, {**self.headers, **(headers or {})})
|
|
467
|
+
attempts = 0
|
|
468
|
+
waited_seconds = 0.0
|
|
469
|
+
while True:
|
|
470
|
+
try:
|
|
471
|
+
response = self.client.request(
|
|
472
|
+
method,
|
|
473
|
+
path,
|
|
474
|
+
params=cast(Any, params),
|
|
475
|
+
json=json_body,
|
|
476
|
+
headers=headers,
|
|
477
|
+
timeout=self.timeout_seconds if timeout_seconds is None else timeout_seconds,
|
|
478
|
+
)
|
|
479
|
+
except httpx.TimeoutException as exc:
|
|
480
|
+
self.exception_handler(method, path, exc, operation_id)
|
|
481
|
+
except httpx.TransportError as exc:
|
|
482
|
+
self.exception_handler(method, path, exc, operation_id)
|
|
483
|
+
attempts += 1
|
|
484
|
+
refusal = db_backpressure_refusal(response) if replay_safe else None
|
|
485
|
+
if refusal is None:
|
|
486
|
+
break
|
|
487
|
+
delay = self.db_backpressure_policy.next_delay_seconds(
|
|
488
|
+
attempts_made=attempts,
|
|
489
|
+
waited_seconds=waited_seconds,
|
|
490
|
+
refusal=refusal,
|
|
491
|
+
rng=self.rng,
|
|
431
492
|
)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
493
|
+
if delay is None:
|
|
494
|
+
raise DbBackpressureRetryExhaustedError(
|
|
495
|
+
method=method,
|
|
496
|
+
path=path,
|
|
497
|
+
operation_id=operation_id,
|
|
498
|
+
refusal=refusal,
|
|
499
|
+
attempts=attempts,
|
|
500
|
+
waited_seconds=waited_seconds,
|
|
501
|
+
)
|
|
502
|
+
self.sleep(delay)
|
|
503
|
+
waited_seconds += delay
|
|
436
504
|
if allow_not_found and response.status_code == 404:
|
|
437
505
|
return None
|
|
438
506
|
if response.is_error:
|
|
@@ -598,6 +666,7 @@ class HttpTransport:
|
|
|
598
666
|
|
|
599
667
|
|
|
600
668
|
__all__ = [
|
|
669
|
+
"db_backpressure_refusal",
|
|
601
670
|
"ErrorHandler",
|
|
602
671
|
"ExceptionHandler",
|
|
603
672
|
"DecodeErrorHandler",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
6
8
|
import os
|
|
7
9
|
import re
|
|
8
10
|
from collections.abc import Iterable, Mapping
|
|
@@ -1018,6 +1020,22 @@ def _code_bundle_upload_payload(
|
|
|
1018
1020
|
return payload
|
|
1019
1021
|
|
|
1020
1022
|
|
|
1023
|
+
def _code_bundle_upload_idempotency_key(project_id: str, payload: Mapping[str, Any]) -> str:
|
|
1024
|
+
"""Content-derived replay identity for one code-bundle upload.
|
|
1025
|
+
|
|
1026
|
+
The backend route is convergent on identical content: ``upload_id`` hashes
|
|
1027
|
+
project+filename+bytes, and committing an identical tree is a no-op
|
|
1028
|
+
(``_apply_files_commit`` returns ``committed=False``). A replay with the same
|
|
1029
|
+
project and payload therefore cannot apply a second, different effect.
|
|
1030
|
+
"""
|
|
1031
|
+
canonical = json.dumps(
|
|
1032
|
+
{"project_id": project_id, "payload": dict(payload)},
|
|
1033
|
+
sort_keys=True,
|
|
1034
|
+
separators=(",", ":"),
|
|
1035
|
+
)
|
|
1036
|
+
return "code-bundle-upload:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
1037
|
+
|
|
1038
|
+
|
|
1021
1039
|
@dataclass
|
|
1022
1040
|
class ResearchSession(ManagedResearchRunAuthorityMixin):
|
|
1023
1041
|
"""Managed Research client implementation."""
|
|
@@ -3051,19 +3069,23 @@ class ResearchSession(ManagedResearchRunAuthorityMixin):
|
|
|
3051
3069
|
default_branch: str = "main",
|
|
3052
3070
|
metadata: Mapping[str, Any] | None = None,
|
|
3053
3071
|
) -> dict[str, Any]:
|
|
3072
|
+
payload = _code_bundle_upload_payload(
|
|
3073
|
+
bundle_path,
|
|
3074
|
+
filename=filename,
|
|
3075
|
+
content_type=content_type,
|
|
3076
|
+
source_kind=source_kind,
|
|
3077
|
+
commit_message=commit_message,
|
|
3078
|
+
default_branch=default_branch,
|
|
3079
|
+
metadata=metadata,
|
|
3080
|
+
)
|
|
3054
3081
|
return _coerce_dict(
|
|
3055
3082
|
self._request_json(
|
|
3056
3083
|
"POST",
|
|
3057
3084
|
f"/smr/projects/{project_id}/workspace/code-bundle:upload",
|
|
3058
|
-
json_body=
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
source_kind=source_kind,
|
|
3063
|
-
commit_message=commit_message,
|
|
3064
|
-
default_branch=default_branch,
|
|
3065
|
-
metadata=metadata,
|
|
3066
|
-
),
|
|
3085
|
+
json_body=payload,
|
|
3086
|
+
headers={
|
|
3087
|
+
"Idempotency-Key": _code_bundle_upload_idempotency_key(project_id, payload)
|
|
3088
|
+
},
|
|
3067
3089
|
),
|
|
3068
3090
|
label="upload_project_code_bundle",
|
|
3069
3091
|
)
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"""Classified retry policy shared by sync and async transports.
|
|
2
|
-
|
|
3
|
-
# See: testing/specifications/sdk/core_research_migration.md
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
import math
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
|
|
11
|
-
from synth_ai.core.errors import ContractMismatchError, RuntimeUnavailableError, SynthError
|
|
12
|
-
from synth_ai.core.http.request import HttpMethod, HttpRequest, OperationMetadata
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass(frozen=True, slots=True)
|
|
16
|
-
class RetryPolicy:
|
|
17
|
-
attempts_max: int = 3
|
|
18
|
-
delay_seconds_initial: float = 0.5
|
|
19
|
-
delay_seconds_max: float = 5.0
|
|
20
|
-
retry_statuses: frozenset[int] = frozenset({408, 409, 429, 500, 502, 503, 504})
|
|
21
|
-
|
|
22
|
-
def __post_init__(self) -> None:
|
|
23
|
-
if (
|
|
24
|
-
isinstance(self.attempts_max, bool)
|
|
25
|
-
or not isinstance(self.attempts_max, int)
|
|
26
|
-
or not 1 <= self.attempts_max <= 100
|
|
27
|
-
):
|
|
28
|
-
raise ValueError("attempts_max must be an integer from 1 through 100")
|
|
29
|
-
for delay in (self.delay_seconds_initial, self.delay_seconds_max):
|
|
30
|
-
if (
|
|
31
|
-
isinstance(delay, bool)
|
|
32
|
-
or not isinstance(delay, (int, float))
|
|
33
|
-
or not math.isfinite(delay)
|
|
34
|
-
):
|
|
35
|
-
raise ValueError("retry delays must be finite numbers")
|
|
36
|
-
if self.delay_seconds_initial < 0 or self.delay_seconds_max < 0:
|
|
37
|
-
raise ValueError("retry delays must be non-negative")
|
|
38
|
-
if self.delay_seconds_initial > self.delay_seconds_max:
|
|
39
|
-
raise ValueError("delay_seconds_initial cannot exceed delay_seconds_max")
|
|
40
|
-
|
|
41
|
-
def permits(
|
|
42
|
-
self,
|
|
43
|
-
operation: OperationMetadata,
|
|
44
|
-
*,
|
|
45
|
-
idempotency_key: str | None,
|
|
46
|
-
) -> bool:
|
|
47
|
-
"""Retry only metadata-declared idempotent operations.
|
|
48
|
-
|
|
49
|
-
Unsafe methods additionally require an idempotency key.
|
|
50
|
-
"""
|
|
51
|
-
if not operation.idempotent:
|
|
52
|
-
return False
|
|
53
|
-
if operation.method is HttpMethod.GET:
|
|
54
|
-
return True
|
|
55
|
-
return bool(idempotency_key)
|
|
56
|
-
|
|
57
|
-
def delay_seconds(
|
|
58
|
-
self,
|
|
59
|
-
attempt_index: int,
|
|
60
|
-
*,
|
|
61
|
-
retry_after_seconds: float | None,
|
|
62
|
-
) -> float:
|
|
63
|
-
"""Deterministic exponential backoff capped by policy and Retry-After."""
|
|
64
|
-
if attempt_index < 0:
|
|
65
|
-
raise ValueError("attempt_index must be non-negative")
|
|
66
|
-
exponential = min(
|
|
67
|
-
self.delay_seconds_max,
|
|
68
|
-
self.delay_seconds_initial * (2**attempt_index),
|
|
69
|
-
)
|
|
70
|
-
if retry_after_seconds is None:
|
|
71
|
-
return exponential
|
|
72
|
-
if not math.isfinite(retry_after_seconds) or retry_after_seconds < 0:
|
|
73
|
-
raise ValueError("retry_after_seconds must be non-negative")
|
|
74
|
-
return min(self.delay_seconds_max, max(exponential, retry_after_seconds))
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def idempotency_key_from_request(request: HttpRequest) -> str | None:
|
|
78
|
-
"""Extract an idempotency key from headers or JSON body fields."""
|
|
79
|
-
values = [value for name, value in request.headers.items() if name.lower() == "idempotency-key"]
|
|
80
|
-
if isinstance(request.body, dict):
|
|
81
|
-
values.extend(
|
|
82
|
-
request.body[name]
|
|
83
|
-
for name in ("idempotency_key", "idempotency_key_run_create")
|
|
84
|
-
if name in request.body
|
|
85
|
-
)
|
|
86
|
-
identities = set()
|
|
87
|
-
for value in values:
|
|
88
|
-
if not isinstance(value, str) or not value.strip():
|
|
89
|
-
raise ValueError("idempotency identity must be a non-empty string")
|
|
90
|
-
identities.add(value)
|
|
91
|
-
if len(identities) > 1:
|
|
92
|
-
raise ValueError("request contains conflicting idempotency identities")
|
|
93
|
-
return next(iter(identities), None)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def should_retry_failure(
|
|
97
|
-
policy: RetryPolicy,
|
|
98
|
-
request: HttpRequest,
|
|
99
|
-
error: BaseException,
|
|
100
|
-
) -> bool:
|
|
101
|
-
"""Decide whether one failed execute attempt may be retried."""
|
|
102
|
-
if isinstance(error, (ContractMismatchError, RuntimeUnavailableError)):
|
|
103
|
-
return False
|
|
104
|
-
if not policy.permits(
|
|
105
|
-
request.operation,
|
|
106
|
-
idempotency_key=idempotency_key_from_request(request),
|
|
107
|
-
):
|
|
108
|
-
return False
|
|
109
|
-
if isinstance(error, SynthError):
|
|
110
|
-
if error.failure is not None:
|
|
111
|
-
# The first-class directive is authoritative, including an explicit false.
|
|
112
|
-
return error.failure.retry.retryable
|
|
113
|
-
if error.retryable:
|
|
114
|
-
return True
|
|
115
|
-
status = None
|
|
116
|
-
if error.failure is not None:
|
|
117
|
-
status = error.failure.status
|
|
118
|
-
if status is None and hasattr(error, "status"):
|
|
119
|
-
status = error.status
|
|
120
|
-
return isinstance(status, int) and status in policy.retry_statuses
|
|
121
|
-
return False
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def retry_after_from_error(error: BaseException) -> float | None:
|
|
125
|
-
if isinstance(error, SynthError):
|
|
126
|
-
return error.retry_after_seconds
|
|
127
|
-
return None
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
__all__ = [
|
|
131
|
-
"RetryPolicy",
|
|
132
|
-
"idempotency_key_from_request",
|
|
133
|
-
"retry_after_from_error",
|
|
134
|
-
"should_retry_failure",
|
|
135
|
-
]
|
|
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
|