glaip-sdk 0.0.15__py3-none-any.whl → 0.0.16__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/branding.py +27 -1
- glaip_sdk/cli/commands/agents.py +26 -17
- glaip_sdk/cli/commands/configure.py +39 -50
- glaip_sdk/cli/commands/mcps.py +1 -3
- glaip_sdk/cli/config.py +42 -0
- glaip_sdk/cli/display.py +92 -26
- glaip_sdk/cli/main.py +141 -124
- glaip_sdk/cli/mcp_validators.py +2 -2
- glaip_sdk/cli/pager.py +3 -2
- glaip_sdk/cli/parsers/json_input.py +2 -2
- glaip_sdk/cli/resolution.py +12 -10
- glaip_sdk/cli/slash/agent_session.py +7 -0
- glaip_sdk/cli/slash/prompt.py +21 -2
- glaip_sdk/cli/slash/session.py +15 -21
- glaip_sdk/cli/update_notifier.py +8 -2
- glaip_sdk/cli/utils.py +110 -53
- glaip_sdk/client/_agent_payloads.py +504 -0
- glaip_sdk/client/agents.py +194 -551
- glaip_sdk/client/base.py +92 -20
- glaip_sdk/client/main.py +6 -0
- glaip_sdk/client/run_rendering.py +275 -0
- glaip_sdk/config/constants.py +3 -0
- glaip_sdk/exceptions.py +15 -0
- glaip_sdk/models.py +5 -0
- glaip_sdk/payload_schemas/__init__.py +19 -0
- glaip_sdk/payload_schemas/agent.py +87 -0
- glaip_sdk/rich_components.py +12 -0
- glaip_sdk/utils/client_utils.py +12 -0
- glaip_sdk/utils/import_export.py +2 -2
- glaip_sdk/utils/rendering/formatting.py +5 -0
- glaip_sdk/utils/rendering/models.py +22 -0
- glaip_sdk/utils/rendering/renderer/base.py +9 -1
- glaip_sdk/utils/rendering/renderer/panels.py +0 -1
- glaip_sdk/utils/rendering/steps.py +59 -0
- glaip_sdk/utils/serialization.py +24 -3
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/METADATA +1 -1
- glaip_sdk-0.0.16.dist-info/RECORD +72 -0
- glaip_sdk-0.0.15.dist-info/RECORD +0 -67
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.15.dist-info → glaip_sdk-0.0.16.dist-info}/entry_points.txt +0 -0
glaip_sdk/client/agents.py
CHANGED
|
@@ -5,20 +5,26 @@ Authors:
|
|
|
5
5
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import io
|
|
9
8
|
import json
|
|
10
9
|
import logging
|
|
11
|
-
from collections.abc import AsyncGenerator
|
|
12
|
-
from time import monotonic
|
|
10
|
+
from collections.abc import AsyncGenerator, Mapping
|
|
13
11
|
from typing import Any, BinaryIO
|
|
14
12
|
|
|
15
13
|
import httpx
|
|
16
|
-
from rich.console import Console as _Console
|
|
17
14
|
|
|
15
|
+
from glaip_sdk.client._agent_payloads import (
|
|
16
|
+
AgentCreateRequest,
|
|
17
|
+
AgentListParams,
|
|
18
|
+
AgentListResult,
|
|
19
|
+
AgentUpdateRequest,
|
|
20
|
+
)
|
|
18
21
|
from glaip_sdk.client.base import BaseClient
|
|
22
|
+
from glaip_sdk.client.run_rendering import (
|
|
23
|
+
AgentRunRenderingManager,
|
|
24
|
+
compute_timeout_seconds,
|
|
25
|
+
)
|
|
19
26
|
from glaip_sdk.config.constants import (
|
|
20
27
|
DEFAULT_AGENT_FRAMEWORK,
|
|
21
|
-
DEFAULT_AGENT_PROVIDER,
|
|
22
28
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
23
29
|
DEFAULT_AGENT_TYPE,
|
|
24
30
|
DEFAULT_AGENT_VERSION,
|
|
@@ -29,14 +35,10 @@ from glaip_sdk.models import Agent
|
|
|
29
35
|
from glaip_sdk.utils.client_utils import (
|
|
30
36
|
aiter_sse_events,
|
|
31
37
|
create_model_instances,
|
|
32
|
-
extract_ids,
|
|
33
38
|
find_by_name,
|
|
34
|
-
iter_sse_events,
|
|
35
39
|
prepare_multipart_data,
|
|
36
40
|
)
|
|
37
|
-
from glaip_sdk.utils.rendering.models import RunStats
|
|
38
41
|
from glaip_sdk.utils.rendering.renderer import RichStreamRenderer
|
|
39
|
-
from glaip_sdk.utils.rendering.renderer.config import RendererConfig
|
|
40
42
|
from glaip_sdk.utils.validation import validate_agent_instruction
|
|
41
43
|
|
|
42
44
|
# API endpoints
|
|
@@ -65,44 +67,51 @@ class AgentClient(BaseClient):
|
|
|
65
67
|
**kwargs: Additional arguments for standalone initialization
|
|
66
68
|
"""
|
|
67
69
|
super().__init__(parent_client=parent_client, **kwargs)
|
|
70
|
+
self._renderer_manager = AgentRunRenderingManager(logger)
|
|
68
71
|
|
|
69
72
|
def list_agents(
|
|
70
73
|
self,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sync_langflow_agents: bool = False,
|
|
76
|
-
) -> list[Agent]:
|
|
77
|
-
"""List agents with optional filtering.
|
|
74
|
+
query: AgentListParams | None = None,
|
|
75
|
+
**kwargs: Any,
|
|
76
|
+
) -> AgentListResult:
|
|
77
|
+
"""List agents with optional filtering and pagination support.
|
|
78
78
|
|
|
79
79
|
Args:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
name: Filter by partial name match (case-insensitive)
|
|
83
|
-
version: Filter by exact version match
|
|
84
|
-
sync_langflow_agents: Sync with LangFlow server before listing (only applies when agent_type=langflow)
|
|
85
|
-
|
|
86
|
-
Returns:
|
|
87
|
-
List of agents matching the filters
|
|
80
|
+
query: Query parameters for filtering agents. If None, uses kwargs to create query.
|
|
81
|
+
**kwargs: Individual filter parameters for backward compatibility.
|
|
88
82
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
83
|
+
if query is not None and kwargs:
|
|
84
|
+
# Both query object and individual parameters provided
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"Provide either `query` or individual filter arguments, not both."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if query is None:
|
|
90
|
+
# Create query from individual parameters for backward compatibility
|
|
91
|
+
query = AgentListParams(**kwargs)
|
|
92
|
+
|
|
93
|
+
params = query.to_query_params()
|
|
94
|
+
envelope = self._request_with_envelope(
|
|
95
|
+
"GET",
|
|
96
|
+
AGENTS_ENDPOINT,
|
|
97
|
+
params=params if params else None,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if not isinstance(envelope, dict):
|
|
101
|
+
envelope = {"data": envelope}
|
|
102
|
+
|
|
103
|
+
data_payload = envelope.get("data") or []
|
|
104
|
+
items = create_model_instances(data_payload, Agent, self)
|
|
105
|
+
|
|
106
|
+
return AgentListResult(
|
|
107
|
+
items=items,
|
|
108
|
+
total=envelope.get("total"),
|
|
109
|
+
page=envelope.get("page"),
|
|
110
|
+
limit=envelope.get("limit"),
|
|
111
|
+
has_next=envelope.get("has_next"),
|
|
112
|
+
has_prev=envelope.get("has_prev"),
|
|
113
|
+
message=envelope.get("message"),
|
|
114
|
+
)
|
|
106
115
|
|
|
107
116
|
def sync_langflow_agents(
|
|
108
117
|
self,
|
|
@@ -151,292 +160,65 @@ class AgentClient(BaseClient):
|
|
|
151
160
|
|
|
152
161
|
def find_agents(self, name: str | None = None) -> list[Agent]:
|
|
153
162
|
"""Find agents by name."""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
params["name"] = name
|
|
157
|
-
|
|
158
|
-
data = self._request("GET", AGENTS_ENDPOINT, params=params)
|
|
159
|
-
agents = create_model_instances(data, Agent, self)
|
|
163
|
+
result = self.list_agents(name=name)
|
|
164
|
+
agents = list(result)
|
|
160
165
|
if name is None:
|
|
161
166
|
return agents
|
|
162
167
|
return find_by_name(agents, name, case_sensitive=False)
|
|
163
168
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
) -> dict[str, Any]:
|
|
174
|
-
"""Build payload for agent creation with proper LM selection and metadata handling.
|
|
175
|
-
|
|
176
|
-
CENTRALIZED PAYLOAD BUILDING LOGIC:
|
|
177
|
-
- LM exclusivity: Uses language_model_id if provided, otherwise provider/model_name
|
|
178
|
-
- Always includes required backend metadata
|
|
179
|
-
- Preserves mem0 keys in agent_config
|
|
180
|
-
- Handles tool/agent ID extraction from objects
|
|
181
|
-
|
|
182
|
-
Args:
|
|
183
|
-
name: Agent name
|
|
184
|
-
instruction: Agent instruction
|
|
185
|
-
model: Language model name (used when language_model_id not provided)
|
|
186
|
-
tools: List of tools to attach
|
|
187
|
-
agents: List of sub-agents to attach
|
|
188
|
-
timeout: Agent execution timeout
|
|
189
|
-
**kwargs: Additional parameters (language_model_id, agent_config, etc.)
|
|
190
|
-
|
|
191
|
-
Returns:
|
|
192
|
-
Complete payload dictionary for agent creation
|
|
193
|
-
"""
|
|
194
|
-
# Prepare the creation payload with required fields
|
|
195
|
-
payload: dict[str, Any] = {
|
|
196
|
-
"name": name.strip(),
|
|
197
|
-
"instruction": instruction.strip(),
|
|
198
|
-
"type": DEFAULT_AGENT_TYPE,
|
|
199
|
-
"framework": DEFAULT_AGENT_FRAMEWORK,
|
|
200
|
-
"version": DEFAULT_AGENT_VERSION,
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
# Language model selection with exclusivity:
|
|
204
|
-
# Priority: language_model_id (if provided) > provider/model_name (fallback)
|
|
205
|
-
if kwargs.get("language_model_id"):
|
|
206
|
-
# Use language_model_id - defer to kwargs update below
|
|
207
|
-
pass
|
|
208
|
-
else:
|
|
209
|
-
# Use provider/model_name fallback
|
|
210
|
-
payload["provider"] = DEFAULT_AGENT_PROVIDER
|
|
211
|
-
payload["model_name"] = model or DEFAULT_MODEL
|
|
212
|
-
|
|
213
|
-
# Include execution timeout if provided
|
|
214
|
-
if timeout is not None:
|
|
215
|
-
payload["timeout"] = str(timeout)
|
|
216
|
-
|
|
217
|
-
# Ensure minimum required metadata for visibility
|
|
218
|
-
if "metadata" not in kwargs:
|
|
219
|
-
kwargs["metadata"] = {}
|
|
220
|
-
if "type" not in kwargs["metadata"]:
|
|
221
|
-
kwargs["metadata"]["type"] = "custom"
|
|
222
|
-
|
|
223
|
-
# Extract IDs from tool and agent objects
|
|
224
|
-
tool_ids = extract_ids(tools)
|
|
225
|
-
agent_ids = extract_ids(agents)
|
|
226
|
-
|
|
227
|
-
# Add tools and agents if provided
|
|
228
|
-
if tool_ids:
|
|
229
|
-
payload["tools"] = tool_ids
|
|
230
|
-
if agent_ids:
|
|
231
|
-
payload["agents"] = agent_ids
|
|
232
|
-
|
|
233
|
-
# Add any additional kwargs (including language_model_id, agent_config, etc.)
|
|
234
|
-
payload.update(kwargs)
|
|
169
|
+
# ------------------------------------------------------------------ #
|
|
170
|
+
# Renderer delegation helpers
|
|
171
|
+
# ------------------------------------------------------------------ #
|
|
172
|
+
def _get_renderer_manager(self) -> AgentRunRenderingManager:
|
|
173
|
+
manager = getattr(self, "_renderer_manager", None)
|
|
174
|
+
if manager is None:
|
|
175
|
+
manager = AgentRunRenderingManager(logger)
|
|
176
|
+
self._renderer_manager = manager
|
|
177
|
+
return manager
|
|
235
178
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
"instruction": instruction
|
|
245
|
-
if instruction is not None
|
|
246
|
-
else current_agent.instruction,
|
|
247
|
-
"type": DEFAULT_AGENT_TYPE, # Required by backend
|
|
248
|
-
"framework": DEFAULT_AGENT_FRAMEWORK, # Required by backend
|
|
249
|
-
"version": DEFAULT_AGENT_VERSION, # Required by backend
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
def _handle_language_model_selection(
|
|
253
|
-
self,
|
|
254
|
-
update_data: dict[str, Any],
|
|
255
|
-
current_agent: "Agent",
|
|
256
|
-
model: str | None,
|
|
257
|
-
language_model_id: str | None,
|
|
258
|
-
) -> None:
|
|
259
|
-
"""Handle language model selection with proper priority and fallbacks."""
|
|
260
|
-
if language_model_id:
|
|
261
|
-
# Use language_model_id if provided
|
|
262
|
-
update_data["language_model_id"] = language_model_id
|
|
263
|
-
elif model is not None:
|
|
264
|
-
# Use explicit model parameter
|
|
265
|
-
update_data["provider"] = DEFAULT_AGENT_PROVIDER
|
|
266
|
-
update_data["model_name"] = model
|
|
267
|
-
else:
|
|
268
|
-
# Use current agent config or fallbacks
|
|
269
|
-
self._set_language_model_from_current_agent(update_data, current_agent)
|
|
270
|
-
|
|
271
|
-
def _set_language_model_from_current_agent(
|
|
272
|
-
self, update_data: dict[str, Any], current_agent: "Agent"
|
|
273
|
-
) -> None:
|
|
274
|
-
"""Set language model from current agent config or use defaults."""
|
|
275
|
-
if hasattr(current_agent, "agent_config") and current_agent.agent_config:
|
|
276
|
-
agent_config = current_agent.agent_config
|
|
277
|
-
if "lm_provider" in agent_config:
|
|
278
|
-
update_data["provider"] = agent_config["lm_provider"]
|
|
279
|
-
if "lm_name" in agent_config:
|
|
280
|
-
update_data["model_name"] = agent_config["lm_name"]
|
|
281
|
-
else:
|
|
282
|
-
# Default fallback values
|
|
283
|
-
update_data["provider"] = DEFAULT_AGENT_PROVIDER
|
|
284
|
-
update_data["model_name"] = DEFAULT_MODEL
|
|
285
|
-
|
|
286
|
-
def _handle_tools_and_agents(
|
|
287
|
-
self,
|
|
288
|
-
update_data: dict[str, Any],
|
|
289
|
-
current_agent: "Agent",
|
|
290
|
-
tools: list | None,
|
|
291
|
-
agents: list | None,
|
|
292
|
-
) -> None:
|
|
293
|
-
"""Handle tools and agents with proper ID extraction."""
|
|
294
|
-
# Handle tools
|
|
295
|
-
if tools is not None:
|
|
296
|
-
tool_ids = extract_ids(tools)
|
|
297
|
-
update_data["tools"] = tool_ids if tool_ids else []
|
|
298
|
-
else:
|
|
299
|
-
update_data["tools"] = self._extract_current_tool_ids(current_agent)
|
|
179
|
+
def _create_renderer(
|
|
180
|
+
self, renderer: RichStreamRenderer | str | None, **kwargs: Any
|
|
181
|
+
) -> RichStreamRenderer:
|
|
182
|
+
manager = self._get_renderer_manager()
|
|
183
|
+
verbose = kwargs.get("verbose", False)
|
|
184
|
+
if isinstance(renderer, RichStreamRenderer) or hasattr(renderer, "on_start"):
|
|
185
|
+
return renderer # type: ignore[return-value]
|
|
186
|
+
return manager.create_renderer(renderer, verbose=verbose)
|
|
300
187
|
|
|
301
|
-
|
|
302
|
-
if agents is not None:
|
|
303
|
-
agent_ids = extract_ids(agents)
|
|
304
|
-
update_data["agents"] = agent_ids if agent_ids else []
|
|
305
|
-
else:
|
|
306
|
-
update_data["agents"] = self._extract_current_agent_ids(current_agent)
|
|
307
|
-
|
|
308
|
-
def _extract_current_tool_ids(self, current_agent: "Agent") -> list[str]:
|
|
309
|
-
"""Extract tool IDs from current agent."""
|
|
310
|
-
if current_agent.tools:
|
|
311
|
-
return [
|
|
312
|
-
tool["id"] if isinstance(tool, dict) else tool
|
|
313
|
-
for tool in current_agent.tools
|
|
314
|
-
]
|
|
315
|
-
return []
|
|
316
|
-
|
|
317
|
-
def _extract_current_agent_ids(self, current_agent: "Agent") -> list[str]:
|
|
318
|
-
"""Extract agent IDs from current agent."""
|
|
319
|
-
if current_agent.agents:
|
|
320
|
-
return [
|
|
321
|
-
agent["id"] if isinstance(agent, dict) else agent
|
|
322
|
-
for agent in current_agent.agents
|
|
323
|
-
]
|
|
324
|
-
return []
|
|
325
|
-
|
|
326
|
-
def _handle_agent_config(
|
|
327
|
-
self,
|
|
328
|
-
update_data: dict[str, Any],
|
|
329
|
-
current_agent: "Agent",
|
|
330
|
-
agent_config: dict | None,
|
|
331
|
-
) -> None:
|
|
332
|
-
"""Handle agent_config with proper merging and cleanup."""
|
|
333
|
-
if agent_config is not None:
|
|
334
|
-
# Use provided agent_config, merging with current if needed
|
|
335
|
-
update_data["agent_config"] = self._merge_agent_configs(
|
|
336
|
-
current_agent, agent_config
|
|
337
|
-
)
|
|
338
|
-
elif hasattr(current_agent, "agent_config") and current_agent.agent_config:
|
|
339
|
-
# Preserve existing agent_config
|
|
340
|
-
update_data["agent_config"] = current_agent.agent_config.copy()
|
|
341
|
-
else:
|
|
342
|
-
# Default agent_config
|
|
343
|
-
update_data["agent_config"] = {
|
|
344
|
-
"lm_provider": DEFAULT_AGENT_PROVIDER,
|
|
345
|
-
"lm_name": DEFAULT_MODEL,
|
|
346
|
-
"lm_hyperparameters": {"temperature": 0.0},
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
# Clean LM keys from agent_config to prevent conflicts
|
|
350
|
-
self._clean_agent_config_lm_keys(update_data)
|
|
351
|
-
|
|
352
|
-
def _merge_agent_configs(self, current_agent: "Agent", new_config: dict) -> dict:
|
|
353
|
-
"""Merge current agent config with new config."""
|
|
354
|
-
if hasattr(current_agent, "agent_config") and current_agent.agent_config:
|
|
355
|
-
merged_config = current_agent.agent_config.copy()
|
|
356
|
-
merged_config.update(new_config)
|
|
357
|
-
return merged_config
|
|
358
|
-
return new_config
|
|
359
|
-
|
|
360
|
-
def _clean_agent_config_lm_keys(self, update_data: dict[str, Any]) -> None:
|
|
361
|
-
"""Remove LM keys from agent_config to prevent conflicts."""
|
|
362
|
-
if "agent_config" in update_data and isinstance(
|
|
363
|
-
update_data["agent_config"], dict
|
|
364
|
-
):
|
|
365
|
-
agent_config = update_data["agent_config"]
|
|
366
|
-
lm_keys_to_remove = {
|
|
367
|
-
"lm_provider",
|
|
368
|
-
"lm_name",
|
|
369
|
-
"lm_base_url",
|
|
370
|
-
"lm_hyperparameters",
|
|
371
|
-
}
|
|
372
|
-
for key in lm_keys_to_remove:
|
|
373
|
-
agent_config.pop(key, None)
|
|
374
|
-
|
|
375
|
-
def _finalize_update_payload(
|
|
188
|
+
def _process_stream_events(
|
|
376
189
|
self,
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return update_data
|
|
190
|
+
stream_response: httpx.Response,
|
|
191
|
+
renderer: RichStreamRenderer,
|
|
192
|
+
timeout_seconds: float,
|
|
193
|
+
agent_name: str | None,
|
|
194
|
+
meta: dict[str, Any],
|
|
195
|
+
) -> tuple[str, dict[str, Any], float | None, float | None]:
|
|
196
|
+
manager = self._get_renderer_manager()
|
|
197
|
+
return manager.process_stream_events(
|
|
198
|
+
stream_response,
|
|
199
|
+
renderer,
|
|
200
|
+
timeout_seconds,
|
|
201
|
+
agent_name,
|
|
202
|
+
meta,
|
|
203
|
+
)
|
|
393
204
|
|
|
394
|
-
def
|
|
205
|
+
def _finalize_renderer(
|
|
395
206
|
self,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
) ->
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
**kwargs: Additional parameters including language_model_id, agent_config, etc.
|
|
410
|
-
|
|
411
|
-
Returns:
|
|
412
|
-
Complete payload dictionary for agent update
|
|
413
|
-
|
|
414
|
-
Notes:
|
|
415
|
-
- LM exclusivity: Uses language_model_id if provided, otherwise provider/model_name
|
|
416
|
-
- Preserves current values as defaults when new values not provided
|
|
417
|
-
- Handles tools/agents updates with proper ID extraction
|
|
418
|
-
"""
|
|
419
|
-
# Build basic payload
|
|
420
|
-
update_data = self._build_basic_update_payload(current_agent, name, instruction)
|
|
421
|
-
|
|
422
|
-
# Handle language model selection
|
|
423
|
-
language_model_id = kwargs.get("language_model_id")
|
|
424
|
-
self._handle_language_model_selection(
|
|
425
|
-
update_data, current_agent, model, language_model_id
|
|
207
|
+
renderer: RichStreamRenderer,
|
|
208
|
+
final_text: str,
|
|
209
|
+
stats_usage: dict[str, Any],
|
|
210
|
+
started_monotonic: float | None,
|
|
211
|
+
finished_monotonic: float | None,
|
|
212
|
+
) -> str:
|
|
213
|
+
manager = self._get_renderer_manager()
|
|
214
|
+
return manager.finalize_renderer(
|
|
215
|
+
renderer,
|
|
216
|
+
final_text,
|
|
217
|
+
stats_usage,
|
|
218
|
+
started_monotonic,
|
|
219
|
+
finished_monotonic,
|
|
426
220
|
)
|
|
427
221
|
|
|
428
|
-
# Handle tools and agents
|
|
429
|
-
tools = kwargs.get("tools")
|
|
430
|
-
agents = kwargs.get("agents")
|
|
431
|
-
self._handle_tools_and_agents(update_data, current_agent, tools, agents)
|
|
432
|
-
|
|
433
|
-
# Handle agent config
|
|
434
|
-
agent_config = kwargs.get("agent_config")
|
|
435
|
-
self._handle_agent_config(update_data, current_agent, agent_config)
|
|
436
|
-
|
|
437
|
-
# Finalize payload
|
|
438
|
-
return self._finalize_update_payload(update_data, current_agent, **kwargs)
|
|
439
|
-
|
|
440
222
|
def create_agent(
|
|
441
223
|
self,
|
|
442
224
|
name: str,
|
|
@@ -445,28 +227,60 @@ class AgentClient(BaseClient):
|
|
|
445
227
|
tools: list[str | Any] | None = None,
|
|
446
228
|
agents: list[str | Any] | None = None,
|
|
447
229
|
timeout: int = DEFAULT_AGENT_RUN_TIMEOUT,
|
|
230
|
+
*,
|
|
231
|
+
mcps: list[str | Any] | None = None,
|
|
232
|
+
tool_configs: Mapping[str, Any] | None = None,
|
|
448
233
|
**kwargs: Any,
|
|
449
234
|
) -> "Agent":
|
|
450
235
|
"""Create a new agent."""
|
|
451
|
-
# Client-side validation
|
|
452
236
|
if not name or not name.strip():
|
|
453
237
|
raise ValueError("Agent name cannot be empty or whitespace")
|
|
454
238
|
|
|
455
|
-
# Validate instruction using centralized validation
|
|
456
239
|
instruction = validate_agent_instruction(instruction)
|
|
457
240
|
|
|
458
|
-
|
|
459
|
-
|
|
241
|
+
agent_type = kwargs.pop("agent_type", kwargs.pop("type", DEFAULT_AGENT_TYPE))
|
|
242
|
+
framework = kwargs.pop("framework", DEFAULT_AGENT_FRAMEWORK)
|
|
243
|
+
version = kwargs.pop("version", DEFAULT_AGENT_VERSION)
|
|
244
|
+
language_model_id = kwargs.pop("language_model_id", None)
|
|
245
|
+
provider_override = kwargs.pop("provider", None)
|
|
246
|
+
model_name_override = kwargs.pop("model_name", None)
|
|
247
|
+
account_id = kwargs.pop("account_id", None)
|
|
248
|
+
description = kwargs.pop("description", None)
|
|
249
|
+
metadata = kwargs.pop("metadata", None)
|
|
250
|
+
agent_config = kwargs.pop("agent_config", None)
|
|
251
|
+
a2a_profile = kwargs.pop("a2a_profile", None)
|
|
252
|
+
mcps = mcps if mcps is not None else kwargs.pop("mcps", None)
|
|
253
|
+
tool_configs = (
|
|
254
|
+
tool_configs
|
|
255
|
+
if tool_configs is not None
|
|
256
|
+
else kwargs.pop("tool_configs", None)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
request = AgentCreateRequest(
|
|
460
260
|
name=name,
|
|
461
261
|
instruction=instruction,
|
|
462
262
|
model=model,
|
|
263
|
+
language_model_id=language_model_id,
|
|
264
|
+
provider=provider_override,
|
|
265
|
+
model_name=model_name_override,
|
|
266
|
+
agent_type=agent_type,
|
|
267
|
+
framework=framework,
|
|
268
|
+
version=version,
|
|
269
|
+
account_id=account_id,
|
|
270
|
+
description=description,
|
|
271
|
+
metadata=metadata,
|
|
463
272
|
tools=tools,
|
|
464
273
|
agents=agents,
|
|
274
|
+
mcps=mcps,
|
|
275
|
+
tool_configs=tool_configs,
|
|
276
|
+
agent_config=agent_config,
|
|
465
277
|
timeout=timeout,
|
|
466
|
-
|
|
278
|
+
a2a_profile=a2a_profile,
|
|
279
|
+
extras=kwargs,
|
|
467
280
|
)
|
|
468
281
|
|
|
469
|
-
|
|
282
|
+
payload = request.to_payload()
|
|
283
|
+
|
|
470
284
|
full_agent_data = self._post_then_fetch(
|
|
471
285
|
id_key="id",
|
|
472
286
|
post_endpoint=AGENTS_ENDPOINT,
|
|
@@ -484,20 +298,49 @@ class AgentClient(BaseClient):
|
|
|
484
298
|
**kwargs: Any,
|
|
485
299
|
) -> "Agent":
|
|
486
300
|
"""Update an existing agent."""
|
|
487
|
-
# First, get the current agent data
|
|
488
301
|
current_agent = self.get_agent_by_id(agent_id)
|
|
489
302
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
303
|
+
language_model_id = kwargs.pop("language_model_id", None)
|
|
304
|
+
provider_override = kwargs.pop("provider", None)
|
|
305
|
+
model_name_override = kwargs.pop("model_name", None)
|
|
306
|
+
agent_type_override = kwargs.pop("agent_type", kwargs.pop("type", None))
|
|
307
|
+
framework_override = kwargs.pop("framework", None)
|
|
308
|
+
version_override = kwargs.pop("version", None)
|
|
309
|
+
account_id = kwargs.pop("account_id", None)
|
|
310
|
+
description = kwargs.pop("description", None)
|
|
311
|
+
metadata = kwargs.pop("metadata", None)
|
|
312
|
+
tools = kwargs.pop("tools", None)
|
|
313
|
+
tool_configs = kwargs.pop("tool_configs", None)
|
|
314
|
+
agents_value = kwargs.pop("agents", None)
|
|
315
|
+
mcps = kwargs.pop("mcps", None)
|
|
316
|
+
agent_config = kwargs.pop("agent_config", None)
|
|
317
|
+
a2a_profile = kwargs.pop("a2a_profile", None)
|
|
318
|
+
|
|
319
|
+
request = AgentUpdateRequest(
|
|
493
320
|
name=name,
|
|
494
321
|
instruction=instruction,
|
|
322
|
+
description=description,
|
|
495
323
|
model=model,
|
|
496
|
-
|
|
324
|
+
language_model_id=language_model_id,
|
|
325
|
+
provider=provider_override,
|
|
326
|
+
model_name=model_name_override,
|
|
327
|
+
agent_type=agent_type_override,
|
|
328
|
+
framework=framework_override,
|
|
329
|
+
version=version_override,
|
|
330
|
+
account_id=account_id,
|
|
331
|
+
metadata=metadata,
|
|
332
|
+
tools=tools,
|
|
333
|
+
tool_configs=tool_configs,
|
|
334
|
+
agents=agents_value,
|
|
335
|
+
mcps=mcps,
|
|
336
|
+
agent_config=agent_config,
|
|
337
|
+
a2a_profile=a2a_profile,
|
|
338
|
+
extras=kwargs,
|
|
497
339
|
)
|
|
498
340
|
|
|
499
|
-
|
|
500
|
-
|
|
341
|
+
payload = request.to_payload(current_agent)
|
|
342
|
+
|
|
343
|
+
data = self._request("PUT", f"/agents/{agent_id}", json=payload)
|
|
501
344
|
return Agent(**data)._set_client(self)
|
|
502
345
|
|
|
503
346
|
def delete_agent(self, agent_id: str) -> None:
|
|
@@ -562,197 +405,6 @@ class AgentClient(BaseClient):
|
|
|
562
405
|
execution_timeout = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
563
406
|
return request_timeout, execution_timeout
|
|
564
407
|
|
|
565
|
-
def _create_renderer(
|
|
566
|
-
self, renderer: RichStreamRenderer | None, **kwargs: Any
|
|
567
|
-
) -> RichStreamRenderer:
|
|
568
|
-
"""Create appropriate renderer based on configuration."""
|
|
569
|
-
if isinstance(renderer, RichStreamRenderer):
|
|
570
|
-
return renderer
|
|
571
|
-
|
|
572
|
-
verbose = kwargs.get("verbose", False)
|
|
573
|
-
|
|
574
|
-
if isinstance(renderer, str):
|
|
575
|
-
if renderer == "silent":
|
|
576
|
-
return self._create_silent_renderer()
|
|
577
|
-
elif renderer == "minimal":
|
|
578
|
-
return self._create_minimal_renderer()
|
|
579
|
-
else:
|
|
580
|
-
return self._create_default_renderer(verbose)
|
|
581
|
-
elif verbose:
|
|
582
|
-
return self._create_verbose_renderer()
|
|
583
|
-
else:
|
|
584
|
-
return self._create_default_renderer(verbose)
|
|
585
|
-
|
|
586
|
-
def _create_silent_renderer(self) -> RichStreamRenderer:
|
|
587
|
-
"""Create a silent renderer that suppresses all output."""
|
|
588
|
-
silent_config = RendererConfig(
|
|
589
|
-
live=False,
|
|
590
|
-
persist_live=False,
|
|
591
|
-
show_delegate_tool_panels=False,
|
|
592
|
-
render_thinking=False,
|
|
593
|
-
)
|
|
594
|
-
return RichStreamRenderer(
|
|
595
|
-
console=_Console(file=io.StringIO(), force_terminal=False),
|
|
596
|
-
cfg=silent_config,
|
|
597
|
-
verbose=False,
|
|
598
|
-
)
|
|
599
|
-
|
|
600
|
-
def _create_minimal_renderer(self) -> RichStreamRenderer:
|
|
601
|
-
"""Create a minimal renderer with basic output."""
|
|
602
|
-
minimal_config = RendererConfig(
|
|
603
|
-
live=False,
|
|
604
|
-
persist_live=False,
|
|
605
|
-
show_delegate_tool_panels=False,
|
|
606
|
-
render_thinking=False,
|
|
607
|
-
)
|
|
608
|
-
return RichStreamRenderer(
|
|
609
|
-
console=_Console(),
|
|
610
|
-
cfg=minimal_config,
|
|
611
|
-
verbose=False,
|
|
612
|
-
)
|
|
613
|
-
|
|
614
|
-
def _create_verbose_renderer(self) -> RichStreamRenderer:
|
|
615
|
-
"""Create a verbose renderer for detailed output."""
|
|
616
|
-
verbose_config = RendererConfig(
|
|
617
|
-
theme="dark",
|
|
618
|
-
style="debug",
|
|
619
|
-
live=False,
|
|
620
|
-
show_delegate_tool_panels=True,
|
|
621
|
-
append_finished_snapshots=False,
|
|
622
|
-
)
|
|
623
|
-
return RichStreamRenderer(
|
|
624
|
-
console=_Console(),
|
|
625
|
-
cfg=verbose_config,
|
|
626
|
-
verbose=True,
|
|
627
|
-
)
|
|
628
|
-
|
|
629
|
-
def _create_default_renderer(self, verbose: bool) -> RichStreamRenderer:
|
|
630
|
-
"""Create the default renderer."""
|
|
631
|
-
if verbose:
|
|
632
|
-
return self._create_verbose_renderer()
|
|
633
|
-
else:
|
|
634
|
-
default_config = RendererConfig(show_delegate_tool_panels=True)
|
|
635
|
-
return RichStreamRenderer(console=_Console(), cfg=default_config)
|
|
636
|
-
|
|
637
|
-
def _initialize_stream_metadata(self, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
638
|
-
"""Initialize stream metadata."""
|
|
639
|
-
return {
|
|
640
|
-
"agent_name": kwargs.get("agent_name", ""),
|
|
641
|
-
"model": kwargs.get("model"),
|
|
642
|
-
"run_id": None,
|
|
643
|
-
"input_message": "", # Will be set from kwargs if available
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
def _capture_request_id(
|
|
647
|
-
self,
|
|
648
|
-
stream_response: httpx.Response,
|
|
649
|
-
meta: dict[str, Any],
|
|
650
|
-
renderer: RichStreamRenderer,
|
|
651
|
-
) -> None:
|
|
652
|
-
"""Capture request ID from response headers."""
|
|
653
|
-
req_id = stream_response.headers.get(
|
|
654
|
-
"x-request-id"
|
|
655
|
-
) or stream_response.headers.get("x-run-id")
|
|
656
|
-
if req_id:
|
|
657
|
-
meta["run_id"] = req_id
|
|
658
|
-
renderer.on_start(meta)
|
|
659
|
-
|
|
660
|
-
def _should_start_timer(self, ev: dict[str, Any]) -> bool:
|
|
661
|
-
"""Check if timer should be started for this event."""
|
|
662
|
-
return "content" in ev or "status" in ev or ev.get("metadata")
|
|
663
|
-
|
|
664
|
-
def _handle_content_event(self, ev: dict[str, Any], final_text: str) -> str:
|
|
665
|
-
"""Handle content events."""
|
|
666
|
-
content = ev.get("content", "")
|
|
667
|
-
if not content.startswith("Artifact received:"):
|
|
668
|
-
return content
|
|
669
|
-
return final_text
|
|
670
|
-
|
|
671
|
-
def _handle_usage_event(
|
|
672
|
-
self, ev: dict[str, Any], stats_usage: dict[str, Any]
|
|
673
|
-
) -> None:
|
|
674
|
-
"""Handle usage events."""
|
|
675
|
-
stats_usage.update(ev.get("usage") or {})
|
|
676
|
-
|
|
677
|
-
def _handle_run_info_event(
|
|
678
|
-
self, ev: dict[str, Any], meta: dict[str, Any], renderer: RichStreamRenderer
|
|
679
|
-
) -> None:
|
|
680
|
-
"""Handle run info events."""
|
|
681
|
-
if ev.get("model"):
|
|
682
|
-
meta["model"] = ev["model"]
|
|
683
|
-
renderer.on_start(meta)
|
|
684
|
-
if ev.get("run_id"):
|
|
685
|
-
meta["run_id"] = ev["run_id"]
|
|
686
|
-
renderer.on_start(meta)
|
|
687
|
-
|
|
688
|
-
def _process_single_event(
|
|
689
|
-
self,
|
|
690
|
-
event: dict[str, Any],
|
|
691
|
-
renderer: RichStreamRenderer,
|
|
692
|
-
final_text: str,
|
|
693
|
-
stats_usage: dict[str, Any],
|
|
694
|
-
meta: dict[str, Any],
|
|
695
|
-
) -> tuple[str, dict[str, Any]]:
|
|
696
|
-
"""Process a single streaming event."""
|
|
697
|
-
try:
|
|
698
|
-
ev = json.loads(event["data"])
|
|
699
|
-
except json.JSONDecodeError:
|
|
700
|
-
logger.debug("Non-JSON SSE fragment skipped")
|
|
701
|
-
return final_text, stats_usage
|
|
702
|
-
|
|
703
|
-
kind = (ev.get("metadata") or {}).get("kind")
|
|
704
|
-
renderer.on_event(ev)
|
|
705
|
-
|
|
706
|
-
# Skip artifacts from content accumulation
|
|
707
|
-
if kind == "artifact":
|
|
708
|
-
return final_text, stats_usage
|
|
709
|
-
|
|
710
|
-
# Handle different event types
|
|
711
|
-
if kind == "final_response" and ev.get("content"):
|
|
712
|
-
final_text = ev.get("content", "")
|
|
713
|
-
elif ev.get("content"):
|
|
714
|
-
final_text = self._handle_content_event(ev, final_text)
|
|
715
|
-
elif kind == "usage":
|
|
716
|
-
self._handle_usage_event(ev, stats_usage)
|
|
717
|
-
elif kind == "run_info":
|
|
718
|
-
self._handle_run_info_event(ev, meta, renderer)
|
|
719
|
-
|
|
720
|
-
return final_text, stats_usage
|
|
721
|
-
|
|
722
|
-
def _process_stream_events(
|
|
723
|
-
self,
|
|
724
|
-
stream_response: httpx.Response,
|
|
725
|
-
renderer: RichStreamRenderer,
|
|
726
|
-
timeout_seconds: float,
|
|
727
|
-
agent_name: str | None,
|
|
728
|
-
kwargs: dict[str, Any],
|
|
729
|
-
) -> tuple[str, dict[str, Any], float | None, float | None]:
|
|
730
|
-
"""Process streaming events and accumulate response."""
|
|
731
|
-
final_text = ""
|
|
732
|
-
stats_usage = {}
|
|
733
|
-
started_monotonic = None
|
|
734
|
-
finished_monotonic = None
|
|
735
|
-
|
|
736
|
-
meta = self._initialize_stream_metadata(kwargs)
|
|
737
|
-
self._capture_request_id(stream_response, meta, renderer)
|
|
738
|
-
|
|
739
|
-
for event in iter_sse_events(stream_response, timeout_seconds, agent_name):
|
|
740
|
-
# Start timer at first meaningful event
|
|
741
|
-
if started_monotonic is None:
|
|
742
|
-
try:
|
|
743
|
-
ev = json.loads(event["data"])
|
|
744
|
-
if self._should_start_timer(ev):
|
|
745
|
-
started_monotonic = monotonic()
|
|
746
|
-
except json.JSONDecodeError:
|
|
747
|
-
pass
|
|
748
|
-
|
|
749
|
-
final_text, stats_usage = self._process_single_event(
|
|
750
|
-
event, renderer, final_text, stats_usage, meta
|
|
751
|
-
)
|
|
752
|
-
|
|
753
|
-
finished_monotonic = monotonic()
|
|
754
|
-
return final_text, stats_usage, started_monotonic, finished_monotonic
|
|
755
|
-
|
|
756
408
|
def run_agent(
|
|
757
409
|
self,
|
|
758
410
|
agent_id: str,
|
|
@@ -764,7 +416,6 @@ class AgentClient(BaseClient):
|
|
|
764
416
|
**kwargs,
|
|
765
417
|
) -> str:
|
|
766
418
|
"""Run an agent with a message, streaming via a renderer."""
|
|
767
|
-
# Prepare request payload and headers
|
|
768
419
|
(
|
|
769
420
|
payload,
|
|
770
421
|
data_payload,
|
|
@@ -773,20 +424,18 @@ class AgentClient(BaseClient):
|
|
|
773
424
|
multipart_data,
|
|
774
425
|
) = self._prepare_sync_request_data(message, files, tty, **kwargs)
|
|
775
426
|
|
|
776
|
-
|
|
777
|
-
|
|
427
|
+
render_manager = self._get_renderer_manager()
|
|
428
|
+
verbose = kwargs.get("verbose", False)
|
|
429
|
+
r = self._create_renderer(renderer, verbose=verbose)
|
|
430
|
+
meta = render_manager.build_initial_metadata(agent_id, message, kwargs)
|
|
431
|
+
render_manager.start_renderer(r, meta)
|
|
778
432
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
"run_id": None,
|
|
784
|
-
"input_message": message,
|
|
785
|
-
}
|
|
786
|
-
r.on_start(meta)
|
|
433
|
+
final_text = ""
|
|
434
|
+
stats_usage: dict[str, Any] = {}
|
|
435
|
+
started_monotonic: float | None = None
|
|
436
|
+
finished_monotonic: float | None = None
|
|
787
437
|
|
|
788
438
|
try:
|
|
789
|
-
# Make streaming request
|
|
790
439
|
response = self.http_client.stream(
|
|
791
440
|
"POST",
|
|
792
441
|
f"/agents/{agent_id}/run",
|
|
@@ -799,8 +448,7 @@ class AgentClient(BaseClient):
|
|
|
799
448
|
with response as stream_response:
|
|
800
449
|
stream_response.raise_for_status()
|
|
801
450
|
|
|
802
|
-
|
|
803
|
-
timeout_seconds = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
451
|
+
timeout_seconds = compute_timeout_seconds(kwargs)
|
|
804
452
|
agent_name = kwargs.get("agent_name")
|
|
805
453
|
|
|
806
454
|
(
|
|
@@ -809,7 +457,11 @@ class AgentClient(BaseClient):
|
|
|
809
457
|
started_monotonic,
|
|
810
458
|
finished_monotonic,
|
|
811
459
|
) = self._process_stream_events(
|
|
812
|
-
stream_response,
|
|
460
|
+
stream_response,
|
|
461
|
+
r,
|
|
462
|
+
timeout_seconds,
|
|
463
|
+
agent_name,
|
|
464
|
+
meta,
|
|
813
465
|
)
|
|
814
466
|
|
|
815
467
|
except KeyboardInterrupt:
|
|
@@ -823,25 +475,16 @@ class AgentClient(BaseClient):
|
|
|
823
475
|
finally:
|
|
824
476
|
raise
|
|
825
477
|
finally:
|
|
826
|
-
# Ensure cleanup
|
|
827
478
|
if multipart_data:
|
|
828
479
|
multipart_data.close()
|
|
829
480
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
if hasattr(r, "state") and hasattr(r.state, "buffer"):
|
|
838
|
-
rendered_text = "".join(r.state.buffer)
|
|
839
|
-
else:
|
|
840
|
-
rendered_text = ""
|
|
841
|
-
|
|
842
|
-
final_payload = final_text or rendered_text or "No response content received."
|
|
843
|
-
r.on_complete(st)
|
|
844
|
-
return final_payload
|
|
481
|
+
return self._finalize_renderer(
|
|
482
|
+
r,
|
|
483
|
+
final_text,
|
|
484
|
+
stats_usage,
|
|
485
|
+
started_monotonic,
|
|
486
|
+
finished_monotonic,
|
|
487
|
+
)
|
|
845
488
|
|
|
846
489
|
def _prepare_request_data(
|
|
847
490
|
self,
|