glaip-sdk 0.6.12__py3-none-any.whl → 0.6.14__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.14.dist-info}/METADATA +31 -37
- glaip_sdk-0.6.14.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.14.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.14.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.14.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,74 +0,0 @@
|
|
|
1
|
-
"""Summary panel helpers shared between renderer and viewer.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Mapping
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from glaip_sdk.utils.rendering.layout.transcript import (
|
|
13
|
-
DEFAULT_TRANSCRIPT_THEME,
|
|
14
|
-
build_transcript_snapshot,
|
|
15
|
-
build_transcript_view,
|
|
16
|
-
normalise_meta_payload,
|
|
17
|
-
)
|
|
18
|
-
from glaip_sdk.utils.rendering.state import RendererState
|
|
19
|
-
from glaip_sdk.utils.rendering.steps import StepManager
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def render_summary_panels(
|
|
23
|
-
state: RendererState,
|
|
24
|
-
steps: StepManager,
|
|
25
|
-
*,
|
|
26
|
-
theme: str | None = None,
|
|
27
|
-
summary_window: int | None = None,
|
|
28
|
-
include_query_panel: bool = True,
|
|
29
|
-
include_final_panel: bool = True,
|
|
30
|
-
step_status_overrides: dict[str, str] | None = None,
|
|
31
|
-
) -> list[Any]:
|
|
32
|
-
"""Return shared summary panels for renderer and offline viewer."""
|
|
33
|
-
resolved_theme = theme or DEFAULT_TRANSCRIPT_THEME
|
|
34
|
-
snapshot_source = state.to_snapshot() if hasattr(state, "to_snapshot") else state
|
|
35
|
-
if isinstance(snapshot_source, Mapping):
|
|
36
|
-
raw_meta = snapshot_source.get("meta")
|
|
37
|
-
else:
|
|
38
|
-
raw_meta = getattr(state, "meta", None)
|
|
39
|
-
snapshot_meta = normalise_meta_payload(raw_meta)
|
|
40
|
-
snapshot = build_transcript_snapshot(
|
|
41
|
-
snapshot_source,
|
|
42
|
-
steps,
|
|
43
|
-
meta=snapshot_meta,
|
|
44
|
-
summary_window=summary_window,
|
|
45
|
-
theme=resolved_theme,
|
|
46
|
-
step_status_overrides=step_status_overrides,
|
|
47
|
-
)
|
|
48
|
-
_header, body = build_transcript_view(snapshot, theme=resolved_theme)
|
|
49
|
-
|
|
50
|
-
return [
|
|
51
|
-
renderable
|
|
52
|
-
for renderable in body
|
|
53
|
-
if _should_include_summary_panel(
|
|
54
|
-
renderable,
|
|
55
|
-
include_query_panel=include_query_panel,
|
|
56
|
-
include_final_panel=include_final_panel,
|
|
57
|
-
)
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def _should_include_summary_panel(
|
|
62
|
-
renderable: Any,
|
|
63
|
-
*,
|
|
64
|
-
include_query_panel: bool,
|
|
65
|
-
include_final_panel: bool,
|
|
66
|
-
) -> bool:
|
|
67
|
-
"""Return True when the panel should be included in the summary list."""
|
|
68
|
-
title = getattr(renderable, "title", "")
|
|
69
|
-
normalised = title.lower() if isinstance(title, str) else ""
|
|
70
|
-
if not include_query_panel and normalised == "user request":
|
|
71
|
-
return False
|
|
72
|
-
if not include_final_panel and normalised.startswith("final result"):
|
|
73
|
-
return False
|
|
74
|
-
return True
|
|
@@ -1,606 +0,0 @@
|
|
|
1
|
-
"""Shared transcript presentation helpers.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
from collections.abc import Mapping, Sequence
|
|
11
|
-
from dataclasses import dataclass
|
|
12
|
-
from typing import Any
|
|
13
|
-
|
|
14
|
-
from rich.console import Group
|
|
15
|
-
from rich.markdown import Markdown
|
|
16
|
-
from rich.text import Text
|
|
17
|
-
|
|
18
|
-
from glaip_sdk.icons import ICON_AGENT
|
|
19
|
-
from glaip_sdk.rich_components import AIPPanel
|
|
20
|
-
from glaip_sdk.utils.rendering.layout.panels import create_final_panel
|
|
21
|
-
from glaip_sdk.utils.rendering.renderer.summary_window import clamp_step_nodes
|
|
22
|
-
from glaip_sdk.utils.rendering.state import RendererState
|
|
23
|
-
from glaip_sdk.utils.rendering.steps import (
|
|
24
|
-
StepManager,
|
|
25
|
-
StepPresentation,
|
|
26
|
-
build_connector_prefix,
|
|
27
|
-
format_step,
|
|
28
|
-
format_step_label,
|
|
29
|
-
humanize_tool_name,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
DEFAULT_TRANSCRIPT_THEME = "dark"
|
|
33
|
-
_NO_STEPS_TEXT = Text("No steps yet", style="dim")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass(slots=True)
|
|
37
|
-
class TranscriptGlyphs:
|
|
38
|
-
"""Glyph overrides for transcript presentation."""
|
|
39
|
-
|
|
40
|
-
branch_fill: str = "│ "
|
|
41
|
-
branch_empty: str = " "
|
|
42
|
-
branch_item: str = "├─ "
|
|
43
|
-
branch_last: str = "└─ "
|
|
44
|
-
query_prefix: str = " "
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@dataclass(slots=True)
|
|
48
|
-
class TranscriptRow:
|
|
49
|
-
"""Renderable row metadata for the transcript/summary tree."""
|
|
50
|
-
|
|
51
|
-
prefix: str
|
|
52
|
-
presentation: StepPresentation
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@dataclass(slots=True)
|
|
56
|
-
class TranscriptSnapshot:
|
|
57
|
-
"""Snapshot consumed by presenter/viewer components."""
|
|
58
|
-
|
|
59
|
-
rows: list[TranscriptRow]
|
|
60
|
-
final_panel: Any | None
|
|
61
|
-
events: list[dict[str, Any]]
|
|
62
|
-
agent_label: str | None = None
|
|
63
|
-
model_label: str | None = None
|
|
64
|
-
run_id: str | None = None
|
|
65
|
-
query_text: str | None = None
|
|
66
|
-
duration_text: str | None = None
|
|
67
|
-
window_header: Text | None = None
|
|
68
|
-
window_footer: Text | None = None
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def format_final_panel_title(state: RendererState | Mapping[str, Any], base_title: str = "Final Result") -> str:
|
|
72
|
-
"""Return the final panel title including duration if available."""
|
|
73
|
-
if isinstance(state, RendererState):
|
|
74
|
-
duration_text = state.final_duration_text
|
|
75
|
-
else:
|
|
76
|
-
duration_text = state.get("final_duration_text") if isinstance(state, Mapping) else None
|
|
77
|
-
if duration_text:
|
|
78
|
-
return f"{base_title} · {duration_text}"
|
|
79
|
-
return base_title
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def build_final_panel(
|
|
83
|
-
state: RendererState | Mapping[str, Any],
|
|
84
|
-
*,
|
|
85
|
-
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
86
|
-
title: str | None = None,
|
|
87
|
-
) -> Any | None:
|
|
88
|
-
"""Create a Rich panel for the renderer/viewer final output."""
|
|
89
|
-
if isinstance(state, RendererState):
|
|
90
|
-
body = (state.final_text or state.buffer.render() or "").strip()
|
|
91
|
-
else:
|
|
92
|
-
final_text = str(state.get("final_text", "")) if isinstance(state, Mapping) else ""
|
|
93
|
-
buffer_text = str(state.get("buffer_text", "")) if isinstance(state, Mapping) else ""
|
|
94
|
-
body = (final_text or buffer_text).strip()
|
|
95
|
-
if not body:
|
|
96
|
-
return None
|
|
97
|
-
panel_title = title or format_final_panel_title(state)
|
|
98
|
-
return create_final_panel(body, title=panel_title, theme=theme)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def build_transcript_snapshot(
|
|
102
|
-
state: RendererState | Mapping[str, Any],
|
|
103
|
-
steps: StepManager,
|
|
104
|
-
*,
|
|
105
|
-
glyphs: TranscriptGlyphs | None = None,
|
|
106
|
-
query_text: str | None = None,
|
|
107
|
-
meta: Mapping[str, Any] | Sequence[tuple[str, Any]] | None = None,
|
|
108
|
-
summary_window: int | None = None,
|
|
109
|
-
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
110
|
-
step_status_overrides: dict[str, str] | None = None,
|
|
111
|
-
) -> TranscriptSnapshot:
|
|
112
|
-
"""Compose a snapshot consumable by renderers/viewers alike."""
|
|
113
|
-
glyphs = glyphs or TranscriptGlyphs()
|
|
114
|
-
final_text, buffer_text, events, meta_payload, duration_text = _resolve_state_payload(state, meta)
|
|
115
|
-
query_value = query_text or extract_query_from_meta(meta_payload)
|
|
116
|
-
rows, window_header, window_footer = _build_rows(
|
|
117
|
-
steps,
|
|
118
|
-
glyphs,
|
|
119
|
-
meta_payload,
|
|
120
|
-
summary_window,
|
|
121
|
-
query_text=query_value,
|
|
122
|
-
step_status_overrides=step_status_overrides,
|
|
123
|
-
)
|
|
124
|
-
if not rows:
|
|
125
|
-
stored = meta_payload.get("transcript_steps")
|
|
126
|
-
if isinstance(stored, list) and stored:
|
|
127
|
-
rows, window_header, window_footer = _rows_from_stored_steps(stored)
|
|
128
|
-
panel_state: RendererState | Mapping[str, Any]
|
|
129
|
-
if isinstance(state, RendererState):
|
|
130
|
-
panel_state = state
|
|
131
|
-
else:
|
|
132
|
-
panel_state = {
|
|
133
|
-
"final_text": final_text,
|
|
134
|
-
"buffer_text": buffer_text,
|
|
135
|
-
"final_duration_text": duration_text,
|
|
136
|
-
}
|
|
137
|
-
final_panel = build_final_panel(panel_state, theme=theme)
|
|
138
|
-
|
|
139
|
-
return TranscriptSnapshot(
|
|
140
|
-
rows=rows,
|
|
141
|
-
final_panel=final_panel,
|
|
142
|
-
events=events,
|
|
143
|
-
agent_label=_friendly_agent_label(meta_payload),
|
|
144
|
-
model_label=meta_payload.get("model"),
|
|
145
|
-
run_id=meta_payload.get("run_id"),
|
|
146
|
-
query_text=query_value,
|
|
147
|
-
duration_text=duration_text,
|
|
148
|
-
window_header=window_header,
|
|
149
|
-
window_footer=window_footer,
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def build_transcript_view(
|
|
154
|
-
snapshot: TranscriptSnapshot,
|
|
155
|
-
*,
|
|
156
|
-
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
157
|
-
) -> tuple[list[Any], list[Any]]:
|
|
158
|
-
"""Return header + body renderables for a transcript snapshot."""
|
|
159
|
-
header_renderables: list[Any] = []
|
|
160
|
-
body_renderables: list[Any] = []
|
|
161
|
-
|
|
162
|
-
header_text = _compose_header_text(snapshot)
|
|
163
|
-
if header_text is not None:
|
|
164
|
-
if theme != DEFAULT_TRANSCRIPT_THEME:
|
|
165
|
-
header_text.style = "bold black"
|
|
166
|
-
header_renderables.append(header_text)
|
|
167
|
-
|
|
168
|
-
if snapshot.query_text:
|
|
169
|
-
body_renderables.append(
|
|
170
|
-
_build_query_panel(snapshot.query_text),
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
body_renderables.append(
|
|
174
|
-
_build_steps_panel(
|
|
175
|
-
snapshot.rows,
|
|
176
|
-
window_header=snapshot.window_header,
|
|
177
|
-
window_footer=snapshot.window_footer,
|
|
178
|
-
theme=theme,
|
|
179
|
-
)
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
if snapshot.final_panel is not None:
|
|
183
|
-
body_renderables.append(snapshot.final_panel)
|
|
184
|
-
|
|
185
|
-
return header_renderables, body_renderables
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def render_final_panel(
|
|
189
|
-
console: Any,
|
|
190
|
-
state: RendererState | Mapping[str, Any],
|
|
191
|
-
*,
|
|
192
|
-
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
193
|
-
title: str | None = None,
|
|
194
|
-
) -> bool:
|
|
195
|
-
"""Print the shared final panel, returning True when rendered."""
|
|
196
|
-
panel = build_final_panel(state, theme=theme, title=title)
|
|
197
|
-
if panel is None:
|
|
198
|
-
return False
|
|
199
|
-
console.print(panel)
|
|
200
|
-
console.print()
|
|
201
|
-
return True
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
__all__ = [
|
|
205
|
-
"DEFAULT_TRANSCRIPT_THEME",
|
|
206
|
-
"TranscriptGlyphs",
|
|
207
|
-
"TranscriptRow",
|
|
208
|
-
"TranscriptSnapshot",
|
|
209
|
-
"build_transcript_snapshot",
|
|
210
|
-
"build_transcript_view",
|
|
211
|
-
"render_final_panel",
|
|
212
|
-
"build_final_panel",
|
|
213
|
-
"format_final_panel_title",
|
|
214
|
-
"extract_query_from_meta",
|
|
215
|
-
]
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def _resolve_state_payload(
|
|
219
|
-
state: RendererState | Mapping[str, Any],
|
|
220
|
-
meta_override: Mapping[str, Any] | Sequence[tuple[str, Any]] | None,
|
|
221
|
-
) -> tuple[str, str, list[dict[str, Any]], dict[str, Any], str | None]:
|
|
222
|
-
if isinstance(state, RendererState):
|
|
223
|
-
final_text = state.final_text
|
|
224
|
-
buffer_text = state.buffer.render()
|
|
225
|
-
events = list(state.events)
|
|
226
|
-
meta_payload = normalise_meta_payload(meta_override or getattr(state, "meta", None))
|
|
227
|
-
duration_text = state.final_duration_text
|
|
228
|
-
else:
|
|
229
|
-
mapping_state = dict(state) if isinstance(state, Mapping) else {}
|
|
230
|
-
final_text = str(mapping_state.get("final_text") or "")
|
|
231
|
-
buffer_text = str(mapping_state.get("buffer_text") or "")
|
|
232
|
-
events = list(mapping_state.get("events") or [])
|
|
233
|
-
base_meta = mapping_state.get("meta")
|
|
234
|
-
meta_payload = normalise_meta_payload(meta_override or base_meta)
|
|
235
|
-
duration_text = mapping_state.get("final_duration_text")
|
|
236
|
-
return final_text, buffer_text, events, meta_payload, duration_text
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def _build_rows(
|
|
240
|
-
steps: StepManager,
|
|
241
|
-
glyphs: TranscriptGlyphs,
|
|
242
|
-
meta_payload: dict[str, Any],
|
|
243
|
-
summary_window: int | None,
|
|
244
|
-
*,
|
|
245
|
-
query_text: str | None,
|
|
246
|
-
step_status_overrides: dict[str, str] | None = None,
|
|
247
|
-
) -> tuple[list[TranscriptRow], Text | None, Text | None]:
|
|
248
|
-
nodes = list(steps.iter_tree())
|
|
249
|
-
header_notice: Text | None = None
|
|
250
|
-
footer_notice: Text | None = None
|
|
251
|
-
if summary_window is not None and summary_window > 0:
|
|
252
|
-
nodes, header_notice, footer_notice = _apply_summary_window(steps, nodes, summary_window)
|
|
253
|
-
|
|
254
|
-
rows: list[TranscriptRow] = []
|
|
255
|
-
for index, (step_id, branch_state) in enumerate(nodes):
|
|
256
|
-
row = _create_step_row(
|
|
257
|
-
steps,
|
|
258
|
-
glyphs,
|
|
259
|
-
meta_payload,
|
|
260
|
-
step_id,
|
|
261
|
-
branch_state,
|
|
262
|
-
step_status_overrides=step_status_overrides,
|
|
263
|
-
)
|
|
264
|
-
if row is not None:
|
|
265
|
-
rows.append(row)
|
|
266
|
-
if index == 0:
|
|
267
|
-
query_row = _create_query_hint_row(glyphs, query_text)
|
|
268
|
-
if query_row is not None:
|
|
269
|
-
rows.append(query_row)
|
|
270
|
-
|
|
271
|
-
return rows, header_notice, footer_notice
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def _apply_summary_window(
|
|
275
|
-
steps: StepManager,
|
|
276
|
-
nodes: list[tuple[str, tuple[bool, ...]]],
|
|
277
|
-
summary_window: int,
|
|
278
|
-
) -> tuple[list[tuple[str, tuple[bool, ...]]], Text | None, Text | None]:
|
|
279
|
-
"""Apply summary window clamping to step nodes."""
|
|
280
|
-
|
|
281
|
-
def _get_label(step_id: str) -> str:
|
|
282
|
-
step = steps.by_id.get(step_id)
|
|
283
|
-
return format_step_label(step) if step else ""
|
|
284
|
-
|
|
285
|
-
def _get_parent(step_id: str) -> str | None:
|
|
286
|
-
step = steps.by_id.get(step_id)
|
|
287
|
-
return step.parent_id if step else None
|
|
288
|
-
|
|
289
|
-
return clamp_step_nodes(
|
|
290
|
-
nodes,
|
|
291
|
-
window=summary_window,
|
|
292
|
-
get_label=_get_label,
|
|
293
|
-
get_parent=_get_parent,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
def _create_step_row(
|
|
298
|
-
steps: StepManager,
|
|
299
|
-
glyphs: TranscriptGlyphs,
|
|
300
|
-
meta_payload: dict[str, Any],
|
|
301
|
-
step_id: str,
|
|
302
|
-
branch_state: tuple[bool, ...],
|
|
303
|
-
*,
|
|
304
|
-
step_status_overrides: dict[str, str] | None = None,
|
|
305
|
-
) -> TranscriptRow | None:
|
|
306
|
-
"""Create a transcript row from a step."""
|
|
307
|
-
step = steps.by_id.get(step_id)
|
|
308
|
-
if not step:
|
|
309
|
-
return None
|
|
310
|
-
override = None
|
|
311
|
-
if not branch_state and _should_override_root_label(meta_payload):
|
|
312
|
-
override = _friendly_root_label(meta_payload, step, getattr(step, "display_label", None))
|
|
313
|
-
presentation = format_step(step, glyphs=glyphs, label=override)
|
|
314
|
-
if step_status_overrides:
|
|
315
|
-
status_text = step_status_overrides.get(step_id)
|
|
316
|
-
if status_text:
|
|
317
|
-
presentation.status_text = status_text
|
|
318
|
-
prefix = build_connector_prefix(branch_state)
|
|
319
|
-
return TranscriptRow(prefix=prefix, presentation=presentation)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
def _create_query_hint_row(glyphs: TranscriptGlyphs, query_text: str | None) -> TranscriptRow | None:
|
|
323
|
-
"""Create a query hint row mirroring the documented transcript output."""
|
|
324
|
-
if not query_text:
|
|
325
|
-
return None
|
|
326
|
-
return TranscriptRow(
|
|
327
|
-
prefix=glyphs.query_prefix,
|
|
328
|
-
presentation=StepPresentation(
|
|
329
|
-
step_id="query",
|
|
330
|
-
title=query_text,
|
|
331
|
-
glyph=None,
|
|
332
|
-
status_style=None,
|
|
333
|
-
args_text=None,
|
|
334
|
-
failure_reason=None,
|
|
335
|
-
duration_ms=None,
|
|
336
|
-
),
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def _rows_from_stored_steps(entries: list[dict[str, Any]]) -> tuple[list[TranscriptRow], Text | None, Text | None]:
|
|
341
|
-
rows: list[TranscriptRow] = []
|
|
342
|
-
for index, entry in enumerate(entries):
|
|
343
|
-
title = entry.get("display_name") or entry.get("name") or "Step"
|
|
344
|
-
finished = entry.get("status") == "finished"
|
|
345
|
-
duration_ms = entry.get("duration_ms")
|
|
346
|
-
presentation = StepPresentation(
|
|
347
|
-
step_id=f"stored-{index}",
|
|
348
|
-
title=str(title),
|
|
349
|
-
glyph="✓" if finished else None,
|
|
350
|
-
status_style="green" if finished else None,
|
|
351
|
-
args_text=None,
|
|
352
|
-
failure_reason=None,
|
|
353
|
-
duration_ms=duration_ms,
|
|
354
|
-
)
|
|
355
|
-
rows.append(TranscriptRow(prefix=" ", presentation=presentation))
|
|
356
|
-
return rows, None, None
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
def _compose_header_text(snapshot: TranscriptSnapshot) -> Text | None:
|
|
360
|
-
parts: list[str] = []
|
|
361
|
-
if snapshot.agent_label:
|
|
362
|
-
parts.append(snapshot.agent_label)
|
|
363
|
-
if snapshot.model_label:
|
|
364
|
-
parts.append(snapshot.model_label)
|
|
365
|
-
if snapshot.duration_text:
|
|
366
|
-
parts.append(snapshot.duration_text)
|
|
367
|
-
if snapshot.run_id:
|
|
368
|
-
parts.append(snapshot.run_id)
|
|
369
|
-
if not parts:
|
|
370
|
-
return None
|
|
371
|
-
return Text(" · ".join(parts), style="bold")
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def _build_query_panel(query_text: str) -> AIPPanel:
|
|
375
|
-
"""Build a query panel."""
|
|
376
|
-
return AIPPanel(
|
|
377
|
-
Markdown(f"**Query:** {query_text.strip()}"),
|
|
378
|
-
title="User Request",
|
|
379
|
-
border_style="#d97706",
|
|
380
|
-
padding=(0, 1),
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
def _build_steps_panel(
|
|
385
|
-
rows: Sequence[TranscriptRow],
|
|
386
|
-
*,
|
|
387
|
-
window_header: Text | None = None,
|
|
388
|
-
window_footer: Text | None = None,
|
|
389
|
-
theme: str = DEFAULT_TRANSCRIPT_THEME,
|
|
390
|
-
) -> AIPPanel:
|
|
391
|
-
if not rows:
|
|
392
|
-
steps_body: Text | Group = _NO_STEPS_TEXT.copy()
|
|
393
|
-
else:
|
|
394
|
-
rendered = [_format_row_text(row) for row in rows]
|
|
395
|
-
style = "dim" if theme == DEFAULT_TRANSCRIPT_THEME else "default"
|
|
396
|
-
steps_body = Text("\n".join(rendered), style=style)
|
|
397
|
-
|
|
398
|
-
renderables: list[Any] = []
|
|
399
|
-
if window_header is not None:
|
|
400
|
-
renderables.append(window_header)
|
|
401
|
-
renderables.append(steps_body)
|
|
402
|
-
if window_footer is not None:
|
|
403
|
-
renderables.append(window_footer)
|
|
404
|
-
|
|
405
|
-
if len(renderables) == 1:
|
|
406
|
-
body: Any = renderables[0]
|
|
407
|
-
else:
|
|
408
|
-
body = Group(*renderables)
|
|
409
|
-
|
|
410
|
-
return AIPPanel(body, title="Steps", border_style="blue")
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
def _format_row_text(row: TranscriptRow) -> str:
|
|
414
|
-
prefix = row.prefix
|
|
415
|
-
title, summary = _split_label(row.presentation.title)
|
|
416
|
-
line = f"{prefix}{title}".rstrip()
|
|
417
|
-
|
|
418
|
-
args_lines = _extract_args_lines(row)
|
|
419
|
-
has_args = bool(args_lines)
|
|
420
|
-
|
|
421
|
-
if summary:
|
|
422
|
-
line += f" — {_truncate_summary(summary)}"
|
|
423
|
-
elif has_args:
|
|
424
|
-
line += " —"
|
|
425
|
-
|
|
426
|
-
badge = _format_duration_badge(row.presentation.duration_ms)
|
|
427
|
-
status_text = row.presentation.status_text
|
|
428
|
-
if status_text:
|
|
429
|
-
line += f" {status_text}"
|
|
430
|
-
elif badge:
|
|
431
|
-
line += f" {badge}"
|
|
432
|
-
|
|
433
|
-
if row.presentation.glyph:
|
|
434
|
-
line += f" {row.presentation.glyph}"
|
|
435
|
-
|
|
436
|
-
if row.presentation.failure_reason:
|
|
437
|
-
line += f" {row.presentation.failure_reason}"
|
|
438
|
-
|
|
439
|
-
if has_args:
|
|
440
|
-
for args_line in args_lines:
|
|
441
|
-
line += f"\n{prefix} {args_line}"
|
|
442
|
-
|
|
443
|
-
return line
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
def _format_duration_badge(duration_ms: int | None) -> str | None:
|
|
447
|
-
if duration_ms is None:
|
|
448
|
-
return None
|
|
449
|
-
try:
|
|
450
|
-
duration_ms = int(duration_ms)
|
|
451
|
-
except Exception:
|
|
452
|
-
return None
|
|
453
|
-
if duration_ms <= 0:
|
|
454
|
-
value = "<1ms"
|
|
455
|
-
elif duration_ms < 1000:
|
|
456
|
-
value = f"{duration_ms}ms"
|
|
457
|
-
else:
|
|
458
|
-
seconds = duration_ms / 1000
|
|
459
|
-
value = f"{seconds:.2f}s"
|
|
460
|
-
return f"[{value}]"
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
def _extract_args_lines(row: TranscriptRow) -> list[str]:
|
|
464
|
-
args_text = row.presentation.args_text
|
|
465
|
-
if _should_skip_args_summary(row, args_text):
|
|
466
|
-
return []
|
|
467
|
-
|
|
468
|
-
parsed = _parse_args_payload(args_text or "")
|
|
469
|
-
title = row.presentation.title or ""
|
|
470
|
-
|
|
471
|
-
if isinstance(parsed, dict) and parsed:
|
|
472
|
-
if title.startswith(ICON_AGENT) and set(parsed.keys()) == {"query"}:
|
|
473
|
-
return [str(parsed["query"])]
|
|
474
|
-
return [f"{key}: {json.dumps(value, ensure_ascii=False)}" for key, value in parsed.items()]
|
|
475
|
-
|
|
476
|
-
if isinstance(parsed, list):
|
|
477
|
-
return [json.dumps(parsed, ensure_ascii=False)]
|
|
478
|
-
|
|
479
|
-
return [args_text or ""]
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
def _should_skip_args_summary(row: TranscriptRow, args_text: str | None) -> bool:
|
|
483
|
-
if not args_text or args_text == "{}":
|
|
484
|
-
return True
|
|
485
|
-
title = (row.presentation.title or "").strip()
|
|
486
|
-
if title.startswith("💭 Thinking…"):
|
|
487
|
-
return True
|
|
488
|
-
if not row.prefix.strip():
|
|
489
|
-
return True
|
|
490
|
-
if args_text.strip() == '{"reason":"deterministic_timeline"}':
|
|
491
|
-
return True
|
|
492
|
-
return False
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def _parse_args_payload(args_text: str) -> Any | None:
|
|
496
|
-
stripped = args_text.lstrip()
|
|
497
|
-
if stripped.startswith("{") or stripped.startswith("["):
|
|
498
|
-
try:
|
|
499
|
-
return json.loads(args_text)
|
|
500
|
-
except Exception:
|
|
501
|
-
return None
|
|
502
|
-
return None
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
def _split_label(label: Any) -> tuple[str, str | None]:
|
|
506
|
-
if not isinstance(label, str):
|
|
507
|
-
try:
|
|
508
|
-
label = str(label)
|
|
509
|
-
except Exception:
|
|
510
|
-
return "Step", None
|
|
511
|
-
if "—" not in label:
|
|
512
|
-
return label, None
|
|
513
|
-
title, summary = label.split("—", 1)
|
|
514
|
-
return title.strip(), summary.strip()
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
def _truncate_summary(summary: str, limit: int = 80) -> str:
|
|
518
|
-
if len(summary) <= limit:
|
|
519
|
-
return summary
|
|
520
|
-
return summary[: limit - 1] + "…"
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
def _friendly_agent_label(meta: Mapping[str, Any]) -> str | None:
|
|
524
|
-
"""Return a user-facing agent label for headers."""
|
|
525
|
-
raw_name = _string_or_none(meta.get("agent_name"))
|
|
526
|
-
if raw_name:
|
|
527
|
-
friendly = humanize_tool_name(raw_name)
|
|
528
|
-
if friendly:
|
|
529
|
-
return friendly
|
|
530
|
-
return _string_or_none(meta.get("agent_id"))
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
def _friendly_root_label(meta: dict[str, Any], step: Any, fallback: str | None) -> str:
|
|
534
|
-
fallback_label = _string_or_none(fallback)
|
|
535
|
-
raw_agent_name = _string_or_none(meta.get("agent_name"))
|
|
536
|
-
agent_name = humanize_tool_name(raw_agent_name) if raw_agent_name else None
|
|
537
|
-
if agent_name:
|
|
538
|
-
agent_name = agent_name.title()
|
|
539
|
-
agent_name = agent_name or fallback_label
|
|
540
|
-
agent_id = _string_or_none(meta.get("agent_id") or getattr(step, "name", ""))
|
|
541
|
-
|
|
542
|
-
if not agent_name:
|
|
543
|
-
return fallback_label or agent_id or ICON_AGENT
|
|
544
|
-
|
|
545
|
-
parts = [ICON_AGENT, agent_name]
|
|
546
|
-
if agent_id and agent_id != agent_name:
|
|
547
|
-
parts.append(f"({agent_id})")
|
|
548
|
-
return " ".join(parts)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
def extract_query_from_meta(meta: Mapping[str, Any] | None) -> str | None:
|
|
552
|
-
"""Return the canonical query string embedded in renderer metadata."""
|
|
553
|
-
if not meta:
|
|
554
|
-
return None
|
|
555
|
-
|
|
556
|
-
payload = dict(meta)
|
|
557
|
-
nested_meta = payload.get("meta") or {}
|
|
558
|
-
candidate = (
|
|
559
|
-
payload.get("input_message")
|
|
560
|
-
or payload.get("query")
|
|
561
|
-
or payload.get("message")
|
|
562
|
-
or nested_meta.get("input_message")
|
|
563
|
-
)
|
|
564
|
-
if isinstance(candidate, str):
|
|
565
|
-
candidate = candidate.strip()
|
|
566
|
-
return candidate or None
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
def _should_override_root_label(meta: Mapping[str, Any] | None) -> bool:
|
|
570
|
-
if not meta:
|
|
571
|
-
return False
|
|
572
|
-
if meta.get("agent_name") or meta.get("agent_id"):
|
|
573
|
-
return True
|
|
574
|
-
return False
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
def _string_or_none(value: Any) -> str | None:
|
|
578
|
-
if value is None:
|
|
579
|
-
return None
|
|
580
|
-
try:
|
|
581
|
-
text = str(value).strip()
|
|
582
|
-
except Exception:
|
|
583
|
-
return None
|
|
584
|
-
return text or None
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
def normalise_meta_payload(meta: Any) -> dict[str, Any]:
|
|
588
|
-
"""Return a defensive dictionary for arbitrary metadata payloads."""
|
|
589
|
-
if not meta:
|
|
590
|
-
return {}
|
|
591
|
-
if isinstance(meta, dict):
|
|
592
|
-
return dict(meta)
|
|
593
|
-
if isinstance(meta, Mapping):
|
|
594
|
-
try:
|
|
595
|
-
return dict(meta)
|
|
596
|
-
except Exception:
|
|
597
|
-
return {}
|
|
598
|
-
if isinstance(meta, Sequence) and not isinstance(meta, (str, bytes)):
|
|
599
|
-
try:
|
|
600
|
-
return dict(meta)
|
|
601
|
-
except Exception:
|
|
602
|
-
return {}
|
|
603
|
-
try:
|
|
604
|
-
return dict(meta)
|
|
605
|
-
except Exception:
|
|
606
|
-
return {}
|