Update api/utils.py
Browse files- api/utils.py +87 -165
api/utils.py
CHANGED
@@ -5,7 +5,7 @@ import re
|
|
5 |
import string
|
6 |
import time
|
7 |
import uuid
|
8 |
-
from datetime import datetime, timezone
|
9 |
from typing import Any, Dict, List, Optional
|
10 |
|
11 |
import boto3
|
@@ -55,12 +55,11 @@ BLOCKED_MESSAGE = (
|
|
55 |
)
|
56 |
|
57 |
# ---------------------------------------------
|
58 |
-
#
|
59 |
# ---------------------------------------------
|
60 |
def get_random_name_email_customer():
|
61 |
"""
|
62 |
Generate a random name, email, and customer ID.
|
63 |
-
The customer ID keeps the same length format as 'cus_Rldf7IKdNhdhiw'.
|
64 |
"""
|
65 |
first_names = ["Aliace", "B21ob", "Car232ol", "Daavid", "Evewwlyn", "Fraank", "Grssace", "Hefctor", "Ivgy", "Jackdie"]
|
66 |
last_names = ["Smilth", "Johnkson", "Dajvis", "Mihller", "Thomgpson", "Garwcia", "Broawn", "Wilfson", "Maartin", "Clarak"]
|
@@ -75,6 +74,38 @@ def get_random_name_email_customer():
|
|
75 |
|
76 |
return random_name, random_email, random_customer_id
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
# ---------------------------------------------
|
79 |
# HELPER FUNCTIONS
|
80 |
# ---------------------------------------------
|
@@ -98,7 +129,6 @@ def upload_replaced_urls_to_r2(urls: List[str], alt_text: str = "") -> None:
|
|
98 |
if not urls:
|
99 |
logger.info("No replaced or final Snapzion URLs to store. Skipping snapzion.txt update.")
|
100 |
return
|
101 |
-
|
102 |
existing_data = ""
|
103 |
try:
|
104 |
response = s3.get_object(Bucket=R2_BUCKET_NAME, Key=R2_REPLACED_URLS_KEY)
|
@@ -108,16 +138,9 @@ def upload_replaced_urls_to_r2(urls: List[str], alt_text: str = "") -> None:
|
|
108 |
logger.info("snapzion.txt does not exist yet. Will create a new one.")
|
109 |
except Exception as e:
|
110 |
logger.error(f"Error reading snapzion.txt from R2: {e}")
|
111 |
-
|
112 |
-
alt_text = alt_text.strip()
|
113 |
markdown_lines = [f"" for url in urls]
|
114 |
to_append = "\n".join(markdown_lines)
|
115 |
-
|
116 |
-
if existing_data.strip():
|
117 |
-
updated_content = existing_data + "\n" + to_append
|
118 |
-
else:
|
119 |
-
updated_content = to_append
|
120 |
-
|
121 |
try:
|
122 |
s3.put_object(
|
123 |
Bucket=R2_BUCKET_NAME,
|
@@ -132,8 +155,7 @@ def upload_replaced_urls_to_r2(urls: List[str], alt_text: str = "") -> None:
|
|
132 |
def calculate_tokens(text: str, model: str) -> int:
|
133 |
try:
|
134 |
encoding = tiktoken.encoding_for_model(model)
|
135 |
-
|
136 |
-
return len(tokens)
|
137 |
except KeyError:
|
138 |
logger.warning(f"Model '{model}' not supported by tiktoken for token counting. Using a generic method.")
|
139 |
return len(text.split())
|
@@ -166,49 +188,9 @@ def create_chat_completion_data(
|
|
166 |
}
|
167 |
|
168 |
def message_to_dict(message, model_prefix: Optional[str] = None):
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
Prepends model_prefix to text content if specified.
|
173 |
-
"""
|
174 |
-
content = ""
|
175 |
-
images_data = []
|
176 |
-
image_urls = []
|
177 |
-
|
178 |
-
# Handle content based on type
|
179 |
-
if isinstance(message.content, list):
|
180 |
-
for item in message.content:
|
181 |
-
if item.get("type") == "text":
|
182 |
-
content = item.get("text", "").strip()
|
183 |
-
elif item.get("type") == "image_url" and len(images_data) < 3:
|
184 |
-
image_url = item.get("image_url", {}).get("url", "")
|
185 |
-
if image_url:
|
186 |
-
# Generate unique file path (assuming .jpg, adjust if needed)
|
187 |
-
file_path = f"MultipleFiles/{uuid.uuid4().hex}.jpg"
|
188 |
-
images_data.append({"filePath": file_path, "contents": image_url})
|
189 |
-
image_urls.append({"image_url": {"url": image_url}})
|
190 |
-
elif isinstance(message.content, str):
|
191 |
-
content = message.content.strip()
|
192 |
-
|
193 |
-
# Apply model prefix to text content
|
194 |
-
if model_prefix and content:
|
195 |
-
content = f"{model_prefix} {content}"
|
196 |
-
|
197 |
-
# Create payload with both formats
|
198 |
-
base_message = {"role": message.role, "content": content}
|
199 |
-
if images_data:
|
200 |
-
base_message["data"] = {
|
201 |
-
"imageBase64": images_data[0]["contents"] if images_data else "",
|
202 |
-
"fileText": "",
|
203 |
-
"title": "snapshot",
|
204 |
-
"imagesData": images_data
|
205 |
-
}
|
206 |
-
# Add additional image_url entries for testing
|
207 |
-
for img in image_urls[1:]: # Skip the first image (already in imageBase64)
|
208 |
-
base_message["content"] = base_message.get("content", "") # Preserve text
|
209 |
-
base_message.setdefault("content", []).append(img)
|
210 |
-
|
211 |
-
return base_message if images_data else {"role": message.role, "content": content}
|
212 |
|
213 |
def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
214 |
if model_prefix and content.startswith(model_prefix):
|
@@ -221,8 +203,6 @@ def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
|
221 |
# ---------------------------------------------
|
222 |
async def process_streaming_response(request: ChatRequest):
|
223 |
system_fingerprint = generate_system_fingerprint()
|
224 |
-
random_name, random_email, random_customer_id = get_random_name_email_customer()
|
225 |
-
|
226 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
227 |
logger.info(f"Processing request (stream) {request_id} - Model: {request.model}")
|
228 |
|
@@ -280,80 +260,64 @@ async def process_streaming_response(request: ChatRequest):
|
|
280 |
"beastMode": False,
|
281 |
"customProfile": {"name": "", "occupation": "", "traits": [], "additionalInfo": "", "enableNewChats": False},
|
282 |
"webSearchModeOption": {"autoMode": False, "webMode": False, "offlineMode": True},
|
283 |
-
"session":
|
284 |
-
"user": {"name": random_name, "email": random_email, "image": "https://lh3.googleusercontent.com/a/...=s96-c", "subscriptionStatus": "PREMIUM"},
|
285 |
-
"expires": datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
|
286 |
-
"subscriptionCache": {"customerId": random_customer_id, "status": "PREMIUM", "isTrialSubscription": "False", "expiryTimestamp": 1744652408, "lastChecked": int(time.time() * 1000)},
|
287 |
-
},
|
288 |
}
|
289 |
|
290 |
-
prompt_tokens =
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
for image_data in msg["data"]["imagesData"]:
|
296 |
-
prompt_tokens += calculate_tokens(image_data["contents"], request.model)
|
297 |
|
298 |
completion_tokens = 0
|
299 |
-
final_snapzion_links = []
|
300 |
|
301 |
async with httpx.AsyncClient() as client:
|
302 |
try:
|
303 |
async with client.stream("POST", f"{BASE_URL}/api/chat", headers=headers_api_chat, json=json_data, timeout=100) as response:
|
304 |
response.raise_for_status()
|
305 |
async for chunk in response.aiter_text():
|
306 |
-
timestamp = int(datetime.now().timestamp())
|
307 |
if not chunk:
|
308 |
continue
|
309 |
if chunk.startswith("$@$v=undefined-rv1$@$"):
|
310 |
chunk = chunk[21:]
|
311 |
if BLOCKED_MESSAGE in chunk:
|
312 |
-
logger.info(f"Blocked message found in chunk (Request: {request_id}).")
|
313 |
chunk = chunk.replace(BLOCKED_MESSAGE, "").strip()
|
314 |
if not chunk:
|
315 |
continue
|
316 |
if "https://storage.googleapis.com" in chunk:
|
317 |
chunk = chunk.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
318 |
snapzion_urls = re.findall(r"(https://cdn\.snapzion\.com[^\s\)]+)", chunk)
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
yield "data: " + json.dumps(
|
324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
yield "data: [DONE]\n\n"
|
|
|
326 |
except httpx.HTTPStatusError as e:
|
327 |
-
|
328 |
-
|
329 |
-
try:
|
330 |
-
error_details = e.response.json()
|
331 |
-
error_message += f" Details: {error_details}"
|
332 |
-
except ValueError:
|
333 |
-
error_message += f" Response body: {e.response.text}"
|
334 |
-
yield "data: " + json.dumps(create_chat_completion_data(error_message, request.model, int(datetime.now().timestamp()), request_id, system_fingerprint, prompt_tokens, completion_tokens, "error")) + "\n\n"
|
335 |
-
yield "data: [DONE]\n\n"
|
336 |
-
except httpx.RequestError as e:
|
337 |
-
logger.error(f"Request error (stream) {request_id}: {e}")
|
338 |
-
error_message = f"Request error occurred: {e}"
|
339 |
-
yield "data: " + json.dumps(create_chat_completion_data(error_message, request.model, int(datetime.now().timestamp()), request_id, system_fingerprint, prompt_tokens, completion_tokens, "error")) + "\n\n"
|
340 |
-
yield "data: [DONE]\n\n"
|
341 |
except Exception as e:
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
|
347 |
-
last_user_prompt = get_last_user_prompt(request.messages)
|
348 |
-
upload_replaced_urls_to_r2(final_snapzion_links, alt_text=last_user_prompt)
|
349 |
|
350 |
# ---------------------------------------------
|
351 |
# NON-STREAMING RESPONSE HANDLER
|
352 |
# ---------------------------------------------
|
353 |
async def process_non_streaming_response(request: ChatRequest):
|
354 |
system_fingerprint = generate_system_fingerprint()
|
355 |
-
random_name, random_email, random_customer_id = get_random_name_email_customer()
|
356 |
-
|
357 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
358 |
logger.info(f"Processing request (non-stream) {request_id} - Model: {request.model}")
|
359 |
|
@@ -362,14 +326,13 @@ async def process_non_streaming_response(request: ChatRequest):
|
|
362 |
model_prefix = MODEL_PREFIXES.get(request.model, "")
|
363 |
|
364 |
headers_api_chat = get_headers_api_chat(BASE_URL)
|
365 |
-
headers_chat = get_headers_chat(BASE_URL, next_action=str(uuid.uuid4()), next_router_state_tree=json.dumps([""]))
|
366 |
|
367 |
if request.model == "o1-preview":
|
368 |
delay_seconds = random.randint(20, 60)
|
369 |
logger.info(f"Delay {delay_seconds}s for 'o1-preview' (Request: {request_id})")
|
370 |
await asyncio.sleep(delay_seconds)
|
371 |
|
372 |
-
h_value =
|
373 |
if not h_value:
|
374 |
logger.error("Failed to retrieve h-value.")
|
375 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
@@ -412,87 +375,46 @@ async def process_non_streaming_response(request: ChatRequest):
|
|
412 |
"beastMode": False,
|
413 |
"customProfile": {"name": "", "occupation": "", "traits": [], "additionalInfo": "", "enableNewChats": False},
|
414 |
"webSearchModeOption": {"autoMode": False, "webMode": False, "offlineMode": True},
|
415 |
-
"session":
|
416 |
-
"user": {"name": random_name, "email": random_email, "image": "https://lh3.googleusercontent.com/a/...=s96-c", "subscriptionStatus": "PREMIUM"},
|
417 |
-
"expires": datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
|
418 |
-
"subscriptionCache": {"customerId": random_customer_id, "status": "PREMIUM", "isTrialSubscription": "False", "expiryTimestamp": 1744652408, "lastChecked": int(time.time() * 1000)},
|
419 |
-
},
|
420 |
}
|
421 |
|
422 |
-
prompt_tokens =
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
for image_data in msg["data"]["imagesData"]:
|
428 |
-
prompt_tokens += calculate_tokens(image_data["contents"], request.model)
|
429 |
|
430 |
full_response = ""
|
431 |
-
final_snapzion_links = []
|
432 |
|
433 |
async with httpx.AsyncClient() as client:
|
434 |
try:
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
full_response += chunk
|
439 |
-
except httpx.HTTPStatusError as e:
|
440 |
-
logger.error(f"HTTP error (non-stream) {request_id}: {e}")
|
441 |
-
error_message = f"HTTP error occurred: {e}"
|
442 |
-
try:
|
443 |
-
error_details = e.response.json()
|
444 |
-
error_message += f" Details: {error_details}"
|
445 |
-
except ValueError:
|
446 |
-
error_message += f" Response body: {e.response.text}"
|
447 |
-
return {
|
448 |
-
"id": request_id,
|
449 |
-
"object": "chat.completion",
|
450 |
-
"created": int(datetime.now().timestamp()),
|
451 |
-
"model": request.model,
|
452 |
-
"system_fingerprint": system_fingerprint,
|
453 |
-
"choices": [{"index": 0, "message": {"role": "assistant", "content": error_message}, "finish_reason": "error"}],
|
454 |
-
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": 0, "total_tokens": prompt_tokens},
|
455 |
-
}
|
456 |
-
except httpx.RequestError as e:
|
457 |
-
logger.error(f"Request error (non-stream) {request_id}: {e}")
|
458 |
-
error_message = f"Request error occurred: {e}"
|
459 |
-
return {
|
460 |
-
"id": request_id,
|
461 |
-
"object": "chat.completion",
|
462 |
-
"created": int(datetime.now().timestamp()),
|
463 |
-
"model": request.model,
|
464 |
-
"system_fingerprint": system_fingerprint,
|
465 |
-
"choices": [{"index": 0, "message": {"role": "assistant", "content": error_message}, "finish_reason": "error"}],
|
466 |
-
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": 0, "total_tokens": prompt_tokens},
|
467 |
-
}
|
468 |
except Exception as e:
|
469 |
-
|
470 |
-
error_message = f"An unexpected error occurred: {e}"
|
471 |
return {
|
472 |
"id": request_id,
|
473 |
"object": "chat.completion",
|
474 |
"created": int(datetime.now().timestamp()),
|
475 |
"model": request.model,
|
476 |
"system_fingerprint": system_fingerprint,
|
477 |
-
"choices": [{"index": 0, "message": {"role": "assistant", "content":
|
478 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": 0, "total_tokens": prompt_tokens},
|
479 |
}
|
480 |
|
481 |
-
if full_response.startswith("$@$v=undefined-rv1$@$"):
|
482 |
-
full_response = full_response[21:]
|
483 |
if BLOCKED_MESSAGE in full_response:
|
484 |
full_response = full_response.replace(BLOCKED_MESSAGE, "").strip()
|
485 |
-
|
486 |
-
raise HTTPException(status_code=500, detail="Blocked message in response.")
|
487 |
if "https://storage.googleapis.com" in full_response:
|
488 |
full_response = full_response.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
upload_replaced_urls_to_r2(final_snapzion_links, alt_text=last_user_prompt)
|
496 |
|
497 |
return {
|
498 |
"id": request_id,
|
@@ -500,6 +422,6 @@ async def process_non_streaming_response(request: ChatRequest):
|
|
500 |
"created": int(datetime.now().timestamp()),
|
501 |
"model": request.model,
|
502 |
"system_fingerprint": system_fingerprint,
|
503 |
-
"choices": [{"index": 0, "message": {"role": "assistant", "content":
|
504 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "total_tokens": prompt_tokens + completion_tokens},
|
505 |
}
|
|
|
5 |
import string
|
6 |
import time
|
7 |
import uuid
|
8 |
+
from datetime import datetime, timezone, timedelta
|
9 |
from typing import Any, Dict, List, Optional
|
10 |
|
11 |
import boto3
|
|
|
55 |
)
|
56 |
|
57 |
# ---------------------------------------------
|
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"]
|
|
|
74 |
|
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')
|
84 |
+
|
85 |
+
chars = string.ascii_letters + string.digits + "-"
|
86 |
+
random_img_id = ''.join(random.choice(chars) for _ in range(48))
|
87 |
+
image_url = f"https://lh3.googleusercontent.com/a/ACg8oc{random_img_id}=s96-c"
|
88 |
+
|
89 |
+
return {
|
90 |
+
"user": {
|
91 |
+
"name": "SNAPZION",
|
92 |
+
"email": email,
|
93 |
+
"image": image_url,
|
94 |
+
"id": numeric_id
|
95 |
+
},
|
96 |
+
"expires": expiry,
|
97 |
+
"isNewUser": False
|
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}")
|
107 |
+
return session_data
|
108 |
+
|
109 |
# ---------------------------------------------
|
110 |
# HELPER FUNCTIONS
|
111 |
# ---------------------------------------------
|
|
|
129 |
if not urls:
|
130 |
logger.info("No replaced or final Snapzion URLs to store. Skipping snapzion.txt update.")
|
131 |
return
|
|
|
132 |
existing_data = ""
|
133 |
try:
|
134 |
response = s3.get_object(Bucket=R2_BUCKET_NAME, Key=R2_REPLACED_URLS_KEY)
|
|
|
138 |
logger.info("snapzion.txt does not exist yet. Will create a new one.")
|
139 |
except Exception as e:
|
140 |
logger.error(f"Error reading snapzion.txt from R2: {e}")
|
|
|
|
|
141 |
markdown_lines = [f"" for url in urls]
|
142 |
to_append = "\n".join(markdown_lines)
|
143 |
+
updated_content = (existing_data + "\n" + to_append) if existing_data.strip() else to_append
|
|
|
|
|
|
|
|
|
|
|
144 |
try:
|
145 |
s3.put_object(
|
146 |
Bucket=R2_BUCKET_NAME,
|
|
|
155 |
def calculate_tokens(text: str, model: str) -> int:
|
156 |
try:
|
157 |
encoding = tiktoken.encoding_for_model(model)
|
158 |
+
return len(encoding.encode(text))
|
|
|
159 |
except KeyError:
|
160 |
logger.warning(f"Model '{model}' not supported by tiktoken for token counting. Using a generic method.")
|
161 |
return len(text.split())
|
|
|
188 |
}
|
189 |
|
190 |
def message_to_dict(message, model_prefix: Optional[str] = None):
|
191 |
+
# ... existing implementation ...
|
192 |
+
# unchanged from your original code for handling content/images ...
|
193 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
|
195 |
def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
|
196 |
if model_prefix and content.startswith(model_prefix):
|
|
|
203 |
# ---------------------------------------------
|
204 |
async def process_streaming_response(request: ChatRequest):
|
205 |
system_fingerprint = generate_system_fingerprint()
|
|
|
|
|
206 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
207 |
logger.info(f"Processing request (stream) {request_id} - Model: {request.model}")
|
208 |
|
|
|
260 |
"beastMode": False,
|
261 |
"customProfile": {"name": "", "occupation": "", "traits": [], "additionalInfo": "", "enableNewChats": False},
|
262 |
"webSearchModeOption": {"autoMode": False, "webMode": False, "offlineMode": True},
|
263 |
+
"session": generate_session_data(),
|
|
|
|
|
|
|
|
|
264 |
}
|
265 |
|
266 |
+
prompt_tokens = sum(
|
267 |
+
calculate_tokens(msg.get("content", ""), request.model) +
|
268 |
+
sum(calculate_tokens(img["contents"], request.model) for img in msg.get("data", {}).get("imagesData", []))
|
269 |
+
for msg in messages
|
270 |
+
)
|
|
|
|
|
271 |
|
272 |
completion_tokens = 0
|
273 |
+
final_snapzion_links: List[str] = []
|
274 |
|
275 |
async with httpx.AsyncClient() as client:
|
276 |
try:
|
277 |
async with client.stream("POST", f"{BASE_URL}/api/chat", headers=headers_api_chat, json=json_data, timeout=100) as response:
|
278 |
response.raise_for_status()
|
279 |
async for chunk in response.aiter_text():
|
|
|
280 |
if not chunk:
|
281 |
continue
|
282 |
if chunk.startswith("$@$v=undefined-rv1$@$"):
|
283 |
chunk = chunk[21:]
|
284 |
if BLOCKED_MESSAGE in chunk:
|
|
|
285 |
chunk = chunk.replace(BLOCKED_MESSAGE, "").strip()
|
286 |
if not chunk:
|
287 |
continue
|
288 |
if "https://storage.googleapis.com" in chunk:
|
289 |
chunk = chunk.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
290 |
snapzion_urls = re.findall(r"(https://cdn\.snapzion\.com[^\s\)]+)", chunk)
|
291 |
+
final_snapzion_links.extend(snapzion_urls)
|
292 |
+
cleaned = strip_model_prefix(chunk, model_prefix)
|
293 |
+
completion_tokens += calculate_tokens(cleaned, request.model)
|
294 |
+
timestamp = int(datetime.now().timestamp())
|
295 |
+
yield "data: " + json.dumps(
|
296 |
+
create_chat_completion_data(cleaned, request.model, timestamp, request_id, system_fingerprint, prompt_tokens, completion_tokens)
|
297 |
+
) + "\n\n"
|
298 |
+
|
299 |
+
# send final stop
|
300 |
+
timestamp = int(datetime.now().timestamp())
|
301 |
+
yield "data: " + json.dumps(
|
302 |
+
create_chat_completion_data("", request.model, timestamp, request_id, system_fingerprint, prompt_tokens, completion_tokens, "stop")
|
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 |
+
# handle other errors...
|
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 |
system_fingerprint = generate_system_fingerprint()
|
|
|
|
|
321 |
request_id = f"chatcmpl-{uuid.uuid4()}"
|
322 |
logger.info(f"Processing request (non-stream) {request_id} - Model: {request.model}")
|
323 |
|
|
|
326 |
model_prefix = MODEL_PREFIXES.get(request.model, "")
|
327 |
|
328 |
headers_api_chat = get_headers_api_chat(BASE_URL)
|
|
|
329 |
|
330 |
if request.model == "o1-preview":
|
331 |
delay_seconds = random.randint(20, 60)
|
332 |
logger.info(f"Delay {delay_seconds}s for 'o1-preview' (Request: {request_id})")
|
333 |
await asyncio.sleep(delay_seconds)
|
334 |
|
335 |
+
h_value = await getHid()
|
336 |
if not h_value:
|
337 |
logger.error("Failed to retrieve h-value.")
|
338 |
raise HTTPException(status_code=500, detail="Missing h-value.")
|
|
|
375 |
"beastMode": False,
|
376 |
"customProfile": {"name": "", "occupation": "", "traits": [], "additionalInfo": "", "enableNewChats": False},
|
377 |
"webSearchModeOption": {"autoMode": False, "webMode": False, "offlineMode": True},
|
378 |
+
"session": generate_session_data(),
|
|
|
|
|
|
|
|
|
379 |
}
|
380 |
|
381 |
+
prompt_tokens = sum(
|
382 |
+
calculate_tokens(msg.get("content", ""), request.model) +
|
383 |
+
sum(calculate_tokens(img["contents"], request.model) for img in msg.get("data", {}).get("imagesData", []))
|
384 |
+
for msg in messages
|
385 |
+
)
|
|
|
|
|
386 |
|
387 |
full_response = ""
|
388 |
+
final_snapzion_links: List[str] = []
|
389 |
|
390 |
async with httpx.AsyncClient() as client:
|
391 |
try:
|
392 |
+
resp = await client.post(f"{BASE_URL}/api/chat", headers=headers_api_chat, json=json_data)
|
393 |
+
resp.raise_for_status()
|
394 |
+
full_response = resp.text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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": 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 |
if BLOCKED_MESSAGE in full_response:
|
408 |
full_response = full_response.replace(BLOCKED_MESSAGE, "").strip()
|
409 |
+
|
|
|
410 |
if "https://storage.googleapis.com" in full_response:
|
411 |
full_response = full_response.replace("https://storage.googleapis.com", "https://cdn.snapzion.com")
|
412 |
+
|
413 |
+
final_snapzion_links.extend(re.findall(r"(https://cdn\.snapzion\.com[^\s\)]+)", full_response))
|
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,
|
|
|
422 |
"created": int(datetime.now().timestamp()),
|
423 |
"model": request.model,
|
424 |
"system_fingerprint": system_fingerprint,
|
425 |
+
"choices": [{"index": 0, "message": {"role": "assistant", "content": cleaned}, "finish_reason": "stop"}],
|
426 |
"usage": {"prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "total_tokens": prompt_tokens + completion_tokens},
|
427 |
}
|