langgraph-api 0.18.0.dev3__tar.gz → 0.18.0.dev4__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.
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/PKG-INFO +1 -1
- langgraph_api-0.18.0.dev4/langgraph_api/__init__.py +1 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/event_streaming.py +71 -7
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/config/__init__.py +2 -4
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/drain.py +64 -2
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/service.py +75 -129
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/session.py +11 -6
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/threads.py +54 -6
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/models/run.py +7 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/headers.py +5 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/validation.py +6 -6
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/openapi.json +20 -0
- langgraph_api-0.18.0.dev3/langgraph_api/__init__.py +0 -1
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/.gitignore +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/LICENSE +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/Makefile +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/README.md +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/.gitignore +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/Makefile +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/README.md +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/REPORTS.md +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/assistant.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/benchmark-runner.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/benchmark_profiles.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/benchmarks.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/cancel_first_second_completes.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/classify-error.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/classify-error.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/delta-turns-lib.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/delta-turns-lib.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/delta_turns.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/enqueued_runs_order.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/interrupt_resume.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/log-failure.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/meta_workload.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/store.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/stream_write.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/thread.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/thread_runs_metadata_search.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/threads_search_metadata.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/types.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/benchmark-runners/wait_write.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/capacity-ci.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/capacity-ci.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/clean-cli.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/clean.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/continuous/README.md +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/continuous/pyproject.toml +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/continuous/runner.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/continuous/uv.lock +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/daily-report.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/daily-report.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/delta-report.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/delta-report.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/graphs.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/history.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/history.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/host-v2.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/host-v2.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/multer.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/package.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/ramp.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/reporting/dd_reporting.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/runner-descriptions.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/server-metrics.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/server-metrics.test.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/slack.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/staircase.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/staircase_step_k6.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/tsconfig.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/update-revision.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/weather.js +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/benchmark/yarn.lock +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/constraints.txt +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/custom_store.sql +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/forbidden.txt +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/hatch_build.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/healthcheck.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph-cloud-debugging-20260210132856.zip +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/_checkpointer/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/_checkpointer/_adapter.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/_checkpointer/protocol.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/_factory_utils.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2a.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui_schemas/README.md +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui_schemas/client_capabilities.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui_schemas/client_data_model.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui_schemas/client_to_server.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/a2ui_schemas/server_to_client.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_calls.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_constants.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_handlers.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_models.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_outcome.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_protocol.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_routes.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/mcp/_sanitizers.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/profile.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/runs.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/ui.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/asgi_transport.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/custom.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/errors.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/middleware.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/auth/studio_user.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/beacon_data_plane_jwt_auth.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/cache.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/cli.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/command.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/config/_parse.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/config/schemas.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/aes_json.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/context.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/custom.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/middleware.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/encryption/shared.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/capabilities.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/constants.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/event_normalizers.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/namespace.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/state_normalizers.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/types.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/executor/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/executor/__main__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/feature_flags.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/graph.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/client.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/generated/core_api_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/assistants.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/cache.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/crons.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/ops/runs.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/server.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/servicers/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/servicers/checkpointer.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/grpc/servicers/encryption.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/http.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/http_metrics.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/http_metrics_utils.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/.prettierrc +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/.yarnclean +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/base.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/client.http.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/errors.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/package.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/schema.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/auth-mutations.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/load.hooks.mjs +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/preload.mjs +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/utils/experiment-tracing.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/utils/files.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/sse.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/traceblock.mts +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/tsconfig.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/ui.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/js/yarn.lock +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/lc_security/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/lc_security/exceptions.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/lc_security/policy.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/lc_security/transport.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/logging.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/metadata.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/metrics_collector.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/metrics_otlp.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/middleware/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/middleware/ensure_store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/middleware/http_logger.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/middleware/private_network.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/middleware/request_id.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/otel_context.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/queue_entrypoint.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/release_tags.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/route.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/schema.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/self_hosted_logs.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/self_hosted_metrics.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/serde.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/server.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/state.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/stream.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/stream_v2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/timing/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/timing/profiler.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/timing/startup.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/timing/timer.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/traceblock.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/tracing_session.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/tunneling/cloudflare.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/cache.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/config.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/errors.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/extract.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/future.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/network.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/retriable_client.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/stream_codec.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/utils/uuids.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/webhook.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/worker.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/worker_runtime.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/checkpointer.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/_compat.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/checkpoint.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/config.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/durability.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/struct.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/conversion/value.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/checkpointer_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/checkpointer_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/core_api_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/core_api_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/core_api_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/core_api_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/encryption_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/encryption_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/encryption_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/encryption_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/engine_common_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/engine_common_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/engine_common_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/engine_common_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_control_signal_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_control_signal_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_durability_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_durability_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_run_status_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_run_status_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_stream_mode_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_stream_mode_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_status_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_status_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/errors_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/errors_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/errors_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/errors_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/orchestrator_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/orchestrator_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/orchestrator_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/orchestrator_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/store_pb2.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/store_pb2.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/store_pb2_grpc.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/proto/store_pb2_grpc.pyi +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/serde.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_grpc_common/store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_license/validation.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/__init__.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/checkpoint.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/database.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/encryption.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/lifespan.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/metrics.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/ops.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/queue.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/retry.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/routes.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_runtime/store.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/logging.json +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/pyproject.toml +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/scripts/build_wheel.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/scripts/create_license.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/scripts/run_a2a_tck.py +0 -0
- {langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.18.0.dev4"
|
{langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/api/event_streaming.py
RENAMED
|
@@ -28,6 +28,7 @@ from uuid import uuid4
|
|
|
28
28
|
|
|
29
29
|
import orjson
|
|
30
30
|
import structlog
|
|
31
|
+
from starlette.exceptions import HTTPException
|
|
31
32
|
from starlette.responses import Response
|
|
32
33
|
from starlette.websockets import WebSocketDisconnect
|
|
33
34
|
|
|
@@ -38,6 +39,7 @@ from langgraph_api.feature_flags import IS_POSTGRES_OR_GRPC_BACKEND
|
|
|
38
39
|
from langgraph_api.route import ApiRoute, ApiWebSocketRoute
|
|
39
40
|
from langgraph_api.serde import json_dumpb
|
|
40
41
|
from langgraph_api.sse import EventSourceResponse
|
|
42
|
+
from langgraph_api.utils import validate_uuid
|
|
41
43
|
|
|
42
44
|
if TYPE_CHECKING:
|
|
43
45
|
from collections.abc import Mapping
|
|
@@ -98,6 +100,7 @@ def _make_manager(
|
|
|
98
100
|
send_event: Any = None,
|
|
99
101
|
*,
|
|
100
102
|
headers: Mapping[str, str] | None = None,
|
|
103
|
+
replay_buffer: bool = True,
|
|
101
104
|
) -> ThreadRunManager:
|
|
102
105
|
# Auth context is propagated via ``ApiRoute``'s ``with_user`` wrapper
|
|
103
106
|
# — ops reads it off the context var, so we don't thread it through
|
|
@@ -108,6 +111,7 @@ def _make_manager(
|
|
|
108
111
|
threads=Threads,
|
|
109
112
|
send_event=send_event,
|
|
110
113
|
headers=headers,
|
|
114
|
+
replay_buffer=replay_buffer,
|
|
111
115
|
)
|
|
112
116
|
|
|
113
117
|
|
|
@@ -143,8 +147,12 @@ async def _thread_events(request: Request) -> Response:
|
|
|
143
147
|
If reading the thread's event stream fails, the server ends the
|
|
144
148
|
stream with a final ``event: error`` frame carrying a protocol error
|
|
145
149
|
(``"type": "error"``, ``"id": null``, ``"error": "unknown_error"``).
|
|
150
|
+
|
|
151
|
+
A thread the caller isn't allowed to read returns 403 or 404 before
|
|
152
|
+
the stream starts, and a malformed thread ID returns 422.
|
|
146
153
|
"""
|
|
147
154
|
thread_id = request.path_params["thread_id"]
|
|
155
|
+
thread_uuid = validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
148
156
|
try:
|
|
149
157
|
body = orjson.loads(await request.body())
|
|
150
158
|
except Exception:
|
|
@@ -217,6 +225,10 @@ async def _thread_events(request: Request) -> Response:
|
|
|
217
225
|
else None
|
|
218
226
|
)
|
|
219
227
|
|
|
228
|
+
# Checked here, before the 200, because clients retry a 200 that ends in
|
|
229
|
+
# an error instead of giving up.
|
|
230
|
+
auth_filters = await Threads.Stream.check_thread_stream_auth(thread_uuid)
|
|
231
|
+
|
|
220
232
|
# Bounded LRU dedup: ``recent_eids`` is the eviction order, ``delivered``
|
|
221
233
|
# is the O(1) membership view. When the deque hits its cap, the oldest
|
|
222
234
|
# entry rolls off; we mirror that on ``delivered`` to keep them in sync.
|
|
@@ -241,7 +253,10 @@ async def _thread_events(request: Request) -> Response:
|
|
|
241
253
|
pending_events.append(event)
|
|
242
254
|
flush_pending.set()
|
|
243
255
|
|
|
244
|
-
|
|
256
|
+
# SSE never replays from the session's buffer: it has no subscribe or
|
|
257
|
+
# reconnect commands, and ``start_thread_stream`` replays from the
|
|
258
|
+
# thread's event stream.
|
|
259
|
+
manager = _make_manager(thread_id, send_event=on_event, replay_buffer=False)
|
|
245
260
|
|
|
246
261
|
filter_sub = Subscription(
|
|
247
262
|
id=str(uuid4()),
|
|
@@ -264,7 +279,7 @@ async def _thread_events(request: Request) -> Response:
|
|
|
264
279
|
# outbound events; the thread-level reader always replays from the
|
|
265
280
|
# beginning so namespace-scoped projections see history. See
|
|
266
281
|
# ``ThreadRunManager.start_thread_stream`` for the rationale.
|
|
267
|
-
manager.start_thread_stream()
|
|
282
|
+
manager.start_thread_stream(auth_filters=auth_filters)
|
|
268
283
|
|
|
269
284
|
async def body_iter():
|
|
270
285
|
try:
|
|
@@ -377,20 +392,51 @@ async def _thread_command(request: Request) -> Response:
|
|
|
377
392
|
# ---------------------------------------------------------------------------
|
|
378
393
|
|
|
379
394
|
|
|
395
|
+
async def _close_for_failed_access_check(websocket: WebSocket, exc: Exception) -> None:
|
|
396
|
+
if isinstance(exc, HTTPException) and exc.status_code < 500:
|
|
397
|
+
await websocket.close(code=4000 + exc.status_code)
|
|
398
|
+
return
|
|
399
|
+
logger.exception("Thread stream access check failed")
|
|
400
|
+
await websocket.send_text(_THREAD_STREAM_FAILED_DATA.decode())
|
|
401
|
+
await websocket.close(code=1011, reason="thread stream ended")
|
|
402
|
+
|
|
403
|
+
|
|
380
404
|
async def _thread_websocket(websocket: WebSocket) -> None:
|
|
381
405
|
"""Full-duplex protocol connection scoped to a thread.
|
|
382
406
|
|
|
383
407
|
Supports ``run.start``, ``input.respond``, ``subscription.subscribe``,
|
|
384
408
|
``subscription.unsubscribe``, ``agent.getTree``.
|
|
409
|
+
|
|
410
|
+
A 4xx from the thread's access check closes the socket with 4000 plus the
|
|
411
|
+
status (4403, 4422). A 404 can be a thread the client minted and has yet
|
|
412
|
+
to create, so the socket then waits for a ``run.start``, which checks
|
|
413
|
+
access like any run start, and closes with 4404 if another command comes
|
|
414
|
+
first. Any other failure of the check, a 5xx included, gets the stream
|
|
415
|
+
error frame and a 1011 close, as when the stream fails later.
|
|
385
416
|
"""
|
|
386
417
|
thread_id = websocket.path_params["thread_id"]
|
|
418
|
+
# Browsers report a refused upgrade only as a 1006 close, so accept first
|
|
419
|
+
# to make the close code readable.
|
|
387
420
|
await websocket.accept()
|
|
421
|
+
try:
|
|
422
|
+
thread_uuid = validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
423
|
+
except HTTPException as exc:
|
|
424
|
+
await _close_for_failed_access_check(websocket, exc)
|
|
425
|
+
return
|
|
426
|
+
awaiting_thread = False
|
|
427
|
+
auth_filters: Any = None
|
|
428
|
+
try:
|
|
429
|
+
auth_filters = await Threads.Stream.check_thread_stream_auth(thread_uuid)
|
|
430
|
+
except Exception as exc:
|
|
431
|
+
if not (isinstance(exc, HTTPException) and exc.status_code == 404):
|
|
432
|
+
await _close_for_failed_access_check(websocket, exc)
|
|
433
|
+
return
|
|
434
|
+
awaiting_thread = True
|
|
388
435
|
|
|
389
436
|
async def ws_send_event(event: dict[str, Any]) -> None:
|
|
390
437
|
await websocket.send_text(orjson.dumps(event).decode("utf-8"))
|
|
391
438
|
|
|
392
439
|
manager = _make_manager(thread_id, send_event=ws_send_event)
|
|
393
|
-
manager.start_thread_stream()
|
|
394
440
|
|
|
395
441
|
# Watchdog: when the thread-stream consumer stops, send the error frame
|
|
396
442
|
# (if it wasn't cancelled) and close the socket, instead of leaving the
|
|
@@ -405,7 +451,11 @@ async def _thread_websocket(websocket: WebSocket) -> None:
|
|
|
405
451
|
with contextlib.suppress(Exception):
|
|
406
452
|
await websocket.close(code=1011, reason="thread stream ended")
|
|
407
453
|
|
|
408
|
-
|
|
454
|
+
def start_stream(filters: Any) -> asyncio.Task[None]:
|
|
455
|
+
manager.start_thread_stream(auth_filters=filters)
|
|
456
|
+
return asyncio.create_task(_stream_watchdog())
|
|
457
|
+
|
|
458
|
+
watchdog = None if awaiting_thread else start_stream(auth_filters)
|
|
409
459
|
|
|
410
460
|
try:
|
|
411
461
|
while True:
|
|
@@ -438,16 +488,30 @@ async def _thread_websocket(websocket: WebSocket) -> None:
|
|
|
438
488
|
)
|
|
439
489
|
continue
|
|
440
490
|
|
|
491
|
+
if awaiting_thread and payload["method"] != "run.start":
|
|
492
|
+
await websocket.close(code=4404)
|
|
493
|
+
return
|
|
441
494
|
response = await manager.handle_command(payload)
|
|
442
495
|
await websocket.send_text(orjson.dumps(response).decode("utf-8"))
|
|
496
|
+
if awaiting_thread and response.get("type") == "success":
|
|
497
|
+
try:
|
|
498
|
+
auth_filters = await Threads.Stream.check_thread_stream_auth(
|
|
499
|
+
thread_uuid
|
|
500
|
+
)
|
|
501
|
+
except Exception as exc:
|
|
502
|
+
await _close_for_failed_access_check(websocket, exc)
|
|
503
|
+
return
|
|
504
|
+
watchdog = start_stream(auth_filters)
|
|
505
|
+
awaiting_thread = False
|
|
443
506
|
except WebSocketDisconnect:
|
|
444
507
|
pass
|
|
445
508
|
except Exception:
|
|
446
509
|
logger.exception("Protocol WebSocket error")
|
|
447
510
|
finally:
|
|
448
|
-
watchdog
|
|
449
|
-
|
|
450
|
-
|
|
511
|
+
if watchdog is not None:
|
|
512
|
+
watchdog.cancel()
|
|
513
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
514
|
+
await watchdog
|
|
451
515
|
await manager.close()
|
|
452
516
|
|
|
453
517
|
|
|
@@ -507,7 +507,8 @@ FF_CRONS_ENABLED = env("FF_CRONS_ENABLED", cast=bool, default=True)
|
|
|
507
507
|
FF_LOG_DROPPED_EVENTS = env("FF_LOG_DROPPED_EVENTS", cast=bool, default=False)
|
|
508
508
|
FF_LOG_QUERY_AND_PARAMS = env("FF_LOG_QUERY_AND_PARAMS", cast=bool, default=False)
|
|
509
509
|
# Opt-in handling of drained runs: the server's control signalling to the
|
|
510
|
-
# graph and its handling of drained runs, together.
|
|
510
|
+
# graph and its handling of drained runs, together. On, a Postgres-backed
|
|
511
|
+
# replica that is shutting down also drains its in-flight runs.
|
|
511
512
|
# Off: no control is created or seeded, and GraphDrained lands in the
|
|
512
513
|
# catch-all error branch.
|
|
513
514
|
FF_RESUME_DRAINED_RUNS = env("FF_RESUME_DRAINED_RUNS", cast=bool, default=False)
|
|
@@ -535,8 +536,6 @@ RESUME_DRAINED_RUNS = _resolve_resume_drained_runs(
|
|
|
535
536
|
is_postgres=IS_POSTGRES_BACKEND,
|
|
536
537
|
langgraph_supported=DRAIN_RESUME_SUPPORT,
|
|
537
538
|
)
|
|
538
|
-
# Not in public docs: internal feature flag
|
|
539
|
-
FF_USE_REDIS_QUEUE = env("FF_USE_REDIS_QUEUE", cast=bool, default=True)
|
|
540
539
|
|
|
541
540
|
|
|
542
541
|
# Internal flag intended for testing only
|
|
@@ -811,7 +810,6 @@ __all__ = [
|
|
|
811
810
|
"FF_PYSPY_PROFILING_ENABLED",
|
|
812
811
|
"FF_PYSPY_PROFILING_MAX_DURATION_SECS",
|
|
813
812
|
"FF_RESUME_DRAINED_RUNS",
|
|
814
|
-
"FF_USE_REDIS_QUEUE",
|
|
815
813
|
"GRPC_CLIENT_HTTP2_INITIAL_WINDOW_SIZE",
|
|
816
814
|
"GRPC_CLIENT_MAX_RECV_MSG_BYTES",
|
|
817
815
|
"GRPC_CLIENT_MAX_SEND_MSG_BYTES",
|
|
@@ -10,6 +10,7 @@ guarded and the placeholders below never match or instantiate anything.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import contextvars
|
|
13
|
+
import threading
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
15
16
|
# The control of the run whose top-level graph this context is executing. The
|
|
@@ -47,17 +48,35 @@ try:
|
|
|
47
48
|
The request is visible only where this is provably the run's top-level
|
|
48
49
|
loop: in a context the server marked for this control (see
|
|
49
50
|
mark_top_level) and outside any graph node. So the run drains only at
|
|
50
|
-
a top-level step boundary, after that step's subgraphs have finished
|
|
51
|
+
a top-level step boundary, after that step's subgraphs have finished,
|
|
52
|
+
and never before its first step.
|
|
51
53
|
Graph code inside a node, and threads a node starts, see no drain
|
|
52
54
|
(drain_requested False, drain_reason None).
|
|
53
55
|
"""
|
|
54
56
|
|
|
57
|
+
__slots__ = ("_first_check_done",)
|
|
58
|
+
|
|
59
|
+
def __init__(self) -> None:
|
|
60
|
+
super().__init__()
|
|
61
|
+
self._first_check_done = False
|
|
62
|
+
|
|
55
63
|
def _visible(self) -> bool:
|
|
56
64
|
return _TOP_LEVEL_CONTROL.get() is self and not _in_graph_node()
|
|
57
65
|
|
|
58
66
|
@property
|
|
59
67
|
def drain_requested(self) -> bool:
|
|
60
|
-
|
|
68
|
+
if not self._visible():
|
|
69
|
+
return False
|
|
70
|
+
# LangGraph checks once per top-level step boundary, before the
|
|
71
|
+
# step. Only a server drain can be pending at the first check. A
|
|
72
|
+
# run started with a Command has no checkpoint of its own until its
|
|
73
|
+
# first step ends, so if it drained there, its next claim would
|
|
74
|
+
# re-send the Command and LangGraph would apply it again. So a run
|
|
75
|
+
# always completes a step before it drains.
|
|
76
|
+
if not self._first_check_done:
|
|
77
|
+
self._first_check_done = True
|
|
78
|
+
return False
|
|
79
|
+
return RunControl.drain_requested.fget(self)
|
|
61
80
|
|
|
62
81
|
@property
|
|
63
82
|
def drain_reason(self) -> str | None:
|
|
@@ -79,6 +98,11 @@ except ImportError:
|
|
|
79
98
|
|
|
80
99
|
DRAIN_SUPPORT = False
|
|
81
100
|
|
|
101
|
+
# Reason the server passes to request_drain() when its pod is shutting down.
|
|
102
|
+
# Graph code can pass any reason (request_drain() defaults to "shutdown"), so
|
|
103
|
+
# this distinct value is what tells server drains from graph drains in logs.
|
|
104
|
+
SERVER_SHUTDOWN_DRAIN_REASON = "server_shutdown"
|
|
105
|
+
|
|
82
106
|
|
|
83
107
|
def mark_top_level(control: ServerRunControl | None) -> None:
|
|
84
108
|
"""Mark the current context as running ``control``'s top-level graph."""
|
|
@@ -92,10 +116,48 @@ def new_run_control() -> ServerRunControl | None:
|
|
|
92
116
|
return None
|
|
93
117
|
|
|
94
118
|
|
|
119
|
+
class RunControlRegistry:
|
|
120
|
+
"""Controls of the runs a queue drains at shutdown, keyed by run ID.
|
|
121
|
+
|
|
122
|
+
Workers can finish on background threads (isolated loops), so every access
|
|
123
|
+
takes a lock and ``drain_all`` works on a snapshot. Controls are registered
|
|
124
|
+
before the run starts, and removed only by the claim that registered them,
|
|
125
|
+
so a fast-finishing or superseded claim can't leave or remove the wrong
|
|
126
|
+
entry.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self) -> None:
|
|
130
|
+
self._lock = threading.Lock()
|
|
131
|
+
self._controls: dict[str, ServerRunControl] = {}
|
|
132
|
+
|
|
133
|
+
def add(self, run_id: Any, control: ServerRunControl) -> None:
|
|
134
|
+
"""Register a run's control before the run starts."""
|
|
135
|
+
with self._lock:
|
|
136
|
+
self._controls[str(run_id)] = control
|
|
137
|
+
|
|
138
|
+
def release(self, run_id: Any, control: ServerRunControl | None) -> None:
|
|
139
|
+
"""Remove a run's control if it is still the one this claim registered."""
|
|
140
|
+
if control is None:
|
|
141
|
+
return
|
|
142
|
+
with self._lock:
|
|
143
|
+
if self._controls.get(str(run_id)) is control:
|
|
144
|
+
del self._controls[str(run_id)]
|
|
145
|
+
|
|
146
|
+
def drain_all(self, reason: str) -> int:
|
|
147
|
+
"""Request a drain from every registered run; return how many."""
|
|
148
|
+
with self._lock:
|
|
149
|
+
controls = list(self._controls.values())
|
|
150
|
+
for control in controls:
|
|
151
|
+
control.request_drain(reason)
|
|
152
|
+
return len(controls)
|
|
153
|
+
|
|
154
|
+
|
|
95
155
|
__all__ = [
|
|
96
156
|
"DRAIN_SUPPORT",
|
|
157
|
+
"SERVER_SHUTDOWN_DRAIN_REASON",
|
|
97
158
|
"GraphDrained",
|
|
98
159
|
"RunControl",
|
|
160
|
+
"RunControlRegistry",
|
|
99
161
|
"ServerRunControl",
|
|
100
162
|
"mark_top_level",
|
|
101
163
|
"new_run_control",
|
{langgraph_api-0.18.0.dev3 → langgraph_api-0.18.0.dev4}/langgraph_api/event_streaming/service.py
RENAMED
|
@@ -22,7 +22,6 @@ import asyncio
|
|
|
22
22
|
import contextlib
|
|
23
23
|
import inspect
|
|
24
24
|
import re
|
|
25
|
-
import time
|
|
26
25
|
from collections.abc import Awaitable, Callable, Mapping
|
|
27
26
|
from typing import Any, cast, get_args
|
|
28
27
|
from uuid import UUID, uuid4
|
|
@@ -46,6 +45,7 @@ from langgraph_api.event_streaming.session import (
|
|
|
46
45
|
from langgraph_api.event_streaming.types import Subscription
|
|
47
46
|
from langgraph_api.feature_flags import FF_V2_EVENT_STREAMING, PREFER_GRPC_CHECKPOINTER
|
|
48
47
|
from langgraph_api.schema import MultitaskStrategy
|
|
48
|
+
from langgraph_api.validation import sanitize_reserved_keys
|
|
49
49
|
|
|
50
50
|
logger = structlog.stdlib.get_logger(__name__)
|
|
51
51
|
|
|
@@ -145,10 +145,7 @@ def _multitask_strategy_from_run_start(params: dict[str, Any]) -> str:
|
|
|
145
145
|
EventSink = Callable[[dict[str, Any]], Awaitable[None] | None]
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
# ``set_joint_status`` commits interrupts after the stream event; gRPC can lag.
|
|
149
148
|
_IN_FLIGHT_THREAD_STATUS = "busy"
|
|
150
|
-
_INTERRUPT_SETTLE_TIMEOUT_SECONDS = 5.0
|
|
151
|
-
_INTERRUPT_SETTLE_POLL_INTERVAL_SECONDS = 0.05
|
|
152
149
|
# langgraph resumes interrupts by id only when every key of the resume map has
|
|
153
150
|
# this shape; any other key turns the whole map into one value for whichever
|
|
154
151
|
# interrupt is pending.
|
|
@@ -192,6 +189,9 @@ class ThreadRunManager:
|
|
|
192
189
|
One ``ThreadRunManager`` is created per connection (HTTP SSE stream or
|
|
193
190
|
WebSocket). It is not shared across connections — each observer owns
|
|
194
191
|
its own view of the thread's run stream.
|
|
192
|
+
|
|
193
|
+
``replay_buffer=False`` keeps no per-run replay buffer, for connections
|
|
194
|
+
that never replay from it.
|
|
195
195
|
"""
|
|
196
196
|
|
|
197
197
|
def __init__(
|
|
@@ -202,12 +202,14 @@ class ThreadRunManager:
|
|
|
202
202
|
threads: Any,
|
|
203
203
|
send_event: EventSink | None = None,
|
|
204
204
|
headers: Mapping[str, str] | None = None,
|
|
205
|
+
replay_buffer: bool = True,
|
|
205
206
|
) -> None:
|
|
206
207
|
self._thread_id = thread_id
|
|
207
208
|
self._runs = runs
|
|
208
209
|
self._threads = threads
|
|
209
210
|
self._send_event = send_event
|
|
210
211
|
self._headers = headers if headers is not None else {}
|
|
212
|
+
self._replay_buffer = replay_buffer
|
|
211
213
|
self._session: EventStreamingSession | None = None
|
|
212
214
|
self._current_run_id: str | None = None
|
|
213
215
|
self._queued_events: list[dict[str, Any]] = []
|
|
@@ -291,7 +293,7 @@ class ThreadRunManager:
|
|
|
291
293
|
active=True,
|
|
292
294
|
)
|
|
293
295
|
|
|
294
|
-
def start_thread_stream(self) -> None:
|
|
296
|
+
def start_thread_stream(self, *, auth_filters: Any = None) -> None:
|
|
295
297
|
"""Start consuming the thread-level stream for this connection.
|
|
296
298
|
|
|
297
299
|
Always start from the beginning of the thread's event stream
|
|
@@ -306,14 +308,19 @@ class ThreadRunManager:
|
|
|
306
308
|
produce output…". The SSE/WS sink applies the caller's ``since``
|
|
307
309
|
as a seq filter on outbound events, and the SDK dedups by
|
|
308
310
|
``event_id``, so repeat observers don't see duplicates.
|
|
311
|
+
|
|
312
|
+
``auth_filters`` is what the route's ``check_thread_stream_auth``
|
|
313
|
+
returned, passed on so the stream doesn't run the read handler again.
|
|
309
314
|
"""
|
|
310
315
|
if self._thread_source_task is not None:
|
|
311
316
|
return
|
|
312
317
|
self._thread_source_task = asyncio.create_task(
|
|
313
|
-
self._consume_thread_stream(last_event_id="-")
|
|
318
|
+
self._consume_thread_stream(last_event_id="-", auth_filters=auth_filters)
|
|
314
319
|
)
|
|
315
320
|
|
|
316
|
-
async def _consume_thread_stream(
|
|
321
|
+
async def _consume_thread_stream(
|
|
322
|
+
self, *, last_event_id: str | None, auth_filters: Any
|
|
323
|
+
) -> None:
|
|
317
324
|
try:
|
|
318
325
|
async for (
|
|
319
326
|
event,
|
|
@@ -324,6 +331,7 @@ class ThreadRunManager:
|
|
|
324
331
|
self._thread_id,
|
|
325
332
|
stream_modes=["run_modes", "lifecycle"],
|
|
326
333
|
last_event_id=last_event_id,
|
|
334
|
+
auth_filters=auth_filters,
|
|
327
335
|
):
|
|
328
336
|
if not run_id or run_id == "*":
|
|
329
337
|
continue
|
|
@@ -781,40 +789,15 @@ class ThreadRunManager:
|
|
|
781
789
|
if not capabilities.ok:
|
|
782
790
|
raise ProtocolV2UnsupportedError(capabilities)
|
|
783
791
|
|
|
792
|
+
# Drop server-owned configurable and metadata keys before the run
|
|
793
|
+
# config is built, as the REST request parser does for its bodies.
|
|
794
|
+
sanitize_reserved_keys(params)
|
|
784
795
|
config = params.get("config") if _is_record(params.get("config")) else {}
|
|
785
796
|
|
|
786
|
-
# ``input.respond`` checked its interrupts already and always resumes
|
|
787
|
-
# the lookups below can wait seconds on a busy thread.
|
|
788
|
-
force_resume = params.get("force_resume")
|
|
789
|
-
current_run = None if force_resume else await self._fetch_current_run()
|
|
790
|
-
|
|
791
|
-
current_status = None
|
|
792
|
-
if current_run is not None:
|
|
793
|
-
current_status = (
|
|
794
|
-
current_run["status"]
|
|
795
|
-
if _is_record(current_run)
|
|
796
|
-
else getattr(current_run, "status", None)
|
|
797
|
-
)
|
|
798
|
-
|
|
799
|
-
has_interrupts = False
|
|
800
|
-
if params.get("input") is not None and not force_resume:
|
|
801
|
-
has_interrupts = await self._has_pending_interrupts()
|
|
802
|
-
if has_interrupts and not _keyed_by_interrupt_id(params["input"]):
|
|
803
|
-
# One answer can't say which of several pending interrupts it is
|
|
804
|
-
# for: langgraph fails the run, or picks one when they share a
|
|
805
|
-
# subgraph task. Only a paused thread's row is current, so a run in
|
|
806
|
-
# flight keeps resuming as before.
|
|
807
|
-
paused_on = await self._collect_interrupt_ids_unless_busy()
|
|
808
|
-
if paused_on is not None and len(paused_on) > 1:
|
|
809
|
-
raise _AmbiguousResumeError(
|
|
810
|
-
f"run.start input can't say which of the {len(paused_on)} "
|
|
811
|
-
"pending interrupts it answers; answer them with input.respond."
|
|
812
|
-
)
|
|
813
|
-
|
|
797
|
+
# ``input.respond`` checked its interrupts already and always resumes.
|
|
814
798
|
is_resume = params.get("input") is not None and (
|
|
815
|
-
force_resume
|
|
816
|
-
or (
|
|
817
|
-
or has_interrupts
|
|
799
|
+
bool(params.get("force_resume"))
|
|
800
|
+
or await self._run_start_resumes(params["input"])
|
|
818
801
|
)
|
|
819
802
|
|
|
820
803
|
run_config: dict[str, Any] = {
|
|
@@ -903,6 +886,16 @@ class ThreadRunManager:
|
|
|
903
886
|
except Exception:
|
|
904
887
|
raise
|
|
905
888
|
|
|
889
|
+
# ``input.respond`` clears the interrupts it answered itself.
|
|
890
|
+
if is_resume and not params.get("force_resume") and self._session is not None:
|
|
891
|
+
# Until the stream rebinds to the resumed run, input sent next must
|
|
892
|
+
# not answer these interrupts a second time.
|
|
893
|
+
answered = params["input"]
|
|
894
|
+
if _keyed_by_interrupt_id(answered):
|
|
895
|
+
for interrupt_id in answered:
|
|
896
|
+
self._session.clear_pending_interrupt(interrupt_id)
|
|
897
|
+
else:
|
|
898
|
+
self._session._pending_interrupts.clear()
|
|
906
899
|
return run
|
|
907
900
|
|
|
908
901
|
async def _ensure_run_session(
|
|
@@ -959,6 +952,7 @@ class ThreadRunManager:
|
|
|
959
952
|
get_thread_state=self._make_get_thread_state(thread_id),
|
|
960
953
|
source=None,
|
|
961
954
|
send=self._make_send(),
|
|
955
|
+
max_buffer_size=None if self._replay_buffer else 0,
|
|
962
956
|
)
|
|
963
957
|
|
|
964
958
|
self._session = session
|
|
@@ -1086,19 +1080,6 @@ class ThreadRunManager:
|
|
|
1086
1080
|
# Run lookup
|
|
1087
1081
|
# ------------------------------------------------------------------
|
|
1088
1082
|
|
|
1089
|
-
async def _fetch_current_run(self) -> dict[str, Any] | None:
|
|
1090
|
-
"""Fetch the currently-bound run from storage.
|
|
1091
|
-
|
|
1092
|
-
``Runs.get`` uses keyword-only ``thread_id`` / ``ctx`` args so we
|
|
1093
|
-
cannot call it positionally. We also rely on ``ApiRoute``'s
|
|
1094
|
-
``with_user`` to populate the auth context var instead of
|
|
1095
|
-
passing ``ctx=self._auth`` (which is a starlette
|
|
1096
|
-
``AuthCredentials``, not a ``BaseAuthContext``).
|
|
1097
|
-
"""
|
|
1098
|
-
if self._current_run_id is None:
|
|
1099
|
-
return None
|
|
1100
|
-
return await self._fetch_run_by_id(self._current_run_id)
|
|
1101
|
-
|
|
1102
1083
|
async def _fetch_run_by_id(self, run_id: str) -> dict[str, Any] | None:
|
|
1103
1084
|
"""Fetch a run by id without searching for the latest run."""
|
|
1104
1085
|
from langgraph_runtime.database import connect # noqa: PLC0415
|
|
@@ -1127,41 +1108,6 @@ class ThreadRunManager:
|
|
|
1127
1108
|
# Interrupt detection
|
|
1128
1109
|
# ------------------------------------------------------------------
|
|
1129
1110
|
|
|
1130
|
-
async def _lookup_interrupt_in_thread_state(
|
|
1131
|
-
self, interrupt_id: str
|
|
1132
|
-
) -> list[str] | None:
|
|
1133
|
-
"""Existence check for ``interrupt_id`` against persisted thread state.
|
|
1134
|
-
|
|
1135
|
-
Fallback for ``input.respond`` on stateless HTTP transports, where the
|
|
1136
|
-
fresh session hasn't observed ``input.requested`` yet. Backed by the
|
|
1137
|
-
durable thread-row map (see :meth:`_collect_persisted_interrupt_ids`).
|
|
1138
|
-
|
|
1139
|
-
Returns ``[]`` if the interrupt exists, else ``None``. The ``[]`` is a
|
|
1140
|
-
sentinel, not a namespace — the map carries no namespace, so callers
|
|
1141
|
-
must treat it as "found, namespace unknown" and trust the client claim.
|
|
1142
|
-
"""
|
|
1143
|
-
found = await self._collect_persisted_interrupt_ids()
|
|
1144
|
-
if found is None:
|
|
1145
|
-
return None
|
|
1146
|
-
return [] if interrupt_id in found else None
|
|
1147
|
-
|
|
1148
|
-
async def _collect_persisted_interrupt_ids(self) -> set[str] | None:
|
|
1149
|
-
"""Collect interrupt ids from the durable thread-row ``interrupts`` map.
|
|
1150
|
-
|
|
1151
|
-
``Threads.set_status`` writes ``{task_id: [{id, value}, ...]}`` onto the
|
|
1152
|
-
thread row atomically with the status whenever a run pauses. Reading it
|
|
1153
|
-
is a plain row fetch (same ``connect()`` + ``Threads.get`` path as
|
|
1154
|
-
:meth:`_get_thread_assistant_id`) — unlike ``Threads.State.get`` it does
|
|
1155
|
-
not rebuild the graph, so it can't spuriously raise and reject a
|
|
1156
|
-
legitimate resume, and it survives reconnects/redeploys.
|
|
1157
|
-
|
|
1158
|
-
Returns the set of interrupt ids (empty when none are pending), or
|
|
1159
|
-
``None`` if the fetch failed — callers treat ``None`` as "unavailable"
|
|
1160
|
-
and fall open rather than fabricating ``no_such_interrupt``.
|
|
1161
|
-
"""
|
|
1162
|
-
thread = await self._fetch_thread_row()
|
|
1163
|
-
return None if thread is None else self._interrupt_ids(thread)
|
|
1164
|
-
|
|
1165
1111
|
async def _fetch_thread_row(self) -> dict[str, Any] | None:
|
|
1166
1112
|
"""Return the durable thread row, or ``None`` if the fetch failed."""
|
|
1167
1113
|
from langgraph_runtime.database import connect # noqa: PLC0415
|
|
@@ -1182,6 +1128,11 @@ class ThreadRunManager:
|
|
|
1182
1128
|
|
|
1183
1129
|
@staticmethod
|
|
1184
1130
|
def _interrupt_ids(thread: dict[str, Any]) -> set[str]:
|
|
1131
|
+
"""Ids in the row's ``interrupts`` map, written with the status when a run ends.
|
|
1132
|
+
|
|
1133
|
+
Read instead of ``Threads.State.get``, which rebuilds the graph and can
|
|
1134
|
+
raise, turning a valid answer into ``no_such_interrupt``.
|
|
1135
|
+
"""
|
|
1185
1136
|
interrupts_map = thread.get("interrupts")
|
|
1186
1137
|
if not _is_record(interrupts_map):
|
|
1187
1138
|
return set()
|
|
@@ -1197,40 +1148,6 @@ class ThreadRunManager:
|
|
|
1197
1148
|
found.add(entry_id)
|
|
1198
1149
|
return found
|
|
1199
1150
|
|
|
1200
|
-
async def _fetch_thread_status(self) -> str | None:
|
|
1201
|
-
"""Return durable thread status, or ``None`` if unreadable."""
|
|
1202
|
-
from langgraph_runtime.database import connect # noqa: PLC0415
|
|
1203
|
-
|
|
1204
|
-
try:
|
|
1205
|
-
async with connect() as conn:
|
|
1206
|
-
result = await self._threads.get(conn, self._thread_id)
|
|
1207
|
-
thread = await anext(result) if hasattr(result, "__anext__") else result
|
|
1208
|
-
except Exception:
|
|
1209
|
-
return None
|
|
1210
|
-
status = thread.get("status") if _is_record(thread) else None
|
|
1211
|
-
return status if isinstance(status, str) else None
|
|
1212
|
-
|
|
1213
|
-
async def _collect_settled_interrupt_ids(self) -> set[str] | None:
|
|
1214
|
-
"""Persisted interrupt ids, polling while the thread is still ``busy``."""
|
|
1215
|
-
ids = await self._collect_persisted_interrupt_ids()
|
|
1216
|
-
if ids is None:
|
|
1217
|
-
return None
|
|
1218
|
-
if ids:
|
|
1219
|
-
return ids
|
|
1220
|
-
deadline = time.monotonic() + _INTERRUPT_SETTLE_TIMEOUT_SECONDS
|
|
1221
|
-
while True:
|
|
1222
|
-
status = await self._fetch_thread_status()
|
|
1223
|
-
if status != _IN_FLIGHT_THREAD_STATUS:
|
|
1224
|
-
return await self._collect_persisted_interrupt_ids()
|
|
1225
|
-
if time.monotonic() >= deadline:
|
|
1226
|
-
return set()
|
|
1227
|
-
await asyncio.sleep(_INTERRUPT_SETTLE_POLL_INTERVAL_SECONDS)
|
|
1228
|
-
ids = await self._collect_persisted_interrupt_ids()
|
|
1229
|
-
if ids is None:
|
|
1230
|
-
return None
|
|
1231
|
-
if ids:
|
|
1232
|
-
return ids
|
|
1233
|
-
|
|
1234
1151
|
async def _collect_interrupt_ids_unless_busy(self) -> set[str] | None:
|
|
1235
1152
|
"""Persisted interrupt ids, or ``None`` while a run is in flight.
|
|
1236
1153
|
|
|
@@ -1247,16 +1164,45 @@ class ThreadRunManager:
|
|
|
1247
1164
|
return None
|
|
1248
1165
|
return self._interrupt_ids(thread)
|
|
1249
1166
|
|
|
1250
|
-
async def
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1167
|
+
async def _run_start_resumes(self, value: Any) -> bool:
|
|
1168
|
+
"""Whether ``run.start`` input answers the interrupts the thread is paused on.
|
|
1169
|
+
|
|
1170
|
+
Decided without waiting, from one read of the thread row. Interrupts
|
|
1171
|
+
reach the row only when their run ends, so while a run is in flight the
|
|
1172
|
+
input is a new run for the multitask strategy (``enqueue``, the default,
|
|
1173
|
+
queues it behind the running one), never an answer for whichever
|
|
1174
|
+
interrupt is open when that run pauses. The exception is the run this
|
|
1175
|
+
connection streams: the session reports it ``interrupted`` as soon as it
|
|
1176
|
+
raises an interrupt, so input sent after that answers the streamed
|
|
1177
|
+
interrupts, until this connection resumes them or that run ends. When
|
|
1178
|
+
the row can't be read, the streamed interrupts decide. The last run's
|
|
1179
|
+
status isn't consulted: a run that pauses on an interrupt ends
|
|
1180
|
+
``success``, and ``interrupted`` means it was cancelled; any interrupt it
|
|
1181
|
+
left open is in the row.
|
|
1182
|
+
|
|
1183
|
+
Raises ``_AmbiguousResumeError`` when the input would answer several
|
|
1184
|
+
interrupts and isn't keyed by interrupt id.
|
|
1185
|
+
"""
|
|
1186
|
+
if _keyed_by_interrupt_id(value):
|
|
1257
1187
|
return True
|
|
1258
|
-
|
|
1259
|
-
|
|
1188
|
+
session = self._session
|
|
1189
|
+
streamed = set(session._pending_interrupts) if session is not None else set()
|
|
1190
|
+
thread = await self._fetch_thread_row()
|
|
1191
|
+
if thread is None:
|
|
1192
|
+
paused_on = streamed
|
|
1193
|
+
elif thread.get("status") == _IN_FLIGHT_THREAD_STATUS:
|
|
1194
|
+
paused_on = set() if session is None or session.run_done else streamed
|
|
1195
|
+
else:
|
|
1196
|
+
paused_on = self._interrupt_ids(thread)
|
|
1197
|
+
if len(paused_on) > 1:
|
|
1198
|
+
# One answer can't say which of several pending interrupts it is
|
|
1199
|
+
# for: langgraph fails the run, or picks one when they share a
|
|
1200
|
+
# subgraph task.
|
|
1201
|
+
raise _AmbiguousResumeError(
|
|
1202
|
+
f"run.start input can't say which of the {len(paused_on)} "
|
|
1203
|
+
"pending interrupts it answers; answer them with input.respond."
|
|
1204
|
+
)
|
|
1205
|
+
return bool(paused_on)
|
|
1260
1206
|
|
|
1261
1207
|
# ------------------------------------------------------------------
|
|
1262
1208
|
# Command forwarding
|