glaip-sdk 0.6.11__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.11.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.11.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.11.dist-info/RECORD +0 -159
- glaip_sdk-0.6.11.dist-info/entry_points.txt +0 -3
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
"""Tool panel controller logic extracted from the renderer.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
from time import monotonic
|
|
11
|
-
from typing import Any, TYPE_CHECKING
|
|
12
|
-
|
|
13
|
-
from rich.console import Console
|
|
14
|
-
|
|
15
|
-
from glaip_sdk.utils.rendering.layout.panels import create_tool_panel
|
|
16
|
-
from glaip_sdk.utils.rendering.layout.progress import format_tool_title, is_delegation_tool
|
|
17
|
-
from glaip_sdk.utils.rendering.layout.transcript import DEFAULT_TRANSCRIPT_THEME
|
|
18
|
-
from glaip_sdk.utils.rendering.models import Step
|
|
19
|
-
from glaip_sdk.utils.rendering.renderer.config import RendererConfig
|
|
20
|
-
from glaip_sdk.utils.rendering.steps import StepManager
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING: # pragma: no cover - typing helper
|
|
23
|
-
from glaip_sdk.utils.rendering.renderer.stream import StreamProcessor
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ToolPanelController:
|
|
27
|
-
"""Encapsulates tool panel lifecycle management."""
|
|
28
|
-
|
|
29
|
-
OUTPUT_PREFIX = "**Output:**\n"
|
|
30
|
-
|
|
31
|
-
def __init__(
|
|
32
|
-
self,
|
|
33
|
-
*,
|
|
34
|
-
steps: StepManager,
|
|
35
|
-
stream_processor: StreamProcessor,
|
|
36
|
-
console: Console,
|
|
37
|
-
cfg: RendererConfig,
|
|
38
|
-
step_server_start_times: dict[str, float],
|
|
39
|
-
output_prefix: str | None = None,
|
|
40
|
-
) -> None:
|
|
41
|
-
"""Initialize the tool panel controller.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
steps: Step manager instance for tracking steps
|
|
45
|
-
stream_processor: Stream processor for handling events
|
|
46
|
-
console: Rich console instance for rendering
|
|
47
|
-
cfg: Renderer configuration
|
|
48
|
-
step_server_start_times: Dictionary mapping step IDs to server start times
|
|
49
|
-
output_prefix: Optional prefix for tool output (defaults to OUTPUT_PREFIX)
|
|
50
|
-
"""
|
|
51
|
-
self._steps = steps
|
|
52
|
-
self._stream_processor = stream_processor
|
|
53
|
-
self._console = console
|
|
54
|
-
self._cfg = cfg
|
|
55
|
-
self._step_server_start_times = step_server_start_times
|
|
56
|
-
self.panels: dict[str, dict[str, Any]] = {}
|
|
57
|
-
self._panel_output_prefix = output_prefix or self.OUTPUT_PREFIX
|
|
58
|
-
|
|
59
|
-
# Public API -------------------------------------------------------
|
|
60
|
-
def update_console(self, console: Console) -> None:
|
|
61
|
-
"""Update the console reference used for snapshot printing."""
|
|
62
|
-
self._console = console
|
|
63
|
-
|
|
64
|
-
def update_config(self, cfg: RendererConfig) -> None:
|
|
65
|
-
"""Update configuration reference (useful when overrides occur)."""
|
|
66
|
-
self._cfg = cfg
|
|
67
|
-
|
|
68
|
-
def finish_all_panels(self) -> None:
|
|
69
|
-
"""Mark all panels as finished (used during cleanup/finalization)."""
|
|
70
|
-
try:
|
|
71
|
-
items = list(self.panels.items())
|
|
72
|
-
except Exception: # pragma: no cover - defensive guard
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
for _sid, meta in items:
|
|
76
|
-
if meta.get("status") != "finished":
|
|
77
|
-
meta["status"] = "finished"
|
|
78
|
-
|
|
79
|
-
def handle_agent_step(
|
|
80
|
-
self,
|
|
81
|
-
event: dict[str, Any],
|
|
82
|
-
tool_name: str | None,
|
|
83
|
-
tool_args: Any,
|
|
84
|
-
_tool_out: Any,
|
|
85
|
-
tool_calls_info: list[tuple[str, Any, Any]],
|
|
86
|
-
*,
|
|
87
|
-
tracked_step: Step | None = None,
|
|
88
|
-
) -> None:
|
|
89
|
-
"""Handle agent step tool bookkeeping."""
|
|
90
|
-
metadata = event.get("metadata", {})
|
|
91
|
-
task_id = event.get("task_id") or metadata.get("task_id")
|
|
92
|
-
context_id = event.get("context_id") or metadata.get("context_id")
|
|
93
|
-
content = event.get("content", "")
|
|
94
|
-
|
|
95
|
-
if tool_name:
|
|
96
|
-
tool_sid = self._ensure_tool_panel(tool_name, tool_args, task_id, context_id)
|
|
97
|
-
self._start_tool_step(
|
|
98
|
-
task_id,
|
|
99
|
-
context_id,
|
|
100
|
-
tool_name,
|
|
101
|
-
tool_args,
|
|
102
|
-
tool_sid,
|
|
103
|
-
tracked_step=tracked_step,
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
self._process_additional_tool_calls(tool_calls_info, tool_name, task_id, context_id)
|
|
107
|
-
|
|
108
|
-
(
|
|
109
|
-
is_tool_finished,
|
|
110
|
-
finished_tool_name,
|
|
111
|
-
finished_tool_output,
|
|
112
|
-
) = self._detect_tool_completion(metadata, content)
|
|
113
|
-
|
|
114
|
-
if not (is_tool_finished and finished_tool_name):
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
self._finish_tool_panel(finished_tool_name, finished_tool_output, task_id, context_id)
|
|
118
|
-
self._finish_tool_step(
|
|
119
|
-
finished_tool_name,
|
|
120
|
-
finished_tool_output,
|
|
121
|
-
task_id,
|
|
122
|
-
context_id,
|
|
123
|
-
tracked_step=tracked_step,
|
|
124
|
-
)
|
|
125
|
-
self._create_tool_snapshot(finished_tool_name, task_id, context_id)
|
|
126
|
-
|
|
127
|
-
# Internal helpers -------------------------------------------------
|
|
128
|
-
def _ensure_tool_panel(self, name: str, args: Any, task_id: str, context_id: str) -> str:
|
|
129
|
-
formatted_title = format_tool_title(name)
|
|
130
|
-
is_delegation = is_delegation_tool(name)
|
|
131
|
-
tool_sid = self._session_id(name, task_id, context_id)
|
|
132
|
-
|
|
133
|
-
if tool_sid not in self.panels:
|
|
134
|
-
self.panels[tool_sid] = {
|
|
135
|
-
"title": formatted_title,
|
|
136
|
-
"status": "running",
|
|
137
|
-
"started_at": monotonic(),
|
|
138
|
-
"server_started_at": self._stream_processor.server_elapsed_time,
|
|
139
|
-
"chunks": [],
|
|
140
|
-
"args": args or {},
|
|
141
|
-
"output": None,
|
|
142
|
-
"is_delegation": is_delegation,
|
|
143
|
-
}
|
|
144
|
-
if args:
|
|
145
|
-
try:
|
|
146
|
-
args_content = "**Args:**\n```json\n" + json.dumps(args, indent=2) + "\n```\n\n"
|
|
147
|
-
except Exception:
|
|
148
|
-
args_content = f"**Args:**\n{args}\n\n"
|
|
149
|
-
self.panels[tool_sid]["chunks"].append(args_content)
|
|
150
|
-
|
|
151
|
-
return tool_sid
|
|
152
|
-
|
|
153
|
-
def _start_tool_step(
|
|
154
|
-
self,
|
|
155
|
-
task_id: str,
|
|
156
|
-
context_id: str,
|
|
157
|
-
tool_name: str,
|
|
158
|
-
tool_args: Any,
|
|
159
|
-
_tool_sid: str,
|
|
160
|
-
*,
|
|
161
|
-
tracked_step: Step | None = None,
|
|
162
|
-
) -> Step | None:
|
|
163
|
-
if tracked_step is not None:
|
|
164
|
-
return tracked_step
|
|
165
|
-
|
|
166
|
-
if is_delegation_tool(tool_name):
|
|
167
|
-
step = self._steps.start_or_get(
|
|
168
|
-
task_id=task_id,
|
|
169
|
-
context_id=context_id,
|
|
170
|
-
kind="delegate",
|
|
171
|
-
name=tool_name,
|
|
172
|
-
args=tool_args,
|
|
173
|
-
)
|
|
174
|
-
else:
|
|
175
|
-
step = self._steps.start_or_get(
|
|
176
|
-
task_id=task_id,
|
|
177
|
-
context_id=context_id,
|
|
178
|
-
kind="tool",
|
|
179
|
-
name=tool_name,
|
|
180
|
-
args=tool_args,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if step and self._stream_processor.server_elapsed_time is not None:
|
|
184
|
-
self._step_server_start_times[step.step_id] = self._stream_processor.server_elapsed_time
|
|
185
|
-
|
|
186
|
-
return step
|
|
187
|
-
|
|
188
|
-
def _process_additional_tool_calls(
|
|
189
|
-
self,
|
|
190
|
-
tool_calls_info: list[tuple[str, Any, Any]],
|
|
191
|
-
tool_name: str | None,
|
|
192
|
-
task_id: str,
|
|
193
|
-
context_id: str,
|
|
194
|
-
) -> None:
|
|
195
|
-
for call_name, call_args, _ in tool_calls_info or []:
|
|
196
|
-
if call_name and call_name != tool_name:
|
|
197
|
-
self._process_single_tool_call(call_name, call_args, task_id, context_id)
|
|
198
|
-
|
|
199
|
-
def _process_single_tool_call(self, call_name: str, call_args: Any, task_id: str, context_id: str) -> None:
|
|
200
|
-
self._ensure_tool_panel(call_name, call_args, task_id, context_id)
|
|
201
|
-
step = self._create_step_for_tool_call(call_name, call_args, task_id, context_id)
|
|
202
|
-
if step and self._stream_processor.server_elapsed_time is not None:
|
|
203
|
-
self._step_server_start_times[step.step_id] = self._stream_processor.server_elapsed_time
|
|
204
|
-
|
|
205
|
-
def _create_step_for_tool_call(self, call_name: str, call_args: Any, task_id: str, context_id: str) -> Any:
|
|
206
|
-
if is_delegation_tool(call_name):
|
|
207
|
-
return self._steps.start_or_get(
|
|
208
|
-
task_id=task_id,
|
|
209
|
-
context_id=context_id,
|
|
210
|
-
kind="delegate",
|
|
211
|
-
name=call_name,
|
|
212
|
-
args=call_args,
|
|
213
|
-
)
|
|
214
|
-
return self._steps.start_or_get(
|
|
215
|
-
task_id=task_id,
|
|
216
|
-
context_id=context_id,
|
|
217
|
-
kind="tool",
|
|
218
|
-
name=call_name,
|
|
219
|
-
args=call_args,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
def _detect_tool_completion(self, metadata: dict[str, Any], content: str) -> tuple[bool, str | None, Any]:
|
|
223
|
-
tool_info = metadata.get("tool_info", {}) if isinstance(metadata, dict) else {}
|
|
224
|
-
|
|
225
|
-
if tool_info.get("status") == "finished" and tool_info.get("name"):
|
|
226
|
-
return True, tool_info.get("name"), tool_info.get("output")
|
|
227
|
-
if content and isinstance(content, str) and content.startswith("Completed "):
|
|
228
|
-
tool_name = content.replace("Completed ", "").strip()
|
|
229
|
-
if tool_name:
|
|
230
|
-
output = tool_info.get("output") if tool_info.get("name") == tool_name else None
|
|
231
|
-
return True, tool_name, output
|
|
232
|
-
if metadata.get("status") == "finished" and tool_info.get("name"):
|
|
233
|
-
return True, tool_info.get("name"), tool_info.get("output")
|
|
234
|
-
return False, None, None
|
|
235
|
-
|
|
236
|
-
def _finish_tool_panel(
|
|
237
|
-
self,
|
|
238
|
-
finished_tool_name: str,
|
|
239
|
-
finished_tool_output: Any,
|
|
240
|
-
task_id: str,
|
|
241
|
-
context_id: str,
|
|
242
|
-
) -> None:
|
|
243
|
-
tool_sid = self._session_id(finished_tool_name, task_id, context_id)
|
|
244
|
-
if tool_sid not in self.panels:
|
|
245
|
-
return
|
|
246
|
-
|
|
247
|
-
meta = self.panels[tool_sid]
|
|
248
|
-
self._mark_panel_as_finished(meta, tool_sid)
|
|
249
|
-
self._add_tool_output_to_panel(meta, finished_tool_output, finished_tool_name)
|
|
250
|
-
|
|
251
|
-
def _finish_tool_step(
|
|
252
|
-
self,
|
|
253
|
-
finished_tool_name: str,
|
|
254
|
-
finished_tool_output: Any,
|
|
255
|
-
task_id: str,
|
|
256
|
-
context_id: str,
|
|
257
|
-
*,
|
|
258
|
-
tracked_step: Step | None = None,
|
|
259
|
-
) -> None:
|
|
260
|
-
if tracked_step is not None:
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
duration = self._get_step_duration(finished_tool_name, task_id, context_id)
|
|
264
|
-
if is_delegation_tool(finished_tool_name):
|
|
265
|
-
self._steps.finish(
|
|
266
|
-
task_id=task_id,
|
|
267
|
-
context_id=context_id,
|
|
268
|
-
kind="delegate",
|
|
269
|
-
name=finished_tool_name,
|
|
270
|
-
output=finished_tool_output,
|
|
271
|
-
duration_raw=duration,
|
|
272
|
-
)
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
self._steps.finish(
|
|
276
|
-
task_id=task_id,
|
|
277
|
-
context_id=context_id,
|
|
278
|
-
kind="tool",
|
|
279
|
-
name=finished_tool_name,
|
|
280
|
-
output=finished_tool_output,
|
|
281
|
-
duration_raw=duration,
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
def _mark_panel_as_finished(self, meta: dict[str, Any], tool_sid: str) -> None:
|
|
285
|
-
if meta.get("status") != "finished":
|
|
286
|
-
meta["status"] = "finished"
|
|
287
|
-
dur = self._calculate_tool_duration(meta)
|
|
288
|
-
self._update_tool_metadata(meta, dur)
|
|
289
|
-
self._stream_processor.current_event_finished_panels.add(tool_sid)
|
|
290
|
-
|
|
291
|
-
def _calculate_tool_duration(self, meta: dict[str, Any]) -> float | None:
|
|
292
|
-
started_at = meta.get("server_started_at")
|
|
293
|
-
finished_at = (
|
|
294
|
-
self._stream_processor.server_elapsed_time
|
|
295
|
-
if isinstance(self._stream_processor.server_elapsed_time, (int, float))
|
|
296
|
-
else None
|
|
297
|
-
)
|
|
298
|
-
if not isinstance(started_at, (int, float)) or finished_at is None:
|
|
299
|
-
started_at = meta.get("started_at")
|
|
300
|
-
finished_at = meta.get("finished_at")
|
|
301
|
-
try:
|
|
302
|
-
if isinstance(started_at, (int, float)) and isinstance(finished_at, (int, float)):
|
|
303
|
-
return max(0.0, float(finished_at) - float(started_at))
|
|
304
|
-
except Exception:
|
|
305
|
-
return None
|
|
306
|
-
return None
|
|
307
|
-
|
|
308
|
-
def _update_tool_metadata(self, meta: dict[str, Any], dur: float | None) -> None:
|
|
309
|
-
if dur is not None:
|
|
310
|
-
meta["duration_seconds"] = dur
|
|
311
|
-
meta["server_finished_at"] = (
|
|
312
|
-
self._stream_processor.server_elapsed_time
|
|
313
|
-
if isinstance(self._stream_processor.server_elapsed_time, (int, float))
|
|
314
|
-
else None
|
|
315
|
-
)
|
|
316
|
-
meta["finished_at"] = monotonic()
|
|
317
|
-
|
|
318
|
-
def _add_tool_output_to_panel(
|
|
319
|
-
self, meta: dict[str, Any], finished_tool_output: Any, finished_tool_name: str
|
|
320
|
-
) -> None:
|
|
321
|
-
if finished_tool_output is None:
|
|
322
|
-
return
|
|
323
|
-
meta.setdefault("chunks", []).append(self._format_output_block(finished_tool_output, finished_tool_name))
|
|
324
|
-
meta["output"] = finished_tool_output
|
|
325
|
-
|
|
326
|
-
def _get_step_duration(self, finished_tool_name: str, task_id: str, context_id: str) -> float | None:
|
|
327
|
-
tool_sid = self._session_id(finished_tool_name, task_id, context_id)
|
|
328
|
-
return self.panels.get(tool_sid, {}).get("duration_seconds")
|
|
329
|
-
|
|
330
|
-
def _should_create_snapshot(self, tool_sid: str) -> bool:
|
|
331
|
-
return self._cfg.append_finished_snapshots and not self.panels.get(tool_sid, {}).get("snapshot_printed")
|
|
332
|
-
|
|
333
|
-
def _create_tool_snapshot(self, finished_tool_name: str, task_id: str, context_id: str) -> None:
|
|
334
|
-
tool_sid = self._session_id(finished_tool_name, task_id, context_id)
|
|
335
|
-
if not self._should_create_snapshot(tool_sid):
|
|
336
|
-
return
|
|
337
|
-
|
|
338
|
-
meta = self.panels[tool_sid]
|
|
339
|
-
adjusted_title = self._get_snapshot_title(meta, finished_tool_name)
|
|
340
|
-
body_text = "".join(meta.get("chunks") or [])
|
|
341
|
-
body_text = self._clamp_snapshot_body(body_text)
|
|
342
|
-
|
|
343
|
-
snapshot_panel = create_tool_panel(
|
|
344
|
-
title=adjusted_title,
|
|
345
|
-
content=body_text or "(no output)",
|
|
346
|
-
status="finished",
|
|
347
|
-
theme=self._panel_theme(),
|
|
348
|
-
is_delegation=is_delegation_tool(finished_tool_name),
|
|
349
|
-
)
|
|
350
|
-
self._console.print(snapshot_panel)
|
|
351
|
-
self.panels[tool_sid]["snapshot_printed"] = True
|
|
352
|
-
|
|
353
|
-
def _get_snapshot_title(self, meta: dict[str, Any], finished_tool_name: str) -> str:
|
|
354
|
-
adjusted_title = meta.get("title") or finished_tool_name
|
|
355
|
-
dur = meta.get("duration_seconds")
|
|
356
|
-
if isinstance(dur, (int, float)):
|
|
357
|
-
elapsed = self._format_snapshot_duration(dur)
|
|
358
|
-
adjusted_title = f"{adjusted_title} · {elapsed}"
|
|
359
|
-
return adjusted_title
|
|
360
|
-
|
|
361
|
-
def _format_snapshot_duration(self, dur: int | float) -> str:
|
|
362
|
-
try:
|
|
363
|
-
if not isinstance(dur, (int, float)):
|
|
364
|
-
return "<1ms"
|
|
365
|
-
if dur >= 1:
|
|
366
|
-
return f"{dur:.2f}s"
|
|
367
|
-
if int(dur * 1000) > 0:
|
|
368
|
-
return f"{int(dur * 1000)}ms"
|
|
369
|
-
return "<1ms"
|
|
370
|
-
except (TypeError, ValueError, OverflowError):
|
|
371
|
-
return "<1ms"
|
|
372
|
-
|
|
373
|
-
def _clamp_snapshot_body(self, body_text: str) -> str:
|
|
374
|
-
max_lines = int(self._cfg.snapshot_max_lines or 0)
|
|
375
|
-
lines = body_text.splitlines()
|
|
376
|
-
if max_lines > 0 and len(lines) > max_lines:
|
|
377
|
-
lines = lines[:max_lines] + ["… (truncated)"]
|
|
378
|
-
body_text = "\n".join(lines)
|
|
379
|
-
|
|
380
|
-
max_chars = int(self._cfg.snapshot_max_chars or 0)
|
|
381
|
-
if max_chars > 0 and len(body_text) > max_chars:
|
|
382
|
-
suffix = "\n… (truncated)"
|
|
383
|
-
body_text = body_text[: max_chars - len(suffix)] + suffix
|
|
384
|
-
|
|
385
|
-
return body_text
|
|
386
|
-
|
|
387
|
-
def _session_id(self, tool_name: str, task_id: str, context_id: str) -> str:
|
|
388
|
-
return f"tool_{tool_name}_{task_id}_{context_id}"
|
|
389
|
-
|
|
390
|
-
# Output formatting helpers ---------------------------------------
|
|
391
|
-
def _format_output_block(self, output_value: Any, tool_name: str | None) -> str:
|
|
392
|
-
if isinstance(output_value, (dict, list)):
|
|
393
|
-
return self._format_dict_or_list_output(output_value)
|
|
394
|
-
if isinstance(output_value, str):
|
|
395
|
-
return self._format_string_output(output_value, tool_name)
|
|
396
|
-
return self._format_other_output(output_value)
|
|
397
|
-
|
|
398
|
-
def _format_dict_or_list_output(self, output_value: dict | list) -> str:
|
|
399
|
-
try:
|
|
400
|
-
return self._panel_output_prefix + "```json\n" + json.dumps(output_value, indent=2) + "\n```\n"
|
|
401
|
-
except Exception:
|
|
402
|
-
return self._panel_output_prefix + str(output_value) + "\n"
|
|
403
|
-
|
|
404
|
-
def _format_string_output(self, output: str, tool_name: str | None) -> str:
|
|
405
|
-
s = output.strip()
|
|
406
|
-
s = self._clean_sub_agent_prefix(s, tool_name)
|
|
407
|
-
if (s.startswith("{") and s.endswith("}")) or (s.startswith("[") and s.endswith("]")):
|
|
408
|
-
return self._format_json_string_output(s)
|
|
409
|
-
return self._panel_output_prefix + s + "\n"
|
|
410
|
-
|
|
411
|
-
def _format_other_output(self, output_value: Any) -> str:
|
|
412
|
-
try:
|
|
413
|
-
return self._panel_output_prefix + json.dumps(output_value, indent=2) + "\n"
|
|
414
|
-
except Exception:
|
|
415
|
-
return self._panel_output_prefix + str(output_value) + "\n"
|
|
416
|
-
|
|
417
|
-
def _format_json_string_output(self, output: str) -> str:
|
|
418
|
-
try:
|
|
419
|
-
parsed = json.loads(output)
|
|
420
|
-
return self._panel_output_prefix + "```json\n" + json.dumps(parsed, indent=2) + "\n```\n"
|
|
421
|
-
except Exception:
|
|
422
|
-
return self._panel_output_prefix + output + "\n"
|
|
423
|
-
|
|
424
|
-
def _clean_sub_agent_prefix(self, output: str, tool_name: str | None) -> str:
|
|
425
|
-
if not (tool_name and is_delegation_tool(tool_name)):
|
|
426
|
-
return output
|
|
427
|
-
|
|
428
|
-
sub = tool_name
|
|
429
|
-
if tool_name.startswith("delegate_to_"):
|
|
430
|
-
sub = tool_name.replace("delegate_to_", "")
|
|
431
|
-
elif tool_name.startswith("delegate_"):
|
|
432
|
-
sub = tool_name.replace("delegate_", "")
|
|
433
|
-
prefix = f"[{sub}]"
|
|
434
|
-
if output.startswith(prefix):
|
|
435
|
-
return output[len(prefix) :].lstrip()
|
|
436
|
-
return output
|
|
437
|
-
|
|
438
|
-
def _panel_theme(self) -> str:
|
|
439
|
-
return DEFAULT_TRANSCRIPT_THEME
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
__all__ = ["ToolPanelController"]
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
"""Transcript mode utilities extracted from the renderer.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from glaip_sdk.utils.rendering.renderer.debug import render_debug_event
|
|
13
|
-
from glaip_sdk.utils.rendering.state import coerce_received_at
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TranscriptModeMixin:
|
|
17
|
-
"""Provides transcript-mode toggling, hints, and replay helpers."""
|
|
18
|
-
|
|
19
|
-
def __init__(self, *args, **kwargs) -> None:
|
|
20
|
-
"""Initialize transcript mode mixin.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
*args: Positional arguments passed to parent class
|
|
24
|
-
**kwargs: Keyword arguments passed to parent class
|
|
25
|
-
"""
|
|
26
|
-
super().__init__(*args, **kwargs)
|
|
27
|
-
self._transcript_mode_enabled: bool = False
|
|
28
|
-
self._transcript_render_cursor: int = 0
|
|
29
|
-
self.transcript_controller: Any | None = None
|
|
30
|
-
self._transcript_hint_message = "[dim]Transcript view · Press Ctrl+T to return to the summary.[/dim]"
|
|
31
|
-
self._summary_hint_message = "[dim]Press Ctrl+T to inspect raw transcript events.[/dim]"
|
|
32
|
-
self._summary_hint_printed_once: bool = False
|
|
33
|
-
self._transcript_hint_printed_once: bool = False
|
|
34
|
-
self._transcript_header_printed: bool = False
|
|
35
|
-
self._transcript_enabled_message_printed: bool = False
|
|
36
|
-
|
|
37
|
-
# ------------------------------------------------------------------
|
|
38
|
-
# Public controls
|
|
39
|
-
# ------------------------------------------------------------------
|
|
40
|
-
@property
|
|
41
|
-
def transcript_mode_enabled(self) -> bool:
|
|
42
|
-
"""Return True when transcript mode is currently active."""
|
|
43
|
-
return self._transcript_mode_enabled
|
|
44
|
-
|
|
45
|
-
def toggle_transcript_mode(self) -> None:
|
|
46
|
-
"""Flip transcript mode on/off."""
|
|
47
|
-
self.set_transcript_mode(not self._transcript_mode_enabled)
|
|
48
|
-
|
|
49
|
-
def set_transcript_mode(self, enabled: bool) -> None:
|
|
50
|
-
"""Set transcript mode explicitly."""
|
|
51
|
-
if enabled == self._transcript_mode_enabled:
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
self._transcript_mode_enabled = enabled
|
|
55
|
-
self.apply_verbosity(enabled)
|
|
56
|
-
|
|
57
|
-
if enabled:
|
|
58
|
-
self._summary_hint_printed_once = False
|
|
59
|
-
self._transcript_hint_printed_once = False
|
|
60
|
-
self._transcript_header_printed = False
|
|
61
|
-
self._transcript_enabled_message_printed = False
|
|
62
|
-
self._stop_live_display()
|
|
63
|
-
self._clear_console_safe()
|
|
64
|
-
self._print_transcript_enabled_message()
|
|
65
|
-
self._render_transcript_backfill()
|
|
66
|
-
else:
|
|
67
|
-
self._transcript_hint_printed_once = False
|
|
68
|
-
self._transcript_header_printed = False
|
|
69
|
-
self._transcript_enabled_message_printed = False
|
|
70
|
-
self._clear_console_safe()
|
|
71
|
-
|
|
72
|
-
self._render_summary_static_sections()
|
|
73
|
-
summary_notice = (
|
|
74
|
-
"[dim]Returning to the summary view. Streaming will continue here.[/dim]"
|
|
75
|
-
if not self.state.finalizing_ui
|
|
76
|
-
else "[dim]Returning to the summary view.[/dim]"
|
|
77
|
-
)
|
|
78
|
-
self.console.print(summary_notice)
|
|
79
|
-
self._render_summary_after_transcript_toggle()
|
|
80
|
-
if not self.state.finalizing_ui:
|
|
81
|
-
self._print_summary_hint(force=True)
|
|
82
|
-
|
|
83
|
-
# ------------------------------------------------------------------
|
|
84
|
-
# Internal helpers
|
|
85
|
-
# ------------------------------------------------------------------
|
|
86
|
-
def _clear_console_safe(self) -> None:
|
|
87
|
-
try:
|
|
88
|
-
self.console.clear()
|
|
89
|
-
except Exception:
|
|
90
|
-
pass
|
|
91
|
-
|
|
92
|
-
def _print_transcript_hint(self) -> None:
|
|
93
|
-
if not self._transcript_mode_enabled:
|
|
94
|
-
return
|
|
95
|
-
try:
|
|
96
|
-
self.console.print(self._transcript_hint_message)
|
|
97
|
-
except Exception:
|
|
98
|
-
pass
|
|
99
|
-
else:
|
|
100
|
-
self._transcript_hint_printed_once = True
|
|
101
|
-
|
|
102
|
-
def _print_transcript_enabled_message(self) -> None:
|
|
103
|
-
if self._transcript_enabled_message_printed:
|
|
104
|
-
return
|
|
105
|
-
try:
|
|
106
|
-
self.console.print("[dim]Transcript mode enabled — streaming raw transcript events.[/dim]")
|
|
107
|
-
except Exception:
|
|
108
|
-
pass
|
|
109
|
-
else:
|
|
110
|
-
self._transcript_enabled_message_printed = True
|
|
111
|
-
|
|
112
|
-
def _ensure_transcript_header(self) -> None:
|
|
113
|
-
if self._transcript_header_printed:
|
|
114
|
-
return
|
|
115
|
-
try:
|
|
116
|
-
self.console.rule("Transcript Events")
|
|
117
|
-
except Exception:
|
|
118
|
-
self._transcript_header_printed = True
|
|
119
|
-
return
|
|
120
|
-
self._transcript_header_printed = True
|
|
121
|
-
|
|
122
|
-
def _print_summary_hint(self, force: bool = False) -> None:
|
|
123
|
-
controller = getattr(self, "transcript_controller", None)
|
|
124
|
-
if controller and not getattr(controller, "enabled", False):
|
|
125
|
-
if not force:
|
|
126
|
-
self._summary_hint_printed_once = True
|
|
127
|
-
return
|
|
128
|
-
if not force and self._summary_hint_printed_once:
|
|
129
|
-
return
|
|
130
|
-
try:
|
|
131
|
-
self.console.print(self._summary_hint_message)
|
|
132
|
-
except Exception:
|
|
133
|
-
return
|
|
134
|
-
self._summary_hint_printed_once = True
|
|
135
|
-
|
|
136
|
-
def _render_transcript_backfill(self) -> None:
|
|
137
|
-
pending = self.state.events[self._transcript_render_cursor :]
|
|
138
|
-
self._ensure_transcript_header()
|
|
139
|
-
if not pending:
|
|
140
|
-
self._print_transcript_hint()
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
baseline = self.state.streaming_started_event_ts
|
|
144
|
-
for ev in pending:
|
|
145
|
-
received_ts = coerce_received_at(ev.get("received_at"))
|
|
146
|
-
render_debug_event(
|
|
147
|
-
ev,
|
|
148
|
-
self.console,
|
|
149
|
-
received_ts=received_ts,
|
|
150
|
-
baseline_ts=baseline,
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
self._transcript_render_cursor = len(self.state.events)
|
|
154
|
-
self._print_transcript_hint()
|
|
155
|
-
|
|
156
|
-
def _capture_event(self, ev: dict[str, Any], received_at: datetime | None = None) -> None:
|
|
157
|
-
self.state.record_event(ev, received_at=received_at)
|
|
158
|
-
if self._transcript_mode_enabled:
|
|
159
|
-
self._transcript_render_cursor = len(self.state.events)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
__all__ = ["TranscriptModeMixin"]
|