Add File
This commit is contained in:
180
src/landppt/api/openai_compat.py
Normal file
180
src/landppt/api/openai_compat.py
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
"""
|
||||||
|
OpenAI-compatible API endpoints
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
ChatCompletionRequest, ChatCompletionResponse, ChatCompletionChoice,
|
||||||
|
CompletionRequest, CompletionResponse, CompletionChoice,
|
||||||
|
ChatMessage, Usage
|
||||||
|
)
|
||||||
|
from ..services.ai_service import AIService
|
||||||
|
from ..core.config import ai_config
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
ai_service = AIService()
|
||||||
|
|
||||||
|
@router.post("/chat/completions", response_model=ChatCompletionResponse)
|
||||||
|
async def create_chat_completion(request: ChatCompletionRequest):
|
||||||
|
"""
|
||||||
|
Create a chat completion (OpenAI compatible)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Check if this is a PPT-related request
|
||||||
|
last_message = request.messages[-1].content if request.messages else ""
|
||||||
|
|
||||||
|
if ai_service.is_ppt_request(last_message):
|
||||||
|
# Handle PPT generation request
|
||||||
|
response_content = await ai_service.handle_ppt_chat_request(request)
|
||||||
|
else:
|
||||||
|
# Handle general chat request
|
||||||
|
response_content = await ai_service.handle_general_chat_request(request)
|
||||||
|
|
||||||
|
# Calculate token usage (simplified)
|
||||||
|
prompt_tokens = sum(len(msg.content.split()) for msg in request.messages)
|
||||||
|
completion_tokens = len(response_content.split())
|
||||||
|
|
||||||
|
choice = ChatCompletionChoice(
|
||||||
|
index=0,
|
||||||
|
message=ChatMessage(role="assistant", content=response_content),
|
||||||
|
finish_reason="stop"
|
||||||
|
)
|
||||||
|
|
||||||
|
return ChatCompletionResponse(
|
||||||
|
model=request.model,
|
||||||
|
choices=[choice],
|
||||||
|
usage=Usage(
|
||||||
|
prompt_tokens=prompt_tokens,
|
||||||
|
completion_tokens=completion_tokens,
|
||||||
|
total_tokens=prompt_tokens + completion_tokens
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Error generating completion: {str(e)}")
|
||||||
|
|
||||||
|
@router.post("/completions", response_model=CompletionResponse)
|
||||||
|
async def create_completion(request: CompletionRequest):
|
||||||
|
"""
|
||||||
|
Create a text completion (OpenAI compatible)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
prompt = request.prompt if isinstance(request.prompt, str) else request.prompt[0]
|
||||||
|
|
||||||
|
if ai_service.is_ppt_request(prompt):
|
||||||
|
# Handle PPT generation request
|
||||||
|
response_text = await ai_service.handle_ppt_completion_request(request)
|
||||||
|
else:
|
||||||
|
# Handle general completion request
|
||||||
|
response_text = await ai_service.handle_general_completion_request(request)
|
||||||
|
|
||||||
|
# Calculate token usage (simplified)
|
||||||
|
prompt_tokens = len(prompt.split())
|
||||||
|
completion_tokens = len(response_text.split())
|
||||||
|
|
||||||
|
choice = CompletionChoice(
|
||||||
|
text=response_text,
|
||||||
|
index=0,
|
||||||
|
finish_reason="stop"
|
||||||
|
)
|
||||||
|
|
||||||
|
return CompletionResponse(
|
||||||
|
model=request.model,
|
||||||
|
choices=[choice],
|
||||||
|
usage=Usage(
|
||||||
|
prompt_tokens=prompt_tokens,
|
||||||
|
completion_tokens=completion_tokens,
|
||||||
|
total_tokens=prompt_tokens + completion_tokens
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Error generating completion: {str(e)}")
|
||||||
|
|
||||||
|
@router.get("/models")
|
||||||
|
async def list_models():
|
||||||
|
"""
|
||||||
|
List available models (OpenAI compatible)
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": "landppt-v1",
|
||||||
|
"object": "model",
|
||||||
|
"created": 1677610602,
|
||||||
|
"owned_by": "landppt",
|
||||||
|
"permission": [],
|
||||||
|
"root": "landppt-v1",
|
||||||
|
"parent": None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "landppt-ppt-generator",
|
||||||
|
"object": "model",
|
||||||
|
"created": 1677610602,
|
||||||
|
"owned_by": "landppt",
|
||||||
|
"permission": [],
|
||||||
|
"root": "landppt-ppt-generator",
|
||||||
|
"parent": None
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async def stream_chat_completion(request: ChatCompletionRequest) -> AsyncGenerator[str, None]:
|
||||||
|
"""
|
||||||
|
Stream chat completion responses
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Simulate streaming response
|
||||||
|
last_message = request.messages[-1].content if request.messages else ""
|
||||||
|
|
||||||
|
if ai_service.is_ppt_request(last_message):
|
||||||
|
response_content = await ai_service.handle_ppt_chat_request(request)
|
||||||
|
else:
|
||||||
|
response_content = await ai_service.handle_general_chat_request(request)
|
||||||
|
|
||||||
|
# Split response into chunks for streaming
|
||||||
|
words = response_content.split()
|
||||||
|
for i, word in enumerate(words):
|
||||||
|
chunk_data = {
|
||||||
|
"id": f"chatcmpl-{i}",
|
||||||
|
"object": "chat.completion.chunk",
|
||||||
|
"created": 1677610602,
|
||||||
|
"model": request.model,
|
||||||
|
"choices": [{
|
||||||
|
"index": 0,
|
||||||
|
"delta": {"content": word + " "},
|
||||||
|
"finish_reason": None
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
yield f"data: {json.dumps(chunk_data)}\n\n"
|
||||||
|
await asyncio.sleep(0.05) # Simulate processing delay
|
||||||
|
|
||||||
|
# Send final chunk
|
||||||
|
final_chunk = {
|
||||||
|
"id": f"chatcmpl-final",
|
||||||
|
"object": "chat.completion.chunk",
|
||||||
|
"created": 1677610602,
|
||||||
|
"model": request.model,
|
||||||
|
"choices": [{
|
||||||
|
"index": 0,
|
||||||
|
"delta": {},
|
||||||
|
"finish_reason": "stop"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
yield f"data: {json.dumps(final_chunk)}\n\n"
|
||||||
|
yield "data: [DONE]\n\n"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_chunk = {
|
||||||
|
"error": {
|
||||||
|
"message": str(e),
|
||||||
|
"type": "server_error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield f"data: {json.dumps(error_chunk)}\n\n"
|
||||||
Reference in New Issue
Block a user