DeskLock gives the Tatlock butler a face and voice on a Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C round display in the living room. - firmware/: ESP-IDF project targeting esp32p4 with the Waveshare XC BSP - gateway/: FastAPI voice bridge (faster-whisper STT, Tatlock chat, Piper TTS) - docs/architecture.md: component design and device<->gateway WS protocol Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
37 lines
1.2 KiB
Python
37 lines
1.2 KiB
Python
"""Client for the Tatlock butler's OpenAI-compatible chat API."""
|
|
|
|
import httpx
|
|
|
|
from .config import settings
|
|
|
|
|
|
class TatlockClient:
|
|
def __init__(self, base_url: str | None = None) -> None:
|
|
self._client = httpx.AsyncClient(
|
|
base_url=base_url or settings.tatlock_base_url,
|
|
timeout=httpx.Timeout(120.0, connect=5.0),
|
|
)
|
|
self._history: list[dict[str, str]] = []
|
|
|
|
async def ask(self, text: str) -> str:
|
|
"""Send one user utterance, return Tatlock's reply, keeping conversation history."""
|
|
self._history.append({"role": "user", "content": text})
|
|
response = await self._client.post(
|
|
"/v1/chat/completions",
|
|
json={
|
|
"model": settings.tatlock_model,
|
|
"messages": self._history,
|
|
"stream": False,
|
|
},
|
|
)
|
|
response.raise_for_status()
|
|
reply = response.json()["choices"][0]["message"]["content"]
|
|
self._history.append({"role": "assistant", "content": reply})
|
|
return reply
|
|
|
|
def reset(self) -> None:
|
|
self._history.clear()
|
|
|
|
async def aclose(self) -> None:
|
|
await self._client.aclose()
|