Update app.py
Browse files
app.py
CHANGED
@@ -1,17 +1,17 @@
|
|
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 |
app = Flask(__name__)
|
13 |
|
14 |
-
# 自定义日志格式化器
|
15 |
class CustomFormatter(logging.Formatter):
|
16 |
def format(self, record):
|
17 |
log_data = {
|
@@ -38,18 +38,11 @@ def setup_logging():
|
|
38 |
|
39 |
logger = logging.getLogger(__name__)
|
40 |
|
41 |
-
# 从环境变量中获取API密钥
|
42 |
API_KEY = os.environ.get('PPLX_KEY')
|
43 |
-
|
44 |
-
# 代理设置
|
45 |
proxy_url = os.environ.get('PROXY_URL')
|
46 |
|
47 |
-
# 设置代理
|
48 |
if proxy_url:
|
49 |
-
proxies = {
|
50 |
-
'http': proxy_url,
|
51 |
-
'https': proxy_url
|
52 |
-
}
|
53 |
transport = requests.Session()
|
54 |
transport.proxies.update(proxies)
|
55 |
else:
|
@@ -57,12 +50,8 @@ else:
|
|
57 |
|
58 |
sio = socketio.Client(http_session=transport, logger=False, engineio_logger=False)
|
59 |
|
60 |
-
|
61 |
-
connect_opts = {
|
62 |
-
'transports': ['websocket', 'polling'],
|
63 |
-
}
|
64 |
|
65 |
-
# 其他选项
|
66 |
sio_opts = {
|
67 |
'extraHeaders': {
|
68 |
'Cookie': os.environ.get('PPLX_COOKIE'),
|
@@ -106,8 +95,7 @@ def calculate_tokens(text):
|
|
106 |
if re.search(r'[^\x00-\x7F]', text):
|
107 |
return len(text)
|
108 |
else:
|
109 |
-
|
110 |
-
return len(tokens)
|
111 |
|
112 |
def create_event(event, data):
|
113 |
if isinstance(data, dict):
|
@@ -129,8 +117,8 @@ def root():
|
|
129 |
},
|
130 |
"body": {
|
131 |
"messages": "Array of message objects",
|
132 |
-
"stream": "Boolean (
|
133 |
-
"model": "Model to be used (optional, defaults to claude-3-
|
134 |
}
|
135 |
}
|
136 |
}
|
@@ -152,7 +140,6 @@ def messages():
|
|
152 |
|
153 |
msg_id = str(uuid.uuid4())
|
154 |
response_event = Event()
|
155 |
-
timeout_event = Event()
|
156 |
response_text = []
|
157 |
total_output_tokens = 0
|
158 |
|
@@ -163,6 +150,9 @@ def messages():
|
|
163 |
|
164 |
def generate():
|
165 |
nonlocal total_output_tokens
|
|
|
|
|
|
|
166 |
|
167 |
def send_event(event_type, data):
|
168 |
event = create_event(event_type, data)
|
@@ -172,7 +162,6 @@ def messages():
|
|
172 |
})
|
173 |
yield event
|
174 |
|
175 |
-
# Send initial events
|
176 |
yield from send_event("message_start", {
|
177 |
"type": "message_start",
|
178 |
"message": {
|
@@ -190,7 +179,8 @@ def messages():
|
|
190 |
yield from send_event("ping", {"type": "ping"})
|
191 |
|
192 |
def on_query_progress(data):
|
193 |
-
nonlocal total_output_tokens, response_text
|
|
|
194 |
if 'text' in data:
|
195 |
text = json.loads(data['text'])
|
196 |
chunk = text['chunks'][-1] if text['chunks'] else None
|
@@ -243,18 +233,13 @@ def messages():
|
|
243 |
sio.on('connect', on_connect)
|
244 |
sio.on('query_progress', on_query_progress)
|
245 |
|
246 |
-
def timeout_handler():
|
247 |
-
logger.warning("Request timed out", extra={'event_type': 'request_timeout'})
|
248 |
-
timeout_event.set()
|
249 |
-
response_event.set()
|
250 |
-
|
251 |
-
timer = Timer(30, timeout_handler) # 30 seconds timeout
|
252 |
-
timer.start()
|
253 |
-
|
254 |
try:
|
255 |
sio.connect('wss://www.perplexity.ai/', **connect_opts, headers=sio_opts['extraHeaders'])
|
256 |
|
257 |
-
while not response_event.is_set() and
|
|
|
|
|
|
|
258 |
sio.sleep(0.1)
|
259 |
while response_text:
|
260 |
chunk = response_text.pop(0)
|
@@ -264,13 +249,22 @@ def messages():
|
|
264 |
"delta": {"type": "text_delta", "text": chunk},
|
265 |
})
|
266 |
|
267 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
yield from send_event("content_block_delta", {
|
269 |
"type": "content_block_delta",
|
270 |
"index": 0,
|
271 |
-
"delta": {"type": "text_delta", "text": "Request timed out"},
|
272 |
})
|
273 |
-
|
274 |
except Exception as e:
|
275 |
logger.error(f"Error during socket connection: {str(e)}", exc_info=True)
|
276 |
yield from send_event("content_block_delta", {
|
@@ -279,11 +273,9 @@ def messages():
|
|
279 |
"delta": {"type": "text_delta", "text": f"Error during socket connection: {str(e)}"},
|
280 |
})
|
281 |
finally:
|
282 |
-
timer.cancel()
|
283 |
if sio.connected:
|
284 |
sio.disconnect()
|
285 |
|
286 |
-
# Send final events
|
287 |
yield from send_event("content_block_stop", {"type": "content_block_stop", "index": 0})
|
288 |
yield from send_event("message_delta", {
|
289 |
"type": "message_delta",
|
@@ -304,6 +296,8 @@ def handle_non_stream(previous_messages, msg_id, model, input_tokens):
|
|
304 |
response_event = Event()
|
305 |
response_text = []
|
306 |
total_output_tokens = 0
|
|
|
|
|
307 |
|
308 |
def on_query_progress(data):
|
309 |
nonlocal total_output_tokens, response_text
|
@@ -314,6 +308,14 @@ def handle_non_stream(previous_messages, msg_id, model, input_tokens):
|
|
314 |
response_text.append(chunk)
|
315 |
chunk_tokens = calculate_tokens(chunk)
|
316 |
total_output_tokens += chunk_tokens
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
if data.get('final', False):
|
319 |
response_event.set()
|
@@ -342,11 +344,18 @@ def handle_non_stream(previous_messages, msg_id, model, input_tokens):
|
|
342 |
sio.connect('wss://www.perplexity.ai/', **connect_opts, headers=sio_opts['extraHeaders'])
|
343 |
|
344 |
# Wait for response with timeout
|
345 |
-
response_event.wait(timeout=
|
346 |
|
347 |
if not response_text:
|
348 |
-
logger.warning("No response received (non-stream)", extra={
|
349 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
|
351 |
full_response = {
|
352 |
"content": [{"text": ''.join(response_text), "type": "text"}],
|
@@ -363,11 +372,14 @@ def handle_non_stream(previous_messages, msg_id, model, input_tokens):
|
|
363 |
}
|
364 |
logger.info("Sending non-stream response", extra={
|
365 |
'event_type': 'non_stream_response',
|
366 |
-
'data': {
|
|
|
|
|
|
|
367 |
})
|
368 |
return Response(json.dumps(full_response, ensure_ascii=False), content_type='application/json')
|
369 |
|
370 |
-
|
371 |
logger.error(f"Error during non-stream socket connection: {str(e)}", exc_info=True)
|
372 |
return jsonify({"error": str(e)}), 500
|
373 |
finally:
|
@@ -394,4 +406,5 @@ if __name__ == '__main__':
|
|
394 |
})
|
395 |
if not API_KEY:
|
396 |
logger.warning("PPLX_KEY environment variable is not set", extra={'event_type': 'config_warning'})
|
397 |
-
app.run(host='0.0.0.0', port=port)
|
|
|
|
1 |
import os
|
2 |
import json
|
3 |
import uuid
|
4 |
+
import time
|
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 |
import re
|
12 |
|
13 |
app = Flask(__name__)
|
14 |
|
|
|
15 |
class CustomFormatter(logging.Formatter):
|
16 |
def format(self, record):
|
17 |
log_data = {
|
|
|
38 |
|
39 |
logger = logging.getLogger(__name__)
|
40 |
|
|
|
41 |
API_KEY = os.environ.get('PPLX_KEY')
|
|
|
|
|
42 |
proxy_url = os.environ.get('PROXY_URL')
|
43 |
|
|
|
44 |
if proxy_url:
|
45 |
+
proxies = {'http': proxy_url, 'https': proxy_url}
|
|
|
|
|
|
|
46 |
transport = requests.Session()
|
47 |
transport.proxies.update(proxies)
|
48 |
else:
|
|
|
50 |
|
51 |
sio = socketio.Client(http_session=transport, logger=False, engineio_logger=False)
|
52 |
|
53 |
+
connect_opts = {'transports': ['websocket', 'polling']}
|
|
|
|
|
|
|
54 |
|
|
|
55 |
sio_opts = {
|
56 |
'extraHeaders': {
|
57 |
'Cookie': os.environ.get('PPLX_COOKIE'),
|
|
|
95 |
if re.search(r'[^\x00-\x7F]', text):
|
96 |
return len(text)
|
97 |
else:
|
98 |
+
return len(text.split())
|
|
|
99 |
|
100 |
def create_event(event, data):
|
101 |
if isinstance(data, dict):
|
|
|
117 |
},
|
118 |
"body": {
|
119 |
"messages": "Array of message objects",
|
120 |
+
"stream": "Boolean (optional, defaults to false)",
|
121 |
+
"model": "Model to be used (optional, defaults to claude-3-5-sonnet-20240620)"
|
122 |
}
|
123 |
}
|
124 |
}
|
|
|
140 |
|
141 |
msg_id = str(uuid.uuid4())
|
142 |
response_event = Event()
|
|
|
143 |
response_text = []
|
144 |
total_output_tokens = 0
|
145 |
|
|
|
150 |
|
151 |
def generate():
|
152 |
nonlocal total_output_tokens
|
153 |
+
start_time = time.time()
|
154 |
+
last_activity_time = start_time
|
155 |
+
timeout = max(60, input_tokens / 1000) # 动态设置超时时间,最少60秒
|
156 |
|
157 |
def send_event(event_type, data):
|
158 |
event = create_event(event_type, data)
|
|
|
162 |
})
|
163 |
yield event
|
164 |
|
|
|
165 |
yield from send_event("message_start", {
|
166 |
"type": "message_start",
|
167 |
"message": {
|
|
|
179 |
yield from send_event("ping", {"type": "ping"})
|
180 |
|
181 |
def on_query_progress(data):
|
182 |
+
nonlocal total_output_tokens, response_text, last_activity_time
|
183 |
+
last_activity_time = time.time()
|
184 |
if 'text' in data:
|
185 |
text = json.loads(data['text'])
|
186 |
chunk = text['chunks'][-1] if text['chunks'] else None
|
|
|
233 |
sio.on('connect', on_connect)
|
234 |
sio.on('query_progress', on_query_progress)
|
235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
try:
|
237 |
sio.connect('wss://www.perplexity.ai/', **connect_opts, headers=sio_opts['extraHeaders'])
|
238 |
|
239 |
+
while not response_event.is_set() and (time.time() - start_time) < timeout:
|
240 |
+
current_time = time.time()
|
241 |
+
if current_time - last_activity_time > 10: # 如果10秒内没有活动,记录警告
|
242 |
+
logger.warning("No activity for 10 seconds", extra={'event_type': 'inactivity_warning'})
|
243 |
sio.sleep(0.1)
|
244 |
while response_text:
|
245 |
chunk = response_text.pop(0)
|
|
|
249 |
"delta": {"type": "text_delta", "text": chunk},
|
250 |
})
|
251 |
|
252 |
+
if not response_event.is_set():
|
253 |
+
logger.warning(f"Request timed out after {timeout} seconds", extra={
|
254 |
+
'event_type': 'request_timeout',
|
255 |
+
'data': {
|
256 |
+
'timeout': timeout,
|
257 |
+
'input_tokens': input_tokens,
|
258 |
+
'output_tokens': total_output_tokens,
|
259 |
+
'elapsed_time': time.time() - start_time
|
260 |
+
}
|
261 |
+
})
|
262 |
yield from send_event("content_block_delta", {
|
263 |
"type": "content_block_delta",
|
264 |
"index": 0,
|
265 |
+
"delta": {"type": "text_delta", "text": f"Request timed out after {timeout} seconds"},
|
266 |
})
|
267 |
+
|
268 |
except Exception as e:
|
269 |
logger.error(f"Error during socket connection: {str(e)}", exc_info=True)
|
270 |
yield from send_event("content_block_delta", {
|
|
|
273 |
"delta": {"type": "text_delta", "text": f"Error during socket connection: {str(e)}"},
|
274 |
})
|
275 |
finally:
|
|
|
276 |
if sio.connected:
|
277 |
sio.disconnect()
|
278 |
|
|
|
279 |
yield from send_event("content_block_stop", {"type": "content_block_stop", "index": 0})
|
280 |
yield from send_event("message_delta", {
|
281 |
"type": "message_delta",
|
|
|
296 |
response_event = Event()
|
297 |
response_text = []
|
298 |
total_output_tokens = 0
|
299 |
+
start_time = time.time()
|
300 |
+
timeout = max(60, input_tokens / 1000) # 动态设置超时时间,最少60秒
|
301 |
|
302 |
def on_query_progress(data):
|
303 |
nonlocal total_output_tokens, response_text
|
|
|
308 |
response_text.append(chunk)
|
309 |
chunk_tokens = calculate_tokens(chunk)
|
310 |
total_output_tokens += chunk_tokens
|
311 |
+
logger.info("Received chunk (non-stream)", extra={
|
312 |
+
'event_type': 'chunk_received_non_stream',
|
313 |
+
'data': {
|
314 |
+
'chunk': chunk,
|
315 |
+
'tokens': chunk_tokens,
|
316 |
+
'total_tokens': total_output_tokens
|
317 |
+
}
|
318 |
+
})
|
319 |
|
320 |
if data.get('final', False):
|
321 |
response_event.set()
|
|
|
344 |
sio.connect('wss://www.perplexity.ai/', **connect_opts, headers=sio_opts['extraHeaders'])
|
345 |
|
346 |
# Wait for response with timeout
|
347 |
+
response_event.wait(timeout=timeout)
|
348 |
|
349 |
if not response_text:
|
350 |
+
logger.warning(f"No response received (non-stream) after {timeout} seconds", extra={
|
351 |
+
'event_type': 'no_response_non_stream',
|
352 |
+
'data': {
|
353 |
+
'timeout': timeout,
|
354 |
+
'input_tokens': input_tokens,
|
355 |
+
'elapsed_time': time.time() - start_time
|
356 |
+
}
|
357 |
+
})
|
358 |
+
return jsonify({"error": f"No response received after {timeout} seconds"}), 504
|
359 |
|
360 |
full_response = {
|
361 |
"content": [{"text": ''.join(response_text), "type": "text"}],
|
|
|
372 |
}
|
373 |
logger.info("Sending non-stream response", extra={
|
374 |
'event_type': 'non_stream_response',
|
375 |
+
'data': {
|
376 |
+
'content': full_response,
|
377 |
+
'elapsed_time': time.time() - start_time
|
378 |
+
}
|
379 |
})
|
380 |
return Response(json.dumps(full_response, ensure_ascii=False), content_type='application/json')
|
381 |
|
382 |
+
except Exception as e:
|
383 |
logger.error(f"Error during non-stream socket connection: {str(e)}", exc_info=True)
|
384 |
return jsonify({"error": str(e)}), 500
|
385 |
finally:
|
|
|
406 |
})
|
407 |
if not API_KEY:
|
408 |
logger.warning("PPLX_KEY environment variable is not set", extra={'event_type': 'config_warning'})
|
409 |
+
app.run(host='0.0.0.0', port=port)
|
410 |
+
|