langgraph-api 0.8.7__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/Makefile +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/PKG-INFO +8 -6
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/continuous/uv.lock +11 -11
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/ramp.js +3 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/constraints.txt +3 -3
- langgraph_api-0.9.0/langgraph_api/__init__.py +1 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/__init__.py +21 -1
- langgraph_api-0.9.0/langgraph_api/api/event_streaming.py +438 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/runs.py +2 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/custom.py +19 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/middleware.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/config/__init__.py +27 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/__init__.py +1 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/capabilities.py +144 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/constants.py +43 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/event_normalizers.py +89 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/namespace.py +48 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/service.py +958 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/session.py +1786 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/state_normalizers.py +385 -0
- langgraph_api-0.9.0/langgraph_api/event_streaming/types.py +168 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/feature_flags.py +22 -3
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/graph.py +51 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/crons.py +13 -2
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/threads.py +24 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/package.json +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/load.hooks.mjs +1 -0
- langgraph_api-0.9.0/langgraph_api/js/src/preload.mjs +108 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/yarn.lock +639 -59
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/metrics_datadog.py +19 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/models/run.py +19 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/release_tags.py +88 -30
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/route.py +34 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/schema.py +2 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/stream.py +79 -10
- langgraph_api-0.9.0/langgraph_api/stream_v2.py +224 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/checkpoint.py +4 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/__init__.py +0 -2
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/checkpointer_pb2.pyi +28 -3
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.py +1 -1
- langgraph_api-0.9.0/langgraph_grpc_common/proto/core_api_pb2.py +272 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/core_api_pb2.pyi +145 -9
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/core_api_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/encryption_pb2.pyi +13 -3
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/encryption_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/engine_common_pb2.pyi +119 -3
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/engine_common_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/errors_pb2.pyi +9 -3
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/errors_pb2_grpc.py +1 -1
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/openapi.json +312 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/pyproject.toml +11 -5
- langgraph_api-0.9.0/uv.lock +2459 -0
- langgraph_api-0.8.7/langgraph_api/__init__.py +0 -1
- langgraph_api-0.8.7/langgraph_api/js/src/preload.mjs +0 -29
- langgraph_api-0.8.7/langgraph_grpc_common/proto/core_api_pb2.py +0 -272
- langgraph_api-0.8.7/langgraph_grpc_common/proto/enum_store_operation_entry_type_pb2.py +0 -37
- langgraph_api-0.8.7/langgraph_grpc_common/proto/enum_store_operation_entry_type_pb2.pyi +0 -32
- langgraph_api-0.8.7/langgraph_grpc_common/proto/enum_store_operation_entry_type_pb2_grpc.py +0 -24
- langgraph_api-0.8.7/langgraph_grpc_common/proto/enum_store_operation_entry_type_pb2_grpc.pyi +0 -20
- langgraph_api-0.8.7/uv.lock +0 -2268
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/.gitignore +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/LICENSE +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/README.md +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/.gitignore +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/Makefile +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/README.md +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/assistant.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/benchmark-runner.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/benchmark_profiles.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/benchmarks.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/cancel_first_second_completes.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/enqueued_runs_order.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/log-failure.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/meta_workload.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/stream_write.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/thread.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/thread_runs_metadata_search.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/threads_search_metadata.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/types.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/benchmark-runners/wait_write.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/capacity_dd_report.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/capacity_k6.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/capacity_runner.mjs +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/capacity_slack_report.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/capacity_urls.mjs +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/clean-cli.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/clean.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/continuous/README.md +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/continuous/pyproject.toml +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/continuous/runner.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/graphs.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/mixed_workload_k6.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/mixed_workload_runner.mjs +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/package.json +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/reporting/dd_reporting.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/reporting/slack_slowest_runs.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/reporting/slack_summary.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/run_local.sh +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/staircase.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/staircase_step_k6.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/tsconfig.json +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/update-revision.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/benchmark/weather.js +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/custom_store.sql +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/forbidden.txt +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/hatch_build.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/healthcheck.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph-cloud-debugging-20260210132856.zip +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/_checkpointer/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/_checkpointer/_adapter.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/_checkpointer/protocol.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/_factory_utils.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/a2a.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/_constants.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/_handlers.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/_models.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/_routes.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/mcp/_sanitizers.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/profile.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/api/ui.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/asgi_transport.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/errors.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/auth/studio_user.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/cache.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/cli.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/command.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/config/_parse.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/config/schemas.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/aes_json.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/context.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/custom.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/middleware.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/encryption/shared.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/client.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/generated/core_api_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/assistants.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/cache.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/ops/runs.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/server.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/servicers/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/servicers/checkpointer.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/grpc/servicers/encryption.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/http.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/http_metrics.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/http_metrics_utils.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/.prettierrc +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/base.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/client.http.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/errors.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/schema.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/utils/experiment-tracing.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/utils/files.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/sse.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/traceblock.mts +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/tsconfig.json +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/js/ui.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/lc_security/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/lc_security/exceptions.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/lc_security/policy.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/lc_security/transport.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/logging.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/metadata.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/middleware/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/middleware/ensure_store.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/middleware/http_logger.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/middleware/private_network.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/middleware/request_id.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/otel_context.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/queue_entrypoint.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/self_hosted_logs.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/self_hosted_metrics.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/serde.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/server.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/state.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/store.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/timing/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/timing/profiler.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/timing/timer.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/traceblock.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/tunneling/cloudflare.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/cache.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/config.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/errors.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/extract.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/future.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/headers.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/network.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/retriable_client.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/stream_codec.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/utils/uuids.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/validation.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/webhook.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_api/worker.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/checkpointer.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/_compat.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/config.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/durability.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/struct.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/conversion/value.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/checkpointer_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/core_api_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/encryption_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/encryption_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/engine_common_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/engine_common_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_control_signal_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_control_signal_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_durability_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_durability_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_run_status_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_run_status_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_status_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_status_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/errors_pb2.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/proto/errors_pb2_grpc.pyi +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_grpc_common/serde.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_license/validation.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/__init__.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/checkpoint.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/database.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/lifespan.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/metrics.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/ops.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/queue.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/retry.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/routes.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/langgraph_runtime/store.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/logging.json +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/scripts/build_wheel.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/scripts/create_license.py +0 -0
- {langgraph_api-0.8.7 → langgraph_api-0.9.0}/scripts/run_a2a_tck.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.PHONY: build release lint format test test_watch start start-inmem start-inmem-license-oss start start-js-server build-go-server start-go-server stop-go-server check-version check-base-imports test-a2a-tck test-a2a-tck-mandatory start-test-a2a-tck start-test-a2a-tck-mandatory start-test-a2a-tck-watch test-mcp-conformance start-test-mcp-conformance list-mcp-scenarios
|
|
2
2
|
|
|
3
3
|
# Environment variables
|
|
4
|
-
LANGSERVE_GRAPHS_ALL = '{"agent": {"path": "./tests/graphs/agent.py:graph", "description": "agent"}, "assistant_id_runtime": "./tests/graphs/assistant_id_runtime.py:graph", "custom_lifespan": "./tests/graphs/my_router.py:graph", "single_node": "./tests/graphs/single_node.py:graph", "benchmark": "./tests/graphs/benchmark.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph", "agent_interrupt": "./tests/graphs/agent_interrupt.py:graph", "agent_parallel_interrupt": "./tests/graphs/agent_parallel_interrupt.py:graph", "message_type_test": "./tests/graphs/message_type_test.py:graph", "remote_subgraph_parent": "./tests/graphs/remote_subgraph_parent.py:graph", "simple_remote": "./tests/graphs/simple_remote.py:graph", "nested_subgraphs": "./tests/graphs/nested_subgraphs.py:graph", "functional_fibonacci": "./tests/graphs/functional_fibonacci.py:fibonacci", "state_graph_fibonacci": "./tests/graphs/state_graph_fibonacci.py:fibonacci", "max_concurrency_graph": "./tests/graphs/max_concurrency_graph.py:graph", "unserializable_subgraph": "./tests/graphs/unserializable_subgraph.py:graph", "agent_interrupt_text": "./tests/graphs/agent_interrupt_text.py:graph", "agent_echo_stream": "./tests/graphs/agent_echo_stream.py:graph", "runtime_graph": "./tests/graphs/runtime_graph.py:graph", "tool_call_chunk_stream": "./tests/graphs/tool_call_chunk_stream.py:graph", "ui_tool_call_stream": "./tests/graphs/ui_tool_call_stream.py:graph"}'
|
|
4
|
+
LANGSERVE_GRAPHS_ALL = '{"agent": {"path": "./tests/graphs/agent.py:graph", "description": "agent"}, "assistant_id_runtime": "./tests/graphs/assistant_id_runtime.py:graph", "custom_lifespan": "./tests/graphs/my_router.py:graph", "single_node": "./tests/graphs/single_node.py:graph", "benchmark": "./tests/graphs/benchmark.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph", "agent_interrupt": "./tests/graphs/agent_interrupt.py:graph", "agent_parallel_interrupt": "./tests/graphs/agent_parallel_interrupt.py:graph", "message_type_test": "./tests/graphs/message_type_test.py:graph", "remote_subgraph_parent": "./tests/graphs/remote_subgraph_parent.py:graph", "simple_remote": "./tests/graphs/simple_remote.py:graph", "nested_subgraphs": "./tests/graphs/nested_subgraphs.py:graph", "functional_fibonacci": "./tests/graphs/functional_fibonacci.py:fibonacci", "state_graph_fibonacci": "./tests/graphs/state_graph_fibonacci.py:fibonacci", "max_concurrency_graph": "./tests/graphs/max_concurrency_graph.py:graph", "unserializable_subgraph": "./tests/graphs/unserializable_subgraph.py:graph", "agent_interrupt_text": "./tests/graphs/agent_interrupt_text.py:graph", "agent_echo_stream": "./tests/graphs/agent_echo_stream.py:graph", "agent_tool_stream": "./tests/graphs/agent_tool_stream.py:graph", "runtime_graph": "./tests/graphs/runtime_graph.py:graph", "tool_call_chunk_stream": "./tests/graphs/tool_call_chunk_stream.py:graph", "ui_tool_call_stream": "./tests/graphs/ui_tool_call_stream.py:graph", "agent_metrics_stream": "./tests/graphs/agent_metrics_stream.py:graph", "agent_multimodal_stream": "./tests/graphs/agent_multimodal_stream.py:graph", "agent_bedtime_story": "./tests/graphs/agent_bedtime_story.py:graph", "delta_channel": "./tests/graphs/delta_channel.py:graph", "delta_channel_freq": "./tests/graphs/delta_channel_freq.py:graph", "delta_channel_files": "./tests/graphs/delta_channel_files.py:graph", "delta_channel_files_freq": "./tests/graphs/delta_channel_files_freq.py:graph", "delta_channel_legacy": "./tests/graphs/delta_channel_migration.py:graph_legacy", "delta_channel_delta": "./tests/graphs/delta_channel_migration.py:graph_delta", "delta_channel_messages_freq": "./tests/graphs/delta_channel_messages.py:graph_freq", "delta_channel_messages": "./tests/graphs/delta_channel_messages.py:graph", "delta_channel_messages_legacy": "./tests/graphs/delta_channel_messages.py:graph_legacy", "delta_channel_messages_remove": "./tests/graphs/delta_channel_messages.py:graph_remove", "delta_channel_messages_update": "./tests/graphs/delta_channel_messages.py:graph_update", "delta_channel_messages_delta": "./tests/graphs/delta_channel_messages.py:graph_delta"}'
|
|
5
5
|
LANGSERVE_GRAPHS_AUTH = '{"agent": {"path": "./tests/graphs/agent.py:graph", "description": "agent"}, "assistant_id_runtime": "./tests/graphs/assistant_id_runtime.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph", "agent_parallel_interrupt": "./tests/graphs/agent_parallel_interrupt.py:graph", "functional_fibonacci": "./tests/graphs/functional_fibonacci.py:fibonacci", "state_graph_fibonacci": "./tests/graphs/state_graph_fibonacci.py:fibonacci", "runtime_graph": "./tests/graphs/runtime_graph.py:graph"}'
|
|
6
6
|
|
|
7
7
|
# Go server management
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Author-email: Will Fu-Hinthorn <will@langchain.dev>, Josh Rogers <josh@langchain.dev>, Parker Rule <parker@langchain.dev>
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
8
|
Requires-Dist: cloudpickle>=3.0.0
|
|
9
|
-
Requires-Dist: cryptography
|
|
10
|
-
Requires-Dist: grpcio-health-checking<1.
|
|
11
|
-
Requires-Dist: grpcio-tools==1.
|
|
12
|
-
Requires-Dist: grpcio<1.
|
|
9
|
+
Requires-Dist: cryptography>=42.0.0
|
|
10
|
+
Requires-Dist: grpcio-health-checking<1.81.0,>=1.80.0
|
|
11
|
+
Requires-Dist: grpcio-tools==1.80.0
|
|
12
|
+
Requires-Dist: grpcio<1.81.0,>=1.80.0
|
|
13
13
|
Requires-Dist: httptools>=0.5.0; platform_system != 'Windows'
|
|
14
14
|
Requires-Dist: httpx>=0.25.0
|
|
15
15
|
Requires-Dist: jsonschema-rs<0.45,>=0.20.0
|
|
16
16
|
Requires-Dist: langchain-core>=0.3.64
|
|
17
|
+
Requires-Dist: langchain-protocol<0.1,>=0.0.15
|
|
17
18
|
Requires-Dist: langgraph-checkpoint<5,>=3.0.1
|
|
18
|
-
Requires-Dist: langgraph-runtime-inmem<0.
|
|
19
|
+
Requires-Dist: langgraph-runtime-inmem<0.30.0,>=0.29.0
|
|
19
20
|
Requires-Dist: langgraph-sdk>=0.3.5
|
|
20
21
|
Requires-Dist: langgraph<2,>=0.4.10
|
|
21
22
|
Requires-Dist: langsmith[otel]>=0.6.3
|
|
@@ -34,6 +35,7 @@ Requires-Dist: uuid-utils>=0.12.0
|
|
|
34
35
|
Requires-Dist: uvicorn>=0.26.0
|
|
35
36
|
Requires-Dist: uvloop>=0.18.0; platform_system != 'Windows'
|
|
36
37
|
Requires-Dist: watchfiles>=0.13
|
|
38
|
+
Requires-Dist: websockets>=13.0
|
|
37
39
|
Requires-Dist: zstandard>=0.23.0
|
|
38
40
|
Description-Content-Type: text/markdown
|
|
39
41
|
|
|
@@ -25,14 +25,14 @@ wheels = [
|
|
|
25
25
|
|
|
26
26
|
[[package]]
|
|
27
27
|
name = "click"
|
|
28
|
-
version = "8.3.
|
|
28
|
+
version = "8.3.3"
|
|
29
29
|
source = { registry = "https://pypi.org/simple" }
|
|
30
30
|
dependencies = [
|
|
31
31
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
32
32
|
]
|
|
33
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
33
|
+
sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" }
|
|
34
34
|
wheels = [
|
|
35
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
35
|
+
{ url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" },
|
|
36
36
|
]
|
|
37
37
|
|
|
38
38
|
[[package]]
|
|
@@ -120,25 +120,25 @@ dependencies = [
|
|
|
120
120
|
|
|
121
121
|
[package.metadata]
|
|
122
122
|
requires-dist = [
|
|
123
|
-
{ name = "click", specifier = ">=8.3.
|
|
123
|
+
{ name = "click", specifier = ">=8.3.3" },
|
|
124
124
|
{ name = "datadog-api-client", specifier = ">=2.0.0" },
|
|
125
125
|
{ name = "httpx", specifier = ">=0.27.0" },
|
|
126
|
-
{ name = "langgraph-sdk", specifier = ">=0.
|
|
126
|
+
{ name = "langgraph-sdk", specifier = ">=0.3.13" },
|
|
127
127
|
{ name = "python-dateutil", specifier = ">=2.8.0" },
|
|
128
128
|
{ name = "structlog", specifier = ">=24.1.0" },
|
|
129
129
|
]
|
|
130
130
|
|
|
131
131
|
[[package]]
|
|
132
132
|
name = "langgraph-sdk"
|
|
133
|
-
version = "0.3.
|
|
133
|
+
version = "0.3.14"
|
|
134
134
|
source = { registry = "https://pypi.org/simple" }
|
|
135
135
|
dependencies = [
|
|
136
136
|
{ name = "httpx" },
|
|
137
137
|
{ name = "orjson" },
|
|
138
138
|
]
|
|
139
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
139
|
+
sdist = { url = "https://files.pythonhosted.org/packages/02/f1/134046c20bc4a4a15d410d1d21c9e298a3e9923777b4cc867b8669bc636b/langgraph_sdk-0.3.14.tar.gz", hash = "sha256:acd1674c538e97f3cdaa610f6dd7e34bc9bad30167f0ccc482dcd563325e81f5", size = 198162, upload-time = "2026-05-05T18:40:03.524Z" }
|
|
140
140
|
wheels = [
|
|
141
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/34/96/1c9f9fbfe756ddd850a2585e7f1949d8ebb97fdaa7a5eff8f45ed1314670/langgraph_sdk-0.3.14-py3-none-any.whl", hash = "sha256:68935bf6f4924eda92617a9e5dfb4f4281197508c648cb9d62ff083907607f9d", size = 97028, upload-time = "2026-05-05T18:40:02.099Z" },
|
|
142
142
|
]
|
|
143
143
|
|
|
144
144
|
[[package]]
|
|
@@ -220,9 +220,9 @@ wheels = [
|
|
|
220
220
|
|
|
221
221
|
[[package]]
|
|
222
222
|
name = "urllib3"
|
|
223
|
-
version = "2.
|
|
223
|
+
version = "2.7.0"
|
|
224
224
|
source = { registry = "https://pypi.org/simple" }
|
|
225
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
225
|
+
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
|
|
226
226
|
wheels = [
|
|
227
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
227
|
+
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
|
|
228
228
|
]
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { sleep } from 'k6';
|
|
2
2
|
import { Counter, Trend } from 'k6/metrics';
|
|
3
|
-
|
|
3
|
+
function randomIntBetween(min, max) {
|
|
4
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
5
|
+
}
|
|
4
6
|
import { Benchmarks } from './benchmark-runners/dist/benchmarks.js';
|
|
5
7
|
import { get_profile } from './benchmark-runners/dist/benchmark_profiles.js';
|
|
6
8
|
|
|
@@ -18,9 +18,9 @@ structlog>=24.1.0
|
|
|
18
18
|
cloudpickle>=3.0.0
|
|
19
19
|
truststore>=0.1
|
|
20
20
|
protobuf>=6.32.1,<7.0.0
|
|
21
|
-
grpcio>=1.
|
|
22
|
-
grpcio-tools>=1.
|
|
23
|
-
grpcio-health-checking>=1.
|
|
21
|
+
grpcio>=1.80.0,<1.81.0
|
|
22
|
+
grpcio-tools>=1.80.0,<1.81.0
|
|
23
|
+
grpcio-health-checking>=1.80.0,<1.81.0
|
|
24
24
|
opentelemetry-api>=0.0.1
|
|
25
25
|
opentelemetry-sdk>=0.0.1
|
|
26
26
|
opentelemetry-exporter-otlp-proto-http>=0.0.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.0"
|
|
@@ -15,6 +15,7 @@ from starlette.routing import BaseRoute, Route
|
|
|
15
15
|
from langgraph_api import timing
|
|
16
16
|
from langgraph_api.api.a2a import a2a_routes
|
|
17
17
|
from langgraph_api.api.assistants import assistants_routes
|
|
18
|
+
from langgraph_api.api.event_streaming import event_streaming_routes
|
|
18
19
|
from langgraph_api.api.mcp import mcp_routes
|
|
19
20
|
from langgraph_api.api.meta import meta_info, meta_metrics
|
|
20
21
|
from langgraph_api.api.openapi import get_openapi_spec
|
|
@@ -30,7 +31,10 @@ from langgraph_api.config import (
|
|
|
30
31
|
LANGGRAPH_ENCRYPTION,
|
|
31
32
|
MIGRATIONS_PATH,
|
|
32
33
|
)
|
|
33
|
-
from langgraph_api.feature_flags import
|
|
34
|
+
from langgraph_api.feature_flags import (
|
|
35
|
+
FF_V2_EVENT_STREAMING,
|
|
36
|
+
IS_POSTGRES_OR_GRPC_BACKEND,
|
|
37
|
+
)
|
|
34
38
|
from langgraph_api.graph import js_bg_tasks
|
|
35
39
|
from langgraph_api.grpc.client import get_shared_client
|
|
36
40
|
from langgraph_api.js.base import is_js_path
|
|
@@ -131,6 +135,20 @@ if HTTP_CONFIG:
|
|
|
131
135
|
protected_routes.extend(mcp_routes)
|
|
132
136
|
if not HTTP_CONFIG.get("disable_a2a"):
|
|
133
137
|
protected_routes.extend(a2a_routes)
|
|
138
|
+
# ``disable_runs`` also gates event streaming because the v2 transport
|
|
139
|
+
# exposes run-creating commands (``run.start`` / ``run.cancel`` /
|
|
140
|
+
# ``run.attach`` over the WS, plus the ``/runs/{run_id}/protocol``
|
|
141
|
+
# attach route). If an operator hardens a deployment with
|
|
142
|
+
# ``disable_runs=true``, leaving event streaming open would be a
|
|
143
|
+
# backdoor that lets clients create runs anyway. ``disable_event_streaming``
|
|
144
|
+
# is the narrow knob (turn off just v2); ``disable_runs`` is the broad
|
|
145
|
+
# knob (no run-related functionality at all).
|
|
146
|
+
if (
|
|
147
|
+
FF_V2_EVENT_STREAMING
|
|
148
|
+
and not HTTP_CONFIG.get("disable_runs")
|
|
149
|
+
and not HTTP_CONFIG.get("disable_event_streaming")
|
|
150
|
+
):
|
|
151
|
+
protected_routes.extend(event_streaming_routes)
|
|
134
152
|
else:
|
|
135
153
|
protected_routes.extend(assistants_routes)
|
|
136
154
|
protected_routes.extend(runs_routes)
|
|
@@ -141,6 +159,8 @@ else:
|
|
|
141
159
|
protected_routes.extend(ui_routes)
|
|
142
160
|
protected_routes.extend(mcp_routes)
|
|
143
161
|
protected_routes.extend(a2a_routes)
|
|
162
|
+
if FF_V2_EVENT_STREAMING:
|
|
163
|
+
protected_routes.extend(event_streaming_routes)
|
|
144
164
|
|
|
145
165
|
|
|
146
166
|
def _metadata_fn(app_import: str) -> dict[str, str]:
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
"""V2 event streaming transport routes (thread-centric).
|
|
2
|
+
|
|
3
|
+
These routes implement the v2 event streaming protocol — see
|
|
4
|
+
:mod:`langgraph_api.event_streaming` for the session/normalizer layer that
|
|
5
|
+
shapes the wire events. Connections are scoped to a thread via the URL
|
|
6
|
+
path; there is no server-side session state across connections.
|
|
7
|
+
|
|
8
|
+
Endpoints:
|
|
9
|
+
|
|
10
|
+
* ``POST /threads/{thread_id}/stream/events`` — SSE stream with
|
|
11
|
+
``EventStreamRequest`` filter body. Each connection IS the subscription;
|
|
12
|
+
closing the connection unsubscribes.
|
|
13
|
+
* ``POST /threads/{thread_id}/commands`` — JSON command request/response.
|
|
14
|
+
* ``WebSocket /threads/{thread_id}/stream/events`` — full-duplex commands and events.
|
|
15
|
+
``subscription.subscribe`` / ``subscription.unsubscribe`` manage
|
|
16
|
+
subscriptions for the lifetime of the socket.
|
|
17
|
+
* ``WebSocket /threads/{thread_id}/runs/{run_id}/protocol`` — compatibility
|
|
18
|
+
route that auto-binds to an existing run on connection.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import asyncio
|
|
24
|
+
import contextlib
|
|
25
|
+
from collections import deque
|
|
26
|
+
from typing import TYPE_CHECKING, Any
|
|
27
|
+
from uuid import uuid4
|
|
28
|
+
|
|
29
|
+
import orjson
|
|
30
|
+
import structlog
|
|
31
|
+
from starlette.responses import Response
|
|
32
|
+
from starlette.websockets import WebSocketDisconnect
|
|
33
|
+
|
|
34
|
+
from langgraph_api.event_streaming.service import ThreadRunManager
|
|
35
|
+
from langgraph_api.event_streaming.session import _is_supported_channel
|
|
36
|
+
from langgraph_api.event_streaming.types import Subscription
|
|
37
|
+
from langgraph_api.feature_flags import IS_POSTGRES_OR_GRPC_BACKEND
|
|
38
|
+
from langgraph_api.route import ApiRoute, ApiWebSocketRoute
|
|
39
|
+
from langgraph_api.serde import json_dumpb
|
|
40
|
+
from langgraph_api.sse import EventSourceResponse
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from starlette.requests import Request
|
|
44
|
+
from starlette.websockets import WebSocket
|
|
45
|
+
|
|
46
|
+
if IS_POSTGRES_OR_GRPC_BACKEND:
|
|
47
|
+
from langgraph_api.grpc.ops import Runs, Threads
|
|
48
|
+
else:
|
|
49
|
+
from langgraph_runtime.ops import Runs, Threads
|
|
50
|
+
|
|
51
|
+
logger = structlog.stdlib.get_logger(__name__)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Maximum number of recently-delivered ``event_id`` values an SSE
|
|
55
|
+
# connection remembers for dedup. The dedup window only needs to cover
|
|
56
|
+
# the brief race where a single event arrives both via the thread
|
|
57
|
+
# source consumer and via the session's send callback (subscription
|
|
58
|
+
# install handoff); after that, events flow through one path
|
|
59
|
+
# consistently. Cap chosen with ample headroom — a pathological
|
|
60
|
+
# replay storm of 2k events still fits in <50 KB of strings per
|
|
61
|
+
# connection, vs. the unbounded set this replaces.
|
|
62
|
+
_DELIVERED_DEDUP_WINDOW = 2048
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _json_response(content: Any, *, status_code: int = 200) -> Response:
|
|
66
|
+
"""JSON response backed by the repo's orjson helper.
|
|
67
|
+
|
|
68
|
+
Starlette's default ``JSONResponse`` uses stdlib ``json`` which cannot
|
|
69
|
+
serialize ``UUID`` / ``datetime`` / ``bytes`` — values that regularly
|
|
70
|
+
appear in command responses (e.g. ``run_id`` from ``create_valid_run``).
|
|
71
|
+
Using ``json_dumpb`` matches the serialization used on the event
|
|
72
|
+
stream and keeps the two surfaces consistent.
|
|
73
|
+
"""
|
|
74
|
+
return Response(
|
|
75
|
+
json_dumpb(content),
|
|
76
|
+
status_code=status_code,
|
|
77
|
+
media_type="application/json",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _make_manager(thread_id: str, send_event: Any = None) -> ThreadRunManager:
|
|
82
|
+
# Auth context is propagated via ``ApiRoute``'s ``with_user`` wrapper
|
|
83
|
+
# — ops reads it off the context var, so we don't thread it through
|
|
84
|
+
# the manager explicitly.
|
|
85
|
+
return ThreadRunManager(
|
|
86
|
+
thread_id=thread_id,
|
|
87
|
+
runs=Runs,
|
|
88
|
+
threads=Threads,
|
|
89
|
+
send_event=send_event,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# POST /threads/{thread_id}/stream/events — SSE event stream
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def _thread_events(request: Request) -> Response:
|
|
99
|
+
"""SSE stream scoped to a thread.
|
|
100
|
+
|
|
101
|
+
Body is an ``EventStreamRequest``:
|
|
102
|
+
|
|
103
|
+
{
|
|
104
|
+
"channels": ["values", "messages", ...],
|
|
105
|
+
"namespaces": [["ns1"], ["ns2", "child"]], // optional
|
|
106
|
+
"depth": 2, // optional
|
|
107
|
+
"since": 42 // optional seq
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
On reconnect, clients pass the last ``seq`` they received as
|
|
111
|
+
``since`` in the body. Buffered events with ``seq > since`` are
|
|
112
|
+
replayed before the stream goes live. The endpoint is POST-only, so
|
|
113
|
+
browser-native ``EventSource`` auto-resume (``Last-Event-ID``)
|
|
114
|
+
doesn't apply — clients drive resume explicitly via the body.
|
|
115
|
+
|
|
116
|
+
The filter applies for the lifetime of the connection; closing the
|
|
117
|
+
connection unsubscribes. No state is persisted server-side beyond the
|
|
118
|
+
connection.
|
|
119
|
+
"""
|
|
120
|
+
thread_id = request.path_params["thread_id"]
|
|
121
|
+
try:
|
|
122
|
+
body = orjson.loads(await request.body())
|
|
123
|
+
except Exception:
|
|
124
|
+
return _json_response({"detail": "Invalid JSON body"}, status_code=400)
|
|
125
|
+
|
|
126
|
+
channels = body.get("channels") if isinstance(body, dict) else None
|
|
127
|
+
if not isinstance(channels, list) or not channels:
|
|
128
|
+
return _json_response(
|
|
129
|
+
{"detail": "channels is required and must be a non-empty array"},
|
|
130
|
+
status_code=400,
|
|
131
|
+
)
|
|
132
|
+
# Reject unknown channel names up front — otherwise the SSE stream
|
|
133
|
+
# would stay open indefinitely with no events flowing, confusing
|
|
134
|
+
# clients that expected a subscription-level error. Aligns the
|
|
135
|
+
# HTTP endpoint with the ``subscription.subscribe`` command which
|
|
136
|
+
# already short-circuits with ``invalid_argument``. ``custom:<name>``
|
|
137
|
+
# passes through ``_is_supported_channel`` unchanged.
|
|
138
|
+
bad: list[str] = []
|
|
139
|
+
validated: list[str] = []
|
|
140
|
+
for c in channels:
|
|
141
|
+
if not isinstance(c, str):
|
|
142
|
+
bad.append(repr(c))
|
|
143
|
+
continue
|
|
144
|
+
if not _is_supported_channel(c):
|
|
145
|
+
bad.append(c)
|
|
146
|
+
continue
|
|
147
|
+
validated.append(c)
|
|
148
|
+
if bad:
|
|
149
|
+
return _json_response(
|
|
150
|
+
{
|
|
151
|
+
"detail": (
|
|
152
|
+
"channels contains unsupported entries: "
|
|
153
|
+
+ ", ".join(bad[:5])
|
|
154
|
+
+ ". Allowed: values, updates, messages, tools, custom, "
|
|
155
|
+
"lifecycle, input, tasks, or any `custom:<name>`."
|
|
156
|
+
)
|
|
157
|
+
},
|
|
158
|
+
status_code=400,
|
|
159
|
+
)
|
|
160
|
+
channels = validated
|
|
161
|
+
|
|
162
|
+
namespaces = body.get("namespaces") if isinstance(body, dict) else None
|
|
163
|
+
if not isinstance(namespaces, list):
|
|
164
|
+
namespaces = None
|
|
165
|
+
else:
|
|
166
|
+
# only accept list[list[str]]
|
|
167
|
+
filtered_ns: list[list[str]] = []
|
|
168
|
+
for ns in namespaces:
|
|
169
|
+
if isinstance(ns, list) and all(isinstance(seg, str) for seg in ns):
|
|
170
|
+
filtered_ns.append(ns)
|
|
171
|
+
namespaces = filtered_ns or None
|
|
172
|
+
|
|
173
|
+
depth = body.get("depth") if isinstance(body, dict) else None
|
|
174
|
+
# ``bool`` is a subclass of ``int``, so a JSON ``"depth": false`` would
|
|
175
|
+
# otherwise fall through as ``depth=0`` ("only the exact prefix
|
|
176
|
+
# namespace, no deeper") and silently mute every nested-subgraph
|
|
177
|
+
# event. Treat any non-int (including bool) as "no depth limit".
|
|
178
|
+
if not isinstance(depth, int) or isinstance(depth, bool) or depth < 0:
|
|
179
|
+
depth = None
|
|
180
|
+
|
|
181
|
+
raw_since = body.get("since") if isinstance(body, dict) else None
|
|
182
|
+
# Reject ``bool`` explicitly — ``isinstance(True, int)`` is ``True``
|
|
183
|
+
# so a JSON ``"since": true`` would otherwise pass as ``since=1``
|
|
184
|
+
# and silently skip the first buffered event on reconnect.
|
|
185
|
+
since: int | None = (
|
|
186
|
+
raw_since
|
|
187
|
+
if isinstance(raw_since, int)
|
|
188
|
+
and not isinstance(raw_since, bool)
|
|
189
|
+
and raw_since >= 0
|
|
190
|
+
else None
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Bounded LRU dedup: ``recent_eids`` is the eviction order, ``delivered``
|
|
194
|
+
# is the O(1) membership view. When the deque hits its cap, the oldest
|
|
195
|
+
# entry rolls off; we mirror that on ``delivered`` to keep them in sync.
|
|
196
|
+
recent_eids: deque[str] = deque(maxlen=_DELIVERED_DEDUP_WINDOW)
|
|
197
|
+
delivered: set[str] = set()
|
|
198
|
+
pending_events: list[dict[str, Any]] = []
|
|
199
|
+
flush_pending = asyncio.Event()
|
|
200
|
+
|
|
201
|
+
async def on_event(event: dict[str, Any]) -> None:
|
|
202
|
+
eid = event.get("event_id")
|
|
203
|
+
if eid is None or eid in delivered:
|
|
204
|
+
return
|
|
205
|
+
# Guard against events with seq <= since slipping through (e.g.
|
|
206
|
+
# events delivered via the session's send callback before the
|
|
207
|
+
# subscription is installed with replay).
|
|
208
|
+
if since is not None and event.get("seq", 0) <= since:
|
|
209
|
+
return
|
|
210
|
+
if len(recent_eids) == recent_eids.maxlen:
|
|
211
|
+
delivered.discard(recent_eids[0])
|
|
212
|
+
recent_eids.append(eid)
|
|
213
|
+
delivered.add(eid)
|
|
214
|
+
pending_events.append(event)
|
|
215
|
+
flush_pending.set()
|
|
216
|
+
|
|
217
|
+
manager = _make_manager(thread_id, send_event=on_event)
|
|
218
|
+
|
|
219
|
+
filter_sub = Subscription(
|
|
220
|
+
id=str(uuid4()),
|
|
221
|
+
channels=set(channels),
|
|
222
|
+
namespaces=namespaces,
|
|
223
|
+
depth=depth,
|
|
224
|
+
active=False,
|
|
225
|
+
)
|
|
226
|
+
await logger.adebug(
|
|
227
|
+
"Installing event streaming subscription",
|
|
228
|
+
thread_id=thread_id,
|
|
229
|
+
subscription_id=filter_sub.id,
|
|
230
|
+
channels=sorted(filter_sub.channels),
|
|
231
|
+
depth=depth,
|
|
232
|
+
since=since,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
manager.install_subscription(filter_sub)
|
|
236
|
+
# ``since`` is enforced by ``on_event`` above as a seq filter on
|
|
237
|
+
# outbound events; the thread-level reader always replays from the
|
|
238
|
+
# beginning so namespace-scoped projections see history. See
|
|
239
|
+
# ``ThreadRunManager.start_thread_stream`` for the rationale.
|
|
240
|
+
manager.start_thread_stream()
|
|
241
|
+
|
|
242
|
+
async def body_iter():
|
|
243
|
+
try:
|
|
244
|
+
while True:
|
|
245
|
+
# Race ``flush_pending`` against the thread-stream consumer
|
|
246
|
+
# ending. If the consumer dies (transient join_event_streaming
|
|
247
|
+
# error, or normal shutdown), we want this loop to exit
|
|
248
|
+
# rather than wedge forever waiting for events that will
|
|
249
|
+
# never arrive.
|
|
250
|
+
flush_task = asyncio.create_task(flush_pending.wait())
|
|
251
|
+
done_task = asyncio.create_task(manager.wait_for_thread_stream_end())
|
|
252
|
+
try:
|
|
253
|
+
done, _pending = await asyncio.wait(
|
|
254
|
+
{flush_task, done_task},
|
|
255
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
256
|
+
)
|
|
257
|
+
finally:
|
|
258
|
+
for p in (flush_task, done_task):
|
|
259
|
+
if not p.done():
|
|
260
|
+
p.cancel()
|
|
261
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
262
|
+
await p
|
|
263
|
+
stream_finished = done_task in done
|
|
264
|
+
# Always drain whatever was buffered before exiting so a
|
|
265
|
+
# final batch that arrived alongside the consumer's exit
|
|
266
|
+
# still reaches the client.
|
|
267
|
+
flush_pending.clear()
|
|
268
|
+
events = pending_events[:]
|
|
269
|
+
pending_events.clear()
|
|
270
|
+
for event in events:
|
|
271
|
+
# The wire ``event_id`` (inside the JSON body) carries
|
|
272
|
+
# the durable upstream Redis stream entry id used by
|
|
273
|
+
# the client's ``seenEventIds`` for cross-session
|
|
274
|
+
# dedup. The SSE ``id:`` field carries the protocol
|
|
275
|
+
# ``seq`` (session-local monotonic int) so server
|
|
276
|
+
# logs and traces can correlate against the body
|
|
277
|
+
# ``since`` cursor a reconnecting client sends.
|
|
278
|
+
sse_id = event.get("seq")
|
|
279
|
+
method = event.get("method", "event")
|
|
280
|
+
yield (
|
|
281
|
+
method.encode() if isinstance(method, str) else b"event",
|
|
282
|
+
json_dumpb(event),
|
|
283
|
+
str(sse_id).encode() if isinstance(sse_id, int) else None,
|
|
284
|
+
)
|
|
285
|
+
if stream_finished:
|
|
286
|
+
break
|
|
287
|
+
finally:
|
|
288
|
+
await manager.close()
|
|
289
|
+
|
|
290
|
+
return EventSourceResponse(body_iter())
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
# POST /threads/{thread_id}/commands — JSON command
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
async def _thread_command(request: Request) -> Response:
|
|
299
|
+
"""Handle a single protocol command scoped to a thread.
|
|
300
|
+
|
|
301
|
+
Commands are stateless in the HTTP transport: a fresh manager is
|
|
302
|
+
created, the command is dispatched, and results are returned. For
|
|
303
|
+
``run.start`` this creates/resumes a run; subsequent event streaming
|
|
304
|
+
happens over a separate ``POST .../stream`` connection.
|
|
305
|
+
"""
|
|
306
|
+
thread_id = request.path_params["thread_id"]
|
|
307
|
+
try:
|
|
308
|
+
payload = orjson.loads(await request.body())
|
|
309
|
+
except Exception:
|
|
310
|
+
return _json_response({"detail": "Invalid JSON body"}, status_code=400)
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
not isinstance(payload, dict)
|
|
314
|
+
or not isinstance(payload.get("id"), int)
|
|
315
|
+
or not isinstance(payload.get("method"), str)
|
|
316
|
+
):
|
|
317
|
+
await logger.awarning(
|
|
318
|
+
"Rejected malformed event streaming command",
|
|
319
|
+
thread_id=thread_id,
|
|
320
|
+
payload_id=payload.get("id") if isinstance(payload, dict) else None,
|
|
321
|
+
payload_method=payload.get("method") if isinstance(payload, dict) else None,
|
|
322
|
+
payload_type=type(payload).__name__,
|
|
323
|
+
)
|
|
324
|
+
return _json_response(
|
|
325
|
+
{
|
|
326
|
+
"type": "error",
|
|
327
|
+
"id": payload.get("id") if isinstance(payload, dict) else None,
|
|
328
|
+
"error": "invalid_argument",
|
|
329
|
+
"message": "Protocol commands must include an integer id and string method.",
|
|
330
|
+
},
|
|
331
|
+
status_code=400,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
manager = _make_manager(thread_id)
|
|
335
|
+
try:
|
|
336
|
+
resp = await manager.handle_command(payload)
|
|
337
|
+
return _json_response(resp)
|
|
338
|
+
finally:
|
|
339
|
+
# Commands that create runs (e.g. run.start) leave the run executing in
|
|
340
|
+
# the background on the worker queue. Downstream clients observe it via
|
|
341
|
+
# the thread-level ``POST .../stream/events`` source.
|
|
342
|
+
await manager.close()
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ---------------------------------------------------------------------------
|
|
346
|
+
# WebSocket /threads/{thread_id}/stream/events
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
async def _thread_websocket(websocket: WebSocket) -> None:
|
|
351
|
+
"""Full-duplex protocol connection scoped to a thread.
|
|
352
|
+
|
|
353
|
+
Supports ``run.start``, ``input.respond``, ``subscription.subscribe``,
|
|
354
|
+
``subscription.unsubscribe``, ``agent.getTree``.
|
|
355
|
+
"""
|
|
356
|
+
thread_id = websocket.path_params["thread_id"]
|
|
357
|
+
await websocket.accept()
|
|
358
|
+
|
|
359
|
+
async def ws_send_event(event: dict[str, Any]) -> None:
|
|
360
|
+
await websocket.send_text(orjson.dumps(event).decode("utf-8"))
|
|
361
|
+
|
|
362
|
+
manager = _make_manager(thread_id, send_event=ws_send_event)
|
|
363
|
+
manager.start_thread_stream()
|
|
364
|
+
|
|
365
|
+
# Watchdog: when the thread-stream consumer ends (transient
|
|
366
|
+
# ``join_event_streaming`` failure or normal shutdown), close the socket so
|
|
367
|
+
# the client sees a clean disconnect instead of an open connection
|
|
368
|
+
# that will never deliver another event. ``receive_text`` below
|
|
369
|
+
# raises ``WebSocketDisconnect`` once the close lands, exiting the
|
|
370
|
+
# loop through its existing handler.
|
|
371
|
+
async def _stream_watchdog() -> None:
|
|
372
|
+
await manager.wait_for_thread_stream_end()
|
|
373
|
+
with contextlib.suppress(Exception):
|
|
374
|
+
await websocket.close(code=1011, reason="thread stream ended")
|
|
375
|
+
|
|
376
|
+
watchdog = asyncio.create_task(_stream_watchdog())
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
while True:
|
|
380
|
+
raw = await websocket.receive_text()
|
|
381
|
+
try:
|
|
382
|
+
payload = orjson.loads(raw)
|
|
383
|
+
except Exception:
|
|
384
|
+
await websocket.send_json(
|
|
385
|
+
{
|
|
386
|
+
"type": "error",
|
|
387
|
+
"id": None,
|
|
388
|
+
"error": "invalid_argument",
|
|
389
|
+
"message": "Protocol commands must be valid JSON.",
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
continue
|
|
393
|
+
|
|
394
|
+
if (
|
|
395
|
+
not isinstance(payload, dict)
|
|
396
|
+
or not isinstance(payload.get("id"), int)
|
|
397
|
+
or not isinstance(payload.get("method"), str)
|
|
398
|
+
):
|
|
399
|
+
await websocket.send_json(
|
|
400
|
+
{
|
|
401
|
+
"type": "error",
|
|
402
|
+
"id": payload.get("id") if isinstance(payload, dict) else None,
|
|
403
|
+
"error": "invalid_argument",
|
|
404
|
+
"message": "Protocol commands must include an integer id and string method.",
|
|
405
|
+
}
|
|
406
|
+
)
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
response = await manager.handle_command(payload)
|
|
410
|
+
await websocket.send_text(orjson.dumps(response).decode("utf-8"))
|
|
411
|
+
except WebSocketDisconnect:
|
|
412
|
+
pass
|
|
413
|
+
except Exception:
|
|
414
|
+
logger.exception("Protocol WebSocket error")
|
|
415
|
+
finally:
|
|
416
|
+
watchdog.cancel()
|
|
417
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
418
|
+
await watchdog
|
|
419
|
+
await manager.close()
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
# Route list
|
|
424
|
+
# ---------------------------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
event_streaming_routes: list[ApiRoute | ApiWebSocketRoute] = [
|
|
427
|
+
ApiRoute(
|
|
428
|
+
"/threads/{thread_id}/stream/events",
|
|
429
|
+
_thread_events,
|
|
430
|
+
methods=["POST"],
|
|
431
|
+
),
|
|
432
|
+
ApiRoute(
|
|
433
|
+
"/threads/{thread_id}/commands",
|
|
434
|
+
_thread_command,
|
|
435
|
+
methods=["POST"],
|
|
436
|
+
),
|
|
437
|
+
ApiWebSocketRoute("/threads/{thread_id}/stream/events", _thread_websocket),
|
|
438
|
+
]
|
|
@@ -933,6 +933,7 @@ async def search_crons(request: ApiRequest):
|
|
|
933
933
|
assistant_id=assistant_id,
|
|
934
934
|
thread_id=thread_id,
|
|
935
935
|
enabled=payload.get("enabled", None),
|
|
936
|
+
metadata=payload.get("metadata"),
|
|
936
937
|
limit=int(payload.get("limit", 10)),
|
|
937
938
|
offset=offset,
|
|
938
939
|
sort_by=payload.get("sort_by"),
|
|
@@ -963,6 +964,7 @@ async def count_crons(request: ApiRequest):
|
|
|
963
964
|
conn,
|
|
964
965
|
assistant_id=assistant_id,
|
|
965
966
|
thread_id=thread_id,
|
|
967
|
+
metadata=payload.get("metadata"),
|
|
966
968
|
)
|
|
967
969
|
return ApiResponse(count)
|
|
968
970
|
|