Update app.py
Browse files
app.py
CHANGED
@@ -1,37 +1,15 @@
|
|
1 |
import os
|
2 |
import json
|
3 |
import uuid
|
|
|
4 |
from datetime import datetime
|
5 |
from flask import Flask, request, Response, jsonify
|
6 |
import socketio
|
7 |
import requests
|
8 |
import logging
|
9 |
from threading import Event
|
10 |
-
import re
|
11 |
-
|
12 |
-
# 自定义日志格式化器
|
13 |
-
class CustomFormatter(logging.Formatter):
|
14 |
-
def format(self, record):
|
15 |
-
log_data = {
|
16 |
-
"timestamp": self.formatTime(record, self.datefmt),
|
17 |
-
"level": record.levelname,
|
18 |
-
"message": record.getMessage(),
|
19 |
-
}
|
20 |
-
if hasattr(record, 'event_type'):
|
21 |
-
log_data['event_type'] = record.event_type
|
22 |
-
if hasattr(record, 'data'):
|
23 |
-
log_data['data'] = record.data
|
24 |
-
return json.dumps(log_data, ensure_ascii=False, indent=2)
|
25 |
-
|
26 |
-
def setup_logging():
|
27 |
-
logger = logging.getLogger()
|
28 |
-
logger.setLevel(logging.INFO)
|
29 |
-
handler = logging.StreamHandler()
|
30 |
-
handler.setFormatter(CustomFormatter())
|
31 |
-
logger.addHandler(handler)
|
32 |
|
33 |
app = Flask(__name__)
|
34 |
-
logger = logging.getLogger(__name__)
|
35 |
|
36 |
# 从环境变量中获取API密钥
|
37 |
API_KEY = os.environ.get('PPLX_KEY')
|
@@ -54,7 +32,7 @@ sio = socketio.Client(http_session=transport, logger=False, engineio_logger=Fals
|
|
54 |
|
55 |
# 连接选项
|
56 |
connect_opts = {
|
57 |
-
'transports': ['websocket', 'polling'],
|
58 |
}
|
59 |
|
60 |
# 其他选项
|
@@ -68,10 +46,38 @@ sio_opts = {
|
|
68 |
}
|
69 |
}
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
def log_request(ip, route, status):
|
72 |
-
|
73 |
-
|
|
|
74 |
'data': {
|
|
|
75 |
'ip': ip,
|
76 |
'route': route,
|
77 |
'status': status
|
@@ -156,7 +162,7 @@ def messages():
|
|
156 |
def generate():
|
157 |
nonlocal total_output_tokens
|
158 |
|
159 |
-
|
160 |
"type": "message_start",
|
161 |
"message": {
|
162 |
"id": msg_id,
|
@@ -169,8 +175,25 @@ def messages():
|
|
169 |
"usage": {"input_tokens": input_tokens, "output_tokens": total_output_tokens},
|
170 |
},
|
171 |
})
|
172 |
-
|
173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
174 |
|
175 |
def on_query_progress(data):
|
176 |
nonlocal total_output_tokens, response_text
|
@@ -181,22 +204,23 @@ def messages():
|
|
181 |
response_text.append(chunk)
|
182 |
chunk_tokens = calculate_tokens(chunk)
|
183 |
total_output_tokens += chunk_tokens
|
184 |
-
|
185 |
-
|
|
|
|
|
|
|
|
|
|
|
186 |
'data': {
|
187 |
'chunk': chunk,
|
188 |
'tokens': chunk_tokens,
|
189 |
-
'total_tokens': total_output_tokens
|
|
|
190 |
}
|
191 |
})
|
|
|
192 |
|
193 |
if data.get('final', False):
|
194 |
-
logger.info("Final response received", extra={
|
195 |
-
'event_type': 'response_complete',
|
196 |
-
'data': {
|
197 |
-
'total_tokens': total_output_tokens
|
198 |
-
}
|
199 |
-
})
|
200 |
response_event.set()
|
201 |
|
202 |
def on_connect():
|
@@ -231,16 +255,9 @@ def messages():
|
|
231 |
|
232 |
while not response_event.is_set():
|
233 |
sio.sleep(0.1)
|
234 |
-
while response_text:
|
235 |
-
chunk = response_text.pop(0)
|
236 |
-
yield create_event("content_block_delta", {
|
237 |
-
"type": "content_block_delta",
|
238 |
-
"index": 0,
|
239 |
-
"delta": {"type": "text_delta", "text": chunk},
|
240 |
-
})
|
241 |
|
242 |
except Exception as e:
|
243 |
-
logger.error(f"Error during socket connection", extra={
|
244 |
'event_type': 'connection_error',
|
245 |
'data': {'error': str(e)}
|
246 |
})
|
@@ -253,18 +270,35 @@ def messages():
|
|
253 |
if sio.connected:
|
254 |
sio.disconnect()
|
255 |
|
256 |
-
|
257 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
"type": "message_delta",
|
259 |
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
|
260 |
"usage": {"output_tokens": total_output_tokens},
|
261 |
})
|
262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
|
264 |
return Response(generate(), content_type='text/event-stream')
|
265 |
|
266 |
except Exception as e:
|
267 |
-
logger.error(f"Request error", extra={
|
268 |
'event_type': 'request_error',
|
269 |
'data': {'error': str(e)}
|
270 |
})
|
@@ -328,11 +362,15 @@ def handle_non_stream(previous_messages, msg_id, model, input_tokens):
|
|
328 |
"output_tokens": total_output_tokens,
|
329 |
},
|
330 |
}
|
|
|
|
|
|
|
|
|
331 |
return Response(json.dumps(full_response, ensure_ascii=False), content_type='application/json')
|
332 |
|
333 |
except Exception as e:
|
334 |
-
logger.error(f"Error during
|
335 |
-
'event_type': '
|
336 |
'data': {'error': str(e)}
|
337 |
})
|
338 |
return jsonify({"error": str(e)}), 500
|
@@ -347,7 +385,7 @@ def not_found(error):
|
|
347 |
|
348 |
@app.errorhandler(500)
|
349 |
def server_error(error):
|
350 |
-
logger.error(f"Server error", extra={
|
351 |
'event_type': 'server_error',
|
352 |
'data': {'error': str(error)}
|
353 |
})
|
@@ -357,7 +395,7 @@ def server_error(error):
|
|
357 |
if __name__ == '__main__':
|
358 |
setup_logging()
|
359 |
port = int(os.environ.get('PORT', 8081))
|
360 |
-
logger.info(
|
361 |
'event_type': 'server_start',
|
362 |
'data': {'port': port}
|
363 |
})
|
|
|
1 |
import os
|
2 |
import json
|
3 |
import uuid
|
4 |
+
import re
|
5 |
from datetime import datetime
|
6 |
from flask import Flask, request, Response, jsonify
|
7 |
import socketio
|
8 |
import requests
|
9 |
import logging
|
10 |
from threading import Event
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
app = Flask(__name__)
|
|
|
13 |
|
14 |
# 从环境变量中获取API密钥
|
15 |
API_KEY = os.environ.get('PPLX_KEY')
|
|
|
32 |
|
33 |
# 连接选项
|
34 |
connect_opts = {
|
35 |
+
'transports': ['websocket', 'polling'],
|
36 |
}
|
37 |
|
38 |
# 其他选项
|
|
|
46 |
}
|
47 |
}
|
48 |
|
49 |
+
class CustomFormatter(logging.Formatter):
|
50 |
+
def format(self, record):
|
51 |
+
log_data = {
|
52 |
+
"timestamp": self.formatTime(record, self.datefmt),
|
53 |
+
"level": record.levelname,
|
54 |
+
"message": self.remove_ansi_escape(record.getMessage()),
|
55 |
+
}
|
56 |
+
if hasattr(record, 'event_type'):
|
57 |
+
log_data['event_type'] = record.event_type
|
58 |
+
if hasattr(record, 'data'):
|
59 |
+
log_data['data'] = record.data
|
60 |
+
return json.dumps(log_data, ensure_ascii=False, indent=2)
|
61 |
+
|
62 |
+
def remove_ansi_escape(self, text):
|
63 |
+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
64 |
+
return ansi_escape.sub('', text)
|
65 |
+
|
66 |
+
def setup_logging():
|
67 |
+
logger = logging.getLogger()
|
68 |
+
logger.setLevel(logging.INFO)
|
69 |
+
handler = logging.StreamHandler()
|
70 |
+
handler.setFormatter(CustomFormatter())
|
71 |
+
logger.addHandler(handler)
|
72 |
+
|
73 |
+
logger = logging.getLogger(__name__)
|
74 |
+
|
75 |
def log_request(ip, route, status):
|
76 |
+
timestamp = datetime.now().isoformat()
|
77 |
+
logger.info(f"Request received", extra={
|
78 |
+
'event_type': 'request',
|
79 |
'data': {
|
80 |
+
'timestamp': timestamp,
|
81 |
'ip': ip,
|
82 |
'route': route,
|
83 |
'status': status
|
|
|
162 |
def generate():
|
163 |
nonlocal total_output_tokens
|
164 |
|
165 |
+
start_event = create_event("message_start", {
|
166 |
"type": "message_start",
|
167 |
"message": {
|
168 |
"id": msg_id,
|
|
|
175 |
"usage": {"input_tokens": input_tokens, "output_tokens": total_output_tokens},
|
176 |
},
|
177 |
})
|
178 |
+
logger.info("Sending message_start event", extra={
|
179 |
+
'event_type': 'message_start',
|
180 |
+
'data': {'content': start_event}
|
181 |
+
})
|
182 |
+
yield start_event
|
183 |
+
|
184 |
+
block_start_event = create_event("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}})
|
185 |
+
logger.info("Sending content_block_start event", extra={
|
186 |
+
'event_type': 'content_block_start',
|
187 |
+
'data': {'content': block_start_event}
|
188 |
+
})
|
189 |
+
yield block_start_event
|
190 |
+
|
191 |
+
ping_event = create_event("ping", {"type": "ping"})
|
192 |
+
logger.info("Sending ping event", extra={
|
193 |
+
'event_type': 'ping',
|
194 |
+
'data': {'content': ping_event}
|
195 |
+
})
|
196 |
+
yield ping_event
|
197 |
|
198 |
def on_query_progress(data):
|
199 |
nonlocal total_output_tokens, response_text
|
|
|
204 |
response_text.append(chunk)
|
205 |
chunk_tokens = calculate_tokens(chunk)
|
206 |
total_output_tokens += chunk_tokens
|
207 |
+
delta_event = create_event("content_block_delta", {
|
208 |
+
"type": "content_block_delta",
|
209 |
+
"index": 0,
|
210 |
+
"delta": {"type": "text_delta", "text": chunk},
|
211 |
+
})
|
212 |
+
logger.info("Sending content_block_delta event", extra={
|
213 |
+
'event_type': 'content_block_delta',
|
214 |
'data': {
|
215 |
'chunk': chunk,
|
216 |
'tokens': chunk_tokens,
|
217 |
+
'total_tokens': total_output_tokens,
|
218 |
+
'content': delta_event
|
219 |
}
|
220 |
})
|
221 |
+
yield delta_event
|
222 |
|
223 |
if data.get('final', False):
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
response_event.set()
|
225 |
|
226 |
def on_connect():
|
|
|
255 |
|
256 |
while not response_event.is_set():
|
257 |
sio.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
except Exception as e:
|
260 |
+
logger.error(f"Error during socket connection: {str(e)}", extra={
|
261 |
'event_type': 'connection_error',
|
262 |
'data': {'error': str(e)}
|
263 |
})
|
|
|
270 |
if sio.connected:
|
271 |
sio.disconnect()
|
272 |
|
273 |
+
stop_event = create_event("content_block_stop", {"type": "content_block_stop", "index": 0})
|
274 |
+
logger.info("Sending content_block_stop event", extra={
|
275 |
+
'event_type': 'content_block_stop',
|
276 |
+
'data': {'content': stop_event}
|
277 |
+
})
|
278 |
+
yield stop_event
|
279 |
+
|
280 |
+
message_delta_event = create_event("message_delta", {
|
281 |
"type": "message_delta",
|
282 |
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
|
283 |
"usage": {"output_tokens": total_output_tokens},
|
284 |
})
|
285 |
+
logger.info("Sending message_delta event", extra={
|
286 |
+
'event_type': 'message_delta',
|
287 |
+
'data': {'content': message_delta_event}
|
288 |
+
})
|
289 |
+
yield message_delta_event
|
290 |
+
|
291 |
+
message_stop_event = create_event("message_stop", {"type": "message_stop"})
|
292 |
+
logger.info("Sending message_stop event", extra={
|
293 |
+
'event_type': 'message_stop',
|
294 |
+
'data': {'content': message_stop_event}
|
295 |
+
})
|
296 |
+
yield message_stop_event
|
297 |
|
298 |
return Response(generate(), content_type='text/event-stream')
|
299 |
|
300 |
except Exception as e:
|
301 |
+
logger.error(f"Request error: {str(e)}", extra={
|
302 |
'event_type': 'request_error',
|
303 |
'data': {'error': str(e)}
|
304 |
})
|
|
|
362 |
"output_tokens": total_output_tokens,
|
363 |
},
|
364 |
}
|
365 |
+
logger.info("Sending non-stream response", extra={
|
366 |
+
'event_type': 'non_stream_response',
|
367 |
+
'data': {'content': full_response}
|
368 |
+
})
|
369 |
return Response(json.dumps(full_response, ensure_ascii=False), content_type='application/json')
|
370 |
|
371 |
except Exception as e:
|
372 |
+
logger.error(f"Error during socket connection: {str(e)}", extra={
|
373 |
+
'event_type': 'connection_error_non_stream',
|
374 |
'data': {'error': str(e)}
|
375 |
})
|
376 |
return jsonify({"error": str(e)}), 500
|
|
|
385 |
|
386 |
@app.errorhandler(500)
|
387 |
def server_error(error):
|
388 |
+
logger.error(f"Server error: {str(error)}", extra={
|
389 |
'event_type': 'server_error',
|
390 |
'data': {'error': str(error)}
|
391 |
})
|
|
|
395 |
if __name__ == '__main__':
|
396 |
setup_logging()
|
397 |
port = int(os.environ.get('PORT', 8081))
|
398 |
+
logger.info("Perplexity proxy starting", extra={
|
399 |
'event_type': 'server_start',
|
400 |
'data': {'port': port}
|
401 |
})
|