sehsapneb commited on
Commit
a920e80
·
verified ·
1 Parent(s): c33d33c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -0
app.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ import uuid
4
+ import time
5
+ from flask import Flask, request, jsonify, Response
6
+
7
+ # --- 1. 初始化Flask应用 ---
8
+ app = Flask(__name__)
9
+
10
+ # --- 2. gpt-oss.com API的固定配置 (来自我们之前的分析) ---
11
+ GPT_OSS_API_URL = "https://api.gpt-oss.com/chatkit"
12
+ GPT_OSS_HEADERS = {
13
+ 'authority': 'api.gpt-oss.com',
14
+ 'accept': 'text/event-stream',
15
+ 'content-type': 'application/json',
16
+ 'origin': 'https://gpt-oss.com',
17
+ 'referer': 'https://gpt-oss.com/',
18
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
19
+ 'x-selected-model': 'gpt-oss-120b', # 模型可以在此硬编码,或后续从请求中动态获取
20
+ }
21
+
22
+ # --- 3. 核心:创建OpenAI兼容的API端点 ---
23
+ @app.route('/v1/chat/completions', methods=['POST'])
24
+ def chat_completions_proxy():
25
+ """
26
+ 这个端点模仿OpenAI的 `/v1/chat/completions` 接口。
27
+ 它接收OpenAI格式的请求,然后代理到gpt-oss.com。
28
+ """
29
+ # 按照要求,我们不验证API Key。可以直接忽略 request.headers['Authorization']。
30
+
31
+ # (一) 解析客户端发来的OpenAI格式请求
32
+ try:
33
+ openai_request_data = request.json
34
+ # 从消息列表中找到用户最新的提问,作为我们的提示词
35
+ messages = openai_request_data.get("messages", [])
36
+ user_prompt = next((m['content'] for m in reversed(messages) if m.get('role') == 'user'), None)
37
+
38
+ if not user_prompt:
39
+ return jsonify({"error": "在请求中未找到用户消息。"}), 400
40
+
41
+ # 检查客户端是否请求了流式响应
42
+ stream_requested = openai_request_data.get("stream", False)
43
+
44
+ except Exception as e:
45
+ return jsonify({"error": f"请求格式无效: {e}"}), 400
46
+
47
+ # (二) 准备发往 gpt-oss.com API 的请求
48
+ # 为每个独立的对话生成一个全新的随机user_id
49
+ random_user_id = str(uuid.uuid4())
50
+ gpt_oss_cookies = {'user_id': random_user_id}
51
+ # 构建gpt-oss服务需要的特殊Payload
52
+ gpt_oss_payload = {
53
+ "op": "threads.create",
54
+ "params": {"input": {"text": user_prompt, "content": [{"type": "input_text", "text": user_prompt}]}}
55
+ }
56
+
57
+ # (三) 定义一个“生成器”函数,用于处理和转换流式数据
58
+ def generate_stream():
59
+ try:
60
+ # 向真正的后端服务发起流式请求
61
+ with requests.post(
62
+ GPT_OSS_API_URL,
63
+ headers=GPT_OSS_HEADERS,
64
+ cookies=gpt_oss_cookies,
65
+ json=gpt_oss_payload,
66
+ stream=True,
67
+ timeout=120
68
+ ) as response:
69
+ response.raise_for_status() # 如果状态码不是2xx,则抛出异常
70
+
71
+ # (四) 核心翻译逻辑:逐行读取gpt-oss的响应,并转换为OpenAI格式
72
+ for line in response.iter_lines():
73
+ if line:
74
+ line_str = line.decode('utf-8')
75
+ if line_str.startswith('data: '):
76
+ json_data_str = line_str[6:]
77
+ try:
78
+ gpt_oss_data = json.loads(json_data_str)
79
+
80
+ # 我们只关心包含文本片段的事件
81
+ event_type = gpt_oss_data.get('type')
82
+ if (event_type == 'thread.item_updated' and
83
+ gpt_oss_data.get('update', {}).get('type') == 'assistant_message.content_part.text_delta'):
84
+
85
+ delta_content = gpt_oss_data['update'].get('delta', '')
86
+
87
+ # 构建一个OpenAI流式响应的JSON块
88
+ openai_chunk = {
89
+ "id": f"chatcmpl-{str(uuid.uuid4())}",
90
+ "object": "chat.completion.chunk",
91
+ "created": int(time.time()),
92
+ "model": "gpt-oss-120b",
93
+ "choices": [
94
+ {
95
+ "index": 0,
96
+ "delta": {
97
+ "content": delta_content
98
+ },
99
+ "finish_reason": None
100
+ }
101
+ ]
102
+ }
103
+ # 使用SSE(服务器发送事件)格式 yield 出去
104
+ yield f"data: {json.dumps(openai_chunk)}\n\n"
105
+
106
+ except json.JSONDecodeError:
107
+ continue # 忽略无法解析的行
108
+
109
+ # (五) 流式传输结束后,发送一个表示结束的特殊标记
110
+ yield "data: [DONE]\n\n"
111
+
112
+ except requests.exceptions.RequestException as e:
113
+ error_chunk = {"error": f"与后端服务通信失败: {e}"}
114
+ yield f"data: {json.dumps(error_chunk)}\n\n"
115
+
116
+ # (六) 根据客户端请求,返回流式响应或一次性完整响应
117
+ if stream_requested:
118
+ # 如果客户端要流式,就返回我们的生成器函数
119
+ return Response(generate_stream(), mimetype='text/event-stream')
120
+ else:
121
+ # 如果客户端要一次性响应,我们就在服务器端拼接完整结果再返回
122
+ # (注意:gpt-oss本身就是流式的,所以这个分支需要我们在服务器端缓存)
123
+ full_response_content = ""
124
+ for chunk in generate_stream():
125
+ # 这里需要更复杂的解析逻辑来拼接,为简化起见,我们优先推荐使用流式
126
+ pass # 简单实现:非流式模式暂不支持或需要更复杂的实现
127
+ # 为了简单起见,我们主要支持流式,因为这是最高效的方式
128
+ return jsonify({"error": "非流式响应目前不受支持,请在请求中设置 'stream': true"}), 501
129
+
130
+
131
+ # --- 4. 启动应用 ---
132
+ if __name__ == '__main__':
133
+ # 在本地测试时,可以使用 app.run()
134
+ # 部署到Gunicorn时,它会直接使用'app'这个实例
135
+ app.run(debug=True, port=7860)