glaip-sdk 0.6.11__py3-none-any.whl → 0.6.14__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 +42 -5
- {glaip_sdk-0.6.11.dist-info → glaip_sdk-0.6.14.dist-info}/METADATA +31 -37
- glaip_sdk-0.6.14.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.11.dist-info → glaip_sdk-0.6.14.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.14.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.14.dist-info/top_level.txt +1 -0
- glaip_sdk/agents/__init__.py +0 -27
- glaip_sdk/agents/base.py +0 -1191
- glaip_sdk/cli/__init__.py +0 -9
- glaip_sdk/cli/account_store.py +0 -540
- glaip_sdk/cli/agent_config.py +0 -78
- glaip_sdk/cli/auth.py +0 -699
- glaip_sdk/cli/commands/__init__.py +0 -5
- glaip_sdk/cli/commands/accounts.py +0 -746
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/common_config.py +0 -101
- glaip_sdk/cli/commands/configure.py +0 -896
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/models.py +0 -69
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/commands/transcripts.py +0 -755
- glaip_sdk/cli/commands/update.py +0 -61
- glaip_sdk/cli/config.py +0 -95
- glaip_sdk/cli/constants.py +0 -38
- glaip_sdk/cli/context.py +0 -150
- glaip_sdk/cli/core/__init__.py +0 -79
- glaip_sdk/cli/core/context.py +0 -124
- glaip_sdk/cli/core/output.py +0 -846
- glaip_sdk/cli/core/prompting.py +0 -649
- glaip_sdk/cli/core/rendering.py +0 -187
- glaip_sdk/cli/display.py +0 -355
- glaip_sdk/cli/hints.py +0 -57
- glaip_sdk/cli/io.py +0 -112
- glaip_sdk/cli/main.py +0 -604
- glaip_sdk/cli/masking.py +0 -136
- glaip_sdk/cli/mcp_validators.py +0 -287
- glaip_sdk/cli/pager.py +0 -266
- glaip_sdk/cli/parsers/__init__.py +0 -7
- glaip_sdk/cli/parsers/json_input.py +0 -177
- glaip_sdk/cli/resolution.py +0 -67
- glaip_sdk/cli/rich_helpers.py +0 -27
- glaip_sdk/cli/slash/__init__.py +0 -15
- glaip_sdk/cli/slash/accounts_controller.py +0 -578
- glaip_sdk/cli/slash/accounts_shared.py +0 -75
- glaip_sdk/cli/slash/agent_session.py +0 -285
- glaip_sdk/cli/slash/prompt.py +0 -256
- glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
- glaip_sdk/cli/slash/session.py +0 -1708
- glaip_sdk/cli/slash/tui/__init__.py +0 -9
- glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
- glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
- glaip_sdk/cli/slash/tui/loading.py +0 -58
- glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
- glaip_sdk/cli/transcript/__init__.py +0 -31
- glaip_sdk/cli/transcript/cache.py +0 -536
- glaip_sdk/cli/transcript/capture.py +0 -329
- glaip_sdk/cli/transcript/export.py +0 -38
- glaip_sdk/cli/transcript/history.py +0 -815
- glaip_sdk/cli/transcript/launcher.py +0 -77
- glaip_sdk/cli/transcript/viewer.py +0 -374
- glaip_sdk/cli/update_notifier.py +0 -290
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk/cli/validators.py +0 -238
- glaip_sdk/client/__init__.py +0 -11
- glaip_sdk/client/_agent_payloads.py +0 -520
- glaip_sdk/client/agent_runs.py +0 -147
- glaip_sdk/client/agents.py +0 -1335
- glaip_sdk/client/base.py +0 -502
- glaip_sdk/client/main.py +0 -249
- glaip_sdk/client/mcps.py +0 -370
- glaip_sdk/client/run_rendering.py +0 -700
- glaip_sdk/client/shared.py +0 -21
- glaip_sdk/client/tools.py +0 -661
- glaip_sdk/client/validators.py +0 -198
- glaip_sdk/config/constants.py +0 -52
- glaip_sdk/mcps/__init__.py +0 -21
- glaip_sdk/mcps/base.py +0 -345
- glaip_sdk/models/__init__.py +0 -90
- glaip_sdk/models/agent.py +0 -47
- glaip_sdk/models/agent_runs.py +0 -116
- glaip_sdk/models/common.py +0 -42
- glaip_sdk/models/mcp.py +0 -33
- glaip_sdk/models/tool.py +0 -33
- glaip_sdk/payload_schemas/__init__.py +0 -7
- glaip_sdk/payload_schemas/agent.py +0 -85
- glaip_sdk/registry/__init__.py +0 -55
- glaip_sdk/registry/agent.py +0 -164
- glaip_sdk/registry/base.py +0 -139
- glaip_sdk/registry/mcp.py +0 -253
- glaip_sdk/registry/tool.py +0 -232
- glaip_sdk/runner/__init__.py +0 -59
- glaip_sdk/runner/base.py +0 -84
- glaip_sdk/runner/deps.py +0 -115
- glaip_sdk/runner/langgraph.py +0 -782
- glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
- glaip_sdk/runner/tool_adapter/__init__.py +0 -18
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
- glaip_sdk/tools/__init__.py +0 -22
- glaip_sdk/tools/base.py +0 -435
- glaip_sdk/utils/__init__.py +0 -86
- glaip_sdk/utils/a2a/__init__.py +0 -34
- glaip_sdk/utils/a2a/event_processor.py +0 -188
- glaip_sdk/utils/agent_config.py +0 -194
- glaip_sdk/utils/bundler.py +0 -267
- glaip_sdk/utils/client.py +0 -111
- glaip_sdk/utils/client_utils.py +0 -486
- glaip_sdk/utils/datetime_helpers.py +0 -58
- glaip_sdk/utils/discovery.py +0 -78
- glaip_sdk/utils/display.py +0 -135
- glaip_sdk/utils/export.py +0 -143
- glaip_sdk/utils/general.py +0 -61
- glaip_sdk/utils/import_export.py +0 -168
- glaip_sdk/utils/import_resolver.py +0 -492
- glaip_sdk/utils/instructions.py +0 -101
- glaip_sdk/utils/rendering/__init__.py +0 -115
- glaip_sdk/utils/rendering/formatting.py +0 -264
- glaip_sdk/utils/rendering/layout/__init__.py +0 -64
- glaip_sdk/utils/rendering/layout/panels.py +0 -156
- glaip_sdk/utils/rendering/layout/progress.py +0 -202
- glaip_sdk/utils/rendering/layout/summary.py +0 -74
- glaip_sdk/utils/rendering/layout/transcript.py +0 -606
- glaip_sdk/utils/rendering/models.py +0 -85
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
- glaip_sdk/utils/rendering/renderer/base.py +0 -1024
- glaip_sdk/utils/rendering/renderer/config.py +0 -27
- glaip_sdk/utils/rendering/renderer/console.py +0 -55
- glaip_sdk/utils/rendering/renderer/debug.py +0 -178
- glaip_sdk/utils/rendering/renderer/factory.py +0 -138
- glaip_sdk/utils/rendering/renderer/stream.py +0 -202
- glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
- glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
- glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
- glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
- glaip_sdk/utils/rendering/state.py +0 -204
- glaip_sdk/utils/rendering/step_tree_state.py +0 -100
- glaip_sdk/utils/rendering/steps/__init__.py +0 -34
- glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
- glaip_sdk/utils/rendering/steps/format.py +0 -176
- glaip_sdk/utils/rendering/steps/manager.py +0 -387
- glaip_sdk/utils/rendering/timing.py +0 -36
- glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
- glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
- glaip_sdk/utils/resource_refs.py +0 -195
- glaip_sdk/utils/run_renderer.py +0 -41
- glaip_sdk/utils/runtime_config.py +0 -425
- glaip_sdk/utils/serialization.py +0 -424
- glaip_sdk/utils/sync.py +0 -142
- glaip_sdk/utils/tool_detection.py +0 -33
- glaip_sdk/utils/validation.py +0 -264
- glaip_sdk-0.6.11.dist-info/RECORD +0 -159
- glaip_sdk-0.6.11.dist-info/entry_points.txt +0 -3
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"""Presentation helpers for rendering steps.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from typing import TYPE_CHECKING, Any
|
|
11
|
-
|
|
12
|
-
from glaip_sdk.icons import ICON_AGENT_STEP, ICON_DELEGATE, ICON_TOOL_STEP
|
|
13
|
-
from glaip_sdk.utils.rendering.formatting import glyph_for_status, normalise_display_label, pretty_args
|
|
14
|
-
from glaip_sdk.utils.rendering.models import Step
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING: # pragma: no cover - typing helpers only
|
|
17
|
-
from glaip_sdk.utils.rendering.layout.transcript import TranscriptGlyphs
|
|
18
|
-
|
|
19
|
-
UNKNOWN_STEP_DETAIL = "Unknown step detail"
|
|
20
|
-
STATUS_ICON_STYLES = {
|
|
21
|
-
"success": "green",
|
|
22
|
-
"failed": "red",
|
|
23
|
-
"warning": "yellow",
|
|
24
|
-
}
|
|
25
|
-
CONNECTOR_VERTICAL = "│ "
|
|
26
|
-
CONNECTOR_EMPTY = " "
|
|
27
|
-
CONNECTOR_BRANCH = "├─ "
|
|
28
|
-
CONNECTOR_LAST = "└─ "
|
|
29
|
-
ROOT_MARKER = ""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@dataclass(slots=True)
|
|
33
|
-
class StepPresentation:
|
|
34
|
-
"""Lightweight view model for formatted steps."""
|
|
35
|
-
|
|
36
|
-
step_id: str
|
|
37
|
-
title: str
|
|
38
|
-
glyph: str | None
|
|
39
|
-
status_style: str | None
|
|
40
|
-
args_text: str | None = None
|
|
41
|
-
failure_reason: str | None = None
|
|
42
|
-
duration_ms: int | None = None
|
|
43
|
-
status_text: str | None = None
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def humanize_tool_name(raw_name: str | None) -> str:
|
|
47
|
-
"""Return a user-facing name for a tool or agent identifier."""
|
|
48
|
-
if not raw_name:
|
|
49
|
-
return UNKNOWN_STEP_DETAIL
|
|
50
|
-
name = raw_name
|
|
51
|
-
if name.startswith("delegate_to_"):
|
|
52
|
-
name = name.removeprefix("delegate_to_")
|
|
53
|
-
elif name.startswith("delegate_"):
|
|
54
|
-
name = name.removeprefix("delegate_")
|
|
55
|
-
cleaned = name.replace("_", " ").replace("-", " ").strip()
|
|
56
|
-
if not cleaned:
|
|
57
|
-
return UNKNOWN_STEP_DETAIL
|
|
58
|
-
lowered = cleaned.lower()
|
|
59
|
-
return lowered[0].upper() + lowered[1:] if lowered else UNKNOWN_STEP_DETAIL
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def step_icon_for_kind(step_kind: str) -> str:
|
|
63
|
-
"""Return the icon prefix for a step kind."""
|
|
64
|
-
if step_kind == "agent":
|
|
65
|
-
return ICON_AGENT_STEP
|
|
66
|
-
if step_kind == "delegate":
|
|
67
|
-
return ICON_DELEGATE
|
|
68
|
-
if step_kind == "thinking":
|
|
69
|
-
return "💭"
|
|
70
|
-
return ICON_TOOL_STEP
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def resolve_label_body(step_kind: str, tool_name: str | None, metadata: dict[str, Any]) -> str:
|
|
74
|
-
"""Resolve the textual body for a step label."""
|
|
75
|
-
if step_kind == "thinking":
|
|
76
|
-
thinking_text = metadata.get("thinking_and_activity_info")
|
|
77
|
-
if isinstance(thinking_text, str) and thinking_text.strip():
|
|
78
|
-
return thinking_text.strip()
|
|
79
|
-
return "Thinking…"
|
|
80
|
-
|
|
81
|
-
if step_kind == "delegate":
|
|
82
|
-
return humanize_tool_name(tool_name)
|
|
83
|
-
|
|
84
|
-
if step_kind == "agent":
|
|
85
|
-
agent_name = metadata.get("agent_name")
|
|
86
|
-
if isinstance(agent_name, str) and agent_name.strip():
|
|
87
|
-
return agent_name.strip()
|
|
88
|
-
|
|
89
|
-
return humanize_tool_name(tool_name)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def compose_display_label(
|
|
93
|
-
step_kind: str,
|
|
94
|
-
tool_name: str | None,
|
|
95
|
-
args: dict[str, Any],
|
|
96
|
-
metadata: dict[str, Any],
|
|
97
|
-
) -> str:
|
|
98
|
-
"""Compose the display label for a step using tool metadata."""
|
|
99
|
-
icon = step_icon_for_kind(step_kind)
|
|
100
|
-
body = resolve_label_body(step_kind, tool_name, metadata)
|
|
101
|
-
label = f"{icon} {body}".strip()
|
|
102
|
-
if isinstance(args, dict) and args:
|
|
103
|
-
label = f"{label} —"
|
|
104
|
-
return label or UNKNOWN_STEP_DETAIL
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def status_icon_for_step(step: Step) -> str:
|
|
108
|
-
"""Return the canonical status icon key for a step."""
|
|
109
|
-
if step.status == "failed":
|
|
110
|
-
return "failed"
|
|
111
|
-
if step.branch_failed:
|
|
112
|
-
return "warning"
|
|
113
|
-
if step.status == "finished":
|
|
114
|
-
return "success"
|
|
115
|
-
if step.status == "stopped":
|
|
116
|
-
return "warning"
|
|
117
|
-
return "spinner"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def format_step_label(step: Step) -> str:
|
|
121
|
-
"""Return the normalized display label for a step."""
|
|
122
|
-
label = normalise_display_label(getattr(step, "display_label", None))
|
|
123
|
-
if label and label != UNKNOWN_STEP_DETAIL:
|
|
124
|
-
return label
|
|
125
|
-
metadata = getattr(step, "metadata", {}) or {}
|
|
126
|
-
computed = compose_display_label(step.kind, getattr(step, "name", None), getattr(step, "args", {}), metadata)
|
|
127
|
-
return normalise_display_label(computed)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def format_tool_args(step: Step, max_len: int = 160) -> str | None:
|
|
131
|
-
"""Return a pretty-printed args summary for a step."""
|
|
132
|
-
if not step.args:
|
|
133
|
-
return None
|
|
134
|
-
try:
|
|
135
|
-
return pretty_args(step.args, max_len=max_len)
|
|
136
|
-
except Exception:
|
|
137
|
-
return None
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def format_step(
|
|
141
|
-
step: Step,
|
|
142
|
-
*,
|
|
143
|
-
glyphs: TranscriptGlyphs | None = None,
|
|
144
|
-
label: str | None = None,
|
|
145
|
-
) -> StepPresentation:
|
|
146
|
-
"""Return a StepPresentation for downstream transcript rendering."""
|
|
147
|
-
del glyphs # Reserved for future glyph customisation hooks
|
|
148
|
-
if label:
|
|
149
|
-
resolved_label = normalise_display_label(label)
|
|
150
|
-
else:
|
|
151
|
-
resolved_label = format_step_label(step)
|
|
152
|
-
glyph_key = status_icon_for_step(step)
|
|
153
|
-
glyph = glyph_for_status(glyph_key)
|
|
154
|
-
style = STATUS_ICON_STYLES.get(glyph_key)
|
|
155
|
-
failure_reason = (step.failure_reason or "").strip() or None
|
|
156
|
-
return StepPresentation(
|
|
157
|
-
step_id=step.step_id,
|
|
158
|
-
title=resolved_label,
|
|
159
|
-
glyph=glyph,
|
|
160
|
-
status_style=style,
|
|
161
|
-
args_text=format_tool_args(step),
|
|
162
|
-
failure_reason=failure_reason,
|
|
163
|
-
duration_ms=getattr(step, "duration_ms", None),
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def build_connector_prefix(branch_state: tuple[bool, ...]) -> str:
|
|
168
|
-
"""Build connector prefix for a tree line based on ancestry state."""
|
|
169
|
-
if not branch_state:
|
|
170
|
-
return ROOT_MARKER
|
|
171
|
-
|
|
172
|
-
parts: list[str] = []
|
|
173
|
-
for ancestor_is_last in branch_state[:-1]:
|
|
174
|
-
parts.append(CONNECTOR_EMPTY if ancestor_is_last else CONNECTOR_VERTICAL)
|
|
175
|
-
parts.append(CONNECTOR_LAST if branch_state[-1] else CONNECTOR_BRANCH)
|
|
176
|
-
return "".join(parts)
|
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
"""Rendering utilities.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Iterator
|
|
10
|
-
|
|
11
|
-
from glaip_sdk.utils.rendering.models import Step
|
|
12
|
-
from glaip_sdk.utils.rendering.step_tree_state import StepTreeState
|
|
13
|
-
from glaip_sdk.utils.rendering.steps.event_processor import StepEventMixin
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class StepManagerError(Exception):
|
|
17
|
-
"""Raised when invalid operations are attempted on the step tree."""
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class StepManager(StepEventMixin):
|
|
21
|
-
"""Manages the lifecycle and organization of execution steps.
|
|
22
|
-
|
|
23
|
-
Tracks step creation, parent-child relationships, and execution state
|
|
24
|
-
with automatic pruning of old steps when limits are reached.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, max_steps: int = 200) -> None:
|
|
28
|
-
"""Initialize the step manager.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
max_steps: Maximum number of steps to retain before pruning
|
|
32
|
-
"""
|
|
33
|
-
normalised_max = int(max_steps) if isinstance(max_steps, (int, float)) else 0
|
|
34
|
-
self.state = StepTreeState(max_steps=normalised_max)
|
|
35
|
-
self.by_id: dict[str, Step] = self.state.step_index
|
|
36
|
-
self.key_index: dict[tuple, str] = {}
|
|
37
|
-
self.slot_counter: dict[tuple, int] = {}
|
|
38
|
-
self.max_steps = normalised_max
|
|
39
|
-
self._last_running: dict[tuple, str] = {}
|
|
40
|
-
self._step_aliases: dict[str, str] = {}
|
|
41
|
-
self.root_agent_id: str | None = None
|
|
42
|
-
self._scope_anchors: dict[str, list[str]] = {}
|
|
43
|
-
self._step_scope_map: dict[str, str] = {}
|
|
44
|
-
|
|
45
|
-
def set_root_agent(self, agent_id: str | None) -> None:
|
|
46
|
-
"""Record the root agent identifier for scope-aware parenting."""
|
|
47
|
-
if isinstance(agent_id, str) and agent_id.strip():
|
|
48
|
-
self.root_agent_id = agent_id.strip()
|
|
49
|
-
|
|
50
|
-
def _alloc_slot(
|
|
51
|
-
self,
|
|
52
|
-
task_id: str | None,
|
|
53
|
-
context_id: str | None,
|
|
54
|
-
kind: str,
|
|
55
|
-
name: str,
|
|
56
|
-
) -> int:
|
|
57
|
-
k = (task_id, context_id, kind, name)
|
|
58
|
-
self.slot_counter[k] = self.slot_counter.get(k, 0) + 1
|
|
59
|
-
return self.slot_counter[k]
|
|
60
|
-
|
|
61
|
-
def _key(
|
|
62
|
-
self,
|
|
63
|
-
task_id: str | None,
|
|
64
|
-
context_id: str | None,
|
|
65
|
-
kind: str,
|
|
66
|
-
name: str,
|
|
67
|
-
slot: int,
|
|
68
|
-
) -> tuple[str | None, str | None, str, str, int]:
|
|
69
|
-
return (task_id, context_id, kind, name, slot)
|
|
70
|
-
|
|
71
|
-
def _make_id(
|
|
72
|
-
self,
|
|
73
|
-
task_id: str | None,
|
|
74
|
-
context_id: str | None,
|
|
75
|
-
kind: str,
|
|
76
|
-
name: str,
|
|
77
|
-
slot: int,
|
|
78
|
-
) -> str:
|
|
79
|
-
return f"{task_id or 't'}::{context_id or 'c'}::{kind}::{name}::{slot}"
|
|
80
|
-
|
|
81
|
-
def start_or_get(
|
|
82
|
-
self,
|
|
83
|
-
*,
|
|
84
|
-
task_id: str | None,
|
|
85
|
-
context_id: str | None,
|
|
86
|
-
kind: str,
|
|
87
|
-
name: str,
|
|
88
|
-
parent_id: str | None = None,
|
|
89
|
-
args: dict[str, object] | None = None,
|
|
90
|
-
) -> Step:
|
|
91
|
-
"""Start a new step or return existing running step with same parameters.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
task_id: Task identifier
|
|
95
|
-
context_id: Context identifier
|
|
96
|
-
kind: Step kind (tool, delegate, agent)
|
|
97
|
-
name: Step name
|
|
98
|
-
parent_id: Parent step ID if this is a child step
|
|
99
|
-
args: Step arguments
|
|
100
|
-
|
|
101
|
-
Returns:
|
|
102
|
-
The Step instance (new or existing)
|
|
103
|
-
"""
|
|
104
|
-
existing = self.find_running(task_id=task_id, context_id=context_id, kind=kind, name=name)
|
|
105
|
-
if existing:
|
|
106
|
-
if args and existing.args != args:
|
|
107
|
-
existing.args = args
|
|
108
|
-
return existing
|
|
109
|
-
slot = self._alloc_slot(task_id, context_id, kind, name)
|
|
110
|
-
key = self._key(task_id, context_id, kind, name, slot)
|
|
111
|
-
step_id = self._make_id(task_id, context_id, kind, name, slot)
|
|
112
|
-
st = Step(
|
|
113
|
-
step_id=step_id,
|
|
114
|
-
kind=kind,
|
|
115
|
-
name=name,
|
|
116
|
-
parent_id=parent_id,
|
|
117
|
-
task_id=task_id,
|
|
118
|
-
context_id=context_id,
|
|
119
|
-
args=args or {},
|
|
120
|
-
)
|
|
121
|
-
self.by_id[step_id] = st
|
|
122
|
-
if parent_id:
|
|
123
|
-
self.children.setdefault(parent_id, []).append(step_id)
|
|
124
|
-
else:
|
|
125
|
-
self.order.append(step_id)
|
|
126
|
-
self.key_index[key] = step_id
|
|
127
|
-
self.state.retained_ids.add(step_id)
|
|
128
|
-
self._prune_steps()
|
|
129
|
-
self._last_running[(task_id, context_id, kind, name)] = step_id
|
|
130
|
-
return st
|
|
131
|
-
|
|
132
|
-
def _calculate_total_steps(self) -> int:
|
|
133
|
-
"""Calculate total number of steps."""
|
|
134
|
-
return len(self.order) + sum(len(v) for v in self.children.values())
|
|
135
|
-
|
|
136
|
-
def _get_subtree_size(self, root_id: str) -> int:
|
|
137
|
-
"""Get the size of a subtree (including root)."""
|
|
138
|
-
subtree = [root_id]
|
|
139
|
-
stack = list(self.children.get(root_id, []))
|
|
140
|
-
while stack:
|
|
141
|
-
x = stack.pop()
|
|
142
|
-
subtree.append(x)
|
|
143
|
-
stack.extend(self.children.get(x, []))
|
|
144
|
-
return len(subtree)
|
|
145
|
-
|
|
146
|
-
def _remove_subtree(self, root_id: str) -> None:
|
|
147
|
-
"""Remove a complete subtree from all data structures."""
|
|
148
|
-
for step_id in self._collect_subtree_ids(root_id):
|
|
149
|
-
self._purge_step_references(step_id)
|
|
150
|
-
|
|
151
|
-
def _collect_subtree_ids(self, root_id: str) -> list[str]:
|
|
152
|
-
"""Return a flat list of step ids contained within a subtree."""
|
|
153
|
-
stack = [root_id]
|
|
154
|
-
collected: list[str] = []
|
|
155
|
-
while stack:
|
|
156
|
-
sid = stack.pop()
|
|
157
|
-
collected.append(sid)
|
|
158
|
-
stack.extend(self.children.pop(sid, []))
|
|
159
|
-
return collected
|
|
160
|
-
|
|
161
|
-
def _purge_step_references(self, step_id: str) -> None:
|
|
162
|
-
"""Remove a single step id from all indexes and helper structures."""
|
|
163
|
-
st = self.by_id.pop(step_id, None)
|
|
164
|
-
if st:
|
|
165
|
-
key = (st.task_id, st.context_id, st.kind, st.name)
|
|
166
|
-
self._last_running.pop(key, None)
|
|
167
|
-
self.state.retained_ids.discard(step_id)
|
|
168
|
-
self.state.discard_running(step_id)
|
|
169
|
-
self._remove_parent_links(step_id)
|
|
170
|
-
if step_id in self.order:
|
|
171
|
-
self.order.remove(step_id)
|
|
172
|
-
self.state.buffered_children.pop(step_id, None)
|
|
173
|
-
self.state.pending_branch_failures.discard(step_id)
|
|
174
|
-
|
|
175
|
-
def _remove_parent_links(self, child_id: str) -> None:
|
|
176
|
-
"""Detach a child id from any parent lists."""
|
|
177
|
-
for parent, kids in self.children.copy().items():
|
|
178
|
-
if child_id not in kids:
|
|
179
|
-
continue
|
|
180
|
-
kids.remove(child_id)
|
|
181
|
-
if not kids:
|
|
182
|
-
self.children.pop(parent, None)
|
|
183
|
-
|
|
184
|
-
def _should_prune_steps(self, total: int) -> bool:
|
|
185
|
-
"""Check if steps should be pruned."""
|
|
186
|
-
if self.max_steps <= 0:
|
|
187
|
-
return False
|
|
188
|
-
return total > self.max_steps
|
|
189
|
-
|
|
190
|
-
def _get_oldest_step_id(self) -> str | None:
|
|
191
|
-
"""Get the oldest step ID for pruning."""
|
|
192
|
-
return self.order[0] if self.order else None
|
|
193
|
-
|
|
194
|
-
def _prune_steps(self) -> None:
|
|
195
|
-
"""Prune steps when total exceeds maximum."""
|
|
196
|
-
total = self._calculate_total_steps()
|
|
197
|
-
if not self._should_prune_steps(total):
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
while self._should_prune_steps(total) and self.order:
|
|
201
|
-
sid = self._get_oldest_step_id()
|
|
202
|
-
if not sid:
|
|
203
|
-
break
|
|
204
|
-
|
|
205
|
-
subtree_size = self._get_subtree_size(sid)
|
|
206
|
-
self._remove_subtree(sid)
|
|
207
|
-
total -= subtree_size
|
|
208
|
-
|
|
209
|
-
def remove_step(self, step_id: str) -> None:
|
|
210
|
-
"""Remove a single step from the tree and cached indexes."""
|
|
211
|
-
step = self.by_id.pop(step_id, None)
|
|
212
|
-
if not step:
|
|
213
|
-
return
|
|
214
|
-
|
|
215
|
-
if step.parent_id:
|
|
216
|
-
self.state.unlink_child(step.parent_id, step_id)
|
|
217
|
-
else:
|
|
218
|
-
self.state.unlink_root(step_id)
|
|
219
|
-
|
|
220
|
-
self.children.pop(step_id, None)
|
|
221
|
-
self.state.buffered_children.pop(step_id, None)
|
|
222
|
-
self.state.retained_ids.discard(step_id)
|
|
223
|
-
self.state.pending_branch_failures.discard(step_id)
|
|
224
|
-
self.state.discard_running(step_id)
|
|
225
|
-
|
|
226
|
-
self.key_index = {key: sid for key, sid in self.key_index.items() if sid != step_id}
|
|
227
|
-
for key, last_sid in self._last_running.copy().items():
|
|
228
|
-
if last_sid == step_id:
|
|
229
|
-
self._last_running.pop(key, None)
|
|
230
|
-
|
|
231
|
-
aliases = [alias for alias, target in self._step_aliases.items() if alias == step_id or target == step_id]
|
|
232
|
-
for alias in aliases:
|
|
233
|
-
self._step_aliases.pop(alias, None)
|
|
234
|
-
|
|
235
|
-
def get_child_count(self, step_id: str) -> int:
|
|
236
|
-
"""Get the number of child steps for a given step.
|
|
237
|
-
|
|
238
|
-
Args:
|
|
239
|
-
step_id: The parent step ID
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
Number of child steps
|
|
243
|
-
"""
|
|
244
|
-
return len(self.children.get(step_id, []))
|
|
245
|
-
|
|
246
|
-
def find_running(
|
|
247
|
-
self,
|
|
248
|
-
*,
|
|
249
|
-
task_id: str | None,
|
|
250
|
-
context_id: str | None,
|
|
251
|
-
kind: str,
|
|
252
|
-
name: str,
|
|
253
|
-
) -> Step | None:
|
|
254
|
-
"""Find a currently running step with the given parameters.
|
|
255
|
-
|
|
256
|
-
Args:
|
|
257
|
-
task_id: Task identifier
|
|
258
|
-
context_id: Context identifier
|
|
259
|
-
kind: Step kind (tool, delegate, agent)
|
|
260
|
-
name: Step name
|
|
261
|
-
|
|
262
|
-
Returns:
|
|
263
|
-
The running Step if found, None otherwise
|
|
264
|
-
"""
|
|
265
|
-
key = (task_id, context_id, kind, name)
|
|
266
|
-
step_id = self._last_running.get(key)
|
|
267
|
-
if step_id:
|
|
268
|
-
st = self.by_id.get(step_id)
|
|
269
|
-
if st and st.status != "finished":
|
|
270
|
-
return st
|
|
271
|
-
for sid in reversed(list(self._iter_all_steps())):
|
|
272
|
-
st = self.by_id.get(sid)
|
|
273
|
-
if (
|
|
274
|
-
st
|
|
275
|
-
and (st.task_id, st.context_id, st.kind, st.name)
|
|
276
|
-
== (
|
|
277
|
-
task_id,
|
|
278
|
-
context_id,
|
|
279
|
-
kind,
|
|
280
|
-
name,
|
|
281
|
-
)
|
|
282
|
-
and st.status != "finished"
|
|
283
|
-
):
|
|
284
|
-
return st
|
|
285
|
-
return None
|
|
286
|
-
|
|
287
|
-
def list_ordered(self) -> list[Step]:
|
|
288
|
-
"""Return a depth-first list of steps currently tracked."""
|
|
289
|
-
ordered: list[Step] = []
|
|
290
|
-
for step_id, _branch_state in self.iter_tree():
|
|
291
|
-
step = self.by_id.get(step_id)
|
|
292
|
-
if step:
|
|
293
|
-
ordered.append(step)
|
|
294
|
-
return ordered
|
|
295
|
-
|
|
296
|
-
def prune_for_summary(self, limit: int) -> list[tuple[str, tuple[bool, ...]]]:
|
|
297
|
-
"""Return the most recent ``limit`` nodes for compact summaries."""
|
|
298
|
-
if limit < 0:
|
|
299
|
-
raise StepManagerError("limit must be non-negative")
|
|
300
|
-
nodes = list(self.iter_tree())
|
|
301
|
-
if limit == 0 or len(nodes) <= limit:
|
|
302
|
-
return nodes
|
|
303
|
-
return nodes[-limit:]
|
|
304
|
-
|
|
305
|
-
def finish(
|
|
306
|
-
self,
|
|
307
|
-
*,
|
|
308
|
-
task_id: str | None,
|
|
309
|
-
context_id: str | None,
|
|
310
|
-
kind: str,
|
|
311
|
-
name: str,
|
|
312
|
-
output: object | None = None,
|
|
313
|
-
duration_raw: float | None = None,
|
|
314
|
-
) -> Step:
|
|
315
|
-
"""Finish a step with the given parameters.
|
|
316
|
-
|
|
317
|
-
Args:
|
|
318
|
-
task_id: Task identifier
|
|
319
|
-
context_id: Context identifier
|
|
320
|
-
kind: Step kind (tool, delegate, agent)
|
|
321
|
-
name: Step name
|
|
322
|
-
output: Step output data
|
|
323
|
-
duration_raw: Raw duration in seconds
|
|
324
|
-
|
|
325
|
-
Returns:
|
|
326
|
-
The finished Step instance
|
|
327
|
-
|
|
328
|
-
Raises:
|
|
329
|
-
RuntimeError: If no matching step is found
|
|
330
|
-
"""
|
|
331
|
-
st = self.find_running(task_id=task_id, context_id=context_id, kind=kind, name=name)
|
|
332
|
-
if not st:
|
|
333
|
-
# Try to find any existing step with matching parameters, even if not running
|
|
334
|
-
for sid in reversed(list(self._iter_all_steps())):
|
|
335
|
-
st_check = self.by_id.get(sid)
|
|
336
|
-
if (
|
|
337
|
-
st_check
|
|
338
|
-
and st_check.task_id == task_id
|
|
339
|
-
and st_check.context_id == context_id
|
|
340
|
-
and st_check.kind == kind
|
|
341
|
-
and st_check.name == name
|
|
342
|
-
):
|
|
343
|
-
st = st_check
|
|
344
|
-
break
|
|
345
|
-
|
|
346
|
-
# If still no step found, create a new one
|
|
347
|
-
if not st:
|
|
348
|
-
st = self.start_or_get(task_id=task_id, context_id=context_id, kind=kind, name=name)
|
|
349
|
-
|
|
350
|
-
if output:
|
|
351
|
-
st.output = output
|
|
352
|
-
st.finish(duration_raw)
|
|
353
|
-
key = (task_id, context_id, kind, name)
|
|
354
|
-
if self._last_running.get(key) == st.step_id:
|
|
355
|
-
self._last_running.pop(key, None)
|
|
356
|
-
return st
|
|
357
|
-
|
|
358
|
-
def _iter_all_steps(self) -> Iterator[str]:
|
|
359
|
-
for root in self.order:
|
|
360
|
-
yield root
|
|
361
|
-
stack = list(self.children.get(root, []))
|
|
362
|
-
while stack:
|
|
363
|
-
sid = stack.pop()
|
|
364
|
-
yield sid
|
|
365
|
-
stack.extend(self.children.get(sid, []))
|
|
366
|
-
|
|
367
|
-
def iter_tree(self) -> Iterator[tuple[str, tuple[bool, ...]]]:
|
|
368
|
-
"""Expose depth-first traversal info for rendering."""
|
|
369
|
-
yield from self.state.iter_visible_tree()
|
|
370
|
-
|
|
371
|
-
@property
|
|
372
|
-
def order(self) -> list[str]:
|
|
373
|
-
"""Root step ordering accessor backed by StepTreeState."""
|
|
374
|
-
return self.state.root_order
|
|
375
|
-
|
|
376
|
-
@order.setter
|
|
377
|
-
def order(self, value: list[str]) -> None:
|
|
378
|
-
self.state.root_order = list(value)
|
|
379
|
-
|
|
380
|
-
@property
|
|
381
|
-
def children(self) -> dict[str, list[str]]:
|
|
382
|
-
"""Child mapping accessor backed by StepTreeState."""
|
|
383
|
-
return self.state.child_map
|
|
384
|
-
|
|
385
|
-
@children.setter
|
|
386
|
-
def children(self, value: dict[str, list[str]]) -> None:
|
|
387
|
-
self.state.child_map = value
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"""Shared timing helpers for renderer components.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def coerce_server_time(value: Any) -> float | None:
|
|
13
|
-
"""Convert a raw SSE/server time payload into a float."""
|
|
14
|
-
if isinstance(value, (int, float)):
|
|
15
|
-
return float(value)
|
|
16
|
-
try:
|
|
17
|
-
return float(value)
|
|
18
|
-
except (TypeError, ValueError):
|
|
19
|
-
return None
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def calculate_timeline_duration(
|
|
23
|
-
start_server: float | None,
|
|
24
|
-
end_server: float | None,
|
|
25
|
-
start_monotonic: float | None,
|
|
26
|
-
end_monotonic: float | None,
|
|
27
|
-
) -> float | None:
|
|
28
|
-
"""Return best-effort elapsed time using server or monotonic clocks."""
|
|
29
|
-
if start_server is not None and end_server is not None:
|
|
30
|
-
return max(0.0, float(end_server) - float(start_server))
|
|
31
|
-
if start_monotonic is not None and end_monotonic is not None:
|
|
32
|
-
try:
|
|
33
|
-
return max(0.0, float(end_monotonic) - float(start_monotonic))
|
|
34
|
-
except Exception:
|
|
35
|
-
return None
|
|
36
|
-
return None
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Shared transcript viewer exports.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from glaip_sdk.utils.rendering.viewer.presenter import (
|
|
8
|
-
ViewerContext,
|
|
9
|
-
prepare_viewer_snapshot,
|
|
10
|
-
render_post_run_view,
|
|
11
|
-
render_transcript_events,
|
|
12
|
-
render_transcript_view,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
__all__ = [
|
|
16
|
-
"ViewerContext",
|
|
17
|
-
"prepare_viewer_snapshot",
|
|
18
|
-
"render_post_run_view",
|
|
19
|
-
"render_transcript_events",
|
|
20
|
-
"render_transcript_view",
|
|
21
|
-
]
|