glaip-sdk 0.0.17__py3-none-any.whl → 0.0.18__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/cli/slash/prompt.py +32 -17
- glaip_sdk/cli/slash/session.py +54 -25
- glaip_sdk/client/agents.py +90 -28
- glaip_sdk/client/main.py +2 -2
- glaip_sdk/client/tools.py +34 -3
- glaip_sdk/utils/agent_config.py +49 -26
- glaip_sdk/utils/rendering/formatting.py +50 -29
- glaip_sdk/utils/rendering/renderer/base.py +156 -70
- {glaip_sdk-0.0.17.dist-info → glaip_sdk-0.0.18.dist-info}/METADATA +1 -1
- {glaip_sdk-0.0.17.dist-info → glaip_sdk-0.0.18.dist-info}/RECORD +12 -12
- {glaip_sdk-0.0.17.dist-info → glaip_sdk-0.0.18.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.17.dist-info → glaip_sdk-0.0.18.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/slash/prompt.py
CHANGED
|
@@ -194,28 +194,43 @@ def _iter_command_completions(
|
|
|
194
194
|
prefix = text[1:]
|
|
195
195
|
seen: set[str] = set()
|
|
196
196
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
and not session.should_include_global_commands()
|
|
200
|
-
):
|
|
197
|
+
# Early return for contextual commands scenario
|
|
198
|
+
if not _should_include_commands(session):
|
|
201
199
|
return []
|
|
202
200
|
|
|
203
201
|
commands = sorted(session._unique_commands.values(), key=lambda c: c.name)
|
|
204
202
|
|
|
205
203
|
for cmd in commands:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
204
|
+
yield from _generate_command_completions(cmd, prefix, text, seen)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _should_include_commands(session: SlashSession) -> bool:
|
|
208
|
+
"""Check if commands should be included in completions."""
|
|
209
|
+
return not (
|
|
210
|
+
session.get_contextual_commands()
|
|
211
|
+
and not session.should_include_global_commands()
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _generate_command_completions(
|
|
216
|
+
cmd: Any, prefix: str, text: str, seen: set[str]
|
|
217
|
+
) -> Iterable[Completion]:
|
|
218
|
+
"""Generate completion items for a single command."""
|
|
219
|
+
for alias in (cmd.name, *cmd.aliases):
|
|
220
|
+
if alias in seen or alias.startswith("?"):
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
if prefix and not alias.startswith(prefix):
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
seen.add(alias)
|
|
227
|
+
label = f"/{alias}"
|
|
228
|
+
yield Completion(
|
|
229
|
+
text=label,
|
|
230
|
+
start_position=-len(text),
|
|
231
|
+
display=label,
|
|
232
|
+
display_meta=cmd.help,
|
|
233
|
+
)
|
|
219
234
|
|
|
220
235
|
|
|
221
236
|
def _iter_contextual_completions(
|
glaip_sdk/cli/slash/session.py
CHANGED
|
@@ -297,51 +297,80 @@ class SlashSession:
|
|
|
297
297
|
return True
|
|
298
298
|
|
|
299
299
|
def _cmd_agents(self, args: list[str], _invoked_from_agent: bool) -> bool:
|
|
300
|
+
client = self._get_client_or_fail()
|
|
301
|
+
if not client:
|
|
302
|
+
return True
|
|
303
|
+
|
|
304
|
+
agents = self._get_agents_or_fail(client)
|
|
305
|
+
if not agents:
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
picked_agent = self._resolve_or_pick_agent(client, agents, args)
|
|
309
|
+
|
|
310
|
+
if not picked_agent:
|
|
311
|
+
return True
|
|
312
|
+
|
|
313
|
+
return self._run_agent_session(picked_agent)
|
|
314
|
+
|
|
315
|
+
def _get_client_or_fail(self) -> Any:
|
|
316
|
+
"""Get client or handle failure and return None."""
|
|
300
317
|
try:
|
|
301
|
-
|
|
318
|
+
return self._get_client()
|
|
302
319
|
except click.ClickException as exc:
|
|
303
320
|
self.console.print(f"[red]{exc}[/red]")
|
|
304
|
-
return
|
|
321
|
+
return None
|
|
305
322
|
|
|
323
|
+
def _get_agents_or_fail(self, client: Any) -> list:
|
|
324
|
+
"""Get agents list or handle failure and return empty list."""
|
|
306
325
|
try:
|
|
307
326
|
agents = client.list_agents()
|
|
327
|
+
if not agents:
|
|
328
|
+
self._handle_no_agents()
|
|
329
|
+
return agents
|
|
308
330
|
except Exception as exc: # pragma: no cover - API failures
|
|
309
331
|
self.console.print(f"[red]Failed to load agents: {exc}[/red]")
|
|
310
|
-
return
|
|
332
|
+
return []
|
|
311
333
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
334
|
+
def _handle_no_agents(self) -> None:
|
|
335
|
+
"""Handle case when no agents are available."""
|
|
336
|
+
hint = command_hint("agents create", slash_command=None, ctx=self.ctx)
|
|
337
|
+
if hint:
|
|
338
|
+
self.console.print(
|
|
339
|
+
f"[yellow]No agents available. Use `{hint}` to add one.[/yellow]"
|
|
340
|
+
)
|
|
341
|
+
else:
|
|
342
|
+
self.console.print("[yellow]No agents available.[/yellow]")
|
|
321
343
|
|
|
344
|
+
def _resolve_or_pick_agent(self, client: Any, agents: list, args: list[str]) -> Any:
|
|
345
|
+
"""Resolve agent from args or pick interactively."""
|
|
322
346
|
if args:
|
|
323
347
|
picked_agent = self._resolve_agent_from_ref(client, agents, args[0])
|
|
324
348
|
if picked_agent is None:
|
|
325
349
|
self.console.print(
|
|
326
350
|
f"[yellow]Could not resolve agent '{args[0]}'. Try `/agents` to browse interactively.[/yellow]"
|
|
327
351
|
)
|
|
328
|
-
return
|
|
352
|
+
return None
|
|
329
353
|
else:
|
|
330
354
|
picked_agent = _fuzzy_pick_for_resources(agents, "agent", "")
|
|
331
355
|
|
|
332
|
-
|
|
333
|
-
return True
|
|
356
|
+
return picked_agent
|
|
334
357
|
|
|
358
|
+
def _run_agent_session(self, picked_agent: Any) -> bool:
|
|
359
|
+
"""Run agent session and show follow-up actions."""
|
|
335
360
|
self._remember_agent(picked_agent)
|
|
336
|
-
|
|
337
361
|
AgentRunSession(self, picked_agent).run()
|
|
338
362
|
|
|
339
|
-
# Refresh the main palette header and surface follow-up actions
|
|
340
|
-
# user has immediate cues after leaving the agent context.
|
|
363
|
+
# Refresh the main palette header and surface follow-up actions
|
|
341
364
|
self._render_header()
|
|
342
365
|
|
|
366
|
+
self._show_agent_followup_actions(picked_agent)
|
|
367
|
+
return True
|
|
368
|
+
|
|
369
|
+
def _show_agent_followup_actions(self, picked_agent: Any) -> None:
|
|
370
|
+
"""Show follow-up action hints after agent session."""
|
|
343
371
|
agent_id = str(getattr(picked_agent, "id", ""))
|
|
344
372
|
agent_label = getattr(picked_agent, "name", "") or agent_id or "this agent"
|
|
373
|
+
|
|
345
374
|
hints: list[tuple[str, str]] = []
|
|
346
375
|
if agent_id:
|
|
347
376
|
hints.append((f"/agents {agent_id}", f"Reopen {agent_label}"))
|
|
@@ -351,8 +380,8 @@ class SlashSession:
|
|
|
351
380
|
(self.STATUS_COMMAND, "Check connection"),
|
|
352
381
|
]
|
|
353
382
|
)
|
|
383
|
+
|
|
354
384
|
self._show_quick_actions(hints, title="Next actions")
|
|
355
|
-
return True
|
|
356
385
|
|
|
357
386
|
def _cmd_exit(self, _args: list[str], invoked_from_agent: bool) -> bool:
|
|
358
387
|
if invoked_from_agent:
|
|
@@ -710,12 +739,12 @@ class SlashSession:
|
|
|
710
739
|
|
|
711
740
|
if full:
|
|
712
741
|
lines = [
|
|
713
|
-
self._branding.
|
|
714
|
-
"",
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
742
|
+
f"GL AIP v{self._branding.version} · GDP Labs AI Agents Package",
|
|
743
|
+
f"API: {api_url or 'Not configured'} · Credentials: {status}",
|
|
744
|
+
(
|
|
745
|
+
f"Verbose: {'on' if self._verbose_enabled else 'off'} "
|
|
746
|
+
"(Ctrl+T toggles verbose streaming)"
|
|
747
|
+
),
|
|
719
748
|
]
|
|
720
749
|
extra: list[str] = []
|
|
721
750
|
self._add_agent_info_to_header(extra, active_agent)
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -64,6 +64,7 @@ logger = logging.getLogger("glaip_sdk.agents")
|
|
|
64
64
|
|
|
65
65
|
_SERVER_ONLY_IMPORT_FIELDS = set(list_server_only_fields()) | {"success", "message"}
|
|
66
66
|
_MERGED_SEQUENCE_FIELDS = ("tools", "agents", "mcps")
|
|
67
|
+
_DEFAULT_METADATA_TYPE = "custom"
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def _normalise_sequence(value: Any) -> list[Any] | None:
|
|
@@ -119,6 +120,20 @@ def _split_known_and_extra(
|
|
|
119
120
|
return known, extras
|
|
120
121
|
|
|
121
122
|
|
|
123
|
+
def _prepare_agent_metadata(value: Any) -> dict[str, Any]:
|
|
124
|
+
"""Ensure agent metadata contains ``type: custom`` by default."""
|
|
125
|
+
if value is None:
|
|
126
|
+
return {"type": _DEFAULT_METADATA_TYPE}
|
|
127
|
+
if not isinstance(value, Mapping):
|
|
128
|
+
return {"type": _DEFAULT_METADATA_TYPE}
|
|
129
|
+
|
|
130
|
+
prepared = dict(value)
|
|
131
|
+
metadata_type = prepared.get("type")
|
|
132
|
+
if not metadata_type:
|
|
133
|
+
prepared["type"] = _DEFAULT_METADATA_TYPE
|
|
134
|
+
return prepared
|
|
135
|
+
|
|
136
|
+
|
|
122
137
|
def _load_agent_file_payload(
|
|
123
138
|
file_path: Path, *, model_override: str | None
|
|
124
139
|
) -> dict[str, Any]:
|
|
@@ -151,16 +166,39 @@ def _prepare_import_payload(
|
|
|
151
166
|
overrides_dict = dict(overrides)
|
|
152
167
|
|
|
153
168
|
raw_definition = load_resource_from_file(file_path)
|
|
154
|
-
original_refs =
|
|
169
|
+
original_refs = _extract_original_refs(raw_definition)
|
|
170
|
+
|
|
171
|
+
base_payload = _load_agent_file_payload(
|
|
172
|
+
file_path, model_override=overrides_dict.get("model")
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
cli_args = _build_cli_args(overrides_dict)
|
|
176
|
+
|
|
177
|
+
merged = merge_import_with_cli_args(base_payload, cli_args)
|
|
178
|
+
|
|
179
|
+
additional = _build_additional_args(overrides_dict, cli_args)
|
|
180
|
+
merged.update(additional)
|
|
181
|
+
|
|
182
|
+
if drop_model_fields:
|
|
183
|
+
_remove_model_fields_if_needed(merged, overrides_dict)
|
|
184
|
+
|
|
185
|
+
_set_default_refs(merged, original_refs)
|
|
186
|
+
|
|
187
|
+
_normalise_sequence_fields(merged)
|
|
188
|
+
return merged
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _extract_original_refs(raw_definition: dict) -> dict[str, list]:
|
|
192
|
+
"""Extract original tool/agent/mcp references from raw definition."""
|
|
193
|
+
return {
|
|
155
194
|
"tools": list(raw_definition.get("tools") or []),
|
|
156
195
|
"agents": list(raw_definition.get("agents") or []),
|
|
157
196
|
"mcps": list(raw_definition.get("mcps") or []),
|
|
158
197
|
}
|
|
159
198
|
|
|
160
|
-
base_payload = _load_agent_file_payload(
|
|
161
|
-
file_path, model_override=overrides_dict.get("model")
|
|
162
|
-
)
|
|
163
199
|
|
|
200
|
+
def _build_cli_args(overrides_dict: dict) -> dict[str, Any]:
|
|
201
|
+
"""Build CLI args from overrides, filtering out None values."""
|
|
164
202
|
cli_args = {
|
|
165
203
|
key: overrides_dict.get(key)
|
|
166
204
|
for key in (
|
|
@@ -175,32 +213,37 @@ def _prepare_import_payload(
|
|
|
175
213
|
if overrides_dict.get(key) is not None
|
|
176
214
|
}
|
|
177
215
|
|
|
216
|
+
# Normalize sequence fields
|
|
178
217
|
for field in _MERGED_SEQUENCE_FIELDS:
|
|
179
218
|
if field in cli_args:
|
|
180
219
|
cli_args[field] = tuple(_normalise_sequence(cli_args[field]) or [])
|
|
181
220
|
|
|
182
|
-
|
|
221
|
+
return cli_args
|
|
183
222
|
|
|
184
|
-
|
|
223
|
+
|
|
224
|
+
def _build_additional_args(overrides_dict: dict, cli_args: dict) -> dict[str, Any]:
|
|
225
|
+
"""Build additional args not already in CLI args."""
|
|
226
|
+
return {
|
|
185
227
|
key: value
|
|
186
228
|
for key, value in overrides_dict.items()
|
|
187
229
|
if value is not None and key not in cli_args
|
|
188
230
|
}
|
|
189
|
-
merged.update(additional)
|
|
190
231
|
|
|
191
|
-
if drop_model_fields:
|
|
192
|
-
if overrides_dict.get("language_model_id") is None:
|
|
193
|
-
merged.pop("language_model_id", None)
|
|
194
|
-
if overrides_dict.get("provider") is None:
|
|
195
|
-
merged.pop("provider", None)
|
|
196
232
|
|
|
233
|
+
def _remove_model_fields_if_needed(merged: dict, overrides_dict: dict) -> None:
|
|
234
|
+
"""Remove model fields if not explicitly overridden."""
|
|
235
|
+
if overrides_dict.get("language_model_id") is None:
|
|
236
|
+
merged.pop("language_model_id", None)
|
|
237
|
+
if overrides_dict.get("provider") is None:
|
|
238
|
+
merged.pop("provider", None)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _set_default_refs(merged: dict, original_refs: dict) -> None:
|
|
242
|
+
"""Set default references if not already present."""
|
|
197
243
|
merged.setdefault("_tool_refs", original_refs["tools"])
|
|
198
244
|
merged.setdefault("_agent_refs", original_refs["agents"])
|
|
199
245
|
merged.setdefault("_mcp_refs", original_refs["mcps"])
|
|
200
246
|
|
|
201
|
-
_normalise_sequence_fields(merged)
|
|
202
|
-
return merged
|
|
203
|
-
|
|
204
247
|
|
|
205
248
|
class AgentClient(BaseClient):
|
|
206
249
|
"""Client for agent operations."""
|
|
@@ -594,6 +637,25 @@ class AgentClient(BaseClient):
|
|
|
594
637
|
resolved_agents = self._resolve_agent_ids(agents_raw, agent_refs)
|
|
595
638
|
resolved_mcps = self._resolve_mcp_ids(mcps_raw, mcp_refs)
|
|
596
639
|
|
|
640
|
+
language_model_id = known.pop("language_model_id", None)
|
|
641
|
+
provider = known.pop("provider", None)
|
|
642
|
+
model_name = known.pop("model_name", None)
|
|
643
|
+
|
|
644
|
+
agent_type_value = known.pop("agent_type", None)
|
|
645
|
+
fallback_type_value = known.pop("type", None)
|
|
646
|
+
if agent_type_value is None:
|
|
647
|
+
agent_type_value = fallback_type_value or DEFAULT_AGENT_TYPE
|
|
648
|
+
|
|
649
|
+
framework_value = known.pop("framework", None) or DEFAULT_AGENT_FRAMEWORK
|
|
650
|
+
version_value = known.pop("version", None) or DEFAULT_AGENT_VERSION
|
|
651
|
+
account_id = known.pop("account_id", None)
|
|
652
|
+
description = known.pop("description", None)
|
|
653
|
+
metadata = _prepare_agent_metadata(known.pop("metadata", None))
|
|
654
|
+
tool_configs = known.pop("tool_configs", None)
|
|
655
|
+
agent_config = known.pop("agent_config", None)
|
|
656
|
+
timeout_value = known.pop("timeout", None)
|
|
657
|
+
a2a_profile = known.pop("a2a_profile", None)
|
|
658
|
+
|
|
597
659
|
final_extras = {**known, **extras}
|
|
598
660
|
final_extras.setdefault("model", resolved_model)
|
|
599
661
|
|
|
@@ -601,22 +663,22 @@ class AgentClient(BaseClient):
|
|
|
601
663
|
name=str(name).strip(),
|
|
602
664
|
instruction=validated_instruction,
|
|
603
665
|
model=resolved_model,
|
|
604
|
-
language_model_id=
|
|
605
|
-
provider=
|
|
606
|
-
model_name=
|
|
607
|
-
agent_type=
|
|
608
|
-
framework=
|
|
609
|
-
version=
|
|
610
|
-
account_id=
|
|
611
|
-
description=
|
|
612
|
-
metadata=
|
|
666
|
+
language_model_id=language_model_id,
|
|
667
|
+
provider=provider,
|
|
668
|
+
model_name=model_name,
|
|
669
|
+
agent_type=agent_type_value,
|
|
670
|
+
framework=framework_value,
|
|
671
|
+
version=version_value,
|
|
672
|
+
account_id=account_id,
|
|
673
|
+
description=description,
|
|
674
|
+
metadata=metadata,
|
|
613
675
|
tools=resolved_tools,
|
|
614
676
|
agents=resolved_agents,
|
|
615
677
|
mcps=resolved_mcps,
|
|
616
|
-
tool_configs=
|
|
617
|
-
agent_config=
|
|
618
|
-
timeout=
|
|
619
|
-
a2a_profile=
|
|
678
|
+
tool_configs=tool_configs,
|
|
679
|
+
agent_config=agent_config,
|
|
680
|
+
timeout=timeout_value or DEFAULT_AGENT_RUN_TIMEOUT,
|
|
681
|
+
a2a_profile=a2a_profile,
|
|
620
682
|
extras=final_extras,
|
|
621
683
|
)
|
|
622
684
|
|
glaip_sdk/client/main.py
CHANGED
|
@@ -158,9 +158,9 @@ class Client(BaseClient):
|
|
|
158
158
|
"""Get tool script content."""
|
|
159
159
|
return self.tools.get_tool_script(tool_id)
|
|
160
160
|
|
|
161
|
-
def update_tool_via_file(self, tool_id: str, file_path: str) -> Tool:
|
|
161
|
+
def update_tool_via_file(self, tool_id: str, file_path: str, **kwargs) -> Tool:
|
|
162
162
|
"""Update tool via file."""
|
|
163
|
-
return self.tools.update_tool_via_file(tool_id, file_path)
|
|
163
|
+
return self.tools.update_tool_via_file(tool_id, file_path, **kwargs)
|
|
164
164
|
|
|
165
165
|
# MCPs
|
|
166
166
|
def create_mcp(self, **kwargs) -> MCP:
|
glaip_sdk/client/tools.py
CHANGED
|
@@ -241,7 +241,15 @@ class ToolClient(BaseClient):
|
|
|
241
241
|
self, update_data: dict[str, Any], kwargs: dict[str, Any]
|
|
242
242
|
) -> None:
|
|
243
243
|
"""Handle additional kwargs in update payload."""
|
|
244
|
-
excluded_keys = {
|
|
244
|
+
excluded_keys = {
|
|
245
|
+
"tags",
|
|
246
|
+
"framework",
|
|
247
|
+
"version",
|
|
248
|
+
"type",
|
|
249
|
+
"tool_type",
|
|
250
|
+
"name",
|
|
251
|
+
"description",
|
|
252
|
+
}
|
|
245
253
|
for key, value in kwargs.items():
|
|
246
254
|
if key not in excluded_keys:
|
|
247
255
|
update_data[key] = value
|
|
@@ -269,9 +277,19 @@ class ToolClient(BaseClient):
|
|
|
269
277
|
- Handles metadata updates properly
|
|
270
278
|
"""
|
|
271
279
|
# Prepare the update payload with current values as defaults
|
|
280
|
+
type_override = kwargs.pop("type", None)
|
|
281
|
+
if type_override is None:
|
|
282
|
+
type_override = kwargs.pop("tool_type", None)
|
|
283
|
+
current_type = (
|
|
284
|
+
type_override
|
|
285
|
+
or getattr(current_tool, "tool_type", None)
|
|
286
|
+
or getattr(current_tool, "type", None)
|
|
287
|
+
or DEFAULT_TOOL_TYPE
|
|
288
|
+
)
|
|
289
|
+
|
|
272
290
|
update_data = {
|
|
273
291
|
"name": name if name is not None else current_tool.name,
|
|
274
|
-
"type":
|
|
292
|
+
"type": current_type,
|
|
275
293
|
"framework": kwargs.get(
|
|
276
294
|
"framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)
|
|
277
295
|
),
|
|
@@ -476,6 +494,19 @@ class ToolClient(BaseClient):
|
|
|
476
494
|
# Validate file exists
|
|
477
495
|
self._validate_and_read_file(file_path)
|
|
478
496
|
|
|
497
|
+
# Fetch current metadata to ensure required fields are preserved
|
|
498
|
+
current_tool = self.get_tool_by_id(tool_id)
|
|
499
|
+
|
|
500
|
+
payload_kwargs = kwargs.copy()
|
|
501
|
+
name = payload_kwargs.pop("name", None)
|
|
502
|
+
description = payload_kwargs.pop("description", None)
|
|
503
|
+
update_payload = self._build_update_payload(
|
|
504
|
+
current_tool=current_tool,
|
|
505
|
+
name=name,
|
|
506
|
+
description=description,
|
|
507
|
+
**payload_kwargs,
|
|
508
|
+
)
|
|
509
|
+
|
|
479
510
|
try:
|
|
480
511
|
# Prepare multipart upload
|
|
481
512
|
with open(file_path, "rb") as fb:
|
|
@@ -491,7 +522,7 @@ class ToolClient(BaseClient):
|
|
|
491
522
|
"PUT",
|
|
492
523
|
TOOLS_UPLOAD_BY_ID_ENDPOINT_FMT.format(tool_id=tool_id),
|
|
493
524
|
files=files,
|
|
494
|
-
data=
|
|
525
|
+
data=update_payload,
|
|
495
526
|
)
|
|
496
527
|
|
|
497
528
|
return Tool(**response)._set_client(self)
|
glaip_sdk/utils/agent_config.py
CHANGED
|
@@ -134,25 +134,51 @@ def normalize_agent_config_for_import(
|
|
|
134
134
|
if not isinstance(agent_config, dict):
|
|
135
135
|
return normalized_data
|
|
136
136
|
|
|
137
|
-
#
|
|
137
|
+
# Apply normalization based on priority order
|
|
138
138
|
if cli_model:
|
|
139
|
-
|
|
140
|
-
normalized_data["model"] = cli_model
|
|
141
|
-
return normalized_data
|
|
139
|
+
return _apply_cli_model_override(normalized_data, cli_model)
|
|
142
140
|
|
|
143
|
-
# Priority 2: language_model_id already exists - clean up agent_config
|
|
144
141
|
if normalized_data.get("language_model_id"):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
142
|
+
return _cleanup_existing_language_model(normalized_data, agent_config)
|
|
143
|
+
|
|
144
|
+
return _extract_lm_from_agent_config(normalized_data, agent_config)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _apply_cli_model_override(normalized_data: dict, cli_model: str) -> dict:
|
|
148
|
+
"""Apply CLI model override (highest priority)."""
|
|
149
|
+
normalized_data["model"] = cli_model
|
|
150
|
+
return normalized_data
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _cleanup_existing_language_model(normalized_data: dict, agent_config: dict) -> dict:
|
|
154
|
+
"""Clean up agent_config when language_model_id already exists."""
|
|
155
|
+
# Remove LM identity keys from agent_config since language_model_id takes precedence
|
|
156
|
+
lm_keys_to_remove = {"lm_provider", "lm_name", "lm_base_url"}
|
|
157
|
+
for key in lm_keys_to_remove:
|
|
158
|
+
agent_config.pop(key, None)
|
|
159
|
+
normalized_data["agent_config"] = agent_config
|
|
160
|
+
return normalized_data
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _extract_lm_from_agent_config(normalized_data: dict, agent_config: dict) -> dict:
|
|
164
|
+
"""Extract LM settings from agent_config (lowest priority)."""
|
|
165
|
+
extracted_lm = _extract_lm_settings(agent_config)
|
|
166
|
+
|
|
167
|
+
if not extracted_lm:
|
|
153
168
|
return normalized_data
|
|
154
169
|
|
|
155
|
-
#
|
|
170
|
+
# Add extracted LM settings to top level
|
|
171
|
+
normalized_data.update(extracted_lm)
|
|
172
|
+
|
|
173
|
+
# Create sanitized agent_config (remove extracted LM settings but keep memory)
|
|
174
|
+
sanitized_config = _sanitize_agent_config(agent_config)
|
|
175
|
+
normalized_data["agent_config"] = sanitized_config
|
|
176
|
+
|
|
177
|
+
return normalized_data
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _extract_lm_settings(agent_config: dict) -> dict[str, Any]:
|
|
181
|
+
"""Extract LM settings from agent_config."""
|
|
156
182
|
extracted_lm = {}
|
|
157
183
|
|
|
158
184
|
# Extract lm_name if present
|
|
@@ -163,19 +189,16 @@ def normalize_agent_config_for_import(
|
|
|
163
189
|
if "lm_provider" in agent_config:
|
|
164
190
|
extracted_lm["lm_provider"] = agent_config["lm_provider"]
|
|
165
191
|
|
|
166
|
-
|
|
167
|
-
if extracted_lm:
|
|
168
|
-
# Add extracted LM settings to top level
|
|
169
|
-
normalized_data.update(extracted_lm)
|
|
192
|
+
return extracted_lm
|
|
170
193
|
|
|
171
|
-
# Create sanitized agent_config (remove extracted LM settings but keep memory)
|
|
172
|
-
sanitized_config = agent_config.copy()
|
|
173
194
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
sanitized_config.pop(key, None)
|
|
195
|
+
def _sanitize_agent_config(agent_config: dict) -> dict:
|
|
196
|
+
"""Create sanitized agent_config by removing LM identity keys."""
|
|
197
|
+
sanitized_config = agent_config.copy()
|
|
178
198
|
|
|
179
|
-
|
|
199
|
+
# Remove LM identity keys but preserve memory and other settings
|
|
200
|
+
lm_keys_to_remove = {"lm_provider", "lm_name", "lm_base_url"}
|
|
201
|
+
for key in lm_keys_to_remove:
|
|
202
|
+
sanitized_config.pop(key, None)
|
|
180
203
|
|
|
181
|
-
return
|
|
204
|
+
return sanitized_config
|
|
@@ -57,41 +57,62 @@ def mask_secrets_in_string(text: str) -> str:
|
|
|
57
57
|
def redact_sensitive(text: str | dict | list) -> str | dict | list:
|
|
58
58
|
"""Redact sensitive information in a string, dict, or list."""
|
|
59
59
|
if isinstance(text, dict):
|
|
60
|
-
|
|
61
|
-
result = {}
|
|
62
|
-
for key, value in text.items():
|
|
63
|
-
# Check if the key itself is sensitive
|
|
64
|
-
key_lower = key.lower()
|
|
65
|
-
if any(
|
|
66
|
-
sensitive in key_lower
|
|
67
|
-
for sensitive in ["password", "secret", "token", "key", "api_key"]
|
|
68
|
-
):
|
|
69
|
-
result[key] = "••••••"
|
|
70
|
-
elif isinstance(value, dict | list) or isinstance(value, str):
|
|
71
|
-
result[key] = redact_sensitive(value)
|
|
72
|
-
else:
|
|
73
|
-
result[key] = value
|
|
74
|
-
return result
|
|
60
|
+
return _redact_dict_values(text)
|
|
75
61
|
elif isinstance(text, list):
|
|
76
|
-
|
|
77
|
-
return [redact_sensitive(item) for item in text]
|
|
62
|
+
return _redact_list_items(text)
|
|
78
63
|
elif isinstance(text, str):
|
|
79
|
-
|
|
80
|
-
result = text
|
|
81
|
-
# First mask secrets
|
|
82
|
-
for pattern in SECRET_VALUE_PATTERNS:
|
|
83
|
-
result = re.sub(pattern, "••••••", result)
|
|
84
|
-
# Then redact sensitive patterns
|
|
85
|
-
result = re.sub(
|
|
86
|
-
SENSITIVE_PATTERNS,
|
|
87
|
-
lambda m: m.group(0).split("=")[0] + "=••••••",
|
|
88
|
-
result,
|
|
89
|
-
)
|
|
90
|
-
return result
|
|
64
|
+
return _redact_string_content(text)
|
|
91
65
|
else:
|
|
92
66
|
return text
|
|
93
67
|
|
|
94
68
|
|
|
69
|
+
def _redact_dict_values(text: dict) -> dict:
|
|
70
|
+
"""Recursively process dictionary values and redact sensitive keys."""
|
|
71
|
+
result = {}
|
|
72
|
+
for key, value in text.items():
|
|
73
|
+
if _is_sensitive_key(key):
|
|
74
|
+
result[key] = "••••••"
|
|
75
|
+
elif _should_recurse_redaction(value):
|
|
76
|
+
result[key] = redact_sensitive(value)
|
|
77
|
+
else:
|
|
78
|
+
result[key] = value
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _redact_list_items(text: list) -> list:
|
|
83
|
+
"""Recursively process list items."""
|
|
84
|
+
return [redact_sensitive(item) for item in text]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _redact_string_content(text: str) -> str:
|
|
88
|
+
"""Process string - first mask secrets, then redact sensitive patterns."""
|
|
89
|
+
result = text
|
|
90
|
+
# First mask secrets
|
|
91
|
+
for pattern in SECRET_VALUE_PATTERNS:
|
|
92
|
+
result = re.sub(pattern, "••••••", result)
|
|
93
|
+
# Then redact sensitive patterns
|
|
94
|
+
result = re.sub(
|
|
95
|
+
SENSITIVE_PATTERNS,
|
|
96
|
+
lambda m: m.group(0).split("=")[0] + "=••••••",
|
|
97
|
+
result,
|
|
98
|
+
)
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _is_sensitive_key(key: str) -> bool:
|
|
103
|
+
"""Check if a key contains sensitive information."""
|
|
104
|
+
key_lower = key.lower()
|
|
105
|
+
return any(
|
|
106
|
+
sensitive in key_lower
|
|
107
|
+
for sensitive in ["password", "secret", "token", "key", "api_key"]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _should_recurse_redaction(value: Any) -> bool:
|
|
112
|
+
"""Check if a value should be recursively processed."""
|
|
113
|
+
return isinstance(value, dict | list) or isinstance(value, str)
|
|
114
|
+
|
|
115
|
+
|
|
95
116
|
def pretty_args(args: dict | None, max_len: int = DEFAULT_ARGS_MAX_LEN) -> str:
|
|
96
117
|
"""Format arguments in a pretty way."""
|
|
97
118
|
if not args:
|
|
@@ -45,16 +45,21 @@ from glaip_sdk.utils.rendering.steps import StepManager
|
|
|
45
45
|
# Configure logger
|
|
46
46
|
logger = logging.getLogger("glaip_sdk.run_renderer")
|
|
47
47
|
|
|
48
|
+
# Constants
|
|
49
|
+
LESS_THAN_1MS = "[<1ms]"
|
|
50
|
+
|
|
48
51
|
|
|
49
52
|
@dataclass
|
|
50
53
|
class RendererState:
|
|
51
54
|
"""Internal state for the renderer."""
|
|
52
55
|
|
|
53
|
-
buffer: list[str] = None
|
|
56
|
+
buffer: list[str] | None = None
|
|
54
57
|
final_text: str = ""
|
|
55
58
|
streaming_started_at: float | None = None
|
|
56
59
|
printed_final_panel: bool = False
|
|
57
60
|
finalizing_ui: bool = False
|
|
61
|
+
final_duration_seconds: float | None = None
|
|
62
|
+
final_duration_text: str | None = None
|
|
58
63
|
|
|
59
64
|
def __post_init__(self) -> None:
|
|
60
65
|
"""Initialize renderer state after dataclass creation.
|
|
@@ -212,59 +217,76 @@ class RichStreamRenderer:
|
|
|
212
217
|
|
|
213
218
|
# Handle different event types
|
|
214
219
|
if kind == "status":
|
|
215
|
-
|
|
216
|
-
status = ev.get("status")
|
|
217
|
-
if status == "streaming_started":
|
|
218
|
-
self.state.streaming_started_at = monotonic()
|
|
219
|
-
return
|
|
220
|
-
|
|
220
|
+
self._handle_status_event(ev)
|
|
221
221
|
elif kind == "content":
|
|
222
|
-
|
|
223
|
-
if content:
|
|
224
|
-
self.state.buffer.append(content)
|
|
225
|
-
self._ensure_live()
|
|
226
|
-
return
|
|
227
|
-
|
|
222
|
+
self._handle_content_event(content)
|
|
228
223
|
elif kind == "final_response":
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
224
|
+
self._handle_final_response_event(content, metadata)
|
|
225
|
+
elif kind in {"agent_step", "agent_thinking_step"}:
|
|
226
|
+
self._handle_agent_step_event(ev)
|
|
227
|
+
else:
|
|
228
|
+
# Update live display for unhandled events
|
|
229
|
+
self._ensure_live()
|
|
234
230
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return
|
|
231
|
+
def _handle_status_event(self, ev: dict[str, Any]) -> None:
|
|
232
|
+
"""Handle status events."""
|
|
233
|
+
status = ev.get("status")
|
|
234
|
+
if status == "streaming_started":
|
|
235
|
+
self.state.streaming_started_at = monotonic()
|
|
241
236
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
self.stream_processor.track_tools_and_agents(
|
|
256
|
-
tool_name, tool_calls_info, is_delegation_tool
|
|
257
|
-
)
|
|
237
|
+
def _handle_content_event(self, content: str) -> None:
|
|
238
|
+
"""Handle content streaming events."""
|
|
239
|
+
if content:
|
|
240
|
+
self.state.buffer.append(content)
|
|
241
|
+
self._ensure_live()
|
|
242
|
+
|
|
243
|
+
def _handle_final_response_event(
|
|
244
|
+
self, content: str, metadata: dict[str, Any]
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Handle final response events."""
|
|
247
|
+
if content:
|
|
248
|
+
self.state.buffer.append(content)
|
|
249
|
+
self.state.final_text = content
|
|
258
250
|
|
|
259
|
-
|
|
260
|
-
self.
|
|
251
|
+
meta_payload = metadata.get("metadata") or {}
|
|
252
|
+
self._update_final_duration(meta_payload.get("time"))
|
|
253
|
+
|
|
254
|
+
self._ensure_live()
|
|
255
|
+
|
|
256
|
+
# In verbose mode, show the final result in a panel
|
|
257
|
+
if self.verbose and content.strip():
|
|
258
|
+
final_panel = create_final_panel(
|
|
259
|
+
content,
|
|
260
|
+
title=self._final_panel_title(),
|
|
261
|
+
theme=self.cfg.theme,
|
|
262
|
+
)
|
|
263
|
+
self.console.print(final_panel)
|
|
264
|
+
self.state.printed_final_panel = True
|
|
265
|
+
|
|
266
|
+
def _handle_agent_step_event(self, ev: dict[str, Any]) -> None:
|
|
267
|
+
"""Handle agent step events."""
|
|
268
|
+
# Extract tool information
|
|
269
|
+
(
|
|
270
|
+
tool_name,
|
|
271
|
+
tool_args,
|
|
272
|
+
tool_out,
|
|
273
|
+
tool_calls_info,
|
|
274
|
+
) = self.stream_processor.parse_tool_calls(ev)
|
|
275
|
+
|
|
276
|
+
# Track tools and sub-agents
|
|
277
|
+
self.stream_processor.track_tools_and_agents(
|
|
278
|
+
tool_name, tool_calls_info, is_delegation_tool
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Handle tool execution
|
|
282
|
+
self._handle_agent_step(ev, tool_name, tool_args, tool_out, tool_calls_info)
|
|
261
283
|
|
|
262
284
|
# Update live display
|
|
263
285
|
self._ensure_live()
|
|
264
286
|
|
|
265
287
|
def _finish_running_steps(self) -> None:
|
|
266
288
|
"""Mark any running steps as finished to avoid lingering spinners."""
|
|
267
|
-
for st in
|
|
289
|
+
for st in self.steps.by_id.values():
|
|
268
290
|
if not is_step_finished(st):
|
|
269
291
|
st.finish(None)
|
|
270
292
|
|
|
@@ -289,14 +311,31 @@ class RichStreamRenderer:
|
|
|
289
311
|
if self.verbose and not self.state.printed_final_panel:
|
|
290
312
|
body = ("".join(self.state.buffer) or "").strip()
|
|
291
313
|
if body:
|
|
292
|
-
final_panel = create_final_panel(
|
|
314
|
+
final_panel = create_final_panel(
|
|
315
|
+
body,
|
|
316
|
+
title=self._final_panel_title(),
|
|
317
|
+
theme=self.cfg.theme,
|
|
318
|
+
)
|
|
293
319
|
self.console.print(final_panel)
|
|
294
320
|
self.state.printed_final_panel = True
|
|
295
321
|
|
|
296
|
-
def on_complete(self,
|
|
322
|
+
def on_complete(self, stats: RunStats) -> None:
|
|
297
323
|
"""Handle completion event."""
|
|
298
324
|
self.state.finalizing_ui = True
|
|
299
325
|
|
|
326
|
+
if isinstance(stats, RunStats):
|
|
327
|
+
duration = None
|
|
328
|
+
try:
|
|
329
|
+
if stats.finished_at is not None and stats.started_at is not None:
|
|
330
|
+
duration = max(
|
|
331
|
+
0.0, float(stats.finished_at) - float(stats.started_at)
|
|
332
|
+
)
|
|
333
|
+
except Exception:
|
|
334
|
+
duration = None
|
|
335
|
+
|
|
336
|
+
if duration is not None:
|
|
337
|
+
self._update_final_duration(duration, overwrite=True)
|
|
338
|
+
|
|
300
339
|
# Mark any running steps as finished to avoid lingering spinners
|
|
301
340
|
self._finish_running_steps()
|
|
302
341
|
|
|
@@ -394,15 +433,23 @@ class RichStreamRenderer:
|
|
|
394
433
|
if not self.verbose:
|
|
395
434
|
final_content = (self.state.final_text or "").strip()
|
|
396
435
|
if final_content:
|
|
436
|
+
title = self._final_panel_title()
|
|
397
437
|
return create_final_panel(
|
|
398
438
|
final_content,
|
|
399
|
-
title=
|
|
439
|
+
title=title,
|
|
400
440
|
theme=self.cfg.theme,
|
|
401
441
|
)
|
|
402
442
|
# Dynamic title with spinner + elapsed/hints
|
|
403
443
|
title = self._format_enhanced_main_title()
|
|
404
444
|
return create_main_panel(body, title, self.cfg.theme)
|
|
405
445
|
|
|
446
|
+
def _final_panel_title(self) -> str:
|
|
447
|
+
"""Compose title for the final result panel including duration."""
|
|
448
|
+
title = "Final Result"
|
|
449
|
+
if self.state.final_duration_text:
|
|
450
|
+
title = f"{title} · {self.state.final_duration_text}"
|
|
451
|
+
return title
|
|
452
|
+
|
|
406
453
|
def apply_verbosity(self, verbose: bool) -> None:
|
|
407
454
|
"""Update verbose behaviour at runtime."""
|
|
408
455
|
if self.verbose == verbose:
|
|
@@ -507,27 +554,43 @@ class RichStreamRenderer:
|
|
|
507
554
|
"""Process additional tool calls to avoid duplicates."""
|
|
508
555
|
for call_name, call_args, _ in tool_calls_info or []:
|
|
509
556
|
if call_name and call_name != tool_name:
|
|
510
|
-
self.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
557
|
+
self._process_single_tool_call(
|
|
558
|
+
call_name, call_args, task_id, context_id
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
def _process_single_tool_call(
|
|
562
|
+
self, call_name: str, call_args: Any, task_id: str, context_id: str
|
|
563
|
+
) -> None:
|
|
564
|
+
"""Process a single additional tool call."""
|
|
565
|
+
self._ensure_tool_panel(call_name, call_args, task_id, context_id)
|
|
566
|
+
|
|
567
|
+
st2 = self._create_step_for_tool_call(call_name, call_args, task_id, context_id)
|
|
568
|
+
|
|
569
|
+
if self.stream_processor.server_elapsed_time is not None and st2:
|
|
570
|
+
self._step_server_start_times[st2.step_id] = (
|
|
571
|
+
self.stream_processor.server_elapsed_time
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
def _create_step_for_tool_call(
|
|
575
|
+
self, call_name: str, call_args: Any, task_id: str, context_id: str
|
|
576
|
+
) -> Any:
|
|
577
|
+
"""Create appropriate step for tool call."""
|
|
578
|
+
if is_delegation_tool(call_name):
|
|
579
|
+
return self.steps.start_or_get(
|
|
580
|
+
task_id=task_id,
|
|
581
|
+
context_id=context_id,
|
|
582
|
+
kind="delegate",
|
|
583
|
+
name=call_name,
|
|
584
|
+
args=call_args,
|
|
585
|
+
)
|
|
586
|
+
else:
|
|
587
|
+
return self.steps.start_or_get(
|
|
588
|
+
task_id=task_id,
|
|
589
|
+
context_id=context_id,
|
|
590
|
+
kind="tool",
|
|
591
|
+
name=call_name,
|
|
592
|
+
args=call_args,
|
|
593
|
+
)
|
|
531
594
|
|
|
532
595
|
def _detect_tool_completion(
|
|
533
596
|
self, metadata: dict, content: str
|
|
@@ -941,19 +1004,19 @@ class RichStreamRenderer:
|
|
|
941
1004
|
"""Format step status with elapsed time or duration."""
|
|
942
1005
|
if is_step_finished(step):
|
|
943
1006
|
if step.duration_ms is None:
|
|
944
|
-
return
|
|
1007
|
+
return LESS_THAN_1MS
|
|
945
1008
|
elif step.duration_ms >= 1000:
|
|
946
1009
|
return f"[{step.duration_ms / 1000:.2f}s]"
|
|
947
1010
|
elif step.duration_ms > 0:
|
|
948
1011
|
return f"[{step.duration_ms}ms]"
|
|
949
|
-
return
|
|
1012
|
+
return LESS_THAN_1MS
|
|
950
1013
|
else:
|
|
951
1014
|
# Calculate elapsed time for running steps
|
|
952
1015
|
elapsed = self._calculate_step_elapsed_time(step)
|
|
953
1016
|
if elapsed >= 1:
|
|
954
1017
|
return f"[{elapsed:.2f}s]"
|
|
955
1018
|
ms = int(elapsed * 1000)
|
|
956
|
-
return f"[{ms}ms]" if ms > 0 else
|
|
1019
|
+
return f"[{ms}ms]" if ms > 0 else LESS_THAN_1MS
|
|
957
1020
|
|
|
958
1021
|
def _calculate_step_elapsed_time(self, step: Step) -> float:
|
|
959
1022
|
"""Calculate elapsed time for a running step."""
|
|
@@ -1024,6 +1087,29 @@ class RichStreamRenderer:
|
|
|
1024
1087
|
and sid not in self.stream_processor.current_event_finished_panels
|
|
1025
1088
|
)
|
|
1026
1089
|
|
|
1090
|
+
def _update_final_duration(
|
|
1091
|
+
self, duration: float | None, *, overwrite: bool = False
|
|
1092
|
+
) -> None:
|
|
1093
|
+
"""Store formatted duration for eventual final panels."""
|
|
1094
|
+
if duration is None:
|
|
1095
|
+
return
|
|
1096
|
+
|
|
1097
|
+
try:
|
|
1098
|
+
duration_val = max(0.0, float(duration))
|
|
1099
|
+
except Exception:
|
|
1100
|
+
return
|
|
1101
|
+
|
|
1102
|
+
existing = self.state.final_duration_seconds
|
|
1103
|
+
|
|
1104
|
+
if not overwrite and existing is not None:
|
|
1105
|
+
return
|
|
1106
|
+
|
|
1107
|
+
if overwrite and existing is not None:
|
|
1108
|
+
duration_val = max(existing, duration_val)
|
|
1109
|
+
|
|
1110
|
+
self.state.final_duration_seconds = duration_val
|
|
1111
|
+
self.state.final_duration_text = self._format_elapsed_time(duration_val)
|
|
1112
|
+
|
|
1027
1113
|
def _calculate_elapsed_time(self, meta: dict[str, Any]) -> str:
|
|
1028
1114
|
"""Calculate elapsed time string for running tools."""
|
|
1029
1115
|
server_elapsed = self.stream_processor.server_elapsed_time
|
|
@@ -24,19 +24,19 @@ glaip_sdk/cli/resolution.py,sha256=jXUNpKKhs30n7Ke0uz1Hbny5DTo2_sxvchIhTbeBubE,2
|
|
|
24
24
|
glaip_sdk/cli/rich_helpers.py,sha256=ByUOmK16IisoXWE7nEiI55BF1KWDrm6KCYAxqHu0XOU,825
|
|
25
25
|
glaip_sdk/cli/slash/__init__.py,sha256=Vdv6Y8bu-pA8dxDlyP4XrhudBPivztUozhLAz9vaLig,682
|
|
26
26
|
glaip_sdk/cli/slash/agent_session.py,sha256=-woZkqH70YUSaEHDF9XpxP-cbh36Jx7yuJW7aA3JszI,7078
|
|
27
|
-
glaip_sdk/cli/slash/prompt.py,sha256=
|
|
28
|
-
glaip_sdk/cli/slash/session.py,sha256=
|
|
27
|
+
glaip_sdk/cli/slash/prompt.py,sha256=Cfd6nL1T-F51WNuRCO09RxXfuJn0I1OyBi5dx3xKtaY,8407
|
|
28
|
+
glaip_sdk/cli/slash/session.py,sha256=WZKAwkio1DMK72r6myR8Ou7weIS5JsQutbtVMox7ctc,32515
|
|
29
29
|
glaip_sdk/cli/update_notifier.py,sha256=nfQ-jRQKn-nZyt7EhxNfZq9Z7nBrYjZJKAgAtuHffnw,3410
|
|
30
30
|
glaip_sdk/cli/utils.py,sha256=pgbV0f5rdjAHeZ-ULCntH7HUG6FdFB9kODv0a9puB40,35503
|
|
31
31
|
glaip_sdk/cli/validators.py,sha256=USbBgY86AwuDHO-Q_g8g7hu-ot4NgITBsWjTWIl62ms,5569
|
|
32
32
|
glaip_sdk/client/__init__.py,sha256=nYLXfBVTTWwKjP0e63iumPYO4k5FifwWaELQPaPIKIg,188
|
|
33
33
|
glaip_sdk/client/_agent_payloads.py,sha256=sYlMzrfAdd8KC37qxokLy2uDd3aOhzQirnv7UYlvwYc,16385
|
|
34
|
-
glaip_sdk/client/agents.py,sha256
|
|
34
|
+
glaip_sdk/client/agents.py,sha256=-ORfxYaoX6JkgGS9B7ZCQmr1o9H5KEr0GesbfeiSros,37594
|
|
35
35
|
glaip_sdk/client/base.py,sha256=OPRlAWhZ77rUK0MRGA83-zW5NVhxJ1RgdfcfGOYr8rI,16267
|
|
36
|
-
glaip_sdk/client/main.py,sha256=
|
|
36
|
+
glaip_sdk/client/main.py,sha256=tELAA36rzthnNKTgwZ6lLPb3Au8Wh1mF8Kz-9N-YtCg,8652
|
|
37
37
|
glaip_sdk/client/mcps.py,sha256=-O-I15qjbwfSA69mouHY6g5_qgPWC4rM98VJLpOkh1A,8975
|
|
38
38
|
glaip_sdk/client/run_rendering.py,sha256=fXUj1FBw8n-nAzjI_zaG7-Ap_UXXe0z4tMdL7m2R7Ek,9213
|
|
39
|
-
glaip_sdk/client/tools.py,sha256=
|
|
39
|
+
glaip_sdk/client/tools.py,sha256=rWxfNO30sS468513IoE5PfEaqNq6HBwmcHVh4FzhvYQ,17532
|
|
40
40
|
glaip_sdk/client/validators.py,sha256=NtPsWjQLjj25LiUnmR-WuS8lL5p4MVRaYT9UVRmj9bo,8809
|
|
41
41
|
glaip_sdk/config/constants.py,sha256=B9CSlYG8LYjQuo_vNpqy-eSks3ej37FMcvJMy6d_F4U,888
|
|
42
42
|
glaip_sdk/exceptions.py,sha256=ILquxC4QGPFR9eY6RpeXzkQsblfsvZMGFqz38ZjeW3E,2345
|
|
@@ -45,16 +45,16 @@ glaip_sdk/payload_schemas/__init__.py,sha256=fJamlkpS3IfS9xyKAQaUbnalvrtG5Ied69O
|
|
|
45
45
|
glaip_sdk/payload_schemas/agent.py,sha256=nlizuv2w4SVzmMJSE90rE6Ll0Hfpcr5hvPsW_NtXCV0,3204
|
|
46
46
|
glaip_sdk/rich_components.py,sha256=veaps1hrSkC3nSVunAevvynSux8Cg3yFEDmbJk66p7w,1267
|
|
47
47
|
glaip_sdk/utils/__init__.py,sha256=fmVGcUFa7G0CCfSMSqfNU2BqFl36G1gOFyDfTvtJfVw,926
|
|
48
|
-
glaip_sdk/utils/agent_config.py,sha256=
|
|
48
|
+
glaip_sdk/utils/agent_config.py,sha256=p3uK5qC0M5uQv9uY7-U8ej11Vh81fwKAPSsYcRoNdlk,7342
|
|
49
49
|
glaip_sdk/utils/client_utils.py,sha256=x27kHQNOxvyVN5GLUiymi0eHzkXRKw-x3s0q0VkMvY4,13938
|
|
50
50
|
glaip_sdk/utils/display.py,sha256=94s9lYF_8ra8jpeqOkbVrUm8oidtCE6OtucyxLQPKmU,3105
|
|
51
51
|
glaip_sdk/utils/general.py,sha256=V5hJrIpYDvDsldU_nChHpuvV2AwhFLUI7Qvcaihq_8A,2270
|
|
52
52
|
glaip_sdk/utils/import_export.py,sha256=jEhl5U6hWWMR1wo5AXpV-_jN_56DcWcemOa2UaFHapk,5217
|
|
53
53
|
glaip_sdk/utils/rendering/__init__.py,sha256=vXjwk5rPhhfPyD8S0DnV4GFFEtPJp4HCCg1Um9SXfs0,70
|
|
54
|
-
glaip_sdk/utils/rendering/formatting.py,sha256=
|
|
54
|
+
glaip_sdk/utils/rendering/formatting.py,sha256=mS4xvbNy1NSH4nXm8mKj03jEXMNinxfbtVJGYf3sXlk,7770
|
|
55
55
|
glaip_sdk/utils/rendering/models.py,sha256=AM9JbToyA3zrAzXQYjh6oxjBkgZDfWEbs5MmNKODnOY,2259
|
|
56
56
|
glaip_sdk/utils/rendering/renderer/__init__.py,sha256=EXwVBmGkSYcype4ocAXo69Z1kXu0gpNXmhH5LW0_B7A,2939
|
|
57
|
-
glaip_sdk/utils/rendering/renderer/base.py,sha256=
|
|
57
|
+
glaip_sdk/utils/rendering/renderer/base.py,sha256=Tk-N0Fpi4kyuJYb-YaYEAytpho2vcWBKdDmzqIK_Pto,45543
|
|
58
58
|
glaip_sdk/utils/rendering/renderer/config.py,sha256=-P35z9JO_1ypJXAqxJ1ybHraH4i-I1LPopeW3Lh7ACE,785
|
|
59
59
|
glaip_sdk/utils/rendering/renderer/console.py,sha256=4cLOw4Q1fkHkApuj6dWW8eYpeYdcT0t2SO5MbVt5UTc,1844
|
|
60
60
|
glaip_sdk/utils/rendering/renderer/debug.py,sha256=FEYxAu4ZB0CjrJKevqQ2TKDgElA2cf6GqZXCNm12sNQ,3721
|
|
@@ -67,7 +67,7 @@ glaip_sdk/utils/rich_utils.py,sha256=-Ij-1bIJvnVAi6DrfftchIlMcvOTjVmSE0Qqax0EY_s
|
|
|
67
67
|
glaip_sdk/utils/run_renderer.py,sha256=d_VMI6LbvHPUUeRmGqh5wK_lHqDEIAcym2iqpbtDad0,1365
|
|
68
68
|
glaip_sdk/utils/serialization.py,sha256=AFbucakFaCtQDfcgsm2gHZ1iZDA8OaJSZUsS6FhWFR0,12820
|
|
69
69
|
glaip_sdk/utils/validation.py,sha256=QNORcdyvuliEs4EH2_mkDgmoyT9utgl7YNhaf45SEf8,6992
|
|
70
|
-
glaip_sdk-0.0.
|
|
71
|
-
glaip_sdk-0.0.
|
|
72
|
-
glaip_sdk-0.0.
|
|
73
|
-
glaip_sdk-0.0.
|
|
70
|
+
glaip_sdk-0.0.18.dist-info/METADATA,sha256=IaSz5FAxBzrHDKtbgjyg6h4vyClUS7C3M4ovlax0rv4,5164
|
|
71
|
+
glaip_sdk-0.0.18.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
72
|
+
glaip_sdk-0.0.18.dist-info/entry_points.txt,sha256=EGs8NO8J1fdFMWA3CsF7sKBEvtHb_fujdCoNPhfMouE,47
|
|
73
|
+
glaip_sdk-0.0.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|