| """ |
| Frox AI — API Schemas |
| |
| OpenAI-compatible request/response shapes. Matching this contract |
| precisely (not "close enough") is what lets Open WebUI, most desktop |
| AI clients, and any future CLI/Android client point at this server |
| via a base-URL setting with zero code changes on their end. |
| """ |
| from __future__ import annotations |
|
|
| import time |
| import uuid |
| from typing import Any, Dict, List, Literal, Optional |
|
|
| from pydantic import BaseModel, Field |
|
|
|
|
| class ToolCallFunction(BaseModel): |
| name: str |
| arguments: str |
|
|
|
|
| class ToolCall(BaseModel): |
| id: str |
| type: Literal["function"] = "function" |
| function: ToolCallFunction |
|
|
|
|
| class ChatMessage(BaseModel): |
| role: Literal["system", "user", "assistant", "tool"] |
| content: Optional[str] = None |
| name: Optional[str] = None |
| tool_calls: Optional[List[ToolCall]] = None |
| tool_call_id: Optional[str] = None |
|
|
|
|
| class ChatCompletionRequest(BaseModel): |
| model: str = "classic" |
| messages: List[ChatMessage] |
| stream: bool = False |
| max_tokens: int = Field(default=1024, ge=1, le=32768) |
| temperature: float = Field(default=0.7, ge=0.0, le=2.0) |
| top_p: float = Field(default=0.9, ge=0.0, le=1.0) |
| top_k: int = Field(default=50, ge=0) |
| repetition_penalty: float = Field(default=1.1, ge=0.1, le=2.0) |
| user: Optional[str] = None |
| session_id: Optional[str] = None |
| tools: Optional[List[Dict[str, Any]]] = None |
|
|
|
|
| class ChatCompletionChoice(BaseModel): |
| index: int = 0 |
| message: ChatMessage |
| finish_reason: Literal["stop", "length", "tool_calls"] = "stop" |
|
|
|
|
| class Usage(BaseModel): |
| prompt_tokens: int |
| completion_tokens: int |
| total_tokens: int |
|
|
|
|
| class ChatCompletionResponse(BaseModel): |
| id: str = Field(default_factory=lambda: f"chatcmpl-{uuid.uuid4().hex[:24]}") |
| object: Literal["chat.completion"] = "chat.completion" |
| created: int = Field(default_factory=lambda: int(time.time())) |
| model: str |
| choices: List[ChatCompletionChoice] |
| usage: Usage |
| frox_trace: Optional[List[Dict[str, Any]]] = None |
|
|
|
|
| class ChatCompletionChunkDelta(BaseModel): |
| role: Optional[Literal["assistant"]] = None |
| content: Optional[str] = None |
| tool_calls: Optional[List[ToolCall]] = None |
|
|
|
|
| class ChatCompletionChunkChoice(BaseModel): |
| index: int = 0 |
| delta: ChatCompletionChunkDelta |
| finish_reason: Optional[Literal["stop", "length", "tool_calls"]] = None |
|
|
|
|
| class ChatCompletionChunk(BaseModel): |
| id: str |
| object: Literal["chat.completion.chunk"] = "chat.completion.chunk" |
| created: int = Field(default_factory=lambda: int(time.time())) |
| model: str |
| choices: List[ChatCompletionChunkChoice] |
|
|
|
|
| class ModelInfo(BaseModel): |
| id: str |
| object: Literal["model"] = "model" |
| created: int = Field(default_factory=lambda: int(time.time())) |
| owned_by: str = "frox-ai" |
|
|
|
|
| class ModelListResponse(BaseModel): |
| object: Literal["list"] = "list" |
| data: List[ModelInfo] |
|
|
|
|
| class ErrorDetail(BaseModel): |
| message: str |
| type: str = "invalid_request_error" |
| code: Optional[str] = None |
| param: Optional[str] = None |
|
|
|
|
| class ErrorResponse(BaseModel): |
| error: ErrorDetail |
|
|
|
|
| class ToolExecuteRequest(BaseModel): |
| tool: str |
| args: Dict[str, Any] = Field(default_factory=dict) |
| user_id: str = "default" |
| session_id: Optional[str] = None |
| plan: str = "free" |
|
|
|
|
| class HealthResponse(BaseModel): |
| status: Literal["ok", "loading", "error"] |
| model: Optional[str] = None |
| device: Optional[str] = None |
| version: str = "1.1.0" |
|
|