From 800cab8d36e0273a3422aef44f05a19578c1d8c9 Mon Sep 17 00:00:00 2001 From: serversdown Date: Sun, 5 Jul 2026 03:29:34 +0000 Subject: [PATCH] =?UTF-8?q?feat(prompting):=20Phase=20C=20=E2=80=94=20make?= =?UTF-8?q?=20tool-backends=20config-driven=20(MI50-ready=20flip)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling MI50 function-calling is now a config flip, not a code change: cfg.tool_backends (env TOOL_BACKENDS, default "cloud") drives which backends get tool specs. Once the MI50 llama.cpp server runs with --jinja + a tool-capable model, set TOOL_BACKENDS="cloud,mi50" and MI50 chat drives the same tool contract as cloud. Default unchanged (cloud-only), so this is safe with the MI50 down/ unverified — no live flip made (server is currently offline; --jinja unconfirmed). Co-Authored-By: Claude Opus 4.8 (1M context) --- lyra/chat.py | 13 +++++++------ lyra/config.py | 5 +++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lyra/chat.py b/lyra/chat.py index 431e98b..f6b4460 100644 --- a/lyra/chat.py +++ b/lyra/chat.py @@ -15,10 +15,11 @@ from lyra import tools as toolkit from lyra.llm import Backend MAX_TOOL_ROUNDS = 5 # cap tool-call iterations per turn -# Backends that support function-calling. The MI50's llama.cpp server only does -# tools when launched with --jinja; until it is, keep tools to cloud so MI50 chat -# doesn't 500 on the tools param. Add "mi50" here once that flag is set. -TOOL_BACKENDS = {"cloud"} +# Which backends get function-calling tools is config-driven (cfg.tool_backends, +# env TOOL_BACKENDS, default "cloud"). The MI50's llama.cpp server only does tools +# when launched with --jinja + a tool-capable model, else it 500s on the tools +# param — so enabling "mi50" is a config flip once that precondition holds (Phase C), +# not a code change. See docs/superpowers/specs/2026-07-01-poker-prompts-design.md. _TANGLED = "(I got tangled using my tools there — say that again?)" @@ -97,7 +98,7 @@ def respond(session_id: str, user_msg: str, backend: Backend = "cloud", turn = mind.assemble(session_id, user_msg, backend, model) messages = turn.messages - tool_specs = toolkit.specs(turn.mode.tools) if backend in TOOL_BACKENDS else None + tool_specs = toolkit.specs(turn.mode.tools) if backend in cfg.tool_backends else None ctx = {"session_id": session_id, "backend": backend} # Persist the user turn before the tool loop so its timestamp precedes any @@ -127,7 +128,7 @@ def respond_stream(session_id: str, user_msg: str, backend: Backend = "cloud", turn = mind.assemble(session_id, user_msg, backend, model) messages = turn.messages - tool_specs = toolkit.specs(turn.mode.tools) if backend in TOOL_BACKENDS else None + tool_specs = toolkit.specs(turn.mode.tools) if backend in cfg.tool_backends else None ctx = {"session_id": session_id, "backend": backend} mouth = _mouth_target(cfg, backend, model) diff --git a/lyra/config.py b/lyra/config.py index e9a84ba..850aa5c 100644 --- a/lyra/config.py +++ b/lyra/config.py @@ -46,6 +46,10 @@ class Config: # External input feed (her #1: react to the world). Comma-separated RSS/Atom URLs. feeds: tuple[str, ...] feed_react_prob: float # chance a would-be new thread reacts to a feed item instead + # Backends allowed to receive function-calling tools. Default cloud-only. Add + # "mi50" ONLY once its llama.cpp server runs with --jinja + a tool-capable model, + # else it 500s on the tools param (Phase C). Env: TOOL_BACKENDS="cloud,mi50". + tool_backends: tuple[str, ...] def _csv(name: str, default: str) -> tuple[str, ...]: @@ -90,4 +94,5 @@ def load() -> Config: mouth_model=os.getenv("MOUTH_MODEL") or None, feeds=_csv("LYRA_FEEDS", "https://hnrss.org/frontpage,https://www.pokernews.com/rss.php"), feed_react_prob=float(os.getenv("FEED_REACT_PROB", "0.5")), + tool_backends=_csv("TOOL_BACKENDS", "cloud"), )