smgc commited on
Commit
e3596d7
1 Parent(s): 6b28435

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -0
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import uuid
4
+ from flask import Flask, request, Response, jsonify, stream_with_context
5
+ from flask_limiter import Limiter
6
+ from flask_limiter.util import get_remote_address
7
+ import socketio
8
+ import requests
9
+ from requests.exceptions import RequestException
10
+
11
+ app = Flask(__name__)
12
+
13
+ # 从环境变量中获取配置
14
+ API_KEY = os.environ.get('PPLX_KEY')
15
+ PPLX_COOKIE = os.environ.get('PPLX_COOKIE')
16
+ USER_AGENT = os.environ.get('USER_AGENT')
17
+ PROXY = os.environ.get('PROXY')
18
+
19
+ # 设置限流
20
+ limiter = Limiter(
21
+ get_remote_address,
22
+ app=app,
23
+ default_limits=["100 per 5 minutes"],
24
+ storage_uri="memory://"
25
+ )
26
+
27
+ # Socket.IO 客户端设置
28
+ sio = socketio.Client()
29
+
30
+ # 代理设置
31
+ proxies = {'https': PROXY} if PROXY else None
32
+
33
+ @app.route('/')
34
+ def home():
35
+ return jsonify({
36
+ "message": "Welcome to the Perplexity AI Proxy API",
37
+ "endpoints": {
38
+ "/ai/v1/messages": {
39
+ "method": "POST",
40
+ "description": "Send a message to the AI",
41
+ "headers": {
42
+ "x-api-key": "Your API key (required)",
43
+ "Content-Type": "application/json"
44
+ },
45
+ "body": {
46
+ "messages": "Array of message objects",
47
+ "stream": "Boolean (true for streaming response)",
48
+ }
49
+ }
50
+ }
51
+ })
52
+
53
+ @app.route('/health')
54
+ def health_check():
55
+ return jsonify({"status": "OK"}), 200
56
+
57
+ def validate_api_key():
58
+ api_key = request.headers.get('x-api-key')
59
+ if api_key != API_KEY:
60
+ return jsonify({"error": "Invalid API key"}), 401
61
+
62
+ @app.route('/ai/v1/messages', methods=['POST'])
63
+ @limiter.limit("100 per 15 minutes")
64
+ def ai_messages():
65
+ auth_result = validate_api_key()
66
+ if auth_result:
67
+ return auth_result
68
+
69
+ try:
70
+ data = request.json
71
+ if not data.get('stream', False):
72
+ return jsonify({
73
+ "id": str(uuid.uuid4()),
74
+ "content": [
75
+ {"text": "Please turn on streaming."},
76
+ {"id": "string", "name": "string", "input": {}}
77
+ ],
78
+ "model": "string",
79
+ "stop_reason": "end_turn",
80
+ "stop_sequence": "string",
81
+ "usage": {"input_tokens": 0, "output_tokens": 0}
82
+ })
83
+
84
+ def generate():
85
+ previous_messages = "\n\n".join([msg['content'] for msg in data['messages']])
86
+ msg_id = str(uuid.uuid4())
87
+
88
+ yield create_event("message_start", {
89
+ "type": "message_start",
90
+ "message": {
91
+ "id": msg_id,
92
+ "type": "message",
93
+ "role": "assistant",
94
+ "content": [],
95
+ "model": "claude-3-opus-20240229",
96
+ "stop_reason": None,
97
+ "stop_sequence": None,
98
+ "usage": {"input_tokens": 8, "output_tokens": 1},
99
+ },
100
+ })
101
+ yield create_event("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}})
102
+ yield create_event("ping", {"type": "ping"})
103
+
104
+ try:
105
+ sio.connect('wss://www.perplexity.ai', transports=['websocket'],
106
+ headers={
107
+ 'Cookie': PPLX_COOKIE,
108
+ 'User-Agent': USER_AGENT
109
+ },
110
+ http_session=requests.Session() if PROXY else None,
111
+ proxies=proxies)
112
+ sio.emit('perplexity_ask', previous_messages, {
113
+ "version": "2.9",
114
+ "source": "default",
115
+ "attachments": [],
116
+ "language": "en-GB",
117
+ "timezone": "Europe/London",
118
+ "search_focus": "writing",
119
+ "frontend_uuid": str(uuid.uuid4()),
120
+ "mode": "concise",
121
+ "is_related_query": False,
122
+ "is_default_related_query": False,
123
+ "visitor_id": str(uuid.uuid4()),
124
+ "frontend_context_uuid": str(uuid.uuid4()),
125
+ "prompt_source": "user",
126
+ "query_source": "home"
127
+ })
128
+
129
+ @sio.on('query_progress')
130
+ def on_query_progress(data):
131
+ if data.get('text'):
132
+ text = json.loads(data['text'])
133
+ chunk = text['chunks'][-1] if text['chunks'] else None
134
+ if chunk:
135
+ yield create_event("content_block_delta", {
136
+ "type": "content_block_delta",
137
+ "index": 0,
138
+ "delta": {"type": "text_delta", "text": chunk},
139
+ })
140
+
141
+ sio.wait()
142
+
143
+ except Exception as e:
144
+ print(f"Socket error: {e}")
145
+ yield create_event("content_block_delta", {
146
+ "type": "content_block_delta",
147
+ "index": 0,
148
+ "delta": {"type": "text_delta", "text": "An error occurred while processing your request"},
149
+ })
150
+ finally:
151
+ sio.disconnect()
152
+
153
+ yield create_event("content_block_stop", {"type": "content_block_stop", "index": 0})
154
+ yield create_event("message_delta", {
155
+ "type": "message_delta",
156
+ "delta": {"stop_reason": "end_turn", "stop_sequence": None},
157
+ "usage": {"output_tokens": 12},
158
+ })
159
+ yield create_event("message_stop", {"type": "message_stop"})
160
+
161
+ return Response(stream_with_context(generate()), content_type='text/event-stream')
162
+
163
+ except Exception as e:
164
+ print(f"Request error: {e}")
165
+ return jsonify({"error": str(e)}), 400
166
+
167
+ def create_event(event, data):
168
+ if isinstance(data, dict):
169
+ data = json.dumps(data)
170
+ return f"event: {event}\ndata: {data}\n\n"
171
+
172
+ @app.errorhandler(404)
173
+ def not_found(error):
174
+ return jsonify({"error": "Not Found"}), 404
175
+
176
+ @app.errorhandler(500)
177
+ def server_error(error):
178
+ return jsonify({"error": "Internal Server Error"}), 500
179
+
180
+ if __name__ == '__main__':
181
+ port = int(os.environ.get('PORT', 8081))
182
+ app.run(host='0.0.0.0', port=port)