"""OpenAI embedding helper for associative memory tier. Single public function: ``embed_text(text) -> list[float] | None``. Returns None on any failure — callers must implement a keyword fallback. Never raises; all exceptions are logged as warnings. """ from __future__ import annotations import logging from openai import AsyncOpenAI logger = logging.getLogger(__name__) _MAX_INPUT_CHARS = 8000 _EMBEDDING_MODEL = "text-embedding-3-small" async def embed_text(text: str) -> list[float] | None: """Call OpenAI text-embedding-3-small. Return None on failure (caller falls back to keyword).""" try: client = AsyncOpenAI() truncated = text[:_MAX_INPUT_CHARS] response = await client.embeddings.create( input=truncated, model=_EMBEDDING_MODEL, ) result: list[float] = response.data[0].embedding logger.debug("embeddings: embed_text dims=%d", len(result)) return result except Exception as exc: logger.warning("embeddings: embed_text failed: %s", exc) return None