playground internal api migration

This commit is contained in:
2025-09-12 09:40:05 +02:00
parent 9130c0953b
commit 40454d6fab
2 changed files with 214 additions and 1 deletions

View File

@@ -3,14 +3,17 @@ Internal LLM API endpoints - for frontend use with JWT authentication
"""
import logging
from typing import Dict, Any, List
from typing import Dict, Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.database import get_db
from app.core.security import get_current_user
from app.services.llm.service import llm_service
from app.services.llm.models import ChatRequest, ChatMessage as LLMChatMessage
from app.services.llm.exceptions import LLMError, ProviderError, SecurityError, ValidationError
from app.api.v1.llm import get_cached_models # Reuse the caching logic
logger = logging.getLogger(__name__)
@@ -108,4 +111,96 @@ async def get_metrics(
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve metrics"
)
class ChatCompletionRequest(BaseModel):
"""Request model for chat completions"""
model: str
messages: List[Dict[str, str]]
temperature: Optional[float] = Field(default=0.7, ge=0.0, le=2.0)
max_tokens: Optional[int] = Field(default=1000, ge=1)
top_p: Optional[float] = Field(default=1.0, ge=0.0, le=1.0)
stream: Optional[bool] = False
@router.post("/chat/completions")
async def create_chat_completion(
request: ChatCompletionRequest,
current_user: Dict[str, Any] = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
Create chat completion for authenticated frontend users.
This endpoint is for playground and internal use only, using JWT authentication.
"""
try:
# Get user ID from JWT token context
user_id = str(current_user.get("id", current_user.get("sub", "0")))
# Convert request to LLM service format
# For internal use, we use a special api_key_id of 0 to indicate JWT auth
chat_request = ChatRequest(
model=request.model,
messages=[
LLMChatMessage(role=msg["role"], content=msg["content"])
for msg in request.messages
],
temperature=request.temperature,
max_tokens=request.max_tokens,
top_p=request.top_p,
stream=request.stream,
user_id=user_id,
api_key_id=0 # Special value for JWT-authenticated requests
)
# Log the request for debugging
logger.info(f"Internal chat completion request from user {current_user.get('id')}: model={request.model}")
# Process the request through the LLM service
response = await llm_service.create_chat_completion(chat_request)
# Format the response to match OpenAI's structure
formatted_response = {
"id": response.id,
"object": "chat.completion",
"created": response.created,
"model": response.model,
"choices": [
{
"index": choice.index,
"message": {
"role": choice.message.role,
"content": choice.message.content
},
"finish_reason": choice.finish_reason
}
for choice in response.choices
],
"usage": {
"prompt_tokens": response.usage.prompt_tokens if response.usage else 0,
"completion_tokens": response.usage.completion_tokens if response.usage else 0,
"total_tokens": response.usage.total_tokens if response.usage else 0
} if response.usage else None
}
return formatted_response
except ValidationError as e:
logger.error(f"Validation error in chat completion: {e}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid request: {str(e)}"
)
except LLMError as e:
logger.error(f"LLM service error: {e}")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"LLM service error: {str(e)}"
)
except Exception as e:
logger.error(f"Unexpected error in chat completion: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to process chat completion"
)

View File

@@ -0,0 +1,118 @@
/* Dark mode improvements for chat interface */
.dark .chat-message-assistant {
background-color: rgb(51 65 85); /* slate-700 */
color: white !important;
}
.dark .chat-message-assistant * {
color: white !important;
}
.dark .chat-message-user {
/* User messages keep their primary color */
}
.dark .chat-input {
background-color: rgb(30 41 59); /* slate-800 */
color: white !important;
border-color: rgb(71 85 105); /* slate-600 */
}
.dark .chat-input::placeholder {
color: rgb(148 163 184); /* slate-400 */
}
.dark .chat-timestamp {
color: rgb(148 163 184); /* slate-400 */
}
.dark .chat-thinking {
background-color: rgb(51 65 85); /* slate-700 */
color: white !important;
}
.dark .chat-thinking * {
color: white !important;
}
/* Ensure ALL text in markdown content is white in dark mode */
.dark .markdown-content,
.dark .markdown-content * {
color: white !important;
}
.dark .markdown-content p,
.dark .markdown-content span,
.dark .markdown-content div,
.dark .markdown-content li,
.dark .markdown-content h1,
.dark .markdown-content h2,
.dark .markdown-content h3,
.dark .markdown-content h4,
.dark .markdown-content h5,
.dark .markdown-content h6,
.dark .markdown-content strong,
.dark .markdown-content b,
.dark .markdown-content em,
.dark .markdown-content i,
.dark .markdown-content a,
.dark .markdown-content blockquote {
color: white !important;
}
.dark .markdown-content code {
background-color: rgb(30 41 59); /* slate-800 */
color: white !important;
border-color: rgb(71 85 105); /* slate-600 */
}
.dark .markdown-content pre,
.dark .markdown-content pre * {
background-color: rgb(30 41 59); /* slate-800 */
color: white !important;
border-color: rgb(71 85 105); /* slate-600 */
}
/* Make sure bold text is white */
.dark strong,
.dark b {
color: white !important;
font-weight: bold;
}
/* Prose styles override for dark mode */
.dark .prose,
.dark .prose-sm {
color: white !important;
}
.dark .prose h1,
.dark .prose h2,
.dark .prose h3,
.dark .prose h4,
.dark .prose h5,
.dark .prose h6,
.dark .prose-sm h1,
.dark .prose-sm h2,
.dark .prose-sm h3,
.dark .prose-sm h4,
.dark .prose-sm h5,
.dark .prose-sm h6 {
color: white !important;
font-weight: bold;
}
.dark .prose strong,
.dark .prose-sm strong,
.dark .prose b,
.dark .prose-sm b {
color: white !important;
font-weight: bold;
}
.dark .prose em,
.dark .prose-sm em,
.dark .prose i,
.dark .prose-sm i {
color: white !important;
}