feat(T07): KI-Copilot with OpenRouter chat, voice input, action system, chat history
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""Copilot router: chat, action execution, history, and voice endpoints.
|
||||
|
||||
All endpoints require authentication (get_current_user).
|
||||
Actions are proposed by the AI and only executed after user confirmation.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.dependencies import get_current_user, get_pagination
|
||||
from app.models.user import User
|
||||
from app.schemas.copilot import (
|
||||
ActionRequest,
|
||||
ActionResponse,
|
||||
ChatHistoryResponse,
|
||||
ChatMessageItem,
|
||||
ChatRequest,
|
||||
ChatResponse,
|
||||
VoiceRequest,
|
||||
VoiceResponse,
|
||||
)
|
||||
from app.services import copilot_service
|
||||
|
||||
router = APIRouter(prefix="/copilot", tags=["copilot"])
|
||||
|
||||
|
||||
@router.post("/chat", response_model=ChatResponse, status_code=status.HTTP_200_OK)
|
||||
async def copilot_chat(
|
||||
body: ChatRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Send a message to the Copilot and receive a response with action proposals."""
|
||||
try:
|
||||
result = await copilot_service.chat(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
message=body.message,
|
||||
session_id=body.session_id,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"error": {"code": "COPILOT_ERROR", "message": str(exc)}},
|
||||
)
|
||||
return ChatResponse(**result)
|
||||
|
||||
|
||||
@router.post("/action", response_model=ActionResponse, status_code=status.HTTP_200_OK)
|
||||
async def copilot_action(
|
||||
body: ActionRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Execute a user-confirmed action proposed by the Copilot."""
|
||||
try:
|
||||
result = await copilot_service.execute_confirmed_action(
|
||||
db,
|
||||
action_type=body.action,
|
||||
params=body.params,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"error": {"code": "INVALID_ACTION", "message": str(exc)}},
|
||||
)
|
||||
return ActionResponse(**result)
|
||||
|
||||
|
||||
@router.get("/history", response_model=ChatHistoryResponse, status_code=status.HTTP_200_OK)
|
||||
async def copilot_history(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
pagination: dict = Depends(get_pagination),
|
||||
session_id: str | None = Query(None, description="Filter by session ID"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Get paginated chat history for the current user."""
|
||||
messages, total = await copilot_service.get_history(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
page=pagination["page"],
|
||||
page_size=pagination["page_size"],
|
||||
session_id=session_id,
|
||||
)
|
||||
return ChatHistoryResponse(
|
||||
items=[ChatMessageItem.model_validate(m.to_dict()) for m in messages],
|
||||
total=total,
|
||||
page=pagination["page"],
|
||||
page_size=pagination["page_size"],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/voice", response_model=VoiceResponse, status_code=status.HTTP_200_OK)
|
||||
async def copilot_voice(
|
||||
body: VoiceRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Accept audio, transcribe, and return a chat response with action proposals."""
|
||||
try:
|
||||
result = await copilot_service.voice_chat(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
audio_b64=body.audio,
|
||||
mime_type=body.mime_type,
|
||||
session_id=body.session_id,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={"error": {"code": "VOICE_ERROR", "message": str(exc)}},
|
||||
)
|
||||
return VoiceResponse(**result)
|
||||
Reference in New Issue
Block a user