Strip Tatlock <think> reasoning before TTS and history
Live smoke test showed /v1/chat/completions replies open with the Steward's <think> block (Open WebUI convention) — without stripping, the device would speak the internal monologue aloud. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
"""Client for the Tatlock butler's OpenAI-compatible chat API."""
|
||||
|
||||
import re
|
||||
|
||||
import httpx
|
||||
|
||||
from .config import settings
|
||||
|
||||
# Tatlock prefixes replies with <think> reasoning (Open WebUI convention).
|
||||
# Strip it: it must never reach TTS or the on-screen reply text.
|
||||
_THINK_RE = re.compile(r"<think>.*?</think>\s*", re.DOTALL)
|
||||
|
||||
|
||||
def strip_reasoning(text: str) -> str:
|
||||
return _THINK_RE.sub("", text).strip()
|
||||
|
||||
|
||||
class TatlockClient:
|
||||
def __init__(self, base_url: str | None = None) -> None:
|
||||
@@ -25,7 +35,7 @@ class TatlockClient:
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
reply = response.json()["choices"][0]["message"]["content"]
|
||||
reply = strip_reasoning(response.json()["choices"][0]["message"]["content"])
|
||||
self._history.append({"role": "assistant", "content": reply})
|
||||
return reply
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
from desklock_gateway.tatlock import strip_reasoning
|
||||
|
||||
|
||||
def test_strip_reasoning_removes_think_block() -> None:
|
||||
raw = "<think>\nDELEGATE: none\nREASON: simple\n</think>\nGood evening. I am Tatlock."
|
||||
assert strip_reasoning(raw) == "Good evening. I am Tatlock."
|
||||
|
||||
|
||||
def test_strip_reasoning_passthrough_without_think() -> None:
|
||||
assert strip_reasoning("Just an answer.") == "Just an answer."
|
||||
Reference in New Issue
Block a user