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 +31 -0
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_schedule_payloads.py +89 -0
- glaip_sdk/client/agents.py +19 -1
- glaip_sdk/client/main.py +4 -0
- glaip_sdk/client/schedules.py +439 -0
- glaip_sdk/models/__init__.py +17 -0
- glaip_sdk/models/agent_runs.py +2 -1
- glaip_sdk/models/schedule.py +224 -0
- glaip_sdk/registry/tool.py +54 -5
- glaip_sdk/schedules/__init__.py +22 -0
- glaip_sdk/schedules/base.py +291 -0
- {glaip_sdk-0.6.23.dist-info → glaip_sdk-0.6.25.dist-info}/METADATA +1 -1
- {glaip_sdk-0.6.23.dist-info → glaip_sdk-0.6.25.dist-info}/RECORD +17 -12
- {glaip_sdk-0.6.23.dist-info → glaip_sdk-0.6.25.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.6.23.dist-info → glaip_sdk-0.6.25.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.6.23.dist-info → glaip_sdk-0.6.25.dist-info}/top_level.txt +0 -0
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,
|
glaip_sdk/client/__init__.py
CHANGED
|
@@ -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__}")
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -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
|
+
)
|
glaip_sdk/models/__init__.py
CHANGED
|
@@ -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
|
]
|
glaip_sdk/models/agent_runs.py
CHANGED
|
@@ -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:
|
|
26
|
+
status: RunStatus
|
|
26
27
|
started_at: datetime
|
|
27
28
|
completed_at: datetime | None = None
|
|
28
29
|
input: str | None = None
|