glaip-sdk 0.6.25__py3-none-any.whl → 0.7.0__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/agents/__init__.py +119 -0
- glaip_sdk/cli/commands/agents/_common.py +561 -0
- glaip_sdk/cli/commands/agents/create.py +151 -0
- glaip_sdk/cli/commands/agents/delete.py +64 -0
- glaip_sdk/cli/commands/agents/get.py +89 -0
- glaip_sdk/cli/commands/agents/list.py +129 -0
- glaip_sdk/cli/commands/agents/run.py +264 -0
- glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
- glaip_sdk/cli/commands/agents/update.py +112 -0
- glaip_sdk/cli/commands/mcps/__init__.py +94 -0
- glaip_sdk/cli/commands/mcps/_common.py +459 -0
- glaip_sdk/cli/commands/mcps/connect.py +82 -0
- glaip_sdk/cli/commands/mcps/create.py +152 -0
- glaip_sdk/cli/commands/mcps/delete.py +73 -0
- glaip_sdk/cli/commands/mcps/get.py +212 -0
- glaip_sdk/cli/commands/mcps/list.py +69 -0
- glaip_sdk/cli/commands/mcps/tools.py +235 -0
- glaip_sdk/cli/commands/mcps/update.py +190 -0
- glaip_sdk/cli/commands/shared/__init__.py +21 -0
- glaip_sdk/cli/commands/shared/formatters.py +91 -0
- glaip_sdk/cli/commands/tools/__init__.py +69 -0
- glaip_sdk/cli/commands/tools/_common.py +80 -0
- glaip_sdk/cli/commands/tools/create.py +228 -0
- glaip_sdk/cli/commands/tools/delete.py +61 -0
- glaip_sdk/cli/commands/tools/get.py +103 -0
- glaip_sdk/cli/commands/tools/list.py +69 -0
- glaip_sdk/cli/commands/tools/script.py +49 -0
- glaip_sdk/cli/commands/tools/update.py +102 -0
- glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
- glaip_sdk/cli/commands/transcripts/_common.py +9 -0
- glaip_sdk/cli/commands/transcripts/clear.py +5 -0
- glaip_sdk/cli/commands/transcripts/detail.py +5 -0
- glaip_sdk/cli/slash/tui/__init__.py +10 -1
- glaip_sdk/cli/slash/tui/context.py +51 -0
- glaip_sdk/cli/slash/tui/terminal.py +402 -0
- glaip_sdk/client/agents.py +1 -1
- glaip_sdk/client/main.py +1 -1
- glaip_sdk/client/mcps.py +44 -13
- glaip_sdk/client/payloads/agent/__init__.py +23 -0
- glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +22 -47
- glaip_sdk/client/payloads/agent/responses.py +43 -0
- glaip_sdk/client/tools.py +52 -23
- glaip_sdk/registry/tool.py +193 -81
- glaip_sdk/tools/base.py +41 -10
- glaip_sdk/utils/import_resolver.py +40 -2
- {glaip_sdk-0.6.25.dist-info → glaip_sdk-0.7.0.dist-info}/METADATA +2 -2
- {glaip_sdk-0.6.25.dist-info → glaip_sdk-0.7.0.dist-info}/RECORD +51 -18
- glaip_sdk/cli/commands/agents.py +0 -1502
- glaip_sdk/cli/commands/mcps.py +0 -1355
- glaip_sdk/cli/commands/tools.py +0 -575
- /glaip_sdk/cli/commands/{transcripts.py → transcripts_original.py} +0 -0
- {glaip_sdk-0.6.25.dist-info → glaip_sdk-0.7.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.6.25.dist-info → glaip_sdk-0.7.0.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.6.25.dist-info → glaip_sdk-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Create agent command.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from glaip_sdk.cli.context import output_flags
|
|
14
|
+
from glaip_sdk.cli.display import (
|
|
15
|
+
display_agent_run_suggestions,
|
|
16
|
+
display_creation_success,
|
|
17
|
+
handle_json_output,
|
|
18
|
+
handle_rich_output,
|
|
19
|
+
)
|
|
20
|
+
from glaip_sdk.cli.core.context import get_client
|
|
21
|
+
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT, DEFAULT_MODEL
|
|
22
|
+
from glaip_sdk.cli.validators import (
|
|
23
|
+
validate_agent_instruction_cli as validate_agent_instruction,
|
|
24
|
+
validate_agent_name_cli as validate_agent_name,
|
|
25
|
+
validate_timeout_cli as validate_timeout,
|
|
26
|
+
)
|
|
27
|
+
from glaip_sdk.utils.validation import coerce_timeout
|
|
28
|
+
|
|
29
|
+
from ._common import (
|
|
30
|
+
_get_language_model_display_name,
|
|
31
|
+
_handle_command_exception,
|
|
32
|
+
_prepare_agent_output,
|
|
33
|
+
_resolve_resources_by_name,
|
|
34
|
+
_split_comma_separated_refs,
|
|
35
|
+
agents_group,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _handle_successful_creation(ctx: Any, agent: Any, model: str | None) -> None:
|
|
40
|
+
"""Handle successful agent creation output."""
|
|
41
|
+
handle_json_output(ctx, _prepare_agent_output(agent))
|
|
42
|
+
|
|
43
|
+
lm_display = _get_language_model_display_name(agent, model)
|
|
44
|
+
|
|
45
|
+
handle_rich_output(
|
|
46
|
+
ctx,
|
|
47
|
+
display_creation_success(
|
|
48
|
+
"Agent",
|
|
49
|
+
agent.name,
|
|
50
|
+
agent.id,
|
|
51
|
+
Model=lm_display,
|
|
52
|
+
Type=getattr(agent, "type", "config"),
|
|
53
|
+
Framework=getattr(agent, "framework", "langchain"),
|
|
54
|
+
Version=getattr(agent, "version", "1.0"),
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
handle_rich_output(ctx, display_agent_run_suggestions(agent))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _handle_creation_exception(ctx: Any, e: Exception) -> None:
|
|
61
|
+
"""Handle exceptions during agent creation."""
|
|
62
|
+
_handle_command_exception(ctx, e)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@agents_group.command()
|
|
66
|
+
@click.option("--name", help="Agent name")
|
|
67
|
+
@click.option("--instruction", help="Agent instruction (prompt)")
|
|
68
|
+
@click.option(
|
|
69
|
+
"--model",
|
|
70
|
+
help=f"Language model to use (e.g., {DEFAULT_MODEL}, default: {DEFAULT_MODEL})",
|
|
71
|
+
)
|
|
72
|
+
@click.option("--tools", multiple=True, help="Tool names or IDs to attach")
|
|
73
|
+
@click.option("--agents", multiple=True, help="Sub-agent names or IDs to attach")
|
|
74
|
+
@click.option("--mcps", multiple=True, help="MCP names or IDs to attach")
|
|
75
|
+
@click.option(
|
|
76
|
+
"--timeout",
|
|
77
|
+
default=DEFAULT_AGENT_RUN_TIMEOUT,
|
|
78
|
+
type=int,
|
|
79
|
+
help="Agent execution timeout in seconds (default: 300s)",
|
|
80
|
+
)
|
|
81
|
+
@click.option(
|
|
82
|
+
"--import",
|
|
83
|
+
"import_file",
|
|
84
|
+
type=click.Path(exists=True, dir_okay=False),
|
|
85
|
+
help="Import agent configuration from JSON file",
|
|
86
|
+
)
|
|
87
|
+
@output_flags()
|
|
88
|
+
@click.pass_context
|
|
89
|
+
def create(
|
|
90
|
+
ctx: Any,
|
|
91
|
+
name: str,
|
|
92
|
+
instruction: str,
|
|
93
|
+
model: str | None,
|
|
94
|
+
tools: tuple[str, ...] | None,
|
|
95
|
+
agents: tuple[str, ...] | None,
|
|
96
|
+
mcps: tuple[str, ...] | None,
|
|
97
|
+
timeout: float | None,
|
|
98
|
+
import_file: str | None,
|
|
99
|
+
) -> None:
|
|
100
|
+
r"""Create a new agent.
|
|
101
|
+
|
|
102
|
+
\b
|
|
103
|
+
Examples:
|
|
104
|
+
aip agents create --name "My Agent" --instruction "You are a helpful assistant"
|
|
105
|
+
aip agents create --import agent.json
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
client = get_client(ctx)
|
|
109
|
+
|
|
110
|
+
if import_file is None:
|
|
111
|
+
if not name:
|
|
112
|
+
raise click.ClickException("Agent name is required (--name or --import)")
|
|
113
|
+
if not instruction:
|
|
114
|
+
raise click.ClickException("Agent instruction is required (--instruction or --import)")
|
|
115
|
+
|
|
116
|
+
if name is not None:
|
|
117
|
+
name = validate_agent_name(name)
|
|
118
|
+
if instruction is not None:
|
|
119
|
+
instruction = validate_agent_instruction(instruction)
|
|
120
|
+
|
|
121
|
+
timeout_value = coerce_timeout(timeout)
|
|
122
|
+
if timeout_value is not None:
|
|
123
|
+
timeout_value = validate_timeout(timeout_value)
|
|
124
|
+
if import_file is None and timeout_value == DEFAULT_AGENT_RUN_TIMEOUT:
|
|
125
|
+
timeout_value = None
|
|
126
|
+
|
|
127
|
+
tools = _split_comma_separated_refs(tools)
|
|
128
|
+
agents = _split_comma_separated_refs(agents)
|
|
129
|
+
mcps = _split_comma_separated_refs(mcps)
|
|
130
|
+
|
|
131
|
+
# Resolve resources
|
|
132
|
+
resolved_tools = _resolve_resources_by_name(client, tools, "tool", client.find_tools, "Tool")
|
|
133
|
+
resolved_agents = _resolve_resources_by_name(client, agents, "agent", client.find_agents, "Agent")
|
|
134
|
+
resolved_mcps = _resolve_resources_by_name(client, mcps, "mcp", client.find_mcps, "MCP")
|
|
135
|
+
|
|
136
|
+
agent = client.agents.create_agent(
|
|
137
|
+
file=import_file,
|
|
138
|
+
name=name,
|
|
139
|
+
instruction=instruction,
|
|
140
|
+
model=model,
|
|
141
|
+
tools=resolved_tools or None,
|
|
142
|
+
agents=resolved_agents or None,
|
|
143
|
+
mcps=resolved_mcps or None,
|
|
144
|
+
timeout=timeout_value,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Handle successful creation
|
|
148
|
+
_handle_successful_creation(ctx, agent, model)
|
|
149
|
+
|
|
150
|
+
except Exception as e:
|
|
151
|
+
_handle_creation_exception(ctx, e)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Delete agent command.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from glaip_sdk.cli.context import output_flags
|
|
14
|
+
from glaip_sdk.cli.display import (
|
|
15
|
+
display_confirmation_prompt,
|
|
16
|
+
display_deletion_success,
|
|
17
|
+
handle_json_output,
|
|
18
|
+
handle_rich_output,
|
|
19
|
+
)
|
|
20
|
+
from glaip_sdk.cli.core.context import get_client
|
|
21
|
+
|
|
22
|
+
from ._common import (
|
|
23
|
+
_handle_click_exception_for_json,
|
|
24
|
+
_handle_command_exception,
|
|
25
|
+
agents_group,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@agents_group.command()
|
|
30
|
+
@click.argument("agent_id")
|
|
31
|
+
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation")
|
|
32
|
+
@output_flags()
|
|
33
|
+
@click.pass_context
|
|
34
|
+
def delete(ctx: Any, agent_id: str, yes: bool) -> None:
|
|
35
|
+
"""Delete an agent."""
|
|
36
|
+
try:
|
|
37
|
+
client = get_client(ctx)
|
|
38
|
+
|
|
39
|
+
# Get agent by ID (no ambiguity handling needed)
|
|
40
|
+
try:
|
|
41
|
+
agent = client.agents.get_agent_by_id(agent_id)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}") from e
|
|
44
|
+
|
|
45
|
+
# Confirm deletion when not forced
|
|
46
|
+
if not yes:
|
|
47
|
+
if not display_confirmation_prompt("Agent", agent.name):
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
client.agents.delete_agent(agent.id)
|
|
51
|
+
|
|
52
|
+
handle_json_output(
|
|
53
|
+
ctx,
|
|
54
|
+
{
|
|
55
|
+
"success": True,
|
|
56
|
+
"message": f"Agent '{agent.name}' deleted",
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
handle_rich_output(ctx, display_deletion_success("Agent", agent.name))
|
|
60
|
+
|
|
61
|
+
except click.ClickException as e:
|
|
62
|
+
_handle_click_exception_for_json(ctx, e)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
_handle_command_exception(ctx, e)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Get agent command.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.cli.context import output_flags
|
|
15
|
+
from glaip_sdk.cli.display import display_agent_run_suggestions, handle_rich_output
|
|
16
|
+
from glaip_sdk.cli.core.context import get_client
|
|
17
|
+
from glaip_sdk.cli.core.output import handle_resource_export
|
|
18
|
+
|
|
19
|
+
from ._common import _display_agent_details, _resolve_agent, agents_group, console
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@agents_group.command()
|
|
23
|
+
@click.argument("agent_ref")
|
|
24
|
+
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--export",
|
|
27
|
+
type=click.Path(dir_okay=False, writable=True),
|
|
28
|
+
help="Export complete agent configuration to file (format auto-detected from .json/.yaml extension)",
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--instruction-preview",
|
|
32
|
+
type=int,
|
|
33
|
+
default=0,
|
|
34
|
+
show_default=True,
|
|
35
|
+
help="Instruction preview length when printing instructions (0 shows full prompt).",
|
|
36
|
+
)
|
|
37
|
+
@output_flags()
|
|
38
|
+
@click.pass_context
|
|
39
|
+
def get(
|
|
40
|
+
ctx: Any,
|
|
41
|
+
agent_ref: str,
|
|
42
|
+
select: int | None,
|
|
43
|
+
export: str | None,
|
|
44
|
+
instruction_preview: int,
|
|
45
|
+
) -> None:
|
|
46
|
+
r"""Get agent details.
|
|
47
|
+
|
|
48
|
+
\b
|
|
49
|
+
Examples:
|
|
50
|
+
aip agents get my-agent
|
|
51
|
+
aip agents get my-agent --export agent.json # Exports complete configuration as JSON
|
|
52
|
+
aip agents get my-agent --export agent.yaml # Exports complete configuration as YAML
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
# Initialize API client for agent retrieval
|
|
56
|
+
api_client = get_client(ctx)
|
|
57
|
+
|
|
58
|
+
# Resolve agent reference using questionary interface for better UX
|
|
59
|
+
agent = _resolve_agent(ctx, api_client, agent_ref, select, interface_preference="questionary")
|
|
60
|
+
|
|
61
|
+
if not agent:
|
|
62
|
+
raise click.ClickException(f"Agent '{agent_ref}' not found")
|
|
63
|
+
|
|
64
|
+
# Handle export option if requested
|
|
65
|
+
if export:
|
|
66
|
+
handle_resource_export(
|
|
67
|
+
ctx,
|
|
68
|
+
agent,
|
|
69
|
+
Path(export),
|
|
70
|
+
resource_type="agent",
|
|
71
|
+
get_by_id_func=api_client.agents.get_agent_by_id,
|
|
72
|
+
console_override=console,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Display full agent details using the standardized helper
|
|
76
|
+
_display_agent_details(
|
|
77
|
+
ctx,
|
|
78
|
+
api_client,
|
|
79
|
+
agent,
|
|
80
|
+
instruction_preview_limit=instruction_preview,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Show run suggestions via centralized display helper
|
|
84
|
+
handle_rich_output(ctx, display_agent_run_suggestions(agent))
|
|
85
|
+
|
|
86
|
+
except click.ClickException:
|
|
87
|
+
raise
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise click.ClickException(str(e)) from e
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""List agents command.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.branding import ACCENT_STYLE, INFO, SUCCESS, WARNING_STYLE
|
|
15
|
+
from glaip_sdk.cli.context import output_flags
|
|
16
|
+
from glaip_sdk.cli.display import display_agent_run_suggestions, handle_rich_output
|
|
17
|
+
from glaip_sdk.cli.core.output import coerce_to_row, output_list
|
|
18
|
+
from glaip_sdk.cli.core.prompting import _fuzzy_pick_for_resources
|
|
19
|
+
from glaip_sdk.cli.core.rendering import with_client_and_spinner
|
|
20
|
+
from glaip_sdk.icons import ICON_AGENT
|
|
21
|
+
|
|
22
|
+
from ._common import _display_agent_details, agents_group, console
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@agents_group.command(name="list")
|
|
26
|
+
@click.option("--simple", is_flag=True, help="Show simple table without interactive picker")
|
|
27
|
+
@click.option("--type", "agent_type", help="Filter by agent type (config, code, a2a, langflow)")
|
|
28
|
+
@click.option("--framework", help="Filter by framework (langchain, langgraph, google_adk)")
|
|
29
|
+
@click.option("--name", help="Filter by partial name match (case-insensitive)")
|
|
30
|
+
@click.option("--version", help="Filter by exact version match")
|
|
31
|
+
@click.option(
|
|
32
|
+
"--sync-langflow",
|
|
33
|
+
is_flag=True,
|
|
34
|
+
help="Sync with LangFlow server before listing (only applies when filtering by langflow type)",
|
|
35
|
+
)
|
|
36
|
+
@output_flags()
|
|
37
|
+
@click.pass_context
|
|
38
|
+
def list_agents(
|
|
39
|
+
ctx: Any,
|
|
40
|
+
simple: bool,
|
|
41
|
+
agent_type: str | None,
|
|
42
|
+
framework: str | None,
|
|
43
|
+
name: str | None,
|
|
44
|
+
version: str | None,
|
|
45
|
+
sync_langflow: bool,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""List agents with optional filtering."""
|
|
48
|
+
try:
|
|
49
|
+
with with_client_and_spinner(
|
|
50
|
+
ctx,
|
|
51
|
+
"[bold blue]Fetching agents…[/bold blue]",
|
|
52
|
+
console_override=console,
|
|
53
|
+
) as client:
|
|
54
|
+
# Query agents with specified filters
|
|
55
|
+
filter_params = {
|
|
56
|
+
"agent_type": agent_type,
|
|
57
|
+
"framework": framework,
|
|
58
|
+
"name": name,
|
|
59
|
+
"version": version,
|
|
60
|
+
"sync_langflow_agents": sync_langflow,
|
|
61
|
+
}
|
|
62
|
+
agents = client.agents.list_agents(**filter_params)
|
|
63
|
+
|
|
64
|
+
# Define table columns: (data_key, header, style, width)
|
|
65
|
+
columns = [
|
|
66
|
+
("id", "ID", "dim", 36),
|
|
67
|
+
("name", "Name", ACCENT_STYLE, None),
|
|
68
|
+
("type", "Type", WARNING_STYLE, None),
|
|
69
|
+
("framework", "Framework", INFO, None),
|
|
70
|
+
("version", "Version", SUCCESS, None),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# Transform function for safe attribute access
|
|
74
|
+
def transform_agent(agent: Any) -> dict[str, Any]:
|
|
75
|
+
"""Transform an agent object to a display row dictionary.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
agent: Agent object to transform.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary with id, name, type, framework, and version fields.
|
|
82
|
+
"""
|
|
83
|
+
row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
|
|
84
|
+
# Ensure id is always a string
|
|
85
|
+
row["id"] = str(row["id"])
|
|
86
|
+
return row
|
|
87
|
+
|
|
88
|
+
# Use fuzzy picker for interactive agent selection and details (default behavior)
|
|
89
|
+
# Skip if --simple flag is used, a name filter is applied, or non-rich output is requested
|
|
90
|
+
ctx_obj = ctx.obj if isinstance(getattr(ctx, "obj", None), dict) else {}
|
|
91
|
+
current_view = ctx_obj.get("view")
|
|
92
|
+
interactive_enabled = (
|
|
93
|
+
not simple
|
|
94
|
+
and name is None
|
|
95
|
+
and current_view not in {"json", "plain", "md"}
|
|
96
|
+
and console.is_terminal
|
|
97
|
+
and os.isatty(1)
|
|
98
|
+
and len(agents) > 0
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Track picker attempt so the fallback table doesn't re-open the palette
|
|
102
|
+
picker_attempted = False
|
|
103
|
+
if interactive_enabled:
|
|
104
|
+
picker_attempted = True
|
|
105
|
+
picked_agent = _fuzzy_pick_for_resources(agents, "agent", "")
|
|
106
|
+
if picked_agent:
|
|
107
|
+
_display_agent_details(ctx, client, picked_agent)
|
|
108
|
+
# Show run suggestions via centralized display helper
|
|
109
|
+
handle_rich_output(ctx, display_agent_run_suggestions(picked_agent))
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Show simple table (either --simple flag or non-interactive)
|
|
113
|
+
output_list(
|
|
114
|
+
ctx,
|
|
115
|
+
agents,
|
|
116
|
+
f"{ICON_AGENT} Available Agents",
|
|
117
|
+
columns,
|
|
118
|
+
transform_agent,
|
|
119
|
+
skip_picker=(
|
|
120
|
+
not interactive_enabled
|
|
121
|
+
or picker_attempted
|
|
122
|
+
or simple
|
|
123
|
+
or any(param is not None for param in (agent_type, framework, name, version))
|
|
124
|
+
),
|
|
125
|
+
use_pager=False,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
raise click.ClickException(str(e)) from e
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Run agent command.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.cli.context import get_ctx_value, output_flags
|
|
15
|
+
from glaip_sdk.cli.display import handle_json_output
|
|
16
|
+
from glaip_sdk.cli.core.context import get_client
|
|
17
|
+
from glaip_sdk.cli.core.rendering import build_renderer
|
|
18
|
+
from glaip_sdk.cli.transcript import maybe_launch_post_run_viewer, store_transcript_for_session
|
|
19
|
+
from glaip_sdk.cli.rich_helpers import print_markup
|
|
20
|
+
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
|
|
21
|
+
from glaip_sdk.exceptions import AgentTimeoutError
|
|
22
|
+
from glaip_sdk.utils.rendering.renderer.toggle import TranscriptToggleController
|
|
23
|
+
|
|
24
|
+
from ._common import (
|
|
25
|
+
_emit_verbose_guidance,
|
|
26
|
+
_get_agent_model_name,
|
|
27
|
+
_handle_command_exception,
|
|
28
|
+
_resolve_agent,
|
|
29
|
+
_running_in_slash_mode,
|
|
30
|
+
_safe_agent_attribute,
|
|
31
|
+
agents_group,
|
|
32
|
+
console,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
from glaip_sdk.branding import SUCCESS_STYLE
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _validate_run_input(input_option: str | None, input_text: str | None) -> str:
|
|
39
|
+
"""Validate and determine the final input text for agent run."""
|
|
40
|
+
final_input_text = input_option if input_option else input_text
|
|
41
|
+
|
|
42
|
+
if not final_input_text:
|
|
43
|
+
raise click.ClickException("Input text is required. Use either positional argument or --input option.")
|
|
44
|
+
|
|
45
|
+
return final_input_text
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _parse_chat_history(chat_history: str | None) -> list[dict[str, Any]] | None:
|
|
49
|
+
"""Parse chat history JSON if provided."""
|
|
50
|
+
if not chat_history:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
return json.loads(chat_history)
|
|
55
|
+
except json.JSONDecodeError as err:
|
|
56
|
+
raise click.ClickException("Invalid JSON in chat history") from err
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
|
|
60
|
+
"""Set up renderer and working console for agent run."""
|
|
61
|
+
tty_enabled = bool(get_ctx_value(ctx, "tty", True))
|
|
62
|
+
return build_renderer(
|
|
63
|
+
ctx,
|
|
64
|
+
save_path=save,
|
|
65
|
+
verbose=verbose,
|
|
66
|
+
_tty_enabled=tty_enabled,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _maybe_attach_transcript_toggle(ctx: Any, renderer: Any) -> None:
|
|
71
|
+
"""Attach transcript toggle controller when interactive TTY is available."""
|
|
72
|
+
if renderer is None:
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
console_obj = getattr(renderer, "console", None)
|
|
76
|
+
if console_obj is None or not getattr(console_obj, "is_terminal", False):
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
tty_enabled = bool(get_ctx_value(ctx, "tty", True))
|
|
80
|
+
if not tty_enabled:
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
controller = TranscriptToggleController(enabled=True)
|
|
84
|
+
renderer.transcript_controller = controller
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _prepare_run_kwargs(
|
|
88
|
+
agent: Any,
|
|
89
|
+
final_input_text: str,
|
|
90
|
+
files: list[str] | None,
|
|
91
|
+
parsed_chat_history: list[dict[str, Any]] | None,
|
|
92
|
+
renderer: Any,
|
|
93
|
+
tty_enabled: bool,
|
|
94
|
+
) -> dict[str, Any]:
|
|
95
|
+
"""Prepare kwargs for agent run."""
|
|
96
|
+
run_kwargs = {
|
|
97
|
+
"agent_id": agent.id,
|
|
98
|
+
"message": final_input_text,
|
|
99
|
+
"files": list(files),
|
|
100
|
+
"agent_name": agent.name,
|
|
101
|
+
"tty": tty_enabled,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if parsed_chat_history:
|
|
105
|
+
run_kwargs["chat_history"] = parsed_chat_history
|
|
106
|
+
|
|
107
|
+
if renderer is not None:
|
|
108
|
+
run_kwargs["renderer"] = renderer
|
|
109
|
+
|
|
110
|
+
return run_kwargs
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _handle_run_output(ctx: Any, result: Any, renderer: Any) -> None:
|
|
114
|
+
"""Handle output formatting for agent run results."""
|
|
115
|
+
printed_by_renderer = bool(renderer)
|
|
116
|
+
selected_view = get_ctx_value(ctx, "view", "rich")
|
|
117
|
+
|
|
118
|
+
if not printed_by_renderer:
|
|
119
|
+
if selected_view == "json":
|
|
120
|
+
handle_json_output(ctx, {"output": result})
|
|
121
|
+
elif selected_view == "md":
|
|
122
|
+
click.echo(f"# Assistant\n\n{result}")
|
|
123
|
+
elif selected_view == "plain":
|
|
124
|
+
click.echo(result)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _save_run_transcript(save: str | None, result: Any, working_console: Any) -> None:
|
|
128
|
+
"""Save transcript to file if requested."""
|
|
129
|
+
if not save:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
ext = (save.rsplit(".", 1)[-1] or "").lower()
|
|
133
|
+
if ext == "json":
|
|
134
|
+
save_data = {
|
|
135
|
+
"output": result or "",
|
|
136
|
+
"full_debug_output": getattr(working_console, "get_captured_output", lambda: "")(),
|
|
137
|
+
"timestamp": "captured during agent execution",
|
|
138
|
+
}
|
|
139
|
+
content = json.dumps(save_data, indent=2)
|
|
140
|
+
else:
|
|
141
|
+
full_output = getattr(working_console, "get_captured_output", lambda: "")()
|
|
142
|
+
if full_output:
|
|
143
|
+
content = f"# Agent Debug Log\n\n{full_output}\n\n---\n\n## Final Result\n\n{result or ''}\n"
|
|
144
|
+
else:
|
|
145
|
+
content = f"# Assistant\n\n{result or ''}\n"
|
|
146
|
+
|
|
147
|
+
with open(save, "w", encoding="utf-8") as f:
|
|
148
|
+
f.write(content)
|
|
149
|
+
print_markup(f"[{SUCCESS_STYLE}]Full debug output saved to: {save}[/]", console=console)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@agents_group.command()
|
|
153
|
+
@click.argument("agent_ref")
|
|
154
|
+
@click.argument("input_text", required=False)
|
|
155
|
+
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
156
|
+
@click.option("--input", "input_option", help="Input text for the agent")
|
|
157
|
+
@click.option("--chat-history", help="JSON string of chat history")
|
|
158
|
+
@click.option(
|
|
159
|
+
"--timeout",
|
|
160
|
+
default=DEFAULT_AGENT_RUN_TIMEOUT,
|
|
161
|
+
type=int,
|
|
162
|
+
help="Agent execution timeout in seconds (default: 300s)",
|
|
163
|
+
)
|
|
164
|
+
@click.option(
|
|
165
|
+
"--save",
|
|
166
|
+
type=click.Path(dir_okay=False, writable=True),
|
|
167
|
+
help="Save transcript to file (md or json)",
|
|
168
|
+
)
|
|
169
|
+
@click.option(
|
|
170
|
+
"--file",
|
|
171
|
+
"files",
|
|
172
|
+
multiple=True,
|
|
173
|
+
type=click.Path(exists=True),
|
|
174
|
+
help="Attach file(s)",
|
|
175
|
+
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--verbose/--no-verbose",
|
|
178
|
+
default=False,
|
|
179
|
+
help="Show detailed SSE events during streaming",
|
|
180
|
+
)
|
|
181
|
+
@output_flags()
|
|
182
|
+
@click.pass_context
|
|
183
|
+
def run(
|
|
184
|
+
ctx: Any,
|
|
185
|
+
agent_ref: str,
|
|
186
|
+
select: int | None,
|
|
187
|
+
input_text: str | None,
|
|
188
|
+
input_option: str | None,
|
|
189
|
+
chat_history: str | None,
|
|
190
|
+
timeout: float | None,
|
|
191
|
+
save: str | None,
|
|
192
|
+
files: tuple[str, ...] | None,
|
|
193
|
+
verbose: bool,
|
|
194
|
+
) -> None:
|
|
195
|
+
r"""Run an agent with input text.
|
|
196
|
+
|
|
197
|
+
Usage: aip agents run <agent_ref> <input_text> [OPTIONS]
|
|
198
|
+
|
|
199
|
+
\b
|
|
200
|
+
Examples:
|
|
201
|
+
aip agents run my-agent "Hello world"
|
|
202
|
+
aip agents run agent-123 "Process this data" --timeout 600
|
|
203
|
+
aip agents run my-agent --input "Hello world" # Legacy style
|
|
204
|
+
"""
|
|
205
|
+
final_input_text = _validate_run_input(input_option, input_text)
|
|
206
|
+
|
|
207
|
+
if verbose:
|
|
208
|
+
_emit_verbose_guidance(ctx)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
client = get_client(ctx)
|
|
213
|
+
agent = _resolve_agent(ctx, client, agent_ref, select, interface_preference="fuzzy")
|
|
214
|
+
|
|
215
|
+
parsed_chat_history = _parse_chat_history(chat_history)
|
|
216
|
+
renderer, working_console = _setup_run_renderer(ctx, save, verbose)
|
|
217
|
+
_maybe_attach_transcript_toggle(ctx, renderer)
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
client.timeout = float(timeout)
|
|
221
|
+
except Exception: # pragma: no cover # Defensive - timeout is int per Click, so float() always succeeds
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
run_kwargs = _prepare_run_kwargs(
|
|
225
|
+
agent,
|
|
226
|
+
final_input_text,
|
|
227
|
+
files,
|
|
228
|
+
parsed_chat_history,
|
|
229
|
+
renderer,
|
|
230
|
+
bool(get_ctx_value(ctx, "tty", True)),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
result = client.agents.run_agent(**run_kwargs, timeout=timeout)
|
|
234
|
+
|
|
235
|
+
slash_mode = _running_in_slash_mode(ctx)
|
|
236
|
+
agent_id = str(_safe_agent_attribute(agent, "id") or "") or None
|
|
237
|
+
agent_name = _safe_agent_attribute(agent, "name")
|
|
238
|
+
model_hint = _get_agent_model_name(agent)
|
|
239
|
+
|
|
240
|
+
transcript_context = store_transcript_for_session(
|
|
241
|
+
ctx,
|
|
242
|
+
renderer,
|
|
243
|
+
final_result=result,
|
|
244
|
+
agent_id=agent_id,
|
|
245
|
+
agent_name=agent_name,
|
|
246
|
+
model=model_hint,
|
|
247
|
+
source="slash" if slash_mode else "cli",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
_handle_run_output(ctx, result, renderer)
|
|
251
|
+
_save_run_transcript(save, result, working_console)
|
|
252
|
+
maybe_launch_post_run_viewer(
|
|
253
|
+
ctx,
|
|
254
|
+
transcript_context,
|
|
255
|
+
console=console,
|
|
256
|
+
slash_mode=slash_mode,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
except AgentTimeoutError as e:
|
|
260
|
+
error_msg = str(e)
|
|
261
|
+
handle_json_output(ctx, error=Exception(error_msg))
|
|
262
|
+
raise click.ClickException(error_msg) from e
|
|
263
|
+
except Exception as e:
|
|
264
|
+
_handle_command_exception(ctx, e)
|