Terminal / api /conversations.py
Baida-A
Initial clean deploy (Reverse Proxy removed)
28a08e7
Raw
History Blame
4.57 kB
"""backend/api/conversations.py β€” Conversations + Messages CRUD (S354)."""
import json, logging
from .state import safe_json_dumps
from typing import Optional, Any
from fastapi import APIRouter, Depends, Body, HTTPException
from .auth_guard import require_role, AuthRole
from pydantic import BaseModel
from .state import sb
router = APIRouter( dependencies=[Depends(require_role(AuthRole.MACHINE))]) # GAP-1-fix: router-level auth
_logger = logging.getLogger("conversations")
class ConversationIn(BaseModel):
id: str
title: str = 'Nuova conversazione'
created_at: int
updated_at: int
class MessageIn(BaseModel):
id: str
conversation_id: str
role: str
content: str
created_at: int
error: Optional[bool] = False
steps: Optional[Any] = None
agent_status: Optional[str] = None
# ── Conversations ──────────────────────────────────────────────────────────────
@router.get('/api/conversations')
async def list_conversations():
try:
data = sb().table('conversations').select('*').order('updated_at', desc=True).limit(200).execute() # BUGFIX: LIMIT 200 β€” senza limit OOM garantito su account con molte conversazioni
return {'conversations': data.data}
except Exception as exc:
_logger.warning("list_conversations: %s", exc)
# S750-GAP-I: Supabase non configurato o irraggiungibile β†’ lista vuota invece di 500
return {'conversations': [], '_error': str(exc)[:120]}
@router.post('/api/conversations')
async def upsert_conversation(conv: ConversationIn):
try:
data = sb().table('conversations').upsert(conv.model_dump()).execute()
return {'conversation': data.data[0] if data.data else conv.model_dump()}
except Exception as exc:
_logger.warning("upsert_conversation %s: %s", conv.id, exc)
return {'conversation': conv.model_dump(), '_error': str(exc)[:120]}
@router.put('/api/conversations/{conv_id}')
async def update_conversation(conv_id: str, body: dict = Body(...)):
body['id'] = conv_id
try:
data = sb().table('conversations').upsert(body).execute()
return {'conversation': data.data[0] if data.data else body}
except Exception as exc:
_logger.warning("update_conversation %s: %s", conv_id, exc)
return {'conversation': body, '_error': str(exc)[:120]}
@router.delete('/api/conversations/{conv_id}')
async def delete_conversation(conv_id: str):
try:
sb().table('messages').delete().eq('conversation_id', conv_id).execute()
sb().table('conversations').delete().eq('id', conv_id).execute()
except Exception as exc:
_logger.warning("delete_conversation %s: %s", conv_id, exc)
return {'deleted': conv_id}
# ── Messages ───────────────────────────────────────────────────────────────────
@router.get('/api/conversations/{conv_id}/messages')
async def list_messages(conv_id: str):
try:
data = sb().table('messages').select('*').eq('conversation_id', conv_id).order('created_at').limit(500).execute() # BUGFIX: LIMIT 500 β€” senza limit OOM garantito su conversazioni lunghe
return {'messages': data.data}
except Exception as exc:
_logger.warning("list_messages %s: %s", conv_id, exc)
return {'messages': [], '_error': str(exc)[:120]}
@router.post('/api/conversations/{conv_id}/messages')
async def upsert_messages(conv_id: str, body: dict = Body(...)):
msgs = body.get('messages', [])
if not msgs:
return {'upserted': 0}
for m in msgs:
m['conversation_id'] = conv_id
if 'steps' in m and m['steps'] is not None:
m['steps'] = safe_json_dumps(m['steps']) if not isinstance(m['steps'], str) else m['steps']
try:
data = sb().table('messages').upsert(msgs).execute()
return {'upserted': len(data.data)}
except Exception as exc:
_logger.warning("upsert_messages %s: %s", conv_id, exc)
return {'upserted': 0, '_error': str(exc)[:120]}
@router.delete('/api/conversations/{conv_id}/messages')
async def clear_messages(conv_id: str):
try:
sb().table('messages').delete().eq('conversation_id', conv_id).execute()
except Exception as exc:
_logger.warning("clear_messages %s: %s", conv_id, exc)
return {'cleared': conv_id}