jchat增加流式返回接口

This commit is contained in:
jingrow 2026-05-26 22:41:18 +08:00
parent 9cd7ef99b6
commit 1bfc77e9b3
3 changed files with 100 additions and 3 deletions

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter, HTTPException, Request from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import StreamingResponse
from service import ChatService from service import ChatService
from utils import jingrow_api_verify_and_billing from utils import jingrow_api_verify_and_billing
from settings import settings from settings import settings
@ -57,3 +58,43 @@ async def chat_api(data: dict, request: Request):
return result return result
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@router.post(settings.stream_route)
@dynamic_billing_wrapper
async def chat_stream_api(data: dict, request: Request):
"""
流式文本聊天API使用SSE (Server-Sent Events)格式返回
Args:
data: 包含以下字段的字典
- messages: 消息列表每个消息包含 role content必需
- model: 选择使用的模型可选默认为配置的默认模型
- temperature: 温度参数可选默认为0.7
- top_p: top_p参数可选默认为0.9
- max_tokens: 最大生成token数可选默认为2048
request: FastAPI 请求对象
Returns:
流式SSE响应
"""
if "messages" not in data:
raise HTTPException(status_code=400, detail="缺少messages参数")
if "model" in data:
service.model = data["model"]
if "temperature" in data:
service.temperature = data["temperature"]
if "top_p" in data:
service.top_p = data["top_p"]
if "max_tokens" in data:
service.max_tokens = data["max_tokens"]
return StreamingResponse(
service.chat_stream(data["messages"]),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no"
}
)

View File

@ -1,7 +1,8 @@
import json import json
import requests import requests
import aiohttp
import asyncio import asyncio
from typing import Dict, Optional, List, Union from typing import Dict, Optional, List, Union, AsyncGenerator
from settings import settings from settings import settings
# 默认模型配置 # 默认模型配置
@ -221,3 +222,57 @@ class ChatService:
"status": "error", "status": "error",
"message": f"聊天请求失败: {str(e)}" "message": f"聊天请求失败: {str(e)}"
} }
async def chat_stream(self, messages: List[Dict]) -> AsyncGenerator[str, None]:
"""异步流式处理聊天请求
Args:
messages: 消息列表每个消息包含 role content
Yields:
流式数据块
"""
model_config = self._get_model_config(self.model or default_model)
model_type = model_config["type"]
model_name = model_config["model"]
api_config = self._get_api_config(model_type)
payload = {
"model": model_name,
"messages": messages,
"temperature": self.temperature,
"top_p": self.top_p,
"max_tokens": self.max_tokens,
"stream": True
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_config['key']}"
}
try:
async with aiohttp.ClientSession() as session:
async with session.post(
api_config["url"],
headers=headers,
json=payload,
timeout=aiohttp.ClientTimeout(total=300)
) as response:
if response.status != 200:
yield f'data: {json.dumps({"error": "API request failed", "code": response.status})}\n\n'
return
async for line in response.content:
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:]
if data == '[DONE]':
yield 'data: [DONE]\n\n'
else:
yield f'data: {data}\n\n'
except asyncio.CancelledError:
raise
except Exception as e:
yield f'data: {json.dumps({"error": str(e)})}\n\n'

View File

@ -11,6 +11,7 @@ class Settings(BaseSettings):
# API路由配置 # API路由配置
router_prefix: str = "/jchat" router_prefix: str = "/jchat"
chat_route: str = "/chat" chat_route: str = "/chat"
stream_route: str = "/chat/stream"
default_api_name: str = "jingrow-chat" # 默认API名称 default_api_name: str = "jingrow-chat" # 默认API名称
upload_url: str = "http://images.jingrow.com:8080/api/v1/image" upload_url: str = "http://images.jingrow.com:8080/api/v1/image"