| """ |
| Dependency injection module for FastAPI. |
| Instantiates all infrastructure components (LLMs, Databases, STT) as singletons |
| and wires them into the core domain services (Orchestrator, PatientService). |
| Provides getter functions to inject these into FastAPI route handlers. |
| """ |
| import os |
| from dotenv import load_dotenv |
|
|
| from providers.llms.groq_llm_provider import GroqLLMProvider |
| from providers.llms.ollama_llm_provider import OllamaLLMProvider |
| from providers.llms.huggingface_llm_provider import HuggingFaceLLMProvider |
| from providers.llms.fallback_llm_provider import FallbackLLMProvider |
| from providers.speech_to_text_provider import SenseVoiceSTTProvider |
| from providers.chroma_provider import ChromaVectorStore |
| from providers.pinecone_provider import PineconeVectorStore |
| from persistence.sqlite_memory_store import SQLiteProfileStore, SQLiteSessionStore |
| from persistence.user_store import SQLiteUserStore |
|
|
| |
| from providers.safety.llm import LLMRiskSignal |
| from providers.safety.clinical import ClinicalRiskSignal |
|
|
| |
| |
| from providers.safety.bert import BertRiskSignal |
| from providers.safety.keyword import KeywordRiskSignal |
| from providers.safety.cascade import CascadeRiskSignal |
| from fastapi.security import OAuth2PasswordBearer |
| from fastapi import Depends, HTTPException, status |
| from services.auth_service import decode_access_token |
| from services.risk_assessment_service import RiskAssessmentService |
| from services.conversation_orchestrator import ConversationOrchestrator |
| from services.patient_service import PatientService |
| from core.ports import ProfileStore, SessionStore, STTProvider, LLMProvider, VectorStore |
|
|
| load_dotenv() |
|
|
| OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434") |
| OLLAMA_LLM1_MODEL = os.getenv("OLLAMA_LLM1_MODEL", "phi4-mini") |
| OLLAMA_LLM2_MODEL = os.getenv("OLLAMA_LLM2_MODEL", "qwen2.5:7b-instruct") |
| GROQ_LLM1_MODEL = os.getenv("GROQ_LLM1_MODEL", "llama-3.1-8b-instant") |
| GROQ_LLM2_MODEL = os.getenv("GROQ_LLM2_MODEL", "llama-3.3-70b-versatile") |
| EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "nomic-embed-text") |
| CHROMA_PERSIST_DIR = os.getenv("CHROMA_PERSIST_DIR", "./chroma_db") |
| CHROMA_COLLECTION = os.getenv("CHROMA_COLLECTION_NAME", "mhcva-knowledge") |
| VECTOR_STORE_TYPE = os.getenv("VECTOR_STORE_TYPE", "chroma").lower() |
| PINECONE_API_KEY = os.getenv("PINECONE_API_KEY", "") |
| PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME", "serinity-knowledge") |
| MEMORY_DB_PATH = os.getenv("MEMORY_DB_PATH", "./data/serinity.db") |
| WORKING_MEMORY_TURNS = int(os.getenv("WORKING_MEMORY_TURNS", "20")) |
| CLOUD_MODE = os.getenv("CLOUD_MODE", "false").lower() == "true" |
| |
| |
| LLM_PROVIDER = os.getenv("LLM_PROVIDER", "groq").lower() |
| HF_LLM1_MODEL = os.getenv("HF_LLM1_MODEL", "meta-llama/Llama-3.1-8B-Instruct") |
| HF_LLM2_MODEL = os.getenv("HF_LLM2_MODEL", "meta-llama/Llama-3.3-70B-Instruct") |
|
|
| _hf_home_raw = os.getenv("HF_HOME", "./models") |
| if not os.path.isabs(_hf_home_raw): |
| _hf_home_raw = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), _hf_home_raw) |
| os.environ["HF_HOME"] = _hf_home_raw |
| os.makedirs(_hf_home_raw, exist_ok=True) |
|
|
| |
| ollama_provider = OllamaLLMProvider(host=OLLAMA_HOST, model1=OLLAMA_LLM1_MODEL, model2=OLLAMA_LLM2_MODEL) |
|
|
| if CLOUD_MODE: |
| if LLM_PROVIDER == "hf": |
| _cloud_provider = HuggingFaceLLMProvider(model1=HF_LLM1_MODEL, model2=HF_LLM2_MODEL) |
| print(f"[STARTUP] LLM backend: HuggingFace ({HF_LLM1_MODEL} / {HF_LLM2_MODEL})") |
| else: |
| _cloud_provider = GroqLLMProvider(model1=GROQ_LLM1_MODEL, model2=GROQ_LLM2_MODEL) |
| print(f"[STARTUP] LLM backend: Groq ({GROQ_LLM1_MODEL} / {GROQ_LLM2_MODEL})") |
| llm_provider = FallbackLLMProvider(providers=[_cloud_provider]) |
| vector_store = PineconeVectorStore( |
| host=OLLAMA_HOST, |
| embedding_model=EMBEDDING_MODEL, |
| index_name=PINECONE_INDEX_NAME, |
| api_key=PINECONE_API_KEY |
| ) |
| else: |
| llm_provider = FallbackLLMProvider(providers=[ollama_provider]) |
| vector_store = ChromaVectorStore( |
| host=OLLAMA_HOST, |
| embedding_model=EMBEDDING_MODEL, |
| persist_dir=CHROMA_PERSIST_DIR, |
| collection_name=CHROMA_COLLECTION |
| ) |
| profile_store = SQLiteProfileStore(db_path=MEMORY_DB_PATH) |
| session_store = SQLiteSessionStore(db_path=MEMORY_DB_PATH, working_memory_turns=WORKING_MEMORY_TURNS) |
| user_store = SQLiteUserStore(db_path=MEMORY_DB_PATH) |
|
|
| stt_provider = SenseVoiceSTTProvider() |
|
|
| |
| def get_llm_provider() -> FallbackLLMProvider: |
| return llm_provider |
|
|
| def get_vector_store(): |
| return vector_store |
|
|
| def get_profile_store() -> SQLiteProfileStore: |
| return profile_store |
|
|
| def get_session_store() -> SQLiteSessionStore: |
| return session_store |
| |
| def get_user_store() -> SQLiteUserStore: |
| return user_store |
|
|
| oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login") |
|
|
| def get_current_user(token: str = Depends(oauth2_scheme), store: SQLiteUserStore = Depends(get_user_store)): |
| credentials_exception = HTTPException( |
| status_code=status.HTTP_401_UNAUTHORIZED, |
| detail="Could not validate credentials", |
| headers={"WWW-Authenticate": "Bearer"}, |
| ) |
| payload = decode_access_token(token) |
| if payload is None: |
| raise credentials_exception |
| |
| user_id: str = payload.get("sub") |
| if user_id is None: |
| raise credentials_exception |
| |
| user = store.get_user_by_id(user_id) |
| if user is None: |
| raise credentials_exception |
| return user |
|
|
| |
| risk_service = RiskAssessmentService([ |
| CascadeRiskSignal( |
| filter_signal=KeywordRiskSignal(), |
| verify_signal=BertRiskSignal() |
| ), |
| LLMRiskSignal(), |
| ClinicalRiskSignal() |
| ]) |
|
|
| |
| conversation_orchestrator = ConversationOrchestrator( |
| llm_provider=llm_provider, |
| vector_store=vector_store, |
| session_store=session_store, |
| profile_store=profile_store, |
| risk_service=risk_service |
| ) |
|
|
| patient_service = PatientService( |
| profile_store=profile_store, |
| session_store=session_store, |
| llm_provider=llm_provider |
| ) |
|
|
| |
| def get_orchestrator( |
| llm_provider: LLMProvider = Depends(get_llm_provider), |
| vector_store: VectorStore = Depends(get_vector_store), |
| session_store: SessionStore = Depends(get_session_store), |
| profile_store: ProfileStore = Depends(get_profile_store) |
| ) -> ConversationOrchestrator: |
| return ConversationOrchestrator( |
| llm_provider=llm_provider, |
| vector_store=vector_store, |
| session_store=session_store, |
| profile_store=profile_store, |
| risk_service=risk_service |
| ) |
|
|
| def get_patient_service( |
| profile_store: ProfileStore = Depends(get_profile_store), |
| session_store: SessionStore = Depends(get_session_store), |
| llm_provider: LLMProvider = Depends(get_llm_provider) |
| ) -> PatientService: |
| return PatientService( |
| profile_store=profile_store, |
| session_store=session_store, |
| llm_provider=llm_provider |
| ) |
|
|
| def get_stt_provider() -> STTProvider: |
| return stt_provider |
|
|
| def get_profile_store() -> ProfileStore: |
| return profile_store |
|
|
| def get_session_store() -> SessionStore: |
| return session_store |
|
|