glaip-sdk 0.6.23__py3-none-any.whl → 0.6.25__py3-none-any.whl

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.
glaip_sdk/agents/base.py CHANGED
@@ -55,6 +55,7 @@ from glaip_sdk.registry import get_agent_registry, get_mcp_registry, get_tool_re
55
55
  from glaip_sdk.utils.resource_refs import is_uuid
56
56
 
57
57
  if TYPE_CHECKING:
58
+ from glaip_sdk.client.schedules import AgentScheduleManager
58
59
  from glaip_sdk.models import AgentResponse
59
60
  from glaip_sdk.registry import AgentRegistry, MCPRegistry, ToolRegistry
60
61
 
@@ -844,6 +845,36 @@ class Agent:
844
845
  self._client = client
845
846
  return self
846
847
 
848
+ @property
849
+ def schedule(self) -> AgentScheduleManager:
850
+ """Get the schedule manager for this agent.
851
+
852
+ Provides a convenient interface for managing schedules scoped to this agent.
853
+
854
+ Returns:
855
+ AgentScheduleManager for schedule operations
856
+
857
+ Raises:
858
+ ValueError: If agent is not deployed
859
+ RuntimeError: If agent is not bound to a client
860
+
861
+ Example:
862
+ >>> agent = client.get_agent_by_id("agent-id")
863
+ >>> schedules = agent.schedule.list()
864
+ >>> new_schedule = agent.schedule.create(
865
+ ... input="Daily task",
866
+ ... schedule="0 9 * * 1-5"
867
+ ... )
868
+ """
869
+ if not self.id:
870
+ raise ValueError(_AGENT_NOT_DEPLOYED_MSG)
871
+ if not self._client:
872
+ raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
873
+
874
+ from glaip_sdk.client.schedules import AgentScheduleManager # noqa: PLC0415
875
+
876
+ return AgentScheduleManager(self, self._client.schedules)
877
+
847
878
  def _prepare_run_kwargs(
848
879
  self,
849
880
  message: str,
@@ -7,5 +7,6 @@ Authors:
7
7
 
8
8
  from glaip_sdk.client.agent_runs import AgentRunsClient
9
9
  from glaip_sdk.client.main import Client
10
+ from glaip_sdk.client.schedules import AgentScheduleManager, ScheduleClient
10
11
 
11
- __all__ = ["AgentRunsClient", "Client"]
12
+ __all__ = ["AgentRunsClient", "AgentScheduleManager", "Client", "ScheduleClient"]
@@ -0,0 +1,89 @@
1
+ """Schedule request payload builders for AIP SDK.
2
+
3
+ Authors:
4
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any
9
+
10
+ from glaip_sdk.models.schedule import ScheduleConfig
11
+
12
+
13
+ @dataclass
14
+ class ScheduleListParams:
15
+ """Parameters for listing schedules.
16
+
17
+ Args:
18
+ limit: Maximum number of schedules to return (1-100, default 50)
19
+ page: Page number for pagination (default 1)
20
+ agent_id: Filter schedules by agent ID
21
+ """
22
+
23
+ limit: int | None = None
24
+ page: int | None = None
25
+ agent_id: str | None = None
26
+
27
+ def to_query_params(self) -> dict[str, Any]:
28
+ """Convert to query parameters dictionary.
29
+
30
+ Returns:
31
+ Dictionary of non-None parameters for the API request
32
+ """
33
+ params: dict[str, Any] = {}
34
+ if self.limit is not None:
35
+ params["limit"] = self.limit
36
+ if self.page is not None:
37
+ params["page"] = self.page
38
+ if self.agent_id is not None:
39
+ params["agent_id"] = self.agent_id
40
+ return params
41
+
42
+
43
+ def normalize_schedule(
44
+ schedule: ScheduleConfig | dict[str, str] | str | None,
45
+ ) -> dict[str, str] | None:
46
+ """Normalize schedule input to a dictionary for API requests.
47
+
48
+ Accepts multiple input formats for user convenience:
49
+ - ScheduleConfig: Pydantic model with cron fields
50
+ - dict: Dictionary with cron fields (minute, hour, etc.)
51
+ - str: Cron string like "0 9 * * 1-5"
52
+ - None: Returns None
53
+
54
+ Args:
55
+ schedule: Schedule in various formats
56
+
57
+ Returns:
58
+ Dictionary suitable for API request or None
59
+
60
+ Raises:
61
+ ValueError: If cron string format is invalid
62
+ TypeError: If schedule is an unsupported type
63
+
64
+ Examples:
65
+ >>> normalize_schedule(ScheduleConfig(minute="0", hour="9"))
66
+ {'minute': '0', 'hour': '9', 'day_of_month': '*', 'month': '*', 'day_of_week': '*'}
67
+
68
+ >>> normalize_schedule({"minute": "0", "hour": "9"})
69
+ {'minute': '0', 'hour': '9', 'day_of_month': '*', 'month': '*', 'day_of_week': '*'}
70
+
71
+ >>> normalize_schedule("0 9 * * 1-5")
72
+ {'minute': '0', 'hour': '9', 'day_of_month': '*', 'month': '*', 'day_of_week': '1-5'}
73
+ """
74
+ if schedule is None:
75
+ return None
76
+
77
+ if isinstance(schedule, ScheduleConfig):
78
+ return schedule.model_dump()
79
+
80
+ if isinstance(schedule, dict):
81
+ # Validate and merge with defaults
82
+ return ScheduleConfig(**schedule).model_dump()
83
+
84
+ if isinstance(schedule, str):
85
+ # Parse cron string
86
+ config = ScheduleConfig.from_cron_string(schedule)
87
+ return config.model_dump()
88
+
89
+ raise TypeError(f"schedule must be ScheduleConfig, dict, or str, got {type(schedule).__name__}")
@@ -13,7 +13,10 @@ from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
13
13
  from contextlib import asynccontextmanager
14
14
  from os import PathLike
15
15
  from pathlib import Path
16
- from typing import Any, BinaryIO
16
+ from typing import TYPE_CHECKING, Any, BinaryIO
17
+
18
+ if TYPE_CHECKING:
19
+ from glaip_sdk.client.schedules import ScheduleClient
17
20
 
18
21
  import httpx
19
22
  from glaip_sdk.agents import Agent
@@ -264,6 +267,7 @@ class AgentClient(BaseClient):
264
267
  self._tool_client: ToolClient | None = None
265
268
  self._mcp_client: MCPClient | None = None
266
269
  self._runs_client: AgentRunsClient | None = None
270
+ self._schedule_client: ScheduleClient | None = None
267
271
 
268
272
  def list_agents(
269
273
  self,
@@ -480,6 +484,20 @@ class AgentClient(BaseClient):
480
484
  self._mcp_client = MCPClient(parent_client=self)
481
485
  return self._mcp_client
482
486
 
487
+ @property
488
+ def schedules(self) -> "ScheduleClient":
489
+ """Get or create the schedule client instance.
490
+
491
+ Returns:
492
+ ScheduleClient instance.
493
+ """
494
+ if self._schedule_client is None:
495
+ # Import here to avoid circular import
496
+ from glaip_sdk.client.schedules import ScheduleClient # noqa: PLC0415
497
+
498
+ self._schedule_client = ScheduleClient(parent_client=self)
499
+ return self._schedule_client
500
+
483
501
  def _normalise_reference_entry(
484
502
  self,
485
503
  entry: Any,
glaip_sdk/client/main.py CHANGED
@@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Any
13
13
  from glaip_sdk.client.agents import AgentClient
14
14
  from glaip_sdk.client.base import BaseClient
15
15
  from glaip_sdk.client.mcps import MCPClient
16
+ from glaip_sdk.client.schedules import ScheduleClient
16
17
  from glaip_sdk.client.shared import build_shared_config
17
18
  from glaip_sdk.client.tools import ToolClient
18
19
 
@@ -38,6 +39,7 @@ class Client(BaseClient):
38
39
  self.agents = AgentClient(**shared_config)
39
40
  self.tools = ToolClient(**shared_config)
40
41
  self.mcps = MCPClient(**shared_config)
42
+ self.schedules = ScheduleClient(**shared_config)
41
43
 
42
44
  # ---- Core API Methods (Public Interface) ----
43
45
 
@@ -236,6 +238,8 @@ class Client(BaseClient):
236
238
  self.tools.http_client = self.http_client
237
239
  if hasattr(self, "mcps"):
238
240
  self.mcps.http_client = self.http_client
241
+ if hasattr(self, "schedules"):
242
+ self.schedules.http_client = self.http_client
239
243
  except Exception:
240
244
  pass
241
245
 
@@ -0,0 +1,439 @@
1
+ """Schedule client for AIP SDK.
2
+
3
+ Authors:
4
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
5
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from glaip_sdk.client._schedule_payloads import ScheduleListParams, normalize_schedule
11
+ from glaip_sdk.client.base import BaseClient
12
+ from glaip_sdk.exceptions import APIError, NotFoundError
13
+ from glaip_sdk.models.agent_runs import RunStatus
14
+ from glaip_sdk.models.schedule import (
15
+ ScheduleConfig,
16
+ ScheduleResponse,
17
+ ScheduleRunResponse,
18
+ ScheduleRunResult,
19
+ )
20
+ from glaip_sdk.schedules import (
21
+ Schedule,
22
+ ScheduleListResult,
23
+ ScheduleRun,
24
+ ScheduleRunListResult,
25
+ )
26
+
27
+ if TYPE_CHECKING:
28
+ from glaip_sdk.models import Agent
29
+
30
+
31
+ class ScheduleClient(BaseClient):
32
+ """Client for managing agent schedules.
33
+
34
+ Provides CRUD operations for scheduled agent executions.
35
+ Schedules allow agents to run automatically at specified times
36
+ using cron-like configurations.
37
+
38
+ Example:
39
+ >>> from glaip_sdk import Client
40
+ >>> from glaip_sdk.models.schedule import ScheduleConfig
41
+ >>>
42
+ >>> client = Client()
43
+ >>> # List all schedules
44
+ >>> result = client.schedules.list()
45
+ >>> for schedule in result:
46
+ ... print(f"{schedule.id}: {schedule.next_run_time}")
47
+ >>>
48
+ >>> # Get a specific schedule
49
+ >>> schedule = client.schedules.get("schedule-id")
50
+ >>>
51
+ >>> # Create a schedule for an agent
52
+ >>> schedule = client.schedules.create(
53
+ ... agent_id="agent-id",
54
+ ... input="Generate daily report",
55
+ ... schedule=ScheduleConfig(minute="0", hour="9", day_of_week="1-5")
56
+ ... )
57
+ >>>
58
+ >>> # Update a schedule
59
+ >>> schedule = client.schedules.update(
60
+ ... schedule_id="schedule-id",
61
+ ... input="Updated report input"
62
+ ... )
63
+ >>>
64
+ >>> # Delete a schedule
65
+ >>> client.schedules.delete("schedule-id")
66
+ """
67
+
68
+ def list(
69
+ self,
70
+ *,
71
+ limit: int | None = None,
72
+ page: int | None = None,
73
+ agent_id: str | None = None,
74
+ ) -> ScheduleListResult:
75
+ """List schedules with optional filtering and pagination.
76
+
77
+ Args:
78
+ limit: Maximum number of schedules to return (1-100)
79
+ page: Page number for pagination
80
+ agent_id: Filter schedules by agent ID
81
+
82
+ Returns:
83
+ ScheduleListResult containing schedules and pagination metadata
84
+ """
85
+ params = ScheduleListParams(limit=limit, page=page, agent_id=agent_id)
86
+
87
+ response = self._request_with_envelope(
88
+ "GET",
89
+ "/agents/schedules",
90
+ params=params.to_query_params(),
91
+ )
92
+
93
+ # Parse schedules from response data
94
+ schedules = [
95
+ Schedule.from_response(ScheduleResponse.model_validate(item), client=self)
96
+ for item in (response.get("data") or [])
97
+ ]
98
+
99
+ return ScheduleListResult(
100
+ items=schedules,
101
+ total=response.get("total"),
102
+ page=response.get("page"),
103
+ limit=response.get("limit"),
104
+ has_next=response.get("has_next"),
105
+ has_prev=response.get("has_prev"),
106
+ )
107
+
108
+ def get(self, schedule_id: str) -> Schedule:
109
+ """Get a schedule by ID.
110
+
111
+ Args:
112
+ schedule_id: The schedule ID to retrieve
113
+
114
+ Returns:
115
+ Schedule instance
116
+
117
+ Raises:
118
+ NotFoundError: If schedule is not found
119
+ AuthenticationError: If API key is invalid
120
+ APIError: If the API request fails
121
+ """
122
+ data = self._request("GET", f"/agents/schedules/{schedule_id}")
123
+
124
+ if data is None:
125
+ raise NotFoundError(f"Schedule not found: {schedule_id}")
126
+ return Schedule.from_response(ScheduleResponse.model_validate(data), client=self)
127
+
128
+ def create(
129
+ self,
130
+ *,
131
+ agent_id: str,
132
+ input: str,
133
+ schedule: ScheduleConfig | dict[str, str] | str,
134
+ ) -> Schedule:
135
+ """Create a new schedule for an agent.
136
+
137
+ Args:
138
+ agent_id: The agent ID to schedule
139
+ input: Input text for scheduled execution
140
+ schedule: Schedule configuration (ScheduleConfig, dict, or cron string)
141
+
142
+ Returns:
143
+ Created Schedule instance
144
+
145
+ Raises:
146
+ ValueError: If schedule format is invalid
147
+ NotFoundError: If agent is not found
148
+ ValidationError: If schedule configuration is invalid
149
+ AuthenticationError: If API key is invalid
150
+ APIError: If the API request fails
151
+ """
152
+ schedule_dict = normalize_schedule(schedule)
153
+ if schedule_dict is None:
154
+ raise ValueError("schedule is required")
155
+
156
+ payload = {
157
+ "input": input,
158
+ "schedule": schedule_dict,
159
+ }
160
+
161
+ response = self._request("POST", f"/agents/{agent_id}/schedule", json=payload)
162
+
163
+ # Response contains schedule_id, fetch the full schedule
164
+ schedule_id = response.get("schedule_id")
165
+ if not schedule_id:
166
+ raise APIError("Missing schedule_id in create response")
167
+
168
+ return self.get(schedule_id)
169
+
170
+ def update(
171
+ self,
172
+ schedule_id: str,
173
+ *,
174
+ input: str | None = None,
175
+ schedule: ScheduleConfig | dict[str, str] | str | None = None,
176
+ ) -> Schedule:
177
+ """Update an existing schedule.
178
+
179
+ Args:
180
+ schedule_id: The schedule ID to update
181
+ input: New input text for scheduled execution
182
+ schedule: New schedule configuration (ScheduleConfig, dict, or cron string)
183
+
184
+ Returns:
185
+ Updated Schedule instance
186
+
187
+ Raises:
188
+ NotFoundError: If schedule is not found
189
+ ValueError: If schedule config is required but not provided
190
+ ValidationError: If schedule configuration is invalid
191
+ AuthenticationError: If API key is invalid
192
+ APIError: If the API request fails
193
+
194
+ Note:
195
+ Updates use explicit replacement (not merge). The SDK normalizes partial
196
+ schedule dicts by filling missing cron fields with "*". This is intentional
197
+ for predictability - what you provide is what you get (plus wildcard defaults).
198
+ If the current schedule metadata is missing and no schedule parameter is
199
+ provided, a ValueError is raised.
200
+ """
201
+ # Get current schedule to merge with updates
202
+ current = self.get(schedule_id)
203
+
204
+ # Handle input - ensure we have valid input data
205
+ if current.input is None and input is None:
206
+ raise ValueError(
207
+ f"Schedule {schedule_id} has missing input metadata and no input parameter provided. "
208
+ "Please provide an input value to update this schedule."
209
+ )
210
+
211
+ # Handle schedule config - ensure we have complete schedule data
212
+ if current.schedule_config is None and schedule is None:
213
+ raise ValueError(
214
+ f"Schedule {schedule_id} has missing metadata and no schedule parameter provided. "
215
+ "Please provide a full schedule configuration to update this schedule."
216
+ )
217
+
218
+ current_input = current.input or ""
219
+ current_schedule = current.schedule_config.model_dump() if current.schedule_config else {}
220
+
221
+ payload: dict[str, Any] = {
222
+ "input": input if input is not None else current_input,
223
+ "schedule": normalize_schedule(schedule) or current_schedule,
224
+ }
225
+
226
+ data = self._request("PUT", f"/agents/schedules/{schedule_id}", json=payload)
227
+ return Schedule.from_response(ScheduleResponse.model_validate(data), client=self)
228
+
229
+ def delete(self, schedule_id: str) -> None:
230
+ """Delete a schedule.
231
+
232
+ Args:
233
+ schedule_id: The schedule ID to delete
234
+
235
+ Raises:
236
+ NotFoundError: If schedule is not found
237
+ AuthenticationError: If API key is invalid
238
+ APIError: If the API request fails
239
+ """
240
+ self._request("DELETE", f"/agents/schedules/{schedule_id}")
241
+
242
+ def list_runs(
243
+ self,
244
+ agent_id: str,
245
+ *,
246
+ schedule_id: str | None = None,
247
+ status: RunStatus | None = None,
248
+ limit: int | None = None,
249
+ page: int | None = None,
250
+ ) -> ScheduleRunListResult:
251
+ """List runs for an agent, optionally filtered by schedule ID.
252
+
253
+ Args:
254
+ agent_id: The agent ID to list runs for
255
+ schedule_id: Optional schedule ID to filter by
256
+ status: Optional status filter
257
+ limit: Maximum number of runs to return (1-100)
258
+ page: Page number for pagination
259
+
260
+ Returns:
261
+ ScheduleRunListResult containing runs and pagination metadata
262
+ """
263
+ params: dict[str, Any] = {"run_type": "schedule"}
264
+ if schedule_id is not None:
265
+ params["schedule_id"] = schedule_id
266
+ if status is not None:
267
+ params["status"] = status
268
+ if limit is not None:
269
+ params["limit"] = limit
270
+ if page is not None:
271
+ params["page"] = page
272
+
273
+ response = self._request_with_envelope(
274
+ "GET",
275
+ f"/agents/{agent_id}/runs",
276
+ params=params,
277
+ )
278
+
279
+ # Parse runs from response data
280
+ runs = [
281
+ ScheduleRun.from_response(ScheduleRunResponse.model_validate(item), client=self)
282
+ for item in (response.get("data") or [])
283
+ ]
284
+
285
+ return ScheduleRunListResult(
286
+ items=runs,
287
+ total=response.get("total"),
288
+ page=response.get("page"),
289
+ limit=response.get("limit"),
290
+ has_next=response.get("has_next"),
291
+ has_prev=response.get("has_prev"),
292
+ )
293
+
294
+ def get_run_result(self, agent_id: str, run_id: str) -> ScheduleRunResult:
295
+ """Get the full output payload for an agent run.
296
+
297
+ Args:
298
+ agent_id: The agent ID the run belongs to
299
+ run_id: The run ID to retrieve
300
+
301
+ Returns:
302
+ ScheduleRunResult containing run details and optional output
303
+ """
304
+ data = self._request("GET", f"/agents/{agent_id}/runs/{run_id}")
305
+ return ScheduleRunResult.model_validate(data)
306
+
307
+
308
+ class AgentScheduleManager:
309
+ """Facade for agent-scoped schedule operations.
310
+
311
+ Provides a convenient interface for managing schedules through
312
+ an Agent instance, automatically scoping operations to that agent.
313
+
314
+ Example:
315
+ >>> agent = client.get_agent_by_id("agent-id")
316
+ >>> # List schedules for this agent
317
+ >>> schedules = agent.schedule.list()
318
+ >>> # Create a schedule for this agent
319
+ >>> schedule = agent.schedule.create(
320
+ ... input="Daily task",
321
+ ... schedule="0 9 * * 1-5"
322
+ ... )
323
+ """
324
+
325
+ def __init__(self, agent: "Agent", client: ScheduleClient) -> None:
326
+ """Initialize the schedule manager.
327
+
328
+ Args:
329
+ agent: The agent to manage schedules for
330
+ client: The ScheduleClient for API operations
331
+ """
332
+ self._agent = agent
333
+ self._client = client
334
+
335
+ def list(
336
+ self,
337
+ *,
338
+ limit: int | None = None,
339
+ page: int | None = None,
340
+ ) -> ScheduleListResult:
341
+ """List schedules for this agent.
342
+
343
+ Args:
344
+ limit: Maximum number of schedules to return (1-100)
345
+ page: Page number for pagination
346
+
347
+ Returns:
348
+ ScheduleListResult containing schedules for this agent
349
+ """
350
+ return self._client.list(limit=limit, page=page, agent_id=self._agent.id)
351
+
352
+ def create(
353
+ self,
354
+ *,
355
+ input: str,
356
+ schedule: ScheduleConfig | dict[str, str] | str,
357
+ ) -> Schedule:
358
+ """Create a schedule for this agent.
359
+
360
+ Args:
361
+ input: Input text for scheduled execution
362
+ schedule: Schedule configuration (ScheduleConfig, dict, or cron string)
363
+
364
+ Returns:
365
+ Created Schedule instance
366
+ """
367
+ return self._client.create(
368
+ agent_id=self._agent.id,
369
+ input=input,
370
+ schedule=schedule,
371
+ )
372
+
373
+ def get(self, schedule_id: str) -> Schedule:
374
+ """Get a schedule by ID.
375
+
376
+ Args:
377
+ schedule_id: The schedule ID to retrieve
378
+
379
+ Returns:
380
+ Schedule instance
381
+
382
+ Raises:
383
+ NotFoundError: If schedule is not found
384
+ """
385
+ return self._client.get(schedule_id)
386
+
387
+ def update(
388
+ self,
389
+ schedule_id: str,
390
+ *,
391
+ input: str | None = None,
392
+ schedule: ScheduleConfig | dict[str, str] | str | None = None,
393
+ ) -> Schedule:
394
+ """Update a schedule.
395
+
396
+ Args:
397
+ schedule_id: The schedule ID to update
398
+ input: New input text for scheduled execution
399
+ schedule: New schedule configuration
400
+
401
+ Returns:
402
+ Updated Schedule instance
403
+ """
404
+ return self._client.update(schedule_id, input=input, schedule=schedule)
405
+
406
+ def delete(self, schedule_id: str) -> None:
407
+ """Delete a schedule.
408
+
409
+ Args:
410
+ schedule_id: The schedule ID to delete
411
+ """
412
+ self._client.delete(schedule_id)
413
+
414
+ def list_runs(
415
+ self,
416
+ schedule_id: str | None = None,
417
+ *,
418
+ status: RunStatus | None = None,
419
+ limit: int | None = None,
420
+ page: int | None = None,
421
+ ) -> ScheduleRunListResult:
422
+ """List runs for this agent.
423
+
424
+ Args:
425
+ schedule_id: Optional schedule ID to filter by
426
+ status: Optional status filter
427
+ limit: Maximum number of runs to return (1-100)
428
+ page: Page number for pagination
429
+
430
+ Returns:
431
+ ScheduleRunListResult containing runs for this agent
432
+ """
433
+ return self._client.list_runs(
434
+ self._agent.id,
435
+ schedule_id=schedule_id,
436
+ status=status,
437
+ limit=limit,
438
+ page=page,
439
+ )
@@ -27,6 +27,16 @@ from glaip_sdk.models.agent_runs import (
27
27
  )
28
28
  from glaip_sdk.models.common import LanguageModelResponse, TTYRenderer
29
29
  from glaip_sdk.models.mcp import MCPResponse
30
+
31
+ # Export schedule models
32
+ from glaip_sdk.models.schedule import ( # noqa: F401
33
+ ScheduleConfig,
34
+ ScheduleMetadata,
35
+ ScheduleResponse,
36
+ ScheduleRunOutputChunk,
37
+ ScheduleRunResponse,
38
+ ScheduleRunResult,
39
+ )
30
40
  from glaip_sdk.models.tool import ToolResponse
31
41
 
32
42
 
@@ -87,4 +97,11 @@ __all__ = [
87
97
  "RunsPage",
88
98
  "RunWithOutput",
89
99
  "RunOutputChunk",
100
+ # Schedule models
101
+ "ScheduleConfig",
102
+ "ScheduleMetadata",
103
+ "ScheduleResponse",
104
+ "ScheduleRunResponse",
105
+ "ScheduleRunOutputChunk",
106
+ "ScheduleRunResult",
90
107
  ]
@@ -13,6 +13,7 @@ from pydantic import BaseModel, Field, field_validator, model_validator
13
13
 
14
14
  # Type alias for SSE event dictionaries
15
15
  RunOutputChunk = dict[str, Any]
16
+ RunStatus = Literal["started", "success", "failed", "cancelled", "aborted", "unavailable"]
16
17
 
17
18
 
18
19
  class RunSummary(BaseModel):
@@ -22,7 +23,7 @@ class RunSummary(BaseModel):
22
23
  agent_id: UUID
23
24
  run_type: Literal["manual", "schedule"]
24
25
  schedule_id: UUID | None = None
25
- status: Literal["started", "success", "failed", "cancelled", "aborted", "unavailable"]
26
+ status: RunStatus
26
27
  started_at: datetime
27
28
  completed_at: datetime | None = None
28
29
  input: str | None = None