glaip-sdk 0.2.1__py3-none-any.whl → 0.3.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/_version.py +8 -0
- glaip_sdk/branding.py +13 -0
- glaip_sdk/cli/commands/agents.py +180 -39
- glaip_sdk/cli/commands/mcps.py +44 -18
- glaip_sdk/cli/commands/models.py +11 -5
- glaip_sdk/cli/commands/tools.py +35 -16
- glaip_sdk/cli/commands/transcripts.py +8 -0
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/display.py +34 -19
- glaip_sdk/cli/main.py +14 -7
- glaip_sdk/cli/masking.py +8 -33
- glaip_sdk/cli/pager.py +9 -10
- glaip_sdk/cli/slash/agent_session.py +57 -20
- glaip_sdk/cli/slash/prompt.py +8 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +341 -46
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +632 -0
- glaip_sdk/cli/transcript/viewer.py +232 -32
- glaip_sdk/cli/update_notifier.py +2 -2
- glaip_sdk/cli/utils.py +266 -35
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +30 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +186 -22
- glaip_sdk/client/main.py +23 -6
- glaip_sdk/client/mcps.py +2 -4
- glaip_sdk/client/run_rendering.py +66 -0
- glaip_sdk/client/tools.py +2 -3
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/models/__init__.py +56 -0
- glaip_sdk/models/agent_runs.py +117 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/utils/client_utils.py +13 -0
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/import_export.py +6 -9
- glaip_sdk/utils/rendering/__init__.py +122 -1
- glaip_sdk/utils/rendering/renderer/base.py +3 -7
- glaip_sdk/utils/rendering/renderer/debug.py +0 -1
- glaip_sdk/utils/rendering/renderer/stream.py +4 -12
- glaip_sdk/utils/rendering/steps.py +1 -0
- glaip_sdk/utils/resource_refs.py +26 -15
- glaip_sdk/utils/serialization.py +16 -0
- {glaip_sdk-0.2.1.dist-info → glaip_sdk-0.3.0.dist-info}/METADATA +24 -2
- glaip_sdk-0.3.0.dist-info/RECORD +94 -0
- glaip_sdk-0.2.1.dist-info/RECORD +0 -86
- {glaip_sdk-0.2.1.dist-info → glaip_sdk-0.3.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.2.1.dist-info → glaip_sdk-0.3.0.dist-info}/entry_points.txt +0 -0
glaip_sdk/_version.py
CHANGED
|
@@ -14,6 +14,14 @@ except Exception: # pragma: no cover - extremely unlikely
|
|
|
14
14
|
PackageNotFoundError = Exception # type: ignore
|
|
15
15
|
|
|
16
16
|
def version(_: str) -> str: # type: ignore
|
|
17
|
+
"""Fallback version function when importlib.metadata is unavailable.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
_: Package name (ignored in fallback).
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Default dev version string.
|
|
24
|
+
"""
|
|
17
25
|
return "0.0.0.dev0"
|
|
18
26
|
|
|
19
27
|
|
glaip_sdk/branding.py
CHANGED
|
@@ -90,6 +90,14 @@ GDP Labs AI Agents Package
|
|
|
90
90
|
# ---- small helpers --------------------------------------------------------
|
|
91
91
|
@staticmethod
|
|
92
92
|
def _auto_version(package_name: str | None) -> str:
|
|
93
|
+
"""Auto-detect version from environment, package metadata, or fallback.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
package_name: Optional package name to read version from installed metadata.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Version string from AIP_VERSION env var, package metadata, or SDK_VERSION fallback.
|
|
100
|
+
"""
|
|
93
101
|
# Priority: env → package metadata → fallback
|
|
94
102
|
env_version = os.getenv("AIP_VERSION")
|
|
95
103
|
if env_version:
|
|
@@ -103,6 +111,11 @@ GDP Labs AI Agents Package
|
|
|
103
111
|
|
|
104
112
|
@staticmethod
|
|
105
113
|
def _make_console() -> Console:
|
|
114
|
+
"""Create a Rich Console instance respecting NO_COLOR environment variables.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Console instance with color system configured based on environment.
|
|
118
|
+
"""
|
|
106
119
|
# Respect NO_COLOR/AIP_NO_COLOR environment variables
|
|
107
120
|
no_color_env = os.getenv("NO_COLOR") is not None or os.getenv("AIP_NO_COLOR") is not None
|
|
108
121
|
if no_color_env:
|
glaip_sdk/cli/commands/agents.py
CHANGED
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
11
|
from collections.abc import Mapping
|
|
12
|
+
from copy import deepcopy
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
@@ -18,6 +19,7 @@ from rich.console import Console
|
|
|
18
19
|
from glaip_sdk.branding import (
|
|
19
20
|
ACCENT_STYLE,
|
|
20
21
|
ERROR_STYLE,
|
|
22
|
+
HINT_PREFIX_STYLE,
|
|
21
23
|
INFO,
|
|
22
24
|
SUCCESS,
|
|
23
25
|
SUCCESS_STYLE,
|
|
@@ -33,6 +35,7 @@ from glaip_sdk.cli.agent_config import (
|
|
|
33
35
|
sanitize_agent_config_for_cli as sanitize_agent_config,
|
|
34
36
|
)
|
|
35
37
|
from glaip_sdk.cli.context import get_ctx_value, output_flags
|
|
38
|
+
from glaip_sdk.cli.constants import DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
36
39
|
from glaip_sdk.cli.display import (
|
|
37
40
|
build_resource_result_data,
|
|
38
41
|
display_agent_run_suggestions,
|
|
@@ -61,9 +64,12 @@ from glaip_sdk.cli.utils import (
|
|
|
61
64
|
build_renderer,
|
|
62
65
|
coerce_to_row,
|
|
63
66
|
get_client,
|
|
67
|
+
handle_resource_export,
|
|
68
|
+
in_slash_mode,
|
|
64
69
|
output_list,
|
|
65
70
|
output_result,
|
|
66
71
|
spinner_context,
|
|
72
|
+
with_client_and_spinner,
|
|
67
73
|
)
|
|
68
74
|
from glaip_sdk.cli.validators import (
|
|
69
75
|
validate_agent_instruction_cli as validate_agent_instruction,
|
|
@@ -74,7 +80,7 @@ from glaip_sdk.cli.validators import (
|
|
|
74
80
|
from glaip_sdk.cli.validators import (
|
|
75
81
|
validate_timeout_cli as validate_timeout,
|
|
76
82
|
)
|
|
77
|
-
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT, DEFAULT_MODEL
|
|
83
|
+
from glaip_sdk.config.constants import AGENT_CONFIG_FIELDS, DEFAULT_AGENT_RUN_TIMEOUT, DEFAULT_MODEL
|
|
78
84
|
from glaip_sdk.exceptions import AgentTimeoutError
|
|
79
85
|
from glaip_sdk.icons import ICON_AGENT
|
|
80
86
|
from glaip_sdk.utils import format_datetime, is_uuid
|
|
@@ -88,6 +94,8 @@ console = Console()
|
|
|
88
94
|
# Error message constants
|
|
89
95
|
AGENT_NOT_FOUND_ERROR = "Agent not found"
|
|
90
96
|
|
|
97
|
+
# Instruction preview controls
|
|
98
|
+
|
|
91
99
|
|
|
92
100
|
def _safe_agent_attribute(agent: Any, name: str) -> Any:
|
|
93
101
|
"""Return attribute value for ``name`` while filtering Mock sentinels."""
|
|
@@ -140,10 +148,7 @@ def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
|
140
148
|
"description",
|
|
141
149
|
"model",
|
|
142
150
|
"agent_config",
|
|
143
|
-
"
|
|
144
|
-
"agents",
|
|
145
|
-
"mcps",
|
|
146
|
-
"timeout",
|
|
151
|
+
*[field for field in AGENT_CONFIG_FIELDS if field not in ("name", "instruction", "model")],
|
|
147
152
|
"tool_configs",
|
|
148
153
|
)
|
|
149
154
|
|
|
@@ -307,12 +312,92 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
307
312
|
return result_data
|
|
308
313
|
|
|
309
314
|
|
|
310
|
-
def
|
|
315
|
+
def _clamp_instruction_preview_limit(limit: int | None) -> int:
|
|
316
|
+
"""Normalise preview limit; 0 disables trimming."""
|
|
317
|
+
default = DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
318
|
+
if limit is None: # pragma: no cover
|
|
319
|
+
return default
|
|
320
|
+
try:
|
|
321
|
+
limit_value = int(limit)
|
|
322
|
+
except (TypeError, ValueError): # pragma: no cover - defensive parsing
|
|
323
|
+
return default
|
|
324
|
+
|
|
325
|
+
if limit_value <= 0:
|
|
326
|
+
return 0
|
|
327
|
+
|
|
328
|
+
return limit_value
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _build_instruction_preview(value: Any, limit: int) -> tuple[Any, bool]:
|
|
332
|
+
"""Return a trimmed preview for long instruction strings."""
|
|
333
|
+
if not isinstance(value, str) or limit <= 0: # pragma: no cover
|
|
334
|
+
return value, False
|
|
335
|
+
|
|
336
|
+
if len(value) <= limit:
|
|
337
|
+
return value, False
|
|
338
|
+
|
|
339
|
+
trimmed_value = value[:limit].rstrip()
|
|
340
|
+
preview = f"{trimmed_value}\n\n... (preview trimmed)"
|
|
341
|
+
return preview, True
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _prepare_agent_details_payload(
|
|
345
|
+
data: dict[str, Any],
|
|
346
|
+
*,
|
|
347
|
+
instruction_preview_limit: int,
|
|
348
|
+
) -> tuple[dict[str, Any], bool]:
|
|
349
|
+
"""Return payload ready for rendering plus trim indicator."""
|
|
350
|
+
payload = deepcopy(data)
|
|
351
|
+
trimmed = False
|
|
352
|
+
if instruction_preview_limit > 0:
|
|
353
|
+
preview, trimmed = _build_instruction_preview(payload.get("instruction"), instruction_preview_limit)
|
|
354
|
+
if trimmed:
|
|
355
|
+
payload["instruction"] = preview
|
|
356
|
+
return payload, trimmed
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _show_instruction_trim_hint(
|
|
360
|
+
ctx: Any,
|
|
361
|
+
*,
|
|
362
|
+
trimmed: bool,
|
|
363
|
+
preview_limit: int,
|
|
364
|
+
) -> None:
|
|
365
|
+
"""Render hint describing how to expand or collapse the instruction preview."""
|
|
366
|
+
if not trimmed or preview_limit <= 0:
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
view = get_ctx_value(ctx, "view", "rich") if ctx is not None else "rich"
|
|
370
|
+
if view != "rich": # pragma: no cover - non-rich view handling
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
suffix = f"[dim](preview: {preview_limit:,} chars)[/]"
|
|
374
|
+
if in_slash_mode(ctx):
|
|
375
|
+
console.print(
|
|
376
|
+
f"[{HINT_PREFIX_STYLE}]Tip:[/] Use '/details' again to toggle between trimmed and full prompts {suffix}"
|
|
377
|
+
)
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
console.print( # pragma: no cover - fallback hint rendering
|
|
381
|
+
f"[{HINT_PREFIX_STYLE}]Tip:[/] Run 'aip agents get <agent> --instruction-preview <n>' "
|
|
382
|
+
f"to control prompt preview length {suffix}"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _display_agent_details(
|
|
387
|
+
ctx: Any,
|
|
388
|
+
client: Any,
|
|
389
|
+
agent: Any,
|
|
390
|
+
*,
|
|
391
|
+
instruction_preview_limit: int | None = None,
|
|
392
|
+
) -> None:
|
|
311
393
|
"""Display full agent details using raw API data to preserve ALL fields."""
|
|
312
394
|
if agent is None:
|
|
313
395
|
handle_rich_output(ctx, markup_text(f"[{ERROR_STYLE}]❌ No agent provided[/]"))
|
|
314
396
|
return
|
|
315
397
|
|
|
398
|
+
preview_limit = _clamp_instruction_preview_limit(instruction_preview_limit)
|
|
399
|
+
trimmed_instruction = False
|
|
400
|
+
|
|
316
401
|
# Try to fetch and format raw agent data first
|
|
317
402
|
with spinner_context(
|
|
318
403
|
ctx,
|
|
@@ -324,9 +409,13 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
324
409
|
if formatted_data:
|
|
325
410
|
# Use raw API data - this preserves ALL fields including account_id
|
|
326
411
|
panel_title = f"{ICON_AGENT} {formatted_data.get('name', 'Unknown')}"
|
|
412
|
+
payload, trimmed_instruction = _prepare_agent_details_payload(
|
|
413
|
+
formatted_data,
|
|
414
|
+
instruction_preview_limit=preview_limit,
|
|
415
|
+
)
|
|
327
416
|
output_result(
|
|
328
417
|
ctx,
|
|
329
|
-
|
|
418
|
+
payload,
|
|
330
419
|
title=panel_title,
|
|
331
420
|
)
|
|
332
421
|
else:
|
|
@@ -344,12 +433,22 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
344
433
|
result_data = _format_fallback_agent_data(client, agent)
|
|
345
434
|
|
|
346
435
|
# Display using output_result
|
|
436
|
+
payload, trimmed_instruction = _prepare_agent_details_payload(
|
|
437
|
+
result_data,
|
|
438
|
+
instruction_preview_limit=preview_limit,
|
|
439
|
+
)
|
|
347
440
|
output_result(
|
|
348
441
|
ctx,
|
|
349
|
-
|
|
442
|
+
payload,
|
|
350
443
|
title="Agent Details",
|
|
351
444
|
)
|
|
352
445
|
|
|
446
|
+
_show_instruction_trim_hint(
|
|
447
|
+
ctx,
|
|
448
|
+
trimmed=trimmed_instruction,
|
|
449
|
+
preview_limit=preview_limit,
|
|
450
|
+
)
|
|
451
|
+
|
|
353
452
|
|
|
354
453
|
@click.group(name="agents", no_args_is_help=True)
|
|
355
454
|
def agents_group() -> None:
|
|
@@ -357,7 +456,6 @@ def agents_group() -> None:
|
|
|
357
456
|
pass
|
|
358
457
|
|
|
359
458
|
|
|
360
|
-
# pylint: disable=duplicate-code
|
|
361
459
|
def _resolve_agent(
|
|
362
460
|
ctx: Any,
|
|
363
461
|
client: Any,
|
|
@@ -365,26 +463,38 @@ def _resolve_agent(
|
|
|
365
463
|
select: int | None = None,
|
|
366
464
|
interface_preference: str = "fuzzy",
|
|
367
465
|
) -> Any | None:
|
|
368
|
-
"""Resolve agent
|
|
466
|
+
"""Resolve an agent by ID or name, supporting fuzzy and questionary interfaces.
|
|
467
|
+
|
|
468
|
+
This function provides agent-specific resolution with flexible UI options.
|
|
469
|
+
It wraps resolve_resource_reference with agent-specific configuration, allowing
|
|
470
|
+
users to choose between fuzzy search and traditional questionary selection.
|
|
369
471
|
|
|
370
472
|
Args:
|
|
371
|
-
ctx: Click context
|
|
372
|
-
client: AIP client instance
|
|
373
|
-
ref: Agent
|
|
374
|
-
select: Pre-selected
|
|
375
|
-
interface_preference: "fuzzy" for
|
|
473
|
+
ctx: Click context for CLI command execution.
|
|
474
|
+
client: AIP SDK client instance.
|
|
475
|
+
ref: Agent identifier (UUID or name string).
|
|
476
|
+
select: Pre-selected index for non-interactive resolution (1-based).
|
|
477
|
+
interface_preference: UI preference - "fuzzy" for search or "questionary" for list.
|
|
376
478
|
|
|
377
479
|
Returns:
|
|
378
|
-
|
|
480
|
+
Agent object when found, None when resolution fails.
|
|
379
481
|
"""
|
|
482
|
+
# Configure agent-specific resolution parameters
|
|
483
|
+
resolution_config = {
|
|
484
|
+
"resource_type": "agent",
|
|
485
|
+
"get_by_id": client.agents.get_agent_by_id,
|
|
486
|
+
"find_by_name": client.agents.find_agents,
|
|
487
|
+
"label": "Agent",
|
|
488
|
+
}
|
|
489
|
+
# Use agent-specific resolution with flexible interface preference
|
|
380
490
|
return resolve_resource_reference(
|
|
381
491
|
ctx,
|
|
382
492
|
client,
|
|
383
493
|
ref,
|
|
384
|
-
"
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
"
|
|
494
|
+
resolution_config["resource_type"],
|
|
495
|
+
resolution_config["get_by_id"],
|
|
496
|
+
resolution_config["find_by_name"],
|
|
497
|
+
resolution_config["label"],
|
|
388
498
|
select=select,
|
|
389
499
|
interface_preference=interface_preference,
|
|
390
500
|
)
|
|
@@ -414,19 +524,20 @@ def list_agents(
|
|
|
414
524
|
) -> None:
|
|
415
525
|
"""List agents with optional filtering."""
|
|
416
526
|
try:
|
|
417
|
-
|
|
418
|
-
with spinner_context(
|
|
527
|
+
with with_client_and_spinner(
|
|
419
528
|
ctx,
|
|
420
529
|
"[bold blue]Fetching agents…[/bold blue]",
|
|
421
530
|
console_override=console,
|
|
422
|
-
):
|
|
423
|
-
agents
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
531
|
+
) as client:
|
|
532
|
+
# Query agents with specified filters
|
|
533
|
+
filter_params = {
|
|
534
|
+
"agent_type": agent_type,
|
|
535
|
+
"framework": framework,
|
|
536
|
+
"name": name,
|
|
537
|
+
"version": version,
|
|
538
|
+
"sync_langflow_agents": sync_langflow,
|
|
539
|
+
}
|
|
540
|
+
agents = client.agents.list_agents(**filter_params)
|
|
430
541
|
|
|
431
542
|
# Define table columns: (data_key, header, style, width)
|
|
432
543
|
columns = [
|
|
@@ -439,6 +550,14 @@ def list_agents(
|
|
|
439
550
|
|
|
440
551
|
# Transform function for safe attribute access
|
|
441
552
|
def transform_agent(agent: Any) -> dict[str, Any]:
|
|
553
|
+
"""Transform an agent object to a display row dictionary.
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
agent: Agent object to transform.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
Dictionary with id, name, type, framework, and version fields.
|
|
560
|
+
"""
|
|
442
561
|
row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
|
|
443
562
|
# Ensure id is always a string
|
|
444
563
|
row["id"] = str(row["id"])
|
|
@@ -488,9 +607,22 @@ def list_agents(
|
|
|
488
607
|
type=click.Path(dir_okay=False, writable=True),
|
|
489
608
|
help="Export complete agent configuration to file (format auto-detected from .json/.yaml extension)",
|
|
490
609
|
)
|
|
610
|
+
@click.option(
|
|
611
|
+
"--instruction-preview",
|
|
612
|
+
type=int,
|
|
613
|
+
default=0,
|
|
614
|
+
show_default=True,
|
|
615
|
+
help="Instruction preview length when printing instructions (0 shows full prompt).",
|
|
616
|
+
)
|
|
491
617
|
@output_flags()
|
|
492
618
|
@click.pass_context
|
|
493
|
-
def get(
|
|
619
|
+
def get(
|
|
620
|
+
ctx: Any,
|
|
621
|
+
agent_ref: str,
|
|
622
|
+
select: int | None,
|
|
623
|
+
export: str | None,
|
|
624
|
+
instruction_preview: int,
|
|
625
|
+
) -> None:
|
|
494
626
|
r"""Get agent details.
|
|
495
627
|
|
|
496
628
|
\b
|
|
@@ -500,30 +632,39 @@ def get(ctx: Any, agent_ref: str, select: int | None, export: str | None) -> Non
|
|
|
500
632
|
aip agents get my-agent --export agent.yaml # Exports complete configuration as YAML
|
|
501
633
|
"""
|
|
502
634
|
try:
|
|
503
|
-
client
|
|
635
|
+
# Initialize API client for agent retrieval
|
|
636
|
+
api_client = get_client(ctx)
|
|
504
637
|
|
|
505
|
-
# Resolve agent
|
|
506
|
-
agent = _resolve_agent(ctx,
|
|
638
|
+
# Resolve agent reference using questionary interface for better UX
|
|
639
|
+
agent = _resolve_agent(ctx, api_client, agent_ref, select, interface_preference="questionary")
|
|
507
640
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
from glaip_sdk.cli.utils import handle_resource_export
|
|
641
|
+
if not agent:
|
|
642
|
+
raise click.ClickException(f"Agent '{agent_ref}' not found")
|
|
511
643
|
|
|
644
|
+
# Handle export option if requested
|
|
645
|
+
if export:
|
|
512
646
|
handle_resource_export(
|
|
513
647
|
ctx,
|
|
514
648
|
agent,
|
|
515
649
|
Path(export),
|
|
516
650
|
resource_type="agent",
|
|
517
|
-
get_by_id_func=
|
|
651
|
+
get_by_id_func=api_client.agents.get_agent_by_id,
|
|
518
652
|
console_override=console,
|
|
519
653
|
)
|
|
520
654
|
|
|
521
655
|
# Display full agent details using the standardized helper
|
|
522
|
-
_display_agent_details(
|
|
656
|
+
_display_agent_details(
|
|
657
|
+
ctx,
|
|
658
|
+
api_client,
|
|
659
|
+
agent,
|
|
660
|
+
instruction_preview_limit=instruction_preview,
|
|
661
|
+
)
|
|
523
662
|
|
|
524
663
|
# Show run suggestions via centralized display helper
|
|
525
664
|
handle_rich_output(ctx, display_agent_run_suggestions(agent))
|
|
526
665
|
|
|
666
|
+
except click.ClickException:
|
|
667
|
+
raise
|
|
527
668
|
except Exception as e:
|
|
528
669
|
raise click.ClickException(str(e)) from e
|
|
529
670
|
|
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -43,10 +43,13 @@ from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
|
43
43
|
from glaip_sdk.cli.rich_helpers import print_markup
|
|
44
44
|
from glaip_sdk.cli.utils import (
|
|
45
45
|
coerce_to_row,
|
|
46
|
+
fetch_resource_for_export,
|
|
47
|
+
format_datetime_fields,
|
|
46
48
|
get_client,
|
|
47
49
|
output_list,
|
|
48
50
|
output_result,
|
|
49
51
|
spinner_context,
|
|
52
|
+
with_client_and_spinner,
|
|
50
53
|
)
|
|
51
54
|
from glaip_sdk.config.constants import (
|
|
52
55
|
DEFAULT_MCP_TYPE,
|
|
@@ -298,28 +301,37 @@ def mcps_group() -> None:
|
|
|
298
301
|
pass
|
|
299
302
|
|
|
300
303
|
|
|
301
|
-
def _resolve_mcp(ctx: Any, client: Any, ref: str, select: int | None = None) -> Any | None:
|
|
302
|
-
"""Resolve MCP
|
|
304
|
+
def _resolve_mcp(ctx: Any, client: Any, ref: str, select: int | None = None) -> Any | None:
|
|
305
|
+
"""Resolve an MCP server by ID or name, with interactive selection support.
|
|
306
|
+
|
|
307
|
+
This function provides MCP-specific resolution logic. It delegates to
|
|
308
|
+
resolve_resource_reference for MCP-specific resolution, supporting UUID
|
|
309
|
+
lookups and name-based fuzzy matching.
|
|
303
310
|
|
|
304
311
|
Args:
|
|
305
|
-
ctx: Click context
|
|
306
|
-
client: API client
|
|
307
|
-
ref: MCP
|
|
308
|
-
select:
|
|
312
|
+
ctx: Click context for command execution.
|
|
313
|
+
client: API client for backend operations.
|
|
314
|
+
ref: MCP identifier (UUID or name string).
|
|
315
|
+
select: Optional selection index when multiple MCPs match (1-based).
|
|
309
316
|
|
|
310
317
|
Returns:
|
|
311
|
-
MCP
|
|
318
|
+
MCP instance if resolution succeeds, None if not found.
|
|
312
319
|
|
|
313
320
|
Raises:
|
|
314
|
-
ClickException:
|
|
321
|
+
click.ClickException: When resolution fails or selection is invalid.
|
|
315
322
|
"""
|
|
323
|
+
# Configure MCP-specific resolution functions
|
|
324
|
+
mcp_client = client.mcps
|
|
325
|
+
get_by_id_func = mcp_client.get_mcp_by_id
|
|
326
|
+
find_by_name_func = mcp_client.find_mcps
|
|
327
|
+
# Use MCP-specific resolution with standard fuzzy matching
|
|
316
328
|
return resolve_resource_reference(
|
|
317
329
|
ctx,
|
|
318
330
|
client,
|
|
319
331
|
ref,
|
|
320
332
|
"mcp",
|
|
321
|
-
|
|
322
|
-
|
|
333
|
+
get_by_id_func,
|
|
334
|
+
find_by_name_func,
|
|
323
335
|
"MCP",
|
|
324
336
|
select=select,
|
|
325
337
|
)
|
|
@@ -512,12 +524,11 @@ def list_mcps(ctx: Any) -> None:
|
|
|
512
524
|
ClickException: If API request fails
|
|
513
525
|
"""
|
|
514
526
|
try:
|
|
515
|
-
|
|
516
|
-
with spinner_context(
|
|
527
|
+
with with_client_and_spinner(
|
|
517
528
|
ctx,
|
|
518
529
|
"[bold blue]Fetching MCPs…[/bold blue]",
|
|
519
530
|
console_override=console,
|
|
520
|
-
):
|
|
531
|
+
) as client:
|
|
521
532
|
mcps = client.mcps.list_mcps()
|
|
522
533
|
|
|
523
534
|
# Define table columns: (data_key, header, style, width)
|
|
@@ -529,6 +540,14 @@ def list_mcps(ctx: Any) -> None:
|
|
|
529
540
|
|
|
530
541
|
# Transform function for safe dictionary access
|
|
531
542
|
def transform_mcp(mcp: Any) -> dict[str, Any]:
|
|
543
|
+
"""Transform an MCP object to a display row dictionary.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
mcp: MCP object to transform.
|
|
547
|
+
|
|
548
|
+
Returns:
|
|
549
|
+
Dictionary with id, name, and config fields.
|
|
550
|
+
"""
|
|
532
551
|
row = coerce_to_row(mcp, ["id", "name", "config"])
|
|
533
552
|
# Ensure id is always a string
|
|
534
553
|
row["id"] = str(row["id"])
|
|
@@ -604,8 +623,10 @@ def create(
|
|
|
604
623
|
aip mcps create --import mcp-export.json --name new-name --transport sse
|
|
605
624
|
"""
|
|
606
625
|
try:
|
|
607
|
-
client
|
|
626
|
+
# Get API client instance for MCP operations
|
|
627
|
+
api_client = get_client(ctx)
|
|
608
628
|
|
|
629
|
+
# Process import file if specified, otherwise use None
|
|
609
630
|
import_payload = _load_import_ready_payload(import_file) if import_file is not None else None
|
|
610
631
|
|
|
611
632
|
merged_payload, missing_fields = _merge_import_payload(
|
|
@@ -649,7 +670,7 @@ def create(
|
|
|
649
670
|
if mcp_metadata is not None:
|
|
650
671
|
create_kwargs["mcp_metadata"] = mcp_metadata
|
|
651
672
|
|
|
652
|
-
mcp =
|
|
673
|
+
mcp = api_client.mcps.create_mcp(**create_kwargs)
|
|
653
674
|
|
|
654
675
|
# Handle JSON output
|
|
655
676
|
handle_json_output(ctx, mcp.model_dump())
|
|
@@ -696,7 +717,6 @@ def _handle_mcp_export(
|
|
|
696
717
|
detected_format = detect_export_format(export_path)
|
|
697
718
|
|
|
698
719
|
# Always export comprehensive data - re-fetch with full details
|
|
699
|
-
from glaip_sdk.cli.utils import fetch_resource_for_export
|
|
700
720
|
|
|
701
721
|
mcp = fetch_resource_for_export(
|
|
702
722
|
ctx,
|
|
@@ -775,8 +795,6 @@ def _display_mcp_details(ctx: Any, client: Any, mcp: Any) -> None:
|
|
|
775
795
|
|
|
776
796
|
if raw_mcp_data:
|
|
777
797
|
# Use raw API data - this preserves ALL fields
|
|
778
|
-
from glaip_sdk.cli.utils import format_datetime_fields
|
|
779
|
-
|
|
780
798
|
formatted_data = format_datetime_fields(raw_mcp_data)
|
|
781
799
|
|
|
782
800
|
output_result(
|
|
@@ -897,6 +915,14 @@ def list_tools(ctx: Any, mcp_ref: str) -> None:
|
|
|
897
915
|
|
|
898
916
|
# Transform function for safe dictionary access
|
|
899
917
|
def transform_tool(tool: dict[str, Any]) -> dict[str, Any]:
|
|
918
|
+
"""Transform a tool dictionary to a display row dictionary.
|
|
919
|
+
|
|
920
|
+
Args:
|
|
921
|
+
tool: Tool dictionary to transform.
|
|
922
|
+
|
|
923
|
+
Returns:
|
|
924
|
+
Dictionary with name and description fields.
|
|
925
|
+
"""
|
|
900
926
|
return {
|
|
901
927
|
"name": tool.get("name", "N/A"),
|
|
902
928
|
"description": tool.get("description", "N/A")[:47] + "..."
|
glaip_sdk/cli/commands/models.py
CHANGED
|
@@ -12,9 +12,8 @@ from rich.console import Console
|
|
|
12
12
|
from glaip_sdk.branding import ACCENT_STYLE, INFO, SUCCESS
|
|
13
13
|
from glaip_sdk.cli.context import output_flags
|
|
14
14
|
from glaip_sdk.cli.utils import (
|
|
15
|
-
get_client,
|
|
16
15
|
output_list,
|
|
17
|
-
|
|
16
|
+
with_client_and_spinner,
|
|
18
17
|
)
|
|
19
18
|
|
|
20
19
|
console = Console()
|
|
@@ -32,12 +31,11 @@ def models_group() -> None:
|
|
|
32
31
|
def list_models(ctx: Any) -> None:
|
|
33
32
|
"""List available language models."""
|
|
34
33
|
try:
|
|
35
|
-
|
|
36
|
-
with spinner_context(
|
|
34
|
+
with with_client_and_spinner(
|
|
37
35
|
ctx,
|
|
38
36
|
"[bold blue]Fetching language models…[/bold blue]",
|
|
39
37
|
console_override=console,
|
|
40
|
-
):
|
|
38
|
+
) as client:
|
|
41
39
|
models = client.list_language_models()
|
|
42
40
|
|
|
43
41
|
# Define table columns: (data_key, header, style, width)
|
|
@@ -50,6 +48,14 @@ def list_models(ctx: Any) -> None:
|
|
|
50
48
|
|
|
51
49
|
# Transform function for safe dictionary access
|
|
52
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
|
+
"""
|
|
53
59
|
return {
|
|
54
60
|
"id": str(model.get("id", "N/A")),
|
|
55
61
|
"provider": model.get("provider", "N/A"),
|