partybobo commited on
Commit
ac44bf9
·
verified ·
1 Parent(s): 9af2de9

Upload 2 files

Browse files
Files changed (2) hide show
  1. API.py +210 -0
  2. requirements.txt +7 -0
API.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, Response, jsonify, stream_with_context
2
+ import requests
3
+ import json
4
+ import sseclient
5
+ import urllib3
6
+ import uuid
7
+ import time
8
+ from typing import List, Dict, Any
9
+
10
+ # 禁用 SSL 警告
11
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
12
+
13
+ app = Flask(__name__)
14
+
15
+ # 认证令牌
16
+ HIGHLIGHT_AUTH_TOKEN = "Bearer 此句替换为你的jwt"
17
+ HIGHLIGHT_API_URL = "https://chat-backend.highlightai.com/api/v1/chat"
18
+
19
+ # API 请求头
20
+ def get_highlight_headers():
21
+ return {
22
+ "accept": "*/*",
23
+ "accept-encoding": "gzip, deflate, br, zstd",
24
+ "accept-language": "zh-CN",
25
+ "authorization": HIGHLIGHT_AUTH_TOKEN,
26
+ "content-type": "application/json",
27
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Highlight/1.0.22 Chrome/126.0.6478.234 Electron/31.7.3 Safari/537.36",
28
+ "sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\"",
29
+ "sec-ch-ua-mobile": "?0",
30
+ "sec-ch-ua-platform": "\"Windows\"",
31
+ }
32
+
33
+ @app.route('/v1/models', methods=['GET'])
34
+ def list_models():
35
+ """返回可用模型列表"""
36
+ return jsonify({
37
+ "object": "list",
38
+ "data": [
39
+ {
40
+ "id": "claude-3-7-sonnet",
41
+ "object": "model",
42
+ "created": 1717027200,
43
+ "owned_by": "anthropic"
44
+ }
45
+ ]
46
+ })
47
+
48
+ @app.route('/v1/chat/completions', methods=['POST'])
49
+ def chat_completions():
50
+ """处理聊天完成请求"""
51
+ data = request.json
52
+
53
+ # 提取请求参数
54
+ messages = data.get('messages', [])
55
+ stream = data.get('stream', False)
56
+
57
+ # 将 OpenAI 格式的消息转换为单个提示
58
+ prompt = format_messages_to_prompt(messages)
59
+
60
+ # 准备 Highlight 请求
61
+ highlight_data = {
62
+ "prompt": prompt,
63
+ "attachedContext": [{"type": "aboutMe", "text": "[\"\\n\"]"}],
64
+ "additionalTools": [],
65
+ "backendPlugins": []
66
+ }
67
+
68
+ if stream:
69
+ return stream_response(highlight_data)
70
+ else:
71
+ return non_stream_response(highlight_data)
72
+
73
+ def format_messages_to_prompt(messages: List[Dict[str, Any]]) -> str:
74
+ """将 OpenAI 格式的消息列表转换为单个提示字符串,保留角色前缀"""
75
+ formatted_messages = []
76
+
77
+ for message in messages:
78
+ role = message.get('role', '')
79
+ content = message.get('content', '')
80
+
81
+ if role and content:
82
+ # 保留角色前缀
83
+ formatted_messages.append(f"{role}: {content}")
84
+
85
+ # 将所有消息连接起来
86
+ return "\n\n".join(formatted_messages)
87
+
88
+ def stream_response(highlight_data: Dict[str, Any]):
89
+ """处理流式响应"""
90
+ def generate():
91
+ try:
92
+ response = requests.post(
93
+ HIGHLIGHT_API_URL,
94
+ headers=get_highlight_headers(),
95
+ json=highlight_data,
96
+ stream=True,
97
+ verify=False
98
+ )
99
+
100
+ if response.status_code != 200:
101
+ yield f"data: {json.dumps({'error': {'message': f'Highlight API returned status code {response.status_code}', 'type': 'api_error'}})}\n\n"
102
+ return
103
+
104
+ client = sseclient.SSEClient(response)
105
+
106
+ # 为这个响应创建一个唯一ID
107
+ response_id = f"chatcmpl-{str(uuid.uuid4())}"
108
+ created = int(time.time())
109
+
110
+ # 发送初始 SSE 消息
111
+ yield f"data: {json.dumps({'id': response_id, 'object': 'chat.completion.chunk', 'created': created, 'model': 'claude-3-7-sonnet-20240620', 'choices': [{'index': 0, 'delta': {'role': 'assistant'}, 'finish_reason': None}]})}\n\n"
112
+
113
+ # 处理 SSE 事件
114
+ for event in client.events():
115
+ event_data = json.loads(event.data)
116
+
117
+ if event_data.get("type") == "text":
118
+ content = event_data.get("content", "")
119
+ if content:
120
+ chunk = {
121
+ 'id': response_id,
122
+ 'object': 'chat.completion.chunk',
123
+ 'created': created,
124
+ 'model': 'claude-3-7-sonnet',
125
+ 'choices': [
126
+ {
127
+ 'index': 0,
128
+ 'delta': {'content': content},
129
+ 'finish_reason': None
130
+ }
131
+ ]
132
+ }
133
+ yield f"data: {json.dumps(chunk)}\n\n"
134
+
135
+ # 发送完成信息
136
+ yield f"data: {json.dumps({'id': response_id, 'object': 'chat.completion.chunk', 'created': created, 'model': 'claude-3-7-sonnet-20240620', 'choices': [{'index': 0, 'delta': {}, 'finish_reason': 'stop'}]})}\n\n"
137
+ yield "data: [DONE]\n\n"
138
+
139
+ except Exception as e:
140
+ yield f"data: {json.dumps({'error': {'message': str(e), 'type': 'server_error'}})}\n\n"
141
+
142
+ return Response(stream_with_context(generate()), mimetype='text/event-stream')
143
+
144
+ def non_stream_response(highlight_data: Dict[str, Any]):
145
+ """处理非流式响应"""
146
+ try:
147
+ response = requests.post(
148
+ HIGHLIGHT_API_URL,
149
+ headers=get_highlight_headers(),
150
+ json=highlight_data,
151
+ stream=True,
152
+ verify=False
153
+ )
154
+
155
+ if response.status_code != 200:
156
+ return jsonify({
157
+ 'error': {
158
+ 'message': f'Highlight API returned status code {response.status_code}',
159
+ 'type': 'api_error'
160
+ }
161
+ }), response.status_code
162
+
163
+ client = sseclient.SSEClient(response)
164
+
165
+ # 收集完整响应
166
+ full_response = ""
167
+ for event in client.events():
168
+ event_data = json.loads(event.data)
169
+ if event_data.get("type") == "text":
170
+ full_response += event_data.get("content", "")
171
+
172
+ # 创建 OpenAI 格式的响应
173
+ response_id = f"chatcmpl-{str(uuid.uuid4())}"
174
+ created = int(time.time())
175
+
176
+ return jsonify({
177
+ 'id': response_id,
178
+ 'object': 'chat.completion',
179
+ 'created': created,
180
+ 'model': 'claude-3-7-sonnet',
181
+ 'choices': [
182
+ {
183
+ 'index': 0,
184
+ 'message': {
185
+ 'role': 'assistant',
186
+ 'content': full_response
187
+ },
188
+ 'finish_reason': 'stop'
189
+ }
190
+ ],
191
+ 'usage': {
192
+ 'prompt_tokens': -1,
193
+ 'completion_tokens': -1,
194
+ 'total_tokens': -1
195
+ }
196
+ })
197
+
198
+ except Exception as e:
199
+ return jsonify({
200
+ 'error': {
201
+ 'message': str(e),
202
+ 'type': 'server_error'
203
+ }
204
+ }), 500
205
+
206
+ if __name__ == '__main__':
207
+ app.run(host='0.0.0.0', port=8080, debug=False)
208
+
209
+
210
+
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ flask==2.3.3
2
+ requests==2.31.0
3
+ sseclient-py==1.7.2
4
+ urllib3==2.0.7
5
+
6
+
7
+