|
|
|
|
|
|
|
|
|
|
|
import time |
|
import uuid |
|
|
|
from fastapi import APIRouter, HTTPException |
|
from fastapi.responses import JSONResponse, StreamingResponse |
|
from src.models.requests import OpenAIChatRequest |
|
from src.cores.sessions import get_or_create_session, session_store |
|
from src.services.streaming import event_generator |
|
from config import MODEL |
|
|
|
|
|
router = APIRouter() |
|
|
|
@router.post("/completions") |
|
async def openai_chat_completions(req: OpenAIChatRequest): |
|
""" |
|
Handle OpenAI-compatible chat completion requests. |
|
Supports streaming and non-streaming modes based on client request. |
|
|
|
Steps: |
|
- Validate the presence and structure of messages in the request |
|
- Extract conversation history and current user input from messages |
|
- Retrieve or create a session for managing conversation state |
|
- Update session history with prior conversation |
|
- If streaming is requested, return a streaming response |
|
- Otherwise, submit input to AI client and collect full response |
|
- Append new interaction to session history |
|
- Return response formatted according to OpenAI chat completion API |
|
|
|
Returns: |
|
JSONResponse or StreamingResponse with chat completion data and session ID |
|
""" |
|
|
|
if not req.messages: |
|
raise HTTPException(status_code=400, detail="Messages cannot be empty") |
|
|
|
history = [] |
|
current_input = "" |
|
|
|
|
|
try: |
|
|
|
if req.messages[-1]["role"] != "user": |
|
raise ValueError("Last message must be from user") |
|
|
|
current_input = req.messages[-1]["content"] |
|
|
|
|
|
messages = req.messages[:-1] |
|
for i in range(0, len(messages), 2): |
|
if i + 1 < len(messages): |
|
user_msg = messages[i] |
|
assistant_msg = messages[i + 1] |
|
|
|
|
|
if user_msg["role"] != "user" or assistant_msg["role"] != "assistant": |
|
continue |
|
|
|
|
|
history.append({ |
|
"input": user_msg["content"], |
|
"response": assistant_msg["content"] |
|
}) |
|
except (KeyError, ValueError) as e: |
|
|
|
raise HTTPException(status_code=400, detail=f"Invalid message format: {str(e)}") |
|
|
|
|
|
model = req.model or MODEL |
|
|
|
|
|
session_id = get_or_create_session(req.session_id, model) |
|
|
|
|
|
last_update, session_data = session_store[session_id] |
|
|
|
|
|
session_data["history"] = history |
|
|
|
|
|
session_store[session_id] = (time.time(), session_data) |
|
|
|
client = session_data["client"] |
|
|
|
|
|
if client is None: |
|
raise HTTPException(status_code=503, detail="AI client not available") |
|
|
|
|
|
if req.stream: |
|
return StreamingResponse( |
|
event_generator(current_input, model, session_id), |
|
media_type="text/event-stream" |
|
) |
|
|
|
|
|
try: |
|
jarvis_response = client.submit(multi={"text": current_input}, api_name="/api") |
|
except Exception as e: |
|
|
|
raise HTTPException(status_code=500, detail=f"Failed to submit to AI: {str(e)}") |
|
|
|
buffer = "" |
|
|
|
|
|
for partial in jarvis_response: |
|
text = partial[0][0][1] |
|
buffer = text |
|
|
|
|
|
session_data["history"].append({"input": current_input, "response": buffer}) |
|
|
|
|
|
session_store[session_id] = (time.time(), session_data) |
|
|
|
|
|
response = { |
|
"id": f"chatcmpl-{uuid.uuid4().hex[:8]}", |
|
"object": "chat.completion", |
|
"created": int(time.time()), |
|
"model": model, |
|
"choices": [ |
|
{ |
|
"index": 0, |
|
"message": { |
|
"role": "assistant", |
|
"content": buffer |
|
}, |
|
"finish_reason": "stop" |
|
} |
|
], |
|
"session_id": session_id |
|
} |
|
|
|
|
|
return JSONResponse(response) |
|
|