diff --git a/gateway/src/desklock_gateway/tatlock.py b/gateway/src/desklock_gateway/tatlock.py index f7f2395..abab7e6 100644 --- a/gateway/src/desklock_gateway/tatlock.py +++ b/gateway/src/desklock_gateway/tatlock.py @@ -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 reasoning (Open WebUI convention). +# Strip it: it must never reach TTS or the on-screen reply text. +_THINK_RE = re.compile(r".*?\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 diff --git a/gateway/tests/test_tatlock.py b/gateway/tests/test_tatlock.py new file mode 100644 index 0000000..defc880 --- /dev/null +++ b/gateway/tests/test_tatlock.py @@ -0,0 +1,10 @@ +from desklock_gateway.tatlock import strip_reasoning + + +def test_strip_reasoning_removes_think_block() -> None: + raw = "\nDELEGATE: none\nREASON: simple\n\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."