chore: embeddings.py warns on fallback for non-default models (T107)
This commit is contained in:
@@ -10,6 +10,7 @@ EmbeddingResult shape stays the same, only the generator changes.
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import math
|
||||
import struct
|
||||
|
||||
@@ -18,6 +19,8 @@ from pydantic import BaseModel
|
||||
from chat.llm.client import LLMClient
|
||||
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_EMBEDDING_DIM = 384
|
||||
DEFAULT_EMBEDDING_MODEL = "pseudo-sha256-384"
|
||||
FALLBACK_EMBEDDING_MODEL = "fallback"
|
||||
@@ -93,7 +96,15 @@ async def generate_embedding(
|
||||
return EmbeddingResult(vector=_pseudo_embed(text, dim), model=model, dim=dim)
|
||||
|
||||
# Future: real embedding via client.embed(...). Phase 4.5 work.
|
||||
# For Phase 4, any non-default model falls through to fallback.
|
||||
# For Phase 4, any non-default model falls through to fallback —
|
||||
# warn so misconfigured callers (e.g., a real-model swap that isn't
|
||||
# wired up yet) don't silently degrade to a zero vector.
|
||||
_log.warning(
|
||||
"generate_embedding: non-default model %r returned fallback "
|
||||
"(model client.embed() not yet implemented in Phase 4.5+); "
|
||||
"downstream search will degrade silently. Configure a supported model.",
|
||||
model,
|
||||
)
|
||||
return EmbeddingResult(
|
||||
vector=[0.0] * dim, model=FALLBACK_EMBEDDING_MODEL, dim=dim
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ The pseudo path doesn't touch the LLMClient, so we pass an empty
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import math
|
||||
|
||||
import pytest
|
||||
@@ -89,3 +90,33 @@ async def test_generate_embedding_unit_normalized():
|
||||
result = await generate_embedding(_client(), text="some non-empty text")
|
||||
norm_sq = sum(x * x for x in result.vector)
|
||||
assert math.isclose(norm_sq, 1.0, abs_tol=1e-6)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_embedding_non_default_model_logs_warning(caplog):
|
||||
"""T107: non-default model falls through to fallback and must warn.
|
||||
|
||||
A Phase 4.5+ caller pointing at a real model that isn't yet wired
|
||||
up would otherwise silently degrade (zero vector → useless cosine).
|
||||
The warning surfaces the misconfiguration in logs.
|
||||
"""
|
||||
caplog.set_level(logging.WARNING, logger="chat.services.embeddings")
|
||||
result = await generate_embedding(_client(), text="hello", model="real-model")
|
||||
|
||||
# Behavior unchanged: still returns the fallback sentinel.
|
||||
assert result.model == FALLBACK_EMBEDDING_MODEL == "fallback"
|
||||
assert all(x == 0.0 for x in result.vector)
|
||||
|
||||
# Warning fired and names the offending model.
|
||||
warnings = [r for r in caplog.records if r.levelno == logging.WARNING]
|
||||
assert any("non-default model" in r.getMessage() for r in warnings)
|
||||
assert any("real-model" in r.getMessage() for r in warnings)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_embedding_default_model_does_not_warn(caplog):
|
||||
"""T107: the silent default path must stay silent."""
|
||||
caplog.set_level(logging.WARNING, logger="chat.services.embeddings")
|
||||
await generate_embedding(_client(), text="hello")
|
||||
warnings = [r for r in caplog.records if r.levelno == logging.WARNING]
|
||||
assert warnings == []
|
||||
|
||||
Reference in New Issue
Block a user