初始化提交

This commit is contained in:
h88782481 2026-03-09 14:18:42 +08:00
commit 202731df74
28 changed files with 3140 additions and 0 deletions

131
utils/http.py Normal file
View file

@ -0,0 +1,131 @@
"""HTTP 工具 - 请求头构建、上游转发、SSE 流解析、响应构建"""
import json
import uuid
import logging
import requests
from flask import Response, jsonify
from config import Config
logger = logging.getLogger(__name__)
def gen_id(prefix=''):
"""生成唯一 ID"""
return f'{prefix}{uuid.uuid4().hex[:24]}'
# ─── 请求头构建 ────────────────────────────────────
def build_openai_headers(api_key):
"""构建 OpenAI 兼容请求头"""
return {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json',
}
def build_anthropic_headers(api_key):
"""构建 Anthropic 请求头,根据密钥前缀自动选择鉴权方式"""
headers = {
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json',
}
if api_key.startswith('sk-'):
headers['x-api-key'] = api_key
else:
headers['Authorization'] = f'Bearer {api_key}'
return headers
# ─── 响应构建 ──────────────────────────────────────
def sse_response(generator):
"""将生成器包装为 SSE 流式响应"""
return Response(
generator,
content_type='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'},
)
def error_json(message, error_type='proxy_error', status=502):
"""构建 JSON 错误响应"""
return jsonify({'error': {'message': str(message), 'type': error_type}}), status
# ─── 上游请求转发 ──────────────────────────────────
def forward_request(url, headers, payload, stream=False):
"""转发请求到上游 API
返回值:
成功: (response, None)
失败流式: (None, error_body_str)
失败非流式: (None, Flask Response)
"""
try:
resp = requests.post(
url, headers=headers, json=payload,
timeout=Config.API_TIMEOUT, stream=stream,
)
if resp.status_code != 200:
body = resp.content.decode('utf-8', errors='replace')
logger.warning(f'上游返回 {resp.status_code}: {body[:300]}')
if stream:
return None, f'上游错误 {resp.status_code}: {body}'
return None, Response(
resp.content, status=resp.status_code,
content_type=resp.headers.get('Content-Type', 'application/json'),
)
return resp, None
except requests.RequestException as e:
logger.error(f'请求上游失败: {e}')
if stream:
return None, str(e)
return None, error_json(str(e))
# ─── SSE 流解析 ───────────────────────────────────
def iter_openai_sse(response):
"""解析 OpenAI SSE 流yield chunk 字典yield None 表示 [DONE]"""
for line in response.iter_lines():
if not line:
continue
decoded = line.decode('utf-8', errors='replace')
if not decoded.startswith('data:'):
continue
data_str = decoded[5:].strip()
if data_str == '[DONE]':
yield None
return
try:
yield json.loads(data_str)
except json.JSONDecodeError:
continue
def iter_anthropic_sse(response):
"""解析 Anthropic SSE 流yield (event_type, data_dict) 元组"""
event_type = ''
for line in response.iter_lines():
if not line:
continue
decoded = line.decode('utf-8', errors='replace')
if decoded.startswith('event:'):
event_type = decoded[6:].strip()
elif decoded.startswith('data:'):
data_str = decoded[5:].strip()
if not data_str:
continue
try:
yield event_type, json.loads(data_str)
except json.JSONDecodeError:
continue