diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19a735069..8c37bac96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,7 @@ jobs: "libs/checkpoint-sqlite", "libs/checkpoint-postgres", "libs/prebuilt", + "libs/sdk-py", ] if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true' uses: ./.github/workflows/_test.yml diff --git a/docs/docs/cloud/reference/api/openapi.json b/docs/docs/cloud/reference/api/openapi.json index 8a3b7f5fc..7c3684811 100644 --- a/docs/docs/cloud/reference/api/openapi.json +++ b/docs/docs/cloud/reference/api/openapi.json @@ -28,14 +28,20 @@ { "name": "Store", "description": "Store is an API for managing persistent key-value store (long-term memory) that is available from any thread." + }, + { + "name": "MCP", + "description": "Model Context Protocol related endpoints for exposing an agent as an MCP server." + }, + { + "name": "System", + "description": "System endpoints for health checks, metrics, and server information." } ], "paths": { "/assistants": { "post": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Create Assistant", "description": "Create an assistant.\n\nAn initial version of the assistant will be created and the assistant is set to that version. To change versions, use the `POST /assistants/{assistant_id}/latest` endpoint.", "operationId": "create_assistant_assistants_post", @@ -95,9 +101,7 @@ }, "/assistants/search": { "post": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Search Assistants", "description": "Search for assistants.\n\nThis endpoint also functions as the endpoint to list all assistants.", "operationId": "search_assistants_assistants_search_post", @@ -149,11 +153,60 @@ } } }, + "/assistants/count": { + "post": { + "tags": ["Assistants"], + "summary": "Count Assistants", + "description": "Get the count of assistants matching the specified criteria.", + "operationId": "count_assistants_assistants_count_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssistantCountRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Count" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, "/assistants/{assistant_id}": { "get": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant", "description": "Get an assistant by ID.", "operationId": "get_assistant_assistants__assistant_id__get", @@ -195,9 +248,7 @@ } }, "delete": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Delete Assistant", "description": "Delete an assistant by ID.\n\nAll versions of the assistant will be deleted as well.", "operationId": "delete_assistant_assistants__assistant_id__delete", @@ -247,9 +298,7 @@ } }, "patch": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Patch Assistant", "description": "Update an assistant.", "operationId": "patch_assistant_assistants__assistant_id__patch", @@ -313,9 +362,7 @@ }, "/assistants/{assistant_id}/graph": { "get": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant Graph", "description": "Get an assistant by ID.", "operationId": "get_assistant_graph_assistants__assistant_id__graph_get", @@ -404,9 +451,7 @@ }, "/assistants/{assistant_id}/subgraphs": { "get": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant Subgraphs", "description": "Get an assistant's subgraphs.", "operationId": "get_assistant_subgraphs_assistants__assistant_id__subgraphs_get", @@ -470,9 +515,7 @@ }, "/assistants/{assistant_id}/subgraphs/{namespace}": { "get": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant Subgraphs by Namespace", "description": "Get an assistant's subgraphs filtered by namespace.", "operationId": "get_assistant_subgraphs_assistants__assistant_id__subgraphs__namespace__get", @@ -536,9 +579,7 @@ }, "/assistants/{assistant_id}/schemas": { "get": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant Schemas", "description": "Get an assistant by ID.", "operationId": "get_assistant_schemas_assistants__assistant_id__schemas_get", @@ -592,9 +633,7 @@ }, "/assistants/{assistant_id}/versions": { "post": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Get Assistant Versions", "description": "Get all versions of an assistant.", "operationId": "get_assistant_versions_assistants__assistant_id__versions_get", @@ -642,9 +681,7 @@ }, "/assistants/{assistant_id}/latest": { "post": { - "tags": [ - "Assistants" - ], + "tags": ["Assistants"], "summary": "Set Latest Assistant Version", "description": "Set the latest version for an assistant.", "operationId": "set_latest_assistant_version_assistants__assistant_id__versions_post", @@ -709,9 +746,7 @@ }, "/threads": { "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Create Thread", "description": "Create a thread.", "operationId": "create_thread_threads_post", @@ -761,9 +796,7 @@ }, "/threads/search": { "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Search Threads", "description": "Search for threads.\n\nThis endpoint also functions as the endpoint to list all threads.", "operationId": "search_threads_threads_search_post", @@ -805,11 +838,60 @@ } } }, + "/threads/count": { + "post": { + "tags": ["Threads"], + "summary": "Count Threads", + "description": "Get the count of threads matching the specified criteria.", + "operationId": "count_threads_threads_count_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ThreadCountRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Count" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, "/threads/{thread_id}/state": { "get": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread State", "description": "Get state for a thread.\n\nThe latest state of the thread (i.e. latest checkpoint) is returned.", "operationId": "get_latest_thread_state_threads__thread_id__state_get", @@ -862,9 +944,7 @@ } }, "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Update Thread State", "description": "Add state to a thread.", "operationId": "update_thread_state_threads__thread_id__state_post", @@ -918,9 +998,7 @@ }, "/threads/{thread_id}/state/{checkpoint_id}": { "get": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread State At Checkpoint", "description": "Get state for a thread at a specific checkpoint.", "operationId": "get_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get", @@ -987,9 +1065,7 @@ }, "/threads/{thread_id}/state/checkpoint": { "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread State At Checkpoint", "description": "Get state for a thread at a specific checkpoint.", "operationId": "post_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get", @@ -1053,9 +1129,7 @@ }, "/threads/{thread_id}/history": { "get": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread History", "description": "Get all past states for a thread.", "operationId": "get_thread_history_threads__thread_id__history_get", @@ -1120,9 +1194,7 @@ } }, "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread History Post", "description": "Get all past states for a thread.", "operationId": "get_thread_history_post_threads__thread_id__history_post", @@ -1180,9 +1252,7 @@ }, "/threads/{thread_id}/copy": { "post": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Copy Thread", "description": "Create a new thread with a copy of the state and checkpoints from an existing thread.", "operationId": "copy_thread_post_threads__thread_id__copy_post", @@ -1236,9 +1306,7 @@ }, "/threads/{thread_id}": { "get": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Get Thread", "description": "Get a thread by ID.", "operationId": "get_thread_threads__thread_id__get", @@ -1290,9 +1358,7 @@ } }, "delete": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Delete Thread", "description": "Delete a thread by ID.", "operationId": "delete_thread_threads__thread_id__delete", @@ -1342,9 +1408,7 @@ } }, "patch": { - "tags": [ - "Threads" - ], + "tags": ["Threads"], "summary": "Patch Thread", "description": "Update a thread.", "operationId": "patch_thread_threads__thread_id__patch", @@ -1408,9 +1472,7 @@ }, "/threads/{thread_id}/runs": { "get": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "List Runs", "description": "List runs for a thread.", "operationId": "list_runs_http_threads__thread_id__runs_get", @@ -1451,16 +1513,34 @@ "required": false, "schema": { "type": "string", - "enum": [ - "pending", - "error", - "success", - "timeout", - "interrupted" - ] + "enum": ["pending", "error", "success", "timeout", "interrupted"] }, "name": "status", "in": "query" + }, + { + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "run_id", + "thread_id", + "assistant_id", + "created_at", + "updated_at", + "status", + "metadata", + "kwargs", + "multitask_strategy" + ] + }, + "title": "Select", + "description": "Specify which fields to return. If not provided, all fields are returned." + }, + "name": "select", + "in": "query" } ], "responses": { @@ -1500,9 +1580,7 @@ } }, "post": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Create Background Run", "description": "Create a run in existing thread, return the run ID immediately. Don't wait for the final run output.", "operationId": "create_run_threads__thread_id__runs_post", @@ -1584,9 +1662,7 @@ }, "/threads/{thread_id}/runs/crons": { "post": { - "tags": [ - "Crons (Plus tier)" - ], + "tags": ["Crons (Plus tier)"], "summary": "Create Thread Cron", "description": "Create a cron to schedule runs on a thread.", "operationId": "create_thread_cron_threads__thread_id__runs_crons_post", @@ -1650,9 +1726,7 @@ }, "/threads/{thread_id}/runs/stream": { "post": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Create Run, Stream Output", "description": "Create a run in existing thread. Stream the output.", "operationId": "stream_run_threads__thread_id__runs_stream_post", @@ -1735,9 +1809,7 @@ }, "/threads/{thread_id}/runs/wait": { "post": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Create Run, Wait for Output", "description": "Create a run in existing thread. Wait for the final output and then return it.", "operationId": "wait_run_threads__thread_id__runs_wait_post", @@ -1817,9 +1889,7 @@ }, "/threads/{thread_id}/runs/{run_id}": { "get": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Get Run", "description": "Get a run by ID.", "operationId": "get_run_http_threads__thread_id__runs__run_id__get", @@ -1883,9 +1953,7 @@ } }, "delete": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Delete Run", "description": "Delete a run by ID.", "operationId": "delete_run_threads__thread_id__runs__run_id__delete", @@ -1949,9 +2017,7 @@ }, "/threads/{thread_id}/runs/{run_id}/join": { "get": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Join Run", "description": "Wait for a run to finish.", "operationId": "join_run_http_threads__thread_id__runs__run_id__join_get", @@ -2026,9 +2092,7 @@ }, "/threads/{thread_id}/runs/{run_id}/stream": { "get": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Join Run Stream", "description": "Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. If the run has been created with `stream_resumable=true`, the stream can be resumed from the last seen event ID.", "operationId": "stream_run_http_threads__thread_id__runs__run_id__join_get", @@ -2126,9 +2190,7 @@ }, "/threads/{thread_id}/runs/{run_id}/cancel": { "post": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Cancel Run", "operationId": "cancel_run_http_threads__thread_id__runs__run_id__cancel_post", "parameters": [ @@ -2171,10 +2233,7 @@ "required": false, "schema": { "type": "string", - "enum": [ - "interrupt", - "rollback" - ], + "enum": ["interrupt", "rollback"], "title": "Action", "default": "interrupt" }, @@ -2216,9 +2275,7 @@ }, "/runs/crons": { "post": { - "tags": [ - "Crons (Plus tier)" - ], + "tags": ["Crons (Plus tier)"], "summary": "Create Cron", "description": "Create a cron to schedule runs on new threads.", "operationId": "create_cron_runs_crons_post", @@ -2268,9 +2325,7 @@ }, "/runs/crons/search": { "post": { - "tags": [ - "Crons (Plus tier)" - ], + "tags": ["Crons (Plus tier)"], "summary": "Search Crons", "description": "Search all active crons", "operationId": "search_crons_runs_crons_post", @@ -2312,11 +2367,60 @@ } } }, + "/runs/crons/count": { + "post": { + "tags": ["Crons (Plus tier)"], + "summary": "Count Crons", + "description": "Get the count of crons matching the specified criteria.", + "operationId": "count_crons_runs_crons_count_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CronCountRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Count" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, "/runs/stream": { "post": { - "tags": [ - "Stateless Runs" - ], + "tags": ["Stateless Runs"], "summary": "Create Run, Stream Output", "description": "Create a run in a new thread, stream the output.", "operationId": "stream_run_stateless_runs_stream_post", @@ -2385,9 +2489,7 @@ }, "/runs/cancel": { "post": { - "tags": [ - "Thread Runs" - ], + "tags": ["Thread Runs"], "summary": "Cancel Runs", "description": "Cancel one or more runs. Can cancel runs by thread ID and run IDs, or by status filter.", "operationId": "cancel_runs_post", @@ -2397,10 +2499,7 @@ "required": false, "schema": { "type": "string", - "enum": [ - "interrupt", - "rollback" - ], + "enum": ["interrupt", "rollback"], "title": "Action", "default": "interrupt" }, @@ -2447,9 +2546,7 @@ }, "/runs/wait": { "post": { - "tags": [ - "Stateless Runs" - ], + "tags": ["Stateless Runs"], "summary": "Create Run, Wait for Output", "description": "Create a run in a new thread. Wait for the final output and then return it.", "operationId": "wait_run_stateless_runs_wait_post", @@ -2515,9 +2612,7 @@ }, "/runs": { "post": { - "tags": [ - "Stateless Runs" - ], + "tags": ["Stateless Runs"], "summary": "Create Background Run", "description": "Create a run in a new thread, return the run ID immediately. Don't wait for the final run output.", "operationId": "run_stateless_runs_post", @@ -2583,9 +2678,7 @@ }, "/runs/batch": { "post": { - "tags": [ - "Stateless Runs" - ], + "tags": ["Stateless Runs"], "summary": "Create Run Batch", "description": "Create a batch of runs in new threads, return immediately.", "operationId": "run_batch_stateless_runs_post", @@ -2643,9 +2736,7 @@ }, "/runs/crons/{cron_id}": { "delete": { - "tags": [ - "Crons (Plus tier)" - ], + "tags": ["Crons (Plus tier)"], "summary": "Delete Cron", "description": "Delete a cron by ID.", "operationId": "delete_cron_runs_crons__cron_id__delete", @@ -2695,9 +2786,7 @@ }, "/store/items": { "put": { - "tags": [ - "Store" - ], + "tags": ["Store"], "summary": "Store or update an item.", "operationId": "put_item", "requestBody": { @@ -2727,9 +2816,7 @@ } }, "delete": { - "tags": [ - "Store" - ], + "tags": ["Store"], "summary": "Delete an item.", "operationId": "delete_item", "requestBody": { @@ -2759,9 +2846,7 @@ } }, "get": { - "tags": [ - "Store" - ], + "tags": ["Store"], "summary": "Retrieve a single item.", "operationId": "get_item", "parameters": [ @@ -2821,9 +2906,7 @@ }, "/store/items/search": { "post": { - "tags": [ - "Store" - ], + "tags": ["Store"], "summary": "Search for items within a namespace prefix.", "operationId": "search_items", "requestBody": { @@ -2862,9 +2945,7 @@ }, "/store/namespaces": { "post": { - "tags": [ - "Store" - ], + "tags": ["Store"], "summary": "List namespaces with optional match conditions.", "operationId": "list_namespaces", "requestBody": { @@ -2913,9 +2994,7 @@ "required": true, "schema": { "type": "string", - "enum": [ - "application/json, text/event-stream" - ] + "enum": ["application/json, text/event-stream"] }, "description": "Accept header must include both 'application/json' and 'text/event-stream' media types." } @@ -2968,9 +3047,7 @@ "description": "Internal server error or unexpected failure." } }, - "tags": [ - "MCP" - ] + "tags": ["MCP"] }, "get": { "operationId": "get_mcp", @@ -2981,9 +3058,7 @@ "description": "GET method not allowed; streaming not supported." } }, - "tags": [ - "MCP" - ] + "tags": ["MCP"] }, "delete": { "operationId": "delete_mcp", @@ -2992,9 +3067,153 @@ "responses": { "404": {} }, - "tags": [ - "MCP" - ] + "tags": ["MCP"] + } + }, + "/info": { + "get": { + "tags": ["System"], + "summary": "Server Information", + "description": "Get server version information, feature flags, and metadata.", + "operationId": "server_info_info_get", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "title": "Version", + "description": "LangGraph API version" + }, + "langgraph_py_version": { + "type": "string", + "title": "LangGraph Python Version", + "description": "LangGraph Python library version" + }, + "flags": { + "type": "object", + "title": "Feature Flags", + "description": "Enabled features and capabilities" + }, + "metadata": { + "type": "object", + "title": "Metadata", + "description": "Server deployment metadata" + } + }, + "required": [ + "version", + "langgraph_py_version", + "flags", + "metadata" + ], + "title": "ServerInfo" + } + } + } + } + } + } + }, + "/metrics": { + "get": { + "tags": ["System"], + "summary": "System Metrics", + "description": "Get system metrics in Prometheus or JSON format for monitoring and observability.", + "operationId": "system_metrics_metrics_get", + "parameters": [ + { + "name": "format", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": ["prometheus", "json"], + "default": "prometheus", + "title": "Output Format", + "description": "Response format: prometheus (default) or json" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "string", + "title": "Prometheus Metrics", + "description": "Metrics in Prometheus exposition format" + } + }, + "application/json": { + "schema": { + "type": "object", + "title": "JSON Metrics", + "description": "Metrics in JSON format including queue stats, worker stats, and HTTP metrics" + } + } + } + } + } + } + }, + "/ok": { + "get": { + "tags": ["System"], + "summary": "Health Check", + "description": "Check the health status of the server. Optionally check database connectivity.", + "operationId": "health_check_ok_get", + "parameters": [ + { + "name": "check_db", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "enum": [0, 1], + "default": 0, + "title": "Check Database", + "description": "Whether to check database connectivity (0=false, 1=true)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "type": "boolean", + "const": true, + "title": "OK", + "description": "Indicates the server is healthy" + } + }, + "required": ["ok"], + "title": "HealthResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } } } }, @@ -3035,6 +3254,11 @@ "title": "Config", "description": "The assistant config." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "created_at": { "type": "string", "format": "date-time", @@ -3063,10 +3287,7 @@ "description": "The name of the assistant" }, "description": { - "type": [ - "string", - "null" - ], + "type": ["string", "null"], "title": "Assistant Description", "description": "The description of the assistant" } @@ -3100,6 +3321,11 @@ "title": "Config", "description": "Configuration to use for the graph. Useful when graph is configurable and you want to create different assistants based on different configurations." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "metadata": { "type": "object", "title": "Metadata", @@ -3107,10 +3333,7 @@ }, "if_exists": { "type": "string", - "enum": [ - "raise", - "do_nothing" - ], + "enum": ["raise", "do_nothing"], "title": "If Exists", "description": "How to handle duplicate creation. Must be either 'raise' (raise error if duplicate), or 'do_nothing' (return existing assistant).", "default": "raise" @@ -3121,18 +3344,13 @@ "description": "The name of the assistant. Defaults to 'Untitled'." }, "description": { - "type": [ - "string", - "null" - ], + "type": ["string", "null"], "title": "Description", "description": "The description of the assistant. Defaults to null." } }, "type": "object", - "required": [ - "graph_id" - ], + "required": ["graph_id"], "title": "AssistantCreate", "description": "Payload for creating an assistant." }, @@ -3148,6 +3366,11 @@ "title": "Config", "description": "Configuration to use for the graph. Useful when graph is configurable and you want to update the assistant's configuration." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "metadata": { "type": "object", "title": "Metadata", @@ -3209,6 +3432,12 @@ "title": "Cron Id", "description": "The ID of the cron." }, + "assistant_id": { + "type": ["string", "null"], + "format": "uuid", + "title": "Assistant Id", + "description": "The ID of the assistant." + }, "thread_id": { "type": "string", "format": "uuid", @@ -3238,10 +3467,26 @@ "title": "Updated At", "description": "The last time the cron was updated." }, + "user_id": { + "type": ["string", "null"], + "title": "User Id", + "description": "The ID of the user." + }, "payload": { "type": "object", "title": "Payload", "description": "The run payload to use for creating new run." + }, + "next_run_date": { + "type": ["string", "null"], + "format": "date-time", + "title": "Next Run Date", + "description": "The next run date of the cron." + }, + "metadata": { + "type": "object", + "title": "Metadata", + "description": "The cron metadata." } }, "type": "object", @@ -3326,6 +3571,11 @@ "title": "Config", "description": "The configuration for the assistant." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "webhook": { "type": "string", "maxLength": 65536, @@ -3338,9 +3588,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -3356,9 +3604,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -3372,22 +3618,14 @@ }, "multitask_strategy": { "type": "string", - "enum": [ - "reject", - "rollback", - "interrupt", - "enqueue" - ], + "enum": ["reject", "rollback", "interrupt", "enqueue"], "title": "Multitask Strategy", "description": "Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'.", - "default": "reject" + "default": "enqueue" } }, "type": "object", - "required": [ - "assistant_id", - "schedule" - ], + "required": ["assistant_id", "schedule"], "title": "CronCreate", "description": "Payload for creating a cron job." }, @@ -3397,7 +3635,7 @@ "type": "string", "format": "uuid", "title": "Assistant Id", - "description": "The assistant ID or graph name to search for." + "description": "The assistant ID or graph name to filter by using exact match." }, "thread_id": { "type": "string", @@ -3425,7 +3663,15 @@ "title": "Sort By", "description": "The field to sort by.", "default": "created_at", - "enum": ["cron_id", "assistant_id", "thread_id", "next_run_date", "end_time", "created_at", "updated_at"] + "enum": [ + "cron_id", + "assistant_id", + "thread_id", + "next_run_date", + "end_time", + "created_at", + "updated_at" + ] }, "sort_order": { "type": "string", @@ -3433,6 +3679,28 @@ "description": "The order to sort by.", "default": "desc", "enum": ["asc", "desc"] + }, + "select": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "cron_id", + "assistant_id", + "thread_id", + "end_time", + "schedule", + "created_at", + "updated_at", + "user_id", + "payload", + "next_run_date", + "metadata", + "now" + ] + }, + "title": "Select", + "description": "Specify which fields to return. If not provided, all fields are returned." } }, "type": "object", @@ -3440,6 +3708,25 @@ "title": "CronSearch", "description": "Payload for listing crons" }, + "CronCountRequest": { + "properties": { + "assistant_id": { + "type": "string", + "format": "uuid", + "title": "Assistant Id", + "description": "The assistant ID or graph name to search for." + }, + "thread_id": { + "type": "string", + "format": "uuid", + "title": "Thread Id", + "description": "The thread ID to search for." + } + }, + "type": "object", + "title": "CronCountRequest", + "description": "Payload for counting crons" + }, "GraphSchema": { "properties": { "graph_id": { @@ -3466,14 +3753,15 @@ "type": "object", "title": "Config Schema", "description": "The schema for the graph config. Missing if unable to generate JSON schema from graph." + }, + "context_schema": { + "type": "object", + "title": "Context Schema", + "description": "The schema for the graph context. Missing if unable to generate JSON schema from graph." } }, "type": "object", - "required": [ - "graph_id", - "state_schema", - "config_schema" - ], + "required": ["graph_id", "state_schema"], "title": "GraphSchema", "description": "Defines the structure and properties of a graph." }, @@ -3498,15 +3786,15 @@ "type": "object", "title": "Config Schema", "description": "The schema for the graph config. Missing if unable to generate JSON schema from graph." + }, + "context_schema": { + "type": "object", + "title": "Context Schema", + "description": "The schema for the graph context. Missing if unable to generate JSON schema from graph." } }, "type": "object", - "required": [ - "input_schema", - "output_schema", - "state_schema", - "config_schema" - ], + "required": ["input_schema", "output_schema", "state_schema"], "title": "GraphSchemaNoId", "description": "Defines the structure and properties of a graph without an ID." }, @@ -3516,7 +3804,7 @@ "$ref": "#/components/schemas/GraphSchemaNoId" }, "title": "Subgraphs", - "description": "Map of graph name to graph schema metadata (`input_schema`, `output_schema`, `state_schema`, `config_schema`)." + "description": "Map of graph name to graph schema metadata (`input_schema`, `output_schema`, `state_schema`, `config_schema`, `context_schema`)." }, "Run": { "properties": { @@ -3574,12 +3862,7 @@ }, "multitask_strategy": { "type": "string", - "enum": [ - "reject", - "rollback", - "interrupt", - "enqueue" - ], + "enum": ["reject", "rollback", "interrupt", "enqueue"], "title": "Multitask Strategy", "description": "Strategy to handle concurrent runs on the same thread." } @@ -3609,22 +3892,12 @@ "description": "The node to send the message to." }, "input": { - "type": [ - "object", - "array", - "number", - "string", - "boolean", - "null" - ], + "type": ["object", "array", "number", "string", "boolean", "null"], "title": "Message", "description": "The message to send." } }, - "required": [ - "node", - "input" - ] + "required": ["node", "input"] }, "Command": { "type": "object", @@ -3632,23 +3905,12 @@ "description": "The command to run.", "properties": { "update": { - "type": [ - "object", - "array", - "null" - ], + "type": ["object", "array", "null"], "title": "Update", "description": "An update to the state." }, "resume": { - "type": [ - "object", - "array", - "number", - "string", - "boolean", - "null" - ], + "type": ["object", "array", "number", "string", "boolean", "null"], "title": "Resume", "description": "A value to pass to an interrupted node." }, @@ -3766,6 +4028,11 @@ "title": "Config", "description": "The configuration for the assistant." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "webhook": { "type": "string", "maxLength": 65536, @@ -3778,9 +4045,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -3796,9 +4061,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -3819,6 +4082,8 @@ "values", "messages", "messages-tuple", + "tasks", + "checkpoints", "updates", "events", "debug", @@ -3833,6 +4098,8 @@ "values", "messages", "messages-tuple", + "tasks", + "checkpoints", "updates", "events", "debug", @@ -3842,9 +4109,7 @@ ], "title": "Stream Mode", "description": "The stream mode(s) to use.", - "default": [ - "values" - ] + "default": ["values"] }, "stream_subgraphs": { "type": "boolean", @@ -3860,10 +4125,7 @@ }, "on_disconnect": { "type": "string", - "enum": [ - "cancel", - "continue" - ], + "enum": ["cancel", "continue"], "title": "On Disconnect", "description": "The disconnect mode to use. Must be one of 'cancel' or 'continue'.", "default": "cancel" @@ -3878,22 +4140,14 @@ }, "multitask_strategy": { "type": "string", - "enum": [ - "reject", - "rollback", - "interrupt", - "enqueue" - ], + "enum": ["reject", "rollback", "interrupt", "enqueue"], "title": "Multitask Strategy", "description": "Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'.", - "default": "reject" + "default": "enqueue" }, "if_not_exists": { "type": "string", - "enum": [ - "create", - "reject" - ], + "enum": ["create", "reject"], "title": "If Not Exists", "description": "How to handle missing thread. Must be either 'reject' (raise error if missing), or 'create' (create new thread).", "default": "reject" @@ -3911,9 +4165,7 @@ } }, "type": "object", - "required": [ - "assistant_id" - ], + "required": ["assistant_id"], "title": "RunCreateStateful", "description": "Payload for creating a run." }, @@ -4005,6 +4257,11 @@ "title": "Config", "description": "The configuration for the assistant." }, + "context": { + "type": "object", + "title": "Context", + "description": "Static context added to the assistant." + }, "webhook": { "type": "string", "maxLength": 65536, @@ -4017,9 +4274,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -4035,9 +4290,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "*" - ] + "enum": ["*"] }, { "items": { @@ -4058,6 +4311,8 @@ "values", "messages", "messages-tuple", + "tasks", + "checkpoints", "updates", "events", "debug", @@ -4072,6 +4327,8 @@ "values", "messages", "messages-tuple", + "tasks", + "checkpoints", "updates", "events", "debug", @@ -4081,9 +4338,7 @@ ], "title": "Stream Mode", "description": "The stream mode(s) to use.", - "default": [ - "values" - ] + "default": ["values"] }, "feedback_keys": { "items": { @@ -4107,20 +4362,14 @@ }, "on_completion": { "type": "string", - "enum": [ - "delete", - "keep" - ], + "enum": ["delete", "keep"], "title": "On Completion", "description": "Whether to delete or keep the thread created for a stateless run. Must be one of 'delete' or 'keep'.", "default": "delete" }, "on_disconnect": { "type": "string", - "enum": [ - "cancel", - "continue" - ], + "enum": ["cancel", "continue"], "title": "On Disconnect", "description": "The disconnect mode to use. Must be one of 'cancel' or 'continue'.", "default": "cancel" @@ -4138,9 +4387,7 @@ } }, "type": "object", - "required": [ - "assistant_id" - ], + "required": ["assistant_id"], "title": "RunCreateStateless", "description": "Payload for creating a run." }, @@ -4185,18 +4432,52 @@ }, "sort_order": { "type": "string", - "enum": [ - "asc", - "desc" - ], + "enum": ["asc", "desc"], "title": "Sort Order", "description": "The order to sort by." + }, + "select": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "assistant_id", + "graph_id", + "name", + "description", + "config", + "context", + "created_at", + "updated_at", + "metadata", + "version" + ] + }, + "title": "Select", + "description": "Specify which fields to return. If not provided, all fields are returned." } }, "type": "object", "title": "AssistantSearchRequest", "description": "Payload for listing assistants." }, + "AssistantCountRequest": { + "properties": { + "metadata": { + "type": "object", + "title": "Metadata", + "description": "Metadata to filter by. Exact match filter for each KV pair." + }, + "graph_id": { + "type": "string", + "title": "Graph Id", + "description": "The ID of the graph to filter by. The graph ID is normally set in your langgraph.json configuration." + } + }, + "type": "object", + "title": "AssistantCountRequest", + "description": "Payload for counting assistants." + }, "AssistantVersionsSearchRequest": { "properties": { "metadata": { @@ -4238,12 +4519,7 @@ }, "status": { "type": "string", - "enum": [ - "idle", - "busy", - "interrupted", - "error" - ], + "enum": ["idle", "busy", "interrupted", "error"], "title": "Status", "description": "Thread status to filter on." }, @@ -4264,29 +4540,63 @@ }, "sort_by": { "type": "string", - "enum": [ - "thread_id", - "status", - "created_at", - "updated_at" - ], + "enum": ["thread_id", "status", "created_at", "updated_at"], "title": "Sort By", "description": "Sort by field." }, "sort_order": { "type": "string", - "enum": [ - "asc", - "desc" - ], + "enum": ["asc", "desc"], "title": "Sort Order", "description": "Sort order." + }, + "select": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "thread_id", + "created_at", + "updated_at", + "metadata", + "config", + "context", + "status", + "values", + "interrupts" + ] + }, + "title": "Select", + "description": "Specify which fields to return. If not provided, all fields are returned." } }, "type": "object", "title": "ThreadSearchRequest", "description": "Payload for listing threads." }, + "ThreadCountRequest": { + "properties": { + "metadata": { + "type": "object", + "title": "Metadata", + "description": "Thread metadata to filter on." + }, + "values": { + "type": "object", + "title": "Values", + "description": "State values to filter on." + }, + "status": { + "type": "string", + "enum": ["idle", "busy", "interrupted", "error"], + "title": "Status", + "description": "Thread status to filter on." + } + }, + "type": "object", + "title": "ThreadCountRequest", + "description": "Payload for counting threads." + }, "Thread": { "properties": { "thread_id": { @@ -4312,14 +4622,14 @@ "title": "Metadata", "description": "The thread metadata." }, + "config": { + "type": "object", + "title": "Config", + "description": "The thread config." + }, "status": { "type": "string", - "enum": [ - "idle", - "busy", - "interrupted", - "error" - ], + "enum": ["idle", "busy", "interrupted", "error"], "title": "Status", "description": "The status of the thread." }, @@ -4327,6 +4637,11 @@ "type": "object", "title": "Values", "description": "The current state of the thread." + }, + "interrupts": { + "type": "object", + "title": "Interrupts", + "description": "The current interrupts of the thread." } }, "type": "object", @@ -4354,10 +4669,7 @@ }, "if_exists": { "type": "string", - "enum": [ - "raise", - "do_nothing" - ], + "enum": ["raise", "do_nothing"], "title": "If Exists", "description": "How to handle duplicate creation. Must be either 'raise' (raise error if duplicate), or 'do_nothing' (return existing thread).", "default": "raise" @@ -4369,9 +4681,7 @@ "properties": { "strategy": { "type": "string", - "enum": [ - "delete" - ], + "enum": ["delete"], "description": "The TTL strategy. 'delete' removes the entire thread.", "default": "delete" }, @@ -4393,9 +4703,7 @@ } } }, - "required": [ - "updates" - ] + "required": ["updates"] } } }, @@ -4428,9 +4736,7 @@ "description": "Include subgraph states." } }, - "required": [ - "checkpoint" - ], + "required": ["checkpoint"], "type": "object", "title": "ThreadStateCheckpointRequest", "description": "Payload for getting the state of a thread at a checkpoint." @@ -4476,7 +4782,9 @@ }, "interrupts": { "type": "array", - "items": {} + "items": { + "$ref": "#/components/schemas/Interrupt" + } }, "checkpoint": { "$ref": "#/components/schemas/CheckpointConfig", @@ -4486,10 +4794,7 @@ "$ref": "#/components/schemas/ThreadState" } }, - "required": [ - "id", - "name" - ] + "required": ["id", "name"] }, "type": "array", "title": "Tasks" @@ -4509,16 +4814,16 @@ "parent_checkpoint": { "type": "object", "title": "Parent Checkpoint" + }, + "interrupts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Interrupt" + } } }, "type": "object", - "required": [ - "values", - "next", - "checkpoint", - "metadata", - "created_at" - ], + "required": ["values", "next", "checkpoint", "metadata", "created_at"], "title": "ThreadState" }, "ThreadStateSearch": { @@ -4527,7 +4832,7 @@ "type": "integer", "title": "Limit", "description": "The maximum number of states to return.", - "default": 10, + "default": 1, "maximum": 1000, "minimum": 1 }, @@ -4617,9 +4922,7 @@ "description": "Update the state as if this node had just executed." } }, - "required": [ - "as_node" - ], + "required": ["as_node"], "type": "object" }, "ThreadStateUpdateResponse": { @@ -4658,11 +4961,7 @@ }, "StorePutRequest": { "type": "object", - "required": [ - "namespace", - "key", - "value" - ], + "required": ["namespace", "key", "value"], "properties": { "namespace": { "type": "array", @@ -4688,9 +4987,7 @@ }, "StoreDeleteRequest": { "type": "object", - "required": [ - "key" - ], + "required": ["key"], "properties": { "namespace": { "type": "array", @@ -4713,10 +5010,7 @@ "type": "object", "properties": { "namespace_prefix": { - "type": [ - "array", - "null" - ], + "type": ["array", "null"], "items": { "type": "string" }, @@ -4724,10 +5018,7 @@ "description": "List of strings representing the namespace prefix." }, "filter": { - "type": [ - "object", - "null" - ], + "type": ["object", "null"], "additionalProperties": true, "title": "Filter", "description": "Optional dictionary of key-value pairs to filter results." @@ -4745,10 +5036,7 @@ "description": "Number of items to skip before returning results (default is 0)." }, "query": { - "type": [ - "string", - "null" - ], + "type": ["string", "null"], "title": "Query", "description": "Query string for semantic/vector search." } @@ -4796,13 +5084,7 @@ }, "Item": { "type": "object", - "required": [ - "namespace", - "key", - "value", - "created_at", - "updated_at" - ], + "required": ["namespace", "key", "value", "created_at", "updated_at"], "properties": { "namespace": { "type": "array", @@ -4839,11 +5121,7 @@ "properties": { "status": { "type": "string", - "enum": [ - "pending", - "running", - "all" - ], + "enum": ["pending", "running", "all"], "title": "Status", "description": "Filter runs by status to cancel. Must be one of 'pending', 'running', or 'all'." }, @@ -4865,23 +5143,16 @@ }, "oneOf": [ { - "required": [ - "status" - ] + "required": ["status"] }, { - "required": [ - "thread_id", - "run_ids" - ] + "required": ["thread_id", "run_ids"] } ] }, "SearchItemsResponse": { "type": "object", - "required": [ - "items" - ], + "required": ["items"], "properties": { "items": { "type": "array", @@ -4954,6 +5225,19 @@ } } } + }, + "Interrupt": { + "type": "object", + "properties": { + "id": { + "type": ["string", "null"] + }, + "value": { + "type": "object" + } + }, + "title": "Interrupt", + "required": ["value"] } } } diff --git a/libs/sdk-py/Makefile b/libs/sdk-py/Makefile index e0848428c..346ea786f 100644 --- a/libs/sdk-py/Makefile +++ b/libs/sdk-py/Makefile @@ -1,7 +1,7 @@ -.PHONY: lint format +.PHONY: lint format test test: - echo "No tests to run" + uv run pytest tests ###################### # LINTING AND FORMATTING diff --git a/libs/sdk-py/langgraph_sdk/client.py b/libs/sdk-py/langgraph_sdk/client.py index 32447e863..54ec6371f 100644 --- a/libs/sdk-py/langgraph_sdk/client.py +++ b/libs/sdk-py/langgraph_sdk/client.py @@ -33,6 +33,7 @@ import langgraph_sdk from langgraph_sdk.schema import ( All, Assistant, + AssistantSelectField, AssistantSortBy, AssistantVersion, CancelAction, @@ -41,6 +42,7 @@ from langgraph_sdk.schema import ( Config, Context, Cron, + CronSelectField, CronSortBy, DisconnectMode, GraphSchema, @@ -54,6 +56,7 @@ from langgraph_sdk.schema import ( Run, RunCreate, RunCreateMetadata, + RunSelectField, RunStatus, SearchItemsResponse, SortOrder, @@ -61,6 +64,7 @@ from langgraph_sdk.schema import ( StreamPart, Subgraphs, Thread, + ThreadSelectField, ThreadSortBy, ThreadState, ThreadStatus, @@ -869,6 +873,7 @@ class AssistantsClient: offset: int = 0, sort_by: AssistantSortBy | None = None, sort_order: SortOrder | None = None, + select: list[AssistantSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Assistant]: """Search for assistants. @@ -910,6 +915,8 @@ class AssistantsClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select return await self.http.post( "/assistants/search", json=payload, @@ -1177,6 +1184,7 @@ class ThreadsClient: offset: int = 0, sort_by: ThreadSortBy | None = None, sort_order: SortOrder | None = None, + select: list[ThreadSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Thread]: """Search for threads. @@ -1222,6 +1230,8 @@ class ThreadsClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select return await self.http.post( "/threads/search", json=payload, @@ -2146,6 +2156,7 @@ class RunsClient: limit: int = 10, offset: int = 0, status: RunStatus | None = None, + select: list[RunSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Run]: """List runs. @@ -2178,6 +2189,8 @@ class RunsClient: } if status is not None: params["status"] = status + if select: + params["select"] = select return await self.http.get( f"/threads/{thread_id}/runs", params=params, headers=headers ) @@ -2574,6 +2587,7 @@ class CronClient: offset: int = 0, sort_by: CronSortBy | None = None, sort_order: SortOrder | None = None, + select: list[CronSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Cron]: """Get a list of cron jobs. @@ -2636,6 +2650,8 @@ class CronClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select payload = {k: v for k, v in payload.items() if v is not None} return await self.http.post("/runs/crons/search", json=payload, headers=headers) @@ -3637,6 +3653,7 @@ class SyncAssistantsClient: offset: int = 0, sort_by: AssistantSortBy | None = None, sort_order: SortOrder | None = None, + select: list[AssistantSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Assistant]: """Search for assistants. @@ -3676,6 +3693,8 @@ class SyncAssistantsClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select return self.http.post( "/assistants/search", json=payload, @@ -3948,6 +3967,7 @@ class SyncThreadsClient: offset: int = 0, sort_by: ThreadSortBy | None = None, sort_order: SortOrder | None = None, + select: list[ThreadSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Thread]: """Search for threads. @@ -3990,6 +4010,8 @@ class SyncThreadsClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select return self.http.post("/threads/search", json=payload, headers=headers) def copy( @@ -4898,6 +4920,8 @@ class SyncRunsClient: *, limit: int = 10, offset: int = 0, + status: RunStatus | None = None, + select: list[RunSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Run]: """List runs. @@ -4923,8 +4947,13 @@ class SyncRunsClient: ``` """ # noqa: E501 + params: dict[str, Any] = {"limit": limit, "offset": offset} + if status is not None: + params["status"] = status + if select: + params["select"] = select return self.http.get( - f"/threads/{thread_id}/runs?limit={limit}&offset={offset}", headers=headers + f"/threads/{thread_id}/runs", params=params, headers=headers ) def get( @@ -5319,6 +5348,7 @@ class SyncCronClient: offset: int = 0, sort_by: CronSortBy | None = None, sort_order: SortOrder | None = None, + select: list[CronSelectField] | None = None, headers: dict[str, str] | None = None, ) -> list[Cron]: """Get a list of cron jobs. @@ -5380,6 +5410,8 @@ class SyncCronClient: payload["sort_by"] = sort_by if sort_order: payload["sort_order"] = sort_order + if select: + payload["select"] = select payload = {k: v for k, v in payload.items() if v is not None} return self.http.post("/runs/crons/search", json=payload, headers=headers) diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index 18759c30b..b2615e992 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -348,6 +348,62 @@ class Cron(TypedDict): """The metadata of the cron.""" +# Select field aliases for client-side typing of `select` parameters. +# These mirror the server's allowed field sets. + +AssistantSelectField = Literal[ + "assistant_id", + "graph_id", + "name", + "description", + "config", + "context", + "created_at", + "updated_at", + "metadata", + "version", +] + +ThreadSelectField = Literal[ + "thread_id", + "created_at", + "updated_at", + "metadata", + "config", + "context", + "status", + "values", + "interrupts", +] + +RunSelectField = Literal[ + "run_id", + "thread_id", + "assistant_id", + "created_at", + "updated_at", + "status", + "metadata", + "kwargs", + "multitask_strategy", +] + +CronSelectField = Literal[ + "cron_id", + "assistant_id", + "thread_id", + "end_time", + "schedule", + "created_at", + "updated_at", + "user_id", + "payload", + "next_run_date", + "metadata", + "now", +] + + class RunCreate(TypedDict): """Defines the parameters for initiating a background run.""" diff --git a/libs/sdk-py/tests/test_select_fields_sync.py b/libs/sdk-py/tests/test_select_fields_sync.py new file mode 100644 index 000000000..9cf8f97b0 --- /dev/null +++ b/libs/sdk-py/tests/test_select_fields_sync.py @@ -0,0 +1,76 @@ +import functools +import json +import os +from pathlib import Path +from typing import get_args + +from langgraph_sdk.schema import ( + AssistantSelectField, + CronSelectField, + RunSelectField, + ThreadSelectField, +) + +current_dir = os.path.dirname(os.path.abspath(__file__)) + + +@functools.cache +def _load_spec() -> dict: + with ( + Path(current_dir).parents[2] + / "docs" + / "docs" + / "cloud" + / "reference" + / "api" + / "openapi.json" + ).open() as f: + return json.load(f) + + +def _enum_from_request_select(spec: dict, path: str, method: str) -> set[str]: + schema = spec["paths"][path][method]["requestBody"]["content"]["application/json"][ + "schema" + ] + if "properties" in schema: + props = schema["properties"] + elif "$ref" in schema: + component = spec + index = schema["$ref"].split("/")[1:] + for part in index: + component = component[part] + props = component["properties"] + else: + raise ValueError(f"Unknown schema: {schema}") + sel = props["select"] + return set(sel["items"]["enum"]) + + +def _enum_from_query_select(spec: dict, path: str, method: str) -> set[str]: + params = spec["paths"][path][method]["parameters"] + sel = next(p for p in params if p["name"] == "select") + return set(sel["schema"]["items"]["enum"]) + + +def test_assistants_select_enum_matches_sdk(): + spec = _load_spec() + expected = set(get_args(AssistantSelectField)) + assert _enum_from_request_select(spec, "/assistants/search", "post") == expected + + +def test_threads_select_enum_matches_sdk(): + spec = _load_spec() + expected = set(get_args(ThreadSelectField)) + assert _enum_from_request_select(spec, "/threads/search", "post") == expected + + +def test_runs_select_enum_matches_sdk(): + spec = _load_spec() + expected = set(get_args(RunSelectField)) + assert _enum_from_query_select(spec, "/threads/{thread_id}/runs", "get") == expected + + +def test_crons_select_enum_matches_sdk(): + spec = _load_spec() + expected = set(get_args(CronSelectField)) + assert _enum_from_request_select(spec, "/runs/crons/search", "post") == expected