glaip-sdk 0.2.2__py3-none-any.whl → 0.4.0__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/cli/auth.py +2 -1
- glaip_sdk/cli/commands/agents.py +51 -36
- glaip_sdk/cli/commands/configure.py +2 -1
- glaip_sdk/cli/commands/mcps.py +219 -62
- glaip_sdk/cli/commands/models.py +3 -5
- glaip_sdk/cli/commands/tools.py +27 -16
- glaip_sdk/cli/commands/transcripts.py +1 -1
- glaip_sdk/cli/constants.py +3 -0
- glaip_sdk/cli/display.py +1 -1
- glaip_sdk/cli/hints.py +58 -0
- glaip_sdk/cli/io.py +6 -3
- glaip_sdk/cli/main.py +3 -4
- glaip_sdk/cli/slash/agent_session.py +4 -13
- glaip_sdk/cli/slash/prompt.py +3 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +139 -48
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +632 -0
- glaip_sdk/cli/transcript/capture.py +1 -1
- glaip_sdk/cli/transcript/viewer.py +19 -678
- glaip_sdk/cli/update_notifier.py +2 -1
- glaip_sdk/cli/utils.py +228 -101
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +40 -22
- glaip_sdk/client/main.py +2 -6
- glaip_sdk/client/mcps.py +13 -5
- glaip_sdk/client/run_rendering.py +90 -111
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +2 -3
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/models/__init__.py +56 -0
- glaip_sdk/models/agent_runs.py +117 -0
- glaip_sdk/models.py +8 -7
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/utils/client_utils.py +13 -0
- glaip_sdk/utils/display.py +23 -15
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/import_export.py +6 -9
- 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 +10 -28
- glaip_sdk/utils/rendering/renderer/base.py +217 -1476
- glaip_sdk/utils/rendering/renderer/debug.py +24 -1
- 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 -439
- 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 +26 -15
- glaip_sdk/utils/validation.py +13 -21
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/METADATA +24 -2
- glaip_sdk-0.4.0.dist-info/RECORD +110 -0
- glaip_sdk-0.2.2.dist-info/RECORD +0 -87
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/entry_points.txt +0 -0
glaip_sdk/client/agents.py
CHANGED
|
@@ -5,9 +5,11 @@ Authors:
|
|
|
5
5
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
8
9
|
import json
|
|
9
10
|
import logging
|
|
10
11
|
from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
|
|
12
|
+
from contextlib import asynccontextmanager
|
|
11
13
|
from os import PathLike
|
|
12
14
|
from pathlib import Path
|
|
13
15
|
from typing import Any, BinaryIO
|
|
@@ -20,14 +22,17 @@ from glaip_sdk.client._agent_payloads import (
|
|
|
20
22
|
AgentListResult,
|
|
21
23
|
AgentUpdateRequest,
|
|
22
24
|
)
|
|
25
|
+
from glaip_sdk.client.agent_runs import AgentRunsClient
|
|
23
26
|
from glaip_sdk.client.base import BaseClient
|
|
24
27
|
from glaip_sdk.client.mcps import MCPClient
|
|
25
28
|
from glaip_sdk.client.run_rendering import (
|
|
26
29
|
AgentRunRenderingManager,
|
|
27
30
|
compute_timeout_seconds,
|
|
28
31
|
)
|
|
32
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
29
33
|
from glaip_sdk.client.tools import ToolClient
|
|
30
34
|
from glaip_sdk.config.constants import (
|
|
35
|
+
AGENT_CONFIG_FIELDS,
|
|
31
36
|
DEFAULT_AGENT_FRAMEWORK,
|
|
32
37
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
33
38
|
DEFAULT_AGENT_TYPE,
|
|
@@ -67,6 +72,19 @@ _MERGED_SEQUENCE_FIELDS = ("tools", "agents", "mcps")
|
|
|
67
72
|
_DEFAULT_METADATA_TYPE = "custom"
|
|
68
73
|
|
|
69
74
|
|
|
75
|
+
@asynccontextmanager
|
|
76
|
+
async def _async_timeout_guard(timeout_seconds: float | None) -> AsyncGenerator[None, None]:
|
|
77
|
+
"""Apply an asyncio timeout when a custom timeout is provided."""
|
|
78
|
+
if timeout_seconds is None:
|
|
79
|
+
yield
|
|
80
|
+
return
|
|
81
|
+
try:
|
|
82
|
+
async with asyncio.timeout(timeout_seconds):
|
|
83
|
+
yield
|
|
84
|
+
except asyncio.TimeoutError as exc:
|
|
85
|
+
raise httpx.TimeoutException(f"Request timed out after {timeout_seconds}s") from exc
|
|
86
|
+
|
|
87
|
+
|
|
70
88
|
def _normalise_sequence(value: Any) -> list[Any] | None:
|
|
71
89
|
"""Normalise optional sequence inputs to plain lists."""
|
|
72
90
|
if value is None:
|
|
@@ -193,19 +211,7 @@ def _extract_original_refs(raw_definition: dict) -> dict[str, list]:
|
|
|
193
211
|
|
|
194
212
|
def _build_cli_args(overrides_dict: dict) -> dict[str, Any]:
|
|
195
213
|
"""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
|
-
}
|
|
214
|
+
cli_args = {key: overrides_dict.get(key) for key in AGENT_CONFIG_FIELDS if overrides_dict.get(key) is not None}
|
|
209
215
|
|
|
210
216
|
# Normalize sequence fields
|
|
211
217
|
for field in _MERGED_SEQUENCE_FIELDS:
|
|
@@ -254,6 +260,7 @@ class AgentClient(BaseClient):
|
|
|
254
260
|
self._renderer_manager = AgentRunRenderingManager(logger)
|
|
255
261
|
self._tool_client: ToolClient | None = None
|
|
256
262
|
self._mcp_client: MCPClient | None = None
|
|
263
|
+
self._runs_client: "AgentRunsClient | None" = None
|
|
257
264
|
|
|
258
265
|
def list_agents(
|
|
259
266
|
self,
|
|
@@ -1172,7 +1179,7 @@ class AgentClient(BaseClient):
|
|
|
1172
1179
|
message: str,
|
|
1173
1180
|
files: list[str | BinaryIO] | None = None,
|
|
1174
1181
|
*,
|
|
1175
|
-
|
|
1182
|
+
request_timeout: float | None = None,
|
|
1176
1183
|
**kwargs,
|
|
1177
1184
|
) -> AsyncGenerator[dict, None]:
|
|
1178
1185
|
"""Async run an agent with a message, yielding streaming JSON chunks.
|
|
@@ -1181,7 +1188,7 @@ class AgentClient(BaseClient):
|
|
|
1181
1188
|
agent_id: ID of the agent to run
|
|
1182
1189
|
message: Message to send to the agent
|
|
1183
1190
|
files: Optional list of files to include
|
|
1184
|
-
|
|
1191
|
+
request_timeout: Optional request timeout in seconds (defaults to client timeout)
|
|
1185
1192
|
**kwargs: Additional arguments (chat_history, pii_mapping, etc.)
|
|
1186
1193
|
|
|
1187
1194
|
Yields:
|
|
@@ -1192,18 +1199,22 @@ class AgentClient(BaseClient):
|
|
|
1192
1199
|
httpx.TimeoutException: When general timeout occurs
|
|
1193
1200
|
Exception: For other unexpected errors
|
|
1194
1201
|
"""
|
|
1202
|
+
# Derive timeout values for request/control flow
|
|
1203
|
+
legacy_timeout = kwargs.get("timeout")
|
|
1204
|
+
http_timeout_override = request_timeout if request_timeout is not None else legacy_timeout
|
|
1205
|
+
http_timeout = http_timeout_override or self.timeout
|
|
1206
|
+
|
|
1195
1207
|
# Prepare request data
|
|
1196
1208
|
payload, data_payload, files_payload, headers = self._prepare_request_data(message, files, **kwargs)
|
|
1197
1209
|
|
|
1198
1210
|
# Create async client configuration
|
|
1199
|
-
async_client_config = self._create_async_client_config(
|
|
1211
|
+
async_client_config = self._create_async_client_config(http_timeout_override, headers)
|
|
1200
1212
|
|
|
1201
1213
|
# Get execution timeout for streaming control
|
|
1202
1214
|
timeout_seconds = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
1203
1215
|
agent_name = kwargs.get("agent_name")
|
|
1204
1216
|
|
|
1205
|
-
|
|
1206
|
-
# Create async client and stream response
|
|
1217
|
+
async def _chunk_stream() -> AsyncGenerator[dict, None]:
|
|
1207
1218
|
async with httpx.AsyncClient(**async_client_config) as async_client:
|
|
1208
1219
|
async for chunk in self._stream_agent_response(
|
|
1209
1220
|
async_client,
|
|
@@ -1217,7 +1228,14 @@ class AgentClient(BaseClient):
|
|
|
1217
1228
|
):
|
|
1218
1229
|
yield chunk
|
|
1219
1230
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1231
|
+
async with _async_timeout_guard(http_timeout):
|
|
1232
|
+
async for chunk in _chunk_stream():
|
|
1233
|
+
yield chunk
|
|
1234
|
+
|
|
1235
|
+
@property
|
|
1236
|
+
def runs(self) -> "AgentRunsClient":
|
|
1237
|
+
"""Get the agent runs client."""
|
|
1238
|
+
if self._runs_client is None:
|
|
1239
|
+
shared_config = build_shared_config(self)
|
|
1240
|
+
self._runs_client = AgentRunsClient(**shared_config)
|
|
1241
|
+
return self._runs_client
|
glaip_sdk/client/main.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import Any
|
|
|
10
10
|
from glaip_sdk.client.agents import AgentClient
|
|
11
11
|
from glaip_sdk.client.base import BaseClient
|
|
12
12
|
from glaip_sdk.client.mcps import MCPClient
|
|
13
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
13
14
|
from glaip_sdk.client.tools import ToolClient
|
|
14
15
|
from glaip_sdk.models import MCP, Agent, Tool
|
|
15
16
|
|
|
@@ -25,12 +26,7 @@ class Client(BaseClient):
|
|
|
25
26
|
"""
|
|
26
27
|
super().__init__(**kwargs)
|
|
27
28
|
# 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
|
-
}
|
|
29
|
+
shared_config = build_shared_config(self)
|
|
34
30
|
self.agents = AgentClient(**shared_config)
|
|
35
31
|
self.tools = ToolClient(**shared_config)
|
|
36
32
|
self.mcps = MCPClient(**shared_config)
|
glaip_sdk/client/mcps.py
CHANGED
|
@@ -14,7 +14,7 @@ from glaip_sdk.config.constants import (
|
|
|
14
14
|
DEFAULT_MCP_TYPE,
|
|
15
15
|
)
|
|
16
16
|
from glaip_sdk.models import MCP
|
|
17
|
-
from glaip_sdk.utils.client_utils import create_model_instances, find_by_name
|
|
17
|
+
from glaip_sdk.utils.client_utils import add_kwargs_to_payload, create_model_instances, find_by_name
|
|
18
18
|
|
|
19
19
|
# API endpoints
|
|
20
20
|
MCPS_ENDPOINT = "/mcps/"
|
|
@@ -147,9 +147,7 @@ class MCPClient(BaseClient):
|
|
|
147
147
|
|
|
148
148
|
# Add any other kwargs (excluding already handled ones)
|
|
149
149
|
excluded_keys = {"type"} # type is handled above
|
|
150
|
-
|
|
151
|
-
if key not in excluded_keys:
|
|
152
|
-
payload[key] = value
|
|
150
|
+
add_kwargs_to_payload(payload, kwargs, excluded_keys)
|
|
153
151
|
|
|
154
152
|
return payload
|
|
155
153
|
|
|
@@ -206,7 +204,17 @@ class MCPClient(BaseClient):
|
|
|
206
204
|
def get_mcp_tools(self, mcp_id: str) -> list[dict[str, Any]]:
|
|
207
205
|
"""Get tools available from an MCP."""
|
|
208
206
|
data = self._request("GET", f"{MCPS_ENDPOINT}{mcp_id}/tools")
|
|
209
|
-
|
|
207
|
+
if data is None:
|
|
208
|
+
return []
|
|
209
|
+
if isinstance(data, list):
|
|
210
|
+
return data
|
|
211
|
+
if isinstance(data, dict):
|
|
212
|
+
if "tools" in data:
|
|
213
|
+
return data.get("tools", []) or []
|
|
214
|
+
logger.warning("Unexpected MCP tools response keys %s; returning empty list", list(data.keys()))
|
|
215
|
+
return []
|
|
216
|
+
logger.warning("Unexpected MCP tools response type %s; returning empty list", type(data).__name__)
|
|
217
|
+
return []
|
|
210
218
|
|
|
211
219
|
def test_mcp_connection(self, config: dict[str, Any]) -> dict[str, Any]:
|
|
212
220
|
"""Test MCP connection using configuration.
|
|
@@ -7,9 +7,9 @@ Authors:
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import io
|
|
11
10
|
import json
|
|
12
11
|
import logging
|
|
12
|
+
from collections.abc import Callable
|
|
13
13
|
from time import monotonic
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
@@ -19,8 +19,17 @@ from rich.console import Console as _Console
|
|
|
19
19
|
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
|
|
20
20
|
from glaip_sdk.utils.client_utils import iter_sse_events
|
|
21
21
|
from glaip_sdk.utils.rendering.models import RunStats
|
|
22
|
-
from glaip_sdk.utils.rendering.renderer import
|
|
23
|
-
|
|
22
|
+
from glaip_sdk.utils.rendering.renderer import (
|
|
23
|
+
RendererFactoryOptions,
|
|
24
|
+
RichStreamRenderer,
|
|
25
|
+
make_default_renderer,
|
|
26
|
+
make_minimal_renderer,
|
|
27
|
+
make_silent_renderer,
|
|
28
|
+
make_verbose_renderer,
|
|
29
|
+
)
|
|
30
|
+
from glaip_sdk.utils.rendering.state import TranscriptBuffer
|
|
31
|
+
|
|
32
|
+
NO_AGENT_RESPONSE_FALLBACK = "No agent response received."
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
def _coerce_to_string(value: Any) -> str:
|
|
@@ -36,41 +45,6 @@ def _has_visible_text(value: Any) -> bool:
|
|
|
36
45
|
return isinstance(value, str) and bool(value.strip())
|
|
37
46
|
|
|
38
47
|
|
|
39
|
-
def _update_state_transcript(state: Any, text_value: str) -> bool:
|
|
40
|
-
"""Inject transcript text into renderer state if possible."""
|
|
41
|
-
if state is None:
|
|
42
|
-
return False
|
|
43
|
-
|
|
44
|
-
updated = False
|
|
45
|
-
|
|
46
|
-
if hasattr(state, "final_text") and not _has_visible_text(getattr(state, "final_text", "")):
|
|
47
|
-
try:
|
|
48
|
-
state.final_text = text_value
|
|
49
|
-
updated = True
|
|
50
|
-
except Exception:
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
buffer = getattr(state, "buffer", None)
|
|
54
|
-
if isinstance(buffer, list) and not any(_has_visible_text(item) for item in buffer):
|
|
55
|
-
buffer.append(text_value)
|
|
56
|
-
updated = True
|
|
57
|
-
|
|
58
|
-
return updated
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def _update_renderer_transcript(renderer: Any, text_value: str) -> None:
|
|
62
|
-
"""Populate the renderer (or its state) with the supplied text."""
|
|
63
|
-
state = getattr(renderer, "state", None)
|
|
64
|
-
if _update_state_transcript(state, text_value):
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
if hasattr(renderer, "final_text") and not _has_visible_text(getattr(renderer, "final_text", "")):
|
|
68
|
-
try:
|
|
69
|
-
renderer.final_text = text_value
|
|
70
|
-
except Exception:
|
|
71
|
-
pass
|
|
72
|
-
|
|
73
|
-
|
|
74
48
|
class AgentRunRenderingManager:
|
|
75
49
|
"""Coordinate renderer creation and streaming event handling."""
|
|
76
50
|
|
|
@@ -81,6 +55,7 @@ class AgentRunRenderingManager:
|
|
|
81
55
|
logger: Optional logger instance, creates default if None
|
|
82
56
|
"""
|
|
83
57
|
self._logger = logger or logging.getLogger(__name__)
|
|
58
|
+
self._buffer_factory = TranscriptBuffer
|
|
84
59
|
|
|
85
60
|
# --------------------------------------------------------------------- #
|
|
86
61
|
# Renderer setup helpers
|
|
@@ -92,17 +67,38 @@ class AgentRunRenderingManager:
|
|
|
92
67
|
verbose: bool = False,
|
|
93
68
|
) -> RichStreamRenderer:
|
|
94
69
|
"""Create an appropriate renderer based on the supplied spec."""
|
|
70
|
+
transcript_buffer = self._buffer_factory()
|
|
71
|
+
base_options = RendererFactoryOptions(console=_Console(), transcript_buffer=transcript_buffer)
|
|
95
72
|
if isinstance(renderer_spec, RichStreamRenderer):
|
|
96
73
|
return renderer_spec
|
|
97
74
|
|
|
98
75
|
if isinstance(renderer_spec, str):
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
76
|
+
lowered = renderer_spec.lower()
|
|
77
|
+
if lowered == "silent":
|
|
78
|
+
return self._attach_buffer(base_options.build(make_silent_renderer), transcript_buffer)
|
|
79
|
+
if lowered == "minimal":
|
|
80
|
+
return self._attach_buffer(base_options.build(make_minimal_renderer), transcript_buffer)
|
|
81
|
+
if lowered == "verbose":
|
|
82
|
+
return self._attach_buffer(base_options.build(make_verbose_renderer), transcript_buffer)
|
|
83
|
+
|
|
84
|
+
if verbose:
|
|
85
|
+
return self._attach_buffer(base_options.build(make_verbose_renderer), transcript_buffer)
|
|
104
86
|
|
|
105
|
-
|
|
87
|
+
default_options = RendererFactoryOptions(
|
|
88
|
+
console=_Console(),
|
|
89
|
+
transcript_buffer=transcript_buffer,
|
|
90
|
+
verbose=verbose,
|
|
91
|
+
)
|
|
92
|
+
return self._attach_buffer(default_options.build(make_default_renderer), transcript_buffer)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _attach_buffer(renderer: RichStreamRenderer, buffer: TranscriptBuffer) -> RichStreamRenderer:
|
|
96
|
+
"""Attach a captured transcript buffer to a renderer for later inspection."""
|
|
97
|
+
try:
|
|
98
|
+
renderer._captured_transcript_buffer = buffer # type: ignore[attr-defined]
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
return renderer
|
|
106
102
|
|
|
107
103
|
def build_initial_metadata(
|
|
108
104
|
self,
|
|
@@ -123,70 +119,6 @@ class AgentRunRenderingManager:
|
|
|
123
119
|
"""Notify renderer that streaming is starting."""
|
|
124
120
|
renderer.on_start(meta)
|
|
125
121
|
|
|
126
|
-
def _create_silent_renderer(self) -> RichStreamRenderer:
|
|
127
|
-
"""Create a silent renderer that outputs to a string buffer.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
RichStreamRenderer configured for silent output.
|
|
131
|
-
"""
|
|
132
|
-
silent_config = RendererConfig(
|
|
133
|
-
live=False,
|
|
134
|
-
persist_live=False,
|
|
135
|
-
render_thinking=False,
|
|
136
|
-
)
|
|
137
|
-
return RichStreamRenderer(
|
|
138
|
-
console=_Console(file=io.StringIO(), force_terminal=False),
|
|
139
|
-
cfg=silent_config,
|
|
140
|
-
verbose=False,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
def _create_minimal_renderer(self) -> RichStreamRenderer:
|
|
144
|
-
"""Create a minimal renderer with reduced output.
|
|
145
|
-
|
|
146
|
-
Returns:
|
|
147
|
-
RichStreamRenderer configured for minimal output.
|
|
148
|
-
"""
|
|
149
|
-
minimal_config = RendererConfig(
|
|
150
|
-
live=False,
|
|
151
|
-
persist_live=False,
|
|
152
|
-
render_thinking=False,
|
|
153
|
-
)
|
|
154
|
-
return RichStreamRenderer(
|
|
155
|
-
console=_Console(),
|
|
156
|
-
cfg=minimal_config,
|
|
157
|
-
verbose=False,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
def _create_verbose_renderer(self) -> RichStreamRenderer:
|
|
161
|
-
"""Create a verbose renderer with detailed output.
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
RichStreamRenderer configured for verbose output.
|
|
165
|
-
"""
|
|
166
|
-
verbose_config = RendererConfig(
|
|
167
|
-
live=False,
|
|
168
|
-
append_finished_snapshots=False,
|
|
169
|
-
)
|
|
170
|
-
return RichStreamRenderer(
|
|
171
|
-
console=_Console(),
|
|
172
|
-
cfg=verbose_config,
|
|
173
|
-
verbose=True,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
def _create_default_renderer(self, verbose: bool) -> RichStreamRenderer:
|
|
177
|
-
"""Create the default renderer based on verbosity.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
verbose: Whether to create a verbose renderer.
|
|
181
|
-
|
|
182
|
-
Returns:
|
|
183
|
-
RichStreamRenderer instance.
|
|
184
|
-
"""
|
|
185
|
-
if verbose:
|
|
186
|
-
return self._create_verbose_renderer()
|
|
187
|
-
default_config = RendererConfig()
|
|
188
|
-
return RichStreamRenderer(console=_Console(), cfg=default_config)
|
|
189
|
-
|
|
190
122
|
# --------------------------------------------------------------------- #
|
|
191
123
|
# Streaming event handling
|
|
192
124
|
# --------------------------------------------------------------------- #
|
|
@@ -382,7 +314,52 @@ class AgentRunRenderingManager:
|
|
|
382
314
|
return
|
|
383
315
|
|
|
384
316
|
text_value = _coerce_to_string(text)
|
|
385
|
-
|
|
317
|
+
state = getattr(renderer, "state", None)
|
|
318
|
+
if state is None:
|
|
319
|
+
self._ensure_renderer_text(renderer, text_value)
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
self._ensure_state_final_text(state, text_value)
|
|
323
|
+
self._ensure_state_buffer(state, text_value)
|
|
324
|
+
|
|
325
|
+
def _ensure_renderer_text(self, renderer: RichStreamRenderer, text_value: str) -> None:
|
|
326
|
+
"""Best-effort assignment for renderer.final_text."""
|
|
327
|
+
if not hasattr(renderer, "final_text"):
|
|
328
|
+
return
|
|
329
|
+
current_text = getattr(renderer, "final_text", "")
|
|
330
|
+
if _has_visible_text(current_text):
|
|
331
|
+
return
|
|
332
|
+
self._safe_set_attr(renderer, "final_text", text_value)
|
|
333
|
+
|
|
334
|
+
def _ensure_state_final_text(self, state: Any, text_value: str) -> None:
|
|
335
|
+
"""Best-effort assignment for renderer.state.final_text."""
|
|
336
|
+
current_text = getattr(state, "final_text", "")
|
|
337
|
+
if _has_visible_text(current_text):
|
|
338
|
+
return
|
|
339
|
+
self._safe_set_attr(state, "final_text", text_value)
|
|
340
|
+
|
|
341
|
+
def _ensure_state_buffer(self, state: Any, text_value: str) -> None:
|
|
342
|
+
"""Append fallback text to the state buffer when available."""
|
|
343
|
+
buffer = getattr(state, "buffer", None)
|
|
344
|
+
if not hasattr(buffer, "append"):
|
|
345
|
+
return
|
|
346
|
+
self._safe_append(buffer.append, text_value)
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def _safe_set_attr(target: Any, attr: str, value: str) -> None:
|
|
350
|
+
"""Assign attribute while masking renderer-specific failures."""
|
|
351
|
+
try:
|
|
352
|
+
setattr(target, attr, value)
|
|
353
|
+
except Exception:
|
|
354
|
+
pass
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def _safe_append(appender: Callable[[str], Any], value: str) -> None:
|
|
358
|
+
"""Invoke append-like functions without leaking renderer errors."""
|
|
359
|
+
try:
|
|
360
|
+
appender(value)
|
|
361
|
+
except Exception:
|
|
362
|
+
pass
|
|
386
363
|
|
|
387
364
|
# --------------------------------------------------------------------- #
|
|
388
365
|
# Finalisation helpers
|
|
@@ -409,7 +386,9 @@ class AgentRunRenderingManager:
|
|
|
409
386
|
elif hasattr(renderer, "buffer"):
|
|
410
387
|
buffer_values = renderer.buffer
|
|
411
388
|
|
|
412
|
-
if buffer_values
|
|
389
|
+
if isinstance(buffer_values, TranscriptBuffer):
|
|
390
|
+
rendered_text = buffer_values.render()
|
|
391
|
+
elif buffer_values is not None:
|
|
413
392
|
try:
|
|
414
393
|
rendered_text = "".join(buffer_values)
|
|
415
394
|
except TypeError:
|
|
@@ -420,7 +399,7 @@ class AgentRunRenderingManager:
|
|
|
420
399
|
self._ensure_renderer_final_content(renderer, fallback_text)
|
|
421
400
|
|
|
422
401
|
renderer.on_complete(st)
|
|
423
|
-
return final_text or rendered_text or
|
|
402
|
+
return final_text or rendered_text or NO_AGENT_RESPONSE_FALLBACK
|
|
424
403
|
|
|
425
404
|
|
|
426
405
|
def compute_timeout_seconds(kwargs: dict[str, Any]) -> float:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Shared helpers for client configuration wiring.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from glaip_sdk.client.base import BaseClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_shared_config(client: BaseClient) -> dict[str, Any]:
|
|
15
|
+
"""Return the keyword arguments used to initialize sub-clients."""
|
|
16
|
+
return {
|
|
17
|
+
"parent_client": client,
|
|
18
|
+
"api_url": client.api_url,
|
|
19
|
+
"api_key": client.api_key,
|
|
20
|
+
"timeout": client._timeout,
|
|
21
|
+
}
|
glaip_sdk/client/tools.py
CHANGED
|
@@ -18,6 +18,7 @@ from glaip_sdk.config.constants import (
|
|
|
18
18
|
)
|
|
19
19
|
from glaip_sdk.models import Tool
|
|
20
20
|
from glaip_sdk.utils.client_utils import (
|
|
21
|
+
add_kwargs_to_payload,
|
|
21
22
|
create_model_instances,
|
|
22
23
|
find_by_name,
|
|
23
24
|
)
|
|
@@ -200,9 +201,7 @@ class ToolClient(BaseClient):
|
|
|
200
201
|
|
|
201
202
|
# Add any other kwargs (excluding already handled ones)
|
|
202
203
|
excluded_keys = {"tags", "version"}
|
|
203
|
-
|
|
204
|
-
if key not in excluded_keys:
|
|
205
|
-
payload[key] = value
|
|
204
|
+
add_kwargs_to_payload(payload, kwargs, excluded_keys)
|
|
206
205
|
|
|
207
206
|
return payload
|
|
208
207
|
|
glaip_sdk/config/constants.py
CHANGED
|
@@ -39,3 +39,14 @@ DEFAULT_MCP_TRANSPORT = "stdio"
|
|
|
39
39
|
|
|
40
40
|
# Default error messages
|
|
41
41
|
DEFAULT_ERROR_MESSAGE = "Unknown error"
|
|
42
|
+
|
|
43
|
+
# Agent configuration fields used for CLI args and payload building
|
|
44
|
+
AGENT_CONFIG_FIELDS = (
|
|
45
|
+
"name",
|
|
46
|
+
"instruction",
|
|
47
|
+
"model",
|
|
48
|
+
"tools",
|
|
49
|
+
"agents",
|
|
50
|
+
"mcps",
|
|
51
|
+
"timeout",
|
|
52
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Models package for AIP SDK.
|
|
2
|
+
|
|
3
|
+
This package re-exports models from the legacy models.py file for backward compatibility.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib.util
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# Export new agent runs models first (no dependencies on legacy models)
|
|
14
|
+
from glaip_sdk.models.agent_runs import ( # noqa: F401
|
|
15
|
+
RunOutputChunk,
|
|
16
|
+
RunSummary,
|
|
17
|
+
RunsPage,
|
|
18
|
+
RunWithOutput,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Import from the parent models.py file
|
|
22
|
+
_parent_file = Path(__file__).parent.parent / "models.py"
|
|
23
|
+
if _parent_file.exists():
|
|
24
|
+
spec = importlib.util.spec_from_file_location("glaip_sdk.models_legacy", _parent_file)
|
|
25
|
+
models_legacy = importlib.util.module_from_spec(spec)
|
|
26
|
+
sys.modules["glaip_sdk.models_legacy"] = models_legacy
|
|
27
|
+
spec.loader.exec_module(models_legacy)
|
|
28
|
+
|
|
29
|
+
# Re-export all models
|
|
30
|
+
Agent = models_legacy.Agent
|
|
31
|
+
Tool = models_legacy.Tool
|
|
32
|
+
MCP = models_legacy.MCP
|
|
33
|
+
LanguageModelResponse = models_legacy.LanguageModelResponse
|
|
34
|
+
TTYRenderer = models_legacy.TTYRenderer
|
|
35
|
+
else:
|
|
36
|
+
# Fallback: try direct import (won't work if models/ exists)
|
|
37
|
+
# pragma: no cover - defensive fallback path, unlikely to execute
|
|
38
|
+
from glaip_sdk import models as models_legacy # type: ignore # pragma: no cover
|
|
39
|
+
|
|
40
|
+
Agent = models_legacy.Agent # pragma: no cover
|
|
41
|
+
Tool = models_legacy.Tool # pragma: no cover
|
|
42
|
+
MCP = models_legacy.MCP # pragma: no cover
|
|
43
|
+
LanguageModelResponse = models_legacy.LanguageModelResponse # pragma: no cover
|
|
44
|
+
TTYRenderer = models_legacy.TTYRenderer # pragma: no cover
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"Agent",
|
|
48
|
+
"Tool",
|
|
49
|
+
"MCP",
|
|
50
|
+
"LanguageModelResponse",
|
|
51
|
+
"TTYRenderer",
|
|
52
|
+
"RunSummary",
|
|
53
|
+
"RunsPage",
|
|
54
|
+
"RunWithOutput",
|
|
55
|
+
"RunOutputChunk",
|
|
56
|
+
]
|