glaip-sdk 0.0.20__py3-none-any.whl → 0.6.5b6__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 +5 -2
- glaip_sdk/_version.py +10 -3
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1126 -0
- glaip_sdk/branding.py +15 -6
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +265 -45
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +270 -173
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +735 -143
- glaip_sdk/cli/commands/mcps.py +265 -134
- glaip_sdk/cli/commands/models.py +13 -9
- glaip_sdk/cli/commands/tools.py +67 -88
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/commands/update.py +3 -8
- glaip_sdk/cli/config.py +49 -7
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +846 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +45 -32
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +14 -17
- glaip_sdk/cli/main.py +232 -143
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/mcp_validators.py +5 -15
- glaip_sdk/cli/pager.py +12 -19
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/parsers/json_input.py +11 -22
- glaip_sdk/cli/resolution.py +3 -9
- glaip_sdk/cli/rich_helpers.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +500 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +61 -28
- glaip_sdk/cli/slash/prompt.py +13 -10
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +772 -222
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +872 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/loading.py +58 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +258 -60
- glaip_sdk/cli/transcript/capture.py +72 -21
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +1 -3
- glaip_sdk/cli/transcript/viewer.py +77 -329
- glaip_sdk/cli/update_notifier.py +177 -24
- glaip_sdk/cli/utils.py +242 -1309
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +53 -37
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +320 -92
- glaip_sdk/client/base.py +78 -35
- glaip_sdk/client/main.py +19 -10
- glaip_sdk/client/mcps.py +123 -15
- glaip_sdk/client/run_rendering.py +218 -78
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +161 -34
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/icons.py +9 -3
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +90 -0
- glaip_sdk/models/agent.py +47 -0
- glaip_sdk/models/agent_runs.py +116 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/payload_schemas/agent.py +1 -3
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +231 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +59 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +115 -0
- glaip_sdk/runner/langgraph.py +597 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +158 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +177 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +58 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +4 -14
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +46 -28
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +25 -21
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +15 -16
- glaip_sdk/utils/import_resolver.py +492 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +38 -23
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +18 -8
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +476 -882
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- glaip_sdk/utils/rendering/renderer/debug.py +30 -34
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +13 -54
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/step_tree_state.py +100 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/{steps.py → steps/manager.py} +122 -26
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +29 -26
- glaip_sdk/utils/runtime_config.py +422 -0
- glaip_sdk/utils/serialization.py +32 -46
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +20 -28
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/METADATA +49 -4
- glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -259
- glaip_sdk-0.0.20.dist-info/RECORD +0 -80
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/entry_points.txt +0 -0
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,
|
|
@@ -32,7 +34,8 @@ from glaip_sdk.cli.agent_config import (
|
|
|
32
34
|
from glaip_sdk.cli.agent_config import (
|
|
33
35
|
sanitize_agent_config_for_cli as sanitize_agent_config,
|
|
34
36
|
)
|
|
35
|
-
from glaip_sdk.cli.
|
|
37
|
+
from glaip_sdk.cli.constants import DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
38
|
+
from glaip_sdk.cli.context import get_ctx_value, output_flags
|
|
36
39
|
from glaip_sdk.cli.display import (
|
|
37
40
|
build_resource_result_data,
|
|
38
41
|
display_agent_run_suggestions,
|
|
@@ -44,9 +47,7 @@ from glaip_sdk.cli.display import (
|
|
|
44
47
|
handle_rich_output,
|
|
45
48
|
print_api_error,
|
|
46
49
|
)
|
|
47
|
-
from glaip_sdk.cli.
|
|
48
|
-
export_resource_to_file_with_validation as export_resource_to_file,
|
|
49
|
-
)
|
|
50
|
+
from glaip_sdk.cli.hints import in_slash_mode
|
|
50
51
|
from glaip_sdk.cli.io import (
|
|
51
52
|
fetch_raw_resource_details,
|
|
52
53
|
)
|
|
@@ -64,9 +65,11 @@ from glaip_sdk.cli.utils import (
|
|
|
64
65
|
build_renderer,
|
|
65
66
|
coerce_to_row,
|
|
66
67
|
get_client,
|
|
68
|
+
handle_resource_export,
|
|
67
69
|
output_list,
|
|
68
70
|
output_result,
|
|
69
71
|
spinner_context,
|
|
72
|
+
with_client_and_spinner,
|
|
70
73
|
)
|
|
71
74
|
from glaip_sdk.cli.validators import (
|
|
72
75
|
validate_agent_instruction_cli as validate_agent_instruction,
|
|
@@ -77,12 +80,13 @@ from glaip_sdk.cli.validators import (
|
|
|
77
80
|
from glaip_sdk.cli.validators import (
|
|
78
81
|
validate_timeout_cli as validate_timeout,
|
|
79
82
|
)
|
|
80
|
-
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
|
|
81
84
|
from glaip_sdk.exceptions import AgentTimeoutError
|
|
82
85
|
from glaip_sdk.icons import ICON_AGENT
|
|
83
86
|
from glaip_sdk.utils import format_datetime, is_uuid
|
|
84
87
|
from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
|
|
85
88
|
from glaip_sdk.utils.import_export import convert_export_to_import_format
|
|
89
|
+
from glaip_sdk.utils.rendering.renderer.toggle import TranscriptToggleController
|
|
86
90
|
from glaip_sdk.utils.validation import coerce_timeout
|
|
87
91
|
|
|
88
92
|
console = Console()
|
|
@@ -90,6 +94,8 @@ console = Console()
|
|
|
90
94
|
# Error message constants
|
|
91
95
|
AGENT_NOT_FOUND_ERROR = "Agent not found"
|
|
92
96
|
|
|
97
|
+
# Instruction preview controls
|
|
98
|
+
|
|
93
99
|
|
|
94
100
|
def _safe_agent_attribute(agent: Any, name: str) -> Any:
|
|
95
101
|
"""Return attribute value for ``name`` while filtering Mock sentinels."""
|
|
@@ -142,10 +148,7 @@ def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
|
142
148
|
"description",
|
|
143
149
|
"model",
|
|
144
150
|
"agent_config",
|
|
145
|
-
"
|
|
146
|
-
"agents",
|
|
147
|
-
"mcps",
|
|
148
|
-
"timeout",
|
|
151
|
+
*[field for field in AGENT_CONFIG_FIELDS if field not in ("name", "instruction", "model")],
|
|
149
152
|
"tool_configs",
|
|
150
153
|
)
|
|
151
154
|
|
|
@@ -246,9 +249,7 @@ def _resolve_resources_by_name(
|
|
|
246
249
|
if not matches:
|
|
247
250
|
raise click.ClickException(f"{label} not found: {ref}")
|
|
248
251
|
if len(matches) > 1:
|
|
249
|
-
raise click.ClickException(
|
|
250
|
-
f"Multiple {resource_type}s named '{ref}'. Use ID instead."
|
|
251
|
-
)
|
|
252
|
+
raise click.ClickException(f"Multiple {resource_type}s named '{ref}'. Use ID instead.")
|
|
252
253
|
out.append(str(matches[0].id))
|
|
253
254
|
return out
|
|
254
255
|
|
|
@@ -311,12 +312,92 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
311
312
|
return result_data
|
|
312
313
|
|
|
313
314
|
|
|
314
|
-
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:
|
|
315
393
|
"""Display full agent details using raw API data to preserve ALL fields."""
|
|
316
394
|
if agent is None:
|
|
317
395
|
handle_rich_output(ctx, markup_text(f"[{ERROR_STYLE}]❌ No agent provided[/]"))
|
|
318
396
|
return
|
|
319
397
|
|
|
398
|
+
preview_limit = _clamp_instruction_preview_limit(instruction_preview_limit)
|
|
399
|
+
trimmed_instruction = False
|
|
400
|
+
|
|
320
401
|
# Try to fetch and format raw agent data first
|
|
321
402
|
with spinner_context(
|
|
322
403
|
ctx,
|
|
@@ -328,9 +409,13 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
328
409
|
if formatted_data:
|
|
329
410
|
# Use raw API data - this preserves ALL fields including account_id
|
|
330
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
|
+
)
|
|
331
416
|
output_result(
|
|
332
417
|
ctx,
|
|
333
|
-
|
|
418
|
+
payload,
|
|
334
419
|
title=panel_title,
|
|
335
420
|
)
|
|
336
421
|
else:
|
|
@@ -348,12 +433,22 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
348
433
|
result_data = _format_fallback_agent_data(client, agent)
|
|
349
434
|
|
|
350
435
|
# Display using output_result
|
|
436
|
+
payload, trimmed_instruction = _prepare_agent_details_payload(
|
|
437
|
+
result_data,
|
|
438
|
+
instruction_preview_limit=preview_limit,
|
|
439
|
+
)
|
|
351
440
|
output_result(
|
|
352
441
|
ctx,
|
|
353
|
-
|
|
442
|
+
payload,
|
|
354
443
|
title="Agent Details",
|
|
355
444
|
)
|
|
356
445
|
|
|
446
|
+
_show_instruction_trim_hint(
|
|
447
|
+
ctx,
|
|
448
|
+
trimmed=trimmed_instruction,
|
|
449
|
+
preview_limit=preview_limit,
|
|
450
|
+
)
|
|
451
|
+
|
|
357
452
|
|
|
358
453
|
@click.group(name="agents", no_args_is_help=True)
|
|
359
454
|
def agents_group() -> None:
|
|
@@ -368,41 +463,47 @@ def _resolve_agent(
|
|
|
368
463
|
select: int | None = None,
|
|
369
464
|
interface_preference: str = "fuzzy",
|
|
370
465
|
) -> Any | None:
|
|
371
|
-
"""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.
|
|
372
471
|
|
|
373
472
|
Args:
|
|
374
|
-
ctx: Click context
|
|
375
|
-
client: AIP client instance
|
|
376
|
-
ref: Agent
|
|
377
|
-
select: Pre-selected
|
|
378
|
-
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.
|
|
379
478
|
|
|
380
479
|
Returns:
|
|
381
|
-
|
|
480
|
+
Agent object when found, None when resolution fails.
|
|
382
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
|
|
383
490
|
return resolve_resource_reference(
|
|
384
491
|
ctx,
|
|
385
492
|
client,
|
|
386
493
|
ref,
|
|
387
|
-
"
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"
|
|
494
|
+
resolution_config["resource_type"],
|
|
495
|
+
resolution_config["get_by_id"],
|
|
496
|
+
resolution_config["find_by_name"],
|
|
497
|
+
resolution_config["label"],
|
|
391
498
|
select=select,
|
|
392
499
|
interface_preference=interface_preference,
|
|
393
500
|
)
|
|
394
501
|
|
|
395
502
|
|
|
396
503
|
@agents_group.command(name="list")
|
|
397
|
-
@click.option(
|
|
398
|
-
|
|
399
|
-
)
|
|
400
|
-
@click.option(
|
|
401
|
-
"--type", "agent_type", help="Filter by agent type (config, code, a2a, langflow)"
|
|
402
|
-
)
|
|
403
|
-
@click.option(
|
|
404
|
-
"--framework", help="Filter by framework (langchain, langgraph, google_adk)"
|
|
405
|
-
)
|
|
504
|
+
@click.option("--simple", is_flag=True, help="Show simple table without interactive picker")
|
|
505
|
+
@click.option("--type", "agent_type", help="Filter by agent type (config, code, a2a, langflow)")
|
|
506
|
+
@click.option("--framework", help="Filter by framework (langchain, langgraph, google_adk)")
|
|
406
507
|
@click.option("--name", help="Filter by partial name match (case-insensitive)")
|
|
407
508
|
@click.option("--version", help="Filter by exact version match")
|
|
408
509
|
@click.option(
|
|
@@ -423,19 +524,20 @@ def list_agents(
|
|
|
423
524
|
) -> None:
|
|
424
525
|
"""List agents with optional filtering."""
|
|
425
526
|
try:
|
|
426
|
-
|
|
427
|
-
with spinner_context(
|
|
527
|
+
with with_client_and_spinner(
|
|
428
528
|
ctx,
|
|
429
529
|
"[bold blue]Fetching agents…[/bold blue]",
|
|
430
530
|
console_override=console,
|
|
431
|
-
):
|
|
432
|
-
agents
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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)
|
|
439
541
|
|
|
440
542
|
# Define table columns: (data_key, header, style, width)
|
|
441
543
|
columns = [
|
|
@@ -448,6 +550,14 @@ def list_agents(
|
|
|
448
550
|
|
|
449
551
|
# Transform function for safe attribute access
|
|
450
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
|
+
"""
|
|
451
561
|
row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
|
|
452
562
|
# Ensure id is always a string
|
|
453
563
|
row["id"] = str(row["id"])
|
|
@@ -466,7 +576,10 @@ def list_agents(
|
|
|
466
576
|
and len(agents) > 0
|
|
467
577
|
)
|
|
468
578
|
|
|
579
|
+
# Track picker attempt so the fallback table doesn't re-open the palette
|
|
580
|
+
picker_attempted = False
|
|
469
581
|
if interactive_enabled:
|
|
582
|
+
picker_attempted = True
|
|
470
583
|
picked_agent = _fuzzy_pick_for_resources(agents, "agent", "")
|
|
471
584
|
if picked_agent:
|
|
472
585
|
_display_agent_details(ctx, client, picked_agent)
|
|
@@ -481,15 +594,17 @@ def list_agents(
|
|
|
481
594
|
f"{ICON_AGENT} Available Agents",
|
|
482
595
|
columns,
|
|
483
596
|
transform_agent,
|
|
484
|
-
skip_picker=
|
|
485
|
-
|
|
486
|
-
|
|
597
|
+
skip_picker=(
|
|
598
|
+
not interactive_enabled
|
|
599
|
+
or picker_attempted
|
|
600
|
+
or simple
|
|
601
|
+
or any(param is not None for param in (agent_type, framework, name, version))
|
|
487
602
|
),
|
|
488
603
|
use_pager=False,
|
|
489
604
|
)
|
|
490
605
|
|
|
491
606
|
except Exception as e:
|
|
492
|
-
raise click.ClickException(str(e))
|
|
607
|
+
raise click.ClickException(str(e)) from e
|
|
493
608
|
|
|
494
609
|
|
|
495
610
|
@agents_group.command()
|
|
@@ -500,68 +615,66 @@ def list_agents(
|
|
|
500
615
|
type=click.Path(dir_okay=False, writable=True),
|
|
501
616
|
help="Export complete agent configuration to file (format auto-detected from .json/.yaml extension)",
|
|
502
617
|
)
|
|
618
|
+
@click.option(
|
|
619
|
+
"--instruction-preview",
|
|
620
|
+
type=int,
|
|
621
|
+
default=0,
|
|
622
|
+
show_default=True,
|
|
623
|
+
help="Instruction preview length when printing instructions (0 shows full prompt).",
|
|
624
|
+
)
|
|
503
625
|
@output_flags()
|
|
504
626
|
@click.pass_context
|
|
505
|
-
def get(
|
|
506
|
-
|
|
627
|
+
def get(
|
|
628
|
+
ctx: Any,
|
|
629
|
+
agent_ref: str,
|
|
630
|
+
select: int | None,
|
|
631
|
+
export: str | None,
|
|
632
|
+
instruction_preview: int,
|
|
633
|
+
) -> None:
|
|
634
|
+
r"""Get agent details.
|
|
507
635
|
|
|
636
|
+
\b
|
|
508
637
|
Examples:
|
|
509
638
|
aip agents get my-agent
|
|
510
639
|
aip agents get my-agent --export agent.json # Exports complete configuration as JSON
|
|
511
640
|
aip agents get my-agent --export agent.yaml # Exports complete configuration as YAML
|
|
512
641
|
"""
|
|
513
642
|
try:
|
|
514
|
-
client
|
|
643
|
+
# Initialize API client for agent retrieval
|
|
644
|
+
api_client = get_client(ctx)
|
|
515
645
|
|
|
516
|
-
# Resolve agent
|
|
517
|
-
agent = _resolve_agent(
|
|
518
|
-
ctx, client, agent_ref, select, interface_preference="questionary"
|
|
519
|
-
)
|
|
646
|
+
# Resolve agent reference using questionary interface for better UX
|
|
647
|
+
agent = _resolve_agent(ctx, api_client, agent_ref, select, interface_preference="questionary")
|
|
520
648
|
|
|
521
|
-
|
|
649
|
+
if not agent:
|
|
650
|
+
raise click.ClickException(f"Agent '{agent_ref}' not found")
|
|
651
|
+
|
|
652
|
+
# Handle export option if requested
|
|
522
653
|
if export:
|
|
523
|
-
|
|
524
|
-
# Auto-detect format from file extension
|
|
525
|
-
detected_format = detect_export_format(export_path)
|
|
526
|
-
|
|
527
|
-
# Always export comprehensive data - re-fetch agent with full details
|
|
528
|
-
try:
|
|
529
|
-
with spinner_context(
|
|
530
|
-
ctx,
|
|
531
|
-
"[bold blue]Fetching complete agent data…[/bold blue]",
|
|
532
|
-
console_override=console,
|
|
533
|
-
):
|
|
534
|
-
agent = client.agents.get_agent_by_id(agent.id)
|
|
535
|
-
except Exception as e:
|
|
536
|
-
handle_rich_output(
|
|
537
|
-
ctx,
|
|
538
|
-
markup_text(
|
|
539
|
-
f"[{WARNING_STYLE}]⚠️ Could not fetch full agent details: {e}[/]"
|
|
540
|
-
),
|
|
541
|
-
)
|
|
542
|
-
handle_rich_output(
|
|
543
|
-
ctx,
|
|
544
|
-
markup_text(
|
|
545
|
-
f"[{WARNING_STYLE}]⚠️ Proceeding with available data[/]"
|
|
546
|
-
),
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
export_resource_to_file(agent, export_path, detected_format)
|
|
550
|
-
handle_rich_output(
|
|
654
|
+
handle_resource_export(
|
|
551
655
|
ctx,
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
656
|
+
agent,
|
|
657
|
+
Path(export),
|
|
658
|
+
resource_type="agent",
|
|
659
|
+
get_by_id_func=api_client.agents.get_agent_by_id,
|
|
660
|
+
console_override=console,
|
|
555
661
|
)
|
|
556
662
|
|
|
557
663
|
# Display full agent details using the standardized helper
|
|
558
|
-
_display_agent_details(
|
|
664
|
+
_display_agent_details(
|
|
665
|
+
ctx,
|
|
666
|
+
api_client,
|
|
667
|
+
agent,
|
|
668
|
+
instruction_preview_limit=instruction_preview,
|
|
669
|
+
)
|
|
559
670
|
|
|
560
671
|
# Show run suggestions via centralized display helper
|
|
561
672
|
handle_rich_output(ctx, display_agent_run_suggestions(agent))
|
|
562
673
|
|
|
674
|
+
except click.ClickException:
|
|
675
|
+
raise
|
|
563
676
|
except Exception as e:
|
|
564
|
-
raise click.ClickException(str(e))
|
|
677
|
+
raise click.ClickException(str(e)) from e
|
|
565
678
|
|
|
566
679
|
|
|
567
680
|
def _validate_run_input(input_option: str | None, input_text: str | None) -> str:
|
|
@@ -569,9 +682,7 @@ def _validate_run_input(input_option: str | None, input_text: str | None) -> str
|
|
|
569
682
|
final_input_text = input_option if input_option else input_text
|
|
570
683
|
|
|
571
684
|
if not final_input_text:
|
|
572
|
-
raise click.ClickException(
|
|
573
|
-
"Input text is required. Use either positional argument or --input option."
|
|
574
|
-
)
|
|
685
|
+
raise click.ClickException("Input text is required. Use either positional argument or --input option.")
|
|
575
686
|
|
|
576
687
|
return final_input_text
|
|
577
688
|
|
|
@@ -583,8 +694,8 @@ def _parse_chat_history(chat_history: str | None) -> list[dict[str, Any]] | None
|
|
|
583
694
|
|
|
584
695
|
try:
|
|
585
696
|
return json.loads(chat_history)
|
|
586
|
-
except json.JSONDecodeError:
|
|
587
|
-
raise click.ClickException("Invalid JSON in chat history")
|
|
697
|
+
except json.JSONDecodeError as err:
|
|
698
|
+
raise click.ClickException("Invalid JSON in chat history") from err
|
|
588
699
|
|
|
589
700
|
|
|
590
701
|
def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
|
|
@@ -598,6 +709,23 @@ def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
|
|
|
598
709
|
)
|
|
599
710
|
|
|
600
711
|
|
|
712
|
+
def _maybe_attach_transcript_toggle(ctx: Any, renderer: Any) -> None:
|
|
713
|
+
"""Attach transcript toggle controller when interactive TTY is available."""
|
|
714
|
+
if renderer is None:
|
|
715
|
+
return
|
|
716
|
+
|
|
717
|
+
console_obj = getattr(renderer, "console", None)
|
|
718
|
+
if console_obj is None or not getattr(console_obj, "is_terminal", False):
|
|
719
|
+
return
|
|
720
|
+
|
|
721
|
+
tty_enabled = bool(get_ctx_value(ctx, "tty", True))
|
|
722
|
+
if not tty_enabled:
|
|
723
|
+
return
|
|
724
|
+
|
|
725
|
+
controller = TranscriptToggleController(enabled=True)
|
|
726
|
+
renderer.transcript_controller = controller
|
|
727
|
+
|
|
728
|
+
|
|
601
729
|
def _prepare_run_kwargs(
|
|
602
730
|
agent: Any,
|
|
603
731
|
final_input_text: str,
|
|
@@ -647,9 +775,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
647
775
|
if ext == "json":
|
|
648
776
|
save_data = {
|
|
649
777
|
"output": result or "",
|
|
650
|
-
"full_debug_output": getattr(
|
|
651
|
-
working_console, "get_captured_output", lambda: ""
|
|
652
|
-
)(),
|
|
778
|
+
"full_debug_output": getattr(working_console, "get_captured_output", lambda: "")(),
|
|
653
779
|
"timestamp": "captured during agent execution",
|
|
654
780
|
}
|
|
655
781
|
content = json.dumps(save_data, indent=2)
|
|
@@ -662,9 +788,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
662
788
|
|
|
663
789
|
with open(save, "w", encoding="utf-8") as f:
|
|
664
790
|
f.write(content)
|
|
665
|
-
print_markup(
|
|
666
|
-
f"[{SUCCESS_STYLE}]Full debug output saved to: {save}[/]", console=console
|
|
667
|
-
)
|
|
791
|
+
print_markup(f"[{SUCCESS_STYLE}]Full debug output saved to: {save}[/]", console=console)
|
|
668
792
|
|
|
669
793
|
|
|
670
794
|
@agents_group.command()
|
|
@@ -710,10 +834,11 @@ def run(
|
|
|
710
834
|
files: tuple[str, ...] | None,
|
|
711
835
|
verbose: bool,
|
|
712
836
|
) -> None:
|
|
713
|
-
"""Run an agent with input text.
|
|
837
|
+
r"""Run an agent with input text.
|
|
714
838
|
|
|
715
839
|
Usage: aip agents run <agent_ref> <input_text> [OPTIONS]
|
|
716
840
|
|
|
841
|
+
\b
|
|
717
842
|
Examples:
|
|
718
843
|
aip agents run my-agent "Hello world"
|
|
719
844
|
aip agents run agent-123 "Process this data" --timeout 600
|
|
@@ -727,12 +852,11 @@ def run(
|
|
|
727
852
|
|
|
728
853
|
try:
|
|
729
854
|
client = get_client(ctx)
|
|
730
|
-
agent = _resolve_agent(
|
|
731
|
-
ctx, client, agent_ref, select, interface_preference="fuzzy"
|
|
732
|
-
)
|
|
855
|
+
agent = _resolve_agent(ctx, client, agent_ref, select, interface_preference="fuzzy")
|
|
733
856
|
|
|
734
857
|
parsed_chat_history = _parse_chat_history(chat_history)
|
|
735
858
|
renderer, working_console = _setup_run_renderer(ctx, save, verbose)
|
|
859
|
+
_maybe_attach_transcript_toggle(ctx, renderer)
|
|
736
860
|
|
|
737
861
|
try:
|
|
738
862
|
client.timeout = float(timeout)
|
|
@@ -777,17 +901,19 @@ def run(
|
|
|
777
901
|
except AgentTimeoutError as e:
|
|
778
902
|
error_msg = str(e)
|
|
779
903
|
handle_json_output(ctx, error=Exception(error_msg))
|
|
780
|
-
raise click.ClickException(error_msg)
|
|
904
|
+
raise click.ClickException(error_msg) from e
|
|
781
905
|
except Exception as e:
|
|
782
906
|
_handle_command_exception(ctx, e)
|
|
783
907
|
|
|
784
908
|
|
|
785
909
|
def _running_in_slash_mode(ctx: Any) -> bool:
|
|
910
|
+
"""Return True if the command is executing inside the slash session."""
|
|
786
911
|
ctx_obj = getattr(ctx, "obj", None)
|
|
787
912
|
return isinstance(ctx_obj, dict) and bool(ctx_obj.get("_slash_session"))
|
|
788
913
|
|
|
789
914
|
|
|
790
915
|
def _emit_verbose_guidance(ctx: Any) -> None:
|
|
916
|
+
"""Explain the modern alternative to the deprecated --verbose flag."""
|
|
791
917
|
if _running_in_slash_mode(ctx):
|
|
792
918
|
message = (
|
|
793
919
|
"[dim]Tip:[/] Verbose streaming has been retired in the command palette. Run the agent normally and open "
|
|
@@ -866,16 +992,12 @@ def _extract_and_validate_fields(
|
|
|
866
992
|
if not name:
|
|
867
993
|
raise click.ClickException("Agent name is required (--name or --import)")
|
|
868
994
|
if not instruction:
|
|
869
|
-
raise click.ClickException(
|
|
870
|
-
"Agent instruction is required (--instruction or --import)"
|
|
871
|
-
)
|
|
995
|
+
raise click.ClickException("Agent instruction is required (--instruction or --import)")
|
|
872
996
|
|
|
873
997
|
return name, instruction, model, tools, agents, mcps, timeout
|
|
874
998
|
|
|
875
999
|
|
|
876
|
-
def _validate_and_coerce_fields(
|
|
877
|
-
name: str, instruction: str, timeout: Any
|
|
878
|
-
) -> tuple[str, str, Any]:
|
|
1000
|
+
def _validate_and_coerce_fields(name: str, instruction: str, timeout: Any) -> tuple[str, str, Any]:
|
|
879
1001
|
"""Validate and coerce field values."""
|
|
880
1002
|
name = validate_agent_name(name)
|
|
881
1003
|
instruction = validate_agent_instruction(instruction)
|
|
@@ -886,19 +1008,11 @@ def _validate_and_coerce_fields(
|
|
|
886
1008
|
return name, instruction, timeout
|
|
887
1009
|
|
|
888
1010
|
|
|
889
|
-
def _resolve_resources(
|
|
890
|
-
client: Any, tools: tuple, agents: tuple, mcps: tuple
|
|
891
|
-
) -> tuple[list, list, list]:
|
|
1011
|
+
def _resolve_resources(client: Any, tools: tuple, agents: tuple, mcps: tuple) -> tuple[list, list, list]:
|
|
892
1012
|
"""Resolve tool, agent, and MCP references."""
|
|
893
|
-
resolved_tools = _resolve_resources_by_name(
|
|
894
|
-
|
|
895
|
-
)
|
|
896
|
-
resolved_agents = _resolve_resources_by_name(
|
|
897
|
-
client, agents, "agent", client.find_agents, "Agent"
|
|
898
|
-
)
|
|
899
|
-
resolved_mcps = _resolve_resources_by_name(
|
|
900
|
-
client, mcps, "mcp", client.find_mcps, "MCP"
|
|
901
|
-
)
|
|
1013
|
+
resolved_tools = _resolve_resources_by_name(client, tools, "tool", client.find_tools, "Tool")
|
|
1014
|
+
resolved_agents = _resolve_resources_by_name(client, agents, "agent", client.find_agents, "Agent")
|
|
1015
|
+
resolved_mcps = _resolve_resources_by_name(client, mcps, "mcp", client.find_mcps, "MCP")
|
|
902
1016
|
|
|
903
1017
|
return resolved_tools, resolved_agents, resolved_mcps
|
|
904
1018
|
|
|
@@ -925,16 +1039,12 @@ def _build_create_kwargs(
|
|
|
925
1039
|
}
|
|
926
1040
|
|
|
927
1041
|
# Handle language model selection
|
|
928
|
-
lm_selection_dict, should_strip_lm_identity = resolve_language_model_selection(
|
|
929
|
-
merged_data, model
|
|
930
|
-
)
|
|
1042
|
+
lm_selection_dict, should_strip_lm_identity = resolve_language_model_selection(merged_data, model)
|
|
931
1043
|
create_kwargs.update(lm_selection_dict)
|
|
932
1044
|
|
|
933
1045
|
# Handle import file specific logic
|
|
934
1046
|
if import_file:
|
|
935
|
-
_add_import_file_attributes(
|
|
936
|
-
create_kwargs, merged_data, should_strip_lm_identity
|
|
937
|
-
)
|
|
1047
|
+
_add_import_file_attributes(create_kwargs, merged_data, should_strip_lm_identity)
|
|
938
1048
|
|
|
939
1049
|
return create_kwargs
|
|
940
1050
|
|
|
@@ -980,12 +1090,7 @@ def _get_language_model_display_name(agent: Any, model: str | None) -> str:
|
|
|
980
1090
|
lm_display = getattr(agent, "model", None)
|
|
981
1091
|
if not lm_display:
|
|
982
1092
|
cfg = getattr(agent, "agent_config", {}) or {}
|
|
983
|
-
lm_display = (
|
|
984
|
-
cfg.get("lm_name")
|
|
985
|
-
or cfg.get("model")
|
|
986
|
-
or model
|
|
987
|
-
or f"{DEFAULT_MODEL} (backend default)"
|
|
988
|
-
)
|
|
1093
|
+
lm_display = cfg.get("lm_name") or cfg.get("model") or model or f"{DEFAULT_MODEL} (backend default)"
|
|
989
1094
|
return lm_display
|
|
990
1095
|
|
|
991
1096
|
|
|
@@ -1020,7 +1125,7 @@ def _handle_command_exception(ctx: Any, e: Exception) -> None:
|
|
|
1020
1125
|
handle_json_output(ctx, error=e)
|
|
1021
1126
|
if get_ctx_value(ctx, "view") != "json":
|
|
1022
1127
|
print_api_error(e)
|
|
1023
|
-
raise click.
|
|
1128
|
+
raise click.exceptions.Exit(1) from e
|
|
1024
1129
|
|
|
1025
1130
|
|
|
1026
1131
|
def _handle_creation_exception(ctx: Any, e: Exception) -> None:
|
|
@@ -1063,8 +1168,9 @@ def create(
|
|
|
1063
1168
|
timeout: float | None,
|
|
1064
1169
|
import_file: str | None,
|
|
1065
1170
|
) -> None:
|
|
1066
|
-
"""Create a new agent.
|
|
1171
|
+
r"""Create a new agent.
|
|
1067
1172
|
|
|
1173
|
+
\b
|
|
1068
1174
|
Examples:
|
|
1069
1175
|
aip agents create --name "My Agent" --instruction "You are a helpful assistant"
|
|
1070
1176
|
aip agents create --import agent.json
|
|
@@ -1074,13 +1180,9 @@ def create(
|
|
|
1074
1180
|
|
|
1075
1181
|
# Handle import file or CLI args
|
|
1076
1182
|
if import_file:
|
|
1077
|
-
merged_data = _handle_import_file_logic(
|
|
1078
|
-
import_file, model, name, instruction, tools, agents, mcps, timeout
|
|
1079
|
-
)
|
|
1183
|
+
merged_data = _handle_import_file_logic(import_file, model, name, instruction, tools, agents, mcps, timeout)
|
|
1080
1184
|
else:
|
|
1081
|
-
merged_data = _build_cli_args_data(
|
|
1082
|
-
name, instruction, model, tools, agents, mcps, timeout
|
|
1083
|
-
)
|
|
1185
|
+
merged_data = _build_cli_args_data(name, instruction, model, tools, agents, mcps, timeout)
|
|
1084
1186
|
|
|
1085
1187
|
# Extract and validate fields
|
|
1086
1188
|
(
|
|
@@ -1092,14 +1194,10 @@ def create(
|
|
|
1092
1194
|
mcps,
|
|
1093
1195
|
timeout,
|
|
1094
1196
|
) = _extract_and_validate_fields(merged_data)
|
|
1095
|
-
name, instruction, timeout = _validate_and_coerce_fields(
|
|
1096
|
-
name, instruction, timeout
|
|
1097
|
-
)
|
|
1197
|
+
name, instruction, timeout = _validate_and_coerce_fields(name, instruction, timeout)
|
|
1098
1198
|
|
|
1099
1199
|
# Resolve resources
|
|
1100
|
-
resolved_tools, resolved_agents, resolved_mcps = _resolve_resources(
|
|
1101
|
-
client, tools, agents, mcps
|
|
1102
|
-
)
|
|
1200
|
+
resolved_tools, resolved_agents, resolved_mcps = _resolve_resources(client, tools, agents, mcps)
|
|
1103
1201
|
|
|
1104
1202
|
# Build create kwargs
|
|
1105
1203
|
create_kwargs = _build_create_kwargs(
|
|
@@ -1129,7 +1227,7 @@ def _get_agent_for_update(client: Any, agent_id: str) -> Any:
|
|
|
1129
1227
|
try:
|
|
1130
1228
|
return client.agents.get_agent_by_id(agent_id)
|
|
1131
1229
|
except Exception as e:
|
|
1132
|
-
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}")
|
|
1230
|
+
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}") from e
|
|
1133
1231
|
|
|
1134
1232
|
|
|
1135
1233
|
def _handle_update_import_file(
|
|
@@ -1211,16 +1309,12 @@ def _handle_update_import_config(
|
|
|
1211
1309
|
if not import_file:
|
|
1212
1310
|
return
|
|
1213
1311
|
|
|
1214
|
-
lm_selection, should_strip_lm_identity = resolve_language_model_selection(
|
|
1215
|
-
merged_data, None
|
|
1216
|
-
)
|
|
1312
|
+
lm_selection, should_strip_lm_identity = resolve_language_model_selection(merged_data, None)
|
|
1217
1313
|
update_data.update(lm_selection)
|
|
1218
1314
|
|
|
1219
1315
|
raw_cfg = merged_data.get("agent_config") if isinstance(merged_data, dict) else None
|
|
1220
1316
|
if isinstance(raw_cfg, dict):
|
|
1221
|
-
update_data["agent_config"] = sanitize_agent_config(
|
|
1222
|
-
raw_cfg, strip_lm_identity=should_strip_lm_identity
|
|
1223
|
-
)
|
|
1317
|
+
update_data["agent_config"] = sanitize_agent_config(raw_cfg, strip_lm_identity=should_strip_lm_identity)
|
|
1224
1318
|
|
|
1225
1319
|
excluded_fields = {
|
|
1226
1320
|
"name",
|
|
@@ -1270,8 +1364,9 @@ def update(
|
|
|
1270
1364
|
timeout: float | None,
|
|
1271
1365
|
import_file: str | None,
|
|
1272
1366
|
) -> None:
|
|
1273
|
-
"""Update an existing agent.
|
|
1367
|
+
r"""Update an existing agent.
|
|
1274
1368
|
|
|
1369
|
+
\b
|
|
1275
1370
|
Examples:
|
|
1276
1371
|
aip agents update my-agent --instruction "New instruction"
|
|
1277
1372
|
aip agents update my-agent --import agent.json
|
|
@@ -1289,16 +1384,18 @@ def update(
|
|
|
1289
1384
|
agents,
|
|
1290
1385
|
mcps,
|
|
1291
1386
|
timeout,
|
|
1292
|
-
) = _handle_update_import_file(
|
|
1293
|
-
import_file, name, instruction, tools, agents, mcps, timeout
|
|
1294
|
-
)
|
|
1387
|
+
) = _handle_update_import_file(import_file, name, instruction, tools, agents, mcps, timeout)
|
|
1295
1388
|
|
|
1296
|
-
update_data = _build_update_data(
|
|
1297
|
-
name, instruction, tools, agents, mcps, timeout
|
|
1298
|
-
)
|
|
1389
|
+
update_data = _build_update_data(name, instruction, tools, agents, mcps, timeout)
|
|
1299
1390
|
|
|
1300
1391
|
if merged_data:
|
|
1301
1392
|
_handle_update_import_config(import_file, merged_data, update_data)
|
|
1393
|
+
# Ensure instruction from import file is included if not already set via CLI
|
|
1394
|
+
# This handles the case where instruction is None in CLI args but exists in import file
|
|
1395
|
+
if import_file and (instruction is None or "instruction" not in update_data):
|
|
1396
|
+
import_instruction = merged_data.get("instruction")
|
|
1397
|
+
if import_instruction is not None:
|
|
1398
|
+
update_data["instruction"] = import_instruction
|
|
1302
1399
|
|
|
1303
1400
|
if not update_data:
|
|
1304
1401
|
raise click.ClickException("No update fields specified")
|
|
@@ -1333,7 +1430,7 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
|
|
|
1333
1430
|
try:
|
|
1334
1431
|
agent = client.agents.get_agent_by_id(agent_id)
|
|
1335
1432
|
except Exception as e:
|
|
1336
|
-
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}")
|
|
1433
|
+
raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}") from e
|
|
1337
1434
|
|
|
1338
1435
|
# Confirm deletion when not forced
|
|
1339
1436
|
if not yes and not display_confirmation_prompt("Agent", agent.name):
|
|
@@ -1365,13 +1462,11 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
|
|
|
1365
1462
|
"--base-url",
|
|
1366
1463
|
help="Custom LangFlow server base URL (overrides LANGFLOW_BASE_URL env var)",
|
|
1367
1464
|
)
|
|
1368
|
-
@click.option(
|
|
1369
|
-
"--api-key", help="Custom LangFlow API key (overrides LANGFLOW_API_KEY env var)"
|
|
1370
|
-
)
|
|
1465
|
+
@click.option("--api-key", help="Custom LangFlow API key (overrides LANGFLOW_API_KEY env var)")
|
|
1371
1466
|
@output_flags()
|
|
1372
1467
|
@click.pass_context
|
|
1373
1468
|
def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
1374
|
-
"""Sync agents with LangFlow server flows.
|
|
1469
|
+
r"""Sync agents with LangFlow server flows.
|
|
1375
1470
|
|
|
1376
1471
|
This command fetches all flows from the configured LangFlow server and
|
|
1377
1472
|
creates/updates corresponding agents in the platform.
|
|
@@ -1380,6 +1475,7 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1380
1475
|
- Command options (--base-url, --api-key)
|
|
1381
1476
|
- Environment variables (LANGFLOW_BASE_URL, LANGFLOW_API_KEY)
|
|
1382
1477
|
|
|
1478
|
+
\b
|
|
1383
1479
|
Examples:
|
|
1384
1480
|
aip agents sync-langflow
|
|
1385
1481
|
aip agents sync-langflow --base-url https://my-langflow.com --api-key my-key
|
|
@@ -1396,15 +1492,16 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1396
1492
|
# Show success message for non-JSON output
|
|
1397
1493
|
if get_ctx_value(ctx, "view") != "json":
|
|
1398
1494
|
# Extract some useful info from the result
|
|
1399
|
-
success_count = result.get("data", {}).get("created_count", 0) + result.get(
|
|
1400
|
-
"
|
|
1401
|
-
)
|
|
1495
|
+
success_count = result.get("data", {}).get("created_count", 0) + result.get("data", {}).get(
|
|
1496
|
+
"updated_count", 0
|
|
1497
|
+
)
|
|
1402
1498
|
total_count = result.get("data", {}).get("total_processed", 0)
|
|
1403
1499
|
|
|
1404
1500
|
handle_rich_output(
|
|
1405
1501
|
ctx,
|
|
1406
1502
|
markup_text(
|
|
1407
|
-
f"[{SUCCESS_STYLE}]✅ Successfully synced {success_count} LangFlow agents
|
|
1503
|
+
f"[{SUCCESS_STYLE}]✅ Successfully synced {success_count} LangFlow agents "
|
|
1504
|
+
f"({total_count} total processed)[/]"
|
|
1408
1505
|
),
|
|
1409
1506
|
)
|
|
1410
1507
|
|