From 5f16bb575a8cefcc40a46c78203a998217e0acfe Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 27 Apr 2026 05:47:55 -0400 Subject: [PATCH] feat: LLMClient Protocol gains embed() method (T112.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds async def embed(self, text: str, *, model: str) -> list[float] to the LLMClient Protocol so Phase 4.5 can wire a real-embedding swap without changing call sites. Protocol is structural — existing implementations that don't use it remain compatible; downstream implementations (FeatherlessClient, MockLLMClient) ship in T112.2 and T112.3. --- chat/llm/client.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/chat/llm/client.py b/chat/llm/client.py index ca34a2d..5c079e1 100644 --- a/chat/llm/client.py +++ b/chat/llm/client.py @@ -12,3 +12,11 @@ class Message: class LLMClient(Protocol): async def generate(self, messages: Sequence[Message], *, model: str, **params) -> str: ... def stream(self, messages: Sequence[Message], *, model: str, **params) -> AsyncIterator[str]: ... + # T112 (Phase 4.5): real-embedding seam. Implementations either call a + # provider's ``/v1/embeddings`` endpoint or, when the provider doesn't + # expose embeddings (e.g. Featherless today), raise ``NotImplementedError`` + # so ``generate_embedding`` can catch it and degrade to the zero-vector + # fallback. The Protocol is structural, so this method only needs to + # exist on implementations; existing callers that don't use it are + # unaffected. + async def embed(self, text: str, *, model: str) -> list[float]: ...