bunnybun07 commited on
Commit
f9a1ce9
·
verified ·
1 Parent(s): 431b91e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -0
app.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, Response, jsonify, stream_with_context
2
+ import requests
3
+ import json
4
+ import uuid
5
+ import time
6
+ from datetime import datetime
7
+
8
+ ORIGINAL_API_URL = "https://app.unlimitedai.chat/api/chat"
9
+
10
+ app = Flask(__name__)
11
+
12
+
13
+ @app.route('/v1/models', methods=['GET'])
14
+ def list_models():
15
+ # 你可以根据实际情况自定义模型列表
16
+ models = [
17
+ {
18
+ "id": "chat-model-reasoning",
19
+ "object": "model",
20
+ "created": 1713235200,
21
+ "owned_by": "organization-owner",
22
+ "permission": [],
23
+ "root": "chat-model-reasoning",
24
+ "parent": None
25
+ }
26
+ ]
27
+ return jsonify({"object": "list", "data": models})
28
+
29
+
30
+ @app.route('/v1/chat/completions', methods=['POST'])
31
+ def chat_completions():
32
+ data = request.json
33
+ is_stream = data.get('stream', False)
34
+ messages = data.get('messages', [])
35
+ original_messages = []
36
+ for msg in messages:
37
+ original_msg = {
38
+ "id": str(uuid.uuid4()),
39
+ "createdAt": datetime.utcnow().isoformat() + "Z",
40
+ "role": msg["role"],
41
+ "content": msg["content"],
42
+ "parts": [
43
+ {
44
+ "type": "text",
45
+ "text": msg["content"]
46
+ }
47
+ ]
48
+ }
49
+ original_messages.append(original_msg)
50
+ original_request = {
51
+ "id": str(uuid.uuid4()),
52
+ "messages": original_messages,
53
+ "selectedChatModel": "chat-model-reasoning"
54
+ }
55
+ headers = {'Content-Type': 'application/json'}
56
+ if is_stream:
57
+ return stream_response(original_request, headers, data)
58
+ else:
59
+ return non_stream_response(original_request, headers, data)
60
+
61
+
62
+ def stream_response(original_request, headers, openai_request):
63
+ def generate():
64
+ response = requests.post(
65
+ ORIGINAL_API_URL,
66
+ headers=headers,
67
+ json=original_request,
68
+ stream=True
69
+ )
70
+
71
+ # 用于存储推理和回复内容
72
+ reasoning_content = ""
73
+ reply_content = ""
74
+
75
+ message_id = None
76
+
77
+ for line in response.iter_lines():
78
+ if not line:
79
+ continue
80
+
81
+ line_str = line.decode('utf-8')
82
+
83
+ # 解析不同类型的响应行
84
+ if line_str.startswith('f:'):
85
+ # 消息 ID
86
+ message_data = json.loads(line_str[2:])
87
+ message_id = message_data.get("messageId")
88
+
89
+ # 发送 OpenAI 兼容的流式开始标记
90
+ start_chunk = {
91
+ "id": f"chatcmpl-{uuid.uuid4()}",
92
+ "object": "chat.completion.chunk",
93
+ "created": int(time.time()),
94
+ "model": openai_request.get("model", "gpt-3.5-turbo"),
95
+ "choices": [
96
+ {
97
+ "index": 0,
98
+ "delta": {"role": "assistant"},
99
+ "finish_reason": None
100
+ }
101
+ ]
102
+ }
103
+ yield f"data: {json.dumps(start_chunk)}\n\n"
104
+
105
+ elif line_str.startswith('g:'):
106
+ # 推理部分,在 OpenAI 格式中不直接显示,但我们可以收集它
107
+ reasoning_part = line_str[2:].strip('"').replace("\\n", "\n")
108
+ reasoning_content += reasoning_part
109
+
110
+ content_chunk = {
111
+ "id": f"chatcmpl-{uuid.uuid4()}",
112
+ "object": "chat.completion.chunk",
113
+ "created": int(time.time()),
114
+ "model": openai_request.get("model", "gpt-3.5-turbo"),
115
+ "choices": [
116
+ {
117
+ "index": 0,
118
+ "delta": {"reasoning_content": reasoning_part},
119
+ "finish_reason": None
120
+ }
121
+ ]
122
+ }
123
+ yield f"data: {json.dumps(content_chunk)}\n\n"
124
+
125
+ elif line_str.startswith('0:'):
126
+ # 回复部分,这是我们需要流式传输的主要内容
127
+ reply_part = line_str[2:].strip('"').replace("\\n", "\n")
128
+ reply_content += reply_part
129
+
130
+ # 发送 OpenAI 兼容的内容块
131
+ content_chunk = {
132
+ "id": f"chatcmpl-{uuid.uuid4()}",
133
+ "object": "chat.completion.chunk",
134
+ "created": int(time.time()),
135
+ "model": openai_request.get("model", "gpt-3.5-turbo"),
136
+ "choices": [
137
+ {
138
+ "index": 0,
139
+ "delta": {"content": reply_part},
140
+ "finish_reason": None
141
+ }
142
+ ]
143
+ }
144
+ yield f"data: {json.dumps(content_chunk)}\n\n"
145
+
146
+ elif line_str.startswith('e:') or line_str.startswith('d:'):
147
+ # 结束标记
148
+ finish_data = json.loads(line_str[2:])
149
+ finish_reason = finish_data.get("finishReason", "stop")
150
+
151
+ # 发送 OpenAI 兼容的结束块
152
+ end_chunk = {
153
+ "id": f"chatcmpl-{uuid.uuid4()}",
154
+ "object": "chat.completion.chunk",
155
+ "created": int(time.time()),
156
+ "model": openai_request.get("model", "gpt-3.5-turbo"),
157
+ "choices": [
158
+ {
159
+ "index": 0,
160
+ "delta": {},
161
+ "finish_reason": finish_reason
162
+ }
163
+ ]
164
+ }
165
+ yield f"data: {json.dumps(end_chunk)}\n\n"
166
+ yield "data: [DONE]\n\n"
167
+ break
168
+
169
+ return Response(
170
+ stream_with_context(generate()),
171
+ content_type='text/event-stream'
172
+ )
173
+
174
+
175
+ def non_stream_response(original_request, headers, openai_request):
176
+ response = requests.post(
177
+ ORIGINAL_API_URL,
178
+ headers=headers,
179
+ json=original_request,
180
+ stream=True
181
+ )
182
+
183
+ # 用于存储推理和回复内容
184
+ reasoning_content = ""
185
+ reply_content = ""
186
+
187
+ message_id = None
188
+ finish_reason = "stop"
189
+
190
+ for line in response.iter_lines():
191
+ if not line:
192
+ continue
193
+
194
+ line_str = line.decode('utf-8')
195
+
196
+ # 解析不同类型的响应行
197
+ if line_str.startswith('f:'):
198
+ # 消息 ID
199
+ message_data = json.loads(line_str[2:])
200
+ message_id = message_data.get("messageId")
201
+
202
+ elif line_str.startswith('g:'):
203
+ # 推理部分
204
+ reasoning_part = line_str[2:].strip('"')
205
+ reasoning_content += reasoning_part
206
+
207
+ elif line_str.startswith('0:'):
208
+ # 回复部分
209
+ reply_part = line_str[2:].strip('"').replace("\\n", "\n")
210
+ reply_content += reply_part
211
+
212
+ elif line_str.startswith('e:') or line_str.startswith('d:'):
213
+ # 结束标记
214
+ finish_data = json.loads(line_str[2:])
215
+ finish_reason = finish_data.get("finishReason", "stop")
216
+
217
+ # 构建 OpenAI 兼容的响应
218
+ openai_response = {
219
+ "id": f"chatcmpl-{uuid.uuid4()}",
220
+ "object": "chat.completion",
221
+ "created": int(time.time()),
222
+ "model": openai_request.get("model", "gpt-3.5-turbo"),
223
+ "choices": [
224
+ {
225
+ "index": 0,
226
+ "message": {
227
+ "role": "assistant",
228
+ "content": reply_content
229
+ },
230
+ "finish_reason": finish_reason
231
+ }
232
+ ],
233
+ "usage": {
234
+ "prompt_tokens": 0, # 这里可以根据实际情况设置
235
+ "completion_tokens": 0,
236
+ "total_tokens": 0
237
+ }
238
+ }
239
+
240
+ return jsonify(openai_response)
241
+
242
+
243
+ import os
244
+
245
+ if __name__ == '__main__':
246
+ port = int(os.environ.get("PORT", 7860)) # 7860 default untuk Hugging Face
247
+ app.run(host='0.0.0.0', port=port)