glaip-sdk 0.6.12__py3-none-any.whl → 0.6.14__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 +42 -5
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.14.dist-info}/METADATA +31 -37
- glaip_sdk-0.6.14.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.14.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.14.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.14.dist-info/top_level.txt +1 -0
- glaip_sdk/agents/__init__.py +0 -27
- glaip_sdk/agents/base.py +0 -1191
- glaip_sdk/cli/__init__.py +0 -9
- glaip_sdk/cli/account_store.py +0 -540
- glaip_sdk/cli/agent_config.py +0 -78
- glaip_sdk/cli/auth.py +0 -699
- glaip_sdk/cli/commands/__init__.py +0 -5
- glaip_sdk/cli/commands/accounts.py +0 -746
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/common_config.py +0 -101
- glaip_sdk/cli/commands/configure.py +0 -896
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/models.py +0 -69
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/commands/transcripts.py +0 -755
- glaip_sdk/cli/commands/update.py +0 -61
- glaip_sdk/cli/config.py +0 -95
- glaip_sdk/cli/constants.py +0 -38
- glaip_sdk/cli/context.py +0 -150
- glaip_sdk/cli/core/__init__.py +0 -79
- glaip_sdk/cli/core/context.py +0 -124
- glaip_sdk/cli/core/output.py +0 -846
- glaip_sdk/cli/core/prompting.py +0 -649
- glaip_sdk/cli/core/rendering.py +0 -187
- glaip_sdk/cli/display.py +0 -355
- glaip_sdk/cli/hints.py +0 -57
- glaip_sdk/cli/io.py +0 -112
- glaip_sdk/cli/main.py +0 -604
- glaip_sdk/cli/masking.py +0 -136
- glaip_sdk/cli/mcp_validators.py +0 -287
- glaip_sdk/cli/pager.py +0 -266
- glaip_sdk/cli/parsers/__init__.py +0 -7
- glaip_sdk/cli/parsers/json_input.py +0 -177
- glaip_sdk/cli/resolution.py +0 -67
- glaip_sdk/cli/rich_helpers.py +0 -27
- glaip_sdk/cli/slash/__init__.py +0 -15
- glaip_sdk/cli/slash/accounts_controller.py +0 -578
- glaip_sdk/cli/slash/accounts_shared.py +0 -75
- glaip_sdk/cli/slash/agent_session.py +0 -285
- glaip_sdk/cli/slash/prompt.py +0 -256
- glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
- glaip_sdk/cli/slash/session.py +0 -1708
- glaip_sdk/cli/slash/tui/__init__.py +0 -9
- glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
- glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
- glaip_sdk/cli/slash/tui/loading.py +0 -58
- glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
- glaip_sdk/cli/transcript/__init__.py +0 -31
- glaip_sdk/cli/transcript/cache.py +0 -536
- glaip_sdk/cli/transcript/capture.py +0 -329
- glaip_sdk/cli/transcript/export.py +0 -38
- glaip_sdk/cli/transcript/history.py +0 -815
- glaip_sdk/cli/transcript/launcher.py +0 -77
- glaip_sdk/cli/transcript/viewer.py +0 -374
- glaip_sdk/cli/update_notifier.py +0 -290
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk/cli/validators.py +0 -238
- glaip_sdk/client/__init__.py +0 -11
- glaip_sdk/client/_agent_payloads.py +0 -520
- glaip_sdk/client/agent_runs.py +0 -147
- glaip_sdk/client/agents.py +0 -1335
- glaip_sdk/client/base.py +0 -502
- glaip_sdk/client/main.py +0 -249
- glaip_sdk/client/mcps.py +0 -370
- glaip_sdk/client/run_rendering.py +0 -700
- glaip_sdk/client/shared.py +0 -21
- glaip_sdk/client/tools.py +0 -661
- glaip_sdk/client/validators.py +0 -198
- glaip_sdk/config/constants.py +0 -52
- glaip_sdk/mcps/__init__.py +0 -21
- glaip_sdk/mcps/base.py +0 -345
- glaip_sdk/models/__init__.py +0 -90
- glaip_sdk/models/agent.py +0 -47
- glaip_sdk/models/agent_runs.py +0 -116
- glaip_sdk/models/common.py +0 -42
- glaip_sdk/models/mcp.py +0 -33
- glaip_sdk/models/tool.py +0 -33
- glaip_sdk/payload_schemas/__init__.py +0 -7
- glaip_sdk/payload_schemas/agent.py +0 -85
- glaip_sdk/registry/__init__.py +0 -55
- glaip_sdk/registry/agent.py +0 -164
- glaip_sdk/registry/base.py +0 -139
- glaip_sdk/registry/mcp.py +0 -253
- glaip_sdk/registry/tool.py +0 -232
- glaip_sdk/runner/__init__.py +0 -59
- glaip_sdk/runner/base.py +0 -84
- glaip_sdk/runner/deps.py +0 -115
- glaip_sdk/runner/langgraph.py +0 -782
- glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
- glaip_sdk/runner/tool_adapter/__init__.py +0 -18
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
- glaip_sdk/tools/__init__.py +0 -22
- glaip_sdk/tools/base.py +0 -435
- glaip_sdk/utils/__init__.py +0 -86
- glaip_sdk/utils/a2a/__init__.py +0 -34
- glaip_sdk/utils/a2a/event_processor.py +0 -188
- glaip_sdk/utils/agent_config.py +0 -194
- glaip_sdk/utils/bundler.py +0 -267
- glaip_sdk/utils/client.py +0 -111
- glaip_sdk/utils/client_utils.py +0 -486
- glaip_sdk/utils/datetime_helpers.py +0 -58
- glaip_sdk/utils/discovery.py +0 -78
- glaip_sdk/utils/display.py +0 -135
- glaip_sdk/utils/export.py +0 -143
- glaip_sdk/utils/general.py +0 -61
- glaip_sdk/utils/import_export.py +0 -168
- glaip_sdk/utils/import_resolver.py +0 -492
- glaip_sdk/utils/instructions.py +0 -101
- glaip_sdk/utils/rendering/__init__.py +0 -115
- glaip_sdk/utils/rendering/formatting.py +0 -264
- glaip_sdk/utils/rendering/layout/__init__.py +0 -64
- glaip_sdk/utils/rendering/layout/panels.py +0 -156
- glaip_sdk/utils/rendering/layout/progress.py +0 -202
- glaip_sdk/utils/rendering/layout/summary.py +0 -74
- glaip_sdk/utils/rendering/layout/transcript.py +0 -606
- glaip_sdk/utils/rendering/models.py +0 -85
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
- glaip_sdk/utils/rendering/renderer/base.py +0 -1024
- glaip_sdk/utils/rendering/renderer/config.py +0 -27
- glaip_sdk/utils/rendering/renderer/console.py +0 -55
- glaip_sdk/utils/rendering/renderer/debug.py +0 -178
- glaip_sdk/utils/rendering/renderer/factory.py +0 -138
- glaip_sdk/utils/rendering/renderer/stream.py +0 -202
- glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
- glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
- glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
- glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
- glaip_sdk/utils/rendering/state.py +0 -204
- glaip_sdk/utils/rendering/step_tree_state.py +0 -100
- glaip_sdk/utils/rendering/steps/__init__.py +0 -34
- glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
- glaip_sdk/utils/rendering/steps/format.py +0 -176
- glaip_sdk/utils/rendering/steps/manager.py +0 -387
- glaip_sdk/utils/rendering/timing.py +0 -36
- glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
- glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
- glaip_sdk/utils/resource_refs.py +0 -195
- glaip_sdk/utils/run_renderer.py +0 -41
- glaip_sdk/utils/runtime_config.py +0 -425
- glaip_sdk/utils/serialization.py +0 -424
- glaip_sdk/utils/sync.py +0 -142
- glaip_sdk/utils/tool_detection.py +0 -33
- glaip_sdk/utils/validation.py +0 -264
- glaip_sdk-0.6.12.dist-info/RECORD +0 -159
- glaip_sdk-0.6.12.dist-info/entry_points.txt +0 -3
glaip_sdk/cli/commands/models.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Language models commands.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
from rich.console import Console
|
|
11
|
-
|
|
12
|
-
from glaip_sdk.branding import ACCENT_STYLE, INFO, SUCCESS
|
|
13
|
-
from glaip_sdk.cli.context import output_flags
|
|
14
|
-
from glaip_sdk.cli.utils import (
|
|
15
|
-
output_list,
|
|
16
|
-
with_client_and_spinner,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
console = Console()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@click.group(name="models", no_args_is_help=True)
|
|
23
|
-
def models_group() -> None:
|
|
24
|
-
"""Language model operations."""
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@models_group.command(name="list")
|
|
29
|
-
@output_flags()
|
|
30
|
-
@click.pass_context
|
|
31
|
-
def list_models(ctx: Any) -> None:
|
|
32
|
-
"""List available language models."""
|
|
33
|
-
try:
|
|
34
|
-
with with_client_and_spinner(
|
|
35
|
-
ctx,
|
|
36
|
-
"[bold blue]Fetching language models…[/bold blue]",
|
|
37
|
-
console_override=console,
|
|
38
|
-
) as client:
|
|
39
|
-
models = client.list_language_models()
|
|
40
|
-
|
|
41
|
-
# Define table columns: (data_key, header, style, width)
|
|
42
|
-
columns = [
|
|
43
|
-
("id", "ID", "dim", 36),
|
|
44
|
-
("provider", "Provider", ACCENT_STYLE, None),
|
|
45
|
-
("name", "Model", SUCCESS, None),
|
|
46
|
-
("base_url", "Base URL", INFO, None),
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
# Transform function for safe dictionary access
|
|
50
|
-
def transform_model(model: dict[str, Any]) -> dict[str, Any]:
|
|
51
|
-
"""Transform a model dictionary to a display row dictionary.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
model: Model dictionary to transform.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
Dictionary with id, provider, name, and base_url fields.
|
|
58
|
-
"""
|
|
59
|
-
return {
|
|
60
|
-
"id": str(model.get("id", "N/A")),
|
|
61
|
-
"provider": model.get("provider", "N/A"),
|
|
62
|
-
"name": model.get("name", "N/A"),
|
|
63
|
-
"base_url": model.get("base_url", "Default") or "Default",
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
output_list(ctx, models, "🧠 Available Language Models", columns, transform_model)
|
|
67
|
-
|
|
68
|
-
except Exception as e:
|
|
69
|
-
raise click.ClickException(str(e)) from e
|
glaip_sdk/cli/commands/tools.py
DELETED
|
@@ -1,576 +0,0 @@
|
|
|
1
|
-
"""Tool management commands.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
import re
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
import click
|
|
13
|
-
from glaip_sdk.branding import (
|
|
14
|
-
ACCENT_STYLE,
|
|
15
|
-
ERROR_STYLE,
|
|
16
|
-
INFO,
|
|
17
|
-
SUCCESS_STYLE,
|
|
18
|
-
WARNING_STYLE,
|
|
19
|
-
)
|
|
20
|
-
from glaip_sdk.cli.context import get_ctx_value, output_flags
|
|
21
|
-
from glaip_sdk.cli.display import (
|
|
22
|
-
display_api_error,
|
|
23
|
-
display_confirmation_prompt,
|
|
24
|
-
display_creation_success,
|
|
25
|
-
display_deletion_success,
|
|
26
|
-
display_update_success,
|
|
27
|
-
handle_json_output,
|
|
28
|
-
handle_rich_output,
|
|
29
|
-
)
|
|
30
|
-
from glaip_sdk.cli.io import fetch_raw_resource_details
|
|
31
|
-
from glaip_sdk.cli.io import (
|
|
32
|
-
load_resource_from_file_with_validation as load_resource_from_file,
|
|
33
|
-
)
|
|
34
|
-
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
35
|
-
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
36
|
-
from glaip_sdk.cli.utils import (
|
|
37
|
-
coerce_to_row,
|
|
38
|
-
format_datetime_fields,
|
|
39
|
-
get_client,
|
|
40
|
-
handle_best_effort_check,
|
|
41
|
-
handle_resource_export,
|
|
42
|
-
output_list,
|
|
43
|
-
output_result,
|
|
44
|
-
spinner_context,
|
|
45
|
-
)
|
|
46
|
-
from glaip_sdk.icons import ICON_TOOL
|
|
47
|
-
from glaip_sdk.utils.import_export import merge_import_with_cli_args
|
|
48
|
-
from rich.console import Console
|
|
49
|
-
|
|
50
|
-
console = Console()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@click.group(name="tools", no_args_is_help=True)
|
|
54
|
-
def tools_group() -> None:
|
|
55
|
-
"""Tool management operations."""
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _resolve_tool(ctx: Any, client: Any, ref: str, select: int | None = None) -> Any | None:
|
|
60
|
-
"""Resolve a tool by ID or name, handling ambiguous matches interactively.
|
|
61
|
-
|
|
62
|
-
This function provides tool-specific resolution logic. It uses
|
|
63
|
-
resolve_resource_reference to find tools by UUID or name, with interactive
|
|
64
|
-
selection when multiple matches are found.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
ctx: Click context for CLI operations.
|
|
68
|
-
client: API client instance.
|
|
69
|
-
ref: Tool reference (UUID string or name).
|
|
70
|
-
select: Pre-selected index for non-interactive mode (1-based).
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
Tool object if found, None otherwise.
|
|
74
|
-
"""
|
|
75
|
-
# Configure tool-specific resolution with standard fuzzy matching
|
|
76
|
-
get_by_id = client.get_tool
|
|
77
|
-
find_by_name = client.find_tools
|
|
78
|
-
return resolve_resource_reference(
|
|
79
|
-
ctx,
|
|
80
|
-
client,
|
|
81
|
-
ref,
|
|
82
|
-
"tool",
|
|
83
|
-
get_by_id,
|
|
84
|
-
find_by_name,
|
|
85
|
-
"Tool",
|
|
86
|
-
select=select,
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# ----------------------------- Helpers --------------------------------- #
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def _extract_internal_name(code: str) -> str:
|
|
94
|
-
"""Extract plugin class name attribute from tool code."""
|
|
95
|
-
m = re.search(r'^\s*name\s*:\s*str\s*=\s*"([^"]+)"', code, re.M)
|
|
96
|
-
if not m:
|
|
97
|
-
m = re.search(r'^\s*name\s*=\s*"([^"]+)"', code, re.M)
|
|
98
|
-
if not m:
|
|
99
|
-
raise click.ClickException(
|
|
100
|
-
"Could not find plugin 'name' attribute in the tool file. "
|
|
101
|
-
'Ensure your plugin class defines e.g. name: str = "my_tool".'
|
|
102
|
-
)
|
|
103
|
-
return m.group(1)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _validate_name_match(provided: str | None, internal: str) -> str:
|
|
107
|
-
"""Validate provided --name against internal name; return effective name."""
|
|
108
|
-
if provided and provided != internal:
|
|
109
|
-
raise click.ClickException(
|
|
110
|
-
f"--name '{provided}' does not match plugin internal name '{internal}'. "
|
|
111
|
-
"Either update the code or pass a matching --name."
|
|
112
|
-
)
|
|
113
|
-
return provided or internal
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def _check_duplicate_name(client: Any, tool_name: str) -> None:
|
|
117
|
-
"""Raise if a tool with the same name already exists."""
|
|
118
|
-
|
|
119
|
-
def _check_duplicate() -> None:
|
|
120
|
-
existing = client.find_tools(name=tool_name)
|
|
121
|
-
if existing:
|
|
122
|
-
raise click.ClickException(
|
|
123
|
-
f"A tool named '{tool_name}' already exists. "
|
|
124
|
-
"Please change your plugin's 'name' to a unique value, then re-run."
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
handle_best_effort_check(_check_duplicate)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def _parse_tags(tags: str | None) -> list[str]:
|
|
131
|
-
"""Return a cleaned list of tag strings from a comma-separated input."""
|
|
132
|
-
return [t.strip() for t in (tags.split(",") if tags else []) if t.strip()]
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def _handle_import_file(
|
|
136
|
-
import_file: str | None,
|
|
137
|
-
name: str | None,
|
|
138
|
-
description: str | None,
|
|
139
|
-
tags: tuple[str, ...] | None,
|
|
140
|
-
) -> dict[str, Any]:
|
|
141
|
-
"""Handle import file logic and merge with CLI arguments."""
|
|
142
|
-
if import_file:
|
|
143
|
-
import_data = load_resource_from_file(Path(import_file), "tool")
|
|
144
|
-
|
|
145
|
-
# Merge CLI args with imported data
|
|
146
|
-
cli_args = {
|
|
147
|
-
"name": name,
|
|
148
|
-
"description": description,
|
|
149
|
-
"tags": tags,
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return merge_import_with_cli_args(import_data, cli_args)
|
|
153
|
-
else:
|
|
154
|
-
# No import file - use CLI args directly
|
|
155
|
-
return {
|
|
156
|
-
"name": name,
|
|
157
|
-
"description": description,
|
|
158
|
-
"tags": tags,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def _create_tool_from_file(
|
|
163
|
-
client: Any,
|
|
164
|
-
file_path: str,
|
|
165
|
-
name: str | None,
|
|
166
|
-
description: str | None,
|
|
167
|
-
tags: str | None,
|
|
168
|
-
) -> Any:
|
|
169
|
-
"""Create tool from file upload."""
|
|
170
|
-
with open(file_path, encoding="utf-8") as f:
|
|
171
|
-
code_content = f.read()
|
|
172
|
-
|
|
173
|
-
internal_name = _extract_internal_name(code_content)
|
|
174
|
-
tool_name = _validate_name_match(name, internal_name)
|
|
175
|
-
_check_duplicate_name(client, tool_name)
|
|
176
|
-
|
|
177
|
-
# Upload the plugin code as-is (no rewrite)
|
|
178
|
-
return client.create_tool_from_code(
|
|
179
|
-
name=tool_name,
|
|
180
|
-
code=code_content,
|
|
181
|
-
framework="langchain", # Always langchain
|
|
182
|
-
description=description,
|
|
183
|
-
tags=_parse_tags(tags) if tags else None,
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def _validate_creation_parameters(
|
|
188
|
-
file: str | None,
|
|
189
|
-
import_file: str | None,
|
|
190
|
-
) -> None:
|
|
191
|
-
"""Validate required parameters for tool creation."""
|
|
192
|
-
if not file and not import_file:
|
|
193
|
-
raise click.ClickException("A tool file must be provided. Use --file to specify the tool file to upload.")
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@tools_group.command(name="list")
|
|
197
|
-
@output_flags()
|
|
198
|
-
@click.option(
|
|
199
|
-
"--type",
|
|
200
|
-
"tool_type",
|
|
201
|
-
help="Filter tools by type (e.g., custom, native)",
|
|
202
|
-
type=str,
|
|
203
|
-
required=False,
|
|
204
|
-
)
|
|
205
|
-
@click.pass_context
|
|
206
|
-
def list_tools(ctx: Any, tool_type: str | None) -> None:
|
|
207
|
-
"""List all tools."""
|
|
208
|
-
try:
|
|
209
|
-
client = get_client(ctx)
|
|
210
|
-
with spinner_context(
|
|
211
|
-
ctx,
|
|
212
|
-
"[bold blue]Fetching tools…[/bold blue]",
|
|
213
|
-
console_override=console,
|
|
214
|
-
):
|
|
215
|
-
tools = client.list_tools(tool_type=tool_type)
|
|
216
|
-
|
|
217
|
-
# Define table columns: (data_key, header, style, width)
|
|
218
|
-
columns = [
|
|
219
|
-
("id", "ID", "dim", 36),
|
|
220
|
-
("name", "Name", ACCENT_STYLE, None),
|
|
221
|
-
("framework", "Framework", INFO, None),
|
|
222
|
-
]
|
|
223
|
-
|
|
224
|
-
# Transform function for safe dictionary access
|
|
225
|
-
def transform_tool(tool: Any) -> dict[str, Any]:
|
|
226
|
-
"""Transform a tool object to a display row dictionary.
|
|
227
|
-
|
|
228
|
-
Args:
|
|
229
|
-
tool: Tool object to transform.
|
|
230
|
-
|
|
231
|
-
Returns:
|
|
232
|
-
Dictionary with id, name, and framework fields.
|
|
233
|
-
"""
|
|
234
|
-
row = coerce_to_row(tool, ["id", "name", "framework"])
|
|
235
|
-
# Ensure id is always a string
|
|
236
|
-
row["id"] = str(row["id"])
|
|
237
|
-
return row
|
|
238
|
-
|
|
239
|
-
output_list(ctx, tools, f"{ICON_TOOL} Available Tools", columns, transform_tool)
|
|
240
|
-
|
|
241
|
-
except Exception as e:
|
|
242
|
-
raise click.ClickException(str(e)) from e
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
@tools_group.command()
|
|
246
|
-
@click.argument("file_arg", required=False, type=click.Path(exists=True))
|
|
247
|
-
@click.option(
|
|
248
|
-
"--file",
|
|
249
|
-
type=click.Path(exists=True),
|
|
250
|
-
help="Tool file to upload",
|
|
251
|
-
)
|
|
252
|
-
@click.option(
|
|
253
|
-
"--name",
|
|
254
|
-
help="Tool name (extracted from script if file provided)",
|
|
255
|
-
)
|
|
256
|
-
@click.option(
|
|
257
|
-
"--description",
|
|
258
|
-
help="Tool description (extracted from script if file provided)",
|
|
259
|
-
)
|
|
260
|
-
@click.option(
|
|
261
|
-
"--tags",
|
|
262
|
-
help="Comma-separated tags for the tool",
|
|
263
|
-
)
|
|
264
|
-
@click.option(
|
|
265
|
-
"--import",
|
|
266
|
-
"import_file",
|
|
267
|
-
type=click.Path(exists=True, dir_okay=False),
|
|
268
|
-
help="Import tool configuration from JSON file",
|
|
269
|
-
)
|
|
270
|
-
@output_flags()
|
|
271
|
-
@click.pass_context
|
|
272
|
-
def create(
|
|
273
|
-
ctx: Any,
|
|
274
|
-
file_arg: str | None,
|
|
275
|
-
file: str | None,
|
|
276
|
-
name: str | None,
|
|
277
|
-
description: str | None,
|
|
278
|
-
tags: tuple[str, ...] | None,
|
|
279
|
-
import_file: str | None,
|
|
280
|
-
) -> None:
|
|
281
|
-
r"""Create a new tool.
|
|
282
|
-
|
|
283
|
-
\b
|
|
284
|
-
Examples:
|
|
285
|
-
aip tools create tool.py # Create from file
|
|
286
|
-
aip tools create --import tool.json # Create from exported configuration
|
|
287
|
-
"""
|
|
288
|
-
try:
|
|
289
|
-
client = get_client(ctx)
|
|
290
|
-
|
|
291
|
-
# Allow positional file argument for better DX (matches examples)
|
|
292
|
-
if not file and file_arg:
|
|
293
|
-
file = file_arg
|
|
294
|
-
|
|
295
|
-
# Handle import file and merge with CLI arguments
|
|
296
|
-
merged_data = _handle_import_file(import_file, name, description, tags)
|
|
297
|
-
|
|
298
|
-
# Extract merged values
|
|
299
|
-
name = merged_data.get("name")
|
|
300
|
-
description = merged_data.get("description")
|
|
301
|
-
tags = merged_data.get("tags")
|
|
302
|
-
|
|
303
|
-
# Validate required parameters
|
|
304
|
-
_validate_creation_parameters(file, import_file)
|
|
305
|
-
|
|
306
|
-
# Create tool from file (either direct file or import file)
|
|
307
|
-
with spinner_context(
|
|
308
|
-
ctx,
|
|
309
|
-
"[bold blue]Creating tool…[/bold blue]",
|
|
310
|
-
console_override=console,
|
|
311
|
-
):
|
|
312
|
-
tool = _create_tool_from_file(client, file, name, description, tags)
|
|
313
|
-
|
|
314
|
-
# Handle JSON output
|
|
315
|
-
handle_json_output(ctx, tool.model_dump())
|
|
316
|
-
|
|
317
|
-
# Handle Rich output
|
|
318
|
-
creation_method = "file upload (custom)"
|
|
319
|
-
rich_panel = display_creation_success(
|
|
320
|
-
"Tool",
|
|
321
|
-
tool.name,
|
|
322
|
-
tool.id,
|
|
323
|
-
Framework=getattr(tool, "framework", "N/A"),
|
|
324
|
-
Type=getattr(tool, "tool_type", "N/A"),
|
|
325
|
-
Description=getattr(tool, "description", "No description"),
|
|
326
|
-
Method=creation_method,
|
|
327
|
-
)
|
|
328
|
-
handle_rich_output(ctx, rich_panel)
|
|
329
|
-
|
|
330
|
-
except Exception as e:
|
|
331
|
-
handle_json_output(ctx, error=e)
|
|
332
|
-
if get_ctx_value(ctx, "view") != "json":
|
|
333
|
-
display_api_error(e, "tool creation")
|
|
334
|
-
raise click.ClickException(str(e)) from e
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
@tools_group.command()
|
|
338
|
-
@click.argument("tool_ref")
|
|
339
|
-
@click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
|
|
340
|
-
@click.option(
|
|
341
|
-
"--export",
|
|
342
|
-
type=click.Path(dir_okay=False, writable=True),
|
|
343
|
-
help="Export complete tool configuration to file (format auto-detected from .json/.yaml extension)",
|
|
344
|
-
)
|
|
345
|
-
@output_flags()
|
|
346
|
-
@click.pass_context
|
|
347
|
-
def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None:
|
|
348
|
-
r"""Get tool details.
|
|
349
|
-
|
|
350
|
-
\b
|
|
351
|
-
Examples:
|
|
352
|
-
aip tools get my-tool
|
|
353
|
-
aip tools get my-tool --export tool.json # Exports complete configuration as JSON
|
|
354
|
-
aip tools get my-tool --export tool.yaml # Exports complete configuration as YAML
|
|
355
|
-
"""
|
|
356
|
-
try:
|
|
357
|
-
client = get_client(ctx)
|
|
358
|
-
|
|
359
|
-
# Resolve tool with ambiguity handling
|
|
360
|
-
tool = _resolve_tool(ctx, client, tool_ref, select)
|
|
361
|
-
|
|
362
|
-
# Handle export option
|
|
363
|
-
if export:
|
|
364
|
-
handle_resource_export(
|
|
365
|
-
ctx,
|
|
366
|
-
tool,
|
|
367
|
-
Path(export),
|
|
368
|
-
resource_type="tool",
|
|
369
|
-
get_by_id_func=client.get_tool_by_id,
|
|
370
|
-
console_override=console,
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
# Try to fetch raw API data first to preserve ALL fields
|
|
374
|
-
with spinner_context(
|
|
375
|
-
ctx,
|
|
376
|
-
"[bold blue]Fetching detailed tool data…[/bold blue]",
|
|
377
|
-
console_override=console,
|
|
378
|
-
):
|
|
379
|
-
raw_tool_data = fetch_raw_resource_details(client, tool, "tools")
|
|
380
|
-
|
|
381
|
-
if raw_tool_data:
|
|
382
|
-
# Use raw API data - this preserves ALL fields
|
|
383
|
-
# Format dates for better display (minimal postprocessing)
|
|
384
|
-
formatted_data = format_datetime_fields(raw_tool_data)
|
|
385
|
-
|
|
386
|
-
# Display using output_result with raw data
|
|
387
|
-
output_result(
|
|
388
|
-
ctx,
|
|
389
|
-
formatted_data,
|
|
390
|
-
title="Tool Details",
|
|
391
|
-
panel_title=f"{ICON_TOOL} {raw_tool_data.get('name', 'Unknown')}",
|
|
392
|
-
)
|
|
393
|
-
else:
|
|
394
|
-
# Fall back to original method if raw fetch fails
|
|
395
|
-
console.print(f"[{WARNING_STYLE}]Falling back to Pydantic model data[/]")
|
|
396
|
-
|
|
397
|
-
# Create result data with all available fields from backend
|
|
398
|
-
result_data = {
|
|
399
|
-
"id": str(getattr(tool, "id", "N/A")),
|
|
400
|
-
"name": getattr(tool, "name", "N/A"),
|
|
401
|
-
"tool_type": getattr(tool, "tool_type", "N/A"),
|
|
402
|
-
"framework": getattr(tool, "framework", "N/A"),
|
|
403
|
-
"version": getattr(tool, "version", "N/A"),
|
|
404
|
-
"description": getattr(tool, "description", "N/A"),
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
output_result(
|
|
408
|
-
ctx,
|
|
409
|
-
result_data,
|
|
410
|
-
title="Tool Details",
|
|
411
|
-
panel_title=f"{ICON_TOOL} {tool.name}",
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
except Exception as e:
|
|
415
|
-
raise click.ClickException(str(e)) from e
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
@tools_group.command()
|
|
419
|
-
@click.argument("tool_id")
|
|
420
|
-
@click.option(
|
|
421
|
-
"--file",
|
|
422
|
-
type=click.Path(exists=True),
|
|
423
|
-
help="New tool file for code update (custom tools only)",
|
|
424
|
-
)
|
|
425
|
-
@click.option("--description", help="New description")
|
|
426
|
-
@click.option("--tags", help="Comma-separated tags")
|
|
427
|
-
@output_flags()
|
|
428
|
-
@click.pass_context
|
|
429
|
-
def update(
|
|
430
|
-
ctx: Any,
|
|
431
|
-
tool_id: str,
|
|
432
|
-
file: str | None,
|
|
433
|
-
description: str | None,
|
|
434
|
-
tags: tuple[str, ...] | None,
|
|
435
|
-
) -> None:
|
|
436
|
-
"""Update a tool (code or metadata)."""
|
|
437
|
-
try:
|
|
438
|
-
client = get_client(ctx)
|
|
439
|
-
|
|
440
|
-
# Get tool by ID (no ambiguity handling needed)
|
|
441
|
-
try:
|
|
442
|
-
with spinner_context(
|
|
443
|
-
ctx,
|
|
444
|
-
"[bold blue]Fetching tool…[/bold blue]",
|
|
445
|
-
console_override=console,
|
|
446
|
-
):
|
|
447
|
-
tool = client.get_tool_by_id(tool_id)
|
|
448
|
-
except Exception as e:
|
|
449
|
-
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}") from e
|
|
450
|
-
|
|
451
|
-
# Prepare update data
|
|
452
|
-
update_data = {}
|
|
453
|
-
if description:
|
|
454
|
-
update_data["description"] = description
|
|
455
|
-
if tags:
|
|
456
|
-
update_data["tags"] = [tag.strip() for tag in tags.split(",")]
|
|
457
|
-
|
|
458
|
-
if file:
|
|
459
|
-
# Update code via file upload (custom tools only)
|
|
460
|
-
if tool.tool_type != "custom":
|
|
461
|
-
raise click.ClickException(
|
|
462
|
-
"File updates are only supported for custom tools. "
|
|
463
|
-
f"Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
464
|
-
)
|
|
465
|
-
with spinner_context(
|
|
466
|
-
ctx,
|
|
467
|
-
"[bold blue]Uploading new tool code…[/bold blue]",
|
|
468
|
-
console_override=console,
|
|
469
|
-
):
|
|
470
|
-
updated_tool = client.tools.update_tool_via_file(tool.id, file, framework=tool.framework)
|
|
471
|
-
handle_rich_output(
|
|
472
|
-
ctx,
|
|
473
|
-
markup_text(f"[{SUCCESS_STYLE}]✓[/] Tool code updated from {file}"),
|
|
474
|
-
)
|
|
475
|
-
elif update_data:
|
|
476
|
-
# Update metadata only (native tools only)
|
|
477
|
-
if tool.tool_type != "native":
|
|
478
|
-
raise click.ClickException(
|
|
479
|
-
"Metadata updates are only supported for native tools. "
|
|
480
|
-
f"Tool '{tool.name}' is of type '{tool.tool_type}'."
|
|
481
|
-
)
|
|
482
|
-
with spinner_context(
|
|
483
|
-
ctx,
|
|
484
|
-
"[bold blue]Updating tool metadata…[/bold blue]",
|
|
485
|
-
console_override=console,
|
|
486
|
-
):
|
|
487
|
-
updated_tool = tool.update(**update_data)
|
|
488
|
-
handle_rich_output(ctx, markup_text(f"[{SUCCESS_STYLE}]✓[/] Tool metadata updated"))
|
|
489
|
-
else:
|
|
490
|
-
handle_rich_output(ctx, markup_text(f"[{WARNING_STYLE}]No updates specified[/]"))
|
|
491
|
-
return
|
|
492
|
-
|
|
493
|
-
handle_json_output(ctx, updated_tool.model_dump())
|
|
494
|
-
handle_rich_output(ctx, display_update_success("Tool", updated_tool.name))
|
|
495
|
-
|
|
496
|
-
except Exception as e:
|
|
497
|
-
handle_json_output(ctx, error=e)
|
|
498
|
-
if get_ctx_value(ctx, "view") != "json":
|
|
499
|
-
display_api_error(e, "tool update")
|
|
500
|
-
raise click.ClickException(str(e)) from e
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
@tools_group.command()
|
|
504
|
-
@click.argument("tool_id")
|
|
505
|
-
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation")
|
|
506
|
-
@output_flags()
|
|
507
|
-
@click.pass_context
|
|
508
|
-
def delete(ctx: Any, tool_id: str, yes: bool) -> None:
|
|
509
|
-
"""Delete a tool."""
|
|
510
|
-
try:
|
|
511
|
-
client = get_client(ctx)
|
|
512
|
-
|
|
513
|
-
# Get tool by ID (no ambiguity handling needed)
|
|
514
|
-
try:
|
|
515
|
-
with spinner_context(
|
|
516
|
-
ctx,
|
|
517
|
-
"[bold blue]Fetching tool…[/bold blue]",
|
|
518
|
-
console_override=console,
|
|
519
|
-
):
|
|
520
|
-
tool = client.get_tool_by_id(tool_id)
|
|
521
|
-
except Exception as e:
|
|
522
|
-
raise click.ClickException(f"Tool with ID '{tool_id}' not found: {e}") from e
|
|
523
|
-
|
|
524
|
-
# Confirm deletion via centralized display helper
|
|
525
|
-
if not yes and not display_confirmation_prompt("Tool", tool.name):
|
|
526
|
-
return
|
|
527
|
-
|
|
528
|
-
with spinner_context(
|
|
529
|
-
ctx,
|
|
530
|
-
"[bold blue]Deleting tool…[/bold blue]",
|
|
531
|
-
console_override=console,
|
|
532
|
-
):
|
|
533
|
-
tool.delete()
|
|
534
|
-
|
|
535
|
-
handle_json_output(
|
|
536
|
-
ctx,
|
|
537
|
-
{
|
|
538
|
-
"success": True,
|
|
539
|
-
"message": f"Tool '{tool.name}' deleted",
|
|
540
|
-
},
|
|
541
|
-
)
|
|
542
|
-
handle_rich_output(ctx, display_deletion_success("Tool", tool.name))
|
|
543
|
-
|
|
544
|
-
except Exception as e:
|
|
545
|
-
handle_json_output(ctx, error=e)
|
|
546
|
-
if get_ctx_value(ctx, "view") != "json":
|
|
547
|
-
display_api_error(e, "tool deletion")
|
|
548
|
-
raise click.ClickException(str(e)) from e
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
@tools_group.command("script")
|
|
552
|
-
@click.argument("tool_id")
|
|
553
|
-
@output_flags()
|
|
554
|
-
@click.pass_context
|
|
555
|
-
def script(ctx: Any, tool_id: str) -> None:
|
|
556
|
-
"""Get tool script content."""
|
|
557
|
-
try:
|
|
558
|
-
client = get_client(ctx)
|
|
559
|
-
with spinner_context(
|
|
560
|
-
ctx,
|
|
561
|
-
"[bold blue]Fetching tool script…[/bold blue]",
|
|
562
|
-
console_override=console,
|
|
563
|
-
):
|
|
564
|
-
script_content = client.get_tool_script(tool_id)
|
|
565
|
-
|
|
566
|
-
if get_ctx_value(ctx, "view") == "json":
|
|
567
|
-
click.echo(json.dumps({"script": script_content}, indent=2))
|
|
568
|
-
else:
|
|
569
|
-
console.print(f"[{SUCCESS_STYLE}]📜 Tool Script for '{tool_id}':[/]")
|
|
570
|
-
console.print(script_content)
|
|
571
|
-
|
|
572
|
-
except Exception as e:
|
|
573
|
-
handle_json_output(ctx, error=e)
|
|
574
|
-
if get_ctx_value(ctx, "view") != "json":
|
|
575
|
-
print_markup(f"[{ERROR_STYLE}]Error getting tool script: {e}[/]", console=console)
|
|
576
|
-
raise click.ClickException(str(e)) from e
|