glaip-sdk 0.6.15b2__py3-none-any.whl → 0.6.16__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/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1196 -0
- glaip_sdk/cli/__init__.py +9 -0
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +78 -0
- glaip_sdk/cli/auth.py +699 -0
- glaip_sdk/cli/commands/__init__.py +5 -0
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +1509 -0
- glaip_sdk/cli/commands/common_config.py +104 -0
- glaip_sdk/cli/commands/configure.py +896 -0
- glaip_sdk/cli/commands/mcps.py +1356 -0
- glaip_sdk/cli/commands/models.py +69 -0
- glaip_sdk/cli/commands/tools.py +576 -0
- 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 +851 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +355 -0
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +112 -0
- glaip_sdk/cli/main.py +615 -0
- 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 +67 -0
- glaip_sdk/cli/rich_helpers.py +27 -0
- glaip_sdk/cli/slash/__init__.py +15 -0
- glaip_sdk/cli/slash/accounts_controller.py +578 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +285 -0
- glaip_sdk/cli/slash/prompt.py +256 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +1708 -0
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +876 -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 +374 -0
- glaip_sdk/cli/update_notifier.py +290 -0
- glaip_sdk/cli/utils.py +263 -0
- glaip_sdk/cli/validators.py +238 -0
- glaip_sdk/client/__init__.py +11 -0
- glaip_sdk/client/_agent_payloads.py +520 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +1335 -0
- glaip_sdk/client/base.py +502 -0
- glaip_sdk/client/main.py +249 -0
- glaip_sdk/client/mcps.py +370 -0
- glaip_sdk/client/run_rendering.py +700 -0
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +661 -0
- glaip_sdk/client/validators.py +198 -0
- glaip_sdk/config/constants.py +52 -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 +232 -0
- glaip_sdk/runner/__init__.py +59 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +112 -0
- glaip_sdk/runner/langgraph.py +782 -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 +257 -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 +219 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +86 -0
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +194 -0
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +486 -0
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +135 -0
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +61 -0
- glaip_sdk/utils/import_export.py +168 -0
- glaip_sdk/utils/import_resolver.py +492 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -0
- glaip_sdk/utils/rendering/formatting.py +264 -0
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/layout/panels.py +156 -0
- 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 +85 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +55 -0
- glaip_sdk/utils/rendering/renderer/base.py +1024 -0
- glaip_sdk/utils/rendering/renderer/config.py +27 -0
- glaip_sdk/utils/rendering/renderer/console.py +55 -0
- glaip_sdk/utils/rendering/renderer/debug.py +178 -0
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +202 -0
- 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 +195 -0
- glaip_sdk/utils/run_renderer.py +41 -0
- glaip_sdk/utils/runtime_config.py +425 -0
- glaip_sdk/utils/serialization.py +424 -0
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +264 -0
- {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.16.dist-info}/METADATA +4 -5
- glaip_sdk-0.6.16.dist-info/RECORD +160 -0
- glaip_sdk-0.6.15b2.dist-info/RECORD +0 -12
- {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.16.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.16.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.16.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Shared presenter utilities for CLI/offline transcript viewing.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.utils.rendering.layout.transcript import (
|
|
15
|
+
DEFAULT_TRANSCRIPT_THEME,
|
|
16
|
+
TranscriptGlyphs,
|
|
17
|
+
TranscriptSnapshot,
|
|
18
|
+
build_transcript_snapshot,
|
|
19
|
+
build_transcript_view,
|
|
20
|
+
)
|
|
21
|
+
from glaip_sdk.utils.rendering.renderer.debug import render_debug_event_stream
|
|
22
|
+
from glaip_sdk.utils.rendering.state import RendererState, coerce_received_at
|
|
23
|
+
from glaip_sdk.utils.rendering.steps import StepManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(slots=True)
|
|
27
|
+
class ViewerContext:
|
|
28
|
+
"""Runtime context passed to transcript presenters."""
|
|
29
|
+
|
|
30
|
+
manifest_entry: dict[str, Any]
|
|
31
|
+
events: list[dict[str, Any]]
|
|
32
|
+
default_output: str
|
|
33
|
+
final_output: str
|
|
34
|
+
stream_started_at: float | None
|
|
35
|
+
meta: dict[str, Any]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def render_post_run_view(
|
|
39
|
+
console: Console,
|
|
40
|
+
ctx: ViewerContext,
|
|
41
|
+
*,
|
|
42
|
+
glyphs: TranscriptGlyphs | None = None,
|
|
43
|
+
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
44
|
+
) -> TranscriptSnapshot:
|
|
45
|
+
"""Render the default summary view and return the snapshot used."""
|
|
46
|
+
snapshot, _state = prepare_viewer_snapshot(
|
|
47
|
+
ctx,
|
|
48
|
+
glyphs=glyphs,
|
|
49
|
+
theme=theme,
|
|
50
|
+
)
|
|
51
|
+
render_transcript_view(console, snapshot, theme=theme)
|
|
52
|
+
return snapshot
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def render_transcript_view(
|
|
56
|
+
console: Console,
|
|
57
|
+
snapshot: TranscriptSnapshot,
|
|
58
|
+
*,
|
|
59
|
+
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Render the transcript summary using a prepared snapshot."""
|
|
62
|
+
header, body = build_transcript_view(snapshot, theme=theme)
|
|
63
|
+
_print_renderables(console, header + body)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def render_transcript_events(console: Console, events: list[dict[str, Any]]) -> None:
|
|
67
|
+
"""Pretty-print transcript events using shared debug presenter."""
|
|
68
|
+
if not events:
|
|
69
|
+
console.print("[dim]No SSE events were captured for this run.[/dim]")
|
|
70
|
+
console.print()
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
console.print("[bold]Transcript Events[/bold]")
|
|
74
|
+
console.print("[dim]────────────────────────────────────────────────────────[/dim]")
|
|
75
|
+
|
|
76
|
+
render_debug_event_stream(
|
|
77
|
+
events,
|
|
78
|
+
console,
|
|
79
|
+
resolve_timestamp=lambda event: coerce_received_at(event.get("received_at")),
|
|
80
|
+
)
|
|
81
|
+
console.print()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def prepare_viewer_snapshot(
|
|
85
|
+
ctx: ViewerContext,
|
|
86
|
+
*,
|
|
87
|
+
glyphs: TranscriptGlyphs | None,
|
|
88
|
+
theme: str,
|
|
89
|
+
) -> tuple[TranscriptSnapshot, RendererState]:
|
|
90
|
+
"""Build a transcript snapshot plus renderer state for reusable viewing."""
|
|
91
|
+
state = _build_renderer_state(ctx)
|
|
92
|
+
manager = _build_steps_from_events(ctx.events)
|
|
93
|
+
query = _extract_query_from_manifest(ctx)
|
|
94
|
+
merged_meta = _merge_meta(ctx)
|
|
95
|
+
snapshot = build_transcript_snapshot(
|
|
96
|
+
state,
|
|
97
|
+
manager,
|
|
98
|
+
glyphs=glyphs,
|
|
99
|
+
query_text=query,
|
|
100
|
+
meta=merged_meta,
|
|
101
|
+
theme=theme,
|
|
102
|
+
)
|
|
103
|
+
return snapshot, state
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _build_renderer_state(ctx: ViewerContext) -> RendererState:
|
|
107
|
+
state = RendererState()
|
|
108
|
+
state.meta = dict(ctx.meta or {})
|
|
109
|
+
|
|
110
|
+
final_text = (ctx.final_output or "").strip()
|
|
111
|
+
default_text = (ctx.default_output or "").strip()
|
|
112
|
+
if final_text:
|
|
113
|
+
state.final_text = final_text
|
|
114
|
+
elif default_text:
|
|
115
|
+
state.final_text = default_text
|
|
116
|
+
state.buffer.append(default_text)
|
|
117
|
+
|
|
118
|
+
duration = _extract_final_duration(ctx.events)
|
|
119
|
+
if duration:
|
|
120
|
+
state.final_duration_text = duration # pragma: no cover - exercised indirectly via end-to-end tests
|
|
121
|
+
state.events = list(ctx.events or [])
|
|
122
|
+
return state
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _build_steps_from_events(events: list[dict[str, Any]]) -> StepManager:
|
|
126
|
+
manager = StepManager()
|
|
127
|
+
for event in events or []:
|
|
128
|
+
payload = _coerce_step_event(event)
|
|
129
|
+
if not payload:
|
|
130
|
+
continue
|
|
131
|
+
try:
|
|
132
|
+
manager.apply_event(payload)
|
|
133
|
+
except ValueError:
|
|
134
|
+
continue
|
|
135
|
+
return manager
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _coerce_step_event(event: dict[str, Any]) -> dict[str, Any] | None:
|
|
139
|
+
metadata = event.get("metadata")
|
|
140
|
+
if not isinstance(metadata, dict):
|
|
141
|
+
return None
|
|
142
|
+
if not isinstance(metadata.get("step_id"), str):
|
|
143
|
+
return None
|
|
144
|
+
return {
|
|
145
|
+
"metadata": metadata,
|
|
146
|
+
"status": event.get("status"),
|
|
147
|
+
"task_state": event.get("task_state"),
|
|
148
|
+
"content": event.get("content"),
|
|
149
|
+
"task_id": event.get("task_id"),
|
|
150
|
+
"context_id": event.get("context_id"),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _extract_final_duration(events: list[dict[str, Any]]) -> str | None:
|
|
155
|
+
for event in events or []:
|
|
156
|
+
metadata = event.get("metadata") or {}
|
|
157
|
+
if metadata.get("kind") != "final_response":
|
|
158
|
+
continue
|
|
159
|
+
time_value = metadata.get("time")
|
|
160
|
+
if isinstance(time_value, (int, float)):
|
|
161
|
+
return f"{float(time_value):.2f}s"
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _extract_query_from_manifest(ctx: ViewerContext) -> str | None:
|
|
166
|
+
query = ctx.manifest_entry.get("input_message") or ctx.meta.get("input_message") or ctx.meta.get("query")
|
|
167
|
+
if isinstance(query, str) and query.strip():
|
|
168
|
+
return query.strip()
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _merge_meta(ctx: ViewerContext) -> dict[str, Any]:
|
|
173
|
+
merged = dict(ctx.meta or {})
|
|
174
|
+
manifest = ctx.manifest_entry or {}
|
|
175
|
+
for key in ("agent_name", "agent_id", "model", "run_id", "input_message"):
|
|
176
|
+
if key in manifest and manifest[key] and key not in merged:
|
|
177
|
+
merged[key] = manifest[key]
|
|
178
|
+
return merged
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _print_renderables(console: Console, renderables: list[Any]) -> None:
|
|
182
|
+
for renderable in renderables:
|
|
183
|
+
console.print(renderable)
|
|
184
|
+
console.print()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Resource reference utilities for ID/name extraction and UUID detection.
|
|
2
|
+
|
|
3
|
+
This module provides normalized helpers for working with resource references
|
|
4
|
+
across the SDK, consolidating logic that was previously duplicated between
|
|
5
|
+
CLI and SDK layers.
|
|
6
|
+
|
|
7
|
+
Authors:
|
|
8
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from typing import Any
|
|
13
|
+
from uuid import UUID
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_uuid(value: str) -> bool:
|
|
17
|
+
"""Check if a string is a valid UUID.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
value: String to check
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
True if value is a valid UUID, False otherwise
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
UUID(value)
|
|
27
|
+
return True
|
|
28
|
+
except (ValueError, TypeError):
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _extract_id_from_item(item: Any, *, skip_missing: bool = False) -> str | None:
|
|
33
|
+
"""Extract ID from a single item.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
item: Item that may be a string, object with .id, or dict with "id" key.
|
|
37
|
+
skip_missing: If True, return None for items without IDs. If False, convert to string.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Extracted ID as string, or None if skip_missing=True and no ID found.
|
|
41
|
+
"""
|
|
42
|
+
if isinstance(item, str):
|
|
43
|
+
return item
|
|
44
|
+
if hasattr(item, "id"):
|
|
45
|
+
return str(item.id)
|
|
46
|
+
if isinstance(item, dict) and "id" in item:
|
|
47
|
+
return str(item["id"])
|
|
48
|
+
if skip_missing:
|
|
49
|
+
return None
|
|
50
|
+
# Fallback: convert to string
|
|
51
|
+
return str(item)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def extract_ids(items: list[str | Any] | None) -> list[str]:
|
|
55
|
+
"""Extract IDs from a list of objects or strings.
|
|
56
|
+
|
|
57
|
+
This function unifies the behavior between CLI and SDK layers, always
|
|
58
|
+
returning a list (empty list for None/empty input) rather than None.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
items: List of items that may be strings, objects with .id, or other types
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
List of extracted IDs (empty list if items is None/empty)
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
extract_ids([{"id": "123"}, "456"]) -> ["123", "456"]
|
|
68
|
+
extract_ids(None) -> []
|
|
69
|
+
extract_ids([]) -> []
|
|
70
|
+
"""
|
|
71
|
+
if not items:
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
# Extract IDs from all items, converting non-ID items to strings
|
|
75
|
+
extracted_ids = [_extract_id_from_item(item, skip_missing=False) for item in items]
|
|
76
|
+
return [id_val for id_val in extracted_ids if id_val is not None]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def extract_names(items: list[str | Any] | None) -> list[str]:
|
|
80
|
+
"""Extract names from a list of objects or strings.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
items: List of items that may be strings, objects with .name, or other types
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of extracted names (empty list if items is None/empty)
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
extract_names([{"name": "tool1"}, "tool2"]) -> ["tool1", "tool2"]
|
|
90
|
+
extract_names(None) -> []
|
|
91
|
+
"""
|
|
92
|
+
if not items:
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
names = []
|
|
96
|
+
for item in items:
|
|
97
|
+
if isinstance(item, str):
|
|
98
|
+
names.append(item)
|
|
99
|
+
elif hasattr(item, "name"):
|
|
100
|
+
names.append(str(item.name))
|
|
101
|
+
elif isinstance(item, dict) and "name" in item:
|
|
102
|
+
names.append(str(item["name"]))
|
|
103
|
+
else:
|
|
104
|
+
# Fallback: convert to string
|
|
105
|
+
names.append(str(item))
|
|
106
|
+
|
|
107
|
+
return names
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def find_by_name(items: list[Any], name: str, case_sensitive: bool = False) -> list[Any]:
|
|
111
|
+
"""Filter items by name with optional case sensitivity.
|
|
112
|
+
|
|
113
|
+
This is a common pattern used across different clients for client-side
|
|
114
|
+
filtering when the backend doesn't support name query parameters.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
items: List of items to filter
|
|
118
|
+
name: Name to search for
|
|
119
|
+
case_sensitive: Whether the search should be case sensitive
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Filtered list of items matching the name
|
|
123
|
+
"""
|
|
124
|
+
if not name:
|
|
125
|
+
return items
|
|
126
|
+
|
|
127
|
+
if case_sensitive:
|
|
128
|
+
return [item for item in items if name in item.name]
|
|
129
|
+
else:
|
|
130
|
+
return [item for item in items if name.lower() in item.name.lower()]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def sanitize_name(name: str) -> str:
|
|
134
|
+
"""Sanitize a name for resource creation.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: Raw name input
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Sanitized name suitable for resource creation
|
|
141
|
+
"""
|
|
142
|
+
# Remove special characters and normalize
|
|
143
|
+
sanitized = re.sub(r"[^a-zA-Z0-9\-_]", "-", name.strip())
|
|
144
|
+
sanitized = re.sub(r"-+", "-", sanitized) # Collapse multiple dashes
|
|
145
|
+
return sanitized.lower().strip("-")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def validate_name_format(name: str, resource_type: str = "resource") -> str:
|
|
149
|
+
"""Validate resource name format and return cleaned version.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
name: Name to validate
|
|
153
|
+
resource_type: Type of resource (for error messages)
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Cleaned name
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
ValueError: If name format is invalid
|
|
160
|
+
"""
|
|
161
|
+
# Map resource types to proper display names
|
|
162
|
+
type_display = {"agent": "Agent", "tool": "Tool", "mcp": "MCP"}
|
|
163
|
+
display_type = type_display.get(resource_type.lower(), resource_type.title())
|
|
164
|
+
|
|
165
|
+
if not name or not name.strip():
|
|
166
|
+
raise ValueError(f"{display_type} name cannot be empty")
|
|
167
|
+
|
|
168
|
+
cleaned_name = name.strip()
|
|
169
|
+
|
|
170
|
+
if len(cleaned_name) < 1:
|
|
171
|
+
raise ValueError(f"{display_type} name cannot be empty")
|
|
172
|
+
|
|
173
|
+
if len(cleaned_name) > 100:
|
|
174
|
+
raise ValueError(f"{display_type} name cannot be longer than 100 characters")
|
|
175
|
+
|
|
176
|
+
# Check for valid characters (alphanumeric, hyphens, underscores)
|
|
177
|
+
if not re.match(r"^[a-zA-Z0-9_-]+$", cleaned_name):
|
|
178
|
+
raise ValueError(f"{display_type} name can only contain letters, numbers, hyphens, and underscores")
|
|
179
|
+
|
|
180
|
+
return cleaned_name
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def validate_name_uniqueness(name: str, existing_names: list[str], resource_type: str = "resource") -> None:
|
|
184
|
+
"""Validate that a resource name is unique.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
name: Name to validate
|
|
188
|
+
existing_names: List of existing names to check against
|
|
189
|
+
resource_type: Type of resource (for error messages)
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
ValueError: If name is not unique
|
|
193
|
+
"""
|
|
194
|
+
if name.lower() in [existing.lower() for existing in existing_names]:
|
|
195
|
+
raise ValueError(f"A {resource_type.lower()} named '{name}' already exists. Please choose a unique name.")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Modern run renderer for agent execution with clean streaming output.
|
|
3
|
+
|
|
4
|
+
This module provides a modern CLI experience similar to Claude Code and Gemini CLI,
|
|
5
|
+
with compact headers, streaming markdown, collapsible tool steps, and clean output.
|
|
6
|
+
|
|
7
|
+
This is a compatibility shim that re-exports components from the new modular renderer package.
|
|
8
|
+
|
|
9
|
+
Authors:
|
|
10
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
# Configure logger
|
|
16
|
+
import logging
|
|
17
|
+
|
|
18
|
+
from glaip_sdk.utils.rendering.models import RunStats
|
|
19
|
+
|
|
20
|
+
# Re-export main components from the new modular renderer package
|
|
21
|
+
from glaip_sdk.utils.rendering.renderer.base import RichStreamRenderer
|
|
22
|
+
from glaip_sdk.utils.rendering.renderer.config import RendererConfig
|
|
23
|
+
from glaip_sdk.utils.rendering.renderer.console import CapturingConsole
|
|
24
|
+
|
|
25
|
+
# Legacy imports for backward compatibility
|
|
26
|
+
from glaip_sdk.utils.rendering.renderer.debug import render_debug_event
|
|
27
|
+
from glaip_sdk.utils.rendering.steps import StepManager
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger("glaip_sdk.run_renderer")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# The full implementation has been moved to glaip_sdk.utils.rendering.renderer.base
|
|
33
|
+
# This file now serves as a compatibility shim for existing imports.
|
|
34
|
+
__all__ = [
|
|
35
|
+
"CapturingConsole",
|
|
36
|
+
"RendererConfig",
|
|
37
|
+
"RichStreamRenderer",
|
|
38
|
+
"RunStats",
|
|
39
|
+
"StepManager",
|
|
40
|
+
"render_debug_event",
|
|
41
|
+
]
|