# backend/app/llm/provider.py from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass @dataclass class Classification: type: str # task | event | idea sphere: str # work | life project_id: str | None = None proj_label: str = "" tone: str = "ink" due_text: str = "" when_text: str = "" extra: str = "" reason: str = "" # 한국어 confidence: float = 0.0 model: str = "" class LLMProvider(ABC): name: str = "base" def tool_capable(self) -> bool: """tool-call(함수 호출) 가능한 모델인가. 기본 False → 에이전트는 scripted 폴백.""" return False @abstractmethod def health(self) -> dict: ... @abstractmethod def generate_json(self, prompt: str, schema: dict | None = None) -> dict: ... @abstractmethod def classify_capture(self, raw: str, context: dict) -> Classification: ... def list_models(self) -> list[str]: """사용 가능한 모델 목록(설정 페이지 드롭다운용). 기본 빈 목록.""" return [] _cached: LLMProvider | None = None def build_provider( provider: str, *, host: str | None = None, model: str | None = None, api_key: str | None = None, timeout: float | None = None, ) -> LLMProvider: """provider 종류로 구현 인스턴스를 만든다. 인자 None 이면 각 구현이 effective_llm 기본값 사용. 연결 테스트(저장 전)에서는 명시 인자(host/model/api_key)로 임시 구성한다.""" if provider == "openai": from .openai_compat import OpenAICompatProvider return OpenAICompatProvider(host=host, model=model, api_key=api_key, timeout=timeout) if provider == "gemini": from .gemini import GeminiProvider return GeminiProvider(host=host, model=model, api_key=api_key, timeout=timeout) from .ollama import OllamaProvider return OllamaProvider(host=host, model=model, timeout=timeout) def get_provider(force: str | None = None) -> LLMProvider: """유효 설정(오버레이>env)의 provider 로 실 LLM 구현을 만든다(ollama|openai|gemini). 미가동/미설정 시 호출부에서 예외(휴리스틱 폴백 없음, phase-16+). 테스트는 conftest 가 get_provider 의존성을 결정적 FakeLLM 으로 오버라이드한다. force 로 provider 를 직접 지정할 수 있다(미지정 시 effective_llm().provider). """ from ..runtime_config import effective_llm return build_provider(force or effective_llm().provider) def reset_provider_cache(): global _cached _cached = None