glaip-sdk 0.0.20__py3-none-any.whl → 0.6.5b6__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 +1126 -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 +270 -173
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +735 -143
- glaip_sdk/cli/commands/mcps.py +265 -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 +500 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +61 -28
- glaip_sdk/cli/slash/prompt.py +13 -10
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +772 -222
- 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 +872 -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 +77 -329
- glaip_sdk/cli/update_notifier.py +177 -24
- glaip_sdk/cli/utils.py +242 -1309
- 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 +218 -78
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +161 -34
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/icons.py +9 -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 +231 -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 +597 -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 +158 -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 +177 -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 +38 -23
- 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 +18 -8
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +476 -882
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- 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 +13 -54
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/step_tree_state.py +100 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/{steps.py → steps/manager.py} +122 -26
- 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 +422 -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.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/METADATA +49 -4
- glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -259
- glaip_sdk-0.0.20.dist-info/RECORD +0 -80
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/entry_points.txt +0 -0
|
@@ -8,11 +8,17 @@ 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
|
|
|
15
|
-
from glaip_sdk.icons import
|
|
14
|
+
from glaip_sdk.icons import (
|
|
15
|
+
ICON_AGENT_STEP,
|
|
16
|
+
ICON_DELEGATE,
|
|
17
|
+
ICON_STATUS_FAILED,
|
|
18
|
+
ICON_STATUS_SUCCESS,
|
|
19
|
+
ICON_STATUS_WARNING,
|
|
20
|
+
ICON_TOOL_STEP,
|
|
21
|
+
)
|
|
16
22
|
|
|
17
23
|
# Constants for argument formatting
|
|
18
24
|
DEFAULT_ARGS_MAX_LEN = 100
|
|
@@ -37,9 +43,15 @@ SECRET_VALUE_PATTERNS = [
|
|
|
37
43
|
re.compile(r"eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+"), # JWT tokens
|
|
38
44
|
]
|
|
39
45
|
SENSITIVE_PATTERNS = re.compile(
|
|
40
|
-
r"password
|
|
46
|
+
r"(?:password|secret|token|key|api_key)(?:\s*[:=]\s*[^\s,}]+)?",
|
|
41
47
|
re.IGNORECASE,
|
|
42
48
|
)
|
|
49
|
+
SECRET_MASK = "••••••"
|
|
50
|
+
STATUS_GLYPHS = {
|
|
51
|
+
"success": ICON_STATUS_SUCCESS,
|
|
52
|
+
"failed": ICON_STATUS_FAILED,
|
|
53
|
+
"warning": ICON_STATUS_WARNING,
|
|
54
|
+
}
|
|
43
55
|
|
|
44
56
|
|
|
45
57
|
def _truncate_string(s: str, max_len: int) -> str:
|
|
@@ -53,7 +65,7 @@ def mask_secrets_in_string(text: str) -> str:
|
|
|
53
65
|
"""Mask sensitive information in a string."""
|
|
54
66
|
result = text
|
|
55
67
|
for pattern in SECRET_VALUE_PATTERNS:
|
|
56
|
-
result = re.sub(pattern,
|
|
68
|
+
result = re.sub(pattern, SECRET_MASK, result)
|
|
57
69
|
return result
|
|
58
70
|
|
|
59
71
|
|
|
@@ -74,7 +86,7 @@ def _redact_dict_values(text: dict) -> dict:
|
|
|
74
86
|
result = {}
|
|
75
87
|
for key, value in text.items():
|
|
76
88
|
if _is_sensitive_key(key):
|
|
77
|
-
result[key] =
|
|
89
|
+
result[key] = SECRET_MASK
|
|
78
90
|
elif _should_recurse_redaction(value):
|
|
79
91
|
result[key] = redact_sensitive(value)
|
|
80
92
|
else:
|
|
@@ -92,11 +104,11 @@ def _redact_string_content(text: str) -> str:
|
|
|
92
104
|
result = text
|
|
93
105
|
# First mask secrets
|
|
94
106
|
for pattern in SECRET_VALUE_PATTERNS:
|
|
95
|
-
result = re.sub(pattern,
|
|
107
|
+
result = re.sub(pattern, SECRET_MASK, result)
|
|
96
108
|
# Then redact sensitive patterns
|
|
97
109
|
result = re.sub(
|
|
98
110
|
SENSITIVE_PATTERNS,
|
|
99
|
-
lambda m: m.group(0).split("=")[0] + "
|
|
111
|
+
lambda m: m.group(0).split("=")[0] + "=" + SECRET_MASK,
|
|
100
112
|
result,
|
|
101
113
|
)
|
|
102
114
|
return result
|
|
@@ -105,15 +117,28 @@ def _redact_string_content(text: str) -> str:
|
|
|
105
117
|
def _is_sensitive_key(key: str) -> bool:
|
|
106
118
|
"""Check if a key contains sensitive information."""
|
|
107
119
|
key_lower = key.lower()
|
|
108
|
-
return any(
|
|
109
|
-
sensitive in key_lower
|
|
110
|
-
for sensitive in ["password", "secret", "token", "key", "api_key"]
|
|
111
|
-
)
|
|
120
|
+
return any(sensitive in key_lower for sensitive in ["password", "secret", "token", "key", "api_key"])
|
|
112
121
|
|
|
113
122
|
|
|
114
123
|
def _should_recurse_redaction(value: Any) -> bool:
|
|
115
124
|
"""Check if a value should be recursively processed."""
|
|
116
|
-
return isinstance(value, dict
|
|
125
|
+
return isinstance(value, (dict, list)) or isinstance(value, str)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def glyph_for_status(icon_key: str | None) -> str | None:
|
|
129
|
+
"""Return glyph representing a step status icon key."""
|
|
130
|
+
if not icon_key:
|
|
131
|
+
return None
|
|
132
|
+
return STATUS_GLYPHS.get(icon_key)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def normalise_display_label(label: str | None) -> str:
|
|
136
|
+
"""Return a user facing label or the Unknown fallback."""
|
|
137
|
+
if not isinstance(label, str):
|
|
138
|
+
text = ""
|
|
139
|
+
else:
|
|
140
|
+
text = label.strip()
|
|
141
|
+
return text or "Unknown step detail"
|
|
117
142
|
|
|
118
143
|
|
|
119
144
|
def pretty_args(args: dict | None, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
@@ -132,7 +157,7 @@ def pretty_args(args: dict | None, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
|
132
157
|
try:
|
|
133
158
|
args_str = json.dumps(masked_args, ensure_ascii=False, separators=(",", ":"))
|
|
134
159
|
return _truncate_string(args_str, max_len)
|
|
135
|
-
except
|
|
160
|
+
except Exception:
|
|
136
161
|
# Fallback to string representation if JSON serialization fails
|
|
137
162
|
args_str = str(masked_args)
|
|
138
163
|
return _truncate_string(args_str, max_len)
|
|
@@ -162,16 +187,6 @@ def pretty_out(output: any, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
|
162
187
|
return _truncate_string(output_str, max_len)
|
|
163
188
|
|
|
164
189
|
|
|
165
|
-
def get_spinner_char() -> str:
|
|
166
|
-
"""Get the next character for a spinner animation.
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
A single character from the spinner frames based on current time
|
|
170
|
-
"""
|
|
171
|
-
frames = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
172
|
-
return frames[int(time.time() * 10) % len(frames)]
|
|
173
|
-
|
|
174
|
-
|
|
175
190
|
def get_step_icon(step_kind: str) -> str:
|
|
176
191
|
"""Get the appropriate icon for a step kind."""
|
|
177
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
|