Niansuh commited on
Commit
65ea21b
·
verified ·
1 Parent(s): 94877ab

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +46 -411
main.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import os
2
  import re
3
  import random
@@ -12,11 +14,13 @@ from typing import List, Dict, Any, Optional, AsyncGenerator, Union
12
 
13
  from datetime import datetime
14
 
15
- from aiohttp import ClientSession, ClientTimeout, ClientError, ClientResponseError
16
  from fastapi import FastAPI, HTTPException, Request, Depends, Header
17
  from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
18
  from pydantic import BaseModel
19
 
 
 
20
  # Configure logging
21
  logging.basicConfig(
22
  level=logging.INFO,
@@ -37,6 +41,7 @@ if not API_KEYS or API_KEYS == ['']:
37
  # Process available models
38
  if AVAILABLE_MODELS:
39
  AVAILABLE_MODELS = [model.strip() for model in AVAILABLE_MODELS.split(',') if model.strip()]
 
40
  else:
41
  AVAILABLE_MODELS = [] # If empty, all models are available
42
 
@@ -96,361 +101,33 @@ class ModelNotWorkingException(Exception):
96
  self.message = f"The model '{model}' is currently not working. Please try another model or wait for it to be fixed."
97
  super().__init__(self.message)
98
 
99
- # Define ImageResponseData class
100
- class ImageResponseData:
101
- def __init__(self, images: str, alt: str):
102
- self.images = images
103
- self.alt = alt
104
-
105
- # Mock implementation of to_data_uri
106
- def to_data_uri(image: Any) -> str:
107
- return "data:image/png;base64,..." # Replace with actual base64 data
108
-
109
- class Blackbox:
110
- url = "https://www.blackbox.ai"
111
- api_endpoint = "https://www.blackbox.ai/api/chat"
112
- working = True
113
- supports_stream = True
114
- supports_system_message = True
115
- supports_message_history = True
116
-
117
- default_model = 'blackboxai'
118
- image_models = ['ImageGeneration']
119
- models = [
120
- default_model,
121
- 'blackboxai-pro',
122
- *image_models,
123
- "llama-3.1-8b",
124
- 'llama-3.1-70b',
125
- 'llama-3.1-405b',
126
- 'gpt-4o',
127
- 'gemini-pro',
128
- 'gemini-1.5-flash',
129
- 'claude-sonnet-3.5',
130
- 'PythonAgent',
131
- 'JavaAgent',
132
- 'JavaScriptAgent',
133
- 'HTMLAgent',
134
- 'GoogleCloudAgent',
135
- 'AndroidDeveloper',
136
- 'SwiftDeveloper',
137
- 'Next.jsAgent',
138
- 'MongoDBAgent',
139
- 'PyTorchAgent',
140
- 'ReactAgent',
141
- 'XcodeAgent',
142
- 'AngularJSAgent',
143
- ]
144
-
145
- # Filter models based on AVAILABLE_MODELS
146
- if AVAILABLE_MODELS:
147
- models = [model for model in models if model in AVAILABLE_MODELS]
148
-
149
- agentMode = {
150
- 'ImageGeneration': {'mode': True, 'id': "ImageGenerationLV45LJp", 'name': "Image Generation"},
151
- 'Niansuh': {'mode': True, 'id': "NiansuhAIk1HgESy", 'name': "Niansuh"},
152
- }
153
- trendingAgentMode = {
154
- "blackboxai": {},
155
- "gemini-1.5-flash": {'mode': True, 'id': 'Gemini'},
156
- "llama-3.1-8b": {'mode': True, 'id': "llama-3.1-8b"},
157
- 'llama-3.1-70b': {'mode': True, 'id': "llama-3.1-70b"},
158
- 'llama-3.1-405b': {'mode': True, 'id': "llama-3.1-405b"},
159
- 'blackboxai-pro': {'mode': True, 'id': "BLACKBOXAI-PRO"},
160
- 'PythonAgent': {'mode': True, 'id': "Python Agent"},
161
- 'JavaAgent': {'mode': True, 'id': "Java Agent"},
162
- 'JavaScriptAgent': {'mode': True, 'id': "JavaScript Agent"},
163
- 'HTMLAgent': {'mode': True, 'id': "HTML Agent"},
164
- 'GoogleCloudAgent': {'mode': True, 'id': "Google Cloud Agent"},
165
- 'AndroidDeveloper': {'mode': True, 'id': "Android Developer"},
166
- 'SwiftDeveloper': {'mode': True, 'id': "Swift Developer"},
167
- 'Next.jsAgent': {'mode': True, 'id': "Next.js Agent"},
168
- 'MongoDBAgent': {'mode': True, 'id': "MongoDB Agent"},
169
- 'PyTorchAgent': {'mode': True, 'id': "PyTorch Agent"},
170
- 'ReactAgent': {'mode': True, 'id': "React Agent"},
171
- 'XcodeAgent': {'mode': True, 'id': "Xcode Agent"},
172
- 'AngularJSAgent': {'mode': True, 'id': "AngularJS Agent"},
173
- }
174
-
175
- userSelectedModel = {
176
- "gpt-4o": "gpt-4o",
177
- "gemini-pro": "gemini-pro",
178
- 'claude-sonnet-3.5': "claude-sonnet-3.5",
179
- }
180
-
181
- model_prefixes = {
182
- 'gpt-4o': '@GPT-4o',
183
- 'gemini-pro': '@Gemini-PRO',
184
- 'claude-sonnet-3.5': '@Claude-Sonnet-3.5',
185
- 'PythonAgent': '@Python Agent',
186
- 'JavaAgent': '@Java Agent',
187
- 'JavaScriptAgent': '@JavaScript Agent',
188
- 'HTMLAgent': '@HTML Agent',
189
- 'GoogleCloudAgent': '@Google Cloud Agent',
190
- 'AndroidDeveloper': '@Android Developer',
191
- 'SwiftDeveloper': '@Swift Developer',
192
- 'Next.jsAgent': '@Next.js Agent',
193
- 'MongoDBAgent': '@MongoDB Agent',
194
- 'PyTorchAgent': '@PyTorch Agent',
195
- 'ReactAgent': '@React Agent',
196
- 'XcodeAgent': '@Xcode Agent',
197
- 'AngularJSAgent': '@AngularJS Agent',
198
- 'blackboxai-pro': '@BLACKBOXAI-PRO',
199
- 'ImageGeneration': '@Image Generation',
200
- 'Niansuh': '@Niansuh',
201
- }
202
-
203
- model_referers = {
204
- "blackboxai": "/?model=blackboxai",
205
- "gpt-4o": "/?model=gpt-4o",
206
- "gemini-pro": "/?model=gemini-pro",
207
- "claude-sonnet-3.5": "/?model=claude-sonnet-3.5"
208
- }
209
-
210
- model_aliases = {
211
- "gemini-flash": "gemini-1.5-flash",
212
- "claude-3.5-sonnet": "claude-sonnet-3.5",
213
- "flux": "ImageGeneration",
214
- "niansuh": "Niansuh",
215
- }
216
-
217
- @classmethod
218
- def get_model(cls, model: str) -> str:
219
- if model in cls.models:
220
- return model
221
- elif model in cls.model_aliases:
222
- return cls.model_aliases[model]
223
- else:
224
- return cls.default_model
225
-
226
- @staticmethod
227
- def generate_random_string(length: int = 7) -> str:
228
- characters = string.ascii_letters + string.digits
229
- return ''.join(random.choices(characters, k=length))
230
-
231
- @staticmethod
232
- def generate_next_action() -> str:
233
- return uuid.uuid4().hex
234
 
235
- @staticmethod
236
- def generate_next_router_state_tree() -> str:
237
- router_state = [
238
- "",
 
 
 
239
  {
240
- "children": [
241
- "(chat)",
242
- {
243
- "children": [
244
- "__PAGE__",
245
- {}
246
- ]
247
- }
248
- ]
249
- },
250
- None,
251
- None,
252
- True
253
- ]
254
- return json.dumps(router_state)
255
-
256
- @staticmethod
257
- def clean_response(text: str) -> str:
258
- pattern = r'^\$\@\$v=undefined-rv1\$\@\$'
259
- cleaned_text = re.sub(pattern, '', text)
260
- return cleaned_text
261
-
262
- @classmethod
263
- async def create_async_generator(
264
- cls,
265
- model: str,
266
- messages: List[Dict[str, str]],
267
- proxy: Optional[str] = None,
268
- websearch: bool = False,
269
- **kwargs
270
- ) -> AsyncGenerator[Union[str, ImageResponseData], None]:
271
- """
272
- Creates an asynchronous generator for streaming responses from Blackbox AI.
273
-
274
- Parameters:
275
- model (str): Model to use for generating responses.
276
- messages (List[Dict[str, str]]): Message history.
277
- proxy (Optional[str]): Proxy URL, if needed.
278
- websearch (bool): Enables or disables web search mode.
279
- **kwargs: Additional keyword arguments.
280
-
281
- Yields:
282
- Union[str, ImageResponseData]: Segments of the generated response or ImageResponseData objects.
283
- """
284
- model = cls.get_model(model)
285
-
286
- chat_id = cls.generate_random_string()
287
- next_action = cls.generate_next_action()
288
- next_router_state_tree = cls.generate_next_router_state_tree()
289
-
290
- agent_mode = cls.agentMode.get(model, {})
291
- trending_agent_mode = cls.trendingAgentMode.get(model, {})
292
-
293
- prefix = cls.model_prefixes.get(model, "")
294
-
295
- formatted_prompt = ""
296
- for message in messages:
297
- role = message.get('role', '').capitalize()
298
- content = message.get('content', '')
299
- if role and content:
300
- formatted_prompt += f"{role}: {content}\n"
301
-
302
- if prefix:
303
- formatted_prompt = f"{prefix} {formatted_prompt}".strip()
304
-
305
- referer_path = cls.model_referers.get(model, f"/?model={model}")
306
- referer_url = f"{cls.url}{referer_path}"
307
-
308
- common_headers = {
309
- 'accept': '*/*',
310
- 'accept-language': 'en-US,en;q=0.9',
311
- 'cache-control': 'no-cache',
312
- 'origin': cls.url,
313
- 'pragma': 'no-cache',
314
- 'priority': 'u=1, i',
315
- 'sec-ch-ua': '"Chromium";v="129", "Not=A?Brand";v="8"',
316
- 'sec-ch-ua-mobile': '?0',
317
- 'sec-ch-ua-platform': '"Linux"',
318
- 'sec-fetch-dest': 'empty',
319
- 'sec-fetch-mode': 'cors',
320
- 'sec-fetch-site': 'same-origin',
321
- 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) '
322
- 'AppleWebKit/537.36 (KHTML, like Gecko) '
323
- 'Chrome/129.0.0.0 Safari/537.36'
324
- }
325
-
326
- headers_api_chat = {
327
- 'Content-Type': 'application/json',
328
- 'Referer': referer_url
329
- }
330
- headers_api_chat_combined = {**common_headers, **headers_api_chat}
331
-
332
- payload_api_chat = {
333
- "messages": [
334
- {
335
- "id": chat_id,
336
- "content": formatted_prompt,
337
- "role": "user"
338
- }
339
- ],
340
- "id": chat_id,
341
- "previewToken": None,
342
- "userId": None,
343
- "codeModelMode": True,
344
- "agentMode": agent_mode,
345
- "trendingAgentMode": trending_agent_mode,
346
- "isMicMode": False,
347
- "userSystemPrompt": None,
348
- "maxTokens": 1024,
349
- "playgroundTopP": 0.9,
350
- "playgroundTemperature": 0.5,
351
- "isChromeExt": False,
352
- "githubToken": None,
353
- "clickedAnswer2": False,
354
- "clickedAnswer3": False,
355
- "clickedForceWebSearch": False,
356
- "visitFromDelta": False,
357
- "mobileClient": False,
358
- "webSearchMode": websearch,
359
- "userSelectedModel": cls.userSelectedModel.get(model, model)
360
- }
361
-
362
- headers_chat = {
363
- 'Accept': 'text/x-component',
364
- 'Content-Type': 'text/plain;charset=UTF-8',
365
- 'Referer': f'{cls.url}/chat/{chat_id}?model={model}',
366
- 'next-action': next_action,
367
- 'next-router-state-tree': next_router_state_tree,
368
- 'next-url': '/'
369
- }
370
- headers_chat_combined = {**common_headers, **headers_chat}
371
-
372
- data_chat = '[]'
373
-
374
- async with ClientSession(headers=common_headers) as session:
375
- try:
376
- async with session.post(
377
- cls.api_endpoint,
378
- headers=headers_api_chat_combined,
379
- json=payload_api_chat,
380
- proxy=proxy
381
- ) as response_api_chat:
382
- response_api_chat.raise_for_status()
383
- text = await response_api_chat.text()
384
- cleaned_response = cls.clean_response(text)
385
-
386
- if model in cls.image_models:
387
- match = re.search(r'!\[.*?\]\((https?://[^\)]+)\)', cleaned_response)
388
- if match:
389
- image_url = match.group(1)
390
- image_response = ImageResponseData(images=image_url, alt="Generated Image")
391
- yield image_response
392
- else:
393
- yield cleaned_response
394
- else:
395
- if websearch:
396
- match = re.search(r'\$~~~\$(.*?)\$~~~\$', cleaned_response, re.DOTALL)
397
- if match:
398
- source_part = match.group(1).strip()
399
- answer_part = cleaned_response[match.end():].strip()
400
- try:
401
- sources = json.loads(source_part)
402
- source_formatted = "**Sources:**\n"
403
- for item in sources[:5]:
404
- title = item.get('title', 'No Title')
405
- link = item.get('link', '#')
406
- position = item.get('position', '')
407
- source_formatted += f"- [{title}]({link})\n"
408
- final_response = f"{answer_part}\n\n{source_formatted}"
409
- except json.JSONDecodeError:
410
- final_response = f"{answer_part}\n\nSource information is unavailable."
411
- else:
412
- final_response = cleaned_response
413
- else:
414
- if '$~~~$' in cleaned_response:
415
- final_response = cleaned_response.split('$~~~$')[0].strip()
416
- else:
417
- final_response = cleaned_response
418
-
419
- yield final_response
420
- except ClientResponseError as e:
421
- error_text = f"Error {e.status}: {e.message}"
422
- try:
423
- error_response = await e.response.text()
424
- cleaned_error = cls.clean_response(error_response)
425
- error_text += f" - {cleaned_error}"
426
- except Exception:
427
- pass
428
- yield error_text
429
- except Exception as e:
430
- yield f"Unexpected error during /api/chat request: {str(e)}"
431
-
432
- chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
433
-
434
- try:
435
- async with session.post(
436
- chat_url,
437
- headers=headers_chat_combined,
438
- data=data_chat,
439
- proxy=proxy
440
- ) as response_chat:
441
- response_chat.raise_for_status()
442
- # No action needed based on the original code
443
- except ClientResponseError as e:
444
- error_text = f"Error {e.status}: {e.message}"
445
- try:
446
- error_response = await e.response.text()
447
- cleaned_error = cls.clean_response(error_response)
448
- error_text += f" - {cleaned_error}"
449
- except Exception:
450
- pass
451
- yield error_text
452
- except Exception as e:
453
- yield f"Unexpected error during /chat/{chat_id} request: {str(e)}"
454
 
455
  # FastAPI app setup
456
  app = FastAPI()
@@ -502,39 +179,11 @@ class ChatRequest(BaseModel):
502
  frequency_penalty: Optional[float] = 0.0
503
  logit_bias: Optional[Dict[str, float]] = None
504
  user: Optional[str] = None
505
- websearch: Optional[bool] = False # Custom parameter
506
 
507
  class TokenizerRequest(BaseModel):
508
  text: str
509
 
510
- def calculate_estimated_cost(prompt_tokens: int, completion_tokens: int) -> float:
511
- """
512
- Calculate the estimated cost based on the number of tokens.
513
- Replace the pricing below with your actual pricing model.
514
- """
515
- # Example pricing: $0.00000268 per token
516
- cost_per_token = 0.00000268
517
- return round((prompt_tokens + completion_tokens) * cost_per_token, 8)
518
-
519
- def create_response(content: str, model: str, finish_reason: Optional[str] = None) -> Dict[str, Any]:
520
- return {
521
- "id": f"chatcmpl-{uuid.uuid4()}",
522
- "object": "chat.completion",
523
- "created": int(datetime.now().timestamp()),
524
- "model": model,
525
- "choices": [
526
- {
527
- "index": 0,
528
- "message": {
529
- "role": "assistant",
530
- "content": content
531
- },
532
- "finish_reason": finish_reason
533
- }
534
- ],
535
- "usage": None, # To be filled in non-streaming responses
536
- }
537
-
538
  @app.post("/v1/chat/completions", dependencies=[Depends(rate_limiter_per_ip)])
539
  async def chat_completions(request: ChatRequest, req: Request, api_key: str = Depends(get_api_key)):
540
  client_ip = req.client.host
@@ -552,55 +201,41 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
552
  # Process the request with actual message content, but don't log it
553
  async_generator = Blackbox.create_async_generator(
554
  model=request.model,
555
- messages=[{"role": msg.role, "content": msg.content} for msg in request.messages], # Actual message content used here
556
- websearch=request.websearch
557
  )
558
 
559
  if request.stream:
560
  async def generate():
561
  try:
562
  assistant_content = ""
563
- for chunk in async_generator:
564
- if isinstance(chunk, ImageResponseData):
565
  # Handle image responses if necessary
566
  image_markdown = f"![image]({chunk.images})\n"
567
  assistant_content += image_markdown
568
- response_chunk = {
569
- "id": f"chatcmpl-{uuid.uuid4()}",
570
- "object": "chat.completion.chunk", # Change to 'chat.completion.chunk'
571
- "created": int(datetime.now().timestamp()),
572
- "model": request.model,
573
- "choices": [
574
- {
575
- "index": 0,
576
- "delta": {"role": "assistant", "content": " "}, # Initial space or any starter
577
- "finish_reason": None
578
- }
579
- ]
580
- }
581
- yield f"data: {json.dumps(response_chunk)}\n\n"
582
- response_chunk["choices"][0]["delta"]["content"] = image_markdown.strip()
583
- yield f"data: {json.dumps(response_chunk)}\n\n"
584
  else:
585
  assistant_content += chunk
586
  # Yield the chunk as a partial choice
587
  response_chunk = {
588
  "id": f"chatcmpl-{uuid.uuid4()}",
589
- "object": "chat.completion.chunk", # Change to 'chat.completion.chunk'
590
  "created": int(datetime.now().timestamp()),
591
  "model": request.model,
592
  "choices": [
593
  {
594
  "index": 0,
595
- "delta": {"role": "assistant", "content": chunk},
596
- "finish_reason": None
597
  }
598
- ]
 
599
  }
600
- yield f"data: {json.dumps(response_chunk)}\n\n"
601
 
602
  # After all chunks are sent, send the final message with finish_reason
603
- prompt_tokens = sum(len(msg.content.split()) for msg in request.messages)
604
  completion_tokens = len(assistant_content.split())
605
  total_tokens = prompt_tokens + completion_tokens
606
  estimated_cost = calculate_estimated_cost(prompt_tokens, completion_tokens)
@@ -637,11 +272,11 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
637
  error_response = {"error": str(e)}
638
  yield f"data: {json.dumps(error_response)}\n\n"
639
 
640
- return StreamingResponse(generate(), media_type="text/event-stream") # Use SSE media type
641
  else:
642
  response_content = ""
643
  async for chunk in async_generator:
644
- if isinstance(chunk, ImageResponseData):
645
  response_content += f"![image]({chunk.images})\n"
646
  else:
647
  response_content += chunk
 
1
+ # main.py
2
+
3
  import os
4
  import re
5
  import random
 
14
 
15
  from datetime import datetime
16
 
17
+ from aiohttp import ClientSession, ClientTimeout, ClientError
18
  from fastapi import FastAPI, HTTPException, Request, Depends, Header
19
  from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
20
  from pydantic import BaseModel
21
 
22
+ from blackbox import Blackbox, ImageResponse # Import the new Blackbox class
23
+
24
  # Configure logging
25
  logging.basicConfig(
26
  level=logging.INFO,
 
41
  # Process available models
42
  if AVAILABLE_MODELS:
43
  AVAILABLE_MODELS = [model.strip() for model in AVAILABLE_MODELS.split(',') if model.strip()]
44
+ Blackbox.models = [model for model in Blackbox.models if model in AVAILABLE_MODELS]
45
  else:
46
  AVAILABLE_MODELS = [] # If empty, all models are available
47
 
 
101
  self.message = f"The model '{model}' is currently not working. Please try another model or wait for it to be fixed."
102
  super().__init__(self.message)
103
 
104
+ def calculate_estimated_cost(prompt_tokens: int, completion_tokens: int) -> float:
105
+ """
106
+ Calculate the estimated cost based on the number of tokens.
107
+ Replace the pricing below with your actual pricing model.
108
+ """
109
+ # Example pricing: $0.00000268 per token
110
+ cost_per_token = 0.00000268
111
+ return round((prompt_tokens + completion_tokens) * cost_per_token, 8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ def create_response(content: str, model: str, finish_reason: Optional[str] = None) -> Dict[str, Any]:
114
+ return {
115
+ "id": f"chatcmpl-{uuid.uuid4()}",
116
+ "object": "chat.completion",
117
+ "created": int(datetime.now().timestamp()),
118
+ "model": model,
119
+ "choices": [
120
  {
121
+ "index": 0,
122
+ "message": {
123
+ "role": "assistant",
124
+ "content": content
125
+ },
126
+ "finish_reason": finish_reason
127
+ }
128
+ ],
129
+ "usage": None, # To be filled in non-streaming responses
130
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  # FastAPI app setup
133
  app = FastAPI()
 
179
  frequency_penalty: Optional[float] = 0.0
180
  logit_bias: Optional[Dict[str, float]] = None
181
  user: Optional[str] = None
182
+ webSearchMode: Optional[bool] = False # Custom parameter
183
 
184
  class TokenizerRequest(BaseModel):
185
  text: str
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  @app.post("/v1/chat/completions", dependencies=[Depends(rate_limiter_per_ip)])
188
  async def chat_completions(request: ChatRequest, req: Request, api_key: str = Depends(get_api_key)):
189
  client_ip = req.client.host
 
201
  # Process the request with actual message content, but don't log it
202
  async_generator = Blackbox.create_async_generator(
203
  model=request.model,
204
+ messages=[{"role": msg.role, "content": msg.content} for msg in request.messages],
205
+ websearch=request.webSearchMode
206
  )
207
 
208
  if request.stream:
209
  async def generate():
210
  try:
211
  assistant_content = ""
212
+ async for chunk in async_generator:
213
+ if isinstance(chunk, ImageResponse):
214
  # Handle image responses if necessary
215
  image_markdown = f"![image]({chunk.images})\n"
216
  assistant_content += image_markdown
217
+ response_chunk = create_response(image_markdown, request.model, finish_reason=None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  else:
219
  assistant_content += chunk
220
  # Yield the chunk as a partial choice
221
  response_chunk = {
222
  "id": f"chatcmpl-{uuid.uuid4()}",
223
+ "object": "chat.completion.chunk",
224
  "created": int(datetime.now().timestamp()),
225
  "model": request.model,
226
  "choices": [
227
  {
228
  "index": 0,
229
+ "delta": {"content": chunk, "role": "assistant"},
230
+ "finish_reason": None,
231
  }
232
+ ],
233
+ "usage": None, # Usage can be updated if you track tokens in real-time
234
  }
235
+ yield f"data: {json.dumps(response_chunk)}\n\n"
236
 
237
  # After all chunks are sent, send the final message with finish_reason
238
+ prompt_tokens = sum(len(msg['content'].split()) for msg in request.messages)
239
  completion_tokens = len(assistant_content.split())
240
  total_tokens = prompt_tokens + completion_tokens
241
  estimated_cost = calculate_estimated_cost(prompt_tokens, completion_tokens)
 
272
  error_response = {"error": str(e)}
273
  yield f"data: {json.dumps(error_response)}\n\n"
274
 
275
+ return StreamingResponse(generate(), media_type="text/event-stream")
276
  else:
277
  response_content = ""
278
  async for chunk in async_generator:
279
+ if isinstance(chunk, ImageResponse):
280
  response_content += f"![image]({chunk.images})\n"
281
  else:
282
  response_content += chunk