Files
gt-ai-os-community/apps/tenant-backend/app/api/messages.py
HackWeasel 310491a557 GT AI OS Community v2.0.33 - Add NVIDIA NIM and Nemotron agents
- Updated python_coding_microproject.csv to use NVIDIA NIM Kimi K2
- Updated kali_linux_shell_simulator.csv to use NVIDIA NIM Kimi K2
  - Made more general-purpose (flexible targets, expanded tools)
- Added nemotron-mini-agent.csv for fast local inference via Ollama
- Added nemotron-agent.csv for advanced reasoning via Ollama
- Added wiki page: Projects for NVIDIA NIMs and Nemotron
2025-12-12 17:47:14 -05:00

137 lines
4.5 KiB
Python

"""
Message API endpoints for GT 2.0 Tenant Backend
Handles message management within conversations.
"""
from fastapi import APIRouter, HTTPException, Depends
from fastapi.responses import JSONResponse, StreamingResponse
from typing import Dict, Any
from pydantic import BaseModel, Field
import json
import logging
from app.services.conversation_service import ConversationService
from app.core.security import get_current_user
from app.core.user_resolver import resolve_user_uuid
router = APIRouter(tags=["messages"])
class SendMessageRequest(BaseModel):
"""Request model for sending a message"""
content: str = Field(..., description="Message content")
stream: bool = Field(default=False, description="Stream the response")
logger = logging.getLogger(__name__)
async def get_conversation_service(
current_user: Dict[str, Any] = Depends(get_current_user)
) -> ConversationService:
"""Get properly initialized conversation service"""
tenant_domain, user_email, user_id = await resolve_user_uuid(current_user)
return ConversationService(tenant_domain=tenant_domain, user_id=user_id)
@router.get("/conversations/{conversation_id}/messages")
async def list_messages(
conversation_id: str,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> JSONResponse:
"""List messages in conversation"""
try:
# Get properly initialized service
service = await get_conversation_service(current_user)
# Get conversation with messages
result = await service.get_conversation(
conversation_id=conversation_id,
user_identifier=service.user_id,
include_messages=True
)
return JSONResponse(
status_code=200,
content={"messages": result.get("messages", [])}
)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/conversations/{conversation_id}/messages")
async def send_message(
conversation_id: str,
role: str,
content: str,
stream: bool = False,
current_user: Dict[str, Any] = Depends(get_current_user)
) -> JSONResponse:
"""Send a message and get AI response"""
try:
# Get properly initialized service
service = await get_conversation_service(current_user)
# Send message
result = await service.send_message(
conversation_id=conversation_id,
content=content,
stream=stream
)
# Generate title after first message
if result.get("is_first_message", False):
try:
await service.auto_generate_conversation_title(
conversation_id=conversation_id,
user_identifier=service.user_id
)
logger.info(f"✅ Title generated for conversation {conversation_id}")
except Exception as e:
logger.warning(f"Failed to generate title: {e}")
# Don't fail the request if title generation fails
return JSONResponse(status_code=201, content=result)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/conversations/{conversation_id}/messages/stream")
async def stream_message(
conversation_id: int,
content: str,
service: ConversationService = Depends(get_conversation_service),
current_user: str = Depends(get_current_user)
):
"""Stream AI response for a message"""
async def generate():
"""Generate SSE stream"""
try:
async for chunk in service.stream_message_response(
conversation_id=conversation_id,
message_content=content,
user_identifier=current_user
):
# Format as Server-Sent Event
yield f"data: {json.dumps({'content': chunk})}\n\n"
# Send completion signal
yield "data: [DONE]\n\n"
except Exception as e:
logger.error(f"Message streaming failed: {e}", exc_info=True)
yield f"data: {json.dumps({'error': 'An internal error occurred. Please try again.'})}\n\n"
return StreamingResponse(
generate(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no"
}
)