glaip-sdk 0.0.7__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 +6 -3
- glaip_sdk/_version.py +12 -5
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1126 -0
- glaip_sdk/branding.py +79 -15
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +699 -0
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +503 -183
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +774 -137
- glaip_sdk/cli/commands/mcps.py +1124 -181
- glaip_sdk/cli/commands/models.py +25 -10
- glaip_sdk/cli/commands/tools.py +144 -92
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/commands/update.py +61 -0
- glaip_sdk/cli/config.py +95 -0
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +150 -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 +143 -53
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +24 -18
- glaip_sdk/cli/main.py +420 -145
- glaip_sdk/cli/masking.py +136 -0
- glaip_sdk/cli/mcp_validators.py +287 -0
- glaip_sdk/cli/pager.py +266 -0
- glaip_sdk/cli/parsers/__init__.py +7 -0
- glaip_sdk/cli/parsers/json_input.py +177 -0
- glaip_sdk/cli/resolution.py +28 -21
- glaip_sdk/cli/rich_helpers.py +27 -0
- glaip_sdk/cli/slash/__init__.py +15 -0
- 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 +282 -0
- glaip_sdk/cli/slash/prompt.py +245 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +1679 -0
- 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 +31 -0
- glaip_sdk/cli/transcript/cache.py +536 -0
- glaip_sdk/cli/transcript/capture.py +329 -0
- glaip_sdk/cli/transcript/export.py +38 -0
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +77 -0
- glaip_sdk/cli/transcript/viewer.py +372 -0
- glaip_sdk/cli/update_notifier.py +290 -0
- glaip_sdk/cli/utils.py +247 -1238
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +520 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +940 -574
- glaip_sdk/client/base.py +163 -48
- glaip_sdk/client/main.py +35 -12
- glaip_sdk/client/mcps.py +126 -18
- glaip_sdk/client/run_rendering.py +415 -0
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +195 -37
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +15 -5
- glaip_sdk/exceptions.py +16 -9
- glaip_sdk/icons.py +25 -0
- 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 +7 -0
- glaip_sdk/payload_schemas/agent.py +85 -0
- 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 +98 -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 +59 -13
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +53 -40
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +58 -26
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +65 -32
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +20 -25
- 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 +85 -43
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +51 -19
- glaip_sdk/utils/rendering/layout/progress.py +202 -0
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +39 -7
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +672 -759
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- glaip_sdk/utils/rendering/renderer/debug.py +75 -22
- 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/manager.py +387 -0
- 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 +184 -51
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +21 -30
- {glaip_sdk-0.0.7.dist-info → glaip_sdk-0.6.5b6.dist-info}/METADATA +58 -12
- glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
- {glaip_sdk-0.0.7.dist-info → glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -250
- glaip_sdk/utils/rendering/renderer/progress.py +0 -118
- glaip_sdk/utils/rendering/steps.py +0 -232
- glaip_sdk/utils/rich_utils.py +0 -29
- glaip_sdk-0.0.7.dist-info/RECORD +0 -55
- {glaip_sdk-0.0.7.dist-info → glaip_sdk-0.6.5b6.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/commands/agents.py
CHANGED
|
@@ -4,15 +4,27 @@ Authors:
|
|
|
4
4
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
7
9
|
import json
|
|
8
10
|
import os
|
|
11
|
+
from collections.abc import Mapping
|
|
12
|
+
from copy import deepcopy
|
|
9
13
|
from pathlib import Path
|
|
10
14
|
from typing import Any
|
|
11
15
|
|
|
12
16
|
import click
|
|
13
17
|
from rich.console import Console
|
|
14
|
-
from rich.text import Text
|
|
15
18
|
|
|
19
|
+
from glaip_sdk.branding import (
|
|
20
|
+
ACCENT_STYLE,
|
|
21
|
+
ERROR_STYLE,
|
|
22
|
+
HINT_PREFIX_STYLE,
|
|
23
|
+
INFO,
|
|
24
|
+
SUCCESS,
|
|
25
|
+
SUCCESS_STYLE,
|
|
26
|
+
WARNING_STYLE,
|
|
27
|
+
)
|
|
16
28
|
from glaip_sdk.cli.agent_config import (
|
|
17
29
|
merge_agent_config_with_cli_args as merge_import_with_cli_args,
|
|
18
30
|
)
|
|
@@ -22,6 +34,8 @@ from glaip_sdk.cli.agent_config import (
|
|
|
22
34
|
from glaip_sdk.cli.agent_config import (
|
|
23
35
|
sanitize_agent_config_for_cli as sanitize_agent_config,
|
|
24
36
|
)
|
|
37
|
+
from glaip_sdk.cli.constants import DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
38
|
+
from glaip_sdk.cli.context import get_ctx_value, output_flags
|
|
25
39
|
from glaip_sdk.cli.display import (
|
|
26
40
|
build_resource_result_data,
|
|
27
41
|
display_agent_run_suggestions,
|
|
@@ -33,9 +47,7 @@ from glaip_sdk.cli.display import (
|
|
|
33
47
|
handle_rich_output,
|
|
34
48
|
print_api_error,
|
|
35
49
|
)
|
|
36
|
-
from glaip_sdk.cli.
|
|
37
|
-
export_resource_to_file_with_validation as export_resource_to_file,
|
|
38
|
-
)
|
|
50
|
+
from glaip_sdk.cli.hints import in_slash_mode
|
|
39
51
|
from glaip_sdk.cli.io import (
|
|
40
52
|
fetch_raw_resource_details,
|
|
41
53
|
)
|
|
@@ -43,16 +55,21 @@ from glaip_sdk.cli.io import (
|
|
|
43
55
|
load_resource_from_file_with_validation as load_resource_from_file,
|
|
44
56
|
)
|
|
45
57
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
58
|
+
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
59
|
+
from glaip_sdk.cli.transcript import (
|
|
60
|
+
maybe_launch_post_run_viewer,
|
|
61
|
+
store_transcript_for_session,
|
|
62
|
+
)
|
|
46
63
|
from glaip_sdk.cli.utils import (
|
|
47
64
|
_fuzzy_pick_for_resources,
|
|
48
65
|
build_renderer,
|
|
49
66
|
coerce_to_row,
|
|
50
|
-
detect_export_format,
|
|
51
67
|
get_client,
|
|
52
|
-
|
|
53
|
-
output_flags,
|
|
68
|
+
handle_resource_export,
|
|
54
69
|
output_list,
|
|
55
70
|
output_result,
|
|
71
|
+
spinner_context,
|
|
72
|
+
with_client_and_spinner,
|
|
56
73
|
)
|
|
57
74
|
from glaip_sdk.cli.validators import (
|
|
58
75
|
validate_agent_instruction_cli as validate_agent_instruction,
|
|
@@ -63,11 +80,13 @@ from glaip_sdk.cli.validators import (
|
|
|
63
80
|
from glaip_sdk.cli.validators import (
|
|
64
81
|
validate_timeout_cli as validate_timeout,
|
|
65
82
|
)
|
|
66
|
-
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
|
|
67
84
|
from glaip_sdk.exceptions import AgentTimeoutError
|
|
85
|
+
from glaip_sdk.icons import ICON_AGENT
|
|
68
86
|
from glaip_sdk.utils import format_datetime, is_uuid
|
|
69
87
|
from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
|
|
70
88
|
from glaip_sdk.utils.import_export import convert_export_to_import_format
|
|
89
|
+
from glaip_sdk.utils.rendering.renderer.toggle import TranscriptToggleController
|
|
71
90
|
from glaip_sdk.utils.validation import coerce_timeout
|
|
72
91
|
|
|
73
92
|
console = Console()
|
|
@@ -75,6 +94,85 @@ console = Console()
|
|
|
75
94
|
# Error message constants
|
|
76
95
|
AGENT_NOT_FOUND_ERROR = "Agent not found"
|
|
77
96
|
|
|
97
|
+
# Instruction preview controls
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _safe_agent_attribute(agent: Any, name: str) -> Any:
|
|
101
|
+
"""Return attribute value for ``name`` while filtering Mock sentinels."""
|
|
102
|
+
try:
|
|
103
|
+
value = getattr(agent, name)
|
|
104
|
+
except Exception:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
if hasattr(value, "_mock_name"):
|
|
108
|
+
return None
|
|
109
|
+
return value
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _coerce_mapping_candidate(candidate: Any) -> dict[str, Any] | None:
|
|
113
|
+
"""Convert a mapping-like candidate to a plain dict when possible."""
|
|
114
|
+
if candidate is None:
|
|
115
|
+
return None
|
|
116
|
+
if isinstance(candidate, Mapping):
|
|
117
|
+
return dict(candidate)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _call_agent_method(agent: Any, method_name: str) -> dict[str, Any] | None:
|
|
122
|
+
"""Attempt to call the named method and coerce its output to a dict."""
|
|
123
|
+
method = getattr(agent, method_name, None)
|
|
124
|
+
if not callable(method):
|
|
125
|
+
return None
|
|
126
|
+
try:
|
|
127
|
+
candidate = method()
|
|
128
|
+
except Exception:
|
|
129
|
+
return None
|
|
130
|
+
return _coerce_mapping_candidate(candidate)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _coerce_agent_via_methods(agent: Any) -> dict[str, Any] | None:
|
|
134
|
+
"""Try standard serialisation helpers to produce a mapping."""
|
|
135
|
+
for attr in ("model_dump", "dict", "to_dict"):
|
|
136
|
+
mapping = _call_agent_method(agent, attr)
|
|
137
|
+
if mapping is not None:
|
|
138
|
+
return mapping
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
|
|
143
|
+
"""Construct a minimal mapping from well-known agent attributes."""
|
|
144
|
+
fallback_fields = (
|
|
145
|
+
"id",
|
|
146
|
+
"name",
|
|
147
|
+
"instruction",
|
|
148
|
+
"description",
|
|
149
|
+
"model",
|
|
150
|
+
"agent_config",
|
|
151
|
+
*[field for field in AGENT_CONFIG_FIELDS if field not in ("name", "instruction", "model")],
|
|
152
|
+
"tool_configs",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
fallback: dict[str, Any] = {}
|
|
156
|
+
for field in fallback_fields:
|
|
157
|
+
value = _safe_agent_attribute(agent, field)
|
|
158
|
+
if value is not None:
|
|
159
|
+
fallback[field] = value
|
|
160
|
+
|
|
161
|
+
return fallback or {"name": str(agent)}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _prepare_agent_output(agent: Any) -> dict[str, Any]:
|
|
165
|
+
"""Build a JSON-serialisable mapping for CLI output."""
|
|
166
|
+
method_mapping = _coerce_agent_via_methods(agent)
|
|
167
|
+
if method_mapping is not None:
|
|
168
|
+
return method_mapping
|
|
169
|
+
|
|
170
|
+
intrinsic = _coerce_mapping_candidate(agent)
|
|
171
|
+
if intrinsic is not None:
|
|
172
|
+
return intrinsic
|
|
173
|
+
|
|
174
|
+
return _build_fallback_agent_mapping(agent)
|
|
175
|
+
|
|
78
176
|
|
|
79
177
|
def _fetch_full_agent_details(client: Any, agent: Any) -> Any | None:
|
|
80
178
|
"""Fetch full agent details by ID to ensure all fields are populated."""
|
|
@@ -88,17 +186,41 @@ def _fetch_full_agent_details(client: Any, agent: Any) -> Any | None:
|
|
|
88
186
|
return agent
|
|
89
187
|
|
|
90
188
|
|
|
189
|
+
def _normalise_model_name(value: Any) -> str | None:
|
|
190
|
+
"""Return a cleaned model name or None when not usable."""
|
|
191
|
+
if value is None:
|
|
192
|
+
return None
|
|
193
|
+
if isinstance(value, str):
|
|
194
|
+
cleaned = value.strip()
|
|
195
|
+
return cleaned or None
|
|
196
|
+
if isinstance(value, bool):
|
|
197
|
+
return None
|
|
198
|
+
return str(value)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _model_from_config(agent: Any) -> str | None:
|
|
202
|
+
"""Extract a usable model name from an agent's configuration mapping."""
|
|
203
|
+
config = getattr(agent, "agent_config", None)
|
|
204
|
+
if not config or not isinstance(config, dict):
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
for key in ("lm_name", "model"):
|
|
208
|
+
normalised = _normalise_model_name(config.get(key))
|
|
209
|
+
if normalised:
|
|
210
|
+
return normalised
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
|
|
91
214
|
def _get_agent_model_name(agent: Any) -> str | None:
|
|
92
215
|
"""Extract model name from agent configuration."""
|
|
93
|
-
|
|
94
|
-
if
|
|
95
|
-
|
|
96
|
-
return agent.agent_config.get("lm_name") or agent.agent_config.get("model")
|
|
216
|
+
config_model = _model_from_config(agent)
|
|
217
|
+
if config_model:
|
|
218
|
+
return config_model
|
|
97
219
|
|
|
98
|
-
|
|
99
|
-
|
|
220
|
+
normalised_attr = _normalise_model_name(getattr(agent, "model", None))
|
|
221
|
+
if normalised_attr:
|
|
222
|
+
return normalised_attr
|
|
100
223
|
|
|
101
|
-
# Default fallback
|
|
102
224
|
return DEFAULT_MODEL
|
|
103
225
|
|
|
104
226
|
|
|
@@ -118,7 +240,7 @@ def _resolve_resources_by_name(
|
|
|
118
240
|
List of resolved resource IDs
|
|
119
241
|
"""
|
|
120
242
|
out = []
|
|
121
|
-
for ref in
|
|
243
|
+
for ref in items or ():
|
|
122
244
|
if is_uuid(ref):
|
|
123
245
|
out.append(ref)
|
|
124
246
|
continue
|
|
@@ -127,9 +249,7 @@ def _resolve_resources_by_name(
|
|
|
127
249
|
if not matches:
|
|
128
250
|
raise click.ClickException(f"{label} not found: {ref}")
|
|
129
251
|
if len(matches) > 1:
|
|
130
|
-
raise click.ClickException(
|
|
131
|
-
f"Multiple {resource_type}s named '{ref}'. Use ID instead."
|
|
132
|
-
)
|
|
252
|
+
raise click.ClickException(f"Multiple {resource_type}s named '{ref}'. Use ID instead.")
|
|
133
253
|
out.append(str(matches[0].id))
|
|
134
254
|
return out
|
|
135
255
|
|
|
@@ -171,11 +291,11 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
171
291
|
"metadata",
|
|
172
292
|
"language_model_id",
|
|
173
293
|
"agent_config",
|
|
174
|
-
"tool_configs",
|
|
175
294
|
"tools",
|
|
176
295
|
"agents",
|
|
177
296
|
"mcps",
|
|
178
297
|
"a2a_profile",
|
|
298
|
+
"tool_configs",
|
|
179
299
|
]
|
|
180
300
|
|
|
181
301
|
result_data = build_resource_result_data(full_agent, fields)
|
|
@@ -192,40 +312,143 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
192
312
|
return result_data
|
|
193
313
|
|
|
194
314
|
|
|
195
|
-
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:
|
|
196
393
|
"""Display full agent details using raw API data to preserve ALL fields."""
|
|
197
394
|
if agent is None:
|
|
198
|
-
handle_rich_output(ctx,
|
|
395
|
+
handle_rich_output(ctx, markup_text(f"[{ERROR_STYLE}]❌ No agent provided[/]"))
|
|
199
396
|
return
|
|
200
397
|
|
|
398
|
+
preview_limit = _clamp_instruction_preview_limit(instruction_preview_limit)
|
|
399
|
+
trimmed_instruction = False
|
|
400
|
+
|
|
201
401
|
# Try to fetch and format raw agent data first
|
|
202
|
-
|
|
402
|
+
with spinner_context(
|
|
403
|
+
ctx,
|
|
404
|
+
"[bold blue]Loading agent details…[/bold blue]",
|
|
405
|
+
console_override=console,
|
|
406
|
+
):
|
|
407
|
+
formatted_data = _fetch_and_format_raw_agent_data(client, agent)
|
|
203
408
|
|
|
204
409
|
if formatted_data:
|
|
205
410
|
# Use raw API data - this preserves ALL fields including account_id
|
|
206
|
-
panel_title = f"
|
|
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
|
+
)
|
|
207
416
|
output_result(
|
|
208
417
|
ctx,
|
|
209
|
-
|
|
210
|
-
title=
|
|
211
|
-
panel_title=panel_title,
|
|
418
|
+
payload,
|
|
419
|
+
title=panel_title,
|
|
212
420
|
)
|
|
213
421
|
else:
|
|
214
422
|
# Fall back to Pydantic model data if raw fetch fails
|
|
215
423
|
handle_rich_output(
|
|
216
|
-
ctx,
|
|
424
|
+
ctx,
|
|
425
|
+
markup_text(f"[{WARNING_STYLE}]Falling back to Pydantic model data[/]"),
|
|
217
426
|
)
|
|
218
427
|
|
|
219
|
-
|
|
428
|
+
with spinner_context(
|
|
429
|
+
ctx,
|
|
430
|
+
"[bold blue]Preparing fallback agent details…[/bold blue]",
|
|
431
|
+
console_override=console,
|
|
432
|
+
):
|
|
433
|
+
result_data = _format_fallback_agent_data(client, agent)
|
|
220
434
|
|
|
221
435
|
# Display using output_result
|
|
436
|
+
payload, trimmed_instruction = _prepare_agent_details_payload(
|
|
437
|
+
result_data,
|
|
438
|
+
instruction_preview_limit=preview_limit,
|
|
439
|
+
)
|
|
222
440
|
output_result(
|
|
223
441
|
ctx,
|
|
224
|
-
|
|
442
|
+
payload,
|
|
225
443
|
title="Agent Details",
|
|
226
|
-
panel_title=f"🤖 {result_data.get('name', 'Unknown')}",
|
|
227
444
|
)
|
|
228
445
|
|
|
446
|
+
_show_instruction_trim_hint(
|
|
447
|
+
ctx,
|
|
448
|
+
trimmed=trimmed_instruction,
|
|
449
|
+
preview_limit=preview_limit,
|
|
450
|
+
)
|
|
451
|
+
|
|
229
452
|
|
|
230
453
|
@click.group(name="agents", no_args_is_help=True)
|
|
231
454
|
def agents_group() -> None:
|
|
@@ -240,34 +463,47 @@ def _resolve_agent(
|
|
|
240
463
|
select: int | None = None,
|
|
241
464
|
interface_preference: str = "fuzzy",
|
|
242
465
|
) -> Any | None:
|
|
243
|
-
"""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.
|
|
244
471
|
|
|
245
472
|
Args:
|
|
246
|
-
|
|
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.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Agent object when found, None when resolution fails.
|
|
247
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
|
|
248
490
|
return resolve_resource_reference(
|
|
249
491
|
ctx,
|
|
250
492
|
client,
|
|
251
493
|
ref,
|
|
252
|
-
"
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"
|
|
494
|
+
resolution_config["resource_type"],
|
|
495
|
+
resolution_config["get_by_id"],
|
|
496
|
+
resolution_config["find_by_name"],
|
|
497
|
+
resolution_config["label"],
|
|
256
498
|
select=select,
|
|
257
499
|
interface_preference=interface_preference,
|
|
258
500
|
)
|
|
259
501
|
|
|
260
502
|
|
|
261
503
|
@agents_group.command(name="list")
|
|
262
|
-
@click.option(
|
|
263
|
-
|
|
264
|
-
)
|
|
265
|
-
@click.option(
|
|
266
|
-
"--type", "agent_type", help="Filter by agent type (config, code, a2a, langflow)"
|
|
267
|
-
)
|
|
268
|
-
@click.option(
|
|
269
|
-
"--framework", help="Filter by framework (langchain, langgraph, google_adk)"
|
|
270
|
-
)
|
|
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)")
|
|
271
507
|
@click.option("--name", help="Filter by partial name match (case-insensitive)")
|
|
272
508
|
@click.option("--version", help="Filter by exact version match")
|
|
273
509
|
@click.option(
|
|
@@ -288,34 +524,62 @@ def list_agents(
|
|
|
288
524
|
) -> None:
|
|
289
525
|
"""List agents with optional filtering."""
|
|
290
526
|
try:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
527
|
+
with with_client_and_spinner(
|
|
528
|
+
ctx,
|
|
529
|
+
"[bold blue]Fetching agents…[/bold blue]",
|
|
530
|
+
console_override=console,
|
|
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)
|
|
299
541
|
|
|
300
542
|
# Define table columns: (data_key, header, style, width)
|
|
301
543
|
columns = [
|
|
302
544
|
("id", "ID", "dim", 36),
|
|
303
|
-
("name", "Name",
|
|
304
|
-
("type", "Type",
|
|
305
|
-
("framework", "Framework",
|
|
306
|
-
("version", "Version",
|
|
545
|
+
("name", "Name", ACCENT_STYLE, None),
|
|
546
|
+
("type", "Type", WARNING_STYLE, None),
|
|
547
|
+
("framework", "Framework", INFO, None),
|
|
548
|
+
("version", "Version", SUCCESS, None),
|
|
307
549
|
]
|
|
308
550
|
|
|
309
551
|
# Transform function for safe attribute access
|
|
310
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
|
+
"""
|
|
311
561
|
row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
|
|
312
562
|
# Ensure id is always a string
|
|
313
563
|
row["id"] = str(row["id"])
|
|
314
564
|
return row
|
|
315
565
|
|
|
316
566
|
# Use fuzzy picker for interactive agent selection and details (default behavior)
|
|
317
|
-
# Skip if --simple flag is used
|
|
318
|
-
|
|
567
|
+
# Skip if --simple flag is used, a name filter is applied, or non-rich output is requested
|
|
568
|
+
ctx_obj = ctx.obj if isinstance(getattr(ctx, "obj", None), dict) else {}
|
|
569
|
+
current_view = ctx_obj.get("view")
|
|
570
|
+
interactive_enabled = (
|
|
571
|
+
not simple
|
|
572
|
+
and name is None
|
|
573
|
+
and current_view not in {"json", "plain", "md"}
|
|
574
|
+
and console.is_terminal
|
|
575
|
+
and os.isatty(1)
|
|
576
|
+
and len(agents) > 0
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# Track picker attempt so the fallback table doesn't re-open the palette
|
|
580
|
+
picker_attempted = False
|
|
581
|
+
if interactive_enabled:
|
|
582
|
+
picker_attempted = True
|
|
319
583
|
picked_agent = _fuzzy_pick_for_resources(agents, "agent", "")
|
|
320
584
|
if picked_agent:
|
|
321
585
|
_display_agent_details(ctx, client, picked_agent)
|
|
@@ -324,10 +588,23 @@ def list_agents(
|
|
|
324
588
|
return
|
|
325
589
|
|
|
326
590
|
# Show simple table (either --simple flag or non-interactive)
|
|
327
|
-
output_list(
|
|
591
|
+
output_list(
|
|
592
|
+
ctx,
|
|
593
|
+
agents,
|
|
594
|
+
f"{ICON_AGENT} Available Agents",
|
|
595
|
+
columns,
|
|
596
|
+
transform_agent,
|
|
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))
|
|
602
|
+
),
|
|
603
|
+
use_pager=False,
|
|
604
|
+
)
|
|
328
605
|
|
|
329
606
|
except Exception as e:
|
|
330
|
-
raise click.ClickException(str(e))
|
|
607
|
+
raise click.ClickException(str(e)) from e
|
|
331
608
|
|
|
332
609
|
|
|
333
610
|
@agents_group.command()
|
|
@@ -338,60 +615,66 @@ def list_agents(
|
|
|
338
615
|
type=click.Path(dir_okay=False, writable=True),
|
|
339
616
|
help="Export complete agent configuration to file (format auto-detected from .json/.yaml extension)",
|
|
340
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
|
+
)
|
|
341
625
|
@output_flags()
|
|
342
626
|
@click.pass_context
|
|
343
|
-
def get(
|
|
344
|
-
|
|
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.
|
|
345
635
|
|
|
636
|
+
\b
|
|
346
637
|
Examples:
|
|
347
638
|
aip agents get my-agent
|
|
348
639
|
aip agents get my-agent --export agent.json # Exports complete configuration as JSON
|
|
349
640
|
aip agents get my-agent --export agent.yaml # Exports complete configuration as YAML
|
|
350
641
|
"""
|
|
351
642
|
try:
|
|
352
|
-
client
|
|
643
|
+
# Initialize API client for agent retrieval
|
|
644
|
+
api_client = get_client(ctx)
|
|
353
645
|
|
|
354
|
-
# Resolve agent
|
|
355
|
-
agent = _resolve_agent(
|
|
356
|
-
ctx, client, agent_ref, select, interface_preference="questionary"
|
|
357
|
-
)
|
|
646
|
+
# Resolve agent reference using questionary interface for better UX
|
|
647
|
+
agent = _resolve_agent(ctx, api_client, agent_ref, select, interface_preference="questionary")
|
|
358
648
|
|
|
359
|
-
|
|
649
|
+
if not agent:
|
|
650
|
+
raise click.ClickException(f"Agent '{agent_ref}' not found")
|
|
651
|
+
|
|
652
|
+
# Handle export option if requested
|
|
360
653
|
if export:
|
|
361
|
-
|
|
362
|
-
# Auto-detect format from file extension
|
|
363
|
-
detected_format = detect_export_format(export_path)
|
|
364
|
-
|
|
365
|
-
# Always export comprehensive data - re-fetch agent with full details
|
|
366
|
-
try:
|
|
367
|
-
agent = client.agents.get_agent_by_id(agent.id)
|
|
368
|
-
except Exception as e:
|
|
369
|
-
handle_rich_output(
|
|
370
|
-
ctx,
|
|
371
|
-
Text(
|
|
372
|
-
f"[yellow]⚠️ Could not fetch full agent details: {e}[/yellow]"
|
|
373
|
-
),
|
|
374
|
-
)
|
|
375
|
-
handle_rich_output(
|
|
376
|
-
ctx, Text("[yellow]⚠️ Proceeding with available data[/yellow]")
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
export_resource_to_file(agent, export_path, detected_format)
|
|
380
|
-
handle_rich_output(
|
|
654
|
+
handle_resource_export(
|
|
381
655
|
ctx,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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,
|
|
385
661
|
)
|
|
386
662
|
|
|
387
663
|
# Display full agent details using the standardized helper
|
|
388
|
-
_display_agent_details(
|
|
664
|
+
_display_agent_details(
|
|
665
|
+
ctx,
|
|
666
|
+
api_client,
|
|
667
|
+
agent,
|
|
668
|
+
instruction_preview_limit=instruction_preview,
|
|
669
|
+
)
|
|
389
670
|
|
|
390
671
|
# Show run suggestions via centralized display helper
|
|
391
672
|
handle_rich_output(ctx, display_agent_run_suggestions(agent))
|
|
392
673
|
|
|
674
|
+
except click.ClickException:
|
|
675
|
+
raise
|
|
393
676
|
except Exception as e:
|
|
394
|
-
raise click.ClickException(str(e))
|
|
677
|
+
raise click.ClickException(str(e)) from e
|
|
395
678
|
|
|
396
679
|
|
|
397
680
|
def _validate_run_input(input_option: str | None, input_text: str | None) -> str:
|
|
@@ -399,9 +682,7 @@ def _validate_run_input(input_option: str | None, input_text: str | None) -> str
|
|
|
399
682
|
final_input_text = input_option if input_option else input_text
|
|
400
683
|
|
|
401
684
|
if not final_input_text:
|
|
402
|
-
raise click.ClickException(
|
|
403
|
-
"Input text is required. Use either positional argument or --input option."
|
|
404
|
-
)
|
|
685
|
+
raise click.ClickException("Input text is required. Use either positional argument or --input option.")
|
|
405
686
|
|
|
406
687
|
return final_input_text
|
|
407
688
|
|
|
@@ -413,8 +694,8 @@ def _parse_chat_history(chat_history: str | None) -> list[dict[str, Any]] | None
|
|
|
413
694
|
|
|
414
695
|
try:
|
|
415
696
|
return json.loads(chat_history)
|
|
416
|
-
except json.JSONDecodeError:
|
|
417
|
-
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
|
|
418
699
|
|
|
419
700
|
|
|
420
701
|
def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
|
|
@@ -428,6 +709,23 @@ def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
|
|
|
428
709
|
)
|
|
429
710
|
|
|
430
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
|
+
|
|
431
729
|
def _prepare_run_kwargs(
|
|
432
730
|
agent: Any,
|
|
433
731
|
final_input_text: str,
|
|
@@ -477,9 +775,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
477
775
|
if ext == "json":
|
|
478
776
|
save_data = {
|
|
479
777
|
"output": result or "",
|
|
480
|
-
"full_debug_output": getattr(
|
|
481
|
-
working_console, "get_captured_output", lambda: ""
|
|
482
|
-
)(),
|
|
778
|
+
"full_debug_output": getattr(working_console, "get_captured_output", lambda: "")(),
|
|
483
779
|
"timestamp": "captured during agent execution",
|
|
484
780
|
}
|
|
485
781
|
content = json.dumps(save_data, indent=2)
|
|
@@ -492,7 +788,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
492
788
|
|
|
493
789
|
with open(save, "w", encoding="utf-8") as f:
|
|
494
790
|
f.write(content)
|
|
495
|
-
|
|
791
|
+
print_markup(f"[{SUCCESS_STYLE}]Full debug output saved to: {save}[/]", console=console)
|
|
496
792
|
|
|
497
793
|
|
|
498
794
|
@agents_group.command()
|
|
@@ -538,10 +834,11 @@ def run(
|
|
|
538
834
|
files: tuple[str, ...] | None,
|
|
539
835
|
verbose: bool,
|
|
540
836
|
) -> None:
|
|
541
|
-
"""Run an agent with input text.
|
|
837
|
+
r"""Run an agent with input text.
|
|
542
838
|
|
|
543
839
|
Usage: aip agents run <agent_ref> <input_text> [OPTIONS]
|
|
544
840
|
|
|
841
|
+
\b
|
|
545
842
|
Examples:
|
|
546
843
|
aip agents run my-agent "Hello world"
|
|
547
844
|
aip agents run agent-123 "Process this data" --timeout 600
|
|
@@ -549,14 +846,17 @@ def run(
|
|
|
549
846
|
"""
|
|
550
847
|
final_input_text = _validate_run_input(input_option, input_text)
|
|
551
848
|
|
|
849
|
+
if verbose:
|
|
850
|
+
_emit_verbose_guidance(ctx)
|
|
851
|
+
return
|
|
852
|
+
|
|
552
853
|
try:
|
|
553
854
|
client = get_client(ctx)
|
|
554
|
-
agent = _resolve_agent(
|
|
555
|
-
ctx, client, agent_ref, select, interface_preference="fuzzy"
|
|
556
|
-
)
|
|
855
|
+
agent = _resolve_agent(ctx, client, agent_ref, select, interface_preference="fuzzy")
|
|
557
856
|
|
|
558
857
|
parsed_chat_history = _parse_chat_history(chat_history)
|
|
559
858
|
renderer, working_console = _setup_run_renderer(ctx, save, verbose)
|
|
859
|
+
_maybe_attach_transcript_toggle(ctx, renderer)
|
|
560
860
|
|
|
561
861
|
try:
|
|
562
862
|
client.timeout = float(timeout)
|
|
@@ -574,17 +874,59 @@ def run(
|
|
|
574
874
|
|
|
575
875
|
result = client.agents.run_agent(**run_kwargs, timeout=timeout)
|
|
576
876
|
|
|
877
|
+
slash_mode = _running_in_slash_mode(ctx)
|
|
878
|
+
agent_id = str(_safe_agent_attribute(agent, "id") or "") or None
|
|
879
|
+
agent_name = _safe_agent_attribute(agent, "name")
|
|
880
|
+
model_hint = _get_agent_model_name(agent)
|
|
881
|
+
|
|
882
|
+
transcript_context = store_transcript_for_session(
|
|
883
|
+
ctx,
|
|
884
|
+
renderer,
|
|
885
|
+
final_result=result,
|
|
886
|
+
agent_id=agent_id,
|
|
887
|
+
agent_name=agent_name,
|
|
888
|
+
model=model_hint,
|
|
889
|
+
source="slash" if slash_mode else "cli",
|
|
890
|
+
)
|
|
891
|
+
|
|
577
892
|
_handle_run_output(ctx, result, renderer)
|
|
578
893
|
_save_run_transcript(save, result, working_console)
|
|
894
|
+
maybe_launch_post_run_viewer(
|
|
895
|
+
ctx,
|
|
896
|
+
transcript_context,
|
|
897
|
+
console=console,
|
|
898
|
+
slash_mode=slash_mode,
|
|
899
|
+
)
|
|
579
900
|
|
|
580
901
|
except AgentTimeoutError as e:
|
|
581
902
|
error_msg = str(e)
|
|
582
903
|
handle_json_output(ctx, error=Exception(error_msg))
|
|
583
|
-
raise click.ClickException(error_msg)
|
|
904
|
+
raise click.ClickException(error_msg) from e
|
|
584
905
|
except Exception as e:
|
|
585
906
|
_handle_command_exception(ctx, e)
|
|
586
907
|
|
|
587
908
|
|
|
909
|
+
def _running_in_slash_mode(ctx: Any) -> bool:
|
|
910
|
+
"""Return True if the command is executing inside the slash session."""
|
|
911
|
+
ctx_obj = getattr(ctx, "obj", None)
|
|
912
|
+
return isinstance(ctx_obj, dict) and bool(ctx_obj.get("_slash_session"))
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def _emit_verbose_guidance(ctx: Any) -> None:
|
|
916
|
+
"""Explain the modern alternative to the deprecated --verbose flag."""
|
|
917
|
+
if _running_in_slash_mode(ctx):
|
|
918
|
+
message = (
|
|
919
|
+
"[dim]Tip:[/] Verbose streaming has been retired in the command palette. Run the agent normally and open "
|
|
920
|
+
"the post-run viewer (Ctrl+T) to inspect the transcript."
|
|
921
|
+
)
|
|
922
|
+
else:
|
|
923
|
+
message = (
|
|
924
|
+
"[dim]Tip:[/] `--verbose` is no longer supported. Re-run without the flag and toggle the post-run viewer "
|
|
925
|
+
"(Ctrl+T) for detailed output."
|
|
926
|
+
)
|
|
927
|
+
handle_rich_output(ctx, markup_text(message))
|
|
928
|
+
|
|
929
|
+
|
|
588
930
|
def _handle_import_file_logic(
|
|
589
931
|
import_file: str,
|
|
590
932
|
model: str | None,
|
|
@@ -650,16 +992,12 @@ def _extract_and_validate_fields(
|
|
|
650
992
|
if not name:
|
|
651
993
|
raise click.ClickException("Agent name is required (--name or --import)")
|
|
652
994
|
if not instruction:
|
|
653
|
-
raise click.ClickException(
|
|
654
|
-
"Agent instruction is required (--instruction or --import)"
|
|
655
|
-
)
|
|
995
|
+
raise click.ClickException("Agent instruction is required (--instruction or --import)")
|
|
656
996
|
|
|
657
997
|
return name, instruction, model, tools, agents, mcps, timeout
|
|
658
998
|
|
|
659
999
|
|
|
660
|
-
def _validate_and_coerce_fields(
|
|
661
|
-
name: str, instruction: str, timeout: Any
|
|
662
|
-
) -> tuple[str, str, Any]:
|
|
1000
|
+
def _validate_and_coerce_fields(name: str, instruction: str, timeout: Any) -> tuple[str, str, Any]:
|
|
663
1001
|
"""Validate and coerce field values."""
|
|
664
1002
|
name = validate_agent_name(name)
|
|
665
1003
|
instruction = validate_agent_instruction(instruction)
|
|
@@ -670,19 +1008,11 @@ def _validate_and_coerce_fields(
|
|
|
670
1008
|
return name, instruction, timeout
|
|
671
1009
|
|
|
672
1010
|
|
|
673
|
-
def _resolve_resources(
|
|
674
|
-
client: Any, tools: tuple, agents: tuple, mcps: tuple
|
|
675
|
-
) -> tuple[list, list, list]:
|
|
1011
|
+
def _resolve_resources(client: Any, tools: tuple, agents: tuple, mcps: tuple) -> tuple[list, list, list]:
|
|
676
1012
|
"""Resolve tool, agent, and MCP references."""
|
|
677
|
-
resolved_tools = _resolve_resources_by_name(
|
|
678
|
-
|
|
679
|
-
)
|
|
680
|
-
resolved_agents = _resolve_resources_by_name(
|
|
681
|
-
client, agents, "agent", client.find_agents, "Agent"
|
|
682
|
-
)
|
|
683
|
-
resolved_mcps = _resolve_resources_by_name(
|
|
684
|
-
client, mcps, "mcp", client.find_mcps, "MCP"
|
|
685
|
-
)
|
|
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")
|
|
686
1016
|
|
|
687
1017
|
return resolved_tools, resolved_agents, resolved_mcps
|
|
688
1018
|
|
|
@@ -709,16 +1039,12 @@ def _build_create_kwargs(
|
|
|
709
1039
|
}
|
|
710
1040
|
|
|
711
1041
|
# Handle language model selection
|
|
712
|
-
lm_selection_dict, should_strip_lm_identity = resolve_language_model_selection(
|
|
713
|
-
merged_data, model
|
|
714
|
-
)
|
|
1042
|
+
lm_selection_dict, should_strip_lm_identity = resolve_language_model_selection(merged_data, model)
|
|
715
1043
|
create_kwargs.update(lm_selection_dict)
|
|
716
1044
|
|
|
717
1045
|
# Handle import file specific logic
|
|
718
1046
|
if import_file:
|
|
719
|
-
_add_import_file_attributes(
|
|
720
|
-
create_kwargs, merged_data, should_strip_lm_identity
|
|
721
|
-
)
|
|
1047
|
+
_add_import_file_attributes(create_kwargs, merged_data, should_strip_lm_identity)
|
|
722
1048
|
|
|
723
1049
|
return create_kwargs
|
|
724
1050
|
|
|
@@ -751,7 +1077,6 @@ def _add_import_file_attributes(
|
|
|
751
1077
|
"type",
|
|
752
1078
|
"framework",
|
|
753
1079
|
"version",
|
|
754
|
-
"tool_configs",
|
|
755
1080
|
"mcps",
|
|
756
1081
|
"a2a_profile",
|
|
757
1082
|
}
|
|
@@ -765,18 +1090,13 @@ def _get_language_model_display_name(agent: Any, model: str | None) -> str:
|
|
|
765
1090
|
lm_display = getattr(agent, "model", None)
|
|
766
1091
|
if not lm_display:
|
|
767
1092
|
cfg = getattr(agent, "agent_config", {}) or {}
|
|
768
|
-
lm_display = (
|
|
769
|
-
cfg.get("lm_name")
|
|
770
|
-
or cfg.get("model")
|
|
771
|
-
or model
|
|
772
|
-
or f"{DEFAULT_MODEL} (backend default)"
|
|
773
|
-
)
|
|
1093
|
+
lm_display = cfg.get("lm_name") or cfg.get("model") or model or f"{DEFAULT_MODEL} (backend default)"
|
|
774
1094
|
return lm_display
|
|
775
1095
|
|
|
776
1096
|
|
|
777
1097
|
def _handle_successful_creation(ctx: Any, agent: Any, model: str | None) -> None:
|
|
778
1098
|
"""Handle successful agent creation output."""
|
|
779
|
-
handle_json_output(ctx, agent
|
|
1099
|
+
handle_json_output(ctx, _prepare_agent_output(agent))
|
|
780
1100
|
|
|
781
1101
|
lm_display = _get_language_model_display_name(agent, model)
|
|
782
1102
|
|
|
@@ -805,7 +1125,7 @@ def _handle_command_exception(ctx: Any, e: Exception) -> None:
|
|
|
805
1125
|
handle_json_output(ctx, error=e)
|
|
806
1126
|
if get_ctx_value(ctx, "view") != "json":
|
|
807
1127
|
print_api_error(e)
|
|
808
|
-
raise click.
|
|
1128
|
+
raise click.exceptions.Exit(1) from e
|
|
809
1129
|
|
|
810
1130
|
|
|
811
1131
|
def _handle_creation_exception(ctx: Any, e: Exception) -> None:
|
|
@@ -848,8 +1168,9 @@ def create(
|
|
|
848
1168
|
timeout: float | None,
|
|
849
1169
|
import_file: str | None,
|
|
850
1170
|
) -> None:
|
|
851
|
-
"""Create a new agent.
|
|
1171
|
+
r"""Create a new agent.
|
|
852
1172
|
|
|
1173
|
+
\b
|
|
853
1174
|
Examples:
|
|
854
1175
|
aip agents create --name "My Agent" --instruction "You are a helpful assistant"
|
|
855
1176
|
aip agents create --import agent.json
|
|
@@ -859,13 +1180,9 @@ def create(
|
|
|
859
1180
|
|
|
860
1181
|
# Handle import file or CLI args
|
|
861
1182
|
if import_file:
|
|
862
|
-
merged_data = _handle_import_file_logic(
|
|
863
|
-
import_file, model, name, instruction, tools, agents, mcps, timeout
|
|
864
|
-
)
|
|
1183
|
+
merged_data = _handle_import_file_logic(import_file, model, name, instruction, tools, agents, mcps, timeout)
|
|
865
1184
|
else:
|
|
866
|
-
merged_data = _build_cli_args_data(
|
|
867
|
-
name, instruction, model, tools, agents, mcps, timeout
|
|
868
|
-
)
|
|
1185
|
+
merged_data = _build_cli_args_data(name, instruction, model, tools, agents, mcps, timeout)
|
|
869
1186
|
|
|
870
1187
|
# Extract and validate fields
|
|
871
1188
|
(
|
|
@@ -877,14 +1194,10 @@ def create(
|
|
|
877
1194
|
mcps,
|
|
878
1195
|
timeout,
|
|
879
1196
|
) = _extract_and_validate_fields(merged_data)
|
|
880
|
-
name, instruction, timeout = _validate_and_coerce_fields(
|
|
881
|
-
name, instruction, timeout
|
|
882
|
-
)
|
|
1197
|
+
name, instruction, timeout = _validate_and_coerce_fields(name, instruction, timeout)
|
|
883
1198
|
|
|
884
1199
|
# Resolve resources
|
|
885
|
-
resolved_tools, resolved_agents, resolved_mcps = _resolve_resources(
|
|
886
|
-
client, tools, agents, mcps
|
|
887
|
-
)
|
|
1200
|
+
resolved_tools, resolved_agents, resolved_mcps = _resolve_resources(client, tools, agents, mcps)
|
|
888
1201
|
|
|
889
1202
|
# Build create kwargs
|
|
890
1203
|
create_kwargs = _build_create_kwargs(
|
|
@@ -914,7 +1227,7 @@ def _get_agent_for_update(client: Any, agent_id: str) -> Any:
|
|
|
914
1227
|
try:
|
|
915
1228
|
return client.agents.get_agent_by_id(agent_id)
|
|
916
1229
|
except Exception as e:
|
|
917
|
-
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
|
|
918
1231
|
|
|
919
1232
|
|
|
920
1233
|
def _handle_update_import_file(
|
|
@@ -923,6 +1236,7 @@ def _handle_update_import_file(
|
|
|
923
1236
|
instruction: str | None,
|
|
924
1237
|
tools: tuple[str, ...] | None,
|
|
925
1238
|
agents: tuple[str, ...] | None,
|
|
1239
|
+
mcps: tuple[str, ...] | None,
|
|
926
1240
|
timeout: float | None,
|
|
927
1241
|
) -> tuple[
|
|
928
1242
|
Any | None,
|
|
@@ -930,11 +1244,12 @@ def _handle_update_import_file(
|
|
|
930
1244
|
str | None,
|
|
931
1245
|
tuple[str, ...] | None,
|
|
932
1246
|
tuple[str, ...] | None,
|
|
1247
|
+
tuple[str, ...] | None,
|
|
933
1248
|
float | None,
|
|
934
1249
|
]:
|
|
935
1250
|
"""Handle import file processing for agent update."""
|
|
936
1251
|
if not import_file:
|
|
937
|
-
return None, name, instruction, tools, agents, timeout
|
|
1252
|
+
return None, name, instruction, tools, agents, mcps, timeout
|
|
938
1253
|
|
|
939
1254
|
import_data = load_resource_from_file(Path(import_file), "agent")
|
|
940
1255
|
import_data = convert_export_to_import_format(import_data)
|
|
@@ -945,6 +1260,7 @@ def _handle_update_import_file(
|
|
|
945
1260
|
"instruction": instruction,
|
|
946
1261
|
"tools": tools or (),
|
|
947
1262
|
"agents": agents or (),
|
|
1263
|
+
"mcps": mcps or (),
|
|
948
1264
|
"timeout": timeout,
|
|
949
1265
|
}
|
|
950
1266
|
|
|
@@ -956,6 +1272,7 @@ def _handle_update_import_file(
|
|
|
956
1272
|
merged_data.get("instruction"),
|
|
957
1273
|
tuple(merged_data.get("tools", ())),
|
|
958
1274
|
tuple(merged_data.get("agents", ())),
|
|
1275
|
+
tuple(merged_data.get("mcps", ())),
|
|
959
1276
|
coerce_timeout(merged_data.get("timeout")),
|
|
960
1277
|
)
|
|
961
1278
|
|
|
@@ -965,6 +1282,7 @@ def _build_update_data(
|
|
|
965
1282
|
instruction: str | None,
|
|
966
1283
|
tools: tuple[str, ...] | None,
|
|
967
1284
|
agents: tuple[str, ...] | None,
|
|
1285
|
+
mcps: tuple[str, ...] | None,
|
|
968
1286
|
timeout: float | None,
|
|
969
1287
|
) -> dict[str, Any]:
|
|
970
1288
|
"""Build the update data dictionary from provided parameters."""
|
|
@@ -977,6 +1295,8 @@ def _build_update_data(
|
|
|
977
1295
|
update_data["tools"] = list(tools)
|
|
978
1296
|
if agents:
|
|
979
1297
|
update_data["agents"] = list(agents)
|
|
1298
|
+
if mcps:
|
|
1299
|
+
update_data["mcps"] = list(mcps)
|
|
980
1300
|
if timeout is not None:
|
|
981
1301
|
update_data["timeout"] = timeout
|
|
982
1302
|
return update_data
|
|
@@ -989,16 +1309,12 @@ def _handle_update_import_config(
|
|
|
989
1309
|
if not import_file:
|
|
990
1310
|
return
|
|
991
1311
|
|
|
992
|
-
lm_selection, should_strip_lm_identity = resolve_language_model_selection(
|
|
993
|
-
merged_data, None
|
|
994
|
-
)
|
|
1312
|
+
lm_selection, should_strip_lm_identity = resolve_language_model_selection(merged_data, None)
|
|
995
1313
|
update_data.update(lm_selection)
|
|
996
1314
|
|
|
997
1315
|
raw_cfg = merged_data.get("agent_config") if isinstance(merged_data, dict) else None
|
|
998
1316
|
if isinstance(raw_cfg, dict):
|
|
999
|
-
update_data["agent_config"] = sanitize_agent_config(
|
|
1000
|
-
raw_cfg, strip_lm_identity=should_strip_lm_identity
|
|
1001
|
-
)
|
|
1317
|
+
update_data["agent_config"] = sanitize_agent_config(raw_cfg, strip_lm_identity=should_strip_lm_identity)
|
|
1002
1318
|
|
|
1003
1319
|
excluded_fields = {
|
|
1004
1320
|
"name",
|
|
@@ -1014,8 +1330,6 @@ def _handle_update_import_config(
|
|
|
1014
1330
|
"type",
|
|
1015
1331
|
"framework",
|
|
1016
1332
|
"version",
|
|
1017
|
-
"tool_configs",
|
|
1018
|
-
"mcps",
|
|
1019
1333
|
"a2a_profile",
|
|
1020
1334
|
}
|
|
1021
1335
|
for key, value in merged_data.items():
|
|
@@ -1029,6 +1343,7 @@ def _handle_update_import_config(
|
|
|
1029
1343
|
@click.option("--instruction", help="New instruction")
|
|
1030
1344
|
@click.option("--tools", multiple=True, help="New tool names or IDs")
|
|
1031
1345
|
@click.option("--agents", multiple=True, help="New sub-agent names")
|
|
1346
|
+
@click.option("--mcps", multiple=True, help="New MCP names or IDs")
|
|
1032
1347
|
@click.option("--timeout", type=int, help="New timeout value")
|
|
1033
1348
|
@click.option(
|
|
1034
1349
|
"--import",
|
|
@@ -1045,11 +1360,13 @@ def update(
|
|
|
1045
1360
|
instruction: str | None,
|
|
1046
1361
|
tools: tuple[str, ...] | None,
|
|
1047
1362
|
agents: tuple[str, ...] | None,
|
|
1363
|
+
mcps: tuple[str, ...] | None,
|
|
1048
1364
|
timeout: float | None,
|
|
1049
1365
|
import_file: str | None,
|
|
1050
1366
|
) -> None:
|
|
1051
|
-
"""Update an existing agent.
|
|
1367
|
+
r"""Update an existing agent.
|
|
1052
1368
|
|
|
1369
|
+
\b
|
|
1053
1370
|
Examples:
|
|
1054
1371
|
aip agents update my-agent --instruction "New instruction"
|
|
1055
1372
|
aip agents update my-agent --import agent.json
|
|
@@ -1065,22 +1382,27 @@ def update(
|
|
|
1065
1382
|
instruction,
|
|
1066
1383
|
tools,
|
|
1067
1384
|
agents,
|
|
1385
|
+
mcps,
|
|
1068
1386
|
timeout,
|
|
1069
|
-
) = _handle_update_import_file(
|
|
1070
|
-
import_file, name, instruction, tools, agents, timeout
|
|
1071
|
-
)
|
|
1387
|
+
) = _handle_update_import_file(import_file, name, instruction, tools, agents, mcps, timeout)
|
|
1072
1388
|
|
|
1073
|
-
update_data = _build_update_data(name, instruction, tools, agents, timeout)
|
|
1389
|
+
update_data = _build_update_data(name, instruction, tools, agents, mcps, timeout)
|
|
1074
1390
|
|
|
1075
1391
|
if merged_data:
|
|
1076
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
|
|
1077
1399
|
|
|
1078
1400
|
if not update_data:
|
|
1079
1401
|
raise click.ClickException("No update fields specified")
|
|
1080
1402
|
|
|
1081
1403
|
updated_agent = client.agents.update_agent(agent.id, **update_data)
|
|
1082
1404
|
|
|
1083
|
-
handle_json_output(ctx, updated_agent
|
|
1405
|
+
handle_json_output(ctx, _prepare_agent_output(updated_agent))
|
|
1084
1406
|
handle_rich_output(ctx, display_update_success("Agent", updated_agent.name))
|
|
1085
1407
|
handle_rich_output(ctx, display_agent_run_suggestions(updated_agent))
|
|
1086
1408
|
|
|
@@ -1108,7 +1430,7 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
|
|
|
1108
1430
|
try:
|
|
1109
1431
|
agent = client.agents.get_agent_by_id(agent_id)
|
|
1110
1432
|
except Exception as e:
|
|
1111
|
-
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
|
|
1112
1434
|
|
|
1113
1435
|
# Confirm deletion when not forced
|
|
1114
1436
|
if not yes and not display_confirmation_prompt("Agent", agent.name):
|
|
@@ -1140,13 +1462,11 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
|
|
|
1140
1462
|
"--base-url",
|
|
1141
1463
|
help="Custom LangFlow server base URL (overrides LANGFLOW_BASE_URL env var)",
|
|
1142
1464
|
)
|
|
1143
|
-
@click.option(
|
|
1144
|
-
"--api-key", help="Custom LangFlow API key (overrides LANGFLOW_API_KEY env var)"
|
|
1145
|
-
)
|
|
1465
|
+
@click.option("--api-key", help="Custom LangFlow API key (overrides LANGFLOW_API_KEY env var)")
|
|
1146
1466
|
@output_flags()
|
|
1147
1467
|
@click.pass_context
|
|
1148
1468
|
def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
1149
|
-
"""Sync agents with LangFlow server flows.
|
|
1469
|
+
r"""Sync agents with LangFlow server flows.
|
|
1150
1470
|
|
|
1151
1471
|
This command fetches all flows from the configured LangFlow server and
|
|
1152
1472
|
creates/updates corresponding agents in the platform.
|
|
@@ -1155,6 +1475,7 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1155
1475
|
- Command options (--base-url, --api-key)
|
|
1156
1476
|
- Environment variables (LANGFLOW_BASE_URL, LANGFLOW_API_KEY)
|
|
1157
1477
|
|
|
1478
|
+
\b
|
|
1158
1479
|
Examples:
|
|
1159
1480
|
aip agents sync-langflow
|
|
1160
1481
|
aip agents sync-langflow --base-url https://my-langflow.com --api-key my-key
|
|
@@ -1170,18 +1491,17 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1170
1491
|
|
|
1171
1492
|
# Show success message for non-JSON output
|
|
1172
1493
|
if get_ctx_value(ctx, "view") != "json":
|
|
1173
|
-
from rich.text import Text
|
|
1174
|
-
|
|
1175
1494
|
# Extract some useful info from the result
|
|
1176
|
-
success_count = result.get("data", {}).get("created_count", 0) + result.get(
|
|
1177
|
-
"
|
|
1178
|
-
)
|
|
1495
|
+
success_count = result.get("data", {}).get("created_count", 0) + result.get("data", {}).get(
|
|
1496
|
+
"updated_count", 0
|
|
1497
|
+
)
|
|
1179
1498
|
total_count = result.get("data", {}).get("total_processed", 0)
|
|
1180
1499
|
|
|
1181
1500
|
handle_rich_output(
|
|
1182
1501
|
ctx,
|
|
1183
|
-
|
|
1184
|
-
f"[
|
|
1502
|
+
markup_text(
|
|
1503
|
+
f"[{SUCCESS_STYLE}]✅ Successfully synced {success_count} LangFlow agents "
|
|
1504
|
+
f"({total_count} total processed)[/]"
|
|
1185
1505
|
),
|
|
1186
1506
|
)
|
|
1187
1507
|
|