feat: replace Redis with in-memory conversation storage
- Remove Redis dependency and redis_client.py - Implement ConversationMemory with module-level dictionary - Add TTL support via timestamp checking - Remove redis_connected from health endpoint - Add embeddings, intent classification, and RAG prompt modules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
"""Intent classification module."""
|
||||
|
||||
from app.intent.classifier import IntentClassifier, get_intent_classifier, IntentClassifierDependency
|
||||
|
||||
__all__ = [
|
||||
"IntentClassifier",
|
||||
"get_intent_classifier",
|
||||
"IntentClassifierDependency",
|
||||
]
|
||||
@@ -0,0 +1,96 @@
|
||||
"""Lightweight intent classification using gpt-4o-mini."""
|
||||
|
||||
import logging
|
||||
from functools import lru_cache
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from fastapi import Depends
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from app.config import settings
|
||||
from app.memory.conversation import Message
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
Intent = Literal["codebase", "general", "clarification"]
|
||||
|
||||
INTENT_PROMPT = """Classify this user message into one category:
|
||||
- "codebase": Questions about trading system code, architecture, files, methods, execution, strategies, exchanges, risk management, order handling, or technical implementation
|
||||
- "general": Greetings, meta-questions, off-topic ("How are you?", "What can you do?", "Hello")
|
||||
- "clarification": Follow-ups that rely on conversation context, not new retrieval ("Tell me more", "What did you mean?", "Can you explain that?")
|
||||
|
||||
IMPORTANT: If the user is asking about specific code, files, classes, methods, or system behavior, classify as "codebase".
|
||||
|
||||
Respond with ONLY the category name, nothing else."""
|
||||
|
||||
|
||||
class IntentClassifier:
|
||||
"""Lightweight intent classifier using gpt-4o-mini."""
|
||||
|
||||
def __init__(self, api_key: str):
|
||||
"""Initialize the classifier.
|
||||
|
||||
Args:
|
||||
api_key: OpenAI API key
|
||||
"""
|
||||
self.client = AsyncOpenAI(api_key=api_key)
|
||||
self.model = "gpt-4o-mini"
|
||||
|
||||
async def classify(
|
||||
self,
|
||||
message: str,
|
||||
history: list[Message] | None = None,
|
||||
) -> Intent:
|
||||
"""Classify user message intent.
|
||||
|
||||
Args:
|
||||
message: User's message
|
||||
history: Optional conversation history for context
|
||||
|
||||
Returns:
|
||||
Classified intent: "codebase", "general", or "clarification"
|
||||
"""
|
||||
# Build context from history (last 2 turns)
|
||||
context = ""
|
||||
if history and len(history) >= 2:
|
||||
recent = history[-4:] # Last 2 exchanges
|
||||
context = "Recent conversation:\n"
|
||||
for msg in recent:
|
||||
role = "User" if msg.role == "user" else "Assistant"
|
||||
context += f"{role}: {msg.content[:100]}...\n" if len(msg.content) > 100 else f"{role}: {msg.content}\n"
|
||||
context += "\n"
|
||||
|
||||
try:
|
||||
response = await self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system", "content": INTENT_PROMPT},
|
||||
{"role": "user", "content": f"{context}Current message: {message}"},
|
||||
],
|
||||
max_tokens=10,
|
||||
temperature=0,
|
||||
)
|
||||
|
||||
raw_intent = response.choices[0].message.content.strip().lower()
|
||||
|
||||
# Validate intent
|
||||
if raw_intent in ("codebase", "general", "clarification"):
|
||||
logger.debug(f"Classified intent: {raw_intent}")
|
||||
return raw_intent
|
||||
|
||||
# Default to codebase for ambiguous cases (safer for RAG)
|
||||
logger.warning(f"Unexpected intent response: {raw_intent}, defaulting to codebase")
|
||||
return "codebase"
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Intent classification failed: {e}, defaulting to codebase")
|
||||
return "codebase"
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_intent_classifier() -> IntentClassifier:
|
||||
"""Get cached intent classifier instance."""
|
||||
return IntentClassifier(api_key=settings.openai_api_key)
|
||||
|
||||
|
||||
IntentClassifierDependency = Annotated[IntentClassifier, Depends(get_intent_classifier)]
|
||||
Reference in New Issue
Block a user