AI generated first iteration

This commit is contained in:
2026-05-11 15:01:55 -05:00
parent af3e282fda
commit 412d7caec3
28 changed files with 2094 additions and 157 deletions

1
app/llm/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""LLM module exports."""

117
app/llm/client.py Normal file
View File

@@ -0,0 +1,117 @@
"""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]}...")
# 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."

60
app/llm/prompts.py Normal file
View File

@@ -0,0 +1,60 @@
"""LLM prompt templates and generation utilities."""
from typing import Optional
class PromptTemplates:
"""Collection of prompt templates for different modes."""
@staticmethod
def gentle_prompt(current_theme: Optional[str] = None) -> str:
"""Generate a gentle prompt when chat has been inactive."""
if current_theme:
return f"Gently prompt the chat about: {current_theme}"
return "Generate a gentle, inviting prompt to encourage discussion in the stream."
@staticmethod
def steward_response(message: str, context: Optional[str] = None) -> str:
"""Generate a response as the Steward mode."""
prompt = f"As a thoughtful steward of this stream, respond briefly and helpfully to: {message}"
if context:
prompt += f"\nContext: {context}"
return prompt
@staticmethod
def warden_analysis(message: str) -> str:
"""Generate analysis for suspicious content detection."""
return f"Analyze this message for suspicious patterns (spam, scams, manipulation): {message}"
@staticmethod
def librarian_summary(messages: list[str]) -> str:
"""Generate a summary of important discussion points."""
messages_text = "\n".join(messages)
return f"Summarize the key discussion points from this chat log:\n{messages_text}"
@staticmethod
def scribe_ledger(
theme: str,
discussion: list[str],
actions: list[str],
clips: list[str],
seeds: list[str],
) -> str:
"""Generate markdown ledger summary."""
return f"""Generate a professional markdown ledger with these sections:
- Theme: {theme}
- Notable Discussion: {len(discussion)} key points
- Agent Actions: {len(actions)} recorded
- Clip Candidates: {len(clips)} identified
- Blog Seeds: {len(seeds)} proposed"""
@staticmethod
def clip_candidate_reason(message: str) -> str:
"""Generate reasoning for marking a message as a clip candidate."""
return f"Explain why this is a good clip candidate: {message}"
@staticmethod
def blog_seed_topic(context: list[str]) -> str:
"""Generate a blog post topic from discussion context."""
context_text = "\n".join(context[:5]) # First 5 messages
return f"Based on this discussion, suggest a blog post topic:\n{context_text}"