Fix 法规对话
This commit is contained in:
38
backend/app/shared/async_utils.py
Normal file
38
backend/app/shared/async_utils.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""Async utility helpers for bridging sync generators to async FastAPI routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
from typing import AsyncGenerator, Generator, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
_SENTINEL = object()
|
||||
|
||||
|
||||
async def iter_in_thread(sync_gen: Generator[T, None, None]) -> AsyncGenerator[T, None]:
|
||||
"""Yield items from a synchronous generator without blocking the event loop.
|
||||
|
||||
Runs the generator in a daemon thread, forwarding items via an asyncio.Queue.
|
||||
Use this to wrap blocking LLM streaming generators inside async FastAPI routes.
|
||||
"""
|
||||
loop = asyncio.get_running_loop()
|
||||
queue: asyncio.Queue = asyncio.Queue(maxsize=32)
|
||||
|
||||
def _drain() -> None:
|
||||
try:
|
||||
for item in sync_gen:
|
||||
future = asyncio.run_coroutine_threadsafe(queue.put(item), loop)
|
||||
future.result()
|
||||
finally:
|
||||
asyncio.run_coroutine_threadsafe(queue.put(_SENTINEL), loop).result()
|
||||
|
||||
thread = threading.Thread(target=_drain, daemon=True)
|
||||
thread.start()
|
||||
|
||||
while True:
|
||||
item = await queue.get()
|
||||
if item is _SENTINEL:
|
||||
break
|
||||
yield item # type: ignore[misc]
|
||||
@@ -19,6 +19,7 @@ from app.infrastructure.storage.json_document_repository import JsonDocumentRepo
|
||||
from app.infrastructure.storage.minio_binary_store import MinioDocumentBinaryStore
|
||||
from app.infrastructure.storage.postgres_document_repository import PostgresDocumentRepository
|
||||
from app.infrastructure.storage.postgres_parse_artifact_store import PostgresParseArtifactStore
|
||||
from app.infrastructure.vectorstore.bm25_retriever import BM25Retriever
|
||||
from app.infrastructure.vectorstore.dense_retriever import DenseRetriever
|
||||
from app.infrastructure.vectorstore.milvus_vector_index import MilvusVectorIndex
|
||||
from app.infrastructure.vectorstore.cross_encoder_reranker import OpenAICompatibleReranker
|
||||
@@ -87,6 +88,13 @@ def get_reranker():
|
||||
return None
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_bm25_retriever() -> BM25Retriever | None:
|
||||
"""Return BM25 retriever if rank_bm25 + jieba are installed, else None."""
|
||||
retriever = BM25Retriever(vector_index=get_vector_index())
|
||||
return retriever if retriever.available else None
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_retrieval_service() -> KnowledgeRetrievalService:
|
||||
"""Return retrieval service."""
|
||||
@@ -96,6 +104,7 @@ def get_retrieval_service() -> KnowledgeRetrievalService:
|
||||
)
|
||||
return KnowledgeRetrievalService(
|
||||
retriever=retriever,
|
||||
bm25_retriever=get_bm25_retriever(),
|
||||
reranker=get_reranker(),
|
||||
reranker_top_k=settings.reranker_top_k,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user