glaip-sdk 0.0.20__py3-none-any.whl → 0.6.5b6__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 +10 -3
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1126 -0
- glaip_sdk/branding.py +15 -6
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +265 -45
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +270 -173
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +735 -143
- glaip_sdk/cli/commands/mcps.py +265 -134
- glaip_sdk/cli/commands/models.py +13 -9
- glaip_sdk/cli/commands/tools.py +67 -88
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/commands/update.py +3 -8
- glaip_sdk/cli/config.py +49 -7
- 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 +45 -32
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +14 -17
- glaip_sdk/cli/main.py +232 -143
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/mcp_validators.py +5 -15
- glaip_sdk/cli/pager.py +12 -19
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/parsers/json_input.py +11 -22
- glaip_sdk/cli/resolution.py +3 -9
- glaip_sdk/cli/rich_helpers.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +500 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +61 -28
- glaip_sdk/cli/slash/prompt.py +13 -10
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +772 -222
- 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 +872 -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 +258 -60
- glaip_sdk/cli/transcript/capture.py +72 -21
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +1 -3
- glaip_sdk/cli/transcript/viewer.py +77 -329
- glaip_sdk/cli/update_notifier.py +177 -24
- glaip_sdk/cli/utils.py +242 -1309
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +53 -37
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +320 -92
- glaip_sdk/client/base.py +78 -35
- glaip_sdk/client/main.py +19 -10
- glaip_sdk/client/mcps.py +123 -15
- glaip_sdk/client/run_rendering.py +218 -78
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +161 -34
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/icons.py +9 -3
- 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/payload_schemas/agent.py +1 -3
- 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 +231 -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 +597 -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 +158 -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 +177 -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/agent_config.py +4 -14
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +46 -28
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +25 -21
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +15 -16
- 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 +38 -23
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +18 -8
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +476 -882
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- glaip_sdk/utils/rendering/renderer/debug.py +30 -34
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +13 -54
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/toggle.py +182 -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/step_tree_state.py +100 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/{steps.py → steps/manager.py} +122 -26
- 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 +29 -26
- glaip_sdk/utils/runtime_config.py +422 -0
- glaip_sdk/utils/serialization.py +32 -46
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +20 -28
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/METADATA +49 -4
- glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -259
- glaip_sdk-0.0.20.dist-info/RECORD +0 -80
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/entry_points.txt +0 -0
glaip_sdk/client/tools.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
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
9
|
import logging
|
|
@@ -16,11 +17,14 @@ from glaip_sdk.config.constants import (
|
|
|
16
17
|
DEFAULT_TOOL_TYPE,
|
|
17
18
|
DEFAULT_TOOL_VERSION,
|
|
18
19
|
)
|
|
19
|
-
from glaip_sdk.models import
|
|
20
|
+
from glaip_sdk.models import ToolResponse
|
|
21
|
+
from glaip_sdk.tools import Tool
|
|
20
22
|
from glaip_sdk.utils.client_utils import (
|
|
23
|
+
add_kwargs_to_payload,
|
|
21
24
|
create_model_instances,
|
|
22
25
|
find_by_name,
|
|
23
26
|
)
|
|
27
|
+
from glaip_sdk.utils.resource_refs import is_uuid
|
|
24
28
|
|
|
25
29
|
# API endpoints
|
|
26
30
|
TOOLS_ENDPOINT = "/tools/"
|
|
@@ -58,11 +62,11 @@ class ToolClient(BaseClient):
|
|
|
58
62
|
def get_tool_by_id(self, tool_id: str) -> Tool:
|
|
59
63
|
"""Get tool by ID."""
|
|
60
64
|
data = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
61
|
-
|
|
65
|
+
response = ToolResponse(**data)
|
|
66
|
+
return Tool.from_response(response, client=self)
|
|
62
67
|
|
|
63
68
|
def find_tools(self, name: str | None = None) -> list[Tool]:
|
|
64
69
|
"""Find tools by name."""
|
|
65
|
-
# Backend doesn't support name query parameter, so we fetch all and filter client-side
|
|
66
70
|
data = self._request("GET", TOOLS_ENDPOINT)
|
|
67
71
|
tools = create_model_instances(data, Tool, self)
|
|
68
72
|
return find_by_name(tools, name, case_sensitive=False)
|
|
@@ -96,9 +100,7 @@ class ToolClient(BaseClient):
|
|
|
96
100
|
"""
|
|
97
101
|
return os.path.splitext(os.path.basename(file_path))[0]
|
|
98
102
|
|
|
99
|
-
def _prepare_upload_data(
|
|
100
|
-
self, name: str, framework: str, description: str | None = None, **kwargs
|
|
101
|
-
) -> dict:
|
|
103
|
+
def _prepare_upload_data(self, name: str, framework: str, description: str | None = None, **kwargs) -> dict:
|
|
102
104
|
"""Prepare upload data dictionary.
|
|
103
105
|
|
|
104
106
|
Args:
|
|
@@ -113,6 +115,7 @@ class ToolClient(BaseClient):
|
|
|
113
115
|
data = {
|
|
114
116
|
"name": name,
|
|
115
117
|
"framework": framework,
|
|
118
|
+
"type": kwargs.pop("tool_type", DEFAULT_TOOL_TYPE), # Default to custom
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
if description:
|
|
@@ -154,7 +157,8 @@ class ToolClient(BaseClient):
|
|
|
154
157
|
data=upload_data,
|
|
155
158
|
)
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
tool_response = ToolResponse(**response)
|
|
161
|
+
return Tool.from_response(tool_response, client=self)
|
|
158
162
|
|
|
159
163
|
def _build_create_payload(
|
|
160
164
|
self,
|
|
@@ -202,9 +206,7 @@ class ToolClient(BaseClient):
|
|
|
202
206
|
|
|
203
207
|
# Add any other kwargs (excluding already handled ones)
|
|
204
208
|
excluded_keys = {"tags", "version"}
|
|
205
|
-
|
|
206
|
-
if key not in excluded_keys:
|
|
207
|
-
payload[key] = value
|
|
209
|
+
add_kwargs_to_payload(payload, kwargs, excluded_keys)
|
|
208
210
|
|
|
209
211
|
return payload
|
|
210
212
|
|
|
@@ -217,29 +219,21 @@ class ToolClient(BaseClient):
|
|
|
217
219
|
elif hasattr(current_tool, "description") and current_tool.description:
|
|
218
220
|
update_data["description"] = current_tool.description
|
|
219
221
|
|
|
220
|
-
def _handle_tags_update(
|
|
221
|
-
self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool
|
|
222
|
-
) -> None:
|
|
222
|
+
def _handle_tags_update(self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool) -> None:
|
|
223
223
|
"""Handle tags field in update payload."""
|
|
224
224
|
if kwargs.get("tags"):
|
|
225
225
|
if isinstance(kwargs["tags"], list):
|
|
226
|
-
update_data["tags"] = ",".join(
|
|
227
|
-
str(tag).strip() for tag in kwargs["tags"]
|
|
228
|
-
)
|
|
226
|
+
update_data["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
|
|
229
227
|
else:
|
|
230
228
|
update_data["tags"] = str(kwargs["tags"])
|
|
231
229
|
elif hasattr(current_tool, "tags") and current_tool.tags:
|
|
232
230
|
# Preserve existing tags if present
|
|
233
231
|
if isinstance(current_tool.tags, list):
|
|
234
|
-
update_data["tags"] = ",".join(
|
|
235
|
-
str(tag).strip() for tag in current_tool.tags
|
|
236
|
-
)
|
|
232
|
+
update_data["tags"] = ",".join(str(tag).strip() for tag in current_tool.tags)
|
|
237
233
|
else:
|
|
238
234
|
update_data["tags"] = str(current_tool.tags)
|
|
239
235
|
|
|
240
|
-
def _handle_additional_kwargs(
|
|
241
|
-
self, update_data: dict[str, Any], kwargs: dict[str, Any]
|
|
242
|
-
) -> None:
|
|
236
|
+
def _handle_additional_kwargs(self, update_data: dict[str, Any], kwargs: dict[str, Any]) -> None:
|
|
243
237
|
"""Handle additional kwargs in update payload."""
|
|
244
238
|
excluded_keys = {
|
|
245
239
|
"tags",
|
|
@@ -286,16 +280,15 @@ class ToolClient(BaseClient):
|
|
|
286
280
|
or getattr(current_tool, "type", None)
|
|
287
281
|
or DEFAULT_TOOL_TYPE
|
|
288
282
|
)
|
|
283
|
+
# Convert enum to string value for API payload
|
|
284
|
+
if hasattr(current_type, "value"):
|
|
285
|
+
current_type = current_type.value
|
|
289
286
|
|
|
290
287
|
update_data = {
|
|
291
288
|
"name": name if name is not None else current_tool.name,
|
|
292
289
|
"type": current_type,
|
|
293
|
-
"framework": kwargs.get(
|
|
294
|
-
|
|
295
|
-
),
|
|
296
|
-
"version": kwargs.get(
|
|
297
|
-
"version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)
|
|
298
|
-
),
|
|
290
|
+
"framework": kwargs.get("framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)),
|
|
291
|
+
"version": kwargs.get("version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)),
|
|
299
292
|
}
|
|
300
293
|
|
|
301
294
|
# Handle description update
|
|
@@ -355,9 +348,7 @@ class ToolClient(BaseClient):
|
|
|
355
348
|
|
|
356
349
|
try:
|
|
357
350
|
# Prepare upload data
|
|
358
|
-
upload_data = self._prepare_upload_data(
|
|
359
|
-
name=name, framework=framework, description=description, **kwargs
|
|
360
|
-
)
|
|
351
|
+
upload_data = self._prepare_upload_data(name=name, framework=framework, description=description, **kwargs)
|
|
361
352
|
|
|
362
353
|
# Upload file
|
|
363
354
|
return self._upload_tool_file(temp_file_path, upload_data)
|
|
@@ -451,12 +442,147 @@ class ToolClient(BaseClient):
|
|
|
451
442
|
def update_tool(self, tool_id: str, **kwargs) -> Tool:
|
|
452
443
|
"""Update an existing tool."""
|
|
453
444
|
data = self._request("PUT", f"{TOOLS_ENDPOINT}{tool_id}", json=kwargs)
|
|
454
|
-
|
|
445
|
+
response = ToolResponse(**data)
|
|
446
|
+
return Tool.from_response(response, client=self)
|
|
455
447
|
|
|
456
448
|
def delete_tool(self, tool_id: str) -> None:
|
|
457
449
|
"""Delete a tool."""
|
|
458
450
|
self._request("DELETE", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
459
451
|
|
|
452
|
+
def upsert_tool(
|
|
453
|
+
self,
|
|
454
|
+
identifier: str | Tool,
|
|
455
|
+
code: str | None = None,
|
|
456
|
+
description: str | None = None,
|
|
457
|
+
framework: str = "langchain",
|
|
458
|
+
**kwargs,
|
|
459
|
+
) -> Tool:
|
|
460
|
+
"""Create or update a tool by instance, ID, or name.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
identifier: Tool instance, ID (UUID string), or name
|
|
464
|
+
code: Python code containing the tool plugin (required for create)
|
|
465
|
+
description: Tool description
|
|
466
|
+
framework: Tool framework (defaults to "langchain")
|
|
467
|
+
**kwargs: Additional parameters (tags, version, etc.)
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
The created or updated tool.
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
>>> # By name with code (creates if not exists)
|
|
474
|
+
>>> tool = client.tools.upsert_tool(
|
|
475
|
+
... "greeting",
|
|
476
|
+
... code=bundled_source,
|
|
477
|
+
... description="A greeting tool",
|
|
478
|
+
... )
|
|
479
|
+
>>> # By instance
|
|
480
|
+
>>> tool = client.tools.upsert_tool(existing_tool, code=new_code)
|
|
481
|
+
>>> # By ID
|
|
482
|
+
>>> tool = client.tools.upsert_tool("uuid-here", code=new_code)
|
|
483
|
+
"""
|
|
484
|
+
# Handle Tool instance
|
|
485
|
+
if isinstance(identifier, Tool):
|
|
486
|
+
if identifier.id:
|
|
487
|
+
logger.info("Updating tool by instance: %s", identifier.name)
|
|
488
|
+
return self._do_tool_upsert_update(
|
|
489
|
+
identifier.id,
|
|
490
|
+
identifier.name,
|
|
491
|
+
code,
|
|
492
|
+
description,
|
|
493
|
+
framework,
|
|
494
|
+
**kwargs,
|
|
495
|
+
)
|
|
496
|
+
identifier = identifier.name
|
|
497
|
+
|
|
498
|
+
# Handle string (ID or name)
|
|
499
|
+
if isinstance(identifier, str):
|
|
500
|
+
if is_uuid(identifier):
|
|
501
|
+
logger.info("Updating tool by ID: %s", identifier)
|
|
502
|
+
existing = self.get_tool_by_id(identifier)
|
|
503
|
+
return self._do_tool_upsert_update(identifier, existing.name, code, description, framework, **kwargs)
|
|
504
|
+
|
|
505
|
+
# It's a name - find or create
|
|
506
|
+
return self._upsert_tool_by_name(identifier, code, description, framework, **kwargs)
|
|
507
|
+
|
|
508
|
+
raise ValueError(f"Invalid identifier type: {type(identifier)}")
|
|
509
|
+
|
|
510
|
+
def _do_tool_upsert_update(
|
|
511
|
+
self,
|
|
512
|
+
tool_id: str,
|
|
513
|
+
name: str | None,
|
|
514
|
+
code: str | None,
|
|
515
|
+
description: str | None,
|
|
516
|
+
framework: str,
|
|
517
|
+
**kwargs,
|
|
518
|
+
) -> Tool:
|
|
519
|
+
"""Perform the update part of tool upsert."""
|
|
520
|
+
if code:
|
|
521
|
+
# Update via file upload
|
|
522
|
+
with tempfile.NamedTemporaryFile(
|
|
523
|
+
mode="w",
|
|
524
|
+
suffix=".py",
|
|
525
|
+
prefix=f"{name or 'tool'}_",
|
|
526
|
+
delete=False,
|
|
527
|
+
encoding="utf-8",
|
|
528
|
+
) as temp_file:
|
|
529
|
+
temp_file.write(code)
|
|
530
|
+
temp_file_path = temp_file.name
|
|
531
|
+
|
|
532
|
+
try:
|
|
533
|
+
return self.update_tool_via_file(
|
|
534
|
+
tool_id,
|
|
535
|
+
temp_file_path,
|
|
536
|
+
name=name,
|
|
537
|
+
description=description,
|
|
538
|
+
framework=framework,
|
|
539
|
+
**kwargs,
|
|
540
|
+
)
|
|
541
|
+
finally:
|
|
542
|
+
try:
|
|
543
|
+
os.unlink(temp_file_path)
|
|
544
|
+
except OSError:
|
|
545
|
+
pass
|
|
546
|
+
else:
|
|
547
|
+
# Metadata-only update
|
|
548
|
+
update_kwargs = {"framework": framework, **kwargs}
|
|
549
|
+
if name:
|
|
550
|
+
update_kwargs["name"] = name
|
|
551
|
+
if description:
|
|
552
|
+
update_kwargs["description"] = description
|
|
553
|
+
return self.update_tool(tool_id, **update_kwargs)
|
|
554
|
+
|
|
555
|
+
def _upsert_tool_by_name(
|
|
556
|
+
self,
|
|
557
|
+
name: str,
|
|
558
|
+
code: str | None,
|
|
559
|
+
description: str | None,
|
|
560
|
+
framework: str,
|
|
561
|
+
**kwargs,
|
|
562
|
+
) -> Tool:
|
|
563
|
+
"""Find tool by name and update, or create if not found."""
|
|
564
|
+
existing = self.find_tools(name)
|
|
565
|
+
|
|
566
|
+
if len(existing) == 1:
|
|
567
|
+
logger.info("Updating existing tool: %s", name)
|
|
568
|
+
return self._do_tool_upsert_update(existing[0].id, name, code, description, framework, **kwargs)
|
|
569
|
+
|
|
570
|
+
if len(existing) > 1:
|
|
571
|
+
raise ValueError(f"Multiple tools found with name '{name}'")
|
|
572
|
+
|
|
573
|
+
# Create new tool - code is required
|
|
574
|
+
if not code:
|
|
575
|
+
raise ValueError(f"Tool '{name}' not found and no code provided for creation")
|
|
576
|
+
|
|
577
|
+
logger.info("Creating new tool: %s", name)
|
|
578
|
+
return self.create_tool_from_code(
|
|
579
|
+
name=name,
|
|
580
|
+
code=code,
|
|
581
|
+
framework=framework,
|
|
582
|
+
description=description,
|
|
583
|
+
**kwargs,
|
|
584
|
+
)
|
|
585
|
+
|
|
460
586
|
def get_tool_script(self, tool_id: str) -> str:
|
|
461
587
|
"""Get the tool script content.
|
|
462
588
|
|
|
@@ -525,8 +651,9 @@ class ToolClient(BaseClient):
|
|
|
525
651
|
data=update_payload,
|
|
526
652
|
)
|
|
527
653
|
|
|
528
|
-
|
|
654
|
+
tool_response = ToolResponse(**response)
|
|
655
|
+
return Tool.from_response(tool_response, client=self)
|
|
529
656
|
|
|
530
657
|
except Exception as e:
|
|
531
|
-
logger.error(
|
|
658
|
+
logger.error("Failed to update tool %s via file: %s", tool_id, e)
|
|
532
659
|
raise
|
glaip_sdk/client/validators.py
CHANGED
|
@@ -39,9 +39,7 @@ class ResourceValidator:
|
|
|
39
39
|
if len(found_tools) == 1:
|
|
40
40
|
return str(found_tools[0].id)
|
|
41
41
|
elif len(found_tools) > 1:
|
|
42
|
-
raise AmbiguousResourceError(
|
|
43
|
-
f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}"
|
|
44
|
-
)
|
|
42
|
+
raise AmbiguousResourceError(f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}")
|
|
45
43
|
else:
|
|
46
44
|
raise NotFoundError(f"Tool not found: {tool_name}")
|
|
47
45
|
|
|
@@ -51,9 +49,7 @@ class ResourceValidator:
|
|
|
51
49
|
if len(found_tools) == 1:
|
|
52
50
|
return str(found_tools[0].id)
|
|
53
51
|
elif len(found_tools) > 1:
|
|
54
|
-
raise AmbiguousResourceError(
|
|
55
|
-
f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}"
|
|
56
|
-
)
|
|
52
|
+
raise AmbiguousResourceError(f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}")
|
|
57
53
|
else:
|
|
58
54
|
raise NotFoundError(f"Tool not found: {tool.name}")
|
|
59
55
|
|
|
@@ -73,9 +69,7 @@ class ResourceValidator:
|
|
|
73
69
|
elif hasattr(tool, "name") and tool.name is not None:
|
|
74
70
|
return self._resolve_tool_by_name_attribute(tool, client)
|
|
75
71
|
else:
|
|
76
|
-
raise ValidationError(
|
|
77
|
-
f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute"
|
|
78
|
-
)
|
|
72
|
+
raise ValidationError(f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute")
|
|
79
73
|
|
|
80
74
|
def _process_single_tool(self, tool: str | Tool, client: Any) -> str:
|
|
81
75
|
"""Process a single tool reference and return its ID."""
|
|
@@ -99,22 +93,14 @@ class ResourceValidator:
|
|
|
99
93
|
try:
|
|
100
94
|
tool_id = cls()._process_single_tool(tool, client)
|
|
101
95
|
tool_ids.append(tool_id)
|
|
102
|
-
except (AmbiguousResourceError, NotFoundError) as
|
|
96
|
+
except (AmbiguousResourceError, NotFoundError) as err:
|
|
103
97
|
# Determine the tool name for the error message
|
|
104
|
-
tool_name = (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
raise ValidationError(
|
|
108
|
-
f"Failed to resolve tool name '{tool_name}' to ID: {e}"
|
|
109
|
-
)
|
|
110
|
-
except Exception as e:
|
|
98
|
+
tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
|
|
99
|
+
raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
|
|
100
|
+
except Exception as err:
|
|
111
101
|
# For other exceptions, wrap them appropriately
|
|
112
|
-
tool_name = (
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
raise ValidationError(
|
|
116
|
-
f"Failed to resolve tool name '{tool_name}' to ID: {e}"
|
|
117
|
-
)
|
|
102
|
+
tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
|
|
103
|
+
raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
|
|
118
104
|
|
|
119
105
|
return tool_ids
|
|
120
106
|
|
|
@@ -158,9 +144,7 @@ class ResourceValidator:
|
|
|
158
144
|
elif hasattr(agent, "name") and agent.name is not None:
|
|
159
145
|
return self._resolve_agent_by_name_attribute(agent, client)
|
|
160
146
|
else:
|
|
161
|
-
raise ValidationError(
|
|
162
|
-
f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute"
|
|
163
|
-
)
|
|
147
|
+
raise ValidationError(f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute")
|
|
164
148
|
|
|
165
149
|
def _process_single_agent(self, agent: str | Any, client: Any) -> str:
|
|
166
150
|
"""Process a single agent reference and return its ID."""
|
|
@@ -184,26 +168,14 @@ class ResourceValidator:
|
|
|
184
168
|
try:
|
|
185
169
|
agent_id = cls()._process_single_agent(agent, client)
|
|
186
170
|
agent_ids.append(agent_id)
|
|
187
|
-
except (AmbiguousResourceError, NotFoundError) as
|
|
171
|
+
except (AmbiguousResourceError, NotFoundError) as err:
|
|
188
172
|
# Determine the agent name for the error message
|
|
189
|
-
agent_name = (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
else getattr(agent, "name", str(agent))
|
|
193
|
-
)
|
|
194
|
-
raise ValidationError(
|
|
195
|
-
f"Failed to resolve agent name '{agent_name}' to ID: {e}"
|
|
196
|
-
)
|
|
197
|
-
except Exception as e:
|
|
173
|
+
agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
|
|
174
|
+
raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
|
|
175
|
+
except Exception as err:
|
|
198
176
|
# For other exceptions, wrap them appropriately
|
|
199
|
-
agent_name = (
|
|
200
|
-
|
|
201
|
-
if isinstance(agent, str)
|
|
202
|
-
else getattr(agent, "name", str(agent))
|
|
203
|
-
)
|
|
204
|
-
raise ValidationError(
|
|
205
|
-
f"Failed to resolve agent name '{agent_name}' to ID: {e}"
|
|
206
|
-
)
|
|
177
|
+
agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
|
|
178
|
+
raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
|
|
207
179
|
|
|
208
180
|
return agent_ids
|
|
209
181
|
|
|
@@ -213,8 +185,8 @@ class ResourceValidator:
|
|
|
213
185
|
for tool_id in tool_ids:
|
|
214
186
|
try:
|
|
215
187
|
client.get_tool_by_id(tool_id)
|
|
216
|
-
except NotFoundError:
|
|
217
|
-
raise ValidationError(f"Tool not found: {tool_id}")
|
|
188
|
+
except NotFoundError as err:
|
|
189
|
+
raise ValidationError(f"Tool not found: {tool_id}") from err
|
|
218
190
|
|
|
219
191
|
@classmethod
|
|
220
192
|
def validate_agents_exist(cls, agent_ids: list[str], client: Any) -> None:
|
|
@@ -222,5 +194,5 @@ class ResourceValidator:
|
|
|
222
194
|
for agent_id in agent_ids:
|
|
223
195
|
try:
|
|
224
196
|
client.get_agent_by_id(agent_id)
|
|
225
|
-
except NotFoundError:
|
|
226
|
-
raise ValidationError(f"Agent not found: {agent_id}")
|
|
197
|
+
except NotFoundError as err:
|
|
198
|
+
raise ValidationError(f"Agent not found: {agent_id}") from err
|
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
|
+
)
|
glaip_sdk/exceptions.py
CHANGED
|
@@ -107,9 +107,7 @@ class AgentTimeoutError(TimeoutError):
|
|
|
107
107
|
agent_name: Optional name of the agent that timed out
|
|
108
108
|
"""
|
|
109
109
|
agent_info = f" for agent '{agent_name}'" if agent_name else ""
|
|
110
|
-
message =
|
|
111
|
-
f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
|
|
112
|
-
)
|
|
110
|
+
message = f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
|
|
113
111
|
super().__init__(message)
|
|
114
112
|
self.timeout_seconds = timeout_seconds
|
|
115
113
|
self.agent_name = agent_name
|
glaip_sdk/icons.py
CHANGED
|
@@ -5,10 +5,13 @@ Authors:
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
ICON_AGENT = "🤖"
|
|
8
|
-
ICON_AGENT_STEP = "
|
|
8
|
+
ICON_AGENT_STEP = "🤖"
|
|
9
9
|
ICON_TOOL = "🔧"
|
|
10
|
-
ICON_TOOL_STEP = "
|
|
11
|
-
ICON_DELEGATE =
|
|
10
|
+
ICON_TOOL_STEP = "🔧"
|
|
11
|
+
ICON_DELEGATE = ICON_AGENT_STEP
|
|
12
|
+
ICON_STATUS_SUCCESS = "✓"
|
|
13
|
+
ICON_STATUS_FAILED = "✗"
|
|
14
|
+
ICON_STATUS_WARNING = "⚠"
|
|
12
15
|
|
|
13
16
|
__all__ = [
|
|
14
17
|
"ICON_AGENT",
|
|
@@ -16,4 +19,7 @@ __all__ = [
|
|
|
16
19
|
"ICON_TOOL",
|
|
17
20
|
"ICON_TOOL_STEP",
|
|
18
21
|
"ICON_DELEGATE",
|
|
22
|
+
"ICON_STATUS_SUCCESS",
|
|
23
|
+
"ICON_STATUS_FAILED",
|
|
24
|
+
"ICON_STATUS_WARNING",
|
|
19
25
|
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) package for GL AIP platform.
|
|
2
|
+
|
|
3
|
+
This package provides the MCP class and MCPRegistry for managing
|
|
4
|
+
Model Context Protocol configurations on the GL AIP platform.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from glaip_sdk.mcps import MCP, get_mcp_registry
|
|
8
|
+
>>> mcp = MCP.from_native("arxiv-search")
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from glaip_sdk.mcps.base import MCP, MCPConfigValue
|
|
14
|
+
from glaip_sdk.registry.mcp import MCPRegistry, get_mcp_registry
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"MCP",
|
|
18
|
+
"MCPConfigValue",
|
|
19
|
+
"MCPRegistry",
|
|
20
|
+
"get_mcp_registry",
|
|
21
|
+
]
|