power-loop 3.3.0__tar.gz → 3.4.0__tar.gz
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.
- {power_loop-3.3.0 → power_loop-3.4.0}/PKG-INFO +1 -1
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/__init__.py +3 -1
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/representation.py +102 -34
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop.egg-info/PKG-INFO +1 -1
- {power_loop-3.3.0 → power_loop-3.4.0}/LICENSE +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/README.md +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/capabilities.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/interface.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/llm_factory.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/_vendor/llm_client/multimodal.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/follow_up.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/sink.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/stateful_loop.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/system_prompt.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/agent/types.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/errors.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/event_payloads.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/events.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/handlers.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/hook_contexts.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/hooks.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/messages.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/protocols.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contracts/tools.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/_redact.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/jsonl_sink.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/logging_sink.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/mcp.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/metrics_sink.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/contrib/otel_sink.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/agent_context.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/events.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/hooks.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/phase.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/pipeline.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/runner.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/core/state.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/py.typed +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/blackboard.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/budget.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/cancellation.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/compact.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/env.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/exec_backend.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/fold.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/fold_adapter.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/history_projector.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/history_sanitize.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/human_input.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/memory.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/notes.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/provider.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/retry.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/runtime_state.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/session_store.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/skills.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/spec.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/backends/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/backends/mysql.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/backends/postgres.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/backends/sqlite.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/capabilities.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/db.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/dialect.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/factory.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/schema.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/store.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/store/types.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/structured.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/stub_provider.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/runtime/timers.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/blackboard.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/default_manifest.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/default_tools.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/registry.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/tools/spawn_agent.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/__init__.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/api.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/engine.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/introspect.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/journal.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/result.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/resume.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/runner.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/spec.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/subprocess_executor.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/tool.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop/workflow/worker.py +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop.egg-info/SOURCES.txt +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop.egg-info/dependency_links.txt +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop.egg-info/requires.txt +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/power_loop.egg-info/top_level.txt +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/pyproject.toml +0 -0
- {power_loop-3.3.0 → power_loop-3.4.0}/setup.cfg +0 -0
|
@@ -15,7 +15,7 @@ Stability tiers
|
|
|
15
15
|
无版本承诺,可随时变更或删除。
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
__version__ = "3.
|
|
18
|
+
__version__ = "3.4.0"
|
|
19
19
|
|
|
20
20
|
# Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
|
|
21
21
|
# a custom LLMService) don't reach into the internal vendored transport package (H3.4).
|
|
@@ -177,6 +177,7 @@ from power_loop.runtime.representation import (
|
|
|
177
177
|
ProjectedRepresentation,
|
|
178
178
|
ProjectedRow,
|
|
179
179
|
ProjectedSend,
|
|
180
|
+
ProjectionRenderConfig,
|
|
180
181
|
Representation,
|
|
181
182
|
VerbatimRepresentation,
|
|
182
183
|
)
|
|
@@ -338,6 +339,7 @@ __all__ = [
|
|
|
338
339
|
"Representation",
|
|
339
340
|
"VerbatimRepresentation",
|
|
340
341
|
"ProjectedRepresentation",
|
|
342
|
+
"ProjectionRenderConfig",
|
|
341
343
|
"FoldStrategy",
|
|
342
344
|
"FoldContext",
|
|
343
345
|
"FoldResult",
|
|
@@ -23,6 +23,7 @@ from __future__ import annotations
|
|
|
23
23
|
import json
|
|
24
24
|
from collections import deque
|
|
25
25
|
from dataclasses import dataclass, field
|
|
26
|
+
from dataclasses import fields as dataclass_fields
|
|
26
27
|
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
27
28
|
|
|
28
29
|
from power_loop.runtime.store.types import MessageRow, ProjectMessageRow
|
|
@@ -40,6 +41,7 @@ __all__ = [
|
|
|
40
41
|
"Representation",
|
|
41
42
|
"VerbatimRepresentation",
|
|
42
43
|
"ProjectedRepresentation",
|
|
44
|
+
"ProjectionRenderConfig",
|
|
43
45
|
"ProjectedRow",
|
|
44
46
|
"ProjectedSend",
|
|
45
47
|
]
|
|
@@ -175,6 +177,34 @@ class VerbatimRepresentation:
|
|
|
175
177
|
# ── Projection (generic structured summary; no LLM) ─────────────────────────────
|
|
176
178
|
|
|
177
179
|
|
|
180
|
+
@dataclass
|
|
181
|
+
class ProjectionRenderConfig:
|
|
182
|
+
"""Tunable format knobs for :meth:`ProjectedRepresentation.render` — they change the SHAPE of the
|
|
183
|
+
rendered text only, never what is stored. Every field is a plain scalar so the whole config
|
|
184
|
+
round-trips through JSON (a host can surface it in an admin UI and retune the rendered context
|
|
185
|
+
live, then pass it back via ``ProjectedRepresentation(render_config=…)``). Placeholders: ``{n}`` in
|
|
186
|
+
a tag → the send_index (empty tag or ``None`` index → no tag); ``{range}`` in ``fold_note`` → the
|
|
187
|
+
folded ``#lo–#hi`` span. The defaults reproduce the built-in rendering exactly."""
|
|
188
|
+
|
|
189
|
+
user_tag: str = "[#{n}] " # prefix on a user/input row; {n}=send_index
|
|
190
|
+
project_tag: str = "#{n} " # prefix on a project/assistant row
|
|
191
|
+
tools_header: str = "[tools] " # before the tool list in a project row
|
|
192
|
+
tool_sep: str = "; " # between tools
|
|
193
|
+
tool_arg_sep: str = ", " # between a tool's k=v fields
|
|
194
|
+
include_tools: bool = True # render the tool list at all
|
|
195
|
+
include_final_text: bool = True # render the project row's trailing final_text
|
|
196
|
+
empty_project: str = "(no output)" # a project row that renders to nothing
|
|
197
|
+
fold_note: str = "[older sends {range} folded — recall_send(send_index=N) to expand]"
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_dict(cls, data: dict[str, Any] | None) -> ProjectionRenderConfig:
|
|
201
|
+
"""Build from a plain dict (e.g. JSON config), silently ignoring unknown keys."""
|
|
202
|
+
if not data:
|
|
203
|
+
return cls()
|
|
204
|
+
names = {f.name for f in dataclass_fields(cls)}
|
|
205
|
+
return cls(**{k: v for k, v in data.items() if k in names})
|
|
206
|
+
|
|
207
|
+
|
|
178
208
|
@dataclass
|
|
179
209
|
class ProjectedRepresentation:
|
|
180
210
|
"""Generic, deterministic, no-LLM per-send projection. Each send →
|
|
@@ -183,14 +213,24 @@ class ProjectedRepresentation:
|
|
|
183
213
|
``project`` row: ``{"tools": [...], "final_text": ...}``. Each tool call is summarized via its
|
|
184
214
|
``ToolDefinition.project`` hook when present, else a truncating fallback. Rendered to terse
|
|
185
215
|
plain text with NO tool-protocol structure. (This is the old ``DefaultDeterministicProjector``
|
|
186
|
-
MINUS its fold knobs/``compact()`` — folding now lives on :class:`FoldStrategy`.)
|
|
216
|
+
MINUS its fold knobs/``compact()`` — folding now lives on :class:`FoldStrategy`.)
|
|
217
|
+
|
|
218
|
+
The render is customizable two ways without copy-pasting it: pass a :class:`ProjectionRenderConfig`
|
|
219
|
+
(``render_config=`` — JSON-friendly format knobs, retunable from config/an admin UI) and/or
|
|
220
|
+
override one of the small per-row methods (``render_row`` → ``render_user_row`` /
|
|
221
|
+
``render_project_row`` / ``render_compact_row``). Defaults reproduce the prior output exactly."""
|
|
187
222
|
|
|
188
223
|
kind: str = "projection"
|
|
189
224
|
version: int = 1
|
|
190
225
|
max_chars: int = 300 # per-field truncation budget
|
|
226
|
+
#: Format knobs for render() — see :class:`ProjectionRenderConfig`. A plain dict is coerced (so a
|
|
227
|
+
#: host can pass JSON config straight through). Subclasses can ALSO override the render_* methods.
|
|
228
|
+
render_config: ProjectionRenderConfig = field(default_factory=ProjectionRenderConfig)
|
|
191
229
|
|
|
192
230
|
def __post_init__(self) -> None:
|
|
193
231
|
_validate_representation_params(version=self.version, max_chars=self.max_chars)
|
|
232
|
+
if isinstance(self.render_config, dict):
|
|
233
|
+
self.render_config = ProjectionRenderConfig.from_dict(self.render_config)
|
|
194
234
|
|
|
195
235
|
def project_send(
|
|
196
236
|
self, send_rows: list[MessageRow], *, send_index: int, tool_registry: ToolRegistry | None
|
|
@@ -264,55 +304,83 @@ class ProjectedRepresentation:
|
|
|
264
304
|
return {"name": name, "result": _truncate(result_str, self.max_chars)}
|
|
265
305
|
|
|
266
306
|
# rendering ----------------------------------------------------------------
|
|
307
|
+
# Orchestration only; the per-row shapes live in small overridable methods (override exactly the
|
|
308
|
+
# one you want instead of copy-pasting render) and the format lives in render_config (retune from
|
|
309
|
+
# config, no code). Each rendered send is tagged with its ``#N`` send_index so the model can call
|
|
310
|
+
# recall_send(send_index=N) on a folded earlier turn — the tool docstring + the host's
|
|
311
|
+
# RECALL_SEND_NOTE tell it to use "the #N the summary shows", so the tags MUST be emitted (else
|
|
312
|
+
# recall_send is undiscoverable); changing user_tag/project_tag away from a ``{n}`` form is at the
|
|
313
|
+
# host's own risk.
|
|
267
314
|
def render(self, rows: list[ProjectMessageRow]) -> list[LoopMessage]:
|
|
268
|
-
# Each rendered send is tagged with its ``#N`` send_index so the model can call
|
|
269
|
-
# recall_send(send_index=N) on a folded/compacted earlier turn — the tool docstring and the
|
|
270
|
-
# host's RECALL_SEND_NOTE both tell it to use "the #N the summary shows", so render MUST
|
|
271
|
-
# actually emit them (else recall_send is undiscoverable). The folded compact carries its
|
|
272
|
-
# covered range.
|
|
273
315
|
out: list[LoopMessage] = []
|
|
274
316
|
for r in rows:
|
|
275
|
-
|
|
276
|
-
if
|
|
277
|
-
content = r.content or {}
|
|
278
|
-
# ``input`` since 3.3; ``human`` is the pre-3.3 key — read both so old projection
|
|
279
|
-
# rows still render correctly after upgrade.
|
|
280
|
-
inputs = content.get("input")
|
|
281
|
-
if inputs is None:
|
|
282
|
-
inputs = content.get("human") or []
|
|
283
|
-
tag = f"[#{si}] " if si is not None else ""
|
|
284
|
-
out.append({"role": "user", "content": tag + "\n".join(str(h) for h in inputs)})
|
|
285
|
-
elif r.kind == "project":
|
|
286
|
-
tag = f"#{si} " if si is not None else ""
|
|
287
|
-
out.append({"role": "assistant", "content": tag + self._render_project(r.content)})
|
|
288
|
-
elif r.kind == "compact":
|
|
289
|
-
msg = _render_compact_row(r)
|
|
290
|
-
lo, hi = r.compact_from_send, r.compact_to_send
|
|
291
|
-
if lo is not None and hi is not None and hi >= lo > 0:
|
|
292
|
-
rng = f"#{lo}" if lo == hi else f"#{lo}–#{hi}"
|
|
293
|
-
msg = {
|
|
294
|
-
"role": "user",
|
|
295
|
-
"content": f"[older sends {rng} folded — recall_send(send_index=N) to "
|
|
296
|
-
f"expand]\n{msg['content']}",
|
|
297
|
-
}
|
|
317
|
+
msg = self.render_row(r)
|
|
318
|
+
if msg is not None:
|
|
298
319
|
out.append(msg)
|
|
299
320
|
return out
|
|
300
321
|
|
|
322
|
+
def render_row(self, r: ProjectMessageRow) -> LoopMessage | None:
|
|
323
|
+
"""Dispatch one stored row to its per-kind renderer. Override to add a kind / change routing;
|
|
324
|
+
an unhandled kind returns None (skipped)."""
|
|
325
|
+
if r.kind == "user":
|
|
326
|
+
return self.render_user_row(r)
|
|
327
|
+
if r.kind == "project":
|
|
328
|
+
return self.render_project_row(r)
|
|
329
|
+
if r.kind == "compact":
|
|
330
|
+
return self.render_compact_row(r)
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
def render_user_row(self, r: ProjectMessageRow) -> LoopMessage:
|
|
334
|
+
content = r.content or {}
|
|
335
|
+
# ``input`` since 3.3; ``human`` is the pre-3.3 key — read both so old rows still render.
|
|
336
|
+
inputs = content.get("input")
|
|
337
|
+
if inputs is None:
|
|
338
|
+
inputs = content.get("human") or []
|
|
339
|
+
return {
|
|
340
|
+
"role": "user",
|
|
341
|
+
"content": self._send_tag(self.render_config.user_tag, r.send_index)
|
|
342
|
+
+ "\n".join(str(h) for h in inputs),
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
def render_project_row(self, r: ProjectMessageRow) -> LoopMessage:
|
|
346
|
+
return {
|
|
347
|
+
"role": "assistant",
|
|
348
|
+
"content": self._send_tag(self.render_config.project_tag, r.send_index)
|
|
349
|
+
+ self._render_project(r.content),
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
def render_compact_row(self, r: ProjectMessageRow) -> LoopMessage:
|
|
353
|
+
msg = _render_compact_row(r)
|
|
354
|
+
lo, hi = r.compact_from_send, r.compact_to_send
|
|
355
|
+
if lo is not None and hi is not None and hi >= lo > 0:
|
|
356
|
+
rng = f"#{lo}" if lo == hi else f"#{lo}–#{hi}"
|
|
357
|
+
note = self.render_config.fold_note.replace("{range}", rng)
|
|
358
|
+
return {"role": "user", "content": f"{note}\n{msg['content']}"}
|
|
359
|
+
return msg
|
|
360
|
+
|
|
361
|
+
@staticmethod
|
|
362
|
+
def _send_tag(template: str, send_index: int | None) -> str:
|
|
363
|
+
"""A send_index tag from a ``{n}`` template; empty template or ``None`` index → no tag."""
|
|
364
|
+
if send_index is None or not template:
|
|
365
|
+
return ""
|
|
366
|
+
return template.replace("{n}", str(send_index))
|
|
367
|
+
|
|
301
368
|
def _render_tool(self, t: dict[str, Any]) -> str:
|
|
302
369
|
name = t.get("name", "?")
|
|
303
370
|
rest = {k: v for k, v in (t or {}).items() if k != "name"}
|
|
304
371
|
if not rest:
|
|
305
372
|
return str(name)
|
|
306
|
-
body =
|
|
373
|
+
body = self.render_config.tool_arg_sep.join(f"{k}={v}" for k, v in rest.items())
|
|
307
374
|
return f"{name}({body})"
|
|
308
375
|
|
|
309
376
|
def _render_project(self, content: dict[str, Any] | None) -> str:
|
|
310
377
|
content = content or {}
|
|
378
|
+
cfg = self.render_config
|
|
311
379
|
parts: list[str] = []
|
|
312
380
|
tools = content.get("tools") or []
|
|
313
|
-
if tools:
|
|
314
|
-
parts.append(
|
|
381
|
+
if tools and cfg.include_tools:
|
|
382
|
+
parts.append(cfg.tools_header + cfg.tool_sep.join(self._render_tool(t) for t in tools))
|
|
315
383
|
ft = content.get("final_text")
|
|
316
|
-
if ft:
|
|
384
|
+
if ft and cfg.include_final_text:
|
|
317
385
|
parts.append(str(ft))
|
|
318
|
-
return "\n".join(parts) if parts else
|
|
386
|
+
return "\n".join(parts) if parts else cfg.empty_project
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|