yuoop commited on
Commit
e57df21
·
verified ·
1 Parent(s): 757e09b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -148
app.py CHANGED
@@ -1,9 +1,11 @@
1
- from flask import Flask, request, jsonify
 
2
  import requests
3
  import json
4
  import os
 
5
 
6
- app = Flask(__name__)
7
 
8
  # 环境变量配置
9
  STATUS_URL = os.environ.get("STATUS_URL", "https://duckduckgo.com/duckchat/v1/status")
@@ -14,153 +16,151 @@ USER_AGENT = os.environ.get("USER_AGENT", "Mozilla/5.0 (Macintosh; Intel Mac OS
14
  COOKIE = os.environ.get("COOKIE", "dcm=3; s=l; bf=1") # 从环境变量获取 Cookie
15
 
16
  DEFAULT_HEADERS = {
17
- "User-Agent": USER_AGENT,
18
- "Accept": "text/event-stream",
19
- "Accept-Language": "en-US,en;q=0.5",
20
- "Accept-Encoding": "gzip, deflate, br",
21
- "Referer": REFERER,
22
- "Content-Type": "application/json",
23
- "Origin": ORIGIN,
24
- "Connection": "keep-alive",
25
- "Cookie": COOKIE,
26
- "Sec-Fetch-Dest": "empty",
27
- "Sec-Fetch-Mode": "cors",
28
- "Sec-Fetch-Site": "same-origin",
29
- "Pragma": "no-cache",
30
- "TE": "trailers",
31
  }
32
 
33
  SUPPORTED_MODELS = ["o3-mini", "gpt-4o-mini", "claude-3-haiku-20240307", "meta-llama/Llama-3.3-70B-Instruct-Turbo"]
34
 
35
- def get_vqd():
36
- """获取 DuckDuckGo Chat 的 VQD 值。"""
37
- headers = {**DEFAULT_HEADERS, "x-vqd-accept": "1"}
38
- try:
39
- response = requests.get(STATUS_URL, headers=headers)
40
- response.raise_for_status() # 抛出 HTTPError,如果状态码不是 200
41
- vqd = response.headers.get("x-vqd-4")
42
- if not vqd:
43
- raise ValueError("x-vqd-4 header 在响应中未找到。")
44
- return vqd
45
- except requests.exceptions.RequestException as e:
46
- raise Exception(f"HTTP 请求失败: {e}")
47
- except ValueError as e:
48
- raise Exception(str(e))
49
-
50
- def duckduckgo_chat(model, messages):
51
- """与 DuckDuckGo Chat 进行交互。"""
52
- try:
53
- x_vqd_4 = get_vqd()
54
-
55
- chat_headers = {
56
- **DEFAULT_HEADERS,
57
- "x-vqd-4": x_vqd_4,
58
- }
59
-
60
- body = json.dumps({
61
- "model": model,
62
- "messages": messages,
63
- })
64
-
65
- response = requests.post(CHAT_URL, headers=chat_headers, data=body, stream=True)
66
- response.raise_for_status()
67
-
68
- full_message = ""
69
- for line in response.iter_lines():
70
- if line:
71
- decoded_line = line.decode('utf-8')
72
- if decoded_line.startswith("data: "):
73
- try:
74
- json_data = json.loads(decoded_line[5:])
75
- full_message += json_data.get("message", "")
76
- except json.JSONDecodeError as e:
77
- print(f"JSON 解析错误: {e}, 行: {decoded_line}")
78
- pass # 忽略解析错误
79
-
80
- return full_message
81
-
82
- except requests.exceptions.RequestException as e:
83
- raise Exception(f"HTTP 请求失败: {e}")
84
- except Exception as e:
85
- raise Exception(f"聊天过程中发生错误: {e}")
86
-
87
- @app.route('/v1/chat/completions', methods=['POST'])
88
- def chat_completions():
89
- try:
90
- body = request.get_json()
91
- if not body:
92
- return jsonify({"error": "无效的请求体"}), 400
93
-
94
- model = body.get("model", "o3-mini")
95
- if model not in SUPPORTED_MODELS:
96
- return jsonify({"error": f"模型 \"{model}\" 不支持。支持的模型有: {', '.join(SUPPORTED_MODELS)}."}), 400
97
-
98
- messages = body.get("messages")
99
- if not messages:
100
- return jsonify({"error": "未提供任何消息内容"}), 400
101
-
102
- # 处理 system 消息
103
- system_message = next((msg for msg in messages if msg.get("role") == "system"), None)
104
- system_prompt = f"你将扮演一个{system_message['content']}.\n" if system_message else ""
105
-
106
- # 提取历史消息并格式化
107
- history_messages = "\n".join(
108
- f"{msg['role']}: {msg['content']}"
109
- for msg in messages
110
- if msg.get("role") != "system" and msg != messages[-1]
111
- )
112
-
113
- # 提取最后一条用户消息
114
- last_user_message = messages[-1]
115
- current_question = last_user_message["content"] if last_user_message.get("role") == "user" else ""
116
-
117
- # 构建合并后的消息
118
- combined_message_content = f"{system_prompt}以下是历史对话记录:\n{history_messages}\n用户当前提问:{current_question}"
119
- combined_message = {"role": "user", "content": combined_message_content}
120
-
121
- # 发送单条消息
122
- response_text = duckduckgo_chat(model, [combined_message])
123
-
124
- # 构建 OpenAI 风格的响应
125
- openai_response = {
126
- "id": f"chatcmpl-{int(time.time() * 1000)}", # ��成唯一 ID
127
- "object": "chat.completion",
128
- "created": int(time.time()),
129
- "model": model,
130
- "choices": [
131
- {
132
- "message": {
133
- "role": "assistant",
134
- "content": response_text,
135
- },
136
- "finish_reason": "stop",
137
- "index": 0,
138
- },
139
- ],
140
- "usage": {
141
- "prompt_tokens": 0,
142
- "completion_tokens": 0,
143
- "total_tokens": 0
144
- },
145
- }
146
-
147
- return jsonify(openai_response)
148
-
149
- except Exception as e:
150
- print(f"API 错误: {e}")
151
- return jsonify({"error": str(e)}), 500
152
-
153
- @app.errorhandler(404)
154
- def not_found(error):
155
- return jsonify({"message": "未找到"}), 404
156
-
157
- @app.errorhandler(Exception)
158
- def handle_exception(e):
159
- print(f"全局错误: {e}")
160
- return jsonify({"error": "服务器内部错误"}), 500
161
-
162
-
163
- import time
164
-
165
- if __name__ == '__main__':
166
- app.run(debug=False, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
 
1
+ from fastapi import FastAPI, HTTPException, Request
2
+ from fastapi.responses import JSONResponse
3
  import requests
4
  import json
5
  import os
6
+ import time
7
 
8
+ app = FastAPI()
9
 
10
  # 环境变量配置
11
  STATUS_URL = os.environ.get("STATUS_URL", "https://duckduckgo.com/duckchat/v1/status")
 
16
  COOKIE = os.environ.get("COOKIE", "dcm=3; s=l; bf=1") # 从环境变量获取 Cookie
17
 
18
  DEFAULT_HEADERS = {
19
+ "User-Agent": USER_AGENT,
20
+ "Accept": "text/event-stream",
21
+ "Accept-Language": "en-US,en;q=0.5",
22
+ "Accept-Encoding": "gzip, deflate, br",
23
+ "Referer": REFERER,
24
+ "Content-Type": "application/json",
25
+ "Origin": ORIGIN,
26
+ "Connection": "keep-alive",
27
+ "Cookie": COOKIE,
28
+ "Sec-Fetch-Dest": "empty",
29
+ "Sec-Fetch-Mode": "cors",
30
+ "Sec-Fetch-Site": "same-origin",
31
+ "Pragma": "no-cache",
32
+ "TE": "trailers",
33
  }
34
 
35
  SUPPORTED_MODELS = ["o3-mini", "gpt-4o-mini", "claude-3-haiku-20240307", "meta-llama/Llama-3.3-70B-Instruct-Turbo"]
36
 
37
+ async def get_vqd():
38
+ """获取 DuckDuckGo Chat 的 VQD 值。"""
39
+ headers = {**DEFAULT_HEADERS, "x-vqd-accept": "1"}
40
+ try:
41
+ response = requests.get(STATUS_URL, headers=headers)
42
+ response.raise_for_status() # 抛出 HTTPError,如果状态码不是 200
43
+ vqd = response.headers.get("x-vqd-4")
44
+ if not vqd:
45
+ raise ValueError("x-vqd-4 header 在响应中未找到。")
46
+ return vqd
47
+ except requests.exceptions.RequestException as e:
48
+ raise HTTPException(status_code=500, detail=f"HTTP 请求失败: {e}")
49
+ except ValueError as e:
50
+ raise HTTPException(status_code=500, detail=str(e))
51
+
52
+ async def duckduckgo_chat(model, messages):
53
+ """与 DuckDuckGo Chat 进行交互。"""
54
+ try:
55
+ x_vqd_4 = await get_vqd()
56
+
57
+ chat_headers = {
58
+ **DEFAULT_HEADERS,
59
+ "x-vqd-4": x_vqd_4,
60
+ }
61
+
62
+ body = json.dumps({
63
+ "model": model,
64
+ "messages": messages,
65
+ })
66
+
67
+ response = requests.post(CHAT_URL, headers=chat_headers, data=body, stream=True)
68
+ response.raise_for_status()
69
+
70
+ full_message = ""
71
+ for line in response.iter_lines():
72
+ if line:
73
+ decoded_line = line.decode('utf-8')
74
+ if decoded_line.startswith("data: "):
75
+ try:
76
+ json_data = json.loads(decoded_line[5:])
77
+ full_message += json_data.get("message", "")
78
+ except json.JSONDecodeError as e:
79
+ print(f"JSON 解析错误: {e}, 行: {decoded_line}")
80
+ pass # 忽略解析错误
81
+
82
+ return full_message
83
+
84
+ except requests.exceptions.RequestException as e:
85
+ raise HTTPException(status_code=500, detail=f"HTTP 请求失败: {e}")
86
+ except Exception as e:
87
+ raise HTTPException(status_code=500, detail=f"聊天过程中发生错误: {e}")
88
+
89
+ @app.post("/v1/chat/completions")
90
+ async def chat_completions(request: Request):
91
+ try:
92
+ body = await request.json()
93
+ if not body:
94
+ raise HTTPException(status_code=400, detail="无效的请求体")
95
+
96
+ model = body.get("model", "o3-mini")
97
+ if model not in SUPPORTED_MODELS:
98
+ raise HTTPException(status_code=400, detail=f"模型 \"{model}\" 不支持。支持的模型有: {', '.join(SUPPORTED_MODELS)}.")
99
+
100
+ messages = body.get("messages")
101
+ if not messages:
102
+ raise HTTPException(status_code=400, detail="未提供任何消息内容")
103
+
104
+ # 处理 system 消息
105
+ system_message = next((msg for msg in messages if msg.get("role") == "system"), None)
106
+ system_prompt = f"你将扮演一个{system_message['content']}.\n" if system_message else ""
107
+
108
+ # 提取历史消息并格式化
109
+ history_messages = "\n".join(
110
+ f"{msg['role']}: {msg['content']}"
111
+ for msg in messages
112
+ if msg.get("role") != "system" and msg != messages[-1]
113
+ )
114
+
115
+ # 提取最后一条用户消息
116
+ last_user_message = messages[-1]
117
+ current_question = last_user_message["content"] if last_user_message.get("role") == "user" else ""
118
+
119
+ # 构建合并后的消息
120
+ combined_message_content = f"{system_prompt}以下是历史对话记录:\n{history_messages}\n用户当前提问:{current_question}"
121
+ combined_message = {"role": "user", "content": combined_message_content}
122
+
123
+ # 发送单条消息
124
+ response_text = await duckduckgo_chat(model, [combined_message])
125
+
126
+ # 构建 OpenAI 风格的响应
127
+ openai_response = {
128
+ "id": f"chatcmpl-{int(time.time() * 1000)}", # 生成唯一 ID
129
+ "object": "chat.completion",
130
+ "created": int(time.time()),
131
+ "model": model,
132
+ "choices": [
133
+ {
134
+ "message": {
135
+ "role": "assistant",
136
+ "content": response_text,
137
+ },
138
+ "finish_reason": "stop",
139
+ "index": 0,
140
+ },
141
+ ],
142
+ "usage": {
143
+ "prompt_tokens": 0,
144
+ "completion_tokens": 0,
145
+ "total_tokens": 0
146
+ },
147
+ }
148
+
149
+ return JSONResponse(content=openai_response)
150
+
151
+ except HTTPException as e:
152
+ raise e # 重新抛出 HTTPException,以便 FastAPI 处理
153
+ except Exception as e:
154
+ print(f"API 错误: {e}")
155
+ raise HTTPException(status_code=500, detail=f"服务器内部错误: {e}")
156
+
157
+ @app.exception_handler(HTTPException)
158
+ async def http_exception_handler(request: Request, exc: HTTPException):
159
+ return JSONResponse(
160
+ status_code=exc.status_code,
161
+ content={"detail": exc.detail},
162
+ )
163
+
164
+ @app.get("/")
165
+ async def greet_json():
166
+ return {"Hello": "World!"}