Update api/utils.py
Browse files- api/utils.py +116 -115
api/utils.py
CHANGED
@@ -37,8 +37,6 @@ R2_ACCESS_KEY_ID = "df9c9eb87e850a8eb27afd3968077b42"
|
|
37 |
R2_SECRET_ACCESS_KEY = "14b08b0855263bb63d2618da3a6537e1b0446d89d51da03a568620b1e5342ea8"
|
38 |
R2_ENDPOINT_URL = "https://f2f92ac53fae792c4155f6e93a514989.r2.cloudflarestorage.com"
|
39 |
R2_BUCKET_NAME = "snapzion"
|
40 |
-
|
41 |
-
# We always store replaced URLs in one file named snapzion.txt
|
42 |
R2_REPLACED_URLS_KEY = "snapzion.txt"
|
43 |
|
44 |
s3 = boto3.client(
|
@@ -48,7 +46,6 @@ s3 = boto3.client(
|
|
48 |
aws_secret_access_key=R2_SECRET_ACCESS_KEY,
|
49 |
)
|
50 |
|
51 |
-
# Example blocked message
|
52 |
BLOCKED_MESSAGE = (
|
53 |
"Generated by BLACKBOX.AI, try unlimited chat https://www.blackbox.ai "
|
54 |
"and for API requests replace https://www.blackbox.ai with https://api.blackbox.ai"
|
@@ -58,15 +55,12 @@ BLOCKED_MESSAGE = (
|
|
58 |
# RANDOM USER-DATA & SESSION GENERATION
|
59 |
# ---------------------------------------------
|
60 |
def get_random_name_email_customer():
|
61 |
-
"""
|
62 |
-
Generate a random name, email, and customer ID.
|
63 |
-
"""
|
64 |
first_names = ["Aliace", "B21ob", "Car232ol", "Daavid", "Evewwlyn", "Fraank", "Grssace", "Hefctor", "Ivgy", "Jackdie"]
|
65 |
last_names = ["Smilth", "Johnkson", "Dajvis", "Mihller", "Thomgpson", "Garwcia", "Broawn", "Wilfson", "Maartin", "Clarak"]
|
66 |
|
67 |
random_name = f"{random.choice(first_names)} {random.choice(last_names)}"
|
68 |
email_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
|
69 |
-
random_email = f"{email_username}@
|
70 |
suffix_length = len("Rldf7IKdNhdhiw")
|
71 |
suffix_chars = string.ascii_letters + string.digits
|
72 |
random_suffix = ''.join(random.choice(suffix_chars) for _ in range(suffix_length))
|
@@ -75,9 +69,6 @@ def get_random_name_email_customer():
|
|
75 |
return random_name, random_email, random_customer_id
|
76 |
|
77 |
def generate_session(email: str, id_length: int = 21, days_ahead: int = 365) -> dict:
|
78 |
-
"""
|
79 |
-
Mirror the normal provider logic to generate session IDs and expiry.
|
80 |
-
"""
|
81 |
numeric_id = ''.join(random.choice('0123456789') for _ in range(id_length))
|
82 |
future_date = datetime.now(timezone.utc) + timedelta(days=days_ahead)
|
83 |
expiry = future_date.isoformat(timespec='milliseconds').replace('+00:00', 'Z')
|
@@ -88,7 +79,7 @@ def generate_session(email: str, id_length: int = 21, days_ahead: int = 365) ->
|
|
88 |
|
89 |
return {
|
90 |
"user": {
|
91 |
-
"name": "
|
92 |
"email": email,
|
93 |
"image": image_url,
|
94 |
"id": numeric_id
|
@@ -98,9 +89,6 @@ def generate_session(email: str, id_length: int = 21, days_ahead: int = 365) ->
|
|
98 |
}
|
99 |
|
100 |
def generate_session_data() -> dict:
|
101 |
-
"""
|
102 |
-
Generate a complete session data object with random email.
|
103 |
-
"""
|
104 |
_, email, _ = get_random_name_email_customer()
|
105 |
session_data = generate_session(email)
|
106 |
logger.info(f"Using generated session with email {email}")
|
@@ -110,8 +98,8 @@ def generate_session_data() -> dict:
|
|
110 |
# HELPER FUNCTIONS
|
111 |
# ---------------------------------------------
|
112 |
def generate_system_fingerprint() -> str:
|
113 |
-
|
114 |
-
short_hash = hashlib.md5(
|
115 |
return f"fp_{short_hash}"
|
116 |
|
117 |
def get_last_user_prompt(messages: List[Any]) -> str:
|
@@ -127,37 +115,36 @@ def get_last_user_prompt(messages: List[Any]) -> str:
|
|
127 |
|
128 |
def upload_replaced_urls_to_r2(urls: List[str], alt_text: str = "") -> None:
|
129 |
if not urls:
|
130 |
-
logger.info("No replaced or final
|
131 |
return
|
132 |
-
|
133 |
try:
|
134 |
-
|
135 |
-
|
136 |
-
logger.info("Successfully read existing snapzion.txt from R2.")
|
137 |
except s3.exceptions.NoSuchKey:
|
138 |
-
|
139 |
except Exception as e:
|
140 |
-
logger.error(f"Error reading
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
144 |
try:
|
145 |
s3.put_object(
|
146 |
Bucket=R2_BUCKET_NAME,
|
147 |
Key=R2_REPLACED_URLS_KEY,
|
148 |
-
Body=
|
149 |
ContentType="text/plain",
|
150 |
)
|
151 |
-
logger.info(f"Appended {len(urls)}
|
152 |
except Exception as e:
|
153 |
-
logger.error(f"
|
154 |
|
155 |
def calculate_tokens(text: str, model: str) -> int:
|
156 |
try:
|
157 |
-
|
158 |
-
return len(
|
159 |
-
except
|
160 |
-
logger.warning(f"Model '{model}' not supported by tiktoken for token counting. Using a generic method.")
|
161 |
return len(text.split())
|
162 |
|
163 |
def create_chat_completion_data(
|
@@ -188,13 +175,47 @@ def create_chat_completion_data(
|
|
188 |
}
|
189 |
|
190 |
def message_to_dict(message, model_prefix: Optional[str] = None):
|
191 |
-
|
192 |
-
|
193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
|
195 |
def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
196 |
if model_prefix and content.startswith(model_prefix):
|
197 |
-
logger.debug(f"Stripping prefix '{model_prefix}' from content.")
|
198 |
return content[len(model_prefix):].strip()
|
199 |
return content
|
200 |
|
@@ -202,27 +223,23 @@ def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
|
202 |
# STREAMING RESPONSE HANDLER
|
203 |
# ---------------------------------------------
|
204 |
async def process_streaming_response(request: ChatRequest):
|
205 |
-
|
206 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
207 |
-
logger.info(f"Processing
|
208 |
|
209 |
agent_mode = AGENT_MODE.get(request.model, {})
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
headers_api_chat = get_headers_api_chat(BASE_URL)
|
214 |
|
|
|
215 |
if request.model == "o1-preview":
|
216 |
-
|
217 |
-
logger.info(f"Delay {delay_seconds}s for model 'o1-preview' (Request: {request_id})")
|
218 |
-
await asyncio.sleep(delay_seconds)
|
219 |
|
220 |
-
|
221 |
-
if not
|
222 |
-
logger.error("No h-value for validation.")
|
223 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
224 |
|
225 |
-
|
226 |
|
227 |
json_data = {
|
228 |
"agentMode": agent_mode,
|
@@ -242,16 +259,16 @@ async def process_streaming_response(request: ChatRequest):
|
|
242 |
"isPremium": True,
|
243 |
"isMemoryEnabled": False,
|
244 |
"maxTokens": request.max_tokens,
|
245 |
-
"messages":
|
246 |
"mobileClient": False,
|
247 |
"playgroundTemperature": request.temperature,
|
248 |
"playgroundTopP": request.top_p,
|
249 |
"previewToken": None,
|
250 |
-
"trendingAgentMode":
|
251 |
"userId": None,
|
252 |
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
|
253 |
"userSystemPrompt": None,
|
254 |
-
"validated":
|
255 |
"visitFromDelta": False,
|
256 |
"webSearchModePrompt": False,
|
257 |
"vscodeClient": False,
|
@@ -264,19 +281,19 @@ async def process_streaming_response(request: ChatRequest):
|
|
264 |
}
|
265 |
|
266 |
prompt_tokens = sum(
|
267 |
-
calculate_tokens(
|
268 |
-
sum(calculate_tokens(img["contents"], request.model) for img in
|
269 |
-
for
|
270 |
)
|
271 |
|
272 |
completion_tokens = 0
|
273 |
-
|
274 |
|
275 |
async with httpx.AsyncClient() as client:
|
276 |
try:
|
277 |
-
async with client.stream("POST", f"{BASE_URL}/api/chat", headers=
|
278 |
-
|
279 |
-
async for chunk in
|
280 |
if not chunk:
|
281 |
continue
|
282 |
if chunk.startswith("$@$v=undefined-rv1$@$"):
|
@@ -285,59 +302,48 @@ async def process_streaming_response(request: ChatRequest):
|
|
285 |
chunk = chunk.replace(BLOCKED_MESSAGE, "").strip()
|
286 |
if not chunk:
|
287 |
continue
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
timestamp = int(datetime.now().timestamp())
|
295 |
yield "data: " + json.dumps(
|
296 |
-
create_chat_completion_data(
|
297 |
) + "\n\n"
|
298 |
|
299 |
-
|
300 |
-
timestamp = int(datetime.now().timestamp())
|
301 |
yield "data: " + json.dumps(
|
302 |
-
create_chat_completion_data("", request.model,
|
303 |
) + "\n\n"
|
304 |
yield "data: [DONE]\n\n"
|
305 |
|
306 |
-
except httpx.HTTPStatusError as e:
|
307 |
-
# handle HTTP error...
|
308 |
-
pass
|
309 |
except Exception as e:
|
310 |
-
|
311 |
-
pass
|
312 |
-
|
313 |
-
upload_replaced_urls_to_r2(final_snapzion_links, alt_text=get_last_user_prompt(request.messages))
|
314 |
|
|
|
315 |
|
316 |
# ---------------------------------------------
|
317 |
# NON-STREAMING RESPONSE HANDLER
|
318 |
# ---------------------------------------------
|
319 |
async def process_non_streaming_response(request: ChatRequest):
|
320 |
-
|
321 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
322 |
-
logger.info(f"Processing
|
323 |
|
324 |
agent_mode = AGENT_MODE.get(request.model, {})
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
headers_api_chat = get_headers_api_chat(BASE_URL)
|
329 |
|
|
|
330 |
if request.model == "o1-preview":
|
331 |
-
|
332 |
-
logger.info(f"Delay {delay_seconds}s for 'o1-preview' (Request: {request_id})")
|
333 |
-
await asyncio.sleep(delay_seconds)
|
334 |
|
335 |
-
|
336 |
-
if not
|
337 |
-
logger.error("Failed to retrieve h-value.")
|
338 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
339 |
|
340 |
-
|
341 |
|
342 |
json_data = {
|
343 |
"agentMode": agent_mode,
|
@@ -357,16 +363,16 @@ async def process_non_streaming_response(request: ChatRequest):
|
|
357 |
"isPremium": True,
|
358 |
"isMemoryEnabled": False,
|
359 |
"maxTokens": request.max_tokens,
|
360 |
-
"messages":
|
361 |
"mobileClient": False,
|
362 |
"playgroundTemperature": request.temperature,
|
363 |
"playgroundTopP": request.top_p,
|
364 |
"previewToken": None,
|
365 |
-
"trendingAgentMode":
|
366 |
"userId": None,
|
367 |
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
|
368 |
"userSystemPrompt": None,
|
369 |
-
"validated":
|
370 |
"visitFromDelta": False,
|
371 |
"webSearchModePrompt": False,
|
372 |
"vscodeClient": False,
|
@@ -379,49 +385,44 @@ async def process_non_streaming_response(request: ChatRequest):
|
|
379 |
}
|
380 |
|
381 |
prompt_tokens = sum(
|
382 |
-
calculate_tokens(
|
383 |
-
sum(calculate_tokens(img["contents"], request.model) for img in
|
384 |
-
for
|
385 |
)
|
386 |
|
387 |
-
|
388 |
-
|
389 |
|
390 |
async with httpx.AsyncClient() as client:
|
391 |
try:
|
392 |
-
resp = await client.post(f"{BASE_URL}/api/chat", headers=
|
393 |
resp.raise_for_status()
|
394 |
-
|
395 |
except Exception as e:
|
396 |
-
# error handling...
|
397 |
return {
|
398 |
"id": request_id,
|
399 |
"object": "chat.completion",
|
400 |
"created": int(datetime.now().timestamp()),
|
401 |
"model": request.model,
|
402 |
-
"system_fingerprint":
|
403 |
"choices": [{"index": 0, "message": {"role": "assistant", "content": str(e)}, "finish_reason": "error"}],
|
404 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": 0, "total_tokens": prompt_tokens},
|
405 |
}
|
406 |
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
|
413 |
-
|
414 |
-
cleaned = strip_model_prefix(full_response, model_prefix)
|
415 |
-
completion_tokens = calculate_tokens(cleaned, request.model)
|
416 |
-
|
417 |
-
upload_replaced_urls_to_r2(final_snapzion_links, alt_text=get_last_user_prompt(request.messages))
|
418 |
|
419 |
return {
|
420 |
"id": request_id,
|
421 |
"object": "chat.completion",
|
422 |
"created": int(datetime.now().timestamp()),
|
423 |
"model": request.model,
|
424 |
-
"system_fingerprint":
|
425 |
-
"choices": [{"index": 0, "message": {"role": "assistant", "content":
|
426 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "total_tokens": prompt_tokens + completion_tokens},
|
427 |
}
|
|
|
37 |
R2_SECRET_ACCESS_KEY = "14b08b0855263bb63d2618da3a6537e1b0446d89d51da03a568620b1e5342ea8"
|
38 |
R2_ENDPOINT_URL = "https://f2f92ac53fae792c4155f6e93a514989.r2.cloudflarestorage.com"
|
39 |
R2_BUCKET_NAME = "snapzion"
|
|
|
|
|
40 |
R2_REPLACED_URLS_KEY = "snapzion.txt"
|
41 |
|
42 |
s3 = boto3.client(
|
|
|
46 |
aws_secret_access_key=R2_SECRET_ACCESS_KEY,
|
47 |
)
|
48 |
|
|
|
49 |
BLOCKED_MESSAGE = (
|
50 |
"Generated by BLACKBOX.AI, try unlimited chat https://www.blackbox.ai "
|
51 |
"and for API requests replace https://www.blackbox.ai with https://api.blackbox.ai"
|
|
|
55 |
# RANDOM USER-DATA & SESSION GENERATION
|
56 |
# ---------------------------------------------
|
57 |
def get_random_name_email_customer():
|
|
|
|
|
|
|
58 |
first_names = ["Aliace", "B21ob", "Car232ol", "Daavid", "Evewwlyn", "Fraank", "Grssace", "Hefctor", "Ivgy", "Jackdie"]
|
59 |
last_names = ["Smilth", "Johnkson", "Dajvis", "Mihller", "Thomgpson", "Garwcia", "Broawn", "Wilfson", "Maartin", "Clarak"]
|
60 |
|
61 |
random_name = f"{random.choice(first_names)} {random.choice(last_names)}"
|
62 |
email_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
|
63 |
+
random_email = f"{email_username}@gmail.com"
|
64 |
suffix_length = len("Rldf7IKdNhdhiw")
|
65 |
suffix_chars = string.ascii_letters + string.digits
|
66 |
random_suffix = ''.join(random.choice(suffix_chars) for _ in range(suffix_length))
|
|
|
69 |
return random_name, random_email, random_customer_id
|
70 |
|
71 |
def generate_session(email: str, id_length: int = 21, days_ahead: int = 365) -> dict:
|
|
|
|
|
|
|
72 |
numeric_id = ''.join(random.choice('0123456789') for _ in range(id_length))
|
73 |
future_date = datetime.now(timezone.utc) + timedelta(days=days_ahead)
|
74 |
expiry = future_date.isoformat(timespec='milliseconds').replace('+00:00', 'Z')
|
|
|
79 |
|
80 |
return {
|
81 |
"user": {
|
82 |
+
"name": "SNAPZION",
|
83 |
"email": email,
|
84 |
"image": image_url,
|
85 |
"id": numeric_id
|
|
|
89 |
}
|
90 |
|
91 |
def generate_session_data() -> dict:
|
|
|
|
|
|
|
92 |
_, email, _ = get_random_name_email_customer()
|
93 |
session_data = generate_session(email)
|
94 |
logger.info(f"Using generated session with email {email}")
|
|
|
98 |
# HELPER FUNCTIONS
|
99 |
# ---------------------------------------------
|
100 |
def generate_system_fingerprint() -> str:
|
101 |
+
raw = f"{platform.node()}-{time.time()}-{uuid.uuid4()}"
|
102 |
+
short_hash = hashlib.md5(raw.encode()).hexdigest()[:12]
|
103 |
return f"fp_{short_hash}"
|
104 |
|
105 |
def get_last_user_prompt(messages: List[Any]) -> str:
|
|
|
115 |
|
116 |
def upload_replaced_urls_to_r2(urls: List[str], alt_text: str = "") -> None:
|
117 |
if not urls:
|
118 |
+
logger.info("No replaced or final URLs to store.")
|
119 |
return
|
120 |
+
existing = ""
|
121 |
try:
|
122 |
+
resp = s3.get_object(Bucket=R2_BUCKET_NAME, Key=R2_REPLACED_URLS_KEY)
|
123 |
+
existing = resp["Body"].read().decode()
|
|
|
124 |
except s3.exceptions.NoSuchKey:
|
125 |
+
pass
|
126 |
except Exception as e:
|
127 |
+
logger.error(f"Error reading {R2_REPLACED_URLS_KEY}: {e}")
|
128 |
+
|
129 |
+
markdown = "\n".join(f"" for u in urls)
|
130 |
+
content = f"{existing}\n{markdown}" if existing.strip() else markdown
|
131 |
+
|
132 |
try:
|
133 |
s3.put_object(
|
134 |
Bucket=R2_BUCKET_NAME,
|
135 |
Key=R2_REPLACED_URLS_KEY,
|
136 |
+
Body=content.encode(),
|
137 |
ContentType="text/plain",
|
138 |
)
|
139 |
+
logger.info(f"Appended {len(urls)} URLs to {R2_REPLACED_URLS_KEY}.")
|
140 |
except Exception as e:
|
141 |
+
logger.error(f"Error writing {R2_REPLACED_URLS_KEY}: {e}")
|
142 |
|
143 |
def calculate_tokens(text: str, model: str) -> int:
|
144 |
try:
|
145 |
+
enc = tiktoken.encoding_for_model(model)
|
146 |
+
return len(enc.encode(text))
|
147 |
+
except Exception:
|
|
|
148 |
return len(text.split())
|
149 |
|
150 |
def create_chat_completion_data(
|
|
|
175 |
}
|
176 |
|
177 |
def message_to_dict(message, model_prefix: Optional[str] = None):
|
178 |
+
"""
|
179 |
+
Convert a ChatRequest message to a dict for the request payload.
|
180 |
+
Supports up to three images with type-based structure and sends multiple formats.
|
181 |
+
Prepends model_prefix to text content if specified.
|
182 |
+
"""
|
183 |
+
content = ""
|
184 |
+
images_data = []
|
185 |
+
image_urls = []
|
186 |
+
|
187 |
+
if isinstance(message.content, list):
|
188 |
+
for item in message.content:
|
189 |
+
if item.get("type") == "text":
|
190 |
+
content = item.get("text", "").strip()
|
191 |
+
elif item.get("type") == "image_url" and len(images_data) < 3:
|
192 |
+
url = item["image_url"].get("url", "")
|
193 |
+
if url:
|
194 |
+
path = f"MultipleFiles/{uuid.uuid4().hex}.jpg"
|
195 |
+
images_data.append({"filePath": path, "contents": url})
|
196 |
+
image_urls.append({"image_url": {"url": url}})
|
197 |
+
elif isinstance(message.content, str):
|
198 |
+
content = message.content.strip()
|
199 |
+
|
200 |
+
if model_prefix and content:
|
201 |
+
content = f"{model_prefix} {content}"
|
202 |
+
|
203 |
+
base = {"role": message.role, "content": content}
|
204 |
+
if images_data:
|
205 |
+
base["data"] = {
|
206 |
+
"imageBase64": images_data[0]["contents"],
|
207 |
+
"fileText": "",
|
208 |
+
"title": "snapshot",
|
209 |
+
"imagesData": images_data
|
210 |
+
}
|
211 |
+
# embed any extra URLs in content list
|
212 |
+
for extra in image_urls[1:]:
|
213 |
+
base.setdefault("content", []).append(extra)
|
214 |
+
|
215 |
+
return base
|
216 |
|
217 |
def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
218 |
if model_prefix and content.startswith(model_prefix):
|
|
|
219 |
return content[len(model_prefix):].strip()
|
220 |
return content
|
221 |
|
|
|
223 |
# STREAMING RESPONSE HANDLER
|
224 |
# ---------------------------------------------
|
225 |
async def process_streaming_response(request: ChatRequest):
|
226 |
+
system_fp = generate_system_fingerprint()
|
227 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
228 |
+
logger.info(f"Processing (stream) {request_id} - Model: {request.model}")
|
229 |
|
230 |
agent_mode = AGENT_MODE.get(request.model, {})
|
231 |
+
trending_mode = TRENDING_AGENT_MODE.get(request.model, {})
|
232 |
+
prefix = MODEL_PREFIXES.get(request.model, "")
|
|
|
|
|
233 |
|
234 |
+
headers_api = get_headers_api_chat(BASE_URL)
|
235 |
if request.model == "o1-preview":
|
236 |
+
await asyncio.sleep(random.randint(1, 60))
|
|
|
|
|
237 |
|
238 |
+
h = await getHid()
|
239 |
+
if not h:
|
|
|
240 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
241 |
|
242 |
+
msgs = [message_to_dict(m, prefix) for m in request.messages]
|
243 |
|
244 |
json_data = {
|
245 |
"agentMode": agent_mode,
|
|
|
259 |
"isPremium": True,
|
260 |
"isMemoryEnabled": False,
|
261 |
"maxTokens": request.max_tokens,
|
262 |
+
"messages": msgs,
|
263 |
"mobileClient": False,
|
264 |
"playgroundTemperature": request.temperature,
|
265 |
"playgroundTopP": request.top_p,
|
266 |
"previewToken": None,
|
267 |
+
"trendingAgentMode": trending_mode,
|
268 |
"userId": None,
|
269 |
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
|
270 |
"userSystemPrompt": None,
|
271 |
+
"validated": h,
|
272 |
"visitFromDelta": False,
|
273 |
"webSearchModePrompt": False,
|
274 |
"vscodeClient": False,
|
|
|
281 |
}
|
282 |
|
283 |
prompt_tokens = sum(
|
284 |
+
calculate_tokens(m.get("content", ""), request.model) +
|
285 |
+
sum(calculate_tokens(img["contents"], request.model) for img in m.get("data", {}).get("imagesData", []))
|
286 |
+
for m in msgs
|
287 |
)
|
288 |
|
289 |
completion_tokens = 0
|
290 |
+
final_links: List[str] = []
|
291 |
|
292 |
async with httpx.AsyncClient() as client:
|
293 |
try:
|
294 |
+
async with client.stream("POST", f"{BASE_URL}/api/chat", headers=headers_api, json=json_data, timeout=100) as resp:
|
295 |
+
resp.raise_for_status()
|
296 |
+
async for chunk in resp.aiter_text():
|
297 |
if not chunk:
|
298 |
continue
|
299 |
if chunk.startswith("$@$v=undefined-rv1$@$"):
|
|
|
302 |
chunk = chunk.replace(BLOCKED_MESSAGE, "").strip()
|
303 |
if not chunk:
|
304 |
continue
|
305 |
+
chunk = chunk.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
306 |
+
links = re.findall(r"(https://cdn\.snapzion\.com[^\s\)]+)", chunk)
|
307 |
+
final_links.extend(links)
|
308 |
+
clean = strip_model_prefix(chunk, prefix)
|
309 |
+
completion_tokens += calculate_tokens(clean, request.model)
|
310 |
+
ts = int(datetime.now().timestamp())
|
|
|
311 |
yield "data: " + json.dumps(
|
312 |
+
create_chat_completion_data(clean, request.model, ts, request_id, system_fp, prompt_tokens, completion_tokens)
|
313 |
) + "\n\n"
|
314 |
|
315 |
+
ts = int(datetime.now().timestamp())
|
|
|
316 |
yield "data: " + json.dumps(
|
317 |
+
create_chat_completion_data("", request.model, ts, request_id, system_fp, prompt_tokens, completion_tokens, "stop")
|
318 |
) + "\n\n"
|
319 |
yield "data: [DONE]\n\n"
|
320 |
|
|
|
|
|
|
|
321 |
except Exception as e:
|
322 |
+
logger.error(f"Stream error {request_id}: {e}")
|
|
|
|
|
|
|
323 |
|
324 |
+
upload_replaced_urls_to_r2(final_links, alt_text=get_last_user_prompt(request.messages))
|
325 |
|
326 |
# ---------------------------------------------
|
327 |
# NON-STREAMING RESPONSE HANDLER
|
328 |
# ---------------------------------------------
|
329 |
async def process_non_streaming_response(request: ChatRequest):
|
330 |
+
system_fp = generate_system_fingerprint()
|
331 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
332 |
+
logger.info(f"Processing (non-stream) {request_id} - Model: {request.model}")
|
333 |
|
334 |
agent_mode = AGENT_MODE.get(request.model, {})
|
335 |
+
trending_mode = TRENDING_AGENT_MODE.get(request.model, {})
|
336 |
+
prefix = MODEL_PREFIXES.get(request.model, "")
|
|
|
|
|
337 |
|
338 |
+
headers_api = get_headers_api_chat(BASE_URL)
|
339 |
if request.model == "o1-preview":
|
340 |
+
await asyncio.sleep(random.randint(20, 60))
|
|
|
|
|
341 |
|
342 |
+
h = await getHid()
|
343 |
+
if not h:
|
|
|
344 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
345 |
|
346 |
+
msgs = [message_to_dict(m, prefix) for m in request.messages]
|
347 |
|
348 |
json_data = {
|
349 |
"agentMode": agent_mode,
|
|
|
363 |
"isPremium": True,
|
364 |
"isMemoryEnabled": False,
|
365 |
"maxTokens": request.max_tokens,
|
366 |
+
"messages": msgs,
|
367 |
"mobileClient": False,
|
368 |
"playgroundTemperature": request.temperature,
|
369 |
"playgroundTopP": request.top_p,
|
370 |
"previewToken": None,
|
371 |
+
"trendingAgentMode": trending_mode,
|
372 |
"userId": None,
|
373 |
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
|
374 |
"userSystemPrompt": None,
|
375 |
+
"validated": h,
|
376 |
"visitFromDelta": False,
|
377 |
"webSearchModePrompt": False,
|
378 |
"vscodeClient": False,
|
|
|
385 |
}
|
386 |
|
387 |
prompt_tokens = sum(
|
388 |
+
calculate_tokens(m.get("content", ""), request.model) +
|
389 |
+
sum(calculate_tokens(img["contents"], request.model) for img in m.get("data", {}).get("imagesData", []))
|
390 |
+
for m in msgs
|
391 |
)
|
392 |
|
393 |
+
full_resp = ""
|
394 |
+
final_links: List[str] = []
|
395 |
|
396 |
async with httpx.AsyncClient() as client:
|
397 |
try:
|
398 |
+
resp = await client.post(f"{BASE_URL}/api/chat", headers=headers_api, json=json_data)
|
399 |
resp.raise_for_status()
|
400 |
+
full_resp = resp.text
|
401 |
except Exception as e:
|
|
|
402 |
return {
|
403 |
"id": request_id,
|
404 |
"object": "chat.completion",
|
405 |
"created": int(datetime.now().timestamp()),
|
406 |
"model": request.model,
|
407 |
+
"system_fingerprint": system_fp,
|
408 |
"choices": [{"index": 0, "message": {"role": "assistant", "content": str(e)}, "finish_reason": "error"}],
|
409 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": 0, "total_tokens": prompt_tokens},
|
410 |
}
|
411 |
|
412 |
+
full_resp = full_resp.replace(BLOCKED_MESSAGE, "").strip()
|
413 |
+
full_resp = full_resp.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
414 |
+
final_links.extend(re.findall(r"(https://cdn\.snapzion\.com[^\s\)]+)", full_resp))
|
415 |
+
clean = strip_model_prefix(full_resp, prefix)
|
416 |
+
completion_tokens = calculate_tokens(clean, request.model)
|
417 |
|
418 |
+
upload_replaced_urls_to_r2(final_links, alt_text=get_last_user_prompt(request.messages))
|
|
|
|
|
|
|
|
|
419 |
|
420 |
return {
|
421 |
"id": request_id,
|
422 |
"object": "chat.completion",
|
423 |
"created": int(datetime.now().timestamp()),
|
424 |
"model": request.model,
|
425 |
+
"system_fingerprint": system_fp,
|
426 |
+
"choices": [{"index": 0, "message": {"role": "assistant", "content": clean}, "finish_reason": "stop"}],
|
427 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "total_tokens": prompt_tokens + completion_tokens},
|
428 |
}
|