feat(bootstrap): refactor runtime dependency management and add lazy loading for binary store and vector index

feat(agent): update import for agent session service
feat(openai): add context truncation check in OpenAI answer generator
docs(README): update frontend environment file conventions
fix(vite): default local frontend development to local backend
This commit is contained in:
ash66
2026-05-25 13:58:48 +08:00
parent 091a02c522
commit 10a034e294
11 changed files with 162 additions and 15 deletions

View File

@@ -3,31 +3,134 @@
from __future__ import annotations
from functools import lru_cache
from typing import Callable
from app.application.agent import AgentConversationService, AgentSessionService
from app.application.documents import DocumentCommandService, DocumentQueryService
from app.application.knowledge import KnowledgeRetrievalService
from app.application.perception.services import PerceptionService
from app.config.settings import settings
from app.domain.documents import DocumentBinaryStore
from app.domain.retrieval import VectorIndex
from app.infrastructure.embedding.openai_compatible_embedding_provider import OpenAICompatibleEmbeddingProvider
from app.infrastructure.llm.openai_compatible_answer_generator import OpenAICompatibleAnswerGenerator
from app.infrastructure.parser.aliyun_document_parser import AliyunDocumentParser
from app.infrastructure.parser.local_chunk_builder import LocalRegulationChunkBuilder
from app.infrastructure.parser.local_document_parser import LocalDocumentParser
from app.infrastructure.parser.vector_chunk_builder import AliyunVectorChunkBuilder
from app.infrastructure.perception.mock_event_store import MockEventStore
from app.infrastructure.session.in_memory_conversation_store import InMemoryConversationStore
from app.infrastructure.storage.json_document_repository import JsonDocumentRepository
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.cross_encoder_reranker import OpenAICompatibleReranker
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
from app.infrastructure.perception.mock_event_store import MockEventStore
from app.application.perception.services import PerceptionService
from app.services.llm.llm_factory import LLMFactory
# Keep shared wiring centralized so dependency construction remains consistent.
class LazyBinaryStore(DocumentBinaryStore):
"""Delay MinIO connection work until binary storage is actually needed."""
def __init__(self, factory: Callable[[], DocumentBinaryStore]) -> None:
"""Initialize the lazy binary store wrapper."""
self._factory = factory
self._store: DocumentBinaryStore | None = None
def _get_store(self) -> DocumentBinaryStore:
"""Create the underlying store on first use and reuse it afterwards."""
if self._store is None:
self._store = self._factory()
return self._store
@property
def client(self):
"""Expose the underlying client for compatibility with health endpoints."""
return self._get_store().client
def save(
self,
*,
object_name: str,
data: bytes,
content_type: str,
metadata: dict[str, str] | None = None,
) -> None:
"""Save data through the underlying binary store implementation."""
self._get_store().save(
object_name=object_name,
data=data,
content_type=content_type,
metadata=metadata,
)
def read(self, object_name: str) -> bytes:
"""Read data through the underlying binary store implementation."""
return self._get_store().read(object_name)
def delete(self, object_name: str) -> None:
"""Delete data through the underlying binary store implementation."""
self._get_store().delete(object_name)
class LazyVectorIndex(VectorIndex):
"""Delay Milvus connection work until vector operations are actually needed."""
def __init__(self, factory: Callable[[], VectorIndex]) -> None:
"""Initialize the lazy vector index wrapper."""
self._factory = factory
self._index: VectorIndex | None = None
def _get_index(self) -> VectorIndex:
"""Create the underlying index on first use and reuse it afterwards."""
if self._index is None:
self._index = self._factory()
return self._index
@property
def collection(self):
"""Expose the underlying Milvus collection for compatibility adapters."""
return self._get_index().collection
def upsert(self, chunks, vectors) -> int:
"""Insert or update vectors through the underlying vector index implementation."""
return self._get_index().upsert(chunks, vectors)
def delete_by_document(self, doc_id: str) -> int:
"""Delete vectors through the underlying vector index implementation."""
return self._get_index().delete_by_document(doc_id)
def search(self, query_vector: list[float], top_k: int, filters: str | None = None):
"""Search vectors through the underlying vector index implementation."""
return self._get_index().search(query_vector, top_k, filters)
def count_by_document(self) -> dict[str, int]:
"""Count document vectors through the underlying vector index implementation."""
return self._get_index().count_by_document()
def list_document_metadata(self) -> list[dict]:
"""List document metadata through the underlying vector index implementation."""
return self._get_index().list_document_metadata()
def health(self) -> dict:
"""Return vector index health through the underlying vector index implementation."""
return self._get_index().health()
@lru_cache
def _build_binary_store() -> MinioDocumentBinaryStore:
"""Return the concrete binary store implementation."""
return MinioDocumentBinaryStore()
@lru_cache
def _build_vector_index() -> MilvusVectorIndex:
"""Return the concrete vector index implementation."""
return MilvusVectorIndex()
@lru_cache
def get_document_repository():
@@ -46,9 +149,9 @@ def get_parse_artifact_store():
@lru_cache
def get_binary_store() -> MinioDocumentBinaryStore:
def get_binary_store() -> DocumentBinaryStore:
"""Return binary store."""
return MinioDocumentBinaryStore()
return LazyBinaryStore(_build_binary_store)
@lru_cache
@@ -77,9 +180,9 @@ def get_embedding_provider() -> OpenAICompatibleEmbeddingProvider:
@lru_cache
def get_vector_index() -> MilvusVectorIndex:
def get_vector_index() -> VectorIndex:
"""Return vector index."""
return MilvusVectorIndex()
return LazyVectorIndex(_build_vector_index)
@lru_cache
@@ -162,6 +265,19 @@ def get_perception_service() -> PerceptionService:
event_store=MockEventStore(),
retrieval_service=get_retrieval_service(),
)
@lru_cache
def get_agent_session_service() -> AgentSessionService:
"""Return agent session service."""
return AgentSessionService(conversation_store=get_conversation_store())
def preload_runtime_dependencies() -> None:
"""Warm dependencies that are safe and useful to preload during startup."""
LLMFactory.preload_clients(["qwen", "deepseek"])
def cleanup_runtime_dependencies() -> None:
"""Release runtime dependencies that expose explicit cleanup hooks."""
LLMFactory.cleanup()