nanoClaw/backend/routes/conversations.py

108 lines
3.6 KiB
Python

"""Conversation API routes"""
import uuid
from datetime import datetime, timezone
from flask import Blueprint, request
from backend import db
from backend.models import Conversation, Project
from backend.utils.helpers import ok, err, to_dict, get_or_create_default_user
from backend.config import DEFAULT_MODEL
bp = Blueprint("conversations", __name__)
def _conv_to_dict(conv, **extra):
"""Convert conversation to dict with project info"""
d = to_dict(conv, **extra)
if conv.project_id:
project = db.session.get(Project, conv.project_id)
if project:
d["project_name"] = project.name
return d
@bp.route("/api/conversations", methods=["GET", "POST"])
def conversation_list():
"""List or create conversations"""
if request.method == "POST":
d = request.json or {}
user = get_or_create_default_user()
# Validate project_id if provided
project_id = d.get("project_id")
if project_id:
project = db.session.get(Project, project_id)
if not project:
return err(404, "Project not found")
conv = Conversation(
id=str(uuid.uuid4()),
user_id=user.id,
project_id=project_id or None,
title=d.get("title", ""),
model=d.get("model", DEFAULT_MODEL),
system_prompt=d.get("system_prompt", ""),
temperature=d.get("temperature", 1.0),
max_tokens=d.get("max_tokens", 65536),
thinking_enabled=d.get("thinking_enabled", False),
)
db.session.add(conv)
db.session.commit()
return ok(_conv_to_dict(conv))
# GET - list conversations
cursor = request.args.get("cursor")
limit = min(int(request.args.get("limit", 20)), 100)
project_id = request.args.get("project_id")
user = get_or_create_default_user()
q = Conversation.query.filter_by(user_id=user.id)
# Filter by project if specified
if project_id:
q = q.filter_by(project_id=project_id)
if cursor:
q = q.filter(Conversation.updated_at < (
db.session.query(Conversation.updated_at).filter_by(id=cursor).scalar() or datetime.now(timezone.utc)))
rows = q.order_by(Conversation.updated_at.desc()).limit(limit + 1).all()
items = [_conv_to_dict(r, message_count=r.messages.count()) for r in rows[:limit]]
return ok({
"items": items,
"next_cursor": items[-1]["id"] if len(rows) > limit else None,
"has_more": len(rows) > limit,
})
@bp.route("/api/conversations/<conv_id>", methods=["GET", "PATCH", "DELETE"])
def conversation_detail(conv_id):
"""Get, update or delete a conversation"""
conv = db.session.get(Conversation, conv_id)
if not conv:
return err(404, "conversation not found")
if request.method == "GET":
return ok(_conv_to_dict(conv))
if request.method == "DELETE":
db.session.delete(conv)
db.session.commit()
return ok(message="deleted")
# PATCH - update conversation
d = request.json or {}
for k in ("title", "model", "system_prompt", "temperature", "max_tokens", "thinking_enabled"):
if k in d:
setattr(conv, k, d[k])
# Support updating project_id
if "project_id" in d:
project_id = d["project_id"]
if project_id:
project = db.session.get(Project, project_id)
if not project:
return err(404, "Project not found")
conv.project_id = project_id or None
db.session.commit()
return ok(_conv_to_dict(conv))