glaip-sdk 0.2.2__py3-none-any.whl → 0.4.0__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.
Files changed (71) hide show
  1. glaip_sdk/cli/auth.py +2 -1
  2. glaip_sdk/cli/commands/agents.py +51 -36
  3. glaip_sdk/cli/commands/configure.py +2 -1
  4. glaip_sdk/cli/commands/mcps.py +219 -62
  5. glaip_sdk/cli/commands/models.py +3 -5
  6. glaip_sdk/cli/commands/tools.py +27 -16
  7. glaip_sdk/cli/commands/transcripts.py +1 -1
  8. glaip_sdk/cli/constants.py +3 -0
  9. glaip_sdk/cli/display.py +1 -1
  10. glaip_sdk/cli/hints.py +58 -0
  11. glaip_sdk/cli/io.py +6 -3
  12. glaip_sdk/cli/main.py +3 -4
  13. glaip_sdk/cli/slash/agent_session.py +4 -13
  14. glaip_sdk/cli/slash/prompt.py +3 -0
  15. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  16. glaip_sdk/cli/slash/session.py +139 -48
  17. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  18. glaip_sdk/cli/slash/tui/remote_runs_app.py +632 -0
  19. glaip_sdk/cli/transcript/capture.py +1 -1
  20. glaip_sdk/cli/transcript/viewer.py +19 -678
  21. glaip_sdk/cli/update_notifier.py +2 -1
  22. glaip_sdk/cli/utils.py +228 -101
  23. glaip_sdk/cli/validators.py +5 -6
  24. glaip_sdk/client/__init__.py +2 -1
  25. glaip_sdk/client/agent_runs.py +147 -0
  26. glaip_sdk/client/agents.py +40 -22
  27. glaip_sdk/client/main.py +2 -6
  28. glaip_sdk/client/mcps.py +13 -5
  29. glaip_sdk/client/run_rendering.py +90 -111
  30. glaip_sdk/client/shared.py +21 -0
  31. glaip_sdk/client/tools.py +2 -3
  32. glaip_sdk/config/constants.py +11 -0
  33. glaip_sdk/models/__init__.py +56 -0
  34. glaip_sdk/models/agent_runs.py +117 -0
  35. glaip_sdk/models.py +8 -7
  36. glaip_sdk/rich_components.py +58 -2
  37. glaip_sdk/utils/client_utils.py +13 -0
  38. glaip_sdk/utils/display.py +23 -15
  39. glaip_sdk/utils/export.py +143 -0
  40. glaip_sdk/utils/import_export.py +6 -9
  41. glaip_sdk/utils/rendering/__init__.py +115 -1
  42. glaip_sdk/utils/rendering/formatting.py +5 -30
  43. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  44. glaip_sdk/utils/rendering/{renderer → layout}/panels.py +9 -0
  45. glaip_sdk/utils/rendering/{renderer → layout}/progress.py +70 -1
  46. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  47. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  48. glaip_sdk/utils/rendering/models.py +1 -0
  49. glaip_sdk/utils/rendering/renderer/__init__.py +10 -28
  50. glaip_sdk/utils/rendering/renderer/base.py +217 -1476
  51. glaip_sdk/utils/rendering/renderer/debug.py +24 -1
  52. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  53. glaip_sdk/utils/rendering/renderer/stream.py +4 -12
  54. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  55. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  56. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  57. glaip_sdk/utils/rendering/state.py +204 -0
  58. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  59. glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +53 -439
  60. glaip_sdk/utils/rendering/steps/format.py +176 -0
  61. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  62. glaip_sdk/utils/rendering/timing.py +36 -0
  63. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  64. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  65. glaip_sdk/utils/resource_refs.py +26 -15
  66. glaip_sdk/utils/validation.py +13 -21
  67. {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/METADATA +24 -2
  68. glaip_sdk-0.4.0.dist-info/RECORD +110 -0
  69. glaip_sdk-0.2.2.dist-info/RECORD +0 -87
  70. {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/WHEEL +0 -0
  71. {glaip_sdk-0.2.2.dist-info → glaip_sdk-0.4.0.dist-info}/entry_points.txt +0 -0
@@ -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
@@ -145,3 +146,11 @@ def create_final_panel(content: str, title: str = "Final Result", theme: str = "
145
146
  border_style=SUCCESS,
146
147
  padding=(0, 1),
147
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 glaip_sdk.utils.rendering.formatting import get_spinner_char
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,
@@ -131,3 +172,31 @@ def format_tool_title(tool_name: str) -> str:
131
172
 
132
173
  # Convert snake_case to Title Case
133
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