fix(embedding): pre-warm embedding client at startup
Addresses coderabbit review: Tokenizer.from_pretrained for the hf: spec fetches from the Hugging Face Hub and ran synchronously under the singleton lock on the first embed/batch_embed call, stalling the event loop and blocking concurrent callers behind the download. Add EmbeddingClient.warmup() and call it from the API lifespan and the deriver startup, right after validate_embedding_schema. This moves any tokenizer download off the request path and fails the process fast on a bad hf: spec instead of stalling the first request. tiktoken: and file: paths are unaffected (no network); unset tokenizer still auto-detects lazily but never hits the network.
This commit is contained in:
parent
e83ab842c1
commit
8c0709eec6
|
|
@ -71,6 +71,11 @@ async def run_deriver():
|
|||
# gate the API runs in its lifespan. Inside the try block so the
|
||||
# telemetry buffer is still flushed if validation raises.
|
||||
await validate_embedding_schema(engine)
|
||||
# Eagerly build the embedding client so an hf: tokenizer download
|
||||
# happens here, not on the first reconciler call under the lock.
|
||||
from src.embedding_client import embedding_client
|
||||
|
||||
embedding_client.warmup()
|
||||
await main()
|
||||
finally:
|
||||
# Shutdown telemetry (flush CloudEvents buffer)
|
||||
|
|
|
|||
|
|
@ -799,6 +799,16 @@ class EmbeddingClient:
|
|||
def _resolve_runtime_config(self) -> EmbeddingModelConfig:
|
||||
return resolve_embedding_model_config(settings.EMBEDDING.MODEL_CONFIG)
|
||||
|
||||
def warmup(self) -> None:
|
||||
"""Eagerly construct the underlying client at process startup.
|
||||
|
||||
``hf:`` tokenizer specs fetch from the Hugging Face Hub; doing that on
|
||||
the first request would stall the event loop under the singleton lock.
|
||||
Calling this from the API and deriver lifespans moves any tokenizer
|
||||
download off the request path and fails the process fast on a bad spec.
|
||||
"""
|
||||
self._get_client()
|
||||
|
||||
def _get_settings_signature(self) -> tuple[object, ...]:
|
||||
runtime_config = self._resolve_runtime_config()
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from src._version import HONCHO_VERSION
|
|||
from src.cache.client import close_cache, init_cache
|
||||
from src.config import settings
|
||||
from src.db import engine, register_db_query_instrumentation, request_context
|
||||
from src.embedding_client import embedding_client
|
||||
from src.exceptions import HonchoException
|
||||
from src.routers import (
|
||||
conclusions,
|
||||
|
|
@ -116,6 +117,10 @@ async def lifespan(_: FastAPI):
|
|||
# writing wrong-dim vectors.
|
||||
await validate_embedding_schema(engine)
|
||||
|
||||
# Eagerly build the embedding client so an hf: tokenizer download happens
|
||||
# here, not on the first request under the singleton lock.
|
||||
embedding_client.warmup()
|
||||
|
||||
try:
|
||||
await init_cache()
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Reference in New Issue