glaip-sdk 0.6.15b2__py3-none-any.whl → 0.6.15b3__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 (154) hide show
  1. glaip_sdk/agents/__init__.py +27 -0
  2. glaip_sdk/agents/base.py +1196 -0
  3. glaip_sdk/cli/__init__.py +9 -0
  4. glaip_sdk/cli/account_store.py +540 -0
  5. glaip_sdk/cli/agent_config.py +78 -0
  6. glaip_sdk/cli/auth.py +699 -0
  7. glaip_sdk/cli/commands/__init__.py +5 -0
  8. glaip_sdk/cli/commands/accounts.py +746 -0
  9. glaip_sdk/cli/commands/agents.py +1509 -0
  10. glaip_sdk/cli/commands/common_config.py +104 -0
  11. glaip_sdk/cli/commands/configure.py +896 -0
  12. glaip_sdk/cli/commands/mcps.py +1356 -0
  13. glaip_sdk/cli/commands/models.py +69 -0
  14. glaip_sdk/cli/commands/tools.py +576 -0
  15. glaip_sdk/cli/commands/transcripts.py +755 -0
  16. glaip_sdk/cli/commands/update.py +61 -0
  17. glaip_sdk/cli/config.py +95 -0
  18. glaip_sdk/cli/constants.py +38 -0
  19. glaip_sdk/cli/context.py +150 -0
  20. glaip_sdk/cli/core/__init__.py +79 -0
  21. glaip_sdk/cli/core/context.py +124 -0
  22. glaip_sdk/cli/core/output.py +851 -0
  23. glaip_sdk/cli/core/prompting.py +649 -0
  24. glaip_sdk/cli/core/rendering.py +187 -0
  25. glaip_sdk/cli/display.py +355 -0
  26. glaip_sdk/cli/hints.py +57 -0
  27. glaip_sdk/cli/io.py +112 -0
  28. glaip_sdk/cli/main.py +615 -0
  29. glaip_sdk/cli/masking.py +136 -0
  30. glaip_sdk/cli/mcp_validators.py +287 -0
  31. glaip_sdk/cli/pager.py +266 -0
  32. glaip_sdk/cli/parsers/__init__.py +7 -0
  33. glaip_sdk/cli/parsers/json_input.py +177 -0
  34. glaip_sdk/cli/resolution.py +67 -0
  35. glaip_sdk/cli/rich_helpers.py +27 -0
  36. glaip_sdk/cli/slash/__init__.py +15 -0
  37. glaip_sdk/cli/slash/accounts_controller.py +578 -0
  38. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  39. glaip_sdk/cli/slash/agent_session.py +285 -0
  40. glaip_sdk/cli/slash/prompt.py +256 -0
  41. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  42. glaip_sdk/cli/slash/session.py +1708 -0
  43. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  44. glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
  45. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  46. glaip_sdk/cli/slash/tui/loading.py +58 -0
  47. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  48. glaip_sdk/cli/transcript/__init__.py +31 -0
  49. glaip_sdk/cli/transcript/cache.py +536 -0
  50. glaip_sdk/cli/transcript/capture.py +329 -0
  51. glaip_sdk/cli/transcript/export.py +38 -0
  52. glaip_sdk/cli/transcript/history.py +815 -0
  53. glaip_sdk/cli/transcript/launcher.py +77 -0
  54. glaip_sdk/cli/transcript/viewer.py +374 -0
  55. glaip_sdk/cli/update_notifier.py +290 -0
  56. glaip_sdk/cli/utils.py +263 -0
  57. glaip_sdk/cli/validators.py +238 -0
  58. glaip_sdk/client/__init__.py +11 -0
  59. glaip_sdk/client/_agent_payloads.py +520 -0
  60. glaip_sdk/client/agent_runs.py +147 -0
  61. glaip_sdk/client/agents.py +1335 -0
  62. glaip_sdk/client/base.py +502 -0
  63. glaip_sdk/client/main.py +249 -0
  64. glaip_sdk/client/mcps.py +370 -0
  65. glaip_sdk/client/run_rendering.py +700 -0
  66. glaip_sdk/client/shared.py +21 -0
  67. glaip_sdk/client/tools.py +661 -0
  68. glaip_sdk/client/validators.py +198 -0
  69. glaip_sdk/config/constants.py +52 -0
  70. glaip_sdk/mcps/__init__.py +21 -0
  71. glaip_sdk/mcps/base.py +345 -0
  72. glaip_sdk/models/__init__.py +90 -0
  73. glaip_sdk/models/agent.py +47 -0
  74. glaip_sdk/models/agent_runs.py +116 -0
  75. glaip_sdk/models/common.py +42 -0
  76. glaip_sdk/models/mcp.py +33 -0
  77. glaip_sdk/models/tool.py +33 -0
  78. glaip_sdk/payload_schemas/__init__.py +7 -0
  79. glaip_sdk/payload_schemas/agent.py +85 -0
  80. glaip_sdk/registry/__init__.py +55 -0
  81. glaip_sdk/registry/agent.py +164 -0
  82. glaip_sdk/registry/base.py +139 -0
  83. glaip_sdk/registry/mcp.py +253 -0
  84. glaip_sdk/registry/tool.py +232 -0
  85. glaip_sdk/runner/__init__.py +59 -0
  86. glaip_sdk/runner/base.py +84 -0
  87. glaip_sdk/runner/deps.py +112 -0
  88. glaip_sdk/runner/langgraph.py +782 -0
  89. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  90. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  91. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
  92. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  93. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  94. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  95. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
  96. glaip_sdk/tools/__init__.py +22 -0
  97. glaip_sdk/tools/base.py +435 -0
  98. glaip_sdk/utils/__init__.py +86 -0
  99. glaip_sdk/utils/a2a/__init__.py +34 -0
  100. glaip_sdk/utils/a2a/event_processor.py +188 -0
  101. glaip_sdk/utils/agent_config.py +194 -0
  102. glaip_sdk/utils/bundler.py +267 -0
  103. glaip_sdk/utils/client.py +111 -0
  104. glaip_sdk/utils/client_utils.py +486 -0
  105. glaip_sdk/utils/datetime_helpers.py +58 -0
  106. glaip_sdk/utils/discovery.py +78 -0
  107. glaip_sdk/utils/display.py +135 -0
  108. glaip_sdk/utils/export.py +143 -0
  109. glaip_sdk/utils/general.py +61 -0
  110. glaip_sdk/utils/import_export.py +168 -0
  111. glaip_sdk/utils/import_resolver.py +492 -0
  112. glaip_sdk/utils/instructions.py +101 -0
  113. glaip_sdk/utils/rendering/__init__.py +115 -0
  114. glaip_sdk/utils/rendering/formatting.py +264 -0
  115. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  116. glaip_sdk/utils/rendering/layout/panels.py +156 -0
  117. glaip_sdk/utils/rendering/layout/progress.py +202 -0
  118. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  119. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  120. glaip_sdk/utils/rendering/models.py +85 -0
  121. glaip_sdk/utils/rendering/renderer/__init__.py +55 -0
  122. glaip_sdk/utils/rendering/renderer/base.py +1024 -0
  123. glaip_sdk/utils/rendering/renderer/config.py +27 -0
  124. glaip_sdk/utils/rendering/renderer/console.py +55 -0
  125. glaip_sdk/utils/rendering/renderer/debug.py +178 -0
  126. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  127. glaip_sdk/utils/rendering/renderer/stream.py +202 -0
  128. glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
  129. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  130. glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
  131. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  132. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  133. glaip_sdk/utils/rendering/state.py +204 -0
  134. glaip_sdk/utils/rendering/step_tree_state.py +100 -0
  135. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  136. glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
  137. glaip_sdk/utils/rendering/steps/format.py +176 -0
  138. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  139. glaip_sdk/utils/rendering/timing.py +36 -0
  140. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  141. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  142. glaip_sdk/utils/resource_refs.py +195 -0
  143. glaip_sdk/utils/run_renderer.py +41 -0
  144. glaip_sdk/utils/runtime_config.py +425 -0
  145. glaip_sdk/utils/serialization.py +424 -0
  146. glaip_sdk/utils/sync.py +142 -0
  147. glaip_sdk/utils/tool_detection.py +33 -0
  148. glaip_sdk/utils/validation.py +264 -0
  149. {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.15b3.dist-info}/METADATA +1 -1
  150. glaip_sdk-0.6.15b3.dist-info/RECORD +160 -0
  151. glaip_sdk-0.6.15b2.dist-info/RECORD +0 -12
  152. {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.15b3.dist-info}/WHEEL +0 -0
  153. {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.15b3.dist-info}/entry_points.txt +0 -0
  154. {glaip_sdk-0.6.15b2.dist-info → glaip_sdk-0.6.15b3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,329 @@
1
+ """Helpers for capturing and caching agent run transcripts.
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
11
+ from io import StringIO
12
+ from typing import Any
13
+
14
+ from rich.console import Console
15
+
16
+ from glaip_sdk.cli.auth import resolve_api_url_from_context
17
+ from glaip_sdk.cli.context import get_ctx_value
18
+ from glaip_sdk.cli.transcript.cache import (
19
+ TranscriptPayload,
20
+ TranscriptStoreResult,
21
+ store_transcript,
22
+ )
23
+ from glaip_sdk.cli.transcript.cache import (
24
+ build_payload as build_transcript_payload,
25
+ )
26
+ from glaip_sdk.utils.rendering.layout.progress import format_tool_title
27
+
28
+
29
+ @dataclass(slots=True)
30
+ class StoredTranscriptContext:
31
+ """Simple container linking payload and manifest data."""
32
+
33
+ payload: TranscriptPayload
34
+ store_result: TranscriptStoreResult
35
+
36
+
37
+ def coerce_events(value: Any) -> list[dict[str, Any]]:
38
+ """Normalise renderer events into a list of dictionaries."""
39
+ if not value:
40
+ return []
41
+ if isinstance(value, list):
42
+ return [item for item in value if isinstance(item, dict)]
43
+ try:
44
+ return [item for item in value if isinstance(item, dict)]
45
+ except Exception:
46
+ return []
47
+
48
+
49
+ def coerce_result_text(result: Any) -> str:
50
+ """Serialise renderer output to a string for transcript payloads."""
51
+ if result is None:
52
+ return ""
53
+ if isinstance(result, str):
54
+ return result
55
+ try:
56
+ return json.dumps(result, ensure_ascii=False, indent=2)
57
+ except Exception:
58
+ return str(result)
59
+
60
+
61
+ def compute_finished_at(renderer: Any) -> float | None:
62
+ """Best-effort end-time calculation based on renderer state."""
63
+ state = getattr(renderer, "state", None)
64
+ if state is not None:
65
+ started_at = getattr(state, "streaming_started_at", None)
66
+ duration = getattr(state, "final_duration_seconds", None)
67
+ else:
68
+ started_at = None
69
+ duration = None
70
+
71
+ if started_at is None:
72
+ stream_processor = getattr(renderer, "stream_processor", None)
73
+ started_at = getattr(stream_processor, "streaming_started_at", None) if stream_processor is not None else None
74
+ if started_at is None or duration is None:
75
+ return None
76
+ try:
77
+ return float(started_at) + float(duration)
78
+ except Exception:
79
+ return None
80
+
81
+
82
+ def extract_server_run_id(meta: dict[str, Any], events: list[dict[str, Any]]) -> str | None:
83
+ """Derive a server-side run identifier from renderer metadata."""
84
+ run_id = meta.get("run_id") or meta.get("id")
85
+ if run_id:
86
+ return str(run_id)
87
+ for event in events:
88
+ metadata = event.get("metadata") or {}
89
+ candidate = metadata.get("run_id") or metadata.get("request_id")
90
+ if candidate:
91
+ return str(candidate)
92
+ return None
93
+
94
+
95
+ def _coerce_meta(meta: Any) -> dict[str, Any]:
96
+ """Ensure renderer metadata is recorded as a plain dictionary."""
97
+ if meta is None:
98
+ return {}
99
+ if isinstance(meta, dict):
100
+ return meta
101
+ if hasattr(meta, "items"):
102
+ try:
103
+ return {str(key): value for key, value in meta.items()}
104
+ except Exception:
105
+ pass
106
+ return {"value": coerce_result_text(meta)}
107
+
108
+
109
+ def register_last_transcript(ctx: Any, payload: TranscriptPayload, store_result: TranscriptStoreResult) -> None:
110
+ """Persist last-run transcript references onto the Click context."""
111
+ ctx_obj = getattr(ctx, "obj", None)
112
+ if not isinstance(ctx_obj, dict):
113
+ return
114
+ ctx_obj["_last_transcript_payload"] = payload
115
+ ctx_obj["_last_transcript_manifest"] = store_result.manifest_entry
116
+ ctx_obj["_last_transcript_path"] = str(store_result.path)
117
+
118
+
119
+ def _resolve_api_url(ctx: Any) -> str | None:
120
+ """Resolve API URL from context or account store (CLI/palette ignores env creds)."""
121
+ return resolve_api_url_from_context(
122
+ ctx,
123
+ get_api_url=lambda c: get_ctx_value(c, "api_url"),
124
+ get_account_name=lambda c: get_ctx_value(c, "account_name"),
125
+ )
126
+
127
+
128
+ def _extract_step_summaries(renderer: Any) -> list[dict[str, Any]]:
129
+ """Return lightweight step summaries for the transcript viewer."""
130
+ steps = getattr(renderer, "steps", None)
131
+ if steps is None:
132
+ return []
133
+
134
+ order = getattr(steps, "order", []) or []
135
+ by_id = getattr(steps, "by_id", {}) or {}
136
+
137
+ return [
138
+ _build_step_summary(by_id.get(step_id), index)
139
+ for index, step_id in enumerate(order)
140
+ if by_id.get(step_id) is not None
141
+ ]
142
+
143
+
144
+ def _build_step_summary(step: Any, index: int) -> dict[str, Any]:
145
+ """Construct a single step summary entry."""
146
+ kind = getattr(step, "kind", "") or ""
147
+ name = getattr(step, "name", "") or ""
148
+ status = getattr(step, "status", "") or ""
149
+ duration_ms = _coerce_duration_ms(getattr(step, "duration_ms", None))
150
+ display_name = _format_step_display_name(name)
151
+
152
+ return {
153
+ "index": index,
154
+ "step_id": getattr(step, "step_id", f"step-{index}"),
155
+ "kind": kind,
156
+ "name": name,
157
+ "display_name": display_name,
158
+ "status": status,
159
+ "duration_ms": duration_ms,
160
+ }
161
+
162
+
163
+ def _coerce_duration_ms(value: Any) -> int | None:
164
+ """Return duration in milliseconds if numeric, otherwise None."""
165
+ try:
166
+ if isinstance(value, (int, float)):
167
+ return int(value)
168
+ except Exception:
169
+ return None
170
+ return None
171
+
172
+
173
+ def _format_step_display_name(name: str) -> str:
174
+ """Apply tool title formatting with a safe fallback."""
175
+ try:
176
+ return format_tool_title(name)
177
+ except Exception:
178
+ return name
179
+
180
+
181
+ def _extract_step_summary_lines(renderer: Any) -> list[str]:
182
+ """Render the live steps summary to plain text lines."""
183
+ if not hasattr(renderer, "_render_steps_text"):
184
+ return []
185
+
186
+ try:
187
+ renderable = renderer._render_steps_text()
188
+ except Exception:
189
+ return []
190
+
191
+ buffer = StringIO()
192
+ console = Console(file=buffer, record=True, force_terminal=False, width=120)
193
+ try:
194
+ console.print(renderable)
195
+ except Exception:
196
+ return []
197
+
198
+ text = console.export_text() or buffer.getvalue()
199
+ lines = [line.rstrip() for line in text.splitlines()]
200
+ half = len(lines) // 2
201
+ if half and lines[:half] == lines[half : half * 2]:
202
+ return lines[:half]
203
+ start = 0
204
+ prefixes = ("🤖", "🔧", "💭", "├", "└", "│", "•")
205
+ for idx, line in enumerate(lines):
206
+ if line.lstrip().startswith(prefixes):
207
+ start = idx
208
+ break
209
+ trimmed = lines[start:]
210
+ return [line for line in trimmed if line]
211
+
212
+
213
+ def _collect_renderer_outputs(
214
+ renderer: Any, final_result: Any
215
+ ) -> tuple[
216
+ list[dict[str, Any]],
217
+ str,
218
+ str,
219
+ ]:
220
+ """Collect events and text outputs from a renderer with safe fallbacks."""
221
+ events_raw = []
222
+ if hasattr(renderer, "get_transcript_events"):
223
+ try:
224
+ events_raw = renderer.get_transcript_events()
225
+ except Exception:
226
+ events_raw = []
227
+ events = coerce_events(events_raw)
228
+
229
+ aggregated_raw = ""
230
+ if hasattr(renderer, "get_aggregated_output"):
231
+ try:
232
+ aggregated_raw = renderer.get_aggregated_output()
233
+ except Exception:
234
+ aggregated_raw = ""
235
+
236
+ aggregated_output = coerce_result_text(aggregated_raw)
237
+ final_output = coerce_result_text(final_result)
238
+ return events, aggregated_output, final_output
239
+
240
+
241
+ def _derive_transcript_meta(
242
+ renderer: Any, model: str | None
243
+ ) -> tuple[dict[str, Any], float | None, float | None, str | None]:
244
+ """Build transcript metadata including step summaries and timings."""
245
+ raw_meta = getattr(getattr(renderer, "state", None), "meta", {}) or {}
246
+ meta = _coerce_meta(raw_meta)
247
+
248
+ step_summaries = _extract_step_summaries(renderer)
249
+ if step_summaries:
250
+ meta["transcript_steps"] = step_summaries
251
+
252
+ step_lines = _extract_step_summary_lines(renderer)
253
+ if step_lines:
254
+ meta["transcript_step_lines"] = step_lines
255
+
256
+ stream_processor = getattr(renderer, "stream_processor", None)
257
+ stream_started_at = (
258
+ getattr(stream_processor, "streaming_started_at", None) if stream_processor is not None else None
259
+ )
260
+ finished_at = compute_finished_at(renderer)
261
+ state = getattr(renderer, "state", None)
262
+ if state is not None:
263
+ duration_hint = getattr(state, "final_duration_seconds", None)
264
+ if duration_hint is not None:
265
+ try:
266
+ meta["final_duration_seconds"] = float(duration_hint)
267
+ except Exception:
268
+ pass
269
+ model_name = meta.get("model") or model
270
+ return meta, stream_started_at, finished_at, model_name
271
+
272
+
273
+ def store_transcript_for_session(
274
+ ctx: Any,
275
+ renderer: Any,
276
+ *,
277
+ final_result: Any,
278
+ agent_id: str | None,
279
+ agent_name: str | None,
280
+ model: str | None,
281
+ source: str,
282
+ ) -> StoredTranscriptContext | None:
283
+ """Capture renderer output and persist the transcript for later reuse."""
284
+ if not hasattr(renderer, "get_transcript_events"):
285
+ return None
286
+
287
+ events, aggregated_output, final_output = _collect_renderer_outputs(renderer, final_result)
288
+
289
+ if not (events or aggregated_output or final_output):
290
+ return None
291
+
292
+ meta, stream_started_at, finished_at, model_name = _derive_transcript_meta(renderer, model)
293
+
294
+ try:
295
+ api_url = _resolve_api_url(ctx)
296
+ except Exception:
297
+ api_url = None
298
+ if api_url:
299
+ meta["api_url"] = api_url
300
+
301
+ payload: TranscriptPayload = build_transcript_payload(
302
+ events=events,
303
+ renderer_output=aggregated_output,
304
+ final_output=final_output,
305
+ agent_id=agent_id,
306
+ agent_name=agent_name,
307
+ model=model_name,
308
+ server_run_id=extract_server_run_id(meta, events),
309
+ started_at=stream_started_at,
310
+ finished_at=finished_at,
311
+ meta=meta,
312
+ source=source,
313
+ )
314
+
315
+ store_result = store_transcript(payload)
316
+ register_last_transcript(ctx, payload, store_result)
317
+
318
+ return StoredTranscriptContext(payload=payload, store_result=store_result)
319
+
320
+
321
+ __all__ = [
322
+ "StoredTranscriptContext",
323
+ "coerce_events",
324
+ "coerce_result_text",
325
+ "compute_finished_at",
326
+ "extract_server_run_id",
327
+ "register_last_transcript",
328
+ "store_transcript_for_session",
329
+ ]
@@ -0,0 +1,38 @@
1
+ """Shared helpers for transcript export workflows.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from glaip_sdk.cli.transcript.cache import (
13
+ latest_manifest_entry,
14
+ resolve_manifest_entry,
15
+ )
16
+
17
+
18
+ def resolve_manifest_for_export(ctx: Any, run_id: str | None) -> dict[str, Any] | None:
19
+ """Resolve a manifest entry for export based on run id or recent context."""
20
+ if run_id:
21
+ return resolve_manifest_entry(run_id)
22
+
23
+ ctx_obj = ctx if isinstance(ctx, dict) else getattr(ctx, "obj", None)
24
+ if isinstance(ctx_obj, dict):
25
+ candidate = ctx_obj.get("_last_transcript_manifest")
26
+ if isinstance(candidate, dict):
27
+ return candidate
28
+
29
+ return latest_manifest_entry()
30
+
31
+
32
+ def normalise_export_destination(path: Path) -> Path:
33
+ """Return an absolute path for the export destination."""
34
+ expanded = path.expanduser()
35
+ return expanded if expanded.is_absolute() else Path.cwd() / expanded
36
+
37
+
38
+ __all__ = ["resolve_manifest_for_export", "normalise_export_destination"]