glaip-sdk 0.1.0__py3-none-any.whl → 0.6.10__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 +5 -2
- glaip_sdk/_version.py +10 -3
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1191 -0
- glaip_sdk/branding.py +15 -6
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +265 -45
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +251 -173
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +735 -143
- glaip_sdk/cli/commands/mcps.py +266 -134
- glaip_sdk/cli/commands/models.py +13 -9
- glaip_sdk/cli/commands/tools.py +67 -88
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/commands/update.py +3 -8
- glaip_sdk/cli/config.py +49 -7
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +846 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +45 -32
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +14 -17
- glaip_sdk/cli/main.py +232 -143
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/mcp_validators.py +5 -15
- glaip_sdk/cli/pager.py +12 -19
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/parsers/json_input.py +11 -22
- glaip_sdk/cli/resolution.py +3 -9
- glaip_sdk/cli/rich_helpers.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +578 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +65 -29
- glaip_sdk/cli/slash/prompt.py +24 -10
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +807 -225
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/loading.py +58 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +258 -60
- glaip_sdk/cli/transcript/capture.py +72 -21
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +1 -3
- glaip_sdk/cli/transcript/viewer.py +79 -499
- glaip_sdk/cli/update_notifier.py +177 -24
- glaip_sdk/cli/utils.py +242 -1308
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +53 -37
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +320 -92
- glaip_sdk/client/base.py +78 -35
- glaip_sdk/client/main.py +19 -10
- glaip_sdk/client/mcps.py +123 -15
- glaip_sdk/client/run_rendering.py +136 -101
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +163 -34
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +90 -0
- glaip_sdk/models/agent.py +47 -0
- glaip_sdk/models/agent_runs.py +116 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/payload_schemas/agent.py +1 -3
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +232 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +59 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +115 -0
- glaip_sdk/runner/langgraph.py +706 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +58 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +4 -14
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +46 -28
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +25 -21
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +15 -16
- glaip_sdk/utils/import_resolver.py +492 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +7 -35
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +3 -6
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -49
- glaip_sdk/utils/rendering/renderer/base.py +258 -1577
- glaip_sdk/utils/rendering/renderer/config.py +1 -5
- glaip_sdk/utils/rendering/renderer/debug.py +30 -34
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +10 -51
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/toggle.py +1 -3
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/step_tree_state.py +1 -3
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +76 -517
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/steps/manager.py +387 -0
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +29 -26
- glaip_sdk/utils/runtime_config.py +425 -0
- glaip_sdk/utils/serialization.py +32 -46
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +20 -28
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/METADATA +42 -4
- glaip_sdk-0.6.10.dist-info/RECORD +159 -0
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -259
- glaip_sdk-0.1.0.dist-info/RECORD +0 -82
- {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/entry_points.txt +0 -0
|
@@ -8,7 +8,6 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
10
|
import re
|
|
11
|
-
import time
|
|
12
11
|
from collections.abc import Callable
|
|
13
12
|
from typing import Any
|
|
14
13
|
|
|
@@ -47,11 +46,6 @@ SENSITIVE_PATTERNS = re.compile(
|
|
|
47
46
|
r"(?:password|secret|token|key|api_key)(?:\s*[:=]\s*[^\s,}]+)?",
|
|
48
47
|
re.IGNORECASE,
|
|
49
48
|
)
|
|
50
|
-
CONNECTOR_VERTICAL = "│ "
|
|
51
|
-
CONNECTOR_EMPTY = " "
|
|
52
|
-
CONNECTOR_BRANCH = "├─ "
|
|
53
|
-
CONNECTOR_LAST = "└─ "
|
|
54
|
-
ROOT_MARKER = ""
|
|
55
49
|
SECRET_MASK = "••••••"
|
|
56
50
|
STATUS_GLYPHS = {
|
|
57
51
|
"success": ICON_STATUS_SUCCESS,
|
|
@@ -123,15 +117,12 @@ def _redact_string_content(text: str) -> str:
|
|
|
123
117
|
def _is_sensitive_key(key: str) -> bool:
|
|
124
118
|
"""Check if a key contains sensitive information."""
|
|
125
119
|
key_lower = key.lower()
|
|
126
|
-
return any(
|
|
127
|
-
sensitive in key_lower
|
|
128
|
-
for sensitive in ["password", "secret", "token", "key", "api_key"]
|
|
129
|
-
)
|
|
120
|
+
return any(sensitive in key_lower for sensitive in ["password", "secret", "token", "key", "api_key"])
|
|
130
121
|
|
|
131
122
|
|
|
132
123
|
def _should_recurse_redaction(value: Any) -> bool:
|
|
133
124
|
"""Check if a value should be recursively processed."""
|
|
134
|
-
return isinstance(value, dict
|
|
125
|
+
return isinstance(value, (dict, list)) or isinstance(value, str)
|
|
135
126
|
|
|
136
127
|
|
|
137
128
|
def glyph_for_status(icon_key: str | None) -> str | None:
|
|
@@ -143,20 +134,11 @@ def glyph_for_status(icon_key: str | None) -> str | None:
|
|
|
143
134
|
|
|
144
135
|
def normalise_display_label(label: str | None) -> str:
|
|
145
136
|
"""Return a user facing label or the Unknown fallback."""
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"""Build connector prefix for a tree line based on ancestry state."""
|
|
152
|
-
if not branch_state:
|
|
153
|
-
return ROOT_MARKER
|
|
154
|
-
|
|
155
|
-
parts: list[str] = []
|
|
156
|
-
for ancestor_is_last in branch_state[:-1]:
|
|
157
|
-
parts.append(CONNECTOR_EMPTY if ancestor_is_last else CONNECTOR_VERTICAL)
|
|
158
|
-
parts.append(CONNECTOR_LAST if branch_state[-1] else CONNECTOR_BRANCH)
|
|
159
|
-
return "".join(parts)
|
|
137
|
+
if not isinstance(label, str):
|
|
138
|
+
text = ""
|
|
139
|
+
else:
|
|
140
|
+
text = label.strip()
|
|
141
|
+
return text or "Unknown step detail"
|
|
160
142
|
|
|
161
143
|
|
|
162
144
|
def pretty_args(args: dict | None, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
@@ -205,16 +187,6 @@ def pretty_out(output: any, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
|
205
187
|
return _truncate_string(output_str, max_len)
|
|
206
188
|
|
|
207
189
|
|
|
208
|
-
def get_spinner_char() -> str:
|
|
209
|
-
"""Get the next character for a spinner animation.
|
|
210
|
-
|
|
211
|
-
Returns:
|
|
212
|
-
A single character from the spinner frames based on current time
|
|
213
|
-
"""
|
|
214
|
-
frames = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
215
|
-
return frames[int(time.time() * 10) % len(frames)]
|
|
216
|
-
|
|
217
|
-
|
|
218
190
|
def get_step_icon(step_kind: str) -> str:
|
|
219
191
|
"""Get the appropriate icon for a step kind."""
|
|
220
192
|
if step_kind == "tool":
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Layout utilities exposed for renderer/viewer consumers.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from glaip_sdk.utils.rendering.layout.panels import (
|
|
8
|
+
create_context_panel,
|
|
9
|
+
create_final_panel,
|
|
10
|
+
create_main_panel,
|
|
11
|
+
create_tool_panel,
|
|
12
|
+
)
|
|
13
|
+
from glaip_sdk.utils.rendering.layout.progress import (
|
|
14
|
+
TrailingSpinnerLine,
|
|
15
|
+
build_progress_footer,
|
|
16
|
+
format_elapsed_time,
|
|
17
|
+
format_tool_title,
|
|
18
|
+
format_working_indicator,
|
|
19
|
+
get_spinner,
|
|
20
|
+
get_spinner_char,
|
|
21
|
+
is_delegation_tool,
|
|
22
|
+
)
|
|
23
|
+
from glaip_sdk.utils.rendering.layout.transcript import (
|
|
24
|
+
DEFAULT_TRANSCRIPT_THEME,
|
|
25
|
+
TranscriptGlyphs,
|
|
26
|
+
TranscriptRow,
|
|
27
|
+
TranscriptSnapshot,
|
|
28
|
+
build_final_panel,
|
|
29
|
+
build_transcript_snapshot,
|
|
30
|
+
build_transcript_view,
|
|
31
|
+
extract_query_from_meta,
|
|
32
|
+
format_final_panel_title,
|
|
33
|
+
render_final_panel,
|
|
34
|
+
)
|
|
35
|
+
from glaip_sdk.utils.rendering.layout.summary import render_summary_panels
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
# Panels
|
|
39
|
+
"create_context_panel",
|
|
40
|
+
"create_final_panel",
|
|
41
|
+
"create_main_panel",
|
|
42
|
+
"create_tool_panel",
|
|
43
|
+
"render_summary_panels",
|
|
44
|
+
# Progress
|
|
45
|
+
"TrailingSpinnerLine",
|
|
46
|
+
"build_progress_footer",
|
|
47
|
+
"format_elapsed_time",
|
|
48
|
+
"format_tool_title",
|
|
49
|
+
"format_working_indicator",
|
|
50
|
+
"get_spinner",
|
|
51
|
+
"get_spinner_char",
|
|
52
|
+
"is_delegation_tool",
|
|
53
|
+
# Transcript
|
|
54
|
+
"DEFAULT_TRANSCRIPT_THEME",
|
|
55
|
+
"TranscriptGlyphs",
|
|
56
|
+
"TranscriptRow",
|
|
57
|
+
"TranscriptSnapshot",
|
|
58
|
+
"build_final_panel",
|
|
59
|
+
"build_transcript_snapshot",
|
|
60
|
+
"build_transcript_view",
|
|
61
|
+
"extract_query_from_meta",
|
|
62
|
+
"format_final_panel_title",
|
|
63
|
+
"render_final_panel",
|
|
64
|
+
]
|
|
@@ -6,6 +6,7 @@ Authors:
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
from rich.align import Align
|
|
10
11
|
from rich.markdown import Markdown
|
|
11
12
|
from rich.spinner import Spinner
|
|
@@ -128,9 +129,7 @@ def create_context_panel(
|
|
|
128
129
|
)
|
|
129
130
|
|
|
130
131
|
|
|
131
|
-
def create_final_panel(
|
|
132
|
-
content: str, title: str = "Final Result", theme: str = "dark"
|
|
133
|
-
) -> AIPPanel:
|
|
132
|
+
def create_final_panel(content: str, title: str = "Final Result", theme: str = "dark") -> AIPPanel:
|
|
134
133
|
"""Create a final result panel.
|
|
135
134
|
|
|
136
135
|
Args:
|
|
@@ -147,3 +146,11 @@ def create_final_panel(
|
|
|
147
146
|
border_style=SUCCESS,
|
|
148
147
|
padding=(0, 1),
|
|
149
148
|
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
__all__ = [
|
|
152
|
+
"create_main_panel",
|
|
153
|
+
"create_tool_panel",
|
|
154
|
+
"create_context_panel",
|
|
155
|
+
"create_final_panel",
|
|
156
|
+
]
|
|
@@ -7,8 +7,22 @@ Authors:
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from time import monotonic
|
|
10
|
+
from typing import Any
|
|
10
11
|
|
|
11
|
-
from
|
|
12
|
+
from rich.console import Console as RichConsole
|
|
13
|
+
from rich.console import Group
|
|
14
|
+
from rich.measure import Measurement
|
|
15
|
+
from rich.spinner import Spinner
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
|
|
18
|
+
from glaip_sdk.utils.rendering.steps.manager import StepManager
|
|
19
|
+
|
|
20
|
+
_SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _spinner_time() -> float:
|
|
24
|
+
"""Return the monotonic time used for spinner animation."""
|
|
25
|
+
return monotonic()
|
|
12
26
|
|
|
13
27
|
|
|
14
28
|
def get_spinner() -> str:
|
|
@@ -16,6 +30,33 @@ def get_spinner() -> str:
|
|
|
16
30
|
return get_spinner_char()
|
|
17
31
|
|
|
18
32
|
|
|
33
|
+
def get_spinner_char() -> str:
|
|
34
|
+
"""Return the spinner frame based on elapsed time."""
|
|
35
|
+
frame_index = int(_spinner_time() * 10) % len(_SPINNER_FRAMES)
|
|
36
|
+
return _SPINNER_FRAMES[frame_index]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TrailingSpinnerLine:
|
|
40
|
+
"""Render a text line with a trailing animated Rich spinner."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, base_text: Text, spinner: Spinner) -> None:
|
|
43
|
+
"""Initialize spinner line with base text and spinner component."""
|
|
44
|
+
self._base_text = base_text
|
|
45
|
+
self._spinner = spinner
|
|
46
|
+
|
|
47
|
+
def __rich_console__(self, console: RichConsole, options: Any) -> Any: # type: ignore[override]
|
|
48
|
+
"""Render the text with trailing animated spinner."""
|
|
49
|
+
spinner_render = self._spinner.render(console.get_time())
|
|
50
|
+
combined = Text.assemble(self._base_text.copy(), " ", spinner_render)
|
|
51
|
+
yield combined
|
|
52
|
+
|
|
53
|
+
def __rich_measure__(self, console: RichConsole, options: Any) -> Measurement: # type: ignore[override]
|
|
54
|
+
"""Measure the combined text and spinner dimensions."""
|
|
55
|
+
snapshot = self._spinner.render(0)
|
|
56
|
+
combined = Text.assemble(self._base_text.copy(), " ", snapshot)
|
|
57
|
+
return Measurement.get(console, options, combined)
|
|
58
|
+
|
|
59
|
+
|
|
19
60
|
def _resolve_elapsed_time(
|
|
20
61
|
started_at: float | None,
|
|
21
62
|
server_elapsed_time: float | None,
|
|
@@ -48,15 +89,11 @@ def format_working_indicator(
|
|
|
48
89
|
"""Format a working indicator with elapsed time."""
|
|
49
90
|
base_message = "Working..."
|
|
50
91
|
|
|
51
|
-
if started_at is None and (
|
|
52
|
-
server_elapsed_time is None or streaming_started_at is None
|
|
53
|
-
):
|
|
92
|
+
if started_at is None and (server_elapsed_time is None or streaming_started_at is None):
|
|
54
93
|
return base_message
|
|
55
94
|
|
|
56
95
|
spinner_chip = f"{get_spinner_char()} {base_message}"
|
|
57
|
-
elapsed = _resolve_elapsed_time(
|
|
58
|
-
started_at, server_elapsed_time, streaming_started_at
|
|
59
|
-
)
|
|
96
|
+
elapsed = _resolve_elapsed_time(started_at, server_elapsed_time, streaming_started_at)
|
|
60
97
|
if elapsed is None:
|
|
61
98
|
return spinner_chip
|
|
62
99
|
|
|
@@ -93,11 +130,7 @@ def is_delegation_tool(tool_name: str) -> bool:
|
|
|
93
130
|
Returns:
|
|
94
131
|
True if this is a delegation tool
|
|
95
132
|
"""
|
|
96
|
-
return (
|
|
97
|
-
tool_name.startswith("delegate_to_")
|
|
98
|
-
or tool_name.startswith("delegate_")
|
|
99
|
-
or "sub_agent" in tool_name.lower()
|
|
100
|
-
)
|
|
133
|
+
return tool_name.startswith("delegate_to_") or tool_name.startswith("delegate_") or "sub_agent" in tool_name.lower()
|
|
101
134
|
|
|
102
135
|
|
|
103
136
|
def _delegation_tool_title(tool_name: str) -> str | None:
|
|
@@ -139,3 +172,31 @@ def format_tool_title(tool_name: str) -> str:
|
|
|
139
172
|
|
|
140
173
|
# Convert snake_case to Title Case
|
|
141
174
|
return clean_name.replace("_", " ").title()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _has_running_steps(steps: StepManager) -> bool:
|
|
178
|
+
for step in steps.by_id.values():
|
|
179
|
+
if getattr(step, "status", None) not in {"finished", "failed", "stopped"}:
|
|
180
|
+
return True
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def build_progress_footer(
|
|
185
|
+
*,
|
|
186
|
+
state: Any,
|
|
187
|
+
steps: StepManager,
|
|
188
|
+
started_at: float | None,
|
|
189
|
+
server_elapsed_time: float | None,
|
|
190
|
+
) -> Group | None:
|
|
191
|
+
"""Return a trailing progress indicator when work is ongoing."""
|
|
192
|
+
if not _has_running_steps(steps):
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
indicator = format_working_indicator(
|
|
196
|
+
started_at,
|
|
197
|
+
server_elapsed_time,
|
|
198
|
+
getattr(state, "streaming_started_at", None),
|
|
199
|
+
)
|
|
200
|
+
text = Text(indicator, style="dim")
|
|
201
|
+
spinner = Spinner("dots", style="dim")
|
|
202
|
+
return Group(TrailingSpinnerLine(text, spinner))
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|