glaip-sdk 0.0.1b5__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 +12 -0
- glaip_sdk/cli/__init__.py +9 -0
- glaip_sdk/cli/commands/__init__.py +5 -0
- glaip_sdk/cli/commands/agents.py +415 -0
- glaip_sdk/cli/commands/configure.py +316 -0
- glaip_sdk/cli/commands/init.py +168 -0
- glaip_sdk/cli/commands/mcps.py +473 -0
- glaip_sdk/cli/commands/models.py +52 -0
- glaip_sdk/cli/commands/tools.py +309 -0
- glaip_sdk/cli/config.py +592 -0
- glaip_sdk/cli/main.py +298 -0
- glaip_sdk/cli/utils.py +733 -0
- glaip_sdk/client/__init__.py +179 -0
- glaip_sdk/client/agents.py +441 -0
- glaip_sdk/client/base.py +223 -0
- glaip_sdk/client/mcps.py +94 -0
- glaip_sdk/client/tools.py +193 -0
- glaip_sdk/client/validators.py +166 -0
- glaip_sdk/config/constants.py +28 -0
- glaip_sdk/exceptions.py +93 -0
- glaip_sdk/models.py +190 -0
- glaip_sdk/utils/__init__.py +95 -0
- glaip_sdk/utils/run_renderer.py +1009 -0
- glaip_sdk/utils.py +167 -0
- glaip_sdk-0.0.1b5.dist-info/METADATA +633 -0
- glaip_sdk-0.0.1b5.dist-info/RECORD +28 -0
- glaip_sdk-0.0.1b5.dist-info/WHEEL +4 -0
- glaip_sdk-0.0.1b5.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Tool management commands.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
|
|
13
|
+
from glaip_sdk.utils import is_uuid
|
|
14
|
+
|
|
15
|
+
from ..utils import (
|
|
16
|
+
get_client,
|
|
17
|
+
handle_ambiguous_resource,
|
|
18
|
+
output_flags,
|
|
19
|
+
output_list,
|
|
20
|
+
output_result,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.group(name="tools", no_args_is_help=True)
|
|
27
|
+
def tools_group():
|
|
28
|
+
"""Tool management operations."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _resolve_tool(ctx, client, ref, select=None):
|
|
33
|
+
"""Resolve tool reference (ID or name) with ambiguity handling."""
|
|
34
|
+
if is_uuid(ref):
|
|
35
|
+
return client.get_tool(ref)
|
|
36
|
+
|
|
37
|
+
# Use find_tools for name-based resolution
|
|
38
|
+
matches = client.find_tools(name=ref)
|
|
39
|
+
if not matches:
|
|
40
|
+
raise click.ClickException(f"Tool not found: {ref}")
|
|
41
|
+
|
|
42
|
+
if len(matches) == 1:
|
|
43
|
+
return matches[0]
|
|
44
|
+
elif len(matches) > 1:
|
|
45
|
+
if select:
|
|
46
|
+
idx = int(select) - 1
|
|
47
|
+
if not (0 <= idx < len(matches)):
|
|
48
|
+
raise click.ClickException(f"--select must be 1..{len(matches)}")
|
|
49
|
+
return matches[idx]
|
|
50
|
+
return handle_ambiguous_resource(ctx, "tool", ref, matches)
|
|
51
|
+
else:
|
|
52
|
+
raise click.ClickException(f"Tool not found: {ref}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@tools_group.command(name="list")
|
|
56
|
+
@output_flags()
|
|
57
|
+
@click.pass_context
|
|
58
|
+
def list_tools(ctx):
|
|
59
|
+
"""List all tools."""
|
|
60
|
+
try:
|
|
61
|
+
client = get_client(ctx)
|
|
62
|
+
tools = client.list_tools()
|
|
63
|
+
|
|
64
|
+
# Define table columns: (data_key, header, style, width)
|
|
65
|
+
columns = [
|
|
66
|
+
("id", "ID", "dim", 36),
|
|
67
|
+
("name", "Name", "cyan", None),
|
|
68
|
+
("framework", "Framework", "blue", None),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
# Transform function for safe dictionary access
|
|
72
|
+
def transform_tool(tool):
|
|
73
|
+
# Handle both dict and object formats
|
|
74
|
+
if isinstance(tool, dict):
|
|
75
|
+
return {
|
|
76
|
+
"id": str(tool.get("id", "N/A")),
|
|
77
|
+
"name": tool.get("name", "N/A"),
|
|
78
|
+
"framework": tool.get("framework", "N/A"),
|
|
79
|
+
}
|
|
80
|
+
else:
|
|
81
|
+
# Fallback to attribute access
|
|
82
|
+
return {
|
|
83
|
+
"id": str(getattr(tool, "id", "N/A")),
|
|
84
|
+
"name": getattr(tool, "name", "N/A"),
|
|
85
|
+
"framework": getattr(tool, "framework", "N/A"),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
output_list(ctx, tools, "🔧 Available Tools", columns, transform_tool)
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise click.ClickException(str(e))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@tools_group.command()
|
|
95
|
+
@click.option(
|
|
96
|
+
"--file",
|
|
97
|
+
type=click.Path(exists=True),
|
|
98
|
+
help="Tool file to upload (optional for metadata-only tools)",
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"--name",
|
|
102
|
+
help="Tool name (required for metadata-only tools, extracted from script if file provided)",
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
"--description",
|
|
106
|
+
help="Tool description (optional - extracted from script if file provided)",
|
|
107
|
+
)
|
|
108
|
+
@click.option(
|
|
109
|
+
"--tags",
|
|
110
|
+
help="Comma-separated tags for the tool",
|
|
111
|
+
)
|
|
112
|
+
@output_flags()
|
|
113
|
+
@click.pass_context
|
|
114
|
+
def create(ctx, file, name, description, tags):
|
|
115
|
+
"""Create a new tool."""
|
|
116
|
+
try:
|
|
117
|
+
client = get_client(ctx)
|
|
118
|
+
|
|
119
|
+
# Validate required parameters based on creation method
|
|
120
|
+
if not file:
|
|
121
|
+
# Metadata-only tool creation
|
|
122
|
+
if not name:
|
|
123
|
+
raise click.ClickException(
|
|
124
|
+
"--name is required when creating metadata-only tools"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Create tool based on whether file is provided
|
|
128
|
+
if file:
|
|
129
|
+
# File-based tool creation - use create_tool_from_code for proper plugin processing
|
|
130
|
+
with open(file, encoding="utf-8") as f:
|
|
131
|
+
code_content = f.read()
|
|
132
|
+
|
|
133
|
+
# Extract name from file if not provided
|
|
134
|
+
if not name:
|
|
135
|
+
import os
|
|
136
|
+
|
|
137
|
+
name = os.path.splitext(os.path.basename(file))[0]
|
|
138
|
+
|
|
139
|
+
# Create tool plugin using the upload endpoint
|
|
140
|
+
tool = client.create_tool_from_code(name, code_content)
|
|
141
|
+
else:
|
|
142
|
+
# Metadata-only tool creation
|
|
143
|
+
tool_kwargs = {}
|
|
144
|
+
if name:
|
|
145
|
+
tool_kwargs["name"] = name
|
|
146
|
+
if description:
|
|
147
|
+
tool_kwargs["description"] = description
|
|
148
|
+
if tags:
|
|
149
|
+
tool_kwargs["tags"] = [tag.strip() for tag in tags.split(",")]
|
|
150
|
+
|
|
151
|
+
tool = client.create_tool(**tool_kwargs)
|
|
152
|
+
|
|
153
|
+
if ctx.obj.get("view") == "json":
|
|
154
|
+
click.echo(json.dumps(tool.model_dump(), indent=2))
|
|
155
|
+
else:
|
|
156
|
+
# Rich output
|
|
157
|
+
creation_method = (
|
|
158
|
+
"file upload (custom)" if file else "metadata only (native)"
|
|
159
|
+
)
|
|
160
|
+
panel = Panel(
|
|
161
|
+
f"[green]✅ Tool '{tool.name}' created successfully via {creation_method}![/green]\n\n"
|
|
162
|
+
f"ID: {tool.id}\n"
|
|
163
|
+
f"Framework: langchain (default)\n"
|
|
164
|
+
f"Type: {'custom' if file else 'native'} (auto-detected)\n"
|
|
165
|
+
f"Description: {getattr(tool, 'description', 'No description')}",
|
|
166
|
+
title="🔧 Tool Created",
|
|
167
|
+
border_style="green",
|
|
168
|
+
)
|
|
169
|
+
console.print(panel)
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
if ctx.obj.get("view") == "json":
|
|
173
|
+
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
174
|
+
else:
|
|
175
|
+
console.print(f"[red]Error creating tool: {e}[/red]")
|
|
176
|
+
raise click.ClickException(str(e))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@tools_group.command()
|
|
180
|
+
@click.argument("tool_ref")
|
|
181
|
+
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
182
|
+
@output_flags()
|
|
183
|
+
@click.pass_context
|
|
184
|
+
def get(ctx, tool_ref, select):
|
|
185
|
+
"""Get tool details."""
|
|
186
|
+
try:
|
|
187
|
+
client = get_client(ctx)
|
|
188
|
+
|
|
189
|
+
# Resolve tool with ambiguity handling
|
|
190
|
+
tool = _resolve_tool(ctx, client, tool_ref, select)
|
|
191
|
+
|
|
192
|
+
# Create result data with all available fields from backend
|
|
193
|
+
result_data = {
|
|
194
|
+
"id": str(getattr(tool, "id", "N/A")),
|
|
195
|
+
"name": getattr(tool, "name", "N/A"),
|
|
196
|
+
"tool_type": getattr(tool, "tool_type", "N/A"),
|
|
197
|
+
"framework": getattr(tool, "framework", "N/A"),
|
|
198
|
+
"version": getattr(tool, "version", "N/A"),
|
|
199
|
+
"description": getattr(tool, "description", "N/A"),
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
output_result(
|
|
203
|
+
ctx, result_data, title="Tool Details", panel_title=f"🔧 {tool.name}"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
raise click.ClickException(str(e))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@tools_group.command()
|
|
211
|
+
@click.argument("tool_id")
|
|
212
|
+
@click.option(
|
|
213
|
+
"--file", type=click.Path(exists=True), help="New tool file for code update"
|
|
214
|
+
)
|
|
215
|
+
@click.option("--description", help="New description")
|
|
216
|
+
@click.option("--tags", help="Comma-separated tags")
|
|
217
|
+
@output_flags()
|
|
218
|
+
@click.pass_context
|
|
219
|
+
def update(ctx, tool_id, file, description, tags):
|
|
220
|
+
"""Update a tool (code or metadata)."""
|
|
221
|
+
try:
|
|
222
|
+
client = get_client(ctx)
|
|
223
|
+
|
|
224
|
+
# Get tool by ID (no ambiguity handling needed)
|
|
225
|
+
try:
|
|
226
|
+
tool = client.get_tool_by_id(tool_id)
|
|
227
|
+
except Exception as e:
|
|
228
|
+
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
229
|
+
|
|
230
|
+
update_data = {}
|
|
231
|
+
|
|
232
|
+
if description:
|
|
233
|
+
update_data["description"] = description
|
|
234
|
+
|
|
235
|
+
if tags:
|
|
236
|
+
update_data["tags"] = [tag.strip() for tag in tags.split(",")]
|
|
237
|
+
|
|
238
|
+
if file:
|
|
239
|
+
# Update code
|
|
240
|
+
updated_tool = tool.update(file_path=file)
|
|
241
|
+
if ctx.obj.get("view") != "json":
|
|
242
|
+
console.print(f"[green]✓[/green] Tool code updated from {file}")
|
|
243
|
+
elif update_data:
|
|
244
|
+
# Update metadata
|
|
245
|
+
updated_tool = tool.update(**update_data)
|
|
246
|
+
if ctx.obj.get("view") != "json":
|
|
247
|
+
console.print("[green]✓[/green] Tool metadata updated")
|
|
248
|
+
else:
|
|
249
|
+
if ctx.obj.get("view") != "json":
|
|
250
|
+
console.print("[yellow]No updates specified[/yellow]")
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
if ctx.obj.get("view") == "json":
|
|
254
|
+
click.echo(json.dumps(updated_tool.model_dump(), indent=2))
|
|
255
|
+
else:
|
|
256
|
+
console.print(
|
|
257
|
+
f"[green]✅ Tool '{updated_tool.name}' updated successfully[/green]"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
if ctx.obj.get("view") == "json":
|
|
262
|
+
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
263
|
+
else:
|
|
264
|
+
console.print(f"[red]Error updating tool: {e}[/red]")
|
|
265
|
+
raise click.ClickException(str(e))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@tools_group.command()
|
|
269
|
+
@click.argument("tool_id")
|
|
270
|
+
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation")
|
|
271
|
+
@output_flags()
|
|
272
|
+
@click.pass_context
|
|
273
|
+
def delete(ctx, tool_id, yes):
|
|
274
|
+
"""Delete a tool."""
|
|
275
|
+
try:
|
|
276
|
+
client = get_client(ctx)
|
|
277
|
+
|
|
278
|
+
# Get tool by ID (no ambiguity handling needed)
|
|
279
|
+
try:
|
|
280
|
+
tool = client.get_tool_by_id(tool_id)
|
|
281
|
+
except Exception as e:
|
|
282
|
+
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}")
|
|
283
|
+
|
|
284
|
+
# Confirm deletion
|
|
285
|
+
if not yes and not click.confirm(
|
|
286
|
+
f"Are you sure you want to delete tool '{tool.name}'?"
|
|
287
|
+
):
|
|
288
|
+
if ctx.obj.get("view") != "json":
|
|
289
|
+
console.print("Deletion cancelled.")
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
tool.delete()
|
|
293
|
+
|
|
294
|
+
if ctx.obj.get("view") == "json":
|
|
295
|
+
click.echo(
|
|
296
|
+
json.dumps(
|
|
297
|
+
{"success": True, "message": f"Tool '{tool.name}' deleted"},
|
|
298
|
+
indent=2,
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
else:
|
|
302
|
+
console.print(f"[green]✅ Tool '{tool.name}' deleted successfully[/green]")
|
|
303
|
+
|
|
304
|
+
except Exception as e:
|
|
305
|
+
if ctx.obj.get("view") == "json":
|
|
306
|
+
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
307
|
+
else:
|
|
308
|
+
console.print(f"[red]Error deleting tool: {e}[/red]")
|
|
309
|
+
raise click.ClickException(str(e))
|