glaip-sdk 0.1.3__py3-none-any.whl → 0.6.10__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 +5 -2
- glaip_sdk/_version.py +9 -0
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1191 -0
- glaip_sdk/branding.py +13 -0
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/auth.py +254 -15
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +213 -73
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +729 -113
- glaip_sdk/cli/commands/mcps.py +241 -72
- glaip_sdk/cli/commands/models.py +11 -5
- glaip_sdk/cli/commands/tools.py +49 -57
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/config.py +48 -4
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +846 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +35 -19
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +6 -3
- glaip_sdk/cli/main.py +228 -119
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/pager.py +9 -10
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +578 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +62 -21
- glaip_sdk/cli/slash/prompt.py +21 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +771 -140
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/loading.py +58 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +255 -44
- glaip_sdk/cli/transcript/capture.py +27 -1
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/viewer.py +72 -499
- glaip_sdk/cli/update_notifier.py +14 -5
- glaip_sdk/cli/utils.py +243 -1252
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +45 -9
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +287 -29
- glaip_sdk/client/base.py +1 -0
- glaip_sdk/client/main.py +19 -10
- glaip_sdk/client/mcps.py +122 -12
- glaip_sdk/client/run_rendering.py +133 -88
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +155 -10
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +90 -0
- glaip_sdk/models/agent.py +47 -0
- glaip_sdk/models/agent_runs.py +116 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +232 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +59 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +115 -0
- glaip_sdk/runner/langgraph.py +706 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +58 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +39 -7
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +23 -15
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +0 -33
- glaip_sdk/utils/import_export.py +12 -7
- glaip_sdk/utils/import_resolver.py +492 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +5 -30
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +9 -0
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +70 -1
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +1 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -47
- glaip_sdk/utils/rendering/renderer/base.py +217 -1476
- glaip_sdk/utils/rendering/renderer/debug.py +26 -20
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +4 -12
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +53 -440
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/steps/manager.py +387 -0
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +25 -13
- glaip_sdk/utils/runtime_config.py +425 -0
- glaip_sdk/utils/serialization.py +18 -0
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +16 -24
- {glaip_sdk-0.1.3.dist-info → glaip_sdk-0.6.10.dist-info}/METADATA +42 -4
- glaip_sdk-0.6.10.dist-info/RECORD +159 -0
- {glaip_sdk-0.1.3.dist-info → glaip_sdk-0.6.10.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -240
- glaip_sdk-0.1.3.dist-info/RECORD +0 -83
- {glaip_sdk-0.1.3.dist-info → glaip_sdk-0.6.10.dist-info}/entry_points.txt +0 -0
glaip_sdk/client/agents.py
CHANGED
|
@@ -3,31 +3,37 @@
|
|
|
3
3
|
|
|
4
4
|
Authors:
|
|
5
5
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
6
7
|
"""
|
|
7
8
|
|
|
9
|
+
import asyncio
|
|
8
10
|
import json
|
|
9
11
|
import logging
|
|
10
12
|
from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
|
|
13
|
+
from contextlib import asynccontextmanager
|
|
11
14
|
from os import PathLike
|
|
12
15
|
from pathlib import Path
|
|
13
16
|
from typing import Any, BinaryIO
|
|
14
17
|
|
|
15
18
|
import httpx
|
|
16
|
-
|
|
19
|
+
from glaip_sdk.agents import Agent
|
|
17
20
|
from glaip_sdk.client._agent_payloads import (
|
|
18
21
|
AgentCreateRequest,
|
|
19
22
|
AgentListParams,
|
|
20
23
|
AgentListResult,
|
|
21
24
|
AgentUpdateRequest,
|
|
22
25
|
)
|
|
26
|
+
from glaip_sdk.client.agent_runs import AgentRunsClient
|
|
23
27
|
from glaip_sdk.client.base import BaseClient
|
|
24
28
|
from glaip_sdk.client.mcps import MCPClient
|
|
25
29
|
from glaip_sdk.client.run_rendering import (
|
|
26
30
|
AgentRunRenderingManager,
|
|
27
31
|
compute_timeout_seconds,
|
|
28
32
|
)
|
|
33
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
29
34
|
from glaip_sdk.client.tools import ToolClient
|
|
30
35
|
from glaip_sdk.config.constants import (
|
|
36
|
+
AGENT_CONFIG_FIELDS,
|
|
31
37
|
DEFAULT_AGENT_FRAMEWORK,
|
|
32
38
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
33
39
|
DEFAULT_AGENT_TYPE,
|
|
@@ -35,7 +41,7 @@ from glaip_sdk.config.constants import (
|
|
|
35
41
|
DEFAULT_MODEL,
|
|
36
42
|
)
|
|
37
43
|
from glaip_sdk.exceptions import NotFoundError, ValidationError
|
|
38
|
-
from glaip_sdk.models import
|
|
44
|
+
from glaip_sdk.models import AgentResponse
|
|
39
45
|
from glaip_sdk.payload_schemas.agent import list_server_only_fields
|
|
40
46
|
from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
|
|
41
47
|
from glaip_sdk.utils.client_utils import (
|
|
@@ -67,6 +73,21 @@ _MERGED_SEQUENCE_FIELDS = ("tools", "agents", "mcps")
|
|
|
67
73
|
_DEFAULT_METADATA_TYPE = "custom"
|
|
68
74
|
|
|
69
75
|
|
|
76
|
+
@asynccontextmanager
|
|
77
|
+
async def _async_timeout_guard(
|
|
78
|
+
timeout_seconds: float | None,
|
|
79
|
+
) -> AsyncGenerator[None, None]:
|
|
80
|
+
"""Apply an asyncio timeout when a custom timeout is provided."""
|
|
81
|
+
if timeout_seconds is None:
|
|
82
|
+
yield
|
|
83
|
+
return
|
|
84
|
+
try:
|
|
85
|
+
async with asyncio.timeout(timeout_seconds):
|
|
86
|
+
yield
|
|
87
|
+
except asyncio.TimeoutError as exc:
|
|
88
|
+
raise httpx.TimeoutException(f"Request timed out after {timeout_seconds}s") from exc
|
|
89
|
+
|
|
90
|
+
|
|
70
91
|
def _normalise_sequence(value: Any) -> list[Any] | None:
|
|
71
92
|
"""Normalise optional sequence inputs to plain lists."""
|
|
72
93
|
if value is None:
|
|
@@ -193,19 +214,7 @@ def _extract_original_refs(raw_definition: dict) -> dict[str, list]:
|
|
|
193
214
|
|
|
194
215
|
def _build_cli_args(overrides_dict: dict) -> dict[str, Any]:
|
|
195
216
|
"""Build CLI args from overrides, filtering out None values."""
|
|
196
|
-
cli_args = {
|
|
197
|
-
key: overrides_dict.get(key)
|
|
198
|
-
for key in (
|
|
199
|
-
"name",
|
|
200
|
-
"instruction",
|
|
201
|
-
"model",
|
|
202
|
-
"tools",
|
|
203
|
-
"agents",
|
|
204
|
-
"mcps",
|
|
205
|
-
"timeout",
|
|
206
|
-
)
|
|
207
|
-
if overrides_dict.get(key) is not None
|
|
208
|
-
}
|
|
217
|
+
cli_args = {key: overrides_dict.get(key) for key in AGENT_CONFIG_FIELDS if overrides_dict.get(key) is not None}
|
|
209
218
|
|
|
210
219
|
# Normalize sequence fields
|
|
211
220
|
for field in _MERGED_SEQUENCE_FIELDS:
|
|
@@ -254,6 +263,7 @@ class AgentClient(BaseClient):
|
|
|
254
263
|
self._renderer_manager = AgentRunRenderingManager(logger)
|
|
255
264
|
self._tool_client: ToolClient | None = None
|
|
256
265
|
self._mcp_client: MCPClient | None = None
|
|
266
|
+
self._runs_client: AgentRunsClient | None = None
|
|
257
267
|
|
|
258
268
|
def list_agents(
|
|
259
269
|
self,
|
|
@@ -352,7 +362,8 @@ class AgentClient(BaseClient):
|
|
|
352
362
|
status_code=404,
|
|
353
363
|
)
|
|
354
364
|
|
|
355
|
-
|
|
365
|
+
response = AgentResponse(**data)
|
|
366
|
+
return Agent.from_response(response, client=self)
|
|
356
367
|
|
|
357
368
|
def find_agents(self, name: str | None = None) -> list[Agent]:
|
|
358
369
|
"""Find agents by name."""
|
|
@@ -366,6 +377,11 @@ class AgentClient(BaseClient):
|
|
|
366
377
|
# Renderer delegation helpers
|
|
367
378
|
# ------------------------------------------------------------------ #
|
|
368
379
|
def _get_renderer_manager(self) -> AgentRunRenderingManager:
|
|
380
|
+
"""Get or create the renderer manager instance.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
AgentRunRenderingManager instance.
|
|
384
|
+
"""
|
|
369
385
|
manager = getattr(self, "_renderer_manager", None)
|
|
370
386
|
if manager is None:
|
|
371
387
|
manager = AgentRunRenderingManager(logger)
|
|
@@ -373,6 +389,15 @@ class AgentClient(BaseClient):
|
|
|
373
389
|
return manager
|
|
374
390
|
|
|
375
391
|
def _create_renderer(self, renderer: RichStreamRenderer | str | None, **kwargs: Any) -> RichStreamRenderer:
|
|
392
|
+
"""Create or return a renderer instance.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
renderer: Renderer instance, string identifier, or None.
|
|
396
|
+
**kwargs: Additional keyword arguments (e.g., verbose).
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
RichStreamRenderer instance.
|
|
400
|
+
"""
|
|
376
401
|
manager = self._get_renderer_manager()
|
|
377
402
|
verbose = kwargs.get("verbose", False)
|
|
378
403
|
if isinstance(renderer, RichStreamRenderer) or hasattr(renderer, "on_start"):
|
|
@@ -387,6 +412,18 @@ class AgentClient(BaseClient):
|
|
|
387
412
|
agent_name: str | None,
|
|
388
413
|
meta: dict[str, Any],
|
|
389
414
|
) -> tuple[str, dict[str, Any], float | None, float | None]:
|
|
415
|
+
"""Process stream events from an HTTP response.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
stream_response: HTTP response stream.
|
|
419
|
+
renderer: Renderer to use for displaying events.
|
|
420
|
+
timeout_seconds: Timeout in seconds.
|
|
421
|
+
agent_name: Optional agent name.
|
|
422
|
+
meta: Metadata dictionary.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Tuple of (final_text, stats_usage, started_monotonic, finished_monotonic).
|
|
426
|
+
"""
|
|
390
427
|
manager = self._get_renderer_manager()
|
|
391
428
|
return manager.process_stream_events(
|
|
392
429
|
stream_response,
|
|
@@ -404,6 +441,18 @@ class AgentClient(BaseClient):
|
|
|
404
441
|
started_monotonic: float | None,
|
|
405
442
|
finished_monotonic: float | None,
|
|
406
443
|
) -> str:
|
|
444
|
+
"""Finalize the renderer and return the final response text.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
renderer: Renderer to finalize.
|
|
448
|
+
final_text: Final text content.
|
|
449
|
+
stats_usage: Usage statistics dictionary.
|
|
450
|
+
started_monotonic: Start time (monotonic).
|
|
451
|
+
finished_monotonic: Finish time (monotonic).
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
Final text string.
|
|
455
|
+
"""
|
|
407
456
|
manager = self._get_renderer_manager()
|
|
408
457
|
return manager.finalize_renderer(
|
|
409
458
|
renderer,
|
|
@@ -414,11 +463,21 @@ class AgentClient(BaseClient):
|
|
|
414
463
|
)
|
|
415
464
|
|
|
416
465
|
def _get_tool_client(self) -> ToolClient:
|
|
466
|
+
"""Get or create the tool client instance.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
ToolClient instance.
|
|
470
|
+
"""
|
|
417
471
|
if self._tool_client is None:
|
|
418
472
|
self._tool_client = ToolClient(parent_client=self)
|
|
419
473
|
return self._tool_client
|
|
420
474
|
|
|
421
475
|
def _get_mcp_client(self) -> MCPClient:
|
|
476
|
+
"""Get or create the MCP client instance.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
MCPClient instance.
|
|
480
|
+
"""
|
|
422
481
|
if self._mcp_client is None:
|
|
423
482
|
self._mcp_client = MCPClient(parent_client=self)
|
|
424
483
|
return self._mcp_client
|
|
@@ -428,6 +487,15 @@ class AgentClient(BaseClient):
|
|
|
428
487
|
entry: Any,
|
|
429
488
|
fallback_iter: Iterator[Any] | None,
|
|
430
489
|
) -> tuple[str | None, str | None]:
|
|
490
|
+
"""Normalize a reference entry to extract ID and name.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
entry: Reference entry (string, dict, or other).
|
|
494
|
+
fallback_iter: Optional iterator for fallback values.
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
Tuple of (entry_id, entry_name).
|
|
498
|
+
"""
|
|
431
499
|
entry_id: str | None = None
|
|
432
500
|
entry_name: str | None = None
|
|
433
501
|
|
|
@@ -464,6 +532,19 @@ class AgentClient(BaseClient):
|
|
|
464
532
|
label: str,
|
|
465
533
|
plural_label: str | None = None,
|
|
466
534
|
) -> list[str] | None:
|
|
535
|
+
"""Resolve a list of resource references to IDs.
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
items: List of resource references to resolve.
|
|
539
|
+
references: Optional list of reference objects for fallback.
|
|
540
|
+
fetch_by_id: Function to fetch resource by ID.
|
|
541
|
+
find_by_name: Function to find resources by name.
|
|
542
|
+
label: Singular label for error messages.
|
|
543
|
+
plural_label: Plural label for error messages.
|
|
544
|
+
|
|
545
|
+
Returns:
|
|
546
|
+
List of resolved resource IDs, or None if items is empty.
|
|
547
|
+
"""
|
|
467
548
|
if not items:
|
|
468
549
|
return None
|
|
469
550
|
|
|
@@ -495,6 +576,22 @@ class AgentClient(BaseClient):
|
|
|
495
576
|
singular: str,
|
|
496
577
|
plural: str,
|
|
497
578
|
) -> str:
|
|
579
|
+
"""Resolve a single resource reference to an ID.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
entry: Resource reference to resolve.
|
|
583
|
+
fallback_iter: Optional iterator for fallback values.
|
|
584
|
+
fetch_by_id: Function to fetch resource by ID.
|
|
585
|
+
find_by_name: Function to find resources by name.
|
|
586
|
+
singular: Singular label for error messages.
|
|
587
|
+
plural: Plural label for error messages.
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
Resolved resource ID string.
|
|
591
|
+
|
|
592
|
+
Raises:
|
|
593
|
+
ValueError: If the resource cannot be resolved.
|
|
594
|
+
"""
|
|
498
595
|
entry_id, entry_name = self._normalise_reference_entry(entry, fallback_iter)
|
|
499
596
|
|
|
500
597
|
validated_id = self._validate_resource_id(fetch_by_id, entry_id)
|
|
@@ -511,6 +608,14 @@ class AgentClient(BaseClient):
|
|
|
511
608
|
|
|
512
609
|
@staticmethod
|
|
513
610
|
def _coerce_reference_value(entry: Any) -> str:
|
|
611
|
+
"""Coerce a reference entry to a string value.
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
entry: Reference entry (dict, string, or other).
|
|
615
|
+
|
|
616
|
+
Returns:
|
|
617
|
+
String representation of the reference.
|
|
618
|
+
"""
|
|
514
619
|
if isinstance(entry, dict):
|
|
515
620
|
if entry.get("id"):
|
|
516
621
|
return str(entry["id"])
|
|
@@ -520,6 +625,15 @@ class AgentClient(BaseClient):
|
|
|
520
625
|
|
|
521
626
|
@staticmethod
|
|
522
627
|
def _validate_resource_id(fetch_by_id: Callable[[str], Any], candidate_id: str | None) -> str | None:
|
|
628
|
+
"""Validate a resource ID by attempting to fetch it.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
fetch_by_id: Function to fetch resource by ID.
|
|
632
|
+
candidate_id: Candidate ID to validate.
|
|
633
|
+
|
|
634
|
+
Returns:
|
|
635
|
+
Validated ID if found, None otherwise.
|
|
636
|
+
"""
|
|
523
637
|
if not candidate_id:
|
|
524
638
|
return None
|
|
525
639
|
try:
|
|
@@ -535,6 +649,20 @@ class AgentClient(BaseClient):
|
|
|
535
649
|
singular: str,
|
|
536
650
|
plural: str,
|
|
537
651
|
) -> tuple[str, bool]:
|
|
652
|
+
"""Resolve a resource by name to an ID.
|
|
653
|
+
|
|
654
|
+
Args:
|
|
655
|
+
find_by_name: Function to find resources by name.
|
|
656
|
+
entry_name: Name of the resource to find.
|
|
657
|
+
singular: Singular label for error messages.
|
|
658
|
+
plural: Plural label for error messages.
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
Tuple of (resolved_id, success).
|
|
662
|
+
|
|
663
|
+
Raises:
|
|
664
|
+
ValueError: If resource not found or multiple matches exist.
|
|
665
|
+
"""
|
|
538
666
|
try:
|
|
539
667
|
matches = find_by_name(entry_name)
|
|
540
668
|
except Exception:
|
|
@@ -555,6 +683,15 @@ class AgentClient(BaseClient):
|
|
|
555
683
|
tools: list[Any] | None,
|
|
556
684
|
references: list[Any] | None = None,
|
|
557
685
|
) -> list[str] | None:
|
|
686
|
+
"""Resolve tool references to IDs.
|
|
687
|
+
|
|
688
|
+
Args:
|
|
689
|
+
tools: List of tool references to resolve.
|
|
690
|
+
references: Optional list of reference objects for fallback.
|
|
691
|
+
|
|
692
|
+
Returns:
|
|
693
|
+
List of resolved tool IDs, or None if tools is empty.
|
|
694
|
+
"""
|
|
558
695
|
tool_client = self._get_tool_client()
|
|
559
696
|
return self._resolve_resource_ids(
|
|
560
697
|
tools,
|
|
@@ -570,6 +707,15 @@ class AgentClient(BaseClient):
|
|
|
570
707
|
agents: list[Any] | None,
|
|
571
708
|
references: list[Any] | None = None,
|
|
572
709
|
) -> list[str] | None:
|
|
710
|
+
"""Resolve agent references to IDs.
|
|
711
|
+
|
|
712
|
+
Args:
|
|
713
|
+
agents: List of agent references to resolve.
|
|
714
|
+
references: Optional list of reference objects for fallback.
|
|
715
|
+
|
|
716
|
+
Returns:
|
|
717
|
+
List of resolved agent IDs, or None if agents is empty.
|
|
718
|
+
"""
|
|
573
719
|
return self._resolve_resource_ids(
|
|
574
720
|
agents,
|
|
575
721
|
references,
|
|
@@ -584,6 +730,15 @@ class AgentClient(BaseClient):
|
|
|
584
730
|
mcps: list[Any] | None,
|
|
585
731
|
references: list[Any] | None = None,
|
|
586
732
|
) -> list[str] | None:
|
|
733
|
+
"""Resolve MCP references to IDs.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
mcps: List of MCP references to resolve.
|
|
737
|
+
references: Optional list of reference objects for fallback.
|
|
738
|
+
|
|
739
|
+
Returns:
|
|
740
|
+
List of resolved MCP IDs, or None if mcps is empty.
|
|
741
|
+
"""
|
|
587
742
|
mcp_client = self._get_mcp_client()
|
|
588
743
|
return self._resolve_resource_ids(
|
|
589
744
|
mcps,
|
|
@@ -675,7 +830,8 @@ class AgentClient(BaseClient):
|
|
|
675
830
|
get_endpoint_fmt=f"{AGENTS_ENDPOINT}{{id}}",
|
|
676
831
|
json=payload_dict,
|
|
677
832
|
)
|
|
678
|
-
|
|
833
|
+
response = AgentResponse(**full_agent_data)
|
|
834
|
+
return Agent.from_response(response, client=self)
|
|
679
835
|
|
|
680
836
|
def create_agent(
|
|
681
837
|
self,
|
|
@@ -770,8 +926,9 @@ class AgentClient(BaseClient):
|
|
|
770
926
|
|
|
771
927
|
payload_dict = request.to_payload(current_agent)
|
|
772
928
|
|
|
773
|
-
|
|
774
|
-
|
|
929
|
+
api_response = self._request("PUT", f"/agents/{agent_id}", json=payload_dict)
|
|
930
|
+
response = AgentResponse(**api_response)
|
|
931
|
+
return Agent.from_response(response, client=self)
|
|
775
932
|
|
|
776
933
|
def update_agent(
|
|
777
934
|
self,
|
|
@@ -818,6 +975,62 @@ class AgentClient(BaseClient):
|
|
|
818
975
|
"""Delete an agent."""
|
|
819
976
|
self._request("DELETE", f"/agents/{agent_id}")
|
|
820
977
|
|
|
978
|
+
def upsert_agent(self, identifier: str | Agent, **kwargs) -> Agent:
|
|
979
|
+
"""Create or update an agent by instance, ID, or name.
|
|
980
|
+
|
|
981
|
+
Args:
|
|
982
|
+
identifier: Agent instance, ID (UUID string), or name
|
|
983
|
+
**kwargs: Agent configuration (instruction, description, tools, etc.)
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
The created or updated agent.
|
|
987
|
+
|
|
988
|
+
Example:
|
|
989
|
+
>>> # By name (creates if not exists)
|
|
990
|
+
>>> agent = client.agents.upsert_agent(
|
|
991
|
+
... "hello_agent",
|
|
992
|
+
... instruction="You are a helpful assistant.",
|
|
993
|
+
... description="A friendly agent",
|
|
994
|
+
... )
|
|
995
|
+
>>> # By instance
|
|
996
|
+
>>> agent = client.agents.upsert_agent(existing_agent, description="Updated")
|
|
997
|
+
>>> # By ID
|
|
998
|
+
>>> agent = client.agents.upsert_agent("uuid-here", description="Updated")
|
|
999
|
+
"""
|
|
1000
|
+
# Handle Agent instance
|
|
1001
|
+
if isinstance(identifier, Agent):
|
|
1002
|
+
if identifier.id:
|
|
1003
|
+
logger.info("Updating agent by instance: %s", identifier.name)
|
|
1004
|
+
return self.update_agent(identifier.id, name=identifier.name, **kwargs)
|
|
1005
|
+
identifier = identifier.name
|
|
1006
|
+
|
|
1007
|
+
# Handle string (ID or name)
|
|
1008
|
+
if isinstance(identifier, str):
|
|
1009
|
+
# Check if it's a UUID
|
|
1010
|
+
if is_uuid(identifier):
|
|
1011
|
+
logger.info("Updating agent by ID: %s", identifier)
|
|
1012
|
+
return self.update_agent(identifier, **kwargs)
|
|
1013
|
+
|
|
1014
|
+
# It's a name - find or create
|
|
1015
|
+
return self._upsert_agent_by_name(identifier, **kwargs)
|
|
1016
|
+
|
|
1017
|
+
raise ValueError(f"Invalid identifier type: {type(identifier)}")
|
|
1018
|
+
|
|
1019
|
+
def _upsert_agent_by_name(self, name: str, **kwargs) -> Agent:
|
|
1020
|
+
"""Find agent by name and update, or create if not found."""
|
|
1021
|
+
existing = self.find_agents(name)
|
|
1022
|
+
|
|
1023
|
+
if len(existing) == 1:
|
|
1024
|
+
logger.info("Updating existing agent: %s", name)
|
|
1025
|
+
return self.update_agent(existing[0].id, name=name, **kwargs)
|
|
1026
|
+
|
|
1027
|
+
if len(existing) > 1:
|
|
1028
|
+
raise ValueError(f"Multiple agents found with name '{name}'")
|
|
1029
|
+
|
|
1030
|
+
# Create new agent
|
|
1031
|
+
logger.info("Creating new agent: %s", name)
|
|
1032
|
+
return self.create_agent(name=name, **kwargs)
|
|
1033
|
+
|
|
821
1034
|
def _prepare_sync_request_data(
|
|
822
1035
|
self,
|
|
823
1036
|
message: str,
|
|
@@ -882,9 +1095,32 @@ class AgentClient(BaseClient):
|
|
|
882
1095
|
tty: bool = False,
|
|
883
1096
|
*,
|
|
884
1097
|
renderer: RichStreamRenderer | str | None = "auto",
|
|
1098
|
+
runtime_config: dict[str, Any] | None = None,
|
|
885
1099
|
**kwargs,
|
|
886
1100
|
) -> str:
|
|
887
|
-
"""Run an agent with a message, streaming via a renderer.
|
|
1101
|
+
"""Run an agent with a message, streaming via a renderer.
|
|
1102
|
+
|
|
1103
|
+
Args:
|
|
1104
|
+
agent_id: The ID of the agent to run.
|
|
1105
|
+
message: The message to send to the agent.
|
|
1106
|
+
files: Optional list of files to include with the request.
|
|
1107
|
+
tty: Whether to enable TTY mode.
|
|
1108
|
+
renderer: Renderer for streaming output.
|
|
1109
|
+
runtime_config: Optional runtime configuration for tools, MCPs, and agents.
|
|
1110
|
+
Keys should be platform IDs. Example:
|
|
1111
|
+
{
|
|
1112
|
+
"tool_configs": {"tool-id": {"param": "value"}},
|
|
1113
|
+
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
1114
|
+
"agent_config": {"planning": True},
|
|
1115
|
+
}
|
|
1116
|
+
**kwargs: Additional arguments to pass to the run API.
|
|
1117
|
+
|
|
1118
|
+
Returns:
|
|
1119
|
+
The agent's response as a string.
|
|
1120
|
+
"""
|
|
1121
|
+
# Include runtime_config in kwargs only when caller hasn't already provided it
|
|
1122
|
+
if runtime_config is not None and "runtime_config" not in kwargs:
|
|
1123
|
+
kwargs["runtime_config"] = runtime_config
|
|
888
1124
|
(
|
|
889
1125
|
payload,
|
|
890
1126
|
data_payload,
|
|
@@ -1028,7 +1264,8 @@ class AgentClient(BaseClient):
|
|
|
1028
1264
|
message: str,
|
|
1029
1265
|
files: list[str | BinaryIO] | None = None,
|
|
1030
1266
|
*,
|
|
1031
|
-
|
|
1267
|
+
request_timeout: float | None = None,
|
|
1268
|
+
runtime_config: dict[str, Any] | None = None,
|
|
1032
1269
|
**kwargs,
|
|
1033
1270
|
) -> AsyncGenerator[dict, None]:
|
|
1034
1271
|
"""Async run an agent with a message, yielding streaming JSON chunks.
|
|
@@ -1037,7 +1274,14 @@ class AgentClient(BaseClient):
|
|
|
1037
1274
|
agent_id: ID of the agent to run
|
|
1038
1275
|
message: Message to send to the agent
|
|
1039
1276
|
files: Optional list of files to include
|
|
1040
|
-
|
|
1277
|
+
request_timeout: Optional request timeout in seconds (defaults to client timeout)
|
|
1278
|
+
runtime_config: Optional runtime configuration for tools, MCPs, and agents.
|
|
1279
|
+
Keys should be platform IDs. Example:
|
|
1280
|
+
{
|
|
1281
|
+
"tool_configs": {"tool-id": {"param": "value"}},
|
|
1282
|
+
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
1283
|
+
"agent_config": {"planning": True},
|
|
1284
|
+
}
|
|
1041
1285
|
**kwargs: Additional arguments (chat_history, pii_mapping, etc.)
|
|
1042
1286
|
|
|
1043
1287
|
Yields:
|
|
@@ -1048,18 +1292,25 @@ class AgentClient(BaseClient):
|
|
|
1048
1292
|
httpx.TimeoutException: When general timeout occurs
|
|
1049
1293
|
Exception: For other unexpected errors
|
|
1050
1294
|
"""
|
|
1295
|
+
# Include runtime_config in kwargs only when caller hasn't already provided it
|
|
1296
|
+
if runtime_config is not None and "runtime_config" not in kwargs:
|
|
1297
|
+
kwargs["runtime_config"] = runtime_config
|
|
1298
|
+
# Derive timeout values for request/control flow
|
|
1299
|
+
legacy_timeout = kwargs.get("timeout")
|
|
1300
|
+
http_timeout_override = request_timeout if request_timeout is not None else legacy_timeout
|
|
1301
|
+
http_timeout = http_timeout_override or self.timeout
|
|
1302
|
+
|
|
1051
1303
|
# Prepare request data
|
|
1052
1304
|
payload, data_payload, files_payload, headers = self._prepare_request_data(message, files, **kwargs)
|
|
1053
1305
|
|
|
1054
1306
|
# Create async client configuration
|
|
1055
|
-
async_client_config = self._create_async_client_config(
|
|
1307
|
+
async_client_config = self._create_async_client_config(http_timeout_override, headers)
|
|
1056
1308
|
|
|
1057
1309
|
# Get execution timeout for streaming control
|
|
1058
1310
|
timeout_seconds = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
1059
1311
|
agent_name = kwargs.get("agent_name")
|
|
1060
1312
|
|
|
1061
|
-
|
|
1062
|
-
# Create async client and stream response
|
|
1313
|
+
async def _chunk_stream() -> AsyncGenerator[dict, None]:
|
|
1063
1314
|
async with httpx.AsyncClient(**async_client_config) as async_client:
|
|
1064
1315
|
async for chunk in self._stream_agent_response(
|
|
1065
1316
|
async_client,
|
|
@@ -1073,7 +1324,14 @@ class AgentClient(BaseClient):
|
|
|
1073
1324
|
):
|
|
1074
1325
|
yield chunk
|
|
1075
1326
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1327
|
+
async with _async_timeout_guard(http_timeout):
|
|
1328
|
+
async for chunk in _chunk_stream():
|
|
1329
|
+
yield chunk
|
|
1330
|
+
|
|
1331
|
+
@property
|
|
1332
|
+
def runs(self) -> "AgentRunsClient":
|
|
1333
|
+
"""Get the agent runs client."""
|
|
1334
|
+
if self._runs_client is None:
|
|
1335
|
+
shared_config = build_shared_config(self)
|
|
1336
|
+
self._runs_client = AgentRunsClient(**shared_config)
|
|
1337
|
+
return self._runs_client
|
glaip_sdk/client/base.py
CHANGED
glaip_sdk/client/main.py
CHANGED
|
@@ -3,15 +3,24 @@
|
|
|
3
3
|
|
|
4
4
|
Authors:
|
|
5
5
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
|
-
from
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
9
12
|
|
|
10
13
|
from glaip_sdk.client.agents import AgentClient
|
|
11
14
|
from glaip_sdk.client.base import BaseClient
|
|
12
15
|
from glaip_sdk.client.mcps import MCPClient
|
|
16
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
13
17
|
from glaip_sdk.client.tools import ToolClient
|
|
14
|
-
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
20
|
+
from glaip_sdk.agents import Agent
|
|
21
|
+
from glaip_sdk.client._agent_payloads import AgentListResult
|
|
22
|
+
from glaip_sdk.mcps import MCP
|
|
23
|
+
from glaip_sdk.tools import Tool
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
class Client(BaseClient):
|
|
@@ -25,12 +34,7 @@ class Client(BaseClient):
|
|
|
25
34
|
"""
|
|
26
35
|
super().__init__(**kwargs)
|
|
27
36
|
# Share the single httpx.Client + config with sub-clients
|
|
28
|
-
shared_config =
|
|
29
|
-
"parent_client": self,
|
|
30
|
-
"api_url": self.api_url,
|
|
31
|
-
"api_key": self.api_key,
|
|
32
|
-
"timeout": self._timeout,
|
|
33
|
-
}
|
|
37
|
+
shared_config = build_shared_config(self)
|
|
34
38
|
self.agents = AgentClient(**shared_config)
|
|
35
39
|
self.tools = ToolClient(**shared_config)
|
|
36
40
|
self.mcps = MCPClient(**shared_config)
|
|
@@ -53,7 +57,7 @@ class Client(BaseClient):
|
|
|
53
57
|
name: str | None = None,
|
|
54
58
|
version: str | None = None,
|
|
55
59
|
sync_langflow_agents: bool = False,
|
|
56
|
-
) ->
|
|
60
|
+
) -> AgentListResult:
|
|
57
61
|
"""List agents with optional filtering.
|
|
58
62
|
|
|
59
63
|
Args:
|
|
@@ -64,7 +68,7 @@ class Client(BaseClient):
|
|
|
64
68
|
sync_langflow_agents: Sync with LangFlow server before listing (only applies when agent_type=langflow)
|
|
65
69
|
|
|
66
70
|
Returns:
|
|
67
|
-
|
|
71
|
+
AgentListResult with agents and pagination metadata. Supports iteration and indexing.
|
|
68
72
|
"""
|
|
69
73
|
return self.agents.list_agents(
|
|
70
74
|
agent_type=agent_type,
|
|
@@ -217,6 +221,11 @@ class Client(BaseClient):
|
|
|
217
221
|
|
|
218
222
|
@timeout.setter
|
|
219
223
|
def timeout(self, value: float) -> None: # type: ignore[override]
|
|
224
|
+
"""Set the client timeout and propagate to sub-clients.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
value: Timeout value in seconds.
|
|
228
|
+
"""
|
|
220
229
|
# Rebuild the root http client
|
|
221
230
|
BaseClient.timeout.fset(self, value) # call parent setter
|
|
222
231
|
# Propagate the new session to sub-clients so they don't hold a closed client
|