feat(T07): KI-Copilot with OpenRouter chat, voice input, action system, chat history

This commit is contained in:
2026-07-17 02:28:41 +02:00
parent cb89e3ff1e
commit 5cc9f8e9a9
24 changed files with 2878 additions and 188 deletions
+94
View File
@@ -0,0 +1,94 @@
"""Pydantic schemas for Copilot chat, action, voice, and history endpoints."""
from datetime import datetime
from typing import Any, Optional
from pydantic import BaseModel, ConfigDict, Field
class ChatRequest(BaseModel):
"""Request body for POST /copilot/chat."""
message: str = Field(..., min_length=1, description="User message to the Copilot")
session_id: Optional[str] = Field(None, description="Existing session UUID to continue")
class ActionItem(BaseModel):
"""A single action proposed by the Copilot."""
type: str = Field(..., description="Action type, e.g. search_vehicles")
params: dict[str, Any] = Field(default_factory=dict, description="Action parameters")
class ChatResponse(BaseModel):
"""Response body for POST /copilot/chat."""
model_config = ConfigDict(from_attributes=True)
response: str = Field(..., description="Assistant text response")
actions: list[ActionItem] = Field(default_factory=list, description="Proposed actions")
session_id: str = Field(..., description="Session UUID")
message_id: str = Field(..., description="Assistant message UUID")
class ActionRequest(BaseModel):
"""Request body for POST /copilot/action — user confirms an action."""
action: str = Field(..., min_length=1, description="Action type to execute")
params: dict[str, Any] = Field(default_factory=dict, description="Action parameters")
session_id: Optional[str] = Field(None, description="Session context")
class ActionResponse(BaseModel):
"""Response body for POST /copilot/action."""
model_config = ConfigDict(from_attributes=True)
action: str = Field(..., description="Executed action type")
result: Any = Field(..., description="Action result data")
success: bool = Field(..., description="Whether the action succeeded")
class ChatMessageItem(BaseModel):
"""A single chat message in history."""
model_config = ConfigDict(from_attributes=True)
id: str
session_id: str
role: str
content: str
actions: Optional[list[dict[str, Any]]] = None
created_at: Optional[datetime] = None
class ChatHistoryResponse(BaseModel):
"""Paginated chat history response."""
items: list[ChatMessageItem] = Field(default_factory=list)
total: int = Field(0)
page: int = Field(1)
page_size: int = Field(20)
class VoiceRequest(BaseModel):
"""Request body for POST /copilot/voice.
Accepts base64-encoded audio data for transcription.
"""
audio: str = Field(..., min_length=1, description="Base64-encoded audio data")
mime_type: str = Field("audio/webm", description="Audio MIME type")
session_id: Optional[str] = Field(None, description="Existing session UUID")
class VoiceResponse(BaseModel):
"""Response body for POST /copilot/voice."""
model_config = ConfigDict(from_attributes=True)
transcription: str = Field(..., description="Transcribed text")
response: str = Field(..., description="Assistant text response")
actions: list[ActionItem] = Field(default_factory=list, description="Proposed actions")
session_id: str = Field(..., description="Session UUID")
message_id: str = Field(..., description="Assistant message UUID")