From 63bd852da968936a497daeefa9149f05386c8c84 Mon Sep 17 00:00:00 2001 From: Rafid Saad Date: Tue, 3 Feb 2026 10:36:48 -0800 Subject: [PATCH] feat(sdk-py): add support for enabling/disabling crons (#6740) # Description - Update Python SDK to support enabling/disabling crons - Pass `enabled` to cron `create`, `create_for_thread`, and `search` methods --- libs/sdk-py/langgraph_sdk/client.py | 32 +++++++++++++++++++++---- libs/sdk-py/langgraph_sdk/schema.py | 3 +++ libs/sdk-py/tests/test_crons_client.py | 33 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/libs/sdk-py/langgraph_sdk/client.py b/libs/sdk-py/langgraph_sdk/client.py index 83fb2e3d9..60a1d66f7 100644 --- a/libs/sdk-py/langgraph_sdk/client.py +++ b/libs/sdk-py/langgraph_sdk/client.py @@ -2984,6 +2984,7 @@ class CronClient: webhook: str | None = None, multitask_strategy: str | None = None, end_time: datetime | None = None, + enabled: bool | None = None, headers: Mapping[str, str] | None = None, params: QueryParamTypes | None = None, ) -> Run: @@ -3009,6 +3010,7 @@ class CronClient: multitask_strategy: Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'. end_time: The time to stop running the cron job. If not provided, the cron job will run indefinitely. + enabled: Whether the cron job is enabled or not. headers: Optional custom headers to include with the request. params: Optional query parameters to include with the request. @@ -3029,7 +3031,8 @@ class CronClient: interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", - multitask_strategy="interrupt" + multitask_strategy="interrupt", + enabled=True, ) ``` """ @@ -3045,6 +3048,7 @@ class CronClient: "interrupt_after": interrupt_after, "webhook": webhook, "end_time": end_time.isoformat() if end_time else None, + "enabled": enabled, } if multitask_strategy: payload["multitask_strategy"] = multitask_strategy @@ -3072,6 +3076,7 @@ class CronClient: on_run_completed: OnCompletionBehavior | None = None, multitask_strategy: str | None = None, end_time: datetime | None = None, + enabled: bool | None = None, headers: Mapping[str, str] | None = None, params: QueryParamTypes | None = None, ) -> Run: @@ -3098,6 +3103,7 @@ class CronClient: multitask_strategy: Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'. end_time: The time to stop running the cron job. If not provided, the cron job will run indefinitely. + enabled: Whether the cron job is enabled or not. headers: Optional custom headers to include with the request. params: Optional query parameters to include with the request. @@ -3117,7 +3123,8 @@ class CronClient: interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", - multitask_strategy="interrupt" + multitask_strategy="interrupt", + enabled=True, ) ``` @@ -3135,6 +3142,7 @@ class CronClient: "webhook": webhook, "on_run_completed": on_run_completed, "end_time": end_time.isoformat() if end_time else None, + "enabled": enabled, } if multitask_strategy: payload["multitask_strategy"] = multitask_strategy @@ -3177,6 +3185,7 @@ class CronClient: *, assistant_id: str | None = None, thread_id: str | None = None, + enabled: bool | None = None, limit: int = 10, offset: int = 0, sort_by: CronSortBy | None = None, @@ -3190,6 +3199,7 @@ class CronClient: Args: assistant_id: The assistant ID or graph name to search for. thread_id: the thread ID to search for. + enabled: The enabled status to search for. limit: The maximum number of results to return. offset: The number of results to skip. headers: Optional custom headers to include with the request. @@ -3205,6 +3215,7 @@ class CronClient: cron_jobs = await client.crons.search( assistant_id="my_assistant_id", thread_id="my_thread_id", + enabled=True, limit=5, offset=5, ) @@ -3239,6 +3250,7 @@ class CronClient: payload = { "assistant_id": assistant_id, "thread_id": thread_id, + "enabled": enabled, "limit": limit, "offset": offset, } @@ -6294,6 +6306,7 @@ class SyncCronClient: webhook: str | None = None, multitask_strategy: str | None = None, end_time: datetime | None = None, + enabled: bool | None = None, headers: Mapping[str, str] | None = None, params: QueryParamTypes | None = None, ) -> Run: @@ -6317,6 +6330,7 @@ class SyncCronClient: multitask_strategy: Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'. end_time: The time to stop running the cron job. If not provided, the cron job will run indefinitely. + enabled: Whether the cron job is enabled. By default, it is considered enabled. headers: Optional custom headers to include with the request. Returns: @@ -6336,7 +6350,8 @@ class SyncCronClient: interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", - multitask_strategy="interrupt" + multitask_strategy="interrupt", + enabled=True ) ``` """ @@ -6353,6 +6368,7 @@ class SyncCronClient: "webhook": webhook, "multitask_strategy": multitask_strategy, "end_time": end_time.isoformat() if end_time else None, + "enabled": enabled, } payload = {k: v for k, v in payload.items() if v is not None} return self.http.post( @@ -6378,6 +6394,7 @@ class SyncCronClient: on_run_completed: OnCompletionBehavior | None = None, multitask_strategy: str | None = None, end_time: datetime | None = None, + enabled: bool | None = None, headers: Mapping[str, str] | None = None, params: QueryParamTypes | None = None, ) -> Run: @@ -6404,6 +6421,7 @@ class SyncCronClient: multitask_strategy: Multitask strategy to use. Must be one of 'reject', 'interrupt', 'rollback', or 'enqueue'. end_time: The time to stop running the cron job. If not provided, the cron job will run indefinitely. + enabled: Whether the cron job is enabled. By default, it is considered enabled. headers: Optional custom headers to include with the request. Returns: @@ -6423,7 +6441,8 @@ class SyncCronClient: interrupt_before=["node_to_stop_before_1","node_to_stop_before_2"], interrupt_after=["node_to_stop_after_1","node_to_stop_after_2"], webhook="https://my.fake.webhook.com", - multitask_strategy="interrupt" + multitask_strategy="interrupt", + enabled=True ) ``` @@ -6442,6 +6461,7 @@ class SyncCronClient: "on_run_completed": on_run_completed, "multitask_strategy": multitask_strategy, "end_time": end_time.isoformat() if end_time else None, + "enabled": enabled, } payload = {k: v for k, v in payload.items() if v is not None} return self.http.post( @@ -6482,6 +6502,7 @@ class SyncCronClient: *, assistant_id: str | None = None, thread_id: str | None = None, + enabled: bool | None = None, limit: int = 10, offset: int = 0, sort_by: CronSortBy | None = None, @@ -6495,6 +6516,7 @@ class SyncCronClient: Args: assistant_id: The assistant ID or graph name to search for. thread_id: the thread ID to search for. + enabled: Whether the cron job is enabled. limit: The maximum number of results to return. offset: The number of results to skip. headers: Optional custom headers to include with the request. @@ -6509,6 +6531,7 @@ class SyncCronClient: cron_jobs = client.crons.search( assistant_id="my_assistant_id", thread_id="my_thread_id", + enabled=True, limit=5, offset=5, ) @@ -6542,6 +6565,7 @@ class SyncCronClient: payload = { "assistant_id": assistant_id, "thread_id": thread_id, + "enabled": enabled, "limit": limit, "offset": offset, } diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index fa66b4d92..c012e883e 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -372,6 +372,8 @@ class Cron(TypedDict): """The next run date of the cron.""" metadata: dict """The metadata of the cron.""" + enabled: bool + """Whether the cron is enabled.""" # Select field aliases for client-side typing of `select` parameters. @@ -428,6 +430,7 @@ CronSelectField = Literal[ "metadata", "now", "on_run_completed", + "enabled", ] PrimitiveData = str | int | float | bool | None diff --git a/libs/sdk-py/tests/test_crons_client.py b/libs/sdk-py/tests/test_crons_client.py index 128727599..0045b1595 100644 --- a/libs/sdk-py/tests/test_crons_client.py +++ b/libs/sdk-py/tests/test_crons_client.py @@ -276,3 +276,36 @@ def test_sync_create_with_end_time(): ) assert result == cron + + +@pytest.mark.parametrize( + "enabled_value", + [True, False], + ids=["enabled", "disabled"], +) +def test_sync_create_with_enabled_parameter(enabled_value): + """Test that SyncCronClient.create includes enabled parameter in the payload.""" + cron = _cron_payload() + + def handler(request: httpx.Request) -> httpx.Response: + assert request.method == "POST" + assert request.url.path == "/runs/crons" + + body = json.loads(request.content) + assert body["schedule"] == "0 12 * * *" + assert body["assistant_id"] == "asst_456" + assert body["enabled"] == enabled_value + + return httpx.Response(200, json=cron) + + transport = httpx.MockTransport(handler) + with httpx.Client(transport=transport, base_url="https://example.com") as client: + http_client = SyncHttpClient(client) + cron_client = SyncCronClient(http_client) + result = cron_client.create( + assistant_id="asst_456", + schedule="0 12 * * *", + enabled=enabled_value, + ) + + assert result == cron