mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
fix(embedding): exclude provider init from health timeout
This commit is contained in:
parent
ebcb154e37
commit
ed67ea80bf
3 changed files with 53 additions and 0 deletions
|
|
@ -121,6 +121,15 @@ class BaseAsEmbedding(BaseComponent):
|
|||
response = await self.model(inputs, **kwargs) # pylint: disable=not-callable
|
||||
return response.embeddings
|
||||
|
||||
def initialize_model(self) -> None:
|
||||
"""Construct the provider without making a remote request.
|
||||
|
||||
Callers that apply their own request timeout can initialize first so
|
||||
one-time SDK imports and client construction do not consume that
|
||||
timeout budget. Normal embedding calls remain lazily initialized.
|
||||
"""
|
||||
self._ensure_model()
|
||||
|
||||
async def _start(self) -> None:
|
||||
"""Defer provider construction until the first remote embedding call."""
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ class LocalEmbeddingStore(BaseEmbeddingStore):
|
|||
async def health_check(self, timeout: float = 5.0) -> bool:
|
||||
tag = f"[EMBEDDING HEALTH CHECK] name={self.name} workspace_dir={self.workspace_path}"
|
||||
try:
|
||||
# Provider construction may synchronously import an SDK and build
|
||||
# its HTTP client. Keep that one-time work outside the request
|
||||
# timeout so the full budget applies to the initialized provider
|
||||
# call instead of being consumed before a request can be sent.
|
||||
self.as_embedding.initialize_model()
|
||||
result = await asyncio.wait_for(self.as_embedding(["ping"]), timeout=timeout)
|
||||
if not result or result[0] is None:
|
||||
raise RuntimeError("empty embedding")
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ class FakeAsEmbedding:
|
|||
dimensions = 2
|
||||
vector_space_id = "fakespace000"
|
||||
|
||||
def initialize_model(self):
|
||||
"""Mirror the real component's idempotent initialization hook."""
|
||||
|
||||
async def __call__(self, texts: list[str], **_kwargs):
|
||||
return [[1.0] if text == "bad" else [1.0, 0.0] for text in texts]
|
||||
|
||||
|
|
@ -29,6 +32,9 @@ class BadHealthAsEmbedding:
|
|||
dimensions = 2
|
||||
vector_space_id = "fakespace000"
|
||||
|
||||
def initialize_model(self):
|
||||
"""Mirror the real component's idempotent initialization hook."""
|
||||
|
||||
async def __call__(self, _texts: list[str], **_kwargs):
|
||||
return [[1.0]]
|
||||
|
||||
|
|
@ -147,6 +153,39 @@ def test_health_check_rejects_wrong_dimension():
|
|||
run(go())
|
||||
|
||||
|
||||
def test_health_check_starts_timeout_after_provider_initialization(monkeypatch):
|
||||
"""One-time client construction must not consume the request timeout."""
|
||||
|
||||
async def go():
|
||||
events = []
|
||||
|
||||
class InitializingAsEmbedding(FakeAsEmbedding):
|
||||
"""Record initialization and provider-call ordering."""
|
||||
|
||||
def initialize_model(self):
|
||||
events.append("initialized")
|
||||
|
||||
async def __call__(self, texts: list[str], **_kwargs):
|
||||
events.append("remote request")
|
||||
return [[1.0, 0.0] for _ in texts]
|
||||
|
||||
original_wait_for = asyncio.wait_for
|
||||
|
||||
async def checked_wait_for(awaitable, timeout):
|
||||
assert events == ["initialized"]
|
||||
assert timeout == 5.0
|
||||
return await original_wait_for(awaitable, timeout)
|
||||
|
||||
store = LocalEmbeddingStore(name="t_local_embedding_health_timeout_scope")
|
||||
store.as_embedding = InitializingAsEmbedding()
|
||||
monkeypatch.setattr(asyncio, "wait_for", checked_wait_for)
|
||||
|
||||
assert await store.health_check(timeout=5.0) is True
|
||||
assert events == ["initialized", "remote request"]
|
||||
|
||||
run(go())
|
||||
|
||||
|
||||
def test_insufficient_quota_waits_sixty_seconds_before_retry(monkeypatch):
|
||||
"""Quota exhaustion uses the dedicated delay before ReMe retries."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue