glaip-sdk 0.0.2__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.2.dist-info → glaip_sdk-0.0.3.dist-info}/METADATA +8 -36
- 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.2.dist-info/RECORD +0 -28
- {glaip_sdk-0.0.2.dist-info → glaip_sdk-0.0.3.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.2.dist-info → glaip_sdk-0.0.3.dist-info}/entry_points.txt +0 -0
glaip_sdk/__init__.py
CHANGED
|
@@ -4,9 +4,9 @@ Authors:
|
|
|
4
4
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from ._version import __version__
|
|
7
8
|
from .client import Client
|
|
8
9
|
from .exceptions import AIPError
|
|
9
10
|
from .models import MCP, Agent, Tool
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
__all__ = ["Client", "Agent", "Tool", "MCP", "AIPError"]
|
|
12
|
+
__all__ = ["Client", "Agent", "Tool", "MCP", "AIPError", "__version__"]
|
glaip_sdk/_version.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Central version definition for glaip_sdk.
|
|
2
|
+
|
|
3
|
+
Resolves from installed package metadata to avoid hardcoding.
|
|
4
|
+
Falls back to a dev marker when running from source without installation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version # Python 3.8+
|
|
11
|
+
except Exception: # pragma: no cover - extremely unlikely
|
|
12
|
+
PackageNotFoundError = Exception # type: ignore
|
|
13
|
+
|
|
14
|
+
def version(_: str) -> str: # type: ignore
|
|
15
|
+
return "0.0.0.dev0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import tomllib as _toml # Python 3.11+
|
|
20
|
+
except Exception: # pragma: no cover
|
|
21
|
+
try:
|
|
22
|
+
import tomli as _toml # type: ignore
|
|
23
|
+
except Exception: # pragma: no cover
|
|
24
|
+
_toml = None # type: ignore
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_version() -> str:
|
|
28
|
+
try:
|
|
29
|
+
return version("glaip-sdk")
|
|
30
|
+
except PackageNotFoundError:
|
|
31
|
+
# Not installed; try reading from local pyproject for dev
|
|
32
|
+
if _toml is not None:
|
|
33
|
+
try:
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
here = Path(__file__).resolve()
|
|
37
|
+
root = here.parent.parent # project root (contains pyproject.toml)
|
|
38
|
+
pyproject = root / "pyproject.toml"
|
|
39
|
+
if pyproject.is_file():
|
|
40
|
+
data = _toml.loads(pyproject.read_text(encoding="utf-8"))
|
|
41
|
+
ver = data.get("project", {}).get("version") or data.get(
|
|
42
|
+
"tool", {}
|
|
43
|
+
).get("poetry", {}).get("version")
|
|
44
|
+
if isinstance(ver, str) and ver:
|
|
45
|
+
return ver
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
return "0.0.0.dev0"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__version__ = _get_version()
|
glaip_sdk/cli/commands/agents.py
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
|
-
"""Agent
|
|
1
|
+
"""Agent CLI commands for AIP SDK.
|
|
2
2
|
|
|
3
3
|
Authors:
|
|
4
4
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
|
+
from datetime import datetime
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
from rich.console import Console
|
|
11
12
|
from rich.panel import Panel
|
|
12
|
-
from rich.text import Text
|
|
13
13
|
|
|
14
|
+
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT, DEFAULT_MODEL
|
|
15
|
+
from glaip_sdk.exceptions import AgentTimeoutError
|
|
14
16
|
from glaip_sdk.utils import is_uuid
|
|
15
17
|
|
|
16
18
|
from ..utils import (
|
|
19
|
+
build_renderer,
|
|
20
|
+
coerce_to_row,
|
|
17
21
|
get_client,
|
|
18
|
-
handle_ambiguous_resource,
|
|
19
22
|
output_flags,
|
|
20
23
|
output_list,
|
|
21
24
|
output_result,
|
|
22
|
-
|
|
25
|
+
resolve_resource,
|
|
23
26
|
)
|
|
24
27
|
|
|
25
28
|
console = Console()
|
|
26
29
|
|
|
27
30
|
|
|
31
|
+
def _format_datetime(dt):
|
|
32
|
+
"""Format datetime object to readable string."""
|
|
33
|
+
if isinstance(dt, datetime):
|
|
34
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
35
|
+
elif dt is None:
|
|
36
|
+
return "N/A"
|
|
37
|
+
return dt
|
|
38
|
+
|
|
39
|
+
|
|
28
40
|
@click.group(name="agents", no_args_is_help=True)
|
|
29
41
|
def agents_group():
|
|
30
42
|
"""Agent management operations."""
|
|
@@ -33,25 +45,14 @@ def agents_group():
|
|
|
33
45
|
|
|
34
46
|
def _resolve_agent(ctx, client, ref, select=None):
|
|
35
47
|
"""Resolve agent reference (ID or name) with ambiguity handling."""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if len(matches) == 1:
|
|
45
|
-
return matches[0]
|
|
46
|
-
|
|
47
|
-
# Multiple matches - handle ambiguity
|
|
48
|
-
if select:
|
|
49
|
-
idx = int(select) - 1
|
|
50
|
-
if not (0 <= idx < len(matches)):
|
|
51
|
-
raise click.ClickException(f"--select must be 1..{len(matches)}")
|
|
52
|
-
return matches[idx]
|
|
53
|
-
|
|
54
|
-
return handle_ambiguous_resource(ctx, "agent", ref, matches)
|
|
48
|
+
return resolve_resource(
|
|
49
|
+
ctx,
|
|
50
|
+
ref,
|
|
51
|
+
get_by_id=client.agents.get_agent_by_id,
|
|
52
|
+
find_by_name=client.agents.find_agents,
|
|
53
|
+
label="Agent",
|
|
54
|
+
select=select,
|
|
55
|
+
)
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
@agents_group.command(name="list")
|
|
@@ -74,13 +75,10 @@ def list_agents(ctx):
|
|
|
74
75
|
|
|
75
76
|
# Transform function for safe attribute access
|
|
76
77
|
def transform_agent(agent):
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"framework": safe_getattr(agent, "framework") or "N/A",
|
|
82
|
-
"version": safe_getattr(agent, "version") or "N/A",
|
|
83
|
-
}
|
|
78
|
+
row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
|
|
79
|
+
# Ensure id is always a string
|
|
80
|
+
row["id"] = str(row["id"])
|
|
81
|
+
return row
|
|
84
82
|
|
|
85
83
|
output_list(ctx, agents, "🤖 Available Agents", columns, transform_agent)
|
|
86
84
|
|
|
@@ -101,6 +99,16 @@ def get(ctx, agent_ref, select):
|
|
|
101
99
|
# Resolve agent with ambiguity handling
|
|
102
100
|
agent = _resolve_agent(ctx, client, agent_ref, select)
|
|
103
101
|
|
|
102
|
+
# If resolved by name, it may be a shallow object from list endpoint.
|
|
103
|
+
# Fetch full details by ID to ensure instruction/tools are populated.
|
|
104
|
+
try:
|
|
105
|
+
agent_id = str(getattr(agent, "id", "")).strip()
|
|
106
|
+
if agent_id:
|
|
107
|
+
agent = client.agents.get_agent_by_id(agent_id)
|
|
108
|
+
except Exception:
|
|
109
|
+
# If fetching full details fails, continue with the resolved object.
|
|
110
|
+
pass
|
|
111
|
+
|
|
104
112
|
# Create result data with all available fields from backend
|
|
105
113
|
result_data = {
|
|
106
114
|
"id": str(getattr(agent, "id", "N/A")),
|
|
@@ -110,9 +118,12 @@ def get(ctx, agent_ref, select):
|
|
|
110
118
|
"version": getattr(agent, "version", "N/A"),
|
|
111
119
|
"description": getattr(agent, "description", "N/A"),
|
|
112
120
|
"instruction": getattr(agent, "instruction", "") or "-",
|
|
121
|
+
"created_at": _format_datetime(getattr(agent, "created_at", "N/A")),
|
|
122
|
+
"updated_at": _format_datetime(getattr(agent, "updated_at", "N/A")),
|
|
113
123
|
"metadata": getattr(agent, "metadata", "N/A"),
|
|
114
124
|
"language_model_id": getattr(agent, "language_model_id", "N/A"),
|
|
115
125
|
"agent_config": getattr(agent, "agent_config", "N/A"),
|
|
126
|
+
"tool_configs": agent.tool_configs or {},
|
|
116
127
|
"tools": getattr(agent, "tools", []),
|
|
117
128
|
"agents": getattr(agent, "agents", []),
|
|
118
129
|
"mcps": getattr(agent, "mcps", []),
|
|
@@ -128,27 +139,21 @@ def get(ctx, agent_ref, select):
|
|
|
128
139
|
|
|
129
140
|
|
|
130
141
|
@agents_group.command()
|
|
131
|
-
@click.argument("
|
|
142
|
+
@click.argument("agent_ref")
|
|
143
|
+
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
132
144
|
@click.option("--input", "input_text", required=True, help="Input text for the agent")
|
|
133
145
|
@click.option("--chat-history", help="JSON string of chat history")
|
|
134
|
-
@click.option("--timeout", default=600, type=int, help="Request timeout in seconds")
|
|
135
146
|
@click.option(
|
|
136
|
-
"--
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
help="
|
|
140
|
-
)
|
|
141
|
-
@click.option(
|
|
142
|
-
"--compact/--verbose", default=True, help="Collapse tool steps (default: compact)"
|
|
147
|
+
"--timeout",
|
|
148
|
+
default=DEFAULT_AGENT_RUN_TIMEOUT,
|
|
149
|
+
type=int,
|
|
150
|
+
help="Agent execution timeout in seconds (default: 300s)",
|
|
143
151
|
)
|
|
144
152
|
@click.option(
|
|
145
153
|
"--save",
|
|
146
154
|
type=click.Path(dir_okay=False, writable=True),
|
|
147
155
|
help="Save transcript to file (md or json)",
|
|
148
156
|
)
|
|
149
|
-
@click.option(
|
|
150
|
-
"--theme", type=click.Choice(["dark", "light"]), default="dark", help="Color theme"
|
|
151
|
-
)
|
|
152
157
|
@click.option(
|
|
153
158
|
"--file",
|
|
154
159
|
"files",
|
|
@@ -156,28 +161,30 @@ def get(ctx, agent_ref, select):
|
|
|
156
161
|
type=click.Path(exists=True),
|
|
157
162
|
help="Attach file(s)",
|
|
158
163
|
)
|
|
164
|
+
@click.option(
|
|
165
|
+
"--verbose/--no-verbose",
|
|
166
|
+
default=False,
|
|
167
|
+
help="Show detailed SSE events during streaming",
|
|
168
|
+
)
|
|
169
|
+
@output_flags()
|
|
159
170
|
@click.pass_context
|
|
160
171
|
def run(
|
|
161
172
|
ctx,
|
|
162
|
-
|
|
173
|
+
agent_ref,
|
|
174
|
+
select,
|
|
163
175
|
input_text,
|
|
164
176
|
chat_history,
|
|
165
177
|
timeout,
|
|
166
|
-
view,
|
|
167
|
-
compact,
|
|
168
178
|
save,
|
|
169
|
-
theme,
|
|
170
179
|
files,
|
|
180
|
+
verbose,
|
|
171
181
|
):
|
|
172
|
-
"""Run an agent with input text (ID
|
|
182
|
+
"""Run an agent with input text (ID or name)."""
|
|
173
183
|
try:
|
|
174
184
|
client = get_client(ctx)
|
|
175
185
|
|
|
176
|
-
#
|
|
177
|
-
|
|
178
|
-
agent = client.agents.get_agent_by_id(agent_id)
|
|
179
|
-
except Exception as e:
|
|
180
|
-
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}")
|
|
186
|
+
# Resolve agent by ID or name (align with other commands)
|
|
187
|
+
agent = _resolve_agent(ctx, client, agent_ref, select)
|
|
181
188
|
|
|
182
189
|
# Parse chat history if provided
|
|
183
190
|
parsed_chat_history = None
|
|
@@ -187,85 +194,126 @@ def run(
|
|
|
187
194
|
except json.JSONDecodeError:
|
|
188
195
|
raise click.ClickException("Invalid JSON in chat history")
|
|
189
196
|
|
|
190
|
-
#
|
|
191
|
-
|
|
197
|
+
# Create custom renderer with CLI flags
|
|
198
|
+
tty_enabled = bool((ctx.obj or {}).get("tty", True))
|
|
192
199
|
|
|
193
|
-
#
|
|
194
|
-
renderer =
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
# Build renderer and capturing console
|
|
201
|
+
renderer, working_console = build_renderer(
|
|
202
|
+
ctx,
|
|
203
|
+
save_path=save,
|
|
204
|
+
verbose=verbose,
|
|
205
|
+
tty_enabled=tty_enabled,
|
|
206
|
+
)
|
|
197
207
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
# Set HTTP timeout to match agent timeout exactly
|
|
209
|
+
# This ensures the agent timeout controls the HTTP timeout
|
|
210
|
+
try:
|
|
211
|
+
client.timeout = float(timeout)
|
|
212
|
+
except Exception:
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
# Ensure timeout is applied to the root client and subclients share its session
|
|
216
|
+
run_kwargs = {
|
|
217
|
+
"agent_id": agent.id,
|
|
218
|
+
"message": input_text,
|
|
219
|
+
"files": list(files),
|
|
220
|
+
"agent_name": agent.name, # Pass agent name for better display
|
|
221
|
+
"tty": tty_enabled,
|
|
222
|
+
}
|
|
203
223
|
|
|
204
|
-
#
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
)
|
|
224
|
+
# Add optional parameters
|
|
225
|
+
if parsed_chat_history:
|
|
226
|
+
run_kwargs["chat_history"] = parsed_chat_history
|
|
227
|
+
|
|
228
|
+
# Pass custom renderer if available
|
|
229
|
+
if renderer is not None:
|
|
230
|
+
run_kwargs["renderer"] = renderer
|
|
231
|
+
|
|
232
|
+
# Pass timeout to client (verbose mode is handled by the renderer)
|
|
233
|
+
result = client.agents.run_agent(**run_kwargs, timeout=timeout)
|
|
214
234
|
|
|
215
235
|
# Check if renderer already printed output (for streaming renderers)
|
|
216
|
-
|
|
236
|
+
# Note: Auto-paging is handled by the renderer when view=="rich"
|
|
237
|
+
printed_by_renderer = bool(renderer)
|
|
238
|
+
|
|
239
|
+
# Resolve selected view from context (output_flags() stores it here)
|
|
240
|
+
selected_view = (ctx.obj or {}).get("view", "rich")
|
|
217
241
|
|
|
218
|
-
# Handle output format for
|
|
242
|
+
# Handle output format for fallback
|
|
219
243
|
# Only print here if nothing was printed by the renderer
|
|
220
244
|
if not printed_by_renderer:
|
|
221
|
-
if
|
|
245
|
+
if selected_view == "json":
|
|
222
246
|
click.echo(json.dumps({"output": result}, indent=2))
|
|
223
|
-
elif
|
|
247
|
+
elif selected_view == "md":
|
|
224
248
|
click.echo(f"# Assistant\n\n{result}")
|
|
225
|
-
elif
|
|
249
|
+
elif selected_view == "plain":
|
|
226
250
|
click.echo(result)
|
|
227
|
-
elif not stream:
|
|
228
|
-
# Rich output for non-streaming
|
|
229
|
-
panel = Panel(
|
|
230
|
-
Text(result, style="green"),
|
|
231
|
-
title="Agent Output",
|
|
232
|
-
border_style="green",
|
|
233
|
-
)
|
|
234
|
-
console.print(panel)
|
|
235
251
|
|
|
236
252
|
# Save transcript if requested
|
|
237
|
-
if save
|
|
253
|
+
if save:
|
|
238
254
|
ext = (save.rsplit(".", 1)[-1] or "").lower()
|
|
239
255
|
if ext == "json":
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
256
|
+
# Save both the result and captured output
|
|
257
|
+
save_data = {
|
|
258
|
+
"output": result or "",
|
|
259
|
+
"full_debug_output": getattr(
|
|
260
|
+
working_console, "get_captured_output", lambda: ""
|
|
261
|
+
)(),
|
|
262
|
+
"timestamp": "captured during agent execution",
|
|
263
|
+
}
|
|
264
|
+
content = json.dumps(save_data, indent=2)
|
|
243
265
|
else:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
266
|
+
# For markdown/text files, save the full captured output if available
|
|
267
|
+
# Get the full captured output including all tool panels and debug info (if available)
|
|
268
|
+
full_output = getattr(
|
|
269
|
+
working_console, "get_captured_output", lambda: ""
|
|
270
|
+
)()
|
|
271
|
+
if full_output:
|
|
272
|
+
content = f"# Agent Debug Log\n\n{full_output}\n\n---\n\n## Final Result\n\n{result or ''}\n"
|
|
273
|
+
else:
|
|
274
|
+
# Fallback to simple format
|
|
275
|
+
content = f"# Assistant\n\n{result or ''}\n"
|
|
276
|
+
|
|
277
|
+
with open(save, "w", encoding="utf-8") as f:
|
|
278
|
+
f.write(content)
|
|
279
|
+
console.print(f"[green]Full debug output saved to: {save}[/green]")
|
|
280
|
+
|
|
281
|
+
except AgentTimeoutError as e:
|
|
282
|
+
# Handle agent timeout errors with specific messages
|
|
283
|
+
error_msg = str(e)
|
|
284
|
+
if ctx.obj.get("view") == "json":
|
|
285
|
+
click.echo(json.dumps({"error": error_msg}, indent=2))
|
|
286
|
+
# Don't print the error message here - Click.ClickException will handle it
|
|
287
|
+
raise click.ClickException(error_msg)
|
|
249
288
|
except Exception as e:
|
|
250
289
|
if ctx.obj.get("view") == "json":
|
|
251
290
|
click.echo(json.dumps({"error": str(e)}, indent=2))
|
|
252
|
-
|
|
253
|
-
console.print(f"[red]Error running agent: {e}[/red]")
|
|
291
|
+
# Don't print the error message here - Click.ClickException will handle it
|
|
254
292
|
raise click.ClickException(str(e))
|
|
255
293
|
|
|
256
294
|
|
|
257
295
|
@agents_group.command()
|
|
258
296
|
@click.option("--name", required=True, help="Agent name")
|
|
259
297
|
@click.option("--instruction", required=True, help="Agent instruction (prompt)")
|
|
298
|
+
@click.option(
|
|
299
|
+
"--model",
|
|
300
|
+
help=f"Language model to use (e.g., {DEFAULT_MODEL}, default: {DEFAULT_MODEL})",
|
|
301
|
+
)
|
|
260
302
|
@click.option("--tools", multiple=True, help="Tool names or IDs to attach")
|
|
261
|
-
@click.option("--agents", multiple=True, help="Sub-agent names to attach")
|
|
262
|
-
@click.option(
|
|
303
|
+
@click.option("--agents", multiple=True, help="Sub-agent names or IDs to attach")
|
|
304
|
+
@click.option(
|
|
305
|
+
"--timeout",
|
|
306
|
+
default=DEFAULT_AGENT_RUN_TIMEOUT,
|
|
307
|
+
type=int,
|
|
308
|
+
help="Agent execution timeout in seconds (default: 300s)",
|
|
309
|
+
)
|
|
263
310
|
@output_flags()
|
|
264
311
|
@click.pass_context
|
|
265
312
|
def create(
|
|
266
313
|
ctx,
|
|
267
314
|
name,
|
|
268
315
|
instruction,
|
|
316
|
+
model,
|
|
269
317
|
tools,
|
|
270
318
|
agents,
|
|
271
319
|
timeout,
|
|
@@ -273,14 +321,57 @@ def create(
|
|
|
273
321
|
"""Create a new agent."""
|
|
274
322
|
try:
|
|
275
323
|
client = get_client(ctx)
|
|
276
|
-
|
|
277
|
-
agent
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
324
|
+
|
|
325
|
+
# Resolve tool and agent references: accept names or IDs
|
|
326
|
+
def _resolve_tools(items: tuple[str, ...]) -> list[str]:
|
|
327
|
+
out: list[str] = []
|
|
328
|
+
for ref in list(items or ()): # tuple -> list
|
|
329
|
+
if is_uuid(ref):
|
|
330
|
+
out.append(ref)
|
|
331
|
+
continue
|
|
332
|
+
matches = client.find_tools(name=ref)
|
|
333
|
+
if not matches:
|
|
334
|
+
raise click.ClickException(f"Tool not found: {ref}")
|
|
335
|
+
if len(matches) > 1:
|
|
336
|
+
raise click.ClickException(
|
|
337
|
+
f"Multiple tools named '{ref}'. Use ID instead."
|
|
338
|
+
)
|
|
339
|
+
out.append(str(matches[0].id))
|
|
340
|
+
return out
|
|
341
|
+
|
|
342
|
+
def _resolve_agents(items: tuple[str, ...]) -> list[str]:
|
|
343
|
+
out: list[str] = []
|
|
344
|
+
for ref in list(items or ()): # tuple -> list
|
|
345
|
+
if is_uuid(ref):
|
|
346
|
+
out.append(ref)
|
|
347
|
+
continue
|
|
348
|
+
matches = client.find_agents(name=ref)
|
|
349
|
+
if not matches:
|
|
350
|
+
raise click.ClickException(f"Agent not found: {ref}")
|
|
351
|
+
if len(matches) > 1:
|
|
352
|
+
raise click.ClickException(
|
|
353
|
+
f"Multiple agents named '{ref}'. Use ID instead."
|
|
354
|
+
)
|
|
355
|
+
out.append(str(matches[0].id))
|
|
356
|
+
return out
|
|
357
|
+
|
|
358
|
+
resolved_tools = _resolve_tools(tools)
|
|
359
|
+
resolved_agents = _resolve_agents(agents)
|
|
360
|
+
|
|
361
|
+
# Create agent with optional model specification
|
|
362
|
+
create_kwargs = {
|
|
363
|
+
"name": name,
|
|
364
|
+
"instruction": instruction,
|
|
365
|
+
"tools": resolved_tools or None,
|
|
366
|
+
"agents": resolved_agents or None,
|
|
367
|
+
"timeout": timeout,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# Add model if specified
|
|
371
|
+
if model:
|
|
372
|
+
create_kwargs["model"] = model
|
|
373
|
+
|
|
374
|
+
agent = client.agents.create_agent(**create_kwargs)
|
|
284
375
|
|
|
285
376
|
if ctx.obj.get("view") == "json":
|
|
286
377
|
click.echo(json.dumps(agent.model_dump(), indent=2))
|
|
@@ -292,7 +383,8 @@ def create(
|
|
|
292
383
|
lm = (
|
|
293
384
|
cfg.get("lm_name")
|
|
294
385
|
or cfg.get("model")
|
|
295
|
-
or
|
|
386
|
+
or model # Use CLI model if specified
|
|
387
|
+
or f"{DEFAULT_MODEL} (backend default)"
|
|
296
388
|
)
|
|
297
389
|
|
|
298
390
|
panel = Panel(
|
|
@@ -352,7 +444,7 @@ def update(ctx, agent_id, name, instruction, tools, agents, timeout):
|
|
|
352
444
|
raise click.ClickException("No update fields specified")
|
|
353
445
|
|
|
354
446
|
# Update agent
|
|
355
|
-
updated_agent = client.agents.update_agent(agent.id, update_data)
|
|
447
|
+
updated_agent = client.agents.update_agent(agent.id, **update_data)
|
|
356
448
|
|
|
357
449
|
if ctx.obj.get("view") == "json":
|
|
358
450
|
click.echo(json.dumps(updated_agent.model_dump(), indent=2))
|
|
@@ -14,6 +14,8 @@ from rich.console import Console
|
|
|
14
14
|
from rich.panel import Panel
|
|
15
15
|
from rich.table import Table
|
|
16
16
|
|
|
17
|
+
from glaip_sdk import Client
|
|
18
|
+
|
|
17
19
|
console = Console()
|
|
18
20
|
|
|
19
21
|
CONFIG_DIR = Path.home() / ".aip"
|
|
@@ -25,8 +27,11 @@ def load_config():
|
|
|
25
27
|
if not CONFIG_FILE.exists():
|
|
26
28
|
return {}
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
try:
|
|
31
|
+
with open(CONFIG_FILE) as f:
|
|
32
|
+
return yaml.safe_load(f) or {}
|
|
33
|
+
except yaml.YAMLError:
|
|
34
|
+
return {}
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
def save_config(config):
|
|
@@ -49,82 +54,6 @@ def config_group():
|
|
|
49
54
|
pass
|
|
50
55
|
|
|
51
56
|
|
|
52
|
-
@config_group.command()
|
|
53
|
-
def configure():
|
|
54
|
-
"""Configure AIP CLI credentials and settings interactively."""
|
|
55
|
-
|
|
56
|
-
console.print(
|
|
57
|
-
Panel(
|
|
58
|
-
"[bold cyan]AIP Configuration[/bold cyan]\nConfigure your AIP CLI settings.",
|
|
59
|
-
title="🔧 Configuration Setup",
|
|
60
|
-
border_style="cyan",
|
|
61
|
-
)
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Load existing config
|
|
65
|
-
config = load_config()
|
|
66
|
-
|
|
67
|
-
console.print("\n[bold]Enter your AIP configuration:[/bold]")
|
|
68
|
-
console.print("(Leave blank to keep current values)")
|
|
69
|
-
console.print("─" * 50)
|
|
70
|
-
|
|
71
|
-
# API URL
|
|
72
|
-
current_url = config.get("api_url", "")
|
|
73
|
-
console.print(
|
|
74
|
-
f"\n[cyan]AIP API URL[/cyan] {f'(current: {current_url})' if current_url else ''}:"
|
|
75
|
-
)
|
|
76
|
-
new_url = input("> ").strip()
|
|
77
|
-
if new_url:
|
|
78
|
-
config["api_url"] = new_url
|
|
79
|
-
elif not current_url:
|
|
80
|
-
config["api_url"] = "https://your-aip-instance.com"
|
|
81
|
-
|
|
82
|
-
# API Key
|
|
83
|
-
current_key_masked = (
|
|
84
|
-
"***" + config.get("api_key", "")[-4:] if config.get("api_key") else ""
|
|
85
|
-
)
|
|
86
|
-
console.print(
|
|
87
|
-
f"\n[cyan]AIP API Key[/cyan] {f'(current: {current_key_masked})' if current_key_masked else ''}:"
|
|
88
|
-
)
|
|
89
|
-
new_key = getpass.getpass("> ")
|
|
90
|
-
if new_key:
|
|
91
|
-
config["api_key"] = new_key
|
|
92
|
-
|
|
93
|
-
# Save configuration
|
|
94
|
-
save_config(config)
|
|
95
|
-
|
|
96
|
-
console.print(f"\n✅ Configuration saved to: {CONFIG_FILE}")
|
|
97
|
-
|
|
98
|
-
# Test the new configuration
|
|
99
|
-
console.print("\n🔌 Testing connection...")
|
|
100
|
-
try:
|
|
101
|
-
from glaip_sdk import Client
|
|
102
|
-
|
|
103
|
-
# Create client with new config
|
|
104
|
-
client = Client(api_url=config["api_url"], api_key=config["api_key"])
|
|
105
|
-
|
|
106
|
-
# Try to list resources to test connection
|
|
107
|
-
try:
|
|
108
|
-
agents = client.list_agents()
|
|
109
|
-
console.print(f"✅ Connection successful! Found {len(agents)} agents")
|
|
110
|
-
except Exception as e:
|
|
111
|
-
console.print(f"⚠️ Connection established but API call failed: {e}")
|
|
112
|
-
console.print(
|
|
113
|
-
" You may need to check your API permissions or network access"
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
client.close()
|
|
117
|
-
|
|
118
|
-
except Exception as e:
|
|
119
|
-
console.print(f"❌ Connection failed: {e}")
|
|
120
|
-
console.print(" Please check your API URL and key")
|
|
121
|
-
console.print(" You can run 'aip status' later to test again")
|
|
122
|
-
|
|
123
|
-
console.print("\n💡 You can now use AIP CLI commands!")
|
|
124
|
-
console.print(" • Run 'aip status' to check connection")
|
|
125
|
-
console.print(" • Run 'aip agents list' to see your agents")
|
|
126
|
-
|
|
127
|
-
|
|
128
57
|
@config_group.command("list")
|
|
129
58
|
def list_config():
|
|
130
59
|
"""List current configuration."""
|
|
@@ -237,13 +166,8 @@ def reset_config(force):
|
|
|
237
166
|
console.print("[yellow]No configuration found to reset.[/yellow]")
|
|
238
167
|
|
|
239
168
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
@click.command()
|
|
244
|
-
def configure_command():
|
|
245
|
-
"""Configure AIP CLI credentials and settings interactively."""
|
|
246
|
-
|
|
169
|
+
def _configure_interactive():
|
|
170
|
+
"""Shared configuration logic for both configure commands."""
|
|
247
171
|
console.print(
|
|
248
172
|
Panel(
|
|
249
173
|
"[bold cyan]AIP Configuration[/bold cyan]\nConfigure your AIP CLI settings.",
|
|
@@ -289,8 +213,6 @@ def configure_command():
|
|
|
289
213
|
# Test the new configuration
|
|
290
214
|
console.print("\n🔌 Testing connection...")
|
|
291
215
|
try:
|
|
292
|
-
from glaip_sdk import Client
|
|
293
|
-
|
|
294
216
|
# Create client with new config
|
|
295
217
|
client = Client(api_url=config["api_url"], api_key=config["api_key"])
|
|
296
218
|
|
|
@@ -314,3 +236,23 @@ def configure_command():
|
|
|
314
236
|
console.print("\n💡 You can now use AIP CLI commands!")
|
|
315
237
|
console.print(" • Run 'aip status' to check connection")
|
|
316
238
|
console.print(" • Run 'aip agents list' to see your agents")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@config_group.command()
|
|
242
|
+
def configure():
|
|
243
|
+
"""Configure AIP CLI credentials and settings interactively."""
|
|
244
|
+
_configure_interactive()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# Alias command for backward compatibility
|
|
248
|
+
@click.command()
|
|
249
|
+
def configure_command():
|
|
250
|
+
"""Configure AIP CLI credentials and settings interactively.
|
|
251
|
+
|
|
252
|
+
This is an alias for 'aip config configure' for backward compatibility.
|
|
253
|
+
"""
|
|
254
|
+
# Delegate to the shared function
|
|
255
|
+
_configure_interactive()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# Note: The config command group should be registered in main.py
|