glaip-sdk 0.0.1b10__py3-none-any.whl → 0.0.3__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 (39) hide show
  1. glaip_sdk/__init__.py +2 -2
  2. glaip_sdk/_version.py +51 -0
  3. glaip_sdk/cli/commands/agents.py +201 -109
  4. glaip_sdk/cli/commands/configure.py +29 -87
  5. glaip_sdk/cli/commands/init.py +16 -7
  6. glaip_sdk/cli/commands/mcps.py +73 -153
  7. glaip_sdk/cli/commands/tools.py +185 -49
  8. glaip_sdk/cli/main.py +30 -27
  9. glaip_sdk/cli/utils.py +126 -13
  10. glaip_sdk/client/__init__.py +54 -2
  11. glaip_sdk/client/agents.py +175 -237
  12. glaip_sdk/client/base.py +62 -2
  13. glaip_sdk/client/mcps.py +63 -20
  14. glaip_sdk/client/tools.py +95 -28
  15. glaip_sdk/config/constants.py +10 -3
  16. glaip_sdk/exceptions.py +13 -0
  17. glaip_sdk/models.py +20 -4
  18. glaip_sdk/utils/__init__.py +116 -18
  19. glaip_sdk/utils/client_utils.py +284 -0
  20. glaip_sdk/utils/rendering/__init__.py +1 -0
  21. glaip_sdk/utils/rendering/formatting.py +211 -0
  22. glaip_sdk/utils/rendering/models.py +53 -0
  23. glaip_sdk/utils/rendering/renderer/__init__.py +38 -0
  24. glaip_sdk/utils/rendering/renderer/base.py +827 -0
  25. glaip_sdk/utils/rendering/renderer/config.py +33 -0
  26. glaip_sdk/utils/rendering/renderer/console.py +54 -0
  27. glaip_sdk/utils/rendering/renderer/debug.py +82 -0
  28. glaip_sdk/utils/rendering/renderer/panels.py +123 -0
  29. glaip_sdk/utils/rendering/renderer/progress.py +118 -0
  30. glaip_sdk/utils/rendering/renderer/stream.py +198 -0
  31. glaip_sdk/utils/rendering/steps.py +168 -0
  32. glaip_sdk/utils/run_renderer.py +22 -1086
  33. {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/METADATA +9 -37
  34. glaip_sdk-0.0.3.dist-info/RECORD +40 -0
  35. glaip_sdk/cli/config.py +0 -592
  36. glaip_sdk/utils.py +0 -167
  37. glaip_sdk-0.0.1b10.dist-info/RECORD +0 -28
  38. {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/WHEEL +0 -0
  39. {glaip_sdk-0.0.1b10.dist-info → glaip_sdk-0.0.3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,168 @@
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 .models import Step
12
+
13
+
14
+ class StepManager:
15
+ def __init__(self, max_steps: int = 200):
16
+ self.by_id: dict[str, Step] = {}
17
+ self.order: list[str] = []
18
+ self.children: dict[str, list[str]] = {}
19
+ self.key_index: dict[tuple, str] = {}
20
+ self.slot_counter: dict[tuple, int] = {}
21
+ self.max_steps = max_steps
22
+ self._last_running: dict[tuple, str] = {}
23
+
24
+ def _alloc_slot(self, task_id, context_id, kind, name) -> int:
25
+ k = (task_id, context_id, kind, name)
26
+ self.slot_counter[k] = self.slot_counter.get(k, 0) + 1
27
+ return self.slot_counter[k]
28
+
29
+ def _key(self, task_id, context_id, kind, name, slot) -> tuple:
30
+ return (task_id, context_id, kind, name, slot)
31
+
32
+ def _make_id(self, task_id, context_id, kind, name, slot) -> str:
33
+ return f"{task_id or 't'}::{context_id or 'c'}::{kind}::{name}::{slot}"
34
+
35
+ def start_or_get(
36
+ self, *, task_id, context_id, kind, name, parent_id=None, args=None
37
+ ) -> Step:
38
+ existing = self.find_running(
39
+ task_id=task_id, context_id=context_id, kind=kind, name=name
40
+ )
41
+ if existing:
42
+ if args and existing.args != args:
43
+ existing.args = args
44
+ return existing
45
+ slot = self._alloc_slot(task_id, context_id, kind, name)
46
+ key = self._key(task_id, context_id, kind, name, slot)
47
+ step_id = self._make_id(task_id, context_id, kind, name, slot)
48
+ st = Step(
49
+ step_id=step_id,
50
+ kind=kind,
51
+ name=name,
52
+ parent_id=parent_id,
53
+ task_id=task_id,
54
+ context_id=context_id,
55
+ args=args or {},
56
+ )
57
+ self.by_id[step_id] = st
58
+ if parent_id:
59
+ self.children.setdefault(parent_id, []).append(step_id)
60
+ else:
61
+ self.order.append(step_id)
62
+ self.key_index[key] = step_id
63
+ self._prune_steps()
64
+ self._last_running[(task_id, context_id, kind, name)] = step_id
65
+ return st
66
+
67
+ def _prune_steps(self):
68
+ total = len(self.order) + sum(len(v) for v in self.children.values())
69
+ if total <= self.max_steps:
70
+ return
71
+
72
+ def remove_subtree(root_id: str):
73
+ stack = [root_id]
74
+ to_remove = []
75
+ while stack:
76
+ sid = stack.pop()
77
+ to_remove.append(sid)
78
+ stack.extend(self.children.pop(sid, []))
79
+ for sid in to_remove:
80
+ st = self.by_id.pop(sid, None)
81
+ if st:
82
+ key = (st.task_id, st.context_id, st.kind, st.name)
83
+ self._last_running.pop(key, None)
84
+ for _parent, kids in list(self.children.items()):
85
+ if sid in kids:
86
+ kids.remove(sid)
87
+ if sid in self.order:
88
+ self.order.remove(sid)
89
+
90
+ while total > self.max_steps and self.order:
91
+ sid = self.order[0]
92
+ subtree = [sid]
93
+ stack = list(self.children.get(sid, []))
94
+ while stack:
95
+ x = stack.pop()
96
+ subtree.append(x)
97
+ stack.extend(self.children.get(x, []))
98
+ total -= len(subtree)
99
+ remove_subtree(sid)
100
+
101
+ def get_child_count(self, step_id: str) -> int:
102
+ return len(self.children.get(step_id, []))
103
+
104
+ def find_running(self, *, task_id, context_id, kind, name) -> Step | None:
105
+ key = (task_id, context_id, kind, name)
106
+ step_id = self._last_running.get(key)
107
+ if step_id:
108
+ st = self.by_id.get(step_id)
109
+ if st and st.status != "finished":
110
+ return st
111
+ for sid in reversed(list(self._iter_all_steps())):
112
+ st = self.by_id.get(sid)
113
+ if (
114
+ st
115
+ and (st.task_id, st.context_id, st.kind, st.name)
116
+ == (
117
+ task_id,
118
+ context_id,
119
+ kind,
120
+ name,
121
+ )
122
+ and st.status != "finished"
123
+ ):
124
+ return st
125
+ return None
126
+
127
+ def finish(
128
+ self, *, task_id, context_id, kind, name, output=None, duration_raw=None
129
+ ):
130
+ st = self.find_running(
131
+ task_id=task_id, context_id=context_id, kind=kind, name=name
132
+ )
133
+ if not st:
134
+ # Try to find any existing step with matching parameters, even if not running
135
+ for sid in reversed(list(self._iter_all_steps())):
136
+ st_check = self.by_id.get(sid)
137
+ if (
138
+ st_check
139
+ and st_check.task_id == task_id
140
+ and st_check.context_id == context_id
141
+ and st_check.kind == kind
142
+ and st_check.name == name
143
+ ):
144
+ st = st_check
145
+ break
146
+
147
+ # If still no step found, create a new one
148
+ if not st:
149
+ st = self.start_or_get(
150
+ task_id=task_id, context_id=context_id, kind=kind, name=name
151
+ )
152
+
153
+ if output:
154
+ st.output = output
155
+ st.finish(duration_raw)
156
+ key = (task_id, context_id, kind, name)
157
+ if self._last_running.get(key) == st.step_id:
158
+ self._last_running.pop(key, None)
159
+ return st
160
+
161
+ def _iter_all_steps(self) -> Iterator[str]:
162
+ for root in self.order:
163
+ yield root
164
+ stack = list(self.children.get(root, []))
165
+ while stack:
166
+ sid = stack.pop()
167
+ yield sid
168
+ stack.extend(self.children.get(sid, []))