glaip-sdk 0.6.5b6__py3-none-any.whl → 0.7.7__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/__init__.py +42 -5
- glaip_sdk/agents/base.py +156 -32
- glaip_sdk/cli/auth.py +14 -8
- glaip_sdk/cli/commands/accounts.py +1 -1
- glaip_sdk/cli/commands/agents/__init__.py +119 -0
- glaip_sdk/cli/commands/agents/_common.py +561 -0
- glaip_sdk/cli/commands/agents/create.py +151 -0
- glaip_sdk/cli/commands/agents/delete.py +64 -0
- glaip_sdk/cli/commands/agents/get.py +89 -0
- glaip_sdk/cli/commands/agents/list.py +129 -0
- glaip_sdk/cli/commands/agents/run.py +264 -0
- glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
- glaip_sdk/cli/commands/agents/update.py +112 -0
- glaip_sdk/cli/commands/common_config.py +15 -12
- glaip_sdk/cli/commands/configure.py +2 -3
- glaip_sdk/cli/commands/mcps/__init__.py +94 -0
- glaip_sdk/cli/commands/mcps/_common.py +459 -0
- glaip_sdk/cli/commands/mcps/connect.py +82 -0
- glaip_sdk/cli/commands/mcps/create.py +152 -0
- glaip_sdk/cli/commands/mcps/delete.py +73 -0
- glaip_sdk/cli/commands/mcps/get.py +212 -0
- glaip_sdk/cli/commands/mcps/list.py +69 -0
- glaip_sdk/cli/commands/mcps/tools.py +235 -0
- glaip_sdk/cli/commands/mcps/update.py +190 -0
- glaip_sdk/cli/commands/models.py +2 -4
- glaip_sdk/cli/commands/shared/__init__.py +21 -0
- glaip_sdk/cli/commands/shared/formatters.py +91 -0
- glaip_sdk/cli/commands/tools/__init__.py +69 -0
- glaip_sdk/cli/commands/tools/_common.py +80 -0
- glaip_sdk/cli/commands/tools/create.py +228 -0
- glaip_sdk/cli/commands/tools/delete.py +61 -0
- glaip_sdk/cli/commands/tools/get.py +103 -0
- glaip_sdk/cli/commands/tools/list.py +69 -0
- glaip_sdk/cli/commands/tools/script.py +49 -0
- glaip_sdk/cli/commands/tools/update.py +102 -0
- glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
- glaip_sdk/cli/commands/transcripts/_common.py +9 -0
- glaip_sdk/cli/commands/transcripts/clear.py +5 -0
- glaip_sdk/cli/commands/transcripts/detail.py +5 -0
- glaip_sdk/cli/commands/{transcripts.py → transcripts_original.py} +2 -1
- glaip_sdk/cli/commands/update.py +163 -17
- glaip_sdk/cli/core/output.py +12 -7
- glaip_sdk/cli/entrypoint.py +20 -0
- glaip_sdk/cli/main.py +127 -39
- glaip_sdk/cli/pager.py +3 -3
- glaip_sdk/cli/resolution.py +2 -1
- glaip_sdk/cli/slash/accounts_controller.py +112 -32
- glaip_sdk/cli/slash/agent_session.py +5 -2
- glaip_sdk/cli/slash/prompt.py +11 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +1 -1
- glaip_sdk/cli/slash/session.py +58 -13
- glaip_sdk/cli/slash/tui/__init__.py +26 -1
- glaip_sdk/cli/slash/tui/accounts.tcss +7 -5
- glaip_sdk/cli/slash/tui/accounts_app.py +70 -9
- glaip_sdk/cli/slash/tui/clipboard.py +147 -0
- glaip_sdk/cli/slash/tui/context.py +59 -0
- glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
- glaip_sdk/cli/slash/tui/terminal.py +402 -0
- glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
- glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
- glaip_sdk/cli/slash/tui/theme/manager.py +86 -0
- glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
- glaip_sdk/cli/slash/tui/toast.py +123 -0
- glaip_sdk/cli/transcript/history.py +1 -1
- glaip_sdk/cli/transcript/viewer.py +5 -3
- glaip_sdk/cli/update_notifier.py +215 -7
- glaip_sdk/cli/validators.py +1 -1
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_schedule_payloads.py +89 -0
- glaip_sdk/client/agents.py +50 -8
- glaip_sdk/client/hitl.py +136 -0
- glaip_sdk/client/main.py +7 -1
- glaip_sdk/client/mcps.py +44 -13
- glaip_sdk/client/payloads/agent/__init__.py +23 -0
- glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +22 -47
- glaip_sdk/client/payloads/agent/responses.py +43 -0
- glaip_sdk/client/run_rendering.py +367 -3
- glaip_sdk/client/schedules.py +439 -0
- glaip_sdk/client/tools.py +57 -26
- glaip_sdk/hitl/__init__.py +48 -0
- glaip_sdk/hitl/base.py +64 -0
- glaip_sdk/hitl/callback.py +43 -0
- glaip_sdk/hitl/local.py +121 -0
- glaip_sdk/hitl/remote.py +523 -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 +273 -59
- glaip_sdk/runner/__init__.py +20 -3
- glaip_sdk/runner/deps.py +5 -8
- glaip_sdk/runner/langgraph.py +317 -42
- glaip_sdk/runner/logging_config.py +77 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +104 -5
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +72 -7
- glaip_sdk/schedules/__init__.py +22 -0
- glaip_sdk/schedules/base.py +291 -0
- glaip_sdk/tools/base.py +44 -11
- glaip_sdk/utils/__init__.py +1 -0
- glaip_sdk/utils/bundler.py +138 -2
- glaip_sdk/utils/import_resolver.py +43 -11
- glaip_sdk/utils/rendering/renderer/base.py +58 -0
- glaip_sdk/utils/runtime_config.py +15 -12
- glaip_sdk/utils/sync.py +31 -11
- glaip_sdk/utils/tool_detection.py +274 -6
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.7.dist-info}/METADATA +47 -37
- glaip_sdk-0.7.7.dist-info/RECORD +213 -0
- {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.7.dist-info}/WHEEL +2 -1
- glaip_sdk-0.7.7.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.7.7.dist-info/top_level.txt +1 -0
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk-0.6.5b6.dist-info/RECORD +0 -159
- glaip_sdk-0.6.5b6.dist-info/entry_points.txt +0 -3
glaip_sdk/client/agents.py
CHANGED
|
@@ -13,11 +13,15 @@ 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
|
|
20
|
+
from glaip_sdk.hitl.remote import RemoteHITLHandler
|
|
17
21
|
|
|
18
22
|
import httpx
|
|
19
23
|
from glaip_sdk.agents import Agent
|
|
20
|
-
from glaip_sdk.client.
|
|
24
|
+
from glaip_sdk.client.payloads.agent import (
|
|
21
25
|
AgentCreateRequest,
|
|
22
26
|
AgentListParams,
|
|
23
27
|
AgentListResult,
|
|
@@ -264,6 +268,7 @@ class AgentClient(BaseClient):
|
|
|
264
268
|
self._tool_client: ToolClient | None = None
|
|
265
269
|
self._mcp_client: MCPClient | None = None
|
|
266
270
|
self._runs_client: AgentRunsClient | None = None
|
|
271
|
+
self._schedule_client: ScheduleClient | None = None
|
|
267
272
|
|
|
268
273
|
def list_agents(
|
|
269
274
|
self,
|
|
@@ -411,6 +416,7 @@ class AgentClient(BaseClient):
|
|
|
411
416
|
timeout_seconds: float,
|
|
412
417
|
agent_name: str | None,
|
|
413
418
|
meta: dict[str, Any],
|
|
419
|
+
hitl_handler: "RemoteHITLHandler | None" = None,
|
|
414
420
|
) -> tuple[str, dict[str, Any], float | None, float | None]:
|
|
415
421
|
"""Process stream events from an HTTP response.
|
|
416
422
|
|
|
@@ -420,6 +426,7 @@ class AgentClient(BaseClient):
|
|
|
420
426
|
timeout_seconds: Timeout in seconds.
|
|
421
427
|
agent_name: Optional agent name.
|
|
422
428
|
meta: Metadata dictionary.
|
|
429
|
+
hitl_handler: Optional HITL handler for approval callbacks.
|
|
423
430
|
|
|
424
431
|
Returns:
|
|
425
432
|
Tuple of (final_text, stats_usage, started_monotonic, finished_monotonic).
|
|
@@ -431,6 +438,7 @@ class AgentClient(BaseClient):
|
|
|
431
438
|
timeout_seconds,
|
|
432
439
|
agent_name,
|
|
433
440
|
meta,
|
|
441
|
+
hitl_handler=hitl_handler,
|
|
434
442
|
)
|
|
435
443
|
|
|
436
444
|
def _finalize_renderer(
|
|
@@ -453,13 +461,11 @@ class AgentClient(BaseClient):
|
|
|
453
461
|
Returns:
|
|
454
462
|
Final text string.
|
|
455
463
|
"""
|
|
464
|
+
from glaip_sdk.client.run_rendering import finalize_render_manager # noqa: PLC0415
|
|
465
|
+
|
|
456
466
|
manager = self._get_renderer_manager()
|
|
457
|
-
return
|
|
458
|
-
renderer,
|
|
459
|
-
final_text,
|
|
460
|
-
stats_usage,
|
|
461
|
-
started_monotonic,
|
|
462
|
-
finished_monotonic,
|
|
467
|
+
return finalize_render_manager(
|
|
468
|
+
manager, renderer, final_text, stats_usage, started_monotonic, finished_monotonic
|
|
463
469
|
)
|
|
464
470
|
|
|
465
471
|
def _get_tool_client(self) -> ToolClient:
|
|
@@ -482,6 +488,20 @@ class AgentClient(BaseClient):
|
|
|
482
488
|
self._mcp_client = MCPClient(parent_client=self)
|
|
483
489
|
return self._mcp_client
|
|
484
490
|
|
|
491
|
+
@property
|
|
492
|
+
def schedules(self) -> "ScheduleClient":
|
|
493
|
+
"""Get or create the schedule client instance.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
ScheduleClient instance.
|
|
497
|
+
"""
|
|
498
|
+
if self._schedule_client is None:
|
|
499
|
+
# Import here to avoid circular import
|
|
500
|
+
from glaip_sdk.client.schedules import ScheduleClient # noqa: PLC0415
|
|
501
|
+
|
|
502
|
+
self._schedule_client = ScheduleClient(parent_client=self)
|
|
503
|
+
return self._schedule_client
|
|
504
|
+
|
|
485
505
|
def _normalise_reference_entry(
|
|
486
506
|
self,
|
|
487
507
|
entry: Any,
|
|
@@ -1096,6 +1116,7 @@ class AgentClient(BaseClient):
|
|
|
1096
1116
|
*,
|
|
1097
1117
|
renderer: RichStreamRenderer | str | None = "auto",
|
|
1098
1118
|
runtime_config: dict[str, Any] | None = None,
|
|
1119
|
+
hitl_handler: "RemoteHITLHandler | None" = None,
|
|
1099
1120
|
**kwargs,
|
|
1100
1121
|
) -> str:
|
|
1101
1122
|
"""Run an agent with a message, streaming via a renderer.
|
|
@@ -1113,6 +1134,8 @@ class AgentClient(BaseClient):
|
|
|
1113
1134
|
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
1114
1135
|
"agent_config": {"planning": True},
|
|
1115
1136
|
}
|
|
1137
|
+
hitl_handler: Optional RemoteHITLHandler for approval callbacks.
|
|
1138
|
+
Set GLAIP_HITL_AUTO_APPROVE=true for auto-approval without handler.
|
|
1116
1139
|
**kwargs: Additional arguments to pass to the run API.
|
|
1117
1140
|
|
|
1118
1141
|
Returns:
|
|
@@ -1169,6 +1192,7 @@ class AgentClient(BaseClient):
|
|
|
1169
1192
|
timeout_seconds,
|
|
1170
1193
|
agent_name,
|
|
1171
1194
|
meta,
|
|
1195
|
+
hitl_handler=hitl_handler,
|
|
1172
1196
|
)
|
|
1173
1197
|
|
|
1174
1198
|
except KeyboardInterrupt:
|
|
@@ -1185,6 +1209,13 @@ class AgentClient(BaseClient):
|
|
|
1185
1209
|
if multipart_data:
|
|
1186
1210
|
multipart_data.close()
|
|
1187
1211
|
|
|
1212
|
+
# Wait for pending HITL decisions before returning
|
|
1213
|
+
if hitl_handler and hasattr(hitl_handler, "wait_for_pending_decisions"):
|
|
1214
|
+
try:
|
|
1215
|
+
hitl_handler.wait_for_pending_decisions(timeout=30)
|
|
1216
|
+
except Exception as e:
|
|
1217
|
+
logger.warning(f"Error waiting for HITL decisions: {e}")
|
|
1218
|
+
|
|
1188
1219
|
return self._finalize_renderer(
|
|
1189
1220
|
r,
|
|
1190
1221
|
final_text,
|
|
@@ -1266,6 +1297,7 @@ class AgentClient(BaseClient):
|
|
|
1266
1297
|
*,
|
|
1267
1298
|
request_timeout: float | None = None,
|
|
1268
1299
|
runtime_config: dict[str, Any] | None = None,
|
|
1300
|
+
hitl_handler: "RemoteHITLHandler | None" = None,
|
|
1269
1301
|
**kwargs,
|
|
1270
1302
|
) -> AsyncGenerator[dict, None]:
|
|
1271
1303
|
"""Async run an agent with a message, yielding streaming JSON chunks.
|
|
@@ -1282,16 +1314,26 @@ class AgentClient(BaseClient):
|
|
|
1282
1314
|
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
1283
1315
|
"agent_config": {"planning": True},
|
|
1284
1316
|
}
|
|
1317
|
+
hitl_handler: Optional HITL handler for remote approval requests.
|
|
1318
|
+
Note: Async HITL support is currently deferred. This parameter
|
|
1319
|
+
is accepted for API consistency but will raise NotImplementedError
|
|
1320
|
+
if provided.
|
|
1285
1321
|
**kwargs: Additional arguments (chat_history, pii_mapping, etc.)
|
|
1286
1322
|
|
|
1287
1323
|
Yields:
|
|
1288
1324
|
Dictionary containing parsed JSON chunks from the streaming response
|
|
1289
1325
|
|
|
1290
1326
|
Raises:
|
|
1327
|
+
NotImplementedError: If hitl_handler is provided (async HITL not yet supported)
|
|
1291
1328
|
AgentTimeoutError: When agent execution times out
|
|
1292
1329
|
httpx.TimeoutException: When general timeout occurs
|
|
1293
1330
|
Exception: For other unexpected errors
|
|
1294
1331
|
"""
|
|
1332
|
+
if hitl_handler is not None:
|
|
1333
|
+
raise NotImplementedError(
|
|
1334
|
+
"Async HITL support is currently deferred. "
|
|
1335
|
+
"Please use the synchronous run_agent() method with hitl_handler."
|
|
1336
|
+
)
|
|
1295
1337
|
# Include runtime_config in kwargs only when caller hasn't already provided it
|
|
1296
1338
|
if runtime_config is not None and "runtime_config" not in kwargs:
|
|
1297
1339
|
kwargs["runtime_config"] = runtime_config
|
glaip_sdk/client/hitl.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""HITL REST client for manual approval operations.
|
|
3
|
+
|
|
4
|
+
Authors:
|
|
5
|
+
GLAIP SDK Team
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from glaip_sdk.client.base import BaseClient
|
|
11
|
+
from glaip_sdk.hitl.base import HITLDecision
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HITLClient(BaseClient):
|
|
15
|
+
"""Client for HITL REST endpoints.
|
|
16
|
+
|
|
17
|
+
Use for manual approval workflows separate from agent runs.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> # List pending approvals
|
|
21
|
+
>>> pending = client.hitl.list_pending()
|
|
22
|
+
>>>
|
|
23
|
+
>>> # Approve a request
|
|
24
|
+
>>> client.hitl.approve(
|
|
25
|
+
... request_id="bc4d0a77-7800-470e-a91c-7fd663a66b4d",
|
|
26
|
+
... operator_input="Verified and approved",
|
|
27
|
+
... )
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def approve(
|
|
31
|
+
self,
|
|
32
|
+
request_id: str,
|
|
33
|
+
operator_input: str | None = None,
|
|
34
|
+
run_id: str | None = None,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Approve a HITL request.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
request_id: HITL request ID from SSE stream
|
|
40
|
+
operator_input: Optional notes/reason for approval
|
|
41
|
+
run_id: Optional client-side run correlation ID
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Response dict: {"status": "ok", "message": "..."}
|
|
45
|
+
"""
|
|
46
|
+
return self._post_decision(
|
|
47
|
+
request_id,
|
|
48
|
+
HITLDecision.APPROVED,
|
|
49
|
+
operator_input,
|
|
50
|
+
run_id,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def reject(
|
|
54
|
+
self,
|
|
55
|
+
request_id: str,
|
|
56
|
+
operator_input: str | None = None,
|
|
57
|
+
run_id: str | None = None,
|
|
58
|
+
) -> dict[str, Any]:
|
|
59
|
+
"""Reject a HITL request.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
request_id: HITL request ID
|
|
63
|
+
operator_input: Optional reason for rejection
|
|
64
|
+
run_id: Optional run correlation ID
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Response dict
|
|
68
|
+
"""
|
|
69
|
+
return self._post_decision(
|
|
70
|
+
request_id,
|
|
71
|
+
HITLDecision.REJECTED,
|
|
72
|
+
operator_input,
|
|
73
|
+
run_id,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def skip(
|
|
77
|
+
self,
|
|
78
|
+
request_id: str,
|
|
79
|
+
operator_input: str | None = None,
|
|
80
|
+
run_id: str | None = None,
|
|
81
|
+
) -> dict[str, Any]:
|
|
82
|
+
"""Skip a HITL request.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
request_id: HITL request ID
|
|
86
|
+
operator_input: Optional notes
|
|
87
|
+
run_id: Optional run correlation ID
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Response dict
|
|
91
|
+
"""
|
|
92
|
+
return self._post_decision(
|
|
93
|
+
request_id,
|
|
94
|
+
HITLDecision.SKIPPED,
|
|
95
|
+
operator_input,
|
|
96
|
+
run_id,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def _post_decision(
|
|
100
|
+
self,
|
|
101
|
+
request_id: str,
|
|
102
|
+
decision: HITLDecision,
|
|
103
|
+
operator_input: str | None,
|
|
104
|
+
run_id: str | None,
|
|
105
|
+
) -> dict[str, Any]:
|
|
106
|
+
"""Post HITL decision to backend."""
|
|
107
|
+
payload = {
|
|
108
|
+
"request_id": request_id,
|
|
109
|
+
"decision": decision.value,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if operator_input:
|
|
113
|
+
payload["operator_input"] = operator_input
|
|
114
|
+
if run_id:
|
|
115
|
+
payload["run_id"] = run_id
|
|
116
|
+
|
|
117
|
+
return self._request("POST", "/agents/hitl/decision", json=payload)
|
|
118
|
+
|
|
119
|
+
def list_pending(self) -> list[dict[str, Any]]:
|
|
120
|
+
"""List all pending HITL requests.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
List of pending request dicts with metadata:
|
|
124
|
+
[
|
|
125
|
+
{
|
|
126
|
+
"request_id": "...",
|
|
127
|
+
"tool": "...",
|
|
128
|
+
"arguments": {...},
|
|
129
|
+
"created_at": "...",
|
|
130
|
+
"agent_id": "...",
|
|
131
|
+
"hitl_metadata": {...},
|
|
132
|
+
},
|
|
133
|
+
...
|
|
134
|
+
]
|
|
135
|
+
"""
|
|
136
|
+
return self._request("GET", "/agents/hitl/pending")
|
glaip_sdk/client/main.py
CHANGED
|
@@ -12,13 +12,15 @@ from typing import TYPE_CHECKING, Any
|
|
|
12
12
|
|
|
13
13
|
from glaip_sdk.client.agents import AgentClient
|
|
14
14
|
from glaip_sdk.client.base import BaseClient
|
|
15
|
+
from glaip_sdk.client.hitl import HITLClient
|
|
15
16
|
from glaip_sdk.client.mcps import MCPClient
|
|
17
|
+
from glaip_sdk.client.schedules import ScheduleClient
|
|
16
18
|
from glaip_sdk.client.shared import build_shared_config
|
|
17
19
|
from glaip_sdk.client.tools import ToolClient
|
|
18
20
|
|
|
19
21
|
if TYPE_CHECKING: # pragma: no cover
|
|
20
22
|
from glaip_sdk.agents import Agent
|
|
21
|
-
from glaip_sdk.client.
|
|
23
|
+
from glaip_sdk.client.payloads.agent import AgentListResult
|
|
22
24
|
from glaip_sdk.mcps import MCP
|
|
23
25
|
from glaip_sdk.tools import Tool
|
|
24
26
|
|
|
@@ -38,6 +40,8 @@ class Client(BaseClient):
|
|
|
38
40
|
self.agents = AgentClient(**shared_config)
|
|
39
41
|
self.tools = ToolClient(**shared_config)
|
|
40
42
|
self.mcps = MCPClient(**shared_config)
|
|
43
|
+
self.schedules = ScheduleClient(**shared_config)
|
|
44
|
+
self.hitl = HITLClient(**shared_config)
|
|
41
45
|
|
|
42
46
|
# ---- Core API Methods (Public Interface) ----
|
|
43
47
|
|
|
@@ -236,6 +240,8 @@ class Client(BaseClient):
|
|
|
236
240
|
self.tools.http_client = self.http_client
|
|
237
241
|
if hasattr(self, "mcps"):
|
|
238
242
|
self.mcps.http_client = self.http_client
|
|
243
|
+
if hasattr(self, "schedules"):
|
|
244
|
+
self.schedules.http_client = self.http_client
|
|
239
245
|
except Exception:
|
|
240
246
|
pass
|
|
241
247
|
|
glaip_sdk/client/mcps.py
CHANGED
|
@@ -85,26 +85,56 @@ class MCPClient(BaseClient):
|
|
|
85
85
|
response = MCPResponse(**full_mcp_data)
|
|
86
86
|
return MCP.from_response(response, client=self)
|
|
87
87
|
|
|
88
|
-
def update_mcp(self, mcp_id: str, **kwargs) -> MCP:
|
|
88
|
+
def update_mcp(self, mcp_id: str | MCP, **kwargs) -> MCP:
|
|
89
89
|
"""Update an existing MCP.
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
Notes:
|
|
92
|
+
- Payload construction is centralized via ``_build_update_payload`` so required
|
|
93
|
+
defaults (e.g., ``type``) and value normalization stay consistent across SDK and CLI.
|
|
94
|
+
- For backward compatibility, still chooses PATCH vs PUT based on which fields the
|
|
95
|
+
caller provided, but uses the SDK payload builder for the final payload.
|
|
95
96
|
"""
|
|
96
|
-
#
|
|
97
|
+
# Backward-compatible: allow passing an MCP instance to avoid an extra fetch.
|
|
98
|
+
if isinstance(mcp_id, MCP):
|
|
99
|
+
current_mcp = mcp_id
|
|
100
|
+
if not current_mcp.id:
|
|
101
|
+
raise ValueError("MCP instance has no id; cannot update.")
|
|
102
|
+
mcp_id_value = str(current_mcp.id)
|
|
103
|
+
else:
|
|
104
|
+
current_mcp = None
|
|
105
|
+
mcp_id_value = mcp_id
|
|
106
|
+
|
|
97
107
|
required_fields = {"name", "config", "transport"}
|
|
98
108
|
provided_fields = set(kwargs.keys())
|
|
109
|
+
method = "PUT" if required_fields.issubset(provided_fields) else "PATCH"
|
|
110
|
+
|
|
111
|
+
if not kwargs:
|
|
112
|
+
data = self._request(method, f"{MCPS_ENDPOINT}{mcp_id_value}", json={})
|
|
113
|
+
response = MCPResponse(**data)
|
|
114
|
+
return MCP.from_response(response, client=self)
|
|
115
|
+
|
|
116
|
+
if current_mcp is None:
|
|
117
|
+
current_mcp = self.get_mcp_by_id(mcp_id_value)
|
|
118
|
+
|
|
119
|
+
payload_kwargs = kwargs.copy()
|
|
120
|
+
name = payload_kwargs.pop("name", None)
|
|
121
|
+
description = payload_kwargs.pop("description", None)
|
|
122
|
+
full_payload = self._build_update_payload(
|
|
123
|
+
current_mcp=current_mcp,
|
|
124
|
+
name=name,
|
|
125
|
+
description=description,
|
|
126
|
+
**payload_kwargs,
|
|
127
|
+
)
|
|
99
128
|
|
|
100
|
-
if
|
|
101
|
-
|
|
102
|
-
method = "PUT"
|
|
129
|
+
if method == "PUT":
|
|
130
|
+
json_payload = full_payload
|
|
103
131
|
else:
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
json_payload = {key: full_payload[key] for key in provided_fields if key in full_payload}
|
|
133
|
+
json_payload["type"] = full_payload["type"]
|
|
134
|
+
if "config" in provided_fields and "transport" not in provided_fields and "transport" in full_payload:
|
|
135
|
+
json_payload["transport"] = full_payload["transport"]
|
|
106
136
|
|
|
107
|
-
data = self._request(method, f"{MCPS_ENDPOINT}{
|
|
137
|
+
data = self._request(method, f"{MCPS_ENDPOINT}{mcp_id_value}", json=json_payload)
|
|
108
138
|
response = MCPResponse(**data)
|
|
109
139
|
return MCP.from_response(response, client=self)
|
|
110
140
|
|
|
@@ -188,7 +218,8 @@ class MCPClient(BaseClient):
|
|
|
188
218
|
**kwargs,
|
|
189
219
|
) -> MCP:
|
|
190
220
|
"""Find by name and update, or create if not found."""
|
|
191
|
-
|
|
221
|
+
all_mcps = self.list_mcps()
|
|
222
|
+
existing = [mcp for mcp in all_mcps if mcp.name.lower() == name.lower()]
|
|
192
223
|
|
|
193
224
|
if len(existing) == 1:
|
|
194
225
|
logger.info("Updating existing MCP: %s", name)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Agent payload types for requests and responses.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from glaip_sdk.client.payloads.agent.requests import (
|
|
8
|
+
AgentCreateRequest,
|
|
9
|
+
AgentListParams,
|
|
10
|
+
AgentUpdateRequest,
|
|
11
|
+
merge_payload_fields,
|
|
12
|
+
resolve_language_model_fields,
|
|
13
|
+
)
|
|
14
|
+
from glaip_sdk.client.payloads.agent.responses import AgentListResult
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AgentCreateRequest",
|
|
18
|
+
"AgentListParams",
|
|
19
|
+
"AgentListResult",
|
|
20
|
+
"AgentUpdateRequest",
|
|
21
|
+
"merge_payload_fields",
|
|
22
|
+
"resolve_language_model_fields",
|
|
23
|
+
]
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
"""Shared helpers for Agent client payload construction and query handling."""
|
|
1
|
+
"""Agent request payload types and helpers.
|
|
3
2
|
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=duplicate-code
|
|
4
8
|
from __future__ import annotations
|
|
5
9
|
|
|
6
10
|
from collections.abc import Callable, Mapping, MutableMapping, Sequence
|
|
7
11
|
from copy import deepcopy
|
|
8
|
-
from dataclasses import dataclass
|
|
12
|
+
from dataclasses import dataclass
|
|
9
13
|
from typing import Any
|
|
10
14
|
|
|
11
15
|
from glaip_sdk.config.constants import (
|
|
@@ -273,38 +277,6 @@ class AgentListParams:
|
|
|
273
277
|
params[f"metadata.{key}"] = value
|
|
274
278
|
|
|
275
279
|
|
|
276
|
-
@dataclass(slots=True)
|
|
277
|
-
class AgentListResult:
|
|
278
|
-
"""Structured response for list_agents that retains pagination metadata."""
|
|
279
|
-
|
|
280
|
-
items: list[Any] = field(default_factory=list)
|
|
281
|
-
total: int | None = None
|
|
282
|
-
page: int | None = None
|
|
283
|
-
limit: int | None = None
|
|
284
|
-
has_next: bool | None = None
|
|
285
|
-
has_prev: bool | None = None
|
|
286
|
-
message: str | None = None
|
|
287
|
-
|
|
288
|
-
def __len__(self) -> int: # pragma: no cover - simple delegation
|
|
289
|
-
"""Return the number of items in the result list."""
|
|
290
|
-
return len(self.items)
|
|
291
|
-
|
|
292
|
-
def __iter__(self): # pragma: no cover - simple delegation
|
|
293
|
-
"""Return an iterator over the items in the result list."""
|
|
294
|
-
return iter(self.items)
|
|
295
|
-
|
|
296
|
-
def __getitem__(self, index: int) -> Any: # pragma: no cover - simple delegation
|
|
297
|
-
"""Get an item from the result list by index.
|
|
298
|
-
|
|
299
|
-
Args:
|
|
300
|
-
index: Index of the item to retrieve.
|
|
301
|
-
|
|
302
|
-
Returns:
|
|
303
|
-
The item at the specified index.
|
|
304
|
-
"""
|
|
305
|
-
return self.items[index]
|
|
306
|
-
|
|
307
|
-
|
|
308
280
|
@dataclass(slots=True)
|
|
309
281
|
class AgentCreateRequest:
|
|
310
282
|
"""Declarative representation of an agent creation payload."""
|
|
@@ -422,16 +394,6 @@ class AgentUpdateRequest:
|
|
|
422
394
|
return payload
|
|
423
395
|
|
|
424
396
|
|
|
425
|
-
__all__ = [
|
|
426
|
-
"AgentCreateRequest",
|
|
427
|
-
"AgentListParams",
|
|
428
|
-
"AgentListResult",
|
|
429
|
-
"AgentUpdateRequest",
|
|
430
|
-
"merge_payload_fields",
|
|
431
|
-
"resolve_language_model_fields",
|
|
432
|
-
]
|
|
433
|
-
|
|
434
|
-
|
|
435
397
|
def _build_base_update_payload(request: AgentUpdateRequest, current_agent: Any) -> dict[str, Any]:
|
|
436
398
|
"""Populate immutable agent update fields using request data or existing agent defaults."""
|
|
437
399
|
# Support both "agent_type" (runtime class) and "type" (API response) attributes
|
|
@@ -451,14 +413,27 @@ def _build_base_update_payload(request: AgentUpdateRequest, current_agent: Any)
|
|
|
451
413
|
|
|
452
414
|
def _resolve_update_language_model_fields(request: AgentUpdateRequest, current_agent: Any) -> dict[str, Any]:
|
|
453
415
|
"""Resolve the language-model portion of an update request with sensible fallbacks."""
|
|
416
|
+
# Check if any LM inputs were provided
|
|
417
|
+
has_lm_inputs = any(
|
|
418
|
+
[
|
|
419
|
+
request.model is not None,
|
|
420
|
+
request.language_model_id is not None,
|
|
421
|
+
request.provider is not None,
|
|
422
|
+
request.model_name is not None,
|
|
423
|
+
]
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
if not has_lm_inputs:
|
|
427
|
+
# No LM inputs provided - preserve existing fields
|
|
428
|
+
return _existing_language_model_fields(current_agent)
|
|
429
|
+
|
|
430
|
+
# LM inputs provided - resolve them (may return defaults if only partial info)
|
|
454
431
|
fields = resolve_language_model_fields(
|
|
455
432
|
model=request.model,
|
|
456
433
|
language_model_id=request.language_model_id,
|
|
457
434
|
provider=request.provider,
|
|
458
435
|
model_name=request.model_name,
|
|
459
436
|
)
|
|
460
|
-
if not fields:
|
|
461
|
-
fields = _existing_language_model_fields(current_agent)
|
|
462
437
|
return fields
|
|
463
438
|
|
|
464
439
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Agent response payload types.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=duplicate-code
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(slots=True)
|
|
15
|
+
class AgentListResult:
|
|
16
|
+
"""Structured response for list_agents that retains pagination metadata."""
|
|
17
|
+
|
|
18
|
+
items: list[Any] = field(default_factory=list)
|
|
19
|
+
total: int | None = None
|
|
20
|
+
page: int | None = None
|
|
21
|
+
limit: int | None = None
|
|
22
|
+
has_next: bool | None = None
|
|
23
|
+
has_prev: bool | None = None
|
|
24
|
+
message: str | None = None
|
|
25
|
+
|
|
26
|
+
def __len__(self) -> int: # pragma: no cover - simple delegation
|
|
27
|
+
"""Return the number of items in the result list."""
|
|
28
|
+
return len(self.items)
|
|
29
|
+
|
|
30
|
+
def __iter__(self): # pragma: no cover - simple delegation
|
|
31
|
+
"""Return an iterator over the items in the result list."""
|
|
32
|
+
return iter(self.items)
|
|
33
|
+
|
|
34
|
+
def __getitem__(self, index: int) -> Any: # pragma: no cover - simple delegation
|
|
35
|
+
"""Get an item from the result list by index.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
index: Index of the item to retrieve.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
The item at the specified index.
|
|
42
|
+
"""
|
|
43
|
+
return self.items[index]
|