jchat增加流式返回接口

This commit is contained in:
jingrow 2026-05-26 22:41:18 +08:00
parent 9cd7ef99b6
commit 23d22609ad
3 changed files with 111 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,7 @@
import json import json
import requests import requests
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 +221,69 @@ class ChatService:
"status": "error", "status": "error",
"message": f"聊天请求失败: {str(e)}" "message": f"聊天请求失败: {str(e)}"
} }
def chat_stream_sync(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:
response = requests.post(
api_config["url"],
headers=headers,
json=payload,
stream=True,
timeout=(10, 300)
)
if response.status_code != 200:
yield f'data: {json.dumps({"error": "API request failed", "code": response.status_code})}\n\n'
return
for line in response.iter_lines():
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 Exception as e:
yield f'data: {json.dumps({"error": str(e)})}\n\n'
async def chat_stream(self, messages: List[Dict]) -> AsyncGenerator[str, None]:
"""异步流式处理聊天请求
Args:
messages: 消息列表每个消息包含 role content
Yields:
流式数据块
"""
loop = asyncio.get_event_loop()
async for chunk in loop.run_in_executor(None, self.chat_stream_sync, messages):
yield chunk

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"