glaip-sdk 0.0.4__py3-none-any.whl → 0.0.5b1__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 -5
- glaip_sdk/branding.py +18 -17
- glaip_sdk/cli/__init__.py +1 -1
- glaip_sdk/cli/agent_config.py +82 -0
- glaip_sdk/cli/commands/__init__.py +3 -3
- glaip_sdk/cli/commands/agents.py +570 -673
- glaip_sdk/cli/commands/configure.py +2 -2
- glaip_sdk/cli/commands/mcps.py +148 -143
- glaip_sdk/cli/commands/models.py +1 -1
- glaip_sdk/cli/commands/tools.py +250 -179
- glaip_sdk/cli/display.py +244 -0
- glaip_sdk/cli/io.py +106 -0
- glaip_sdk/cli/main.py +14 -18
- glaip_sdk/cli/resolution.py +59 -0
- glaip_sdk/cli/utils.py +305 -264
- glaip_sdk/cli/validators.py +235 -0
- glaip_sdk/client/__init__.py +3 -224
- glaip_sdk/client/agents.py +631 -191
- glaip_sdk/client/base.py +66 -4
- glaip_sdk/client/main.py +226 -0
- glaip_sdk/client/mcps.py +143 -18
- glaip_sdk/client/tools.py +146 -11
- glaip_sdk/config/constants.py +10 -1
- glaip_sdk/models.py +42 -2
- glaip_sdk/rich_components.py +29 -0
- glaip_sdk/utils/__init__.py +18 -171
- glaip_sdk/utils/agent_config.py +181 -0
- glaip_sdk/utils/client_utils.py +159 -79
- glaip_sdk/utils/display.py +100 -0
- glaip_sdk/utils/general.py +94 -0
- glaip_sdk/utils/import_export.py +140 -0
- glaip_sdk/utils/rendering/formatting.py +6 -1
- glaip_sdk/utils/rendering/renderer/__init__.py +67 -8
- glaip_sdk/utils/rendering/renderer/base.py +340 -247
- glaip_sdk/utils/rendering/renderer/debug.py +3 -2
- glaip_sdk/utils/rendering/renderer/panels.py +11 -10
- glaip_sdk/utils/rendering/steps.py +1 -1
- glaip_sdk/utils/resource_refs.py +192 -0
- glaip_sdk/utils/rich_utils.py +29 -0
- glaip_sdk/utils/serialization.py +285 -0
- glaip_sdk/utils/validation.py +273 -0
- {glaip_sdk-0.0.4.dist-info → glaip_sdk-0.0.5b1.dist-info}/METADATA +22 -21
- glaip_sdk-0.0.5b1.dist-info/RECORD +55 -0
- {glaip_sdk-0.0.4.dist-info → glaip_sdk-0.0.5b1.dist-info}/WHEEL +1 -1
- glaip_sdk-0.0.5b1.dist-info/entry_points.txt +3 -0
- glaip_sdk/cli/commands/init.py +0 -93
- glaip_sdk-0.0.4.dist-info/RECORD +0 -41
- glaip_sdk-0.0.4.dist-info/entry_points.txt +0 -2
glaip_sdk/client/tools.py
CHANGED
|
@@ -8,14 +8,25 @@ Authors:
|
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
10
10
|
import tempfile
|
|
11
|
+
from typing import Any
|
|
11
12
|
|
|
12
13
|
from glaip_sdk.client.base import BaseClient
|
|
14
|
+
from glaip_sdk.config.constants import (
|
|
15
|
+
DEFAULT_TOOL_FRAMEWORK,
|
|
16
|
+
DEFAULT_TOOL_TYPE,
|
|
17
|
+
DEFAULT_TOOL_VERSION,
|
|
18
|
+
)
|
|
13
19
|
from glaip_sdk.models import Tool
|
|
14
20
|
from glaip_sdk.utils.client_utils import (
|
|
15
21
|
create_model_instances,
|
|
16
22
|
find_by_name,
|
|
17
23
|
)
|
|
18
24
|
|
|
25
|
+
# API endpoints
|
|
26
|
+
TOOLS_ENDPOINT = "/tools/"
|
|
27
|
+
TOOLS_UPLOAD_ENDPOINT = "/tools/upload"
|
|
28
|
+
TOOLS_UPLOAD_BY_ID_ENDPOINT_FMT = "/tools/{tool_id}/upload"
|
|
29
|
+
|
|
19
30
|
# Set up module-level logger
|
|
20
31
|
logger = logging.getLogger("glaip_sdk.tools")
|
|
21
32
|
|
|
@@ -32,20 +43,27 @@ class ToolClient(BaseClient):
|
|
|
32
43
|
"""
|
|
33
44
|
super().__init__(parent_client=parent_client, **kwargs)
|
|
34
45
|
|
|
35
|
-
def list_tools(self) -> list[Tool]:
|
|
36
|
-
"""List all tools.
|
|
37
|
-
|
|
46
|
+
def list_tools(self, tool_type: str | None = None) -> list[Tool]:
|
|
47
|
+
"""List all tools, optionally filtered by type.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
tool_type: Filter tools by type (e.g., "custom", "native")
|
|
51
|
+
"""
|
|
52
|
+
endpoint = TOOLS_ENDPOINT
|
|
53
|
+
if tool_type:
|
|
54
|
+
endpoint += f"?type={tool_type}"
|
|
55
|
+
data = self._request("GET", endpoint)
|
|
38
56
|
return create_model_instances(data, Tool, self)
|
|
39
57
|
|
|
40
58
|
def get_tool_by_id(self, tool_id: str) -> Tool:
|
|
41
59
|
"""Get tool by ID."""
|
|
42
|
-
data = self._request("GET", f"
|
|
60
|
+
data = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
43
61
|
return Tool(**data)._set_client(self)
|
|
44
62
|
|
|
45
63
|
def find_tools(self, name: str | None = None) -> list[Tool]:
|
|
46
64
|
"""Find tools by name."""
|
|
47
65
|
# Backend doesn't support name query parameter, so we fetch all and filter client-side
|
|
48
|
-
data = self._request("GET",
|
|
66
|
+
data = self._request("GET", TOOLS_ENDPOINT)
|
|
49
67
|
tools = create_model_instances(data, Tool, self)
|
|
50
68
|
return find_by_name(tools, name, case_sensitive=False)
|
|
51
69
|
|
|
@@ -101,7 +119,7 @@ class ToolClient(BaseClient):
|
|
|
101
119
|
data["description"] = description
|
|
102
120
|
|
|
103
121
|
# Handle tags if provided in kwargs
|
|
104
|
-
if
|
|
122
|
+
if kwargs.get("tags"):
|
|
105
123
|
if isinstance(kwargs["tags"], list):
|
|
106
124
|
data["tags"] = ",".join(kwargs["tags"])
|
|
107
125
|
else:
|
|
@@ -131,13 +149,130 @@ class ToolClient(BaseClient):
|
|
|
131
149
|
|
|
132
150
|
response = self._request(
|
|
133
151
|
"POST",
|
|
134
|
-
|
|
152
|
+
TOOLS_UPLOAD_ENDPOINT,
|
|
135
153
|
files=files,
|
|
136
154
|
data=upload_data,
|
|
137
155
|
)
|
|
138
156
|
|
|
139
157
|
return Tool(**response)._set_client(self)
|
|
140
158
|
|
|
159
|
+
def _build_create_payload(
|
|
160
|
+
self,
|
|
161
|
+
name: str,
|
|
162
|
+
description: str | None = None,
|
|
163
|
+
framework: str = DEFAULT_TOOL_FRAMEWORK,
|
|
164
|
+
tool_type: str = DEFAULT_TOOL_TYPE,
|
|
165
|
+
**kwargs,
|
|
166
|
+
) -> dict[str, Any]:
|
|
167
|
+
"""Build payload for tool creation with proper metadata handling.
|
|
168
|
+
|
|
169
|
+
CENTRALIZED PAYLOAD BUILDING LOGIC:
|
|
170
|
+
- Handles file vs metadata-only tool creation
|
|
171
|
+
- Sets proper defaults and required fields
|
|
172
|
+
- Processes tags and other metadata consistently
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
name: Tool name
|
|
176
|
+
description: Tool description
|
|
177
|
+
framework: Tool framework (defaults to langchain)
|
|
178
|
+
tool_type: Tool type (defaults to custom)
|
|
179
|
+
**kwargs: Additional parameters (tags, version, etc.)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Complete payload dictionary for tool creation
|
|
183
|
+
"""
|
|
184
|
+
# Prepare the creation payload with required fields
|
|
185
|
+
payload: dict[str, any] = {
|
|
186
|
+
"name": name.strip(),
|
|
187
|
+
"type": tool_type,
|
|
188
|
+
"framework": framework,
|
|
189
|
+
"version": kwargs.get("version", DEFAULT_TOOL_VERSION),
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Add description if provided
|
|
193
|
+
if description:
|
|
194
|
+
payload["description"] = description.strip()
|
|
195
|
+
|
|
196
|
+
# Handle tags - convert list to comma-separated string for API
|
|
197
|
+
if kwargs.get("tags"):
|
|
198
|
+
if isinstance(kwargs["tags"], list):
|
|
199
|
+
payload["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
|
|
200
|
+
else:
|
|
201
|
+
payload["tags"] = str(kwargs["tags"])
|
|
202
|
+
|
|
203
|
+
# Add any other kwargs (excluding already handled ones)
|
|
204
|
+
excluded_keys = {"tags", "version"}
|
|
205
|
+
for key, value in kwargs.items():
|
|
206
|
+
if key not in excluded_keys:
|
|
207
|
+
payload[key] = value
|
|
208
|
+
|
|
209
|
+
return payload
|
|
210
|
+
|
|
211
|
+
def _build_update_payload(
|
|
212
|
+
self,
|
|
213
|
+
current_tool: Tool,
|
|
214
|
+
name: str | None = None,
|
|
215
|
+
description: str | None = None,
|
|
216
|
+
**kwargs,
|
|
217
|
+
) -> dict[str, Any]:
|
|
218
|
+
"""Build payload for tool update with proper current state preservation.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
current_tool: Current tool object to update
|
|
222
|
+
name: New tool name (None to keep current)
|
|
223
|
+
description: New description (None to keep current)
|
|
224
|
+
**kwargs: Additional parameters (tags, framework, etc.)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Complete payload dictionary for tool update
|
|
228
|
+
|
|
229
|
+
Notes:
|
|
230
|
+
- Preserves current values as defaults when new values not provided
|
|
231
|
+
- Handles metadata updates properly
|
|
232
|
+
"""
|
|
233
|
+
# Prepare the update payload with current values as defaults
|
|
234
|
+
update_data = {
|
|
235
|
+
"name": name if name is not None else current_tool.name,
|
|
236
|
+
"type": DEFAULT_TOOL_TYPE, # Required by backend
|
|
237
|
+
"framework": kwargs.get(
|
|
238
|
+
"framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)
|
|
239
|
+
),
|
|
240
|
+
"version": kwargs.get(
|
|
241
|
+
"version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)
|
|
242
|
+
),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# Handle description with proper None handling
|
|
246
|
+
if description is not None:
|
|
247
|
+
update_data["description"] = description.strip()
|
|
248
|
+
elif hasattr(current_tool, "description") and current_tool.description:
|
|
249
|
+
update_data["description"] = current_tool.description
|
|
250
|
+
|
|
251
|
+
# Handle tags - convert list to comma-separated string for API
|
|
252
|
+
if kwargs.get("tags"):
|
|
253
|
+
if isinstance(kwargs["tags"], list):
|
|
254
|
+
update_data["tags"] = ",".join(
|
|
255
|
+
str(tag).strip() for tag in kwargs["tags"]
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
update_data["tags"] = str(kwargs["tags"])
|
|
259
|
+
elif hasattr(current_tool, "tags") and current_tool.tags:
|
|
260
|
+
# Preserve existing tags if present
|
|
261
|
+
if isinstance(current_tool.tags, list):
|
|
262
|
+
update_data["tags"] = ",".join(
|
|
263
|
+
str(tag).strip() for tag in current_tool.tags
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
update_data["tags"] = str(current_tool.tags)
|
|
267
|
+
|
|
268
|
+
# Add any other kwargs (excluding already handled ones)
|
|
269
|
+
excluded_keys = {"tags", "framework", "version"}
|
|
270
|
+
for key, value in kwargs.items():
|
|
271
|
+
if key not in excluded_keys:
|
|
272
|
+
update_data[key] = value
|
|
273
|
+
|
|
274
|
+
return update_data
|
|
275
|
+
|
|
141
276
|
def _create_tool_from_file(
|
|
142
277
|
self,
|
|
143
278
|
file_path: str,
|
|
@@ -279,12 +414,12 @@ class ToolClient(BaseClient):
|
|
|
279
414
|
|
|
280
415
|
def update_tool(self, tool_id: str, **kwargs) -> Tool:
|
|
281
416
|
"""Update an existing tool."""
|
|
282
|
-
data = self._request("PUT", f"
|
|
417
|
+
data = self._request("PUT", f"{TOOLS_ENDPOINT}{tool_id}", json=kwargs)
|
|
283
418
|
return Tool(**data)._set_client(self)
|
|
284
419
|
|
|
285
420
|
def delete_tool(self, tool_id: str) -> None:
|
|
286
421
|
"""Delete a tool."""
|
|
287
|
-
self._request("DELETE", f"
|
|
422
|
+
self._request("DELETE", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
288
423
|
|
|
289
424
|
def get_tool_script(self, tool_id: str) -> str:
|
|
290
425
|
"""Get the tool script content.
|
|
@@ -299,7 +434,7 @@ class ToolClient(BaseClient):
|
|
|
299
434
|
Exception: If the tool script cannot be retrieved
|
|
300
435
|
"""
|
|
301
436
|
try:
|
|
302
|
-
response = self._request("GET", f"
|
|
437
|
+
response = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}/script")
|
|
303
438
|
return response.get("script", "") or response.get("content", "")
|
|
304
439
|
except Exception as e:
|
|
305
440
|
logger.error(f"Failed to get tool script for {tool_id}: {e}")
|
|
@@ -336,7 +471,7 @@ class ToolClient(BaseClient):
|
|
|
336
471
|
|
|
337
472
|
response = self._request(
|
|
338
473
|
"PUT",
|
|
339
|
-
|
|
474
|
+
TOOLS_UPLOAD_BY_ID_ENDPOINT_FMT.format(tool_id=tool_id),
|
|
340
475
|
files=files,
|
|
341
476
|
data=kwargs, # Pass kwargs directly as data
|
|
342
477
|
)
|
glaip_sdk/config/constants.py
CHANGED
|
@@ -10,7 +10,7 @@ DEFAULT_MODEL_PROVIDER = "openai"
|
|
|
10
10
|
|
|
11
11
|
# Default timeout values
|
|
12
12
|
DEFAULT_TIMEOUT = 30.0
|
|
13
|
-
DEFAULT_AGENT_RUN_TIMEOUT = 300
|
|
13
|
+
DEFAULT_AGENT_RUN_TIMEOUT = 300
|
|
14
14
|
|
|
15
15
|
# User agent and version
|
|
16
16
|
|
|
@@ -33,3 +33,12 @@ DEFAULT_AGENT_TYPE = "config"
|
|
|
33
33
|
DEFAULT_AGENT_FRAMEWORK = "langchain"
|
|
34
34
|
DEFAULT_AGENT_VERSION = "1.0"
|
|
35
35
|
DEFAULT_AGENT_PROVIDER = "openai"
|
|
36
|
+
|
|
37
|
+
# Tool creation/update constants
|
|
38
|
+
DEFAULT_TOOL_TYPE = "custom"
|
|
39
|
+
DEFAULT_TOOL_FRAMEWORK = "langchain"
|
|
40
|
+
DEFAULT_TOOL_VERSION = "1.0"
|
|
41
|
+
|
|
42
|
+
# MCP creation/update constants
|
|
43
|
+
DEFAULT_MCP_TYPE = "server"
|
|
44
|
+
DEFAULT_MCP_TRANSPORT = "stdio"
|
glaip_sdk/models.py
CHANGED
|
@@ -5,6 +5,7 @@ Authors:
|
|
|
5
5
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from collections.abc import AsyncGenerator
|
|
8
9
|
from datetime import datetime
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
@@ -60,6 +61,31 @@ class Agent(BaseModel):
|
|
|
60
61
|
# Pass verbose flag through to enable event JSON output
|
|
61
62
|
return self._client.run_agent(self.id, message, verbose=verbose, **kwargs)
|
|
62
63
|
|
|
64
|
+
async def arun(self, message: str, **kwargs) -> AsyncGenerator[dict, None]:
|
|
65
|
+
"""Async run the agent with a message, yielding streaming JSON chunks.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
message: The message to send to the agent
|
|
69
|
+
**kwargs: Additional arguments passed to arun_agent
|
|
70
|
+
|
|
71
|
+
Yields:
|
|
72
|
+
Dictionary containing parsed JSON chunks from the streaming response
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
RuntimeError: When no client is available
|
|
76
|
+
AgentTimeoutError: When agent execution times out
|
|
77
|
+
Exception: For other unexpected errors
|
|
78
|
+
"""
|
|
79
|
+
if not self._client:
|
|
80
|
+
raise RuntimeError(
|
|
81
|
+
"No client available. Use client.get_agent_by_id() to get a client-connected agent."
|
|
82
|
+
)
|
|
83
|
+
# Automatically pass the agent name for better context
|
|
84
|
+
kwargs.setdefault("agent_name", self.name)
|
|
85
|
+
|
|
86
|
+
async for chunk in self._client.arun_agent(self.id, message, **kwargs):
|
|
87
|
+
yield chunk
|
|
88
|
+
|
|
63
89
|
def update(self, **kwargs) -> "Agent":
|
|
64
90
|
"""Update agent attributes."""
|
|
65
91
|
if not self._client:
|
|
@@ -110,12 +136,26 @@ class Tool(BaseModel):
|
|
|
110
136
|
return "No script content available"
|
|
111
137
|
|
|
112
138
|
def update(self, **kwargs) -> "Tool":
|
|
113
|
-
"""Update tool attributes.
|
|
139
|
+
"""Update tool attributes.
|
|
140
|
+
|
|
141
|
+
Supports both metadata updates and file uploads.
|
|
142
|
+
Pass 'file' parameter to update tool code via file upload.
|
|
143
|
+
"""
|
|
114
144
|
if not self._client:
|
|
115
145
|
raise RuntimeError(
|
|
116
146
|
"No client available. Use client.get_tool_by_id() to get a client-connected tool."
|
|
117
147
|
)
|
|
118
|
-
|
|
148
|
+
|
|
149
|
+
# Check if file upload is requested
|
|
150
|
+
if "file" in kwargs:
|
|
151
|
+
file_path = kwargs.pop("file") # Remove file from kwargs for metadata
|
|
152
|
+
updated_tool = self._client.tools.update_tool_via_file(
|
|
153
|
+
self.id, file_path, **kwargs
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
# Regular metadata update
|
|
157
|
+
updated_tool = self._client.tools.update_tool(self.id, **kwargs)
|
|
158
|
+
|
|
119
159
|
# Update current instance with new data
|
|
120
160
|
for key, value in updated_tool.model_dump().items():
|
|
121
161
|
if hasattr(self, key):
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Custom Rich components with copy-friendly defaults."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from rich import box
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AIPPanel(Panel):
|
|
11
|
+
"""Rich Panel configured without vertical borders by default."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args, **kwargs):
|
|
14
|
+
kwargs.setdefault("box", box.HORIZONTALS)
|
|
15
|
+
kwargs.setdefault("padding", (0, 1))
|
|
16
|
+
super().__init__(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AIPTable(Table):
|
|
20
|
+
"""Rich Table configured without vertical borders by default."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, *args, **kwargs):
|
|
23
|
+
kwargs.setdefault("box", box.HORIZONTALS)
|
|
24
|
+
kwargs.setdefault("show_edge", False)
|
|
25
|
+
kwargs.setdefault("pad_edge", False)
|
|
26
|
+
super().__init__(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = ["AIPPanel", "AIPTable"]
|
glaip_sdk/utils/__init__.py
CHANGED
|
@@ -4,177 +4,23 @@ Authors:
|
|
|
4
4
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from .
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
except ImportError:
|
|
25
|
-
RICH_AVAILABLE = False
|
|
26
|
-
|
|
27
|
-
# Functions are defined below in this file
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def is_uuid(value: str) -> bool:
|
|
31
|
-
"""Check if a string is a valid UUID.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
value: String to check
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
True if value is a valid UUID, False otherwise
|
|
38
|
-
"""
|
|
39
|
-
try:
|
|
40
|
-
UUID(value)
|
|
41
|
-
return True
|
|
42
|
-
except (ValueError, TypeError):
|
|
43
|
-
return False
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def sanitize_name(name: str) -> str:
|
|
47
|
-
"""Sanitize a name for resource creation.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
name: Raw name input
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
Sanitized name suitable for resource creation
|
|
54
|
-
"""
|
|
55
|
-
# Remove special characters and normalize
|
|
56
|
-
sanitized = re.sub(r"[^a-zA-Z0-9\-_]", "-", name.strip())
|
|
57
|
-
sanitized = re.sub(r"-+", "-", sanitized) # Collapse multiple dashes
|
|
58
|
-
return sanitized.lower().strip("-")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def format_file_size(size_bytes: int) -> str:
|
|
62
|
-
"""Format file size in human readable format.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
size_bytes: Size in bytes
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
Human readable size string (e.g., "1.5 MB")
|
|
69
|
-
"""
|
|
70
|
-
for unit in ["B", "KB", "MB", "GB"]:
|
|
71
|
-
if size_bytes < 1024.0:
|
|
72
|
-
return f"{size_bytes:.1f} {unit}"
|
|
73
|
-
size_bytes /= 1024.0
|
|
74
|
-
return f"{size_bytes:.1f} TB"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def progress_bar(iterable, description: str = "Processing"):
|
|
78
|
-
"""Simple progress bar using click.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
iterable: Iterable to process
|
|
82
|
-
description: Progress description
|
|
83
|
-
|
|
84
|
-
Yields:
|
|
85
|
-
Items from iterable with progress display
|
|
86
|
-
"""
|
|
87
|
-
try:
|
|
88
|
-
with click.progressbar(iterable, label=description) as bar:
|
|
89
|
-
for item in bar:
|
|
90
|
-
yield item
|
|
91
|
-
except ImportError:
|
|
92
|
-
# Fallback if click not available
|
|
93
|
-
for item in iterable:
|
|
94
|
-
yield item
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Rich display utilities for enhanced output
|
|
98
|
-
# RICH_AVAILABLE is determined by the try/except block above
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def print_agent_output(output: str, title: str = "Agent Output") -> None:
|
|
102
|
-
"""Print agent output with rich formatting.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
output: The agent's response text
|
|
106
|
-
title: Title for the output panel
|
|
107
|
-
"""
|
|
108
|
-
if RICH_AVAILABLE:
|
|
109
|
-
console = Console()
|
|
110
|
-
panel = Panel(
|
|
111
|
-
Text(output, style="green"),
|
|
112
|
-
title=title,
|
|
113
|
-
border_style="green",
|
|
114
|
-
box=box.ROUNDED,
|
|
115
|
-
)
|
|
116
|
-
console.print(panel)
|
|
117
|
-
else:
|
|
118
|
-
print(f"\n=== {title} ===")
|
|
119
|
-
print(output)
|
|
120
|
-
print("=" * (len(title) + 8))
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def print_agent_created(agent, title: str = "🤖 Agent Created") -> None:
|
|
124
|
-
"""Print agent creation success with rich formatting.
|
|
125
|
-
|
|
126
|
-
Args:
|
|
127
|
-
agent: The created agent object
|
|
128
|
-
title: Title for the output panel
|
|
129
|
-
"""
|
|
130
|
-
if RICH_AVAILABLE:
|
|
131
|
-
console = Console()
|
|
132
|
-
panel = Panel(
|
|
133
|
-
f"[green]✅ Agent '{agent.name}' created successfully![/green]\n\n"
|
|
134
|
-
f"ID: {agent.id}\n"
|
|
135
|
-
f"Model: {getattr(agent, 'model', 'N/A')}\n"
|
|
136
|
-
f"Type: {getattr(agent, 'type', 'config')}\n"
|
|
137
|
-
f"Framework: {getattr(agent, 'framework', 'langchain')}\n"
|
|
138
|
-
f"Version: {getattr(agent, 'version', '1.0')}",
|
|
139
|
-
title=title,
|
|
140
|
-
border_style="green",
|
|
141
|
-
box=box.ROUNDED,
|
|
142
|
-
)
|
|
143
|
-
console.print(panel)
|
|
144
|
-
else:
|
|
145
|
-
print(f"✅ Agent '{agent.name}' created successfully!")
|
|
146
|
-
print(f"ID: {agent.id}")
|
|
147
|
-
print(f"Model: {getattr(agent, 'model', 'N/A')}")
|
|
148
|
-
print(f"Type: {getattr(agent, 'type', 'config')}")
|
|
149
|
-
print(f"Framework: {getattr(agent, 'framework', 'langchain')}")
|
|
150
|
-
print(f"Version: {getattr(agent, 'version', '1.0')}")
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def print_agent_updated(agent) -> None:
|
|
154
|
-
"""Print agent update success with rich formatting.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
agent: The updated agent object
|
|
158
|
-
"""
|
|
159
|
-
if RICH_AVAILABLE:
|
|
160
|
-
console = Console()
|
|
161
|
-
console.print(f"[green]✅ Agent '{agent.name}' updated successfully[/green]")
|
|
162
|
-
else:
|
|
163
|
-
print(f"✅ Agent '{agent.name}' updated successfully")
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def print_agent_deleted(agent_id: str) -> None:
|
|
167
|
-
"""Print agent deletion success with rich formatting.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
agent_id: The deleted agent's ID
|
|
171
|
-
"""
|
|
172
|
-
if RICH_AVAILABLE:
|
|
173
|
-
console = Console()
|
|
174
|
-
console.print(f"[green]✅ Agent deleted successfully (ID: {agent_id})[/green]")
|
|
175
|
-
else:
|
|
176
|
-
print(f"✅ Agent deleted successfully (ID: {agent_id})")
|
|
177
|
-
|
|
7
|
+
from glaip_sdk.utils.display import (
|
|
8
|
+
print_agent_created,
|
|
9
|
+
print_agent_deleted,
|
|
10
|
+
print_agent_output,
|
|
11
|
+
print_agent_updated,
|
|
12
|
+
)
|
|
13
|
+
from glaip_sdk.utils.general import (
|
|
14
|
+
format_datetime,
|
|
15
|
+
format_file_size,
|
|
16
|
+
is_uuid,
|
|
17
|
+
progress_bar,
|
|
18
|
+
sanitize_name,
|
|
19
|
+
)
|
|
20
|
+
from glaip_sdk.utils.rendering.models import RunStats, Step
|
|
21
|
+
from glaip_sdk.utils.rendering.steps import StepManager
|
|
22
|
+
from glaip_sdk.utils.rich_utils import RICH_AVAILABLE
|
|
23
|
+
from glaip_sdk.utils.run_renderer import RichStreamRenderer
|
|
178
24
|
|
|
179
25
|
__all__ = [
|
|
180
26
|
"RichStreamRenderer",
|
|
@@ -185,6 +31,7 @@ __all__ = [
|
|
|
185
31
|
"is_uuid",
|
|
186
32
|
"sanitize_name",
|
|
187
33
|
"format_file_size",
|
|
34
|
+
"format_datetime",
|
|
188
35
|
"progress_bar",
|
|
189
36
|
"print_agent_output",
|
|
190
37
|
"print_agent_created",
|