Files

124 lines
4.2 KiB
Python

"""LLM client abstraction for pluggable LLM providers."""
import logging
from typing import Optional
from app.config import settings
logger = logging.getLogger(__name__)
class LLMClient:
"""
Abstraction layer for LLM providers.
Supports: OpenAI, Ollama, LM Studio, or offline/mock mode.
Can be extended to support other providers.
"""
def __init__(
self,
provider: Optional[str] = None,
api_key: Optional[str] = None,
base_url: Optional[str] = None,
model: Optional[str] = None,
):
"""
Initialize LLM client.
Args:
provider: "openai", "ollama", "lm_studio", or None for mock
api_key: API key for provider
base_url: Base URL for API (for ollama/lm_studio)
model: Model identifier
"""
self.provider = provider or settings.LLM_PROVIDER
self.api_key = api_key or settings.LLM_API_KEY
self.base_url = base_url or settings.LLM_BASE_URL
self.model = model or settings.LLM_MODEL
logger.info(f"LLMClient initialized with provider: {self.provider}")
async def generate(self, prompt: str, max_tokens: int = 200) -> str:
"""
Generate text from a prompt.
Args:
prompt: Input prompt
max_tokens: Maximum tokens to generate
Returns:
Generated text
TODO: Implement OpenAI API integration
TODO: Implement Ollama API integration
TODO: Implement LM Studio API integration
"""
if self.provider == "openai":
return await self._generate_openai(prompt, max_tokens)
elif self.provider == "ollama":
return await self._generate_ollama(prompt, max_tokens)
elif self.provider == "lm_studio":
return await self._generate_lm_studio(prompt, max_tokens)
else:
return self._generate_mock(prompt)
async def _generate_openai(self, prompt: str, max_tokens: int) -> str:
"""
Generate using OpenAI API.
TODO: Implement using openai library
- Create client with api_key
- Call ChatCompletion
- Handle errors and retries
"""
logger.warning("OpenAI provider not yet implemented (stub)")
return self._generate_mock(prompt)
async def _generate_ollama(self, prompt: str, max_tokens: int) -> str:
"""
Generate using Ollama local API.
TODO: Implement using httpx or requests
- POST to base_url/api/generate
- Stream response and accumulate
- Handle model pulling if needed
"""
logger.warning("Ollama provider not yet implemented (stub)")
return self._generate_mock(prompt)
async def _generate_lm_studio(self, prompt: str, max_tokens: int) -> str:
"""
Generate using LM Studio local API.
TODO: Implement OpenAI-compatible API calls
- POST to base_url/v1/chat/completions
- Use same logic as OpenAI but with local endpoint
"""
logger.warning("LM Studio provider not yet implemented (stub)")
return self._generate_mock(prompt)
def _generate_mock(self, prompt: str) -> str:
"""
Generate deterministic mock response (no API needed).
Used when no provider is configured or for testing.
"""
logger.debug(f"Mock generation for prompt: {prompt[:50]}...")
if "content angle:" in prompt.lower():
for line in prompt.splitlines():
if line.lower().startswith("content angle:"):
angle = line.split(":", 1)[1].strip()
return f"The quiet here keeps circling back to {angle}."
# Simple deterministic responses for testing
if "hello" in prompt.lower():
return "Greetings, traveler! Welcome to The Sanctum."
elif "help" in prompt.lower():
return "I am here to guide your discourse through the streams."
elif "topic" in prompt.lower():
return "The archives speak of many topics worthy of discussion."
else:
return "An interesting observation. Tell me more."