glaip-sdk 0.0.1b10__py3-none-any.whl → 0.0.3__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 +2 -2
- glaip_sdk/_version.py +51 -0
- glaip_sdk/cli/commands/agents.py +201 -109
- glaip_sdk/cli/commands/configure.py +29 -87
- glaip_sdk/cli/commands/init.py +16 -7
- glaip_sdk/cli/commands/mcps.py +73 -153
- glaip_sdk/cli/commands/tools.py +185 -49
- glaip_sdk/cli/main.py +30 -27
- glaip_sdk/cli/utils.py +126 -13
- glaip_sdk/client/__init__.py +54 -2
- glaip_sdk/client/agents.py +175 -237
- glaip_sdk/client/base.py +62 -2
- glaip_sdk/client/mcps.py +63 -20
- glaip_sdk/client/tools.py +95 -28
- glaip_sdk/config/constants.py +10 -3
- glaip_sdk/exceptions.py +13 -0
- glaip_sdk/models.py +20 -4
- glaip_sdk/utils/__init__.py +116 -18
- glaip_sdk/utils/client_utils.py +284 -0
- glaip_sdk/utils/rendering/__init__.py +1 -0
- glaip_sdk/utils/rendering/formatting.py +211 -0
- glaip_sdk/utils/rendering/models.py +53 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +38 -0
- glaip_sdk/utils/rendering/renderer/base.py +827 -0
- glaip_sdk/utils/rendering/renderer/config.py +33 -0
- glaip_sdk/utils/rendering/renderer/console.py +54 -0
- glaip_sdk/utils/rendering/renderer/debug.py +82 -0
- glaip_sdk/utils/rendering/renderer/panels.py +123 -0
- glaip_sdk/utils/rendering/renderer/progress.py +118 -0
- glaip_sdk/utils/rendering/renderer/stream.py +198 -0
- glaip_sdk/utils/rendering/steps.py +168 -0
- glaip_sdk/utils/run_renderer.py +22 -1086
- {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/METADATA +9 -37
- glaip_sdk-0.0.3.dist-info/RECORD +40 -0
- glaip_sdk/cli/config.py +0 -592
- glaip_sdk/utils.py +0 -167
- glaip_sdk-0.0.1b10.dist-info/RECORD +0 -28
- {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/commands/init.py
CHANGED
|
@@ -13,6 +13,8 @@ import yaml
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
from rich.panel import Panel
|
|
15
15
|
|
|
16
|
+
from glaip_sdk import Client
|
|
17
|
+
|
|
16
18
|
console = Console()
|
|
17
19
|
|
|
18
20
|
|
|
@@ -50,14 +52,22 @@ def init_command(no_scaffold, no_demo):
|
|
|
50
52
|
|
|
51
53
|
# Create config directory
|
|
52
54
|
config_dir = Path.home() / ".aip"
|
|
53
|
-
|
|
55
|
+
try:
|
|
56
|
+
config_dir.mkdir(exist_ok=True)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
console.print(f"⚠️ Warning: Could not create config directory: {e}")
|
|
59
|
+
return
|
|
54
60
|
|
|
55
61
|
# Save configuration
|
|
56
62
|
config = {"api_url": api_url, "api_key": api_key}
|
|
57
63
|
|
|
58
64
|
config_file = config_dir / "config.yaml"
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
try:
|
|
66
|
+
with open(config_file, "w") as f:
|
|
67
|
+
yaml.dump(config, f, default_flow_style=False)
|
|
68
|
+
except Exception as e:
|
|
69
|
+
console.print(f"⚠️ Warning: Could not save configuration: {e}")
|
|
70
|
+
return
|
|
61
71
|
|
|
62
72
|
# Set secure file permissions (0600) - best effort on all platforms
|
|
63
73
|
try:
|
|
@@ -77,8 +87,6 @@ def init_command(no_scaffold, no_demo):
|
|
|
77
87
|
create_sample_resources = input("> ").strip().lower()
|
|
78
88
|
if create_sample_resources in ["", "y", "yes"]:
|
|
79
89
|
try:
|
|
80
|
-
from glaip_sdk import Client
|
|
81
|
-
|
|
82
90
|
# Set environment variables
|
|
83
91
|
os.environ["AIP_API_URL"] = api_url
|
|
84
92
|
os.environ["AIP_API_KEY"] = api_key
|
|
@@ -127,8 +135,6 @@ def init_command(no_scaffold, no_demo):
|
|
|
127
135
|
def launch_interactive_demo():
|
|
128
136
|
"""Launch interactive demo with sample agent."""
|
|
129
137
|
try:
|
|
130
|
-
from glaip_sdk import Client
|
|
131
|
-
|
|
132
138
|
console.print(
|
|
133
139
|
Panel(
|
|
134
140
|
"[bold green]Interactive Demo[/bold green]\nType to talk to hello-world. Ctrl+C to exit.",
|
|
@@ -152,6 +158,9 @@ def launch_interactive_demo():
|
|
|
152
158
|
if user_input.lower() in ["exit", "quit", "bye"]:
|
|
153
159
|
break
|
|
154
160
|
|
|
161
|
+
if not user_input: # Skip empty inputs
|
|
162
|
+
continue
|
|
163
|
+
|
|
155
164
|
response = agent.run(user_input)
|
|
156
165
|
console.print(f"🤖 {response}")
|
|
157
166
|
|
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -11,14 +11,13 @@ from rich.console import Console
|
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
from glaip_sdk.utils import is_uuid
|
|
15
|
-
|
|
16
14
|
from ..utils import (
|
|
15
|
+
coerce_to_row,
|
|
17
16
|
get_client,
|
|
18
|
-
handle_ambiguous_resource,
|
|
19
17
|
output_flags,
|
|
20
18
|
output_list,
|
|
21
19
|
output_result,
|
|
20
|
+
resolve_resource,
|
|
22
21
|
)
|
|
23
22
|
|
|
24
23
|
console = Console()
|
|
@@ -32,25 +31,14 @@ def mcps_group():
|
|
|
32
31
|
|
|
33
32
|
def _resolve_mcp(ctx, client, ref, select=None):
|
|
34
33
|
"""Resolve MCP reference (ID or name) with ambiguity handling."""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if len(matches) == 1:
|
|
44
|
-
return matches[0]
|
|
45
|
-
|
|
46
|
-
# Multiple matches - handle ambiguity
|
|
47
|
-
if select:
|
|
48
|
-
idx = int(select) - 1
|
|
49
|
-
if not (0 <= idx < len(matches)):
|
|
50
|
-
raise click.ClickException(f"--select must be 1..{len(matches)}")
|
|
51
|
-
return matches[idx]
|
|
52
|
-
|
|
53
|
-
return handle_ambiguous_resource(ctx, "MCP", ref, matches)
|
|
34
|
+
return resolve_resource(
|
|
35
|
+
ctx,
|
|
36
|
+
ref,
|
|
37
|
+
get_by_id=client.mcps.get_mcp_by_id,
|
|
38
|
+
find_by_name=client.mcps.find_mcps,
|
|
39
|
+
label="MCP",
|
|
40
|
+
select=select,
|
|
41
|
+
)
|
|
54
42
|
|
|
55
43
|
|
|
56
44
|
@mcps_group.command(name="list")
|
|
@@ -71,24 +59,17 @@ def list_mcps(ctx):
|
|
|
71
59
|
|
|
72
60
|
# Transform function for safe dictionary access
|
|
73
61
|
def transform_mcp(mcp):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
"id": str(getattr(mcp, "id", "N/A")),
|
|
87
|
-
"name": getattr(mcp, "name", "N/A"),
|
|
88
|
-
"config": str(getattr(mcp, "config", "N/A"))[:50] + "..."
|
|
89
|
-
if getattr(mcp, "config", None)
|
|
90
|
-
else "N/A",
|
|
91
|
-
}
|
|
62
|
+
row = coerce_to_row(mcp, ["id", "name", "config"])
|
|
63
|
+
# Ensure id is always a string
|
|
64
|
+
row["id"] = str(row["id"])
|
|
65
|
+
# Truncate config field for display
|
|
66
|
+
if row["config"] != "N/A":
|
|
67
|
+
row["config"] = (
|
|
68
|
+
str(row["config"])[:50] + "..."
|
|
69
|
+
if len(str(row["config"])) > 50
|
|
70
|
+
else str(row["config"])
|
|
71
|
+
)
|
|
72
|
+
return row
|
|
92
73
|
|
|
93
74
|
output_list(ctx, mcps, "🔌 Available MCPs", columns, transform_mcp)
|
|
94
75
|
|
|
@@ -118,13 +99,14 @@ def create(ctx, name, transport, description, config):
|
|
|
118
99
|
|
|
119
100
|
mcp = client.mcps.create_mcp(
|
|
120
101
|
name=name,
|
|
121
|
-
type="server", #
|
|
102
|
+
type="server", # MCPs are always server type
|
|
122
103
|
transport=transport,
|
|
123
104
|
description=description,
|
|
124
105
|
config=mcp_config,
|
|
125
106
|
)
|
|
126
107
|
|
|
127
|
-
|
|
108
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
109
|
+
if view == "json":
|
|
128
110
|
click.echo(json.dumps(mcp.model_dump(), indent=2))
|
|
129
111
|
else:
|
|
130
112
|
# Rich output
|
|
@@ -139,7 +121,8 @@ def create(ctx, name, transport, description, config):
|
|
|
139
121
|
console.print(panel)
|
|
140
122
|
|
|
141
123
|
except Exception as e:
|
|
142
|
-
|
|
124
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
125
|
+
if view == "json":
|
|
143
126
|
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
144
127
|
else:
|
|
145
128
|
console.print(f"[red]Error creating MCP: {e}[/red]")
|
|
@@ -155,25 +138,8 @@ def get(ctx, mcp_ref):
|
|
|
155
138
|
try:
|
|
156
139
|
client = get_client(ctx)
|
|
157
140
|
|
|
158
|
-
#
|
|
159
|
-
|
|
160
|
-
mcp = client.mcps.get_mcp_by_id(mcp_ref)
|
|
161
|
-
else:
|
|
162
|
-
# Find MCPs by name
|
|
163
|
-
matches = client.mcps.find_mcps(name=mcp_ref)
|
|
164
|
-
if not matches:
|
|
165
|
-
raise click.ClickException(f"MCP '{mcp_ref}' not found")
|
|
166
|
-
elif len(matches) == 1:
|
|
167
|
-
mcp = matches[0]
|
|
168
|
-
else:
|
|
169
|
-
# Show all matches
|
|
170
|
-
console.print(
|
|
171
|
-
f"[yellow]Multiple MCPs found with name '{mcp_ref}':[/yellow]"
|
|
172
|
-
)
|
|
173
|
-
for i, match in enumerate(matches, 1):
|
|
174
|
-
console.print(f" {i}. {match.name} (ID: {match.id})")
|
|
175
|
-
console.print("[yellow]Use the ID for unambiguous operations.[/yellow]")
|
|
176
|
-
return
|
|
141
|
+
# Resolve MCP using helper function
|
|
142
|
+
mcp = _resolve_mcp(ctx, client, mcp_ref)
|
|
177
143
|
|
|
178
144
|
# Create result data with actual available fields
|
|
179
145
|
result_data = {
|
|
@@ -202,53 +168,32 @@ def list_tools(ctx, mcp_ref):
|
|
|
202
168
|
try:
|
|
203
169
|
client = get_client(ctx)
|
|
204
170
|
|
|
205
|
-
#
|
|
206
|
-
|
|
207
|
-
mcp = client.get_mcp_by_id(mcp_ref)
|
|
208
|
-
else:
|
|
209
|
-
# Find MCPs by name
|
|
210
|
-
matches = client.find_mcps(name=mcp_ref)
|
|
211
|
-
if not matches:
|
|
212
|
-
raise click.ClickException(f"MCP '{mcp_ref}' not found")
|
|
213
|
-
elif len(matches) == 1:
|
|
214
|
-
mcp = matches[0]
|
|
215
|
-
else:
|
|
216
|
-
# Show all matches
|
|
217
|
-
console.print(
|
|
218
|
-
f"[yellow]Multiple MCPs found with name '{mcp_ref}':[/yellow]"
|
|
219
|
-
)
|
|
220
|
-
for i, match in enumerate(matches, 1):
|
|
221
|
-
console.print(f" {i}. {match.name} (ID: {match.id})")
|
|
222
|
-
console.print("[yellow]Use the ID for unambiguous operations.[/yellow]")
|
|
223
|
-
return
|
|
171
|
+
# Resolve MCP using helper function
|
|
172
|
+
mcp = _resolve_mcp(ctx, client, mcp_ref)
|
|
224
173
|
|
|
225
174
|
# Get tools from MCP
|
|
226
175
|
tools = client.mcps.get_mcp_tools(mcp.id)
|
|
227
176
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
show_header=True,
|
|
235
|
-
header_style="bold magenta",
|
|
236
|
-
)
|
|
237
|
-
table.add_column("Name", style="cyan", no_wrap=True)
|
|
238
|
-
table.add_column("Description", style="green")
|
|
239
|
-
table.add_column("Type", style="yellow")
|
|
177
|
+
# Define table columns: (data_key, header, style, width)
|
|
178
|
+
columns = [
|
|
179
|
+
("name", "Name", "cyan", None),
|
|
180
|
+
("description", "Description", "green", 50),
|
|
181
|
+
("type", "Type", "yellow", None),
|
|
182
|
+
]
|
|
240
183
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
184
|
+
# Transform function for safe dictionary access
|
|
185
|
+
def transform_tool(tool):
|
|
186
|
+
return {
|
|
187
|
+
"name": tool.get("name", "N/A"),
|
|
188
|
+
"description": tool.get("description", "N/A")[:47] + "..."
|
|
189
|
+
if len(tool.get("description", "")) > 47
|
|
190
|
+
else tool.get("description", "N/A"),
|
|
191
|
+
"type": tool.get("type", "N/A"),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
output_list(
|
|
195
|
+
ctx, tools, f"🔧 Tools from MCP: {mcp.name}", columns, transform_tool
|
|
196
|
+
)
|
|
252
197
|
|
|
253
198
|
except Exception as e:
|
|
254
199
|
raise click.ClickException(str(e))
|
|
@@ -273,15 +218,17 @@ def tools_from_config(ctx, config_file):
|
|
|
273
218
|
with open(config_file) as f:
|
|
274
219
|
config = json.load(f)
|
|
275
220
|
|
|
276
|
-
|
|
221
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
222
|
+
if view != "json":
|
|
277
223
|
console.print(
|
|
278
224
|
f"[yellow]Fetching tools from MCP config in {config_file}...[/yellow]"
|
|
279
225
|
)
|
|
280
226
|
|
|
281
227
|
# Get tools from MCP config
|
|
282
|
-
tools = client.mcps.
|
|
228
|
+
tools = client.mcps.get_mcp_tools_from_config(config)
|
|
283
229
|
|
|
284
|
-
|
|
230
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
231
|
+
if view == "json":
|
|
285
232
|
click.echo(json.dumps(tools, indent=2))
|
|
286
233
|
else: # rich output
|
|
287
234
|
if tools:
|
|
@@ -328,7 +275,8 @@ def test_connection(ctx, config_file):
|
|
|
328
275
|
with open(config_file) as f:
|
|
329
276
|
config = json.load(f)
|
|
330
277
|
|
|
331
|
-
|
|
278
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
279
|
+
if view != "json":
|
|
332
280
|
console.print(
|
|
333
281
|
f"[yellow]Testing MCP connection with config from {config_file}...[/yellow]"
|
|
334
282
|
)
|
|
@@ -336,7 +284,8 @@ def test_connection(ctx, config_file):
|
|
|
336
284
|
# Test connection using config
|
|
337
285
|
result = client.mcps.test_mcp_connection_from_config(config)
|
|
338
286
|
|
|
339
|
-
|
|
287
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
288
|
+
if view == "json":
|
|
340
289
|
click.echo(json.dumps(result, indent=2))
|
|
341
290
|
else:
|
|
342
291
|
success_panel = Panel(
|
|
@@ -363,25 +312,8 @@ def update(ctx, mcp_ref, name, description, config):
|
|
|
363
312
|
try:
|
|
364
313
|
client = get_client(ctx)
|
|
365
314
|
|
|
366
|
-
#
|
|
367
|
-
|
|
368
|
-
mcp = client.mcps.get_mcp_by_id(mcp_ref)
|
|
369
|
-
else:
|
|
370
|
-
# Find MCPs by name
|
|
371
|
-
matches = client.mcps.find_mcps(name=mcp_ref)
|
|
372
|
-
if not matches:
|
|
373
|
-
raise click.ClickException(f"MCP '{mcp_ref}' not found")
|
|
374
|
-
elif len(matches) == 1:
|
|
375
|
-
mcp = matches[0]
|
|
376
|
-
else:
|
|
377
|
-
# Show all matches
|
|
378
|
-
console.print(
|
|
379
|
-
f"[yellow]Multiple MCPs found with name '{mcp_ref}':[/yellow]"
|
|
380
|
-
)
|
|
381
|
-
for i, match in enumerate(matches, 1):
|
|
382
|
-
console.print(f" {i}. {match.name} (ID: {match.id})")
|
|
383
|
-
console.print("[yellow]Use the ID for unambiguous operations.[/yellow]")
|
|
384
|
-
return
|
|
315
|
+
# Resolve MCP using helper function
|
|
316
|
+
mcp = _resolve_mcp(ctx, client, mcp_ref)
|
|
385
317
|
|
|
386
318
|
# Build update data
|
|
387
319
|
update_data = {}
|
|
@@ -399,9 +331,10 @@ def update(ctx, mcp_ref, name, description, config):
|
|
|
399
331
|
raise click.ClickException("No update fields specified")
|
|
400
332
|
|
|
401
333
|
# Update MCP
|
|
402
|
-
updated_mcp = client.mcps.update_mcp(mcp.id, update_data)
|
|
334
|
+
updated_mcp = client.mcps.update_mcp(mcp.id, **update_data)
|
|
403
335
|
|
|
404
|
-
|
|
336
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
337
|
+
if view == "json":
|
|
405
338
|
click.echo(json.dumps(updated_mcp.model_dump(), indent=2))
|
|
406
339
|
else:
|
|
407
340
|
console.print(
|
|
@@ -409,7 +342,8 @@ def update(ctx, mcp_ref, name, description, config):
|
|
|
409
342
|
)
|
|
410
343
|
|
|
411
344
|
except Exception as e:
|
|
412
|
-
|
|
345
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
346
|
+
if view == "json":
|
|
413
347
|
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
414
348
|
else:
|
|
415
349
|
console.print(f"[red]Error updating MCP: {e}[/red]")
|
|
@@ -426,37 +360,22 @@ def delete(ctx, mcp_ref, yes):
|
|
|
426
360
|
try:
|
|
427
361
|
client = get_client(ctx)
|
|
428
362
|
|
|
429
|
-
#
|
|
430
|
-
|
|
431
|
-
mcp = client.mcps.get_mcp_by_id(mcp_ref)
|
|
432
|
-
else:
|
|
433
|
-
# Find MCPs by name
|
|
434
|
-
matches = client.mcps.find_mcps(name=mcp_ref)
|
|
435
|
-
if not matches:
|
|
436
|
-
raise click.ClickException(f"MCP '{mcp_ref}' not found")
|
|
437
|
-
elif len(matches) == 1:
|
|
438
|
-
mcp = matches[0]
|
|
439
|
-
else:
|
|
440
|
-
# Show all matches
|
|
441
|
-
console.print(
|
|
442
|
-
f"[yellow]Multiple MCPs found with name '{mcp_ref}':[/yellow]"
|
|
443
|
-
)
|
|
444
|
-
for i, match in enumerate(matches, 1):
|
|
445
|
-
console.print(f" {i}. {match.name} (ID: {match.id})")
|
|
446
|
-
console.print("[yellow]Use the ID for unambiguous operations.[/yellow]")
|
|
447
|
-
return
|
|
363
|
+
# Resolve MCP using helper function
|
|
364
|
+
mcp = _resolve_mcp(ctx, client, mcp_ref)
|
|
448
365
|
|
|
449
366
|
# Confirm deletion
|
|
450
367
|
if not yes and not click.confirm(
|
|
451
368
|
f"Are you sure you want to delete MCP '{mcp.name}'?"
|
|
452
369
|
):
|
|
453
|
-
|
|
370
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
371
|
+
if view != "json":
|
|
454
372
|
console.print("Deletion cancelled.")
|
|
455
373
|
return
|
|
456
374
|
|
|
457
375
|
client.mcps.delete_mcp(mcp.id)
|
|
458
376
|
|
|
459
|
-
|
|
377
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
378
|
+
if view == "json":
|
|
460
379
|
click.echo(
|
|
461
380
|
json.dumps(
|
|
462
381
|
{"success": True, "message": f"MCP '{mcp.name}' deleted"}, indent=2
|
|
@@ -466,7 +385,8 @@ def delete(ctx, mcp_ref, yes):
|
|
|
466
385
|
console.print(f"[green]✅ MCP '{mcp.name}' deleted successfully[/green]")
|
|
467
386
|
|
|
468
387
|
except Exception as e:
|
|
469
|
-
|
|
388
|
+
view = (ctx.obj or {}).get("view", "rich")
|
|
389
|
+
if view == "json":
|
|
470
390
|
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
471
391
|
else:
|
|
472
392
|
console.print(f"[red]Error deleting MCP: {e}[/red]")
|