glaip-sdk 0.6.12__py3-none-any.whl → 0.6.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glaip_sdk/__init__.py +42 -5
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.15.dist-info}/METADATA +32 -37
- glaip_sdk-0.6.15.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.15.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.15.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.15.dist-info/top_level.txt +1 -0
- glaip_sdk/agents/__init__.py +0 -27
- glaip_sdk/agents/base.py +0 -1191
- glaip_sdk/cli/__init__.py +0 -9
- glaip_sdk/cli/account_store.py +0 -540
- glaip_sdk/cli/agent_config.py +0 -78
- glaip_sdk/cli/auth.py +0 -699
- glaip_sdk/cli/commands/__init__.py +0 -5
- glaip_sdk/cli/commands/accounts.py +0 -746
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/common_config.py +0 -101
- glaip_sdk/cli/commands/configure.py +0 -896
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/models.py +0 -69
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/commands/transcripts.py +0 -755
- glaip_sdk/cli/commands/update.py +0 -61
- glaip_sdk/cli/config.py +0 -95
- glaip_sdk/cli/constants.py +0 -38
- glaip_sdk/cli/context.py +0 -150
- glaip_sdk/cli/core/__init__.py +0 -79
- glaip_sdk/cli/core/context.py +0 -124
- glaip_sdk/cli/core/output.py +0 -846
- glaip_sdk/cli/core/prompting.py +0 -649
- glaip_sdk/cli/core/rendering.py +0 -187
- glaip_sdk/cli/display.py +0 -355
- glaip_sdk/cli/hints.py +0 -57
- glaip_sdk/cli/io.py +0 -112
- glaip_sdk/cli/main.py +0 -604
- glaip_sdk/cli/masking.py +0 -136
- glaip_sdk/cli/mcp_validators.py +0 -287
- glaip_sdk/cli/pager.py +0 -266
- glaip_sdk/cli/parsers/__init__.py +0 -7
- glaip_sdk/cli/parsers/json_input.py +0 -177
- glaip_sdk/cli/resolution.py +0 -67
- glaip_sdk/cli/rich_helpers.py +0 -27
- glaip_sdk/cli/slash/__init__.py +0 -15
- glaip_sdk/cli/slash/accounts_controller.py +0 -578
- glaip_sdk/cli/slash/accounts_shared.py +0 -75
- glaip_sdk/cli/slash/agent_session.py +0 -285
- glaip_sdk/cli/slash/prompt.py +0 -256
- glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
- glaip_sdk/cli/slash/session.py +0 -1708
- glaip_sdk/cli/slash/tui/__init__.py +0 -9
- glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
- glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
- glaip_sdk/cli/slash/tui/loading.py +0 -58
- glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
- glaip_sdk/cli/transcript/__init__.py +0 -31
- glaip_sdk/cli/transcript/cache.py +0 -536
- glaip_sdk/cli/transcript/capture.py +0 -329
- glaip_sdk/cli/transcript/export.py +0 -38
- glaip_sdk/cli/transcript/history.py +0 -815
- glaip_sdk/cli/transcript/launcher.py +0 -77
- glaip_sdk/cli/transcript/viewer.py +0 -374
- glaip_sdk/cli/update_notifier.py +0 -290
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk/cli/validators.py +0 -238
- glaip_sdk/client/__init__.py +0 -11
- glaip_sdk/client/_agent_payloads.py +0 -520
- glaip_sdk/client/agent_runs.py +0 -147
- glaip_sdk/client/agents.py +0 -1335
- glaip_sdk/client/base.py +0 -502
- glaip_sdk/client/main.py +0 -249
- glaip_sdk/client/mcps.py +0 -370
- glaip_sdk/client/run_rendering.py +0 -700
- glaip_sdk/client/shared.py +0 -21
- glaip_sdk/client/tools.py +0 -661
- glaip_sdk/client/validators.py +0 -198
- glaip_sdk/config/constants.py +0 -52
- glaip_sdk/mcps/__init__.py +0 -21
- glaip_sdk/mcps/base.py +0 -345
- glaip_sdk/models/__init__.py +0 -90
- glaip_sdk/models/agent.py +0 -47
- glaip_sdk/models/agent_runs.py +0 -116
- glaip_sdk/models/common.py +0 -42
- glaip_sdk/models/mcp.py +0 -33
- glaip_sdk/models/tool.py +0 -33
- glaip_sdk/payload_schemas/__init__.py +0 -7
- glaip_sdk/payload_schemas/agent.py +0 -85
- glaip_sdk/registry/__init__.py +0 -55
- glaip_sdk/registry/agent.py +0 -164
- glaip_sdk/registry/base.py +0 -139
- glaip_sdk/registry/mcp.py +0 -253
- glaip_sdk/registry/tool.py +0 -232
- glaip_sdk/runner/__init__.py +0 -59
- glaip_sdk/runner/base.py +0 -84
- glaip_sdk/runner/deps.py +0 -115
- glaip_sdk/runner/langgraph.py +0 -782
- glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
- glaip_sdk/runner/tool_adapter/__init__.py +0 -18
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
- glaip_sdk/tools/__init__.py +0 -22
- glaip_sdk/tools/base.py +0 -435
- glaip_sdk/utils/__init__.py +0 -86
- glaip_sdk/utils/a2a/__init__.py +0 -34
- glaip_sdk/utils/a2a/event_processor.py +0 -188
- glaip_sdk/utils/agent_config.py +0 -194
- glaip_sdk/utils/bundler.py +0 -267
- glaip_sdk/utils/client.py +0 -111
- glaip_sdk/utils/client_utils.py +0 -486
- glaip_sdk/utils/datetime_helpers.py +0 -58
- glaip_sdk/utils/discovery.py +0 -78
- glaip_sdk/utils/display.py +0 -135
- glaip_sdk/utils/export.py +0 -143
- glaip_sdk/utils/general.py +0 -61
- glaip_sdk/utils/import_export.py +0 -168
- glaip_sdk/utils/import_resolver.py +0 -492
- glaip_sdk/utils/instructions.py +0 -101
- glaip_sdk/utils/rendering/__init__.py +0 -115
- glaip_sdk/utils/rendering/formatting.py +0 -264
- glaip_sdk/utils/rendering/layout/__init__.py +0 -64
- glaip_sdk/utils/rendering/layout/panels.py +0 -156
- glaip_sdk/utils/rendering/layout/progress.py +0 -202
- glaip_sdk/utils/rendering/layout/summary.py +0 -74
- glaip_sdk/utils/rendering/layout/transcript.py +0 -606
- glaip_sdk/utils/rendering/models.py +0 -85
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
- glaip_sdk/utils/rendering/renderer/base.py +0 -1024
- glaip_sdk/utils/rendering/renderer/config.py +0 -27
- glaip_sdk/utils/rendering/renderer/console.py +0 -55
- glaip_sdk/utils/rendering/renderer/debug.py +0 -178
- glaip_sdk/utils/rendering/renderer/factory.py +0 -138
- glaip_sdk/utils/rendering/renderer/stream.py +0 -202
- glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
- glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
- glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
- glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
- glaip_sdk/utils/rendering/state.py +0 -204
- glaip_sdk/utils/rendering/step_tree_state.py +0 -100
- glaip_sdk/utils/rendering/steps/__init__.py +0 -34
- glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
- glaip_sdk/utils/rendering/steps/format.py +0 -176
- glaip_sdk/utils/rendering/steps/manager.py +0 -387
- glaip_sdk/utils/rendering/timing.py +0 -36
- glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
- glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
- glaip_sdk/utils/resource_refs.py +0 -195
- glaip_sdk/utils/run_renderer.py +0 -41
- glaip_sdk/utils/runtime_config.py +0 -425
- glaip_sdk/utils/serialization.py +0 -424
- glaip_sdk/utils/sync.py +0 -142
- glaip_sdk/utils/tool_detection.py +0 -33
- glaip_sdk/utils/validation.py +0 -264
- glaip_sdk-0.6.12.dist-info/RECORD +0 -159
- glaip_sdk-0.6.12.dist-info/entry_points.txt +0 -3
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""Configuration types for the renderer package.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class RendererConfig:
|
|
14
|
-
"""Configuration for the RichStreamRenderer."""
|
|
15
|
-
|
|
16
|
-
# Performance
|
|
17
|
-
refresh_debounce: float = 0.25
|
|
18
|
-
render_thinking: bool = True
|
|
19
|
-
live: bool = True
|
|
20
|
-
persist_live: bool = True
|
|
21
|
-
summary_display_window: int = 20
|
|
22
|
-
|
|
23
|
-
# Scrollback/append options
|
|
24
|
-
summary_max_steps: int = 0
|
|
25
|
-
append_finished_snapshots: bool = False
|
|
26
|
-
snapshot_max_chars: int = 0
|
|
27
|
-
snapshot_max_lines: int = 0
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Console handling utilities for the renderer package.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import io
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from rich.console import Console as RichConsole
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class CapturingConsole:
|
|
16
|
-
"""Console wrapper that captures all output for saving."""
|
|
17
|
-
|
|
18
|
-
def __init__(self, original_console: RichConsole, capture: bool = False) -> None:
|
|
19
|
-
"""Initialize the capturing console.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
original_console: The original Rich console instance
|
|
23
|
-
capture: Whether to capture output in addition to displaying it
|
|
24
|
-
"""
|
|
25
|
-
self.original_console = original_console
|
|
26
|
-
self.capture = capture
|
|
27
|
-
self.captured_output: list[str] = []
|
|
28
|
-
|
|
29
|
-
def print(self, *args: Any, **kwargs: Any) -> None:
|
|
30
|
-
"""Print to both original console and capture buffer if capturing."""
|
|
31
|
-
# Always print to original console
|
|
32
|
-
self.original_console.print(*args, **kwargs)
|
|
33
|
-
|
|
34
|
-
if self.capture:
|
|
35
|
-
# Capture the output as text
|
|
36
|
-
# Create a temporary console to capture output
|
|
37
|
-
temp_output = io.StringIO()
|
|
38
|
-
temp_console = RichConsole(
|
|
39
|
-
file=temp_output,
|
|
40
|
-
width=self.original_console.size.width,
|
|
41
|
-
legacy_windows=False,
|
|
42
|
-
force_terminal=False,
|
|
43
|
-
)
|
|
44
|
-
temp_console.print(*args, **kwargs)
|
|
45
|
-
self.captured_output.append(temp_output.getvalue())
|
|
46
|
-
|
|
47
|
-
def get_captured_output(self) -> str:
|
|
48
|
-
"""Get the captured output as plain text."""
|
|
49
|
-
if self.capture:
|
|
50
|
-
return "".join(self.captured_output)
|
|
51
|
-
return ""
|
|
52
|
-
|
|
53
|
-
def __getattr__(self, name: str) -> Any:
|
|
54
|
-
"""Delegate all other attributes to the original console."""
|
|
55
|
-
return getattr(self.original_console, name)
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
"""Debug rendering utilities for verbose SSE event display.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
from datetime import datetime, timezone
|
|
9
|
-
from typing import Any
|
|
10
|
-
from collections.abc import Callable, Iterable
|
|
11
|
-
|
|
12
|
-
from rich.console import Console
|
|
13
|
-
from rich.markdown import Markdown
|
|
14
|
-
|
|
15
|
-
from glaip_sdk.branding import PRIMARY, SUCCESS, WARNING
|
|
16
|
-
from glaip_sdk.rich_components import AIPPanel
|
|
17
|
-
from glaip_sdk.utils.datetime_helpers import coerce_datetime
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _parse_event_timestamp(event: dict[str, Any], received_ts: datetime | None = None) -> datetime | None:
|
|
21
|
-
"""Resolve the most accurate timestamp available for the event."""
|
|
22
|
-
if received_ts is not None:
|
|
23
|
-
return received_ts if received_ts.tzinfo else received_ts.replace(tzinfo=timezone.utc)
|
|
24
|
-
|
|
25
|
-
ts_value = event.get("timestamp") or (event.get("metadata") or {}).get("timestamp")
|
|
26
|
-
return coerce_datetime(ts_value)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _format_timestamp_for_display(dt: datetime) -> str:
|
|
30
|
-
"""Format timestamp for panel title, including timezone offset."""
|
|
31
|
-
local_dt = dt.astimezone()
|
|
32
|
-
ts_ms = local_dt.strftime("%H:%M:%S.%f")[:-3]
|
|
33
|
-
offset = local_dt.strftime("%z")
|
|
34
|
-
# offset is always non-empty for timezone-aware datetimes
|
|
35
|
-
offset = f"{offset[:3]}:{offset[3:]}"
|
|
36
|
-
return f"{ts_ms} {offset}"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _calculate_relative_time(
|
|
40
|
-
event_ts: datetime | None,
|
|
41
|
-
baseline_ts: datetime | None,
|
|
42
|
-
) -> tuple[float, str]:
|
|
43
|
-
"""Calculate relative time since start and format event timestamp."""
|
|
44
|
-
rel = 0.0
|
|
45
|
-
|
|
46
|
-
# Determine display timestamp - use event timestamp when present, otherwise current time
|
|
47
|
-
display_ts: datetime | None = event_ts
|
|
48
|
-
if display_ts is None:
|
|
49
|
-
display_ts = datetime.now(timezone.utc)
|
|
50
|
-
|
|
51
|
-
if event_ts is not None and baseline_ts is not None:
|
|
52
|
-
rel = max(0.0, (event_ts - baseline_ts).total_seconds())
|
|
53
|
-
|
|
54
|
-
ts_ms = _format_timestamp_for_display(display_ts)
|
|
55
|
-
|
|
56
|
-
return rel, ts_ms
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _get_event_metadata(event: dict[str, Any]) -> tuple[str, str | None]:
|
|
60
|
-
"""Extract event kind and status."""
|
|
61
|
-
sse_kind = (event.get("metadata") or {}).get("kind") or "event"
|
|
62
|
-
status_str = event.get("status") or (event.get("metadata") or {}).get("status")
|
|
63
|
-
return sse_kind, status_str
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _build_debug_title(sse_kind: str, status_str: str | None, ts_ms: str, rel: float) -> str:
|
|
67
|
-
"""Build the debug event title."""
|
|
68
|
-
if status_str:
|
|
69
|
-
return f"SSE: {sse_kind} — {status_str} @ {ts_ms} (+{rel:.2f}s)"
|
|
70
|
-
else:
|
|
71
|
-
return f"SSE: {sse_kind} @ {ts_ms} (+{rel:.2f}s)"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _dejson_value(obj: Any) -> Any:
|
|
75
|
-
"""Deep-parse JSON strings in nested objects."""
|
|
76
|
-
if isinstance(obj, dict):
|
|
77
|
-
return {k: _dejson_value(v) for k, v in obj.items()}
|
|
78
|
-
if isinstance(obj, list):
|
|
79
|
-
return [_dejson_value(x) for x in obj]
|
|
80
|
-
if isinstance(obj, str):
|
|
81
|
-
s = obj.strip()
|
|
82
|
-
if (s.startswith("{") and s.endswith("}")) or (s.startswith("[") and s.endswith("]")):
|
|
83
|
-
try:
|
|
84
|
-
return _dejson_value(json.loads(s))
|
|
85
|
-
except Exception:
|
|
86
|
-
return obj
|
|
87
|
-
return obj
|
|
88
|
-
return obj
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def _format_event_json(event: dict[str, Any]) -> str:
|
|
92
|
-
"""Format event as JSON with deep parsing."""
|
|
93
|
-
try:
|
|
94
|
-
return json.dumps(_dejson_value(event), indent=2, ensure_ascii=False)
|
|
95
|
-
except Exception:
|
|
96
|
-
return str(event)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def _get_border_color(sse_kind: str) -> str:
|
|
100
|
-
"""Get border color for event type."""
|
|
101
|
-
border_map = {
|
|
102
|
-
"agent_step": PRIMARY,
|
|
103
|
-
"content": SUCCESS,
|
|
104
|
-
"final_response": SUCCESS,
|
|
105
|
-
"status": WARNING,
|
|
106
|
-
"artifact": "grey42",
|
|
107
|
-
}
|
|
108
|
-
return border_map.get(sse_kind, "grey42")
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def _create_debug_panel(title: str, event_json: str, border: str) -> AIPPanel:
|
|
112
|
-
"""Create the debug panel."""
|
|
113
|
-
md = Markdown(f"```json\n{event_json}\n```", code_theme="monokai")
|
|
114
|
-
return AIPPanel(md, title=title, border_style=border)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def render_debug_event(
|
|
118
|
-
event: dict[str, Any],
|
|
119
|
-
console: Console,
|
|
120
|
-
*,
|
|
121
|
-
received_ts: datetime | None = None,
|
|
122
|
-
baseline_ts: datetime | None = None,
|
|
123
|
-
) -> None:
|
|
124
|
-
"""Render a debug panel for an SSE event.
|
|
125
|
-
|
|
126
|
-
Args:
|
|
127
|
-
event: The SSE event data
|
|
128
|
-
console: Rich console to print to
|
|
129
|
-
received_ts: Client-side receipt timestamp, if available
|
|
130
|
-
baseline_ts: Baseline event timestamp for elapsed timing
|
|
131
|
-
"""
|
|
132
|
-
try:
|
|
133
|
-
# Calculate timing information
|
|
134
|
-
event_ts = _parse_event_timestamp(event, received_ts)
|
|
135
|
-
rel, ts_ms = _calculate_relative_time(event_ts, baseline_ts)
|
|
136
|
-
|
|
137
|
-
# Extract event metadata
|
|
138
|
-
sse_kind, status_str = _get_event_metadata(event)
|
|
139
|
-
|
|
140
|
-
# Build title
|
|
141
|
-
title = _build_debug_title(sse_kind, status_str, ts_ms, rel)
|
|
142
|
-
|
|
143
|
-
# Format event JSON
|
|
144
|
-
event_json = _format_event_json(event)
|
|
145
|
-
|
|
146
|
-
# Get border color
|
|
147
|
-
border = _get_border_color(sse_kind)
|
|
148
|
-
|
|
149
|
-
# Create and print panel
|
|
150
|
-
panel = _create_debug_panel(title, event_json, border)
|
|
151
|
-
console.print(panel)
|
|
152
|
-
|
|
153
|
-
except Exception as e:
|
|
154
|
-
# Debug helpers must not break streaming
|
|
155
|
-
print(f"Debug error: {e}") # Fallback debug output
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def render_debug_event_stream(
|
|
159
|
-
events: Iterable[dict[str, Any]],
|
|
160
|
-
console: Console,
|
|
161
|
-
*,
|
|
162
|
-
resolve_timestamp: Callable[[dict[str, Any]], datetime | None],
|
|
163
|
-
) -> None:
|
|
164
|
-
"""Render a sequence of SSE events with baseline-aware timestamps."""
|
|
165
|
-
baseline: datetime | None = None
|
|
166
|
-
for event in events:
|
|
167
|
-
try:
|
|
168
|
-
received_ts = resolve_timestamp(event)
|
|
169
|
-
if baseline is None and received_ts is not None:
|
|
170
|
-
baseline = received_ts
|
|
171
|
-
render_debug_event(
|
|
172
|
-
event,
|
|
173
|
-
console,
|
|
174
|
-
received_ts=received_ts,
|
|
175
|
-
baseline_ts=baseline,
|
|
176
|
-
)
|
|
177
|
-
except Exception as exc: # pragma: no cover - debug stream resilience
|
|
178
|
-
console.print(f"[red]Debug stream error: {exc}[/red]")
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"""Renderer factory helpers for CLI, SDK, and slash sessions.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import io
|
|
10
|
-
from dataclasses import dataclass, is_dataclass, replace
|
|
11
|
-
from inspect import signature
|
|
12
|
-
from typing import Any
|
|
13
|
-
from collections.abc import Callable
|
|
14
|
-
|
|
15
|
-
from rich.console import Console
|
|
16
|
-
|
|
17
|
-
from glaip_sdk.utils.rendering.renderer.base import RichStreamRenderer
|
|
18
|
-
from glaip_sdk.utils.rendering.renderer.config import RendererConfig
|
|
19
|
-
from glaip_sdk.utils.rendering.state import TranscriptBuffer
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@dataclass(slots=True)
|
|
23
|
-
class RendererFactoryOptions:
|
|
24
|
-
"""Shared options for renderer factories."""
|
|
25
|
-
|
|
26
|
-
console: Console | None = None
|
|
27
|
-
cfg_overrides: dict[str, Any] | None = None
|
|
28
|
-
verbose: bool | None = None
|
|
29
|
-
transcript_buffer: TranscriptBuffer | None = None
|
|
30
|
-
callbacks: dict[str, Any] | None = None
|
|
31
|
-
|
|
32
|
-
def build(self, factory: Callable[..., RichStreamRenderer]) -> RichStreamRenderer:
|
|
33
|
-
"""Instantiate a renderer using the provided factory and stored options."""
|
|
34
|
-
params = signature(factory).parameters
|
|
35
|
-
kwargs: dict[str, Any] = {}
|
|
36
|
-
if self.console is not None and "console" in params:
|
|
37
|
-
kwargs["console"] = self.console
|
|
38
|
-
if self.cfg_overrides is not None and "cfg_overrides" in params:
|
|
39
|
-
kwargs["cfg_overrides"] = self.cfg_overrides
|
|
40
|
-
if self.verbose is not None and "verbose" in params:
|
|
41
|
-
kwargs["verbose"] = self.verbose
|
|
42
|
-
if self.transcript_buffer is not None and "transcript_buffer" in params:
|
|
43
|
-
kwargs["transcript_buffer"] = self.transcript_buffer
|
|
44
|
-
if self.callbacks is not None and "callbacks" in params:
|
|
45
|
-
kwargs["callbacks"] = self.callbacks
|
|
46
|
-
return factory(**kwargs)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _build_config(base: RendererConfig, overrides: dict[str, Any] | None = None) -> RendererConfig:
|
|
50
|
-
cfg = replace(base) if is_dataclass(base) else base
|
|
51
|
-
if overrides:
|
|
52
|
-
for key, value in overrides.items():
|
|
53
|
-
if hasattr(cfg, key):
|
|
54
|
-
setattr(cfg, key, value)
|
|
55
|
-
return cfg
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def make_default_renderer(
|
|
59
|
-
*,
|
|
60
|
-
console: Console | None = None,
|
|
61
|
-
cfg_overrides: dict[str, Any] | None = None,
|
|
62
|
-
verbose: bool = False,
|
|
63
|
-
transcript_buffer: TranscriptBuffer | None = None,
|
|
64
|
-
callbacks: dict[str, Any] | None = None,
|
|
65
|
-
) -> RichStreamRenderer:
|
|
66
|
-
"""Create the default renderer used by SDK and CLI flows."""
|
|
67
|
-
cfg = _build_config(RendererConfig(), cfg_overrides)
|
|
68
|
-
return RichStreamRenderer(
|
|
69
|
-
console=console or Console(),
|
|
70
|
-
cfg=cfg,
|
|
71
|
-
verbose=verbose,
|
|
72
|
-
transcript_buffer=transcript_buffer,
|
|
73
|
-
callbacks=callbacks,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def make_verbose_renderer(
|
|
78
|
-
*,
|
|
79
|
-
console: Console | None = None,
|
|
80
|
-
cfg_overrides: dict[str, Any] | None = None,
|
|
81
|
-
transcript_buffer: TranscriptBuffer | None = None,
|
|
82
|
-
callbacks: dict[str, Any] | None = None,
|
|
83
|
-
) -> RichStreamRenderer:
|
|
84
|
-
"""Create a verbose renderer with snapshot appending disabled."""
|
|
85
|
-
verbose_cfg = RendererConfig(live=True, append_finished_snapshots=False)
|
|
86
|
-
cfg = _build_config(verbose_cfg, cfg_overrides)
|
|
87
|
-
return RichStreamRenderer(
|
|
88
|
-
console=console or Console(),
|
|
89
|
-
cfg=cfg,
|
|
90
|
-
verbose=True,
|
|
91
|
-
transcript_buffer=transcript_buffer,
|
|
92
|
-
callbacks=callbacks,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def make_minimal_renderer(
|
|
97
|
-
*,
|
|
98
|
-
console: Console | None = None,
|
|
99
|
-
cfg_overrides: dict[str, Any] | None = None,
|
|
100
|
-
transcript_buffer: TranscriptBuffer | None = None,
|
|
101
|
-
callbacks: dict[str, Any] | None = None,
|
|
102
|
-
) -> RichStreamRenderer:
|
|
103
|
-
"""Create a renderer that prints only essential output."""
|
|
104
|
-
minimal_cfg = RendererConfig(live=False, persist_live=False, render_thinking=False)
|
|
105
|
-
cfg = _build_config(minimal_cfg, cfg_overrides)
|
|
106
|
-
return RichStreamRenderer(
|
|
107
|
-
console=console or Console(),
|
|
108
|
-
cfg=cfg,
|
|
109
|
-
verbose=False,
|
|
110
|
-
transcript_buffer=transcript_buffer,
|
|
111
|
-
callbacks=callbacks,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def make_silent_renderer(
|
|
116
|
-
*,
|
|
117
|
-
console: Console | None = None,
|
|
118
|
-
cfg_overrides: dict[str, Any] | None = None,
|
|
119
|
-
transcript_buffer: TranscriptBuffer | None = None,
|
|
120
|
-
callbacks: dict[str, Any] | None = None,
|
|
121
|
-
) -> RichStreamRenderer:
|
|
122
|
-
"""Create a renderer that suppresses terminal output for background flows."""
|
|
123
|
-
cfg = _build_config(
|
|
124
|
-
RendererConfig(
|
|
125
|
-
live=False,
|
|
126
|
-
persist_live=False,
|
|
127
|
-
render_thinking=False,
|
|
128
|
-
),
|
|
129
|
-
cfg_overrides,
|
|
130
|
-
)
|
|
131
|
-
silent_console = console or Console(file=io.StringIO(), force_terminal=False)
|
|
132
|
-
return RichStreamRenderer(
|
|
133
|
-
console=silent_console,
|
|
134
|
-
cfg=cfg,
|
|
135
|
-
verbose=False,
|
|
136
|
-
transcript_buffer=transcript_buffer,
|
|
137
|
-
callbacks=callbacks,
|
|
138
|
-
)
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
"""Event routing and parsing utilities for the renderer package.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Callable
|
|
10
|
-
from time import monotonic
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class StreamProcessor:
|
|
15
|
-
"""Handles event routing and parsing for streaming agent execution."""
|
|
16
|
-
|
|
17
|
-
def __init__(self) -> None:
|
|
18
|
-
"""Initialize the stream processor."""
|
|
19
|
-
self.streaming_started_at: float | None = None
|
|
20
|
-
self.server_elapsed_time: float | None = None
|
|
21
|
-
self.current_event_tools: set[str] = set()
|
|
22
|
-
self.current_event_sub_agents: set[str] = set()
|
|
23
|
-
self.current_event_finished_panels: set[str] = set()
|
|
24
|
-
self.last_event_time_by_ctx: dict[str, float] = {}
|
|
25
|
-
|
|
26
|
-
def reset_event_tracking(self) -> None:
|
|
27
|
-
"""Reset tracking for the current event."""
|
|
28
|
-
self.current_event_tools.clear()
|
|
29
|
-
self.current_event_sub_agents.clear()
|
|
30
|
-
self.current_event_finished_panels.clear()
|
|
31
|
-
|
|
32
|
-
def extract_event_metadata(self, event: dict[str, Any]) -> dict[str, Any]:
|
|
33
|
-
"""Extract metadata from an event.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
event: Event dictionary
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
Dictionary with extracted metadata
|
|
40
|
-
"""
|
|
41
|
-
metadata = event.get("metadata") or {}
|
|
42
|
-
# Update server elapsed timing if backend provides it
|
|
43
|
-
try:
|
|
44
|
-
t = metadata.get("time")
|
|
45
|
-
if isinstance(t, (int, float)):
|
|
46
|
-
self.server_elapsed_time = float(t)
|
|
47
|
-
except Exception:
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
"kind": metadata.get("kind") if metadata else event.get("kind"),
|
|
52
|
-
"task_id": metadata.get("task_id") or event.get("task_id"),
|
|
53
|
-
"context_id": metadata.get("context_id") or event.get("context_id"),
|
|
54
|
-
"content": event.get("content", ""),
|
|
55
|
-
"status": metadata.get("status") if metadata else event.get("status"),
|
|
56
|
-
"metadata": metadata,
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
def _extract_metadata_tool_calls(self, metadata: dict[str, Any]) -> tuple[str | None, dict, Any, list]:
|
|
60
|
-
"""Extract tool calls from metadata."""
|
|
61
|
-
tool_calls = metadata.get("tool_calls", [])
|
|
62
|
-
if not tool_calls:
|
|
63
|
-
return None, {}, None, []
|
|
64
|
-
|
|
65
|
-
# Take the first tool call if multiple exist
|
|
66
|
-
first_call = tool_calls[0] if isinstance(tool_calls, list) else tool_calls
|
|
67
|
-
tool_name = first_call.get("name")
|
|
68
|
-
tool_args = first_call.get("arguments", {})
|
|
69
|
-
tool_out = first_call.get("output")
|
|
70
|
-
|
|
71
|
-
# Collect info for all tool calls
|
|
72
|
-
tool_calls_info = []
|
|
73
|
-
for call in tool_calls if isinstance(tool_calls, list) else [tool_calls]:
|
|
74
|
-
if isinstance(call, dict) and "name" in call:
|
|
75
|
-
tool_calls_info.append(
|
|
76
|
-
(
|
|
77
|
-
call.get("name", ""),
|
|
78
|
-
call.get("arguments", {}),
|
|
79
|
-
call.get("output"),
|
|
80
|
-
)
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return tool_name, tool_args, tool_out, tool_calls_info
|
|
84
|
-
|
|
85
|
-
def _extract_tool_info_calls(self, tool_info: dict[str, Any]) -> tuple[str | None, dict, Any, list]:
|
|
86
|
-
"""Extract tool calls from tool_info structure."""
|
|
87
|
-
tool_calls_info = []
|
|
88
|
-
tool_name = None
|
|
89
|
-
tool_args = {}
|
|
90
|
-
tool_out = None
|
|
91
|
-
|
|
92
|
-
# Case 1: tool_info.tool_calls
|
|
93
|
-
ti_calls = tool_info.get("tool_calls")
|
|
94
|
-
if isinstance(ti_calls, list) and ti_calls:
|
|
95
|
-
for call in ti_calls:
|
|
96
|
-
if isinstance(call, dict) and call.get("name"):
|
|
97
|
-
tool_calls_info.append((call.get("name"), call.get("args", {}), call.get("output")))
|
|
98
|
-
if tool_calls_info:
|
|
99
|
-
tool_name, tool_args, tool_out = tool_calls_info[0]
|
|
100
|
-
return tool_name, tool_args, tool_out, tool_calls_info
|
|
101
|
-
|
|
102
|
-
# Case 2: single tool_info name/args/output
|
|
103
|
-
if tool_info.get("name"):
|
|
104
|
-
tool_name = tool_info.get("name")
|
|
105
|
-
tool_args = tool_info.get("args", {})
|
|
106
|
-
tool_out = tool_info.get("output")
|
|
107
|
-
tool_calls_info.append((tool_name, tool_args, tool_out))
|
|
108
|
-
|
|
109
|
-
return tool_name, tool_args, tool_out, tool_calls_info
|
|
110
|
-
|
|
111
|
-
def _extract_tool_calls_from_metadata(self, metadata: dict[str, Any]) -> tuple[str | None, dict, Any, list]:
|
|
112
|
-
"""Extract tool calls from metadata structure."""
|
|
113
|
-
tool_info = metadata.get("tool_info", {}) or {}
|
|
114
|
-
|
|
115
|
-
if tool_info:
|
|
116
|
-
return self._extract_tool_info_calls(tool_info)
|
|
117
|
-
|
|
118
|
-
return None, {}, None, []
|
|
119
|
-
|
|
120
|
-
def parse_tool_calls(self, event: dict[str, Any]) -> tuple[str | None, Any, Any, list[tuple[str, Any, Any]]]:
|
|
121
|
-
"""Parse tool call information from an event.
|
|
122
|
-
|
|
123
|
-
Args:
|
|
124
|
-
event: Event dictionary
|
|
125
|
-
|
|
126
|
-
Returns:
|
|
127
|
-
Tuple of (tool_name, tool_args, tool_output, tool_calls_info)
|
|
128
|
-
"""
|
|
129
|
-
metadata = event.get("metadata", {})
|
|
130
|
-
|
|
131
|
-
# Try primary extraction method
|
|
132
|
-
tool_calls_result = self._extract_metadata_tool_calls(metadata)
|
|
133
|
-
tool_name, tool_args, tool_out, tool_calls_info = tool_calls_result
|
|
134
|
-
|
|
135
|
-
# Fallback to nested metadata.tool_info (newer schema)
|
|
136
|
-
if not tool_calls_info:
|
|
137
|
-
fallback_result = self._extract_tool_calls_from_metadata(metadata)
|
|
138
|
-
tool_name, tool_args, tool_out, tool_calls_info = fallback_result
|
|
139
|
-
|
|
140
|
-
return tool_name, tool_args, tool_out, tool_calls_info
|
|
141
|
-
|
|
142
|
-
def update_timing(self, context_id: str | None) -> None:
|
|
143
|
-
"""Update timing information for the given context.
|
|
144
|
-
|
|
145
|
-
Args:
|
|
146
|
-
context_id: Context identifier
|
|
147
|
-
"""
|
|
148
|
-
if context_id:
|
|
149
|
-
self.last_event_time_by_ctx[context_id] = monotonic()
|
|
150
|
-
|
|
151
|
-
def track_tools_and_agents(
|
|
152
|
-
self,
|
|
153
|
-
tool_name: str | None,
|
|
154
|
-
tool_calls_info: list[tuple[str, Any, Any]],
|
|
155
|
-
is_delegation_tool_func: Callable[[str], bool],
|
|
156
|
-
) -> None:
|
|
157
|
-
"""Track tools and sub-agents mentioned in the current event.
|
|
158
|
-
|
|
159
|
-
Args:
|
|
160
|
-
tool_name: Primary tool name
|
|
161
|
-
tool_calls_info: List of tool call information
|
|
162
|
-
is_delegation_tool_func: Function to check if tool is delegation
|
|
163
|
-
"""
|
|
164
|
-
# Track all tools mentioned in this event
|
|
165
|
-
if tool_name:
|
|
166
|
-
self.current_event_tools.add(tool_name)
|
|
167
|
-
# If it's a delegation tool, add the sub-agent name
|
|
168
|
-
if is_delegation_tool_func(tool_name):
|
|
169
|
-
sub_agent_name = self._extract_sub_agent_name(tool_name)
|
|
170
|
-
self.current_event_sub_agents.add(sub_agent_name)
|
|
171
|
-
|
|
172
|
-
if tool_calls_info:
|
|
173
|
-
for tool_call_name, _, _ in tool_calls_info:
|
|
174
|
-
self.current_event_tools.add(tool_call_name)
|
|
175
|
-
# If it's a delegation tool, add the sub-agent name
|
|
176
|
-
if is_delegation_tool_func(tool_call_name):
|
|
177
|
-
sub_agent_name = self._extract_sub_agent_name(tool_call_name)
|
|
178
|
-
self.current_event_sub_agents.add(sub_agent_name)
|
|
179
|
-
|
|
180
|
-
def _extract_sub_agent_name(self, tool_name: str) -> str:
|
|
181
|
-
"""Extract sub-agent name from delegation tool name.
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
tool_name: Delegation tool name
|
|
185
|
-
|
|
186
|
-
Returns:
|
|
187
|
-
Sub-agent name
|
|
188
|
-
"""
|
|
189
|
-
if tool_name.startswith("delegate_to_"):
|
|
190
|
-
return tool_name.replace("delegate_to_", "")
|
|
191
|
-
elif tool_name.startswith("delegate_"):
|
|
192
|
-
return tool_name.replace("delegate_", "")
|
|
193
|
-
else:
|
|
194
|
-
return tool_name
|
|
195
|
-
|
|
196
|
-
def get_current_event_tools(self) -> set[str]:
|
|
197
|
-
"""Get the set of tools mentioned in the current event."""
|
|
198
|
-
return self.current_event_tools.copy()
|
|
199
|
-
|
|
200
|
-
def get_current_event_sub_agents(self) -> set[str]:
|
|
201
|
-
"""Get the set of sub-agents mentioned in the current event."""
|
|
202
|
-
return self.current_event_sub_agents.copy()
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"""Helpers for clamping the steps summary view to a rolling window.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Callable
|
|
10
|
-
|
|
11
|
-
from rich.text import Text
|
|
12
|
-
|
|
13
|
-
Node = tuple[str, tuple[bool, ...]]
|
|
14
|
-
LabelFn = Callable[[str], str]
|
|
15
|
-
ParentFn = Callable[[str], str | None]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def clamp_step_nodes(
|
|
19
|
-
nodes: list[Node],
|
|
20
|
-
*,
|
|
21
|
-
window: int,
|
|
22
|
-
get_label: LabelFn,
|
|
23
|
-
get_parent: ParentFn,
|
|
24
|
-
) -> tuple[list[Node], Text | None, Text | None]:
|
|
25
|
-
"""Return a windowed slice of nodes plus optional header/footer notices."""
|
|
26
|
-
if window <= 0 or len(nodes) <= window:
|
|
27
|
-
return nodes, None, None
|
|
28
|
-
|
|
29
|
-
start_index = len(nodes) - window
|
|
30
|
-
first_visible_step_id = nodes[start_index][0]
|
|
31
|
-
header = _build_header(first_visible_step_id, window, len(nodes), get_label, get_parent)
|
|
32
|
-
footer = _build_footer(len(nodes) - window)
|
|
33
|
-
return nodes[start_index:], header, footer
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _build_header(
|
|
37
|
-
step_id: str,
|
|
38
|
-
window: int,
|
|
39
|
-
total: int,
|
|
40
|
-
get_label: LabelFn,
|
|
41
|
-
get_parent: ParentFn,
|
|
42
|
-
) -> Text:
|
|
43
|
-
"""Construct the leading notice for a truncated window."""
|
|
44
|
-
parts = [f"… (latest {window} of {total} steps shown"]
|
|
45
|
-
path = _collect_path_labels(step_id, get_label, get_parent)
|
|
46
|
-
if path:
|
|
47
|
-
parts.append("; continuing with ")
|
|
48
|
-
parts.append(" / ".join(path))
|
|
49
|
-
parts.append(")")
|
|
50
|
-
return Text("".join(parts), style="dim")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _build_footer(hidden_count: int) -> Text:
|
|
54
|
-
"""Construct the footer notice indicating hidden steps."""
|
|
55
|
-
noun = "step" if hidden_count == 1 else "steps"
|
|
56
|
-
message = f"{hidden_count} earlier {noun} hidden. Press Ctrl+T to inspect the full transcript."
|
|
57
|
-
return Text(message, style="dim")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _collect_path_labels(
|
|
61
|
-
step_id: str,
|
|
62
|
-
get_label: LabelFn,
|
|
63
|
-
get_parent: ParentFn,
|
|
64
|
-
) -> list[str]:
|
|
65
|
-
"""Collect labels for the ancestry of the provided step."""
|
|
66
|
-
labels: list[str] = []
|
|
67
|
-
seen: set[str] = set()
|
|
68
|
-
current = step_id
|
|
69
|
-
while current and current not in seen:
|
|
70
|
-
seen.add(current)
|
|
71
|
-
label = get_label(current)
|
|
72
|
-
if label:
|
|
73
|
-
labels.append(label)
|
|
74
|
-
parent = get_parent(current)
|
|
75
|
-
if not parent:
|
|
76
|
-
break
|
|
77
|
-
current = parent
|
|
78
|
-
labels.reverse()
|
|
79
|
-
return labels
|