glaip-sdk 0.6.26__py3-none-any.whl → 0.7.1__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/commands/mcps/__init__.py +0 -4
- glaip_sdk/cli/commands/mcps/_common.py +11 -42
- glaip_sdk/cli/commands/mcps/create.py +8 -9
- glaip_sdk/cli/commands/mcps/update.py +56 -12
- glaip_sdk/cli/commands/tools/create.py +2 -2
- glaip_sdk/cli/slash/accounts_controller.py +3 -1
- glaip_sdk/cli/slash/session.py +19 -0
- glaip_sdk/cli/slash/tui/__init__.py +26 -1
- glaip_sdk/cli/slash/tui/accounts.tcss +7 -5
- glaip_sdk/cli/slash/tui/accounts_app.py +66 -9
- glaip_sdk/cli/slash/tui/clipboard.py +147 -0
- glaip_sdk/cli/slash/tui/context.py +59 -0
- glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
- glaip_sdk/cli/slash/tui/terminal.py +402 -0
- glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
- glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
- glaip_sdk/cli/slash/tui/theme/manager.py +86 -0
- glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
- glaip_sdk/cli/slash/tui/toast.py +123 -0
- glaip_sdk/client/tools.py +14 -20
- glaip_sdk/registry/tool.py +193 -81
- {glaip_sdk-0.6.26.dist-info → glaip_sdk-0.7.1.dist-info}/METADATA +2 -2
- {glaip_sdk-0.6.26.dist-info → glaip_sdk-0.7.1.dist-info}/RECORD +26 -18
- glaip_sdk/client/_agent_payloads.py +0 -52
- {glaip_sdk-0.6.26.dist-info → glaip_sdk-0.7.1.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.6.26.dist-info → glaip_sdk-0.7.1.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.6.26.dist-info → glaip_sdk-0.7.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Toast notification helpers for Textual TUIs.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToastVariant(str, Enum):
|
|
15
|
+
"""Toast message variant."""
|
|
16
|
+
|
|
17
|
+
INFO = "info"
|
|
18
|
+
SUCCESS = "success"
|
|
19
|
+
WARNING = "warning"
|
|
20
|
+
ERROR = "error"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DEFAULT_TOAST_DURATIONS_SECONDS: dict[ToastVariant, float] = {
|
|
24
|
+
ToastVariant.SUCCESS: 2.0,
|
|
25
|
+
ToastVariant.INFO: 3.0,
|
|
26
|
+
ToastVariant.WARNING: 3.0,
|
|
27
|
+
ToastVariant.ERROR: 5.0,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, slots=True)
|
|
32
|
+
class ToastState:
|
|
33
|
+
"""Immutable toast payload."""
|
|
34
|
+
|
|
35
|
+
message: str
|
|
36
|
+
variant: ToastVariant
|
|
37
|
+
duration_seconds: float
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ToastBus:
|
|
41
|
+
"""Single-toast state holder with auto-dismiss."""
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
"""Initialize the bus."""
|
|
45
|
+
self._state: ToastState | None = None
|
|
46
|
+
self._dismiss_task: asyncio.Task[None] | None = None
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def state(self) -> ToastState | None:
|
|
50
|
+
"""Return the current toast state."""
|
|
51
|
+
return self._state
|
|
52
|
+
|
|
53
|
+
def show(
|
|
54
|
+
self,
|
|
55
|
+
message: str,
|
|
56
|
+
variant: ToastVariant | str = ToastVariant.INFO,
|
|
57
|
+
*,
|
|
58
|
+
duration_seconds: float | None = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Set toast state and schedule auto-dismiss."""
|
|
61
|
+
resolved_variant = self._coerce_variant(variant)
|
|
62
|
+
resolved_duration = (
|
|
63
|
+
DEFAULT_TOAST_DURATIONS_SECONDS[resolved_variant] if duration_seconds is None else float(duration_seconds)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self._state = ToastState(
|
|
67
|
+
message=message,
|
|
68
|
+
variant=resolved_variant,
|
|
69
|
+
duration_seconds=resolved_duration,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
self._cancel_dismiss_task()
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
loop = asyncio.get_running_loop()
|
|
76
|
+
except RuntimeError:
|
|
77
|
+
raise RuntimeError(
|
|
78
|
+
"Cannot schedule toast auto-dismiss: no running event loop. "
|
|
79
|
+
"ToastBus.show() must be called from within an async context."
|
|
80
|
+
) from None
|
|
81
|
+
|
|
82
|
+
self._dismiss_task = loop.create_task(self._auto_dismiss(resolved_duration))
|
|
83
|
+
|
|
84
|
+
def clear(self) -> None:
|
|
85
|
+
"""Clear the current toast."""
|
|
86
|
+
self._cancel_dismiss_task()
|
|
87
|
+
self._state = None
|
|
88
|
+
|
|
89
|
+
def copy_success(self, label: str | None = None) -> None:
|
|
90
|
+
"""Show clipboard success toast."""
|
|
91
|
+
message = "Copied to clipboard" if not label else f"Copied {label} to clipboard"
|
|
92
|
+
self.show(message=message, variant=ToastVariant.SUCCESS)
|
|
93
|
+
|
|
94
|
+
def copy_failed(self) -> None:
|
|
95
|
+
"""Show clipboard failure toast."""
|
|
96
|
+
self.show(
|
|
97
|
+
message="Clipboard unavailable. Text printed below",
|
|
98
|
+
variant=ToastVariant.WARNING,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def _coerce_variant(self, variant: ToastVariant | str) -> ToastVariant:
|
|
102
|
+
if isinstance(variant, ToastVariant):
|
|
103
|
+
return variant
|
|
104
|
+
try:
|
|
105
|
+
return ToastVariant(variant)
|
|
106
|
+
except ValueError:
|
|
107
|
+
return ToastVariant.INFO
|
|
108
|
+
|
|
109
|
+
def _cancel_dismiss_task(self) -> None:
|
|
110
|
+
if self._dismiss_task is None:
|
|
111
|
+
return
|
|
112
|
+
if not self._dismiss_task.done():
|
|
113
|
+
self._dismiss_task.cancel()
|
|
114
|
+
self._dismiss_task = None
|
|
115
|
+
|
|
116
|
+
async def _auto_dismiss(self, duration_seconds: float) -> None:
|
|
117
|
+
try:
|
|
118
|
+
await asyncio.sleep(duration_seconds)
|
|
119
|
+
except asyncio.CancelledError:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
self._state = None
|
|
123
|
+
self._dismiss_task = None
|
glaip_sdk/client/tools.py
CHANGED
|
@@ -103,6 +103,9 @@ class ToolClient(BaseClient):
|
|
|
103
103
|
def _prepare_upload_data(self, name: str, framework: str, description: str | None = None, **kwargs) -> dict:
|
|
104
104
|
"""Prepare upload data dictionary.
|
|
105
105
|
|
|
106
|
+
Uses the same payload building logic as _build_create_payload to ensure
|
|
107
|
+
consistency between upload and metadata-only tool creation.
|
|
108
|
+
|
|
106
109
|
Args:
|
|
107
110
|
name: Tool name
|
|
108
111
|
framework: Tool framework
|
|
@@ -112,28 +115,19 @@ class ToolClient(BaseClient):
|
|
|
112
115
|
Returns:
|
|
113
116
|
dict: Upload data dictionary
|
|
114
117
|
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"framework": framework,
|
|
118
|
-
"type": kwargs.pop("tool_type", DEFAULT_TOOL_TYPE), # Default to custom
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if description:
|
|
122
|
-
data["description"] = description
|
|
123
|
-
|
|
124
|
-
# Handle tags if provided in kwargs
|
|
125
|
-
if kwargs.get("tags"):
|
|
126
|
-
if isinstance(kwargs["tags"], list):
|
|
127
|
-
data["tags"] = ",".join(kwargs["tags"])
|
|
128
|
-
else:
|
|
129
|
-
data["tags"] = kwargs["tags"]
|
|
118
|
+
# Extract tool_type from kwargs if present, defaulting to DEFAULT_TOOL_TYPE
|
|
119
|
+
tool_type = kwargs.pop("tool_type", DEFAULT_TOOL_TYPE)
|
|
130
120
|
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
121
|
+
# Use _build_create_payload to build the payload consistently
|
|
122
|
+
payload = self._build_create_payload(
|
|
123
|
+
name=name,
|
|
124
|
+
description=description,
|
|
125
|
+
framework=framework,
|
|
126
|
+
tool_type=tool_type,
|
|
127
|
+
**kwargs,
|
|
128
|
+
)
|
|
135
129
|
|
|
136
|
-
return
|
|
130
|
+
return payload
|
|
137
131
|
|
|
138
132
|
def _upload_tool_file(self, file_path: str, upload_data: dict) -> Tool:
|
|
139
133
|
"""Upload tool file to server.
|
glaip_sdk/registry/tool.py
CHANGED
|
@@ -54,6 +54,32 @@ class ToolRegistry(BaseRegistry["Tool"]):
|
|
|
54
54
|
value = getattr(obj, attr, None)
|
|
55
55
|
return value if isinstance(value, str) else None
|
|
56
56
|
|
|
57
|
+
def _extract_name_from_instance(self, ref: Any) -> str | None:
|
|
58
|
+
"""Extract name from a non-type instance.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
ref: The instance to extract name from.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The extracted name, or None if not found.
|
|
65
|
+
"""
|
|
66
|
+
if isinstance(ref, type):
|
|
67
|
+
return None
|
|
68
|
+
return self._get_string_attr(ref, "name")
|
|
69
|
+
|
|
70
|
+
def _extract_name_from_class(self, ref: Any) -> str | None:
|
|
71
|
+
"""Extract name from a class.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
ref: The class to extract name from.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The extracted name, or None if not found.
|
|
78
|
+
"""
|
|
79
|
+
if not isinstance(ref, type):
|
|
80
|
+
return None
|
|
81
|
+
return self._get_string_attr(ref, "name") or self._get_name_from_model_fields(ref)
|
|
82
|
+
|
|
57
83
|
def _extract_name(self, ref: Any) -> str:
|
|
58
84
|
"""Extract tool name from a reference.
|
|
59
85
|
|
|
@@ -81,31 +107,37 @@ class ToolRegistry(BaseRegistry["Tool"]):
|
|
|
81
107
|
return ref.get("name") or ref.get("id") or ""
|
|
82
108
|
|
|
83
109
|
# Tool instance (not a class) with name attribute
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return name
|
|
110
|
+
name = self._extract_name_from_instance(ref)
|
|
111
|
+
if name:
|
|
112
|
+
return name
|
|
88
113
|
|
|
89
114
|
# Tool class - try direct attribute first, then model_fields
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return name
|
|
115
|
+
name = self._extract_name_from_class(ref)
|
|
116
|
+
if name:
|
|
117
|
+
return name
|
|
94
118
|
|
|
95
119
|
raise ValueError(f"Cannot extract name from: {ref}")
|
|
96
120
|
|
|
97
|
-
def
|
|
98
|
-
"""
|
|
121
|
+
def _cache_tool(self, tool: Tool, name: str) -> None:
|
|
122
|
+
"""Cache a tool by name and ID if available.
|
|
99
123
|
|
|
100
124
|
Args:
|
|
101
|
-
|
|
125
|
+
tool: The tool to cache.
|
|
126
|
+
name: The tool name.
|
|
127
|
+
"""
|
|
128
|
+
self._cache[name] = tool
|
|
129
|
+
if hasattr(tool, "id") and tool.id:
|
|
130
|
+
self._cache[tool.id] = tool
|
|
131
|
+
|
|
132
|
+
def _resolve_tool_instance(self, ref: Any, name: str) -> Tool | None:
|
|
133
|
+
"""Resolve a ToolClass instance.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
ref: The ToolClass instance to resolve.
|
|
102
137
|
name: The extracted tool name.
|
|
103
138
|
|
|
104
139
|
Returns:
|
|
105
|
-
The resolved
|
|
106
|
-
|
|
107
|
-
Raises:
|
|
108
|
-
ValueError: If the tool cannot be resolved.
|
|
140
|
+
The resolved tool, or None if not a ToolClass instance.
|
|
109
141
|
"""
|
|
110
142
|
# Lazy imports to avoid circular dependency
|
|
111
143
|
from glaip_sdk.tools.base import Tool as ToolClass # noqa: PLC0415
|
|
@@ -113,56 +145,23 @@ class ToolRegistry(BaseRegistry["Tool"]):
|
|
|
113
145
|
from glaip_sdk.utils.discovery import find_tool # noqa: PLC0415
|
|
114
146
|
from glaip_sdk.utils.sync import update_or_create_tool # noqa: PLC0415
|
|
115
147
|
|
|
116
|
-
# Tool instance from Tool.from_native() or Tool.from_langchain()
|
|
117
148
|
# Use try/except to handle mocked Tool class in tests
|
|
118
149
|
try:
|
|
119
150
|
is_tool_instance = isinstance(ref, ToolClass)
|
|
120
151
|
except TypeError:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if is_tool_instance:
|
|
124
|
-
# If Tool has an ID, it's already deployed - return as-is
|
|
125
|
-
if ref.id is not None:
|
|
126
|
-
logger.debug("Caching already deployed tool: %s", name)
|
|
127
|
-
self._cache[name] = ref
|
|
128
|
-
# Also cache by id for consistency with other resolution branches
|
|
129
|
-
self._cache[ref.id] = ref
|
|
130
|
-
return ref
|
|
131
|
-
|
|
132
|
-
# Tool.from_native() - look up on platform
|
|
133
|
-
if ref.tool_type == ToolType.NATIVE:
|
|
134
|
-
logger.info("Looking up native tool: %s", name)
|
|
135
|
-
tool = find_tool(name)
|
|
136
|
-
if tool:
|
|
137
|
-
self._cache[name] = tool
|
|
138
|
-
return tool
|
|
139
|
-
raise ValueError(f"Native tool not found on platform: {name}")
|
|
140
|
-
|
|
141
|
-
# Tool.from_langchain() - upload the tool_class
|
|
142
|
-
if ref.tool_class is not None:
|
|
143
|
-
logger.info("Uploading custom tool: %s", name)
|
|
144
|
-
tool = update_or_create_tool(ref.tool_class)
|
|
145
|
-
self._cache[name] = tool
|
|
146
|
-
if tool.id:
|
|
147
|
-
self._cache[tool.id] = tool
|
|
148
|
-
return tool
|
|
152
|
+
return None
|
|
149
153
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
f"Cannot resolve Tool instance: {ref}. "
|
|
153
|
-
f"Tool has no id, is not NATIVE type, and has no tool_class. "
|
|
154
|
-
f"Ensure Tool is created via Tool.from_native() or Tool.from_langchain()."
|
|
155
|
-
)
|
|
154
|
+
if not is_tool_instance:
|
|
155
|
+
return None
|
|
156
156
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
self._cache[name] = ref
|
|
163
|
-
return ref
|
|
157
|
+
# If Tool has an ID, it's already deployed - return as-is
|
|
158
|
+
if ref.id is not None:
|
|
159
|
+
logger.debug("Caching already deployed tool: %s", name)
|
|
160
|
+
self._cache_tool(ref, name)
|
|
161
|
+
return ref
|
|
164
162
|
|
|
165
|
-
|
|
163
|
+
# Tool.from_native() - look up on platform
|
|
164
|
+
if ref.tool_type == ToolType.NATIVE:
|
|
166
165
|
logger.info("Looking up native tool: %s", name)
|
|
167
166
|
tool = find_tool(name)
|
|
168
167
|
if tool:
|
|
@@ -170,32 +169,144 @@ class ToolRegistry(BaseRegistry["Tool"]):
|
|
|
170
169
|
return tool
|
|
171
170
|
raise ValueError(f"Native tool not found on platform: {name}")
|
|
172
171
|
|
|
173
|
-
#
|
|
174
|
-
if
|
|
172
|
+
# Tool.from_langchain() - upload the tool_class
|
|
173
|
+
if ref.tool_class is not None:
|
|
175
174
|
logger.info("Uploading custom tool: %s", name)
|
|
176
|
-
tool = update_or_create_tool(ref)
|
|
177
|
-
self.
|
|
178
|
-
if tool.id:
|
|
179
|
-
self._cache[tool.id] = tool
|
|
175
|
+
tool = update_or_create_tool(ref.tool_class)
|
|
176
|
+
self._cache_tool(tool, name)
|
|
180
177
|
return tool
|
|
181
178
|
|
|
182
|
-
#
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return tool
|
|
189
|
-
raise ValueError(f"Tool dict missing 'id': {ref}")
|
|
179
|
+
# Unresolvable Tool instance - neither native nor has tool_class
|
|
180
|
+
raise ValueError(
|
|
181
|
+
f"Cannot resolve Tool instance: {ref}. "
|
|
182
|
+
f"Tool has no id, is not NATIVE type, and has no tool_class. "
|
|
183
|
+
f"Ensure Tool is created via Tool.from_native() or Tool.from_langchain()."
|
|
184
|
+
)
|
|
190
185
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
186
|
+
def _resolve_deployed_tool(self, ref: Any, name: str) -> Tool | None:
|
|
187
|
+
"""Resolve an already deployed tool (has id/name attributes).
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
ref: The tool reference to resolve.
|
|
191
|
+
name: The extracted tool name.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
The resolved tool, or None if not a deployed tool.
|
|
195
|
+
"""
|
|
196
|
+
from glaip_sdk.utils.discovery import find_tool # noqa: PLC0415
|
|
197
|
+
|
|
198
|
+
# Already deployed tool (not a ToolClass, but has id/name)
|
|
199
|
+
# This handles API response objects and backward compatibility
|
|
200
|
+
if not (hasattr(ref, "id") and hasattr(ref, "name") and not isinstance(ref, type)):
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
if ref.id is not None:
|
|
204
|
+
logger.debug("Caching already deployed tool: %s", name)
|
|
205
|
+
# Use _cache_tool to cache by both name and ID for consistency
|
|
206
|
+
self._cache_tool(ref, name)
|
|
207
|
+
return ref
|
|
208
|
+
|
|
209
|
+
# Tool without ID (backward compatibility) - look up on platform
|
|
210
|
+
logger.info("Looking up native tool: %s", name)
|
|
211
|
+
tool = find_tool(name)
|
|
212
|
+
if tool:
|
|
213
|
+
# Use _cache_tool to cache by both name and ID if available
|
|
214
|
+
self._cache_tool(tool, name)
|
|
215
|
+
return tool
|
|
216
|
+
raise ValueError(f"Native tool not found on platform: {name}")
|
|
217
|
+
|
|
218
|
+
def _resolve_custom_tool(self, ref: Any, name: str) -> Tool | None:
|
|
219
|
+
"""Resolve a custom tool class.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
ref: The tool reference to resolve.
|
|
223
|
+
name: The extracted tool name.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
The resolved tool, or None if not a custom tool.
|
|
227
|
+
"""
|
|
228
|
+
from glaip_sdk.utils.sync import update_or_create_tool # noqa: PLC0415
|
|
229
|
+
|
|
230
|
+
if not self._is_custom_tool(ref):
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
logger.info("Uploading custom tool: %s", name)
|
|
234
|
+
tool = update_or_create_tool(ref)
|
|
235
|
+
self._cache_tool(tool, name)
|
|
236
|
+
return tool
|
|
237
|
+
|
|
238
|
+
def _resolve_dict_tool(self, ref: Any, name: str) -> Tool | None:
|
|
239
|
+
"""Resolve a tool from a dict (API response).
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
ref: The dict to resolve.
|
|
243
|
+
name: The extracted tool name.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
The resolved tool, or None if not a dict.
|
|
247
|
+
"""
|
|
248
|
+
from glaip_sdk.tools.base import Tool as ToolClass # noqa: PLC0415
|
|
249
|
+
|
|
250
|
+
if not isinstance(ref, dict):
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
tool_id = ref.get("id")
|
|
254
|
+
if tool_id:
|
|
255
|
+
tool = ToolClass(id=tool_id, name=ref.get("name", ""))
|
|
256
|
+
# Use _cache_tool to cache by both name and ID for consistency
|
|
257
|
+
self._cache_tool(tool, name)
|
|
258
|
+
return tool
|
|
259
|
+
raise ValueError(f"Tool dict missing 'id': {ref}")
|
|
260
|
+
|
|
261
|
+
def _resolve_string_tool(self, ref: Any, name: str) -> Tool | None:
|
|
262
|
+
"""Resolve a tool from a string name.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
ref: The string to resolve.
|
|
266
|
+
name: The extracted tool name.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
The resolved tool, or None if not a string.
|
|
270
|
+
"""
|
|
271
|
+
from glaip_sdk.utils.discovery import find_tool # noqa: PLC0415
|
|
272
|
+
|
|
273
|
+
if not isinstance(ref, str):
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
logger.info("Looking up tool by name: %s", name)
|
|
277
|
+
tool = find_tool(name)
|
|
278
|
+
if tool:
|
|
279
|
+
# Use _cache_tool to cache by both name and ID for consistency
|
|
280
|
+
self._cache_tool(tool, name)
|
|
281
|
+
return tool
|
|
282
|
+
raise ValueError(f"Tool not found on platform: {name}")
|
|
283
|
+
|
|
284
|
+
def _resolve_and_cache(self, ref: Any, name: str) -> Tool:
|
|
285
|
+
"""Resolve tool reference - upload if class, find if string/native.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
ref: The tool reference to resolve.
|
|
289
|
+
name: The extracted tool name.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
The resolved glaip_sdk.models.Tool object.
|
|
293
|
+
|
|
294
|
+
Raises:
|
|
295
|
+
ValueError: If the tool cannot be resolved.
|
|
296
|
+
"""
|
|
297
|
+
# Try each resolution strategy in order
|
|
298
|
+
resolvers = [
|
|
299
|
+
self._resolve_tool_instance,
|
|
300
|
+
self._resolve_deployed_tool,
|
|
301
|
+
self._resolve_custom_tool,
|
|
302
|
+
self._resolve_dict_tool,
|
|
303
|
+
self._resolve_string_tool,
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
for resolver in resolvers:
|
|
307
|
+
result = resolver(ref, name)
|
|
308
|
+
if result is not None:
|
|
309
|
+
return result
|
|
199
310
|
|
|
200
311
|
raise ValueError(f"Could not resolve tool reference: {ref}")
|
|
201
312
|
|
|
@@ -234,7 +345,8 @@ class ToolRegistry(BaseRegistry["Tool"]):
|
|
|
234
345
|
if ref.id is not None:
|
|
235
346
|
name = self._extract_name(ref)
|
|
236
347
|
if name not in self._cache:
|
|
237
|
-
|
|
348
|
+
# Use _cache_tool to cache by both name and ID for consistency
|
|
349
|
+
self._cache_tool(ref, name)
|
|
238
350
|
return ref
|
|
239
351
|
|
|
240
352
|
# Tool without ID (e.g., from Tool.from_native()) - needs platform lookup
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glaip-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Python SDK and CLI for GL AIP (GDP Labs AI Agent Package) - Build, run, and manage AI agents
|
|
5
5
|
Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
|
|
6
6
|
License: MIT
|
|
@@ -144,7 +144,7 @@ print("--- Stream complete ---")
|
|
|
144
144
|
|
|
145
145
|
🎉 **SDK Success!** You're now ready to build AI-powered applications with Python.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
______________________________________________________________________
|
|
148
148
|
|
|
149
149
|
## 💻 Hello World - CLI
|
|
150
150
|
|
|
@@ -40,20 +40,20 @@ glaip_sdk/cli/commands/agents/list.py,sha256=u4gGYYMLJZatKVtpIovcxqzU8caIyvZCuou
|
|
|
40
40
|
glaip_sdk/cli/commands/agents/run.py,sha256=XtahMOHhh8K3kaUODXGxbuvA4FfcVEO8yBGfCPqP8zY,8187
|
|
41
41
|
glaip_sdk/cli/commands/agents/sync_langflow.py,sha256=NVejCglmKAzy9WUnj_VkutyOl-jF8ro4Rh_JLul3xxs,2329
|
|
42
42
|
glaip_sdk/cli/commands/agents/update.py,sha256=uMX_-DFhOTBS-tboG-JEkGLlf1q-cfj1FGABGIQSh9g,3441
|
|
43
|
-
glaip_sdk/cli/commands/mcps/__init__.py,sha256=
|
|
44
|
-
glaip_sdk/cli/commands/mcps/_common.py,sha256
|
|
43
|
+
glaip_sdk/cli/commands/mcps/__init__.py,sha256=QRCdjBQlYc0ocXazbvqA0xA32FnrJF0XvErdb5OwVTE,2834
|
|
44
|
+
glaip_sdk/cli/commands/mcps/_common.py,sha256=YOSOijID1s8UMIm98K6fyXrp1jkHv2ovWS1x9ipFcP0,14578
|
|
45
45
|
glaip_sdk/cli/commands/mcps/connect.py,sha256=dxz4Y43boZivRGwe5jWM5KwwUNNqiZE6HLKb_BZWgD8,2416
|
|
46
|
-
glaip_sdk/cli/commands/mcps/create.py,sha256=
|
|
46
|
+
glaip_sdk/cli/commands/mcps/create.py,sha256=QryzfgVeI8XPJRdY2FWnUYWktSBTrwlfJqtoZ5CphNU,4955
|
|
47
47
|
glaip_sdk/cli/commands/mcps/delete.py,sha256=yIEFuzY6DswVblTdEql3k6b6JSNstNqIHg0vZqazTXc,1945
|
|
48
48
|
glaip_sdk/cli/commands/mcps/get.py,sha256=XQns1wfydmN-7fiNGzlXLWTktLr4pwgW1jhoHVf9NYM,7072
|
|
49
49
|
glaip_sdk/cli/commands/mcps/list.py,sha256=e0qtTtkmOsZVsBNu_ytfyFPV0eDtdlVrUfTfcoI8Ivk,2051
|
|
50
50
|
glaip_sdk/cli/commands/mcps/tools.py,sha256=iMi2mfwQS-lE4yhhHRiBrVeK6qG-IfVKGyV1P4stZVs,7089
|
|
51
|
-
glaip_sdk/cli/commands/mcps/update.py,sha256=
|
|
51
|
+
glaip_sdk/cli/commands/mcps/update.py,sha256=7a8b77nDdSRz1jwh-0RoSNbcKw6K6XuZsl2qSC1AnS0,6330
|
|
52
52
|
glaip_sdk/cli/commands/shared/__init__.py,sha256=LA1GQMwBSNpeSHifPOJ9V4VjOuGAlVOyD1MIQO1z1ms,465
|
|
53
53
|
glaip_sdk/cli/commands/shared/formatters.py,sha256=QWjVTihmQV7O6MjMI_8tnTycu0rgGHKF5vMh_FanZMg,2499
|
|
54
54
|
glaip_sdk/cli/commands/tools/__init__.py,sha256=KkcMYJNe164V25Eqp2Bygwf49LIcyECm3r5k59p6cQU,2111
|
|
55
55
|
glaip_sdk/cli/commands/tools/_common.py,sha256=IFJEoyP-lphu0X3eR6txr4QD8Qr1g-AP1kLtahZ29Fo,2190
|
|
56
|
-
glaip_sdk/cli/commands/tools/create.py,sha256=
|
|
56
|
+
glaip_sdk/cli/commands/tools/create.py,sha256=X0xSKG9MyuZC_ZdSGHX2RIk7xGvlfNzgT1WSobMA-Es,7134
|
|
57
57
|
glaip_sdk/cli/commands/tools/delete.py,sha256=lSACJivmpT4Z7KVWOYVErdcWb487UpnlBpjCrlMI_lM,1696
|
|
58
58
|
glaip_sdk/cli/commands/tools/get.py,sha256=VBexy7ZJI418OCYBGQhn5vUO9r22kTctGrTih78qDa8,3530
|
|
59
59
|
glaip_sdk/cli/commands/tools/list.py,sha256=cHHc5pj-NWJaGXpAgdbtuA1gOrqjecUk83eUOuFrp78,1991
|
|
@@ -71,18 +71,27 @@ glaip_sdk/cli/core/rendering.py,sha256=QgbYzTcKH8wa7-BdR3UgiS3KBx1QYZjDcV2Hyy5ox
|
|
|
71
71
|
glaip_sdk/cli/parsers/__init__.py,sha256=NzLrSH6GOdNoewXtKNpB6GwrauA8rb_IGYV6cz5Hn3o,113
|
|
72
72
|
glaip_sdk/cli/parsers/json_input.py,sha256=kxoxeIlgfsaH2jhe6apZAgSxAtwlpSINLTMRsZZYboQ,5630
|
|
73
73
|
glaip_sdk/cli/slash/__init__.py,sha256=J9TPL2UcNTkW8eifG6nRmAEGHhyEgdYMYk4cHaaObC0,386
|
|
74
|
-
glaip_sdk/cli/slash/accounts_controller.py,sha256
|
|
74
|
+
glaip_sdk/cli/slash/accounts_controller.py,sha256=SceJlc2F2ZdlSDkuWO3Js3akL89bVtQLyGM_oA-F2qI,24928
|
|
75
75
|
glaip_sdk/cli/slash/accounts_shared.py,sha256=Mq5HxlI0YsVEQ0KKISWvyBZhzOFFWCzwRbhF5xwvUbM,2626
|
|
76
76
|
glaip_sdk/cli/slash/agent_session.py,sha256=tuVOme-NbEyr6rwJvsBEKZYWQmsaRf4piJeRvIGu0ns,11384
|
|
77
77
|
glaip_sdk/cli/slash/prompt.py,sha256=q4f1c2zr7ZMUeO6AgOBF2Nz4qgMOXrVPt6WzPRQMbAM,8501
|
|
78
78
|
glaip_sdk/cli/slash/remote_runs_controller.py,sha256=a5X5rYgb9l6dHhvTewRUCj-hAo7mKRnuM_MwGvxs8jI,21363
|
|
79
|
-
glaip_sdk/cli/slash/session.py,sha256=
|
|
80
|
-
glaip_sdk/cli/slash/tui/__init__.py,sha256=
|
|
81
|
-
glaip_sdk/cli/slash/tui/accounts.tcss,sha256=
|
|
82
|
-
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=
|
|
79
|
+
glaip_sdk/cli/slash/session.py,sha256=Zn2hXND_Tfameh_PI8g4VIMd7GPWxwhtPNMN9p6cF7M,65081
|
|
80
|
+
glaip_sdk/cli/slash/tui/__init__.py,sha256=oBUzeoslYwPKVlhqhgg4I7480b77vQNc9ec0NgdTC1s,977
|
|
81
|
+
glaip_sdk/cli/slash/tui/accounts.tcss,sha256=BCjIuTetmVjydv6DCliY38Cze2LUEu7IY44sL5nIuLU,1194
|
|
82
|
+
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=6ihnAnzKD49eeXYW3dYWUAdUEyoXNFwoEoi3kS3WtXM,35999
|
|
83
83
|
glaip_sdk/cli/slash/tui/background_tasks.py,sha256=SAe1mV2vXB3mJcSGhelU950vf8Lifjhws9iomyIVFKw,2422
|
|
84
|
+
glaip_sdk/cli/slash/tui/clipboard.py,sha256=HL_RWIdONyRmDtTYuDzxJTS_mRcLxuR37Ac9Ug5nh40,4730
|
|
85
|
+
glaip_sdk/cli/slash/tui/context.py,sha256=03mo2kgvpyUcNBYz7G2Uyu7X3FJlSUzVoP5Rt9MCZZY,2141
|
|
86
|
+
glaip_sdk/cli/slash/tui/keybind_registry.py,sha256=_rK05BxTxNudYc4iJ9gDxpgeUkjDAq8rarIT-9A-jyM,6739
|
|
84
87
|
glaip_sdk/cli/slash/tui/loading.py,sha256=nW5pv_Tnl9FUOPR3Qf2O5gt1AGHSo3b5-Uofg34F6AE,1909
|
|
85
88
|
glaip_sdk/cli/slash/tui/remote_runs_app.py,sha256=RCrI-c5ilKV6Iy1lz2Aok9xo2Ou02vqcXACMXTdodnE,24716
|
|
89
|
+
glaip_sdk/cli/slash/tui/terminal.py,sha256=iC31XChTL34gXY6vXdSIX3HmD36tuA9EYTPZ2Sn4uOI,12108
|
|
90
|
+
glaip_sdk/cli/slash/tui/toast.py,sha256=LP_myZwgnrdowrRxGK24lMlx7iZt7iOwFhrbc4NW0DY,3493
|
|
91
|
+
glaip_sdk/cli/slash/tui/theme/__init__.py,sha256=rtM2ik83YNCRcI1qh_Sf3rnxco2OvCNNT3NbHY6cLvw,432
|
|
92
|
+
glaip_sdk/cli/slash/tui/theme/catalog.py,sha256=G52eU3h8YI9D8XUALVg1KVZ4Lq65VnZdgPS3F_P7XLE,2544
|
|
93
|
+
glaip_sdk/cli/slash/tui/theme/manager.py,sha256=X600J_WIBM1CHgsQeMFGFPuaVAFCINFcBXFWmeD4B5Q,2707
|
|
94
|
+
glaip_sdk/cli/slash/tui/theme/tokens.py,sha256=ympMRny_d-gHtmnPR-lmNZ-C9SGBy2q-MH81l0L1h-Y,1423
|
|
86
95
|
glaip_sdk/cli/transcript/__init__.py,sha256=yiYHyNtebMCu3BXu56Xm5RBC2tDc865q8UGPnoe6QRs,920
|
|
87
96
|
glaip_sdk/cli/transcript/cache.py,sha256=Wi1uln6HP1U6F-MRTrfnxi9bn6XJTxwWXhREIRPoMqQ,17439
|
|
88
97
|
glaip_sdk/cli/transcript/capture.py,sha256=t8j_62cC6rhb51oCluZd17N04vcXqyjkhPRcRd3ZcmM,10291
|
|
@@ -91,7 +100,6 @@ glaip_sdk/cli/transcript/history.py,sha256=IAUaY41QCr9jKgQ1t8spDJiO3Me5r1vAoTX47
|
|
|
91
100
|
glaip_sdk/cli/transcript/launcher.py,sha256=z5ivkPXDQJpATIqtRLUK8jH3p3WIZ72PvOPqYRDMJvw,2327
|
|
92
101
|
glaip_sdk/cli/transcript/viewer.py,sha256=Y4G40WR6v1g4TfxRbGSZqdrqhLcqBxoWkQgToQoGGxM,13198
|
|
93
102
|
glaip_sdk/client/__init__.py,sha256=s2REOumgE8Z8lA9dWJpwXqpgMdzSELSuCQkZ7sYngX0,381
|
|
94
|
-
glaip_sdk/client/_agent_payloads.py,sha256=ElpukrTQo2mUrtQ5TrIPIxuoAHnk8NXYaRP7s7dUpGg,1423
|
|
95
103
|
glaip_sdk/client/_schedule_payloads.py,sha256=9BXa75CCx3clsKgwmG9AWyvhPY6kVwzQtoLvTTw40CQ,2759
|
|
96
104
|
glaip_sdk/client/agent_runs.py,sha256=tZSFEZZ3Yx0uYRgnwkLe-X0TlmgKJQ-ivzb6SrVnxY8,4862
|
|
97
105
|
glaip_sdk/client/agents.py,sha256=jR6DmpW1XRvpa_ewtXfzOH-q2ipTIF--yHZDdasq5Mk,48249
|
|
@@ -101,7 +109,7 @@ glaip_sdk/client/mcps.py,sha256=-JdaIkg0QE3egJ8p93eoOPULup8KbM2WRCcwlvqlqrA,1449
|
|
|
101
109
|
glaip_sdk/client/run_rendering.py,sha256=kERp78v50jojsNWHrjNEkbC8sgOpMacaqUdw5YZuK6A,26074
|
|
102
110
|
glaip_sdk/client/schedules.py,sha256=ZfPzCYzk4YRuPkjkTTgLe5Rqa07mi-h2WmP4H91mMZ0,14113
|
|
103
111
|
glaip_sdk/client/shared.py,sha256=esHlsR0LEfL-pFDaWebQjKKOLl09jsRY-2pllBUn4nU,522
|
|
104
|
-
glaip_sdk/client/tools.py,sha256=
|
|
112
|
+
glaip_sdk/client/tools.py,sha256=NzQTIsn-bjYN9EfGWCBqqawCIVs7auaccFv7BM_3oCc,23871
|
|
105
113
|
glaip_sdk/client/validators.py,sha256=ioF9VCs-LG2yLkaRDd7Hff74lojDZZ0_Q3CiLbdm1RY,8381
|
|
106
114
|
glaip_sdk/client/payloads/agent/__init__.py,sha256=gItEH2zt2secVq6n60oGA-ztdE5mc0GLECn-QMX47ew,558
|
|
107
115
|
glaip_sdk/client/payloads/agent/requests.py,sha256=5FuGEuypaEXlWBhB07JrDca_ecLg4bvo8mjyFBxAV9U,17139
|
|
@@ -124,7 +132,7 @@ glaip_sdk/registry/__init__.py,sha256=mjvElYE-wwmbriGe-c6qy4on0ccEuWxW_EWWrSbptC
|
|
|
124
132
|
glaip_sdk/registry/agent.py,sha256=F0axW4BIUODqnttIOzxnoS5AqQkLZ1i48FTeZNnYkhA,5203
|
|
125
133
|
glaip_sdk/registry/base.py,sha256=0x2ZBhiERGUcf9mQeWlksSYs5TxDG6FxBYQToYZa5D4,4143
|
|
126
134
|
glaip_sdk/registry/mcp.py,sha256=kNJmiijIbZL9Btx5o2tFtbaT-WG6O4Xf_nl3wz356Ow,7978
|
|
127
|
-
glaip_sdk/registry/tool.py,sha256=
|
|
135
|
+
glaip_sdk/registry/tool.py,sha256=QnbAlk09lYvEb9PEdCsvpg4CGxlLbvvFWBS8WkM1ZoM,12955
|
|
128
136
|
glaip_sdk/runner/__init__.py,sha256=8RrngoGfpF8x9X27RPdX4gJjch75ZvhtVt_6UV0ULLQ,1615
|
|
129
137
|
glaip_sdk/runner/base.py,sha256=KIjcSAyDCP9_mn2H4rXR5gu1FZlwD9pe0gkTBmr6Yi4,2663
|
|
130
138
|
glaip_sdk/runner/deps.py,sha256=Du3hr2R5RHOYCRAv7RVmx661x-ayVXIeZ8JD7ODirTA,3884
|
|
@@ -192,8 +200,8 @@ glaip_sdk/utils/rendering/steps/format.py,sha256=Chnq7OBaj8XMeBntSBxrX5zSmrYeGcO
|
|
|
192
200
|
glaip_sdk/utils/rendering/steps/manager.py,sha256=BiBmTeQMQhjRMykgICXsXNYh1hGsss-fH9BIGVMWFi0,13194
|
|
193
201
|
glaip_sdk/utils/rendering/viewer/__init__.py,sha256=XrxmE2cMAozqrzo1jtDFm8HqNtvDcYi2mAhXLXn5CjI,457
|
|
194
202
|
glaip_sdk/utils/rendering/viewer/presenter.py,sha256=mlLMTjnyeyPVtsyrAbz1BJu9lFGQSlS-voZ-_Cuugv0,5725
|
|
195
|
-
glaip_sdk-0.
|
|
196
|
-
glaip_sdk-0.
|
|
197
|
-
glaip_sdk-0.
|
|
198
|
-
glaip_sdk-0.
|
|
199
|
-
glaip_sdk-0.
|
|
203
|
+
glaip_sdk-0.7.1.dist-info/METADATA,sha256=Pb8KaBV3ypCfgVvXTZd8mWSP_MgdaqFFdUTEmgPal5I,8365
|
|
204
|
+
glaip_sdk-0.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
205
|
+
glaip_sdk-0.7.1.dist-info/entry_points.txt,sha256=65vNPUggyYnVGhuw7RhNJ8Fp2jygTcX0yxJBcBY3iLU,48
|
|
206
|
+
glaip_sdk-0.7.1.dist-info/top_level.txt,sha256=td7yXttiYX2s94-4wFhv-5KdT0rSZ-pnJRSire341hw,10
|
|
207
|
+
glaip_sdk-0.7.1.dist-info/RECORD,,
|