Niansuh commited on
Commit
5511af6
·
verified ·
1 Parent(s): 98ba9c7

Update api/utils.py

Browse files
Files changed (1) hide show
  1. api/utils.py +164 -142
api/utils.py CHANGED
@@ -1,7 +1,12 @@
 
 
1
  from datetime import datetime
2
  import json
3
- from typing import Any, Dict, Optional
4
  import uuid
 
 
 
 
5
 
6
  import httpx
7
  from fastapi import Depends, HTTPException
@@ -13,8 +18,9 @@ from api.config import (
13
  headers,
14
  AGENT_MODE,
15
  TRENDING_AGENT_MODE,
16
- APP_SECRET,
17
- BASE_URL,
 
18
  )
19
  from api.models import ChatRequest
20
 
@@ -26,12 +32,16 @@ logger = setup_logger(__name__)
26
  bearer_scheme = HTTPBearer()
27
 
28
 
 
 
 
 
 
 
29
  def create_chat_completion_data(
30
  content: str, model: str, timestamp: int, finish_reason: Optional[str] = None
31
  ) -> Dict[str, Any]:
32
- """
33
- Create a dictionary representing a chat completion chunk.
34
- """
35
  return {
36
  "id": f"chatcmpl-{uuid.uuid4()}",
37
  "object": "chat.completion.chunk",
@@ -49,9 +59,7 @@ def create_chat_completion_data(
49
 
50
 
51
  def verify_app_secret(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)):
52
- """
53
- Verify the APP_SECRET from the authorization credentials.
54
- """
55
  if credentials.credentials != APP_SECRET:
56
  logger.warning("Invalid APP_SECRET provided.")
57
  raise HTTPException(status_code=403, detail="Invalid APP_SECRET")
@@ -59,97 +67,111 @@ def verify_app_secret(credentials: HTTPAuthorizationCredentials = Depends(bearer
59
  return credentials.credentials
60
 
61
 
62
- def message_to_dict(message):
63
  """
64
  Convert a message object to a dictionary suitable for the API request.
65
- Handles different content types gracefully.
66
  """
67
- message_dict = {"role": message.role}
68
-
69
  if isinstance(message.content, str):
70
- message_dict["content"] = message.content
71
  elif isinstance(message.content, list):
72
- # Handle list content more robustly
73
- try:
74
- if len(message.content) >= 2:
75
- # Assuming the first element has 'text' and the second has 'image_url'
76
- text_content = message.content[0].get("text", "")
77
- image_url = message.content[1].get("image_url", {}).get("url", "")
78
- message_dict["content"] = text_content
79
- message_dict["data"] = {
80
- "imageBase64": image_url,
81
- "fileText": "",
82
- "title": "snapshot",
83
- }
84
- else:
85
- # Fallback if the list doesn't have expected structure
86
- message_dict["content"] = json.dumps(message.content)
87
- except (AttributeError, KeyError, TypeError) as e:
88
- logger.error(f"Error parsing message content: {e}")
89
- message_dict["content"] = "Invalid message format."
90
  else:
91
- # Fallback for unexpected content types
92
- message_dict["content"] = str(message.content)
93
 
94
- return message_dict
 
95
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- def get_agent_mode(model: str) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  """
99
- Retrieves the agent mode configuration.
100
- Falls back to an empty dictionary if not found.
101
  """
102
- agent_mode = AGENT_MODE.get(model, {})
103
  if not agent_mode:
104
  logger.warning(f"No AGENT_MODE configuration found for model: {model}")
105
- return agent_mode
106
 
107
 
108
- def get_trending_agent_mode(model: str) -> Dict[str, Any]:
109
  """
110
- Retrieves the trending agent mode configuration.
111
- Falls back to an empty dictionary if not found.
112
  """
113
- trending_agent_mode = TRENDING_AGENT_MODE.get(model, {})
114
  if not trending_agent_mode:
115
  logger.warning(f"No TRENDING_AGENT_MODE configuration found for model: {model}")
116
- return trending_agent_mode
117
 
118
 
119
  async def process_streaming_response(request: ChatRequest):
120
- """
121
- Process a streaming response for a chat completion request.
122
- """
123
- agent_mode = get_agent_mode(request.model)
124
- trending_agent_mode = get_trending_agent_mode(request.model)
 
 
 
125
 
126
- # Log reduced information
127
- logger.info(
128
- f"Streaming request for model: '{request.model}', "
129
- f"agent mode: {agent_mode}, trending agent mode: {trending_agent_mode}"
130
- )
 
131
 
132
  json_data = {
133
- "messages": [message_to_dict(msg) for msg in request.messages],
134
- "previewToken": None,
135
- "userId": None,
136
- "codeModelMode": True,
137
  "agentMode": agent_mode,
138
- "trendingAgentMode": trending_agent_mode,
139
- "isMicMode": False,
140
- "userSystemPrompt": None,
141
- "maxTokens": request.max_tokens,
142
- "playgroundTopP": request.top_p,
143
- "playgroundTemperature": request.temperature,
144
- "isChromeExt": False,
145
- "githubToken": None,
146
  "clickedAnswer2": False,
147
  "clickedAnswer3": False,
148
  "clickedForceWebSearch": False,
149
- "visitFromDelta": False,
 
 
 
 
 
 
150
  "mobileClient": False,
151
- "userSelectedModel": MODEL_MAPPING.get(request.model),
 
 
 
 
 
 
152
  "validated": validate.getHid(),
 
153
  }
154
 
155
  async with httpx.AsyncClient() as client:
@@ -157,9 +179,9 @@ async def process_streaming_response(request: ChatRequest):
157
  async with client.stream(
158
  "POST",
159
  f"{BASE_URL}/api/chat",
160
- headers=headers,
161
  json=json_data,
162
- timeout=httpx.Timeout(100.0),
163
  ) as response:
164
  response.raise_for_status()
165
  timestamp = int(datetime.now().timestamp())
@@ -173,96 +195,96 @@ async def process_streaming_response(request: ChatRequest):
173
  break
174
  if content.startswith("$@$v=undefined-rv1$@$"):
175
  content = content[21:]
176
- yield f"data: {json.dumps(create_chat_completion_data(content, request.model, timestamp))}\n\n"
 
177
 
178
  # Indicate the end of the stream
179
  yield f"data: {json.dumps(create_chat_completion_data('', request.model, timestamp, 'stop'))}\n\n"
180
  yield "data: [DONE]\n\n"
181
  except httpx.HTTPStatusError as e:
182
- logger.error(f"HTTP error occurred: {e.response.status_code} - {e.response.text}")
183
- raise HTTPException(status_code=e.response.status_code, detail="Error from upstream service.")
184
  except httpx.RequestError as e:
185
- logger.error(f"Request error occurred: {e}")
186
- raise HTTPException(status_code=500, detail="Internal server error.")
187
  except Exception as e:
188
- logger.error(f"Unexpected error: {e}")
189
- raise HTTPException(status_code=500, detail="Internal server error.")
190
 
191
 
192
  async def process_non_streaming_response(request: ChatRequest):
193
- """
194
- Process a non-streaming response for a chat completion request.
195
- """
196
- agent_mode = get_agent_mode(request.model)
197
- trending_agent_mode = get_trending_agent_mode(request.model)
198
 
199
- # Log reduced information
200
- logger.info(
201
- f"Non-streaming request for model: '{request.model}', "
202
- f"agent mode: {agent_mode}, trending agent mode: {trending_agent_mode}"
203
- )
 
 
 
 
 
204
 
205
  json_data = {
206
- "messages": [message_to_dict(msg) for msg in request.messages],
207
- "previewToken": None,
208
- "userId": None,
209
- "codeModelMode": True,
210
  "agentMode": agent_mode,
211
- "trendingAgentMode": trending_agent_mode,
212
- "isMicMode": False,
213
- "userSystemPrompt": None,
214
- "maxTokens": request.max_tokens,
215
- "playgroundTopP": request.top_p,
216
- "playgroundTemperature": request.temperature,
217
- "isChromeExt": False,
218
- "githubToken": None,
219
  "clickedAnswer2": False,
220
  "clickedAnswer3": False,
221
  "clickedForceWebSearch": False,
222
- "visitFromDelta": False,
 
 
 
 
 
 
223
  "mobileClient": False,
224
- "userSelectedModel": MODEL_MAPPING.get(request.model),
 
 
 
 
 
 
225
  "validated": validate.getHid(),
 
226
  }
227
 
228
- try:
229
- async with httpx.AsyncClient() as client:
230
- response = await client.post(
231
- f"{BASE_URL}/api/chat",
232
- headers=headers,
233
- json=json_data,
234
- timeout=httpx.Timeout(100.0),
235
- )
236
- response.raise_for_status()
237
- full_response = response.text
238
-
239
- # Process the full response
240
- if "https://www.blackbox.ai" in full_response:
241
- validate.getHid(True)
242
- full_response = "hid已刷新,重新对话即可"
243
- if full_response.startswith("$@$v=undefined-rv1$@$"):
244
- full_response = full_response[21:]
245
 
246
- return {
247
- "id": f"chatcmpl-{uuid.uuid4()}",
248
- "object": "chat.completion",
249
- "created": int(datetime.now().timestamp()),
250
- "model": request.model,
251
- "choices": [
252
- {
253
- "index": 0,
254
- "message": {"role": "assistant", "content": full_response},
255
- "finish_reason": "stop",
256
- }
257
- ],
258
- "usage": None,
259
- }
260
- except httpx.HTTPStatusError as e:
261
- logger.error(f"HTTP error occurred: {e.response.status_code} - {e.response.text}")
262
- raise HTTPException(status_code=e.response.status_code, detail="Error from upstream service.")
263
- except httpx.RequestError as e:
264
- logger.error(f"Request error occurred: {e}")
265
- raise HTTPException(status_code=500, detail="Internal server error.")
266
- except Exception as e:
267
- logger.error(f"Unexpected error: {e}")
268
- raise HTTPException(status_code=500, detail="Internal server error.")
 
1
+ # utils.py
2
+
3
  from datetime import datetime
4
  import json
 
5
  import uuid
6
+ import asyncio
7
+ import random
8
+ import string
9
+ from typing import Any, Dict, Optional
10
 
11
  import httpx
12
  from fastapi import Depends, HTTPException
 
18
  headers,
19
  AGENT_MODE,
20
  TRENDING_AGENT_MODE,
21
+ MODEL_PREFIXES,
22
+ MODEL_REFERERS,
23
+ BASE_URL
24
  )
25
  from api.models import ChatRequest
26
 
 
32
  bearer_scheme = HTTPBearer()
33
 
34
 
35
+ def generate_chat_id(length: int = 7) -> str:
36
+ """Generate a random alphanumeric chat ID."""
37
+ characters = string.ascii_letters + string.digits
38
+ return ''.join(random.choices(characters, k=length))
39
+
40
+
41
  def create_chat_completion_data(
42
  content: str, model: str, timestamp: int, finish_reason: Optional[str] = None
43
  ) -> Dict[str, Any]:
44
+ """Create a dictionary representing a chat completion chunk."""
 
 
45
  return {
46
  "id": f"chatcmpl-{uuid.uuid4()}",
47
  "object": "chat.completion.chunk",
 
59
 
60
 
61
  def verify_app_secret(credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)):
62
+ """Verify the APP_SECRET from the authorization credentials."""
 
 
63
  if credentials.credentials != APP_SECRET:
64
  logger.warning("Invalid APP_SECRET provided.")
65
  raise HTTPException(status_code=403, detail="Invalid APP_SECRET")
 
67
  return credentials.credentials
68
 
69
 
70
+ def message_to_dict(message, model_prefix: Optional[str] = None):
71
  """
72
  Convert a message object to a dictionary suitable for the API request.
73
+ Ensures base64 data and optional model prefix are handled.
74
  """
 
 
75
  if isinstance(message.content, str):
76
+ content = message.content
77
  elif isinstance(message.content, list):
78
+ content = message.content[0].get("text", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  else:
80
+ content = str(message.content)
 
81
 
82
+ if model_prefix:
83
+ content = f"{model_prefix} {content}"
84
 
85
+ if isinstance(message.content, list) and len(message.content) == 2 and "image_url" in message.content[1]:
86
+ return {
87
+ "role": message.role,
88
+ "content": content,
89
+ "data": {
90
+ "imageBase64": message.content[1]["image_url"]["url"],
91
+ "fileText": "",
92
+ "title": "snapshot",
93
+ },
94
+ }
95
 
96
+ return {"role": message.role, "content": content}
97
+
98
+
99
+ def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
100
+ """Remove the model prefix from the response content if present."""
101
+ if model_prefix and content.startswith(model_prefix):
102
+ logger.debug(f"Stripping prefix '{model_prefix}' from content.")
103
+ return content[len(model_prefix):].strip()
104
+ return content
105
+
106
+
107
+ def get_referer_url(chat_id: str, model: str) -> str:
108
+ """Generate the referer URL based on specific models listed in MODEL_REFERERS."""
109
+ if model in MODEL_REFERERS:
110
+ return f"{BASE_URL}/chat/{chat_id}?model={model}"
111
+ return BASE_URL
112
+
113
+
114
+ def get_agent_mode_config(model: str) -> Dict[str, Any]:
115
  """
116
+ Retrieve the agent mode configuration.
117
+ Logs a warning if the configuration is missing.
118
  """
119
+ agent_mode = AGENT_MODE.get(model)
120
  if not agent_mode:
121
  logger.warning(f"No AGENT_MODE configuration found for model: {model}")
122
+ return agent_mode or {}
123
 
124
 
125
+ def get_trending_agent_mode_config(model: str) -> Dict[str, Any]:
126
  """
127
+ Retrieve the trending agent mode configuration.
128
+ Logs a warning if the configuration is missing.
129
  """
130
+ trending_agent_mode = TRENDING_AGENT_MODE.get(model)
131
  if not trending_agent_mode:
132
  logger.warning(f"No TRENDING_AGENT_MODE configuration found for model: {model}")
133
+ return trending_agent_mode or {}
134
 
135
 
136
  async def process_streaming_response(request: ChatRequest):
137
+ """Process a streaming response for a chat completion request."""
138
+ chat_id = generate_chat_id()
139
+ referer_url = get_referer_url(chat_id, request.model)
140
+ logger.info(f"Generated Chat ID: {chat_id} - Model: {request.model} - URL: {referer_url}")
141
+
142
+ agent_mode = get_agent_mode_config(request.model)
143
+ trending_agent_mode = get_trending_agent_mode_config(request.model)
144
+ model_prefix = MODEL_PREFIXES.get(request.model, "")
145
 
146
+ headers_api_chat = headers # Assuming 'headers' from config.py is suitable
147
+
148
+ if request.model == 'o1-preview':
149
+ delay_seconds = random.randint(1, 60)
150
+ logger.info(f"Introducing a delay of {delay_seconds} seconds for model 'o1-preview' (Chat ID: {chat_id})")
151
+ await asyncio.sleep(delay_seconds)
152
 
153
  json_data = {
 
 
 
 
154
  "agentMode": agent_mode,
 
 
 
 
 
 
 
 
155
  "clickedAnswer2": False,
156
  "clickedAnswer3": False,
157
  "clickedForceWebSearch": False,
158
+ "codeModelMode": True,
159
+ "githubToken": None,
160
+ "id": chat_id,
161
+ "isChromeExt": False,
162
+ "isMicMode": False,
163
+ "maxTokens": request.max_tokens,
164
+ "messages": [message_to_dict(msg, model_prefix=model_prefix) for msg in request.messages],
165
  "mobileClient": False,
166
+ "playgroundTemperature": request.temperature,
167
+ "playgroundTopP": request.top_p,
168
+ "previewToken": None,
169
+ "trendingAgentMode": trending_agent_mode,
170
+ "userId": None,
171
+ "userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
172
+ "userSystemPrompt": None,
173
  "validated": validate.getHid(),
174
+ "visitFromDelta": False,
175
  }
176
 
177
  async with httpx.AsyncClient() as client:
 
179
  async with client.stream(
180
  "POST",
181
  f"{BASE_URL}/api/chat",
182
+ headers=headers_api_chat,
183
  json=json_data,
184
+ timeout=100.0,
185
  ) as response:
186
  response.raise_for_status()
187
  timestamp = int(datetime.now().timestamp())
 
195
  break
196
  if content.startswith("$@$v=undefined-rv1$@$"):
197
  content = content[21:]
198
+ cleaned_content = strip_model_prefix(content, model_prefix)
199
+ yield f"data: {json.dumps(create_chat_completion_data(cleaned_content, request.model, timestamp))}\n\n"
200
 
201
  # Indicate the end of the stream
202
  yield f"data: {json.dumps(create_chat_completion_data('', request.model, timestamp, 'stop'))}\n\n"
203
  yield "data: [DONE]\n\n"
204
  except httpx.HTTPStatusError as e:
205
+ logger.error(f"HTTP error occurred for Chat ID {chat_id}: {e}")
206
+ raise HTTPException(status_code=e.response.status_code, detail=str(e))
207
  except httpx.RequestError as e:
208
+ logger.error(f"Request error occurred for Chat ID {chat_id}: {e}")
209
+ raise HTTPException(status_code=500, detail=str(e))
210
  except Exception as e:
211
+ logger.error(f"Unexpected error for Chat ID {chat_id}: {e}")
212
+ raise HTTPException(status_code=500, detail=str(e))
213
 
214
 
215
  async def process_non_streaming_response(request: ChatRequest):
216
+ """Process a non-streaming response for a chat completion request."""
217
+ chat_id = generate_chat_id()
218
+ referer_url = get_referer_url(chat_id, request.model)
219
+ logger.info(f"Generated Chat ID: {chat_id} - Model: {request.model} - URL: {referer_url}")
 
220
 
221
+ agent_mode = get_agent_mode_config(request.model)
222
+ trending_agent_mode = get_trending_agent_mode_config(request.model)
223
+ model_prefix = MODEL_PREFIXES.get(request.model, "")
224
+
225
+ headers_api_chat = headers # Assuming 'headers' from config.py is suitable
226
+
227
+ if request.model == 'o1-preview':
228
+ delay_seconds = random.randint(20, 60)
229
+ logger.info(f"Introducing a delay of {delay_seconds} seconds for model 'o1-preview' (Chat ID: {chat_id})")
230
+ await asyncio.sleep(delay_seconds)
231
 
232
  json_data = {
 
 
 
 
233
  "agentMode": agent_mode,
 
 
 
 
 
 
 
 
234
  "clickedAnswer2": False,
235
  "clickedAnswer3": False,
236
  "clickedForceWebSearch": False,
237
+ "codeModelMode": True,
238
+ "githubToken": None,
239
+ "id": chat_id,
240
+ "isChromeExt": False,
241
+ "isMicMode": False,
242
+ "maxTokens": request.max_tokens,
243
+ "messages": [message_to_dict(msg, model_prefix=model_prefix) for msg in request.messages],
244
  "mobileClient": False,
245
+ "playgroundTemperature": request.temperature,
246
+ "playgroundTopP": request.top_p,
247
+ "previewToken": None,
248
+ "trendingAgentMode": trending_agent_mode,
249
+ "userId": None,
250
+ "userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
251
+ "userSystemPrompt": None,
252
  "validated": validate.getHid(),
253
+ "visitFromDelta": False,
254
  }
255
 
256
+ full_response = ""
257
+ async with httpx.AsyncClient() as client:
258
+ try:
259
+ async with client.stream(
260
+ method="POST", url=f"{BASE_URL}/api/chat", headers=headers_api_chat, json=json_data
261
+ ) as response:
262
+ response.raise_for_status()
263
+ async for chunk in response.aiter_text():
264
+ full_response += chunk
265
+ except httpx.HTTPStatusError as e:
266
+ logger.error(f"HTTP error occurred for Chat ID {chat_id}: {e}")
267
+ raise HTTPException(status_code=e.response.status_code, detail=str(e))
268
+ except httpx.RequestError as e:
269
+ logger.error(f"Request error occurred for Chat ID {chat_id}: {e}")
270
+ raise HTTPException(status_code=500, detail=str(e))
 
 
271
 
272
+ if full_response.startswith("$@$v=undefined-rv1$@$"):
273
+ full_response = full_response[21:]
274
+
275
+ cleaned_full_response = strip_model_prefix(full_response, model_prefix)
276
+
277
+ return {
278
+ "id": f"chatcmpl-{uuid.uuid4()}",
279
+ "object": "chat.completion",
280
+ "created": int(datetime.now().timestamp()),
281
+ "model": request.model,
282
+ "choices": [
283
+ {
284
+ "index": 0,
285
+ "message": {"role": "assistant", "content": cleaned_full_response},
286
+ "finish_reason": "stop",
287
+ }
288
+ ],
289
+ "usage": None,
290
+ }