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.
Files changed (156) hide show
  1. glaip_sdk/__init__.py +5 -2
  2. glaip_sdk/_version.py +10 -3
  3. glaip_sdk/agents/__init__.py +27 -0
  4. glaip_sdk/agents/base.py +1191 -0
  5. glaip_sdk/branding.py +15 -6
  6. glaip_sdk/cli/account_store.py +540 -0
  7. glaip_sdk/cli/agent_config.py +2 -6
  8. glaip_sdk/cli/auth.py +265 -45
  9. glaip_sdk/cli/commands/__init__.py +2 -2
  10. glaip_sdk/cli/commands/accounts.py +746 -0
  11. glaip_sdk/cli/commands/agents.py +251 -173
  12. glaip_sdk/cli/commands/common_config.py +101 -0
  13. glaip_sdk/cli/commands/configure.py +735 -143
  14. glaip_sdk/cli/commands/mcps.py +266 -134
  15. glaip_sdk/cli/commands/models.py +13 -9
  16. glaip_sdk/cli/commands/tools.py +67 -88
  17. glaip_sdk/cli/commands/transcripts.py +755 -0
  18. glaip_sdk/cli/commands/update.py +3 -8
  19. glaip_sdk/cli/config.py +49 -7
  20. glaip_sdk/cli/constants.py +38 -0
  21. glaip_sdk/cli/context.py +8 -0
  22. glaip_sdk/cli/core/__init__.py +79 -0
  23. glaip_sdk/cli/core/context.py +124 -0
  24. glaip_sdk/cli/core/output.py +846 -0
  25. glaip_sdk/cli/core/prompting.py +649 -0
  26. glaip_sdk/cli/core/rendering.py +187 -0
  27. glaip_sdk/cli/display.py +45 -32
  28. glaip_sdk/cli/hints.py +57 -0
  29. glaip_sdk/cli/io.py +14 -17
  30. glaip_sdk/cli/main.py +232 -143
  31. glaip_sdk/cli/masking.py +21 -33
  32. glaip_sdk/cli/mcp_validators.py +5 -15
  33. glaip_sdk/cli/pager.py +12 -19
  34. glaip_sdk/cli/parsers/__init__.py +1 -3
  35. glaip_sdk/cli/parsers/json_input.py +11 -22
  36. glaip_sdk/cli/resolution.py +3 -9
  37. glaip_sdk/cli/rich_helpers.py +1 -3
  38. glaip_sdk/cli/slash/__init__.py +0 -9
  39. glaip_sdk/cli/slash/accounts_controller.py +578 -0
  40. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  41. glaip_sdk/cli/slash/agent_session.py +65 -29
  42. glaip_sdk/cli/slash/prompt.py +24 -10
  43. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  44. glaip_sdk/cli/slash/session.py +807 -225
  45. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  46. glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
  47. glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
  48. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  49. glaip_sdk/cli/slash/tui/loading.py +58 -0
  50. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  51. glaip_sdk/cli/transcript/__init__.py +12 -52
  52. glaip_sdk/cli/transcript/cache.py +258 -60
  53. glaip_sdk/cli/transcript/capture.py +72 -21
  54. glaip_sdk/cli/transcript/history.py +815 -0
  55. glaip_sdk/cli/transcript/launcher.py +1 -3
  56. glaip_sdk/cli/transcript/viewer.py +79 -499
  57. glaip_sdk/cli/update_notifier.py +177 -24
  58. glaip_sdk/cli/utils.py +242 -1308
  59. glaip_sdk/cli/validators.py +16 -18
  60. glaip_sdk/client/__init__.py +2 -1
  61. glaip_sdk/client/_agent_payloads.py +53 -37
  62. glaip_sdk/client/agent_runs.py +147 -0
  63. glaip_sdk/client/agents.py +320 -92
  64. glaip_sdk/client/base.py +78 -35
  65. glaip_sdk/client/main.py +19 -10
  66. glaip_sdk/client/mcps.py +123 -15
  67. glaip_sdk/client/run_rendering.py +136 -101
  68. glaip_sdk/client/shared.py +21 -0
  69. glaip_sdk/client/tools.py +163 -34
  70. glaip_sdk/client/validators.py +20 -48
  71. glaip_sdk/config/constants.py +11 -0
  72. glaip_sdk/exceptions.py +1 -3
  73. glaip_sdk/mcps/__init__.py +21 -0
  74. glaip_sdk/mcps/base.py +345 -0
  75. glaip_sdk/models/__init__.py +90 -0
  76. glaip_sdk/models/agent.py +47 -0
  77. glaip_sdk/models/agent_runs.py +116 -0
  78. glaip_sdk/models/common.py +42 -0
  79. glaip_sdk/models/mcp.py +33 -0
  80. glaip_sdk/models/tool.py +33 -0
  81. glaip_sdk/payload_schemas/__init__.py +1 -13
  82. glaip_sdk/payload_schemas/agent.py +1 -3
  83. glaip_sdk/registry/__init__.py +55 -0
  84. glaip_sdk/registry/agent.py +164 -0
  85. glaip_sdk/registry/base.py +139 -0
  86. glaip_sdk/registry/mcp.py +253 -0
  87. glaip_sdk/registry/tool.py +232 -0
  88. glaip_sdk/rich_components.py +58 -2
  89. glaip_sdk/runner/__init__.py +59 -0
  90. glaip_sdk/runner/base.py +84 -0
  91. glaip_sdk/runner/deps.py +115 -0
  92. glaip_sdk/runner/langgraph.py +706 -0
  93. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  94. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  95. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
  96. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  97. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  98. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  99. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
  100. glaip_sdk/tools/__init__.py +22 -0
  101. glaip_sdk/tools/base.py +435 -0
  102. glaip_sdk/utils/__init__.py +58 -12
  103. glaip_sdk/utils/a2a/__init__.py +34 -0
  104. glaip_sdk/utils/a2a/event_processor.py +188 -0
  105. glaip_sdk/utils/agent_config.py +4 -14
  106. glaip_sdk/utils/bundler.py +267 -0
  107. glaip_sdk/utils/client.py +111 -0
  108. glaip_sdk/utils/client_utils.py +46 -28
  109. glaip_sdk/utils/datetime_helpers.py +58 -0
  110. glaip_sdk/utils/discovery.py +78 -0
  111. glaip_sdk/utils/display.py +25 -21
  112. glaip_sdk/utils/export.py +143 -0
  113. glaip_sdk/utils/general.py +1 -36
  114. glaip_sdk/utils/import_export.py +15 -16
  115. glaip_sdk/utils/import_resolver.py +492 -0
  116. glaip_sdk/utils/instructions.py +101 -0
  117. glaip_sdk/utils/rendering/__init__.py +115 -1
  118. glaip_sdk/utils/rendering/formatting.py +7 -35
  119. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  120. glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
  121. glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
  122. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  123. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  124. glaip_sdk/utils/rendering/models.py +3 -6
  125. glaip_sdk/utils/rendering/renderer/__init__.py +9 -49
  126. glaip_sdk/utils/rendering/renderer/base.py +258 -1577
  127. glaip_sdk/utils/rendering/renderer/config.py +1 -5
  128. glaip_sdk/utils/rendering/renderer/debug.py +30 -34
  129. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  130. glaip_sdk/utils/rendering/renderer/stream.py +10 -51
  131. glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
  132. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  133. glaip_sdk/utils/rendering/renderer/toggle.py +1 -3
  134. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  135. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  136. glaip_sdk/utils/rendering/state.py +204 -0
  137. glaip_sdk/utils/rendering/step_tree_state.py +1 -3
  138. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  139. glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +76 -517
  140. glaip_sdk/utils/rendering/steps/format.py +176 -0
  141. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  142. glaip_sdk/utils/rendering/timing.py +36 -0
  143. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  144. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  145. glaip_sdk/utils/resource_refs.py +29 -26
  146. glaip_sdk/utils/runtime_config.py +425 -0
  147. glaip_sdk/utils/serialization.py +32 -46
  148. glaip_sdk/utils/sync.py +142 -0
  149. glaip_sdk/utils/tool_detection.py +33 -0
  150. glaip_sdk/utils/validation.py +20 -28
  151. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/METADATA +42 -4
  152. glaip_sdk-0.6.10.dist-info/RECORD +159 -0
  153. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/WHEEL +1 -1
  154. glaip_sdk/models.py +0 -259
  155. glaip_sdk-0.1.0.dist-info/RECORD +0 -82
  156. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,204 @@
1
+ """Renderer state utilities and helpers.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime, timezone
12
+ from typing import Any
13
+ from collections.abc import Iterable
14
+
15
+
16
+ def coerce_received_at(value: Any) -> datetime | None:
17
+ """Coerce arbitrary values into timezone-aware datetimes if possible."""
18
+ if value is None:
19
+ return None
20
+
21
+ if isinstance(value, datetime):
22
+ return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
23
+
24
+ if isinstance(value, str):
25
+ try:
26
+ normalised = value.replace("Z", "+00:00")
27
+ dt = datetime.fromisoformat(normalised)
28
+ except ValueError:
29
+ return None
30
+ return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)
31
+
32
+ return None
33
+
34
+
35
+ def truncate_display(text: str | None, limit: int = 160) -> str:
36
+ """Return text capped at the given character limit with ellipsis."""
37
+ if not text:
38
+ return ""
39
+ stripped = str(text).strip()
40
+ if len(stripped) <= limit:
41
+ return stripped
42
+ return stripped[: limit - 1] + "…"
43
+
44
+
45
+ @dataclass(slots=True)
46
+ class TranscriptBuffer:
47
+ """Utility container for streaming transcript text."""
48
+
49
+ lines: list[str] = field(default_factory=list)
50
+
51
+ def append(self, value: str | None) -> None:
52
+ """Append a chunk of transcript text."""
53
+ if not value:
54
+ return
55
+ self.lines.append(value)
56
+
57
+ def extend(self, chunks: Iterable[str]) -> None:
58
+ """Append multiple chunks."""
59
+ for chunk in chunks:
60
+ self.append(chunk)
61
+
62
+ def clear(self) -> None:
63
+ """Reset the buffer."""
64
+ self.lines.clear()
65
+
66
+ def render(self) -> str:
67
+ """Return the concatenated transcript text."""
68
+ return "".join(self.lines)
69
+
70
+ def has_visible_text(self) -> bool:
71
+ """Return True when any chunk contains non-whitespace characters."""
72
+ return any(chunk and chunk.strip() for chunk in self.lines)
73
+
74
+ def __bool__(self) -> bool:
75
+ """Allow truthiness checks like a regular list."""
76
+ return bool(self.lines)
77
+
78
+ def __len__(self) -> int:
79
+ """Return buffered chunk count."""
80
+ return len(self.lines)
81
+
82
+ def __iter__(self):
83
+ """Iterate over buffered chunks."""
84
+ return iter(self.lines)
85
+
86
+ def __getitem__(self, index: int) -> str:
87
+ """Return the chunk at the requested index."""
88
+ return self.lines[index]
89
+
90
+ def __contains__(self, item: object) -> bool:
91
+ """Membership test for convenience."""
92
+ return item in self.lines
93
+
94
+
95
+ @dataclass
96
+ class RendererState:
97
+ """Internal state for the renderer."""
98
+
99
+ buffer: TranscriptBuffer = field(default_factory=TranscriptBuffer)
100
+ final_text: str = ""
101
+ streaming_started_at: float | None = None
102
+ printed_final_output: bool = False
103
+ finalizing_ui: bool = False
104
+ final_duration_seconds: float | None = None
105
+ final_duration_text: str | None = None
106
+ events: list[dict[str, Any]] = field(default_factory=list)
107
+ meta: dict[str, Any] = field(default_factory=dict)
108
+ streaming_started_event_ts: datetime | None = None
109
+
110
+ def record_event(self, event: dict[str, Any], *, received_at: datetime | None = None) -> None:
111
+ """Capture an event snapshot for transcript replay."""
112
+ try:
113
+ captured = json.loads(json.dumps(event))
114
+ except Exception:
115
+ captured = dict(event)
116
+
117
+ if received_at is not None:
118
+ try:
119
+ captured["received_at"] = received_at.isoformat()
120
+ except Exception:
121
+ try:
122
+ captured["received_at"] = str(received_at)
123
+ except Exception:
124
+ captured["received_at"] = repr(received_at)
125
+
126
+ self.events.append(captured)
127
+
128
+ def set_final_output(self, value: str) -> None:
129
+ """Record the final assistant output."""
130
+ self.final_text = value
131
+
132
+ def append_transcript_text(self, value: str) -> None:
133
+ """Append streaming text to the transcript buffer."""
134
+ self.buffer.append(value)
135
+
136
+ def start_stream_timer(self, now: float | None) -> None:
137
+ """Record start timestamp when streaming begins."""
138
+ if now is None or self.streaming_started_at is not None:
139
+ return
140
+ self.streaming_started_at = now
141
+
142
+ def stop_stream_timer(self, now: float | None) -> float | None:
143
+ """Record the total elapsed duration."""
144
+ if now is None or self.streaming_started_at is None:
145
+ return None
146
+ duration = max(0.0, now - self.streaming_started_at)
147
+ self.final_duration_seconds = duration
148
+ return duration
149
+
150
+ def mark_final_duration(self, duration: float | None, *, formatted: str | None = None) -> None:
151
+ """Store the final duration metadata."""
152
+ if duration is not None:
153
+ self.final_duration_seconds = duration
154
+ self.final_duration_text = formatted
155
+
156
+ def to_snapshot(self) -> dict[str, Any]:
157
+ """Return a serialisable snapshot for presenters."""
158
+ return prepare_transcript_snapshot(self)
159
+
160
+
161
+ @dataclass
162
+ class ThinkingScopeState:
163
+ """Runtime bookkeeping for deterministic thinking spans."""
164
+
165
+ anchor_id: str
166
+ task_id: str | None
167
+ context_id: str | None
168
+ anchor_started_at: float | None = None
169
+ anchor_finished_at: float | None = None
170
+ idle_started_at: float | None = None
171
+ idle_started_monotonic: float | None = None
172
+ active_thinking_id: str | None = None
173
+ running_children: set[str] = field(default_factory=set)
174
+ closed: bool = False
175
+
176
+
177
+ def accumulate_final_text(*, state: RendererState) -> str:
178
+ """Return the most relevant final text for summary panels."""
179
+ if state.final_text.strip():
180
+ return state.final_text.strip()
181
+ return state.buffer.render().strip()
182
+
183
+
184
+ def prepare_transcript_snapshot(state: RendererState) -> dict[str, Any]:
185
+ """Return a dictionary capturing renderer transcript state."""
186
+ return {
187
+ "final_text": state.final_text,
188
+ "buffer_text": state.buffer.render(),
189
+ "events": list(state.events),
190
+ "meta": dict(state.meta),
191
+ "final_duration_seconds": state.final_duration_seconds,
192
+ "final_duration_text": state.final_duration_text,
193
+ }
194
+
195
+
196
+ __all__ = [
197
+ "RendererState",
198
+ "ThinkingScopeState",
199
+ "TranscriptBuffer",
200
+ "accumulate_final_text",
201
+ "coerce_received_at",
202
+ "prepare_transcript_snapshot",
203
+ "truncate_display",
204
+ ]
@@ -20,9 +20,7 @@ class StepTreeState:
20
20
  root_order: list[str] = field(default_factory=list)
21
21
  child_map: dict[str, list[str]] = field(default_factory=dict)
22
22
  buffered_children: dict[str, list[str]] = field(default_factory=dict)
23
- running_by_context: dict[tuple[str | None, str | None], set[str]] = field(
24
- default_factory=dict
25
- )
23
+ running_by_context: dict[tuple[str | None, str | None], set[str]] = field(default_factory=dict)
26
24
  retained_ids: set[str] = field(default_factory=set)
27
25
  step_index: dict[str, Step] = field(default_factory=dict)
28
26
  pending_branch_failures: set[str] = field(default_factory=set)
@@ -0,0 +1,34 @@
1
+ """Step management and presentation helpers."""
2
+
3
+ from glaip_sdk.utils.rendering.steps.format import (
4
+ UNKNOWN_STEP_DETAIL,
5
+ STATUS_ICON_STYLES,
6
+ StepPresentation,
7
+ build_connector_prefix,
8
+ compose_display_label,
9
+ format_step,
10
+ format_step_label,
11
+ format_tool_args,
12
+ humanize_tool_name,
13
+ resolve_label_body,
14
+ status_icon_for_step,
15
+ step_icon_for_kind,
16
+ )
17
+ from glaip_sdk.utils.rendering.steps.manager import StepManager, StepManagerError
18
+
19
+ __all__ = [
20
+ "StepManager",
21
+ "StepManagerError",
22
+ "UNKNOWN_STEP_DETAIL",
23
+ "STATUS_ICON_STYLES",
24
+ "StepPresentation",
25
+ "build_connector_prefix",
26
+ "compose_display_label",
27
+ "format_step",
28
+ "format_step_label",
29
+ "format_tool_args",
30
+ "humanize_tool_name",
31
+ "resolve_label_body",
32
+ "status_icon_for_step",
33
+ "step_icon_for_kind",
34
+ ]