from typing import Any, Optional import httpx from app.config import get_settings from app.context import FlowContext from app.nodes.base import NodeExecutor settings = get_settings() class AIResponseExecutor(NodeExecutor): """Generate AI response using DeepSeek API""" async def execute( self, config: dict, context: FlowContext, session: Any ) -> Optional[str]: prompt = context.interpolate(config.get("prompt", "")) system_prompt = config.get("system_prompt", "Eres un asistente Ăștil.") output_variable = config.get("output_variable", "_ai_response") max_tokens = config.get("max_tokens", 500) temperature = config.get("temperature", 0.7) if not settings.DEEPSEEK_API_KEY: context.set("_ai_error", "DeepSeek API key not configured") return "error" if not prompt: return "error" messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ] if config.get("include_history", False): history = context.get("_conversation_history") or [] messages = ( [{"role": "system", "content": system_prompt}] + history + [{"role": "user", "content": prompt}] ) async with httpx.AsyncClient() as client: response = await client.post( f"{settings.DEEPSEEK_BASE_URL}/v1/chat/completions", headers={ "Authorization": f"Bearer {settings.DEEPSEEK_API_KEY}", "Content-Type": "application/json", }, json={ "model": settings.DEEPSEEK_MODEL, "messages": messages, "max_tokens": max_tokens, "temperature": temperature, }, timeout=30, ) if response.status_code != 200: context.set("_ai_error", response.text) return "error" data = response.json() ai_response = data["choices"][0]["message"]["content"] context.set(output_variable, ai_response) return "success" class AISentimentExecutor(NodeExecutor): """Analyze sentiment of user message using DeepSeek""" async def execute( self, config: dict, context: FlowContext, session: Any ) -> Optional[str]: text = context.get(config.get("variable", "")) or context.message.get( "content", "" ) output_variable = config.get("output_variable", "_sentiment") if not settings.DEEPSEEK_API_KEY or not text: return "neutral" async with httpx.AsyncClient() as client: response = await client.post( f"{settings.DEEPSEEK_BASE_URL}/v1/chat/completions", headers={ "Authorization": f"Bearer {settings.DEEPSEEK_API_KEY}", "Content-Type": "application/json", }, json={ "model": settings.DEEPSEEK_MODEL, "messages": [ { "role": "system", "content": "Analyze the sentiment. Reply with only one word: positive, negative, or neutral", }, {"role": "user", "content": text}, ], "max_tokens": 10, "temperature": 0, }, timeout=15, ) if response.status_code != 200: return "neutral" data = response.json() sentiment = data["choices"][0]["message"]["content"].lower().strip() context.set(output_variable, sentiment) if "positive" in sentiment: return "positive" if "negative" in sentiment: return "negative" return "neutral"