Maia/backend/app/services/ollama_service.py

106 lines
4.5 KiB
Python

import httpx
import json
import logging
import re
from app.config import settings
logger = logging.getLogger(__name__)
PROMPTS = {
"minutes": (
"Voce e um assistente especializado em reunioes corporativas. "
"Com base na transcricao a seguir, gere uma ata estruturada em JSON contendo: "
"title (string), participants (array de objetos com name e role), "
"agenda (array de strings), decisions (array de strings), "
"action_items (array de objetos com description, responsible e deadline), "
"next_steps (array de strings), open_points (array de strings). "
"Retorne APENAS o JSON valido, sem texto adicional. "
"Transcricao: {transcription}"
),
"sentiment": (
"Analise o tom emocional desta reuniao e retorne um JSON com: "
"overall_sentiment (positive/neutral/negative), "
"timeline (array de objetos com time_range, sentiment e notes), "
"participants_sentiment (array de objetos com speaker, sentiment e details), "
"tension_moments (array de strings), "
"consensus_moments (array de strings), "
"participation_metrics (array de objetos com speaker, talk_percentage e interventions_count). "
"Retorne APENAS o JSON valido. "
"Transcricao: {transcription}"
),
"psychological": (
"Analise os padroes comportamentais dos participantes e retorne um JSON com: "
"participants_profiles (array de objetos com speaker, name, communication_style, "
"leadership_tendencies, disengagement_signs, power_dynamics_role), "
"group_dynamics (string), "
"key_observations (array de strings). "
"Retorne APENAS o JSON valido. "
"Transcricao: {transcription}"
),
"communication": (
"Avalie a comunicacao dos participantes nesta reuniao e retorne um JSON com: "
"overall_score (numero de 1 a 10), "
"dimensions (objeto com clarity, active_listening, assertiveness, meeting_facilitation, "
"cada um com score e notes), "
"strengths (array de objetos com description e example), "
"improvement_areas (array de objetos com description e suggestion), "
"general_feedback (string). "
"Retorne APENAS o JSON valido. "
"Transcricao: {transcription}"
),
"summary": (
"Gere um resumo executivo desta reuniao em JSON com: "
"summary (string), "
"key_points (array de strings), "
"duration_minutes (number), "
"total_participants (number). "
"Retorne APENAS o JSON valido. "
"Transcricao: {transcription}"
),
}
async def run_analysis(analysis_type: str, transcription: dict) -> dict:
"""Run AI analysis on transcription using Ollama"""
transcription_text = json.dumps(transcription, ensure_ascii=False)
prompt_template = PROMPTS.get(analysis_type, PROMPTS["summary"])
prompt = prompt_template.replace("{transcription}", transcription_text[:8000])
logger.info(f"Running {analysis_type} analysis with Ollama model {settings.OLLAMA_MODEL}")
try:
headers = {}
if settings.OLLAMA_API_KEY:
headers["X-API-Key"] = settings.OLLAMA_API_KEY
async with httpx.AsyncClient(timeout=180.0) as client:
response = await client.post(
f"{settings.OLLAMA_URL}/api/generate",
headers=headers,
json={
"model": settings.OLLAMA_MODEL,
"prompt": prompt,
"stream": False,
"format": "json",
"options": {"temperature": 0.3},
},
)
response.raise_for_status()
result = response.json()
response_text = result.get("response", "{}")
try:
return json.loads(response_text)
except json.JSONDecodeError:
json_match = re.search(r"\{.*\}", response_text, re.DOTALL)
if json_match:
try:
return json.loads(json_match.group())
except json.JSONDecodeError:
pass
return {"raw_response": response_text, "parse_error": "Could not parse JSON"}
except httpx.ConnectError as e:
logger.error(f"Cannot connect to Ollama at {settings.OLLAMA_URL}: {e}")
return {"error": "Ollama unavailable", "analysis_type": analysis_type}
except Exception as e:
logger.error(f"Ollama analysis error for {analysis_type}: {e}")
return {"error": str(e), "analysis_type": analysis_type}