XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Improve gradient in gui, show only models with enabled providers (#2492)

- **Cloudflare Provider**: Added error handling for missing requirements when fetching arguments. - **Copilot Provider**: Updated the prompt formatting to use a maximum length function, improving message handling. - **PollinationsAI Provider**: Adjusted the prompt length to a maximum of 5000 characters. - **GitHub Copilot Provider**: Updated to use `ClientSession` for better session management. - **CSS Updates**: Enhanced the gradient styling in the GUI for a more visually appealing interface. - **JavaScript Updates**: Added functionality to toggle search options in the chat interface.

bbb85824
H Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

20 个文件 +254 -199
Modified g4f/Provider/Blackbox.py +1 -0
@@ -287,6 +287,7 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
287 287 return
288 288
289 289 response_text = re.sub(r'Generated by BLACKBOX.AI, try unlimited chat https://www.blackbox.ai', '', response_text, flags=re.DOTALL)
290 response_text = re.sub(r'and for API requests replace https://www.blackbox.ai with https://api.blackbox.ai', '', response_text, flags=re.DOTALL)
290 291
291 292 json_match = re.search(r'\$~~~\$(.*?)\$~~~\$', response_text, re.DOTALL)
292 293 if json_match:
Modified g4f/Provider/Cloudflare.py +6 -3
@@ -5,8 +5,8 @@ import json
5 5
6 6 from ..typing import AsyncResult, Messages, Cookies
7 7 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin, get_running_loop
8 from ..requests import Session, StreamSession, get_args_from_nodriver, raise_for_status, merge_cookies
9 from ..errors import ResponseStatusError
8 from ..requests import Session, StreamSession, get_args_from_nodriver, raise_for_status, merge_cookies, DEFAULT_HEADERS
9 from ..errors import ResponseStatusError, MissingRequirementsError
10 10
11 11 class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin):
12 12 label = "Cloudflare AI"
@@ -62,7 +62,10 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin):
62 62 **kwargs
63 63 ) -> AsyncResult:
64 64 if cls._args is None:
65 cls._args = await get_args_from_nodriver(cls.url, proxy, timeout, cookies)
65 try:
66 cls._args = await get_args_from_nodriver(cls.url, proxy, timeout, cookies)
67 except MissingRequirementsError:
68 cls._args = {"headers": DEFAULT_HEADERS, cookies: {}}
66 69 model = cls.get_model(model)
67 70 data = {
68 71 "messages": messages,
Modified g4f/Provider/Copilot.py +2 -11
@@ -18,7 +18,7 @@ except ImportError:
18 18 has_nodriver = False
19 19
20 20 from .base_provider import AbstractProvider, ProviderModelMixin, BaseConversation
21 from .helper import format_prompt
21 from .helper import format_prompt_max_length
22 22 from ..typing import CreateResult, Messages, ImagesType
23 23 from ..errors import MissingRequirementsError, NoValidHarFileError
24 24 from ..requests.raise_for_status import raise_for_status
@@ -120,16 +120,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
120 120 conversation_id = response.json().get("id")
121 121 if return_conversation:
122 122 yield Conversation(conversation_id)
123 prompt = format_prompt(messages)
124 if len(prompt) > 10000:
125 if len(messages) > 6:
126 prompt = format_prompt(messages[:3] + messages[-3:])
127 if len(prompt) > 10000:
128 if len(messages) > 2:
129 prompt = format_prompt(messages[:2] + messages[-1:])
130 if len(prompt) > 10000:
131 prompt = messages[-1]["content"]
132 debug.log(f"Copilot: Trim messages to: {len(prompt)}")
123 prompt = format_prompt_max_length(messages, 10000)
133 124 debug.log(f"Copilot: Created conversation: {conversation_id}")
134 125 else:
135 126 conversation_id = conversation.conversation_id
Modified g4f/Provider/PollinationsAI.py +2 -2
@@ -10,7 +10,7 @@ from ..image import ImageResponse
10 10 from ..requests.raise_for_status import raise_for_status
11 11 from ..requests.aiohttp import get_connector
12 12 from .needs_auth.OpenaiAPI import OpenaiAPI
13 from .helper import format_prompt
13 from .helper import format_prompt_max_length
14 14
15 15 class PollinationsAI(OpenaiAPI):
16 16 label = "Pollinations AI"
@@ -97,7 +97,7 @@ class PollinationsAI(OpenaiAPI):
97 97 async def _generate_text(cls, model: str, messages: Messages, api_key: str = None, proxy: str = None, **kwargs):
98 98 if api_key is None:
99 99 async with ClientSession(connector=get_connector(proxy=proxy), headers=cls.headers) as session:
100 prompt = format_prompt(messages)
100 prompt = format_prompt_max_length(messages, 5000)
101 101 async with session.get(f"https://text.pollinations.ai/{quote(prompt)}?model={quote(model)}") as response:
102 102 await raise_for_status(response)
103 103 async for line in response.content.iter_any():
Modified g4f/Provider/needs_auth/Gemini.py +4 -1
@@ -207,7 +207,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
207 207 ) as session:
208 208 if not cls._snlm0e:
209 209 await cls.fetch_snlm0e(session, cls._cookies) if cls._cookies else None
210 inner_data = json.dumps([None, params["text"], "de-DE", None, 2])
210 inner_data = json.dumps([None, params["text"], "en-US", None, 2])
211 211 async with session.post(
212 212 "https://gemini.google.com/_/BardChatUi/data/batchexecute",
213 213 data={
@@ -334,8 +334,11 @@ async def iter_filter_base64(response_iter: AsyncIterator[bytes]) -> AsyncIterat
334 334
335 335 async def iter_base64_decode(response_iter: AsyncIterator[bytes]) -> AsyncIterator[bytes]:
336 336 buffer = b""
337 rest = 0
337 338 async for chunk in response_iter:
338 339 chunk = buffer + chunk
339 340 rest = len(chunk) % 4
340 341 buffer = chunk[-rest:]
341 342 yield base64.b64decode(chunk[:-rest])
343 if rest > 0:
344 yield base64.b64decode(buffer+rest*b"=")
Modified g4f/Provider/needs_auth/GithubCopilot.py +18 -7
@@ -1,11 +1,12 @@
1 1 from __future__ import annotations
2 2
3 3 import json
4 from aiohttp import ClientSession
4 5
5 6 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, BaseConversation
6 7 from ...typing import AsyncResult, Messages, Cookies
7 8 from ...requests.raise_for_status import raise_for_status
8 from ...requests import StreamSession
9 from ...requests.aiohttp import get_connector
9 10 from ...providers.helper import format_prompt
10 11 from ...cookies import get_cookies
11 12
@@ -16,6 +17,7 @@ class Conversation(BaseConversation):
16 17 self.conversation_id = conversation_id
17 18
18 19 class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin):
20 label = "GitHub Copilot"
19 21 url = "https://github.com/copilot"
20 22
21 23 working = True
@@ -42,13 +44,22 @@ class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin):
42 44 if not model:
43 45 model = cls.default_model
44 46 if cookies is None:
45 cookies = get_cookies(".github.com")
46 async with StreamSession(
47 proxy=proxy,
48 impersonate="chrome",
47 cookies = get_cookies("github.com")
48 async with ClientSession(
49 connector=get_connector(proxy=proxy),
49 50 cookies=cookies,
50 51 headers={
51 "GitHub-Verified-Fetch": "true",
52 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0',
53 'Accept-Language': 'en-US,en;q=0.5',
54 'Referer': 'https://github.com/copilot',
55 'Content-Type': 'application/json',
56 'GitHub-Verified-Fetch': 'true',
57 'X-Requested-With': 'XMLHttpRequest',
58 'Origin': 'https://github.com',
59 'Connection': 'keep-alive',
60 'Sec-Fetch-Dest': 'empty',
61 'Sec-Fetch-Mode': 'cors',
62 'Sec-Fetch-Site': 'same-origin',
52 63 }
53 64 ) as session:
54 65 headers = {}
@@ -87,7 +98,7 @@ class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin):
87 98 json=json_data,
88 99 headers=headers
89 100 ) as response:
90 async for line in response.iter_lines():
101 async for line in response.content:
91 102 if line.startswith(b"data: "):
92 103 data = json.loads(line[6:])
93 104 if data.get("type") == "content":
Modified g4f/Provider/needs_auth/OpenaiChat.py +8 -6
@@ -352,7 +352,10 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
352 352 json={"p": get_requirements_token(RequestConfig.proof_token) if RequestConfig.proof_token else None},
353 353 headers=cls._headers
354 354 ) as response:
355 cls._update_request_args(session)
355 if response.status == 401:
356 cls._headers = cls._api_key = None
357 else:
358 cls._update_request_args(session)
356 359 await raise_for_status(response)
357 360 chat_requirements = await response.json()
358 361 need_turnstile = chat_requirements.get("turnstile", {}).get("required", False)
@@ -525,7 +528,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
525 528 cls._set_api_key(RequestConfig.access_token)
526 529 except NoValidHarFileError:
527 530 if has_nodriver:
528 if RequestConfig.access_token is None:
531 if cls._api_key is None:
529 532 await cls.nodriver_auth(proxy)
530 533 else:
531 534 raise
@@ -545,7 +548,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
545 548 if "OpenAI-Sentinel-Turnstile-Token" in event.request.headers:
546 549 RequestConfig.turnstile_token = event.request.headers["OpenAI-Sentinel-Turnstile-Token"]
547 550 if "Authorization" in event.request.headers:
548 RequestConfig.access_token = event.request.headers["Authorization"].split()[-1]
551 cls._set_api_key(event.request.headers["Authorization"].split()[-1])
549 552 elif event.request.url == arkose_url:
550 553 RequestConfig.arkose_request = arkReq(
551 554 arkURL=event.request.url,
@@ -560,13 +563,13 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
560 563 user_agent = await page.evaluate("window.navigator.userAgent")
561 564 await page.select("#prompt-textarea", 240)
562 565 while True:
563 if RequestConfig.access_token:
566 if cls._api_key is not None:
564 567 break
565 568 body = await page.evaluate("JSON.stringify(window.__remixContext)")
566 569 if body:
567 570 match = re.search(r'"accessToken":"(.*?)"', body)
568 571 if match:
569 RequestConfig.access_token = match.group(1)
572 cls._set_api_key(match.group(1))
570 573 break
571 574 await asyncio.sleep(1)
572 575 while True:
@@ -578,7 +581,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
578 581 RequestConfig.cookies[c.name] = c.value
579 582 await page.close()
580 583 cls._create_request_args(RequestConfig.cookies, RequestConfig.headers, user_agent=user_agent)
581 cls._set_api_key(RequestConfig.access_token)
582 584
583 585 @staticmethod
584 586 def get_default_headers() -> dict:
Modified g4f/__init__.py +14 -1
@@ -42,7 +42,9 @@ class ChatCompletion:
42 42 if "proxy" not in kwargs:
43 43 proxy = os.environ.get("G4F_PROXY")
44 44 if proxy:
45 kwargs['proxy'] = proxy
45 kwargs["proxy"] = proxy
46 if ignore_stream:
47 kwargs["ignore_stream"] = True
46 48
47 49 result = provider.create_completion(model, messages, stream=stream, **kwargs)
48 50
@@ -53,9 +55,20 @@ class ChatCompletion:
53 55 messages : Messages,
54 56 provider : Union[ProviderType, str, None] = None,
55 57 stream : bool = False,
58 image : ImageType = None,
59 image_name: Optional[str] = None,
60 ignore_stream: bool = False,
56 61 ignore_working: bool = False,
57 62 **kwargs) -> Union[AsyncResult, Coroutine[str]]:
58 63 model, provider = get_model_and_provider(model, provider, False, ignore_working)
64 if image is not None:
65 kwargs["images"] = [(image, image_name)]
66 if "proxy" not in kwargs:
67 proxy = os.environ.get("G4F_PROXY")
68 if proxy:
69 kwargs["proxy"] = proxy
70 if ignore_stream:
71 kwargs["ignore_stream"] = True
59 72
60 73 if stream:
61 74 if hasattr(provider, "create_async_generator"):
Modified g4f/api/__init__.py +16 -75
@@ -26,7 +26,6 @@ from fastapi.encoders import jsonable_encoder
26 26 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, HTTPBasic
27 27 from fastapi.middleware.cors import CORSMiddleware
28 28 from starlette.responses import FileResponse
29 from pydantic import BaseModel, Field
30 29 from typing import Union, Optional, List
31 30 try:
32 31 from typing import Annotated
@@ -40,11 +39,16 @@ from g4f.client import AsyncClient, ChatCompletion, ImagesResponse, convert_to_p
40 39 from g4f.providers.response import BaseConversation
41 40 from g4f.client.helper import filter_none
42 41 from g4f.image import is_accepted_format, is_data_uri_an_image, images_dir
43 from g4f.typing import Messages
44 from g4f.errors import ProviderNotFoundError, ModelNotFoundError, MissingAuthError
42 from g4f.errors import ProviderNotFoundError, ModelNotFoundError, MissingAuthError, NoValidHarFileError
45 43 from g4f.cookies import read_cookie_files, get_cookies_dir
46 44 from g4f.Provider import ProviderType, ProviderUtils, __providers__
47 45 from g4f.gui import get_gui_app
46 from .stubs import (
47 ChatCompletionsConfig, ImageGenerationConfig,
48 ProviderResponseModel, ModelResponseModel,
49 ErrorResponseModel, ProviderResponseDetailModel,
50 FileResponseModel
51 )
48 52
49 53 logger = logging.getLogger(__name__)
50 54
@@ -64,18 +68,10 @@ def create_app():
64 68
65 69 api = Api(app)
66 70
67 if AppConfig.gui:
68 @app.get("/")
69 async def home():
70 return HTMLResponse(f'g4f v-{g4f.version.utils.current_version}:<br><br>'
71 'Start to chat: <a href="/chat/">/chat/</a><br>'
72 'Open Swagger UI at: '
73 '<a href="/docs">/docs</a>')
74
75 71 api.register_routes()
76 72 api.register_authorization()
77 73 api.register_validation_exception_handler()
78
74
79 75 if AppConfig.gui:
80 76 gui_app = WSGIMiddleware(get_gui_app())
81 77 app.mount("/", gui_app)
@@ -100,63 +96,6 @@ def create_app_with_gui_and_debug():
100 96 AppConfig.gui = True
101 97 return create_app()
102 98
103 class ChatCompletionsConfig(BaseModel):
104 messages: Messages = Field(examples=[[{"role": "system", "content": ""}, {"role": "user", "content": ""}]])
105 model: str = Field(default="")
106 provider: Optional[str] = None
107 stream: bool = False
108 image: Optional[str] = None
109 image_name: Optional[str] = None
110 images: Optional[list[tuple[str, str]]] = None
111 temperature: Optional[float] = None
112 max_tokens: Optional[int] = None
113 stop: Union[list[str], str, None] = None
114 api_key: Optional[str] = None
115 web_search: Optional[bool] = None
116 proxy: Optional[str] = None
117 conversation_id: Optional[str] = None
118 history_disabled: Optional[bool] = None
119 auto_continue: Optional[bool] = None
120 timeout: Optional[int] = None
121
122 class ImageGenerationConfig(BaseModel):
123 prompt: str
124 model: Optional[str] = None
125 provider: Optional[str] = None
126 response_format: Optional[str] = None
127 api_key: Optional[str] = None
128 proxy: Optional[str] = None
129
130 class ProviderResponseModel(BaseModel):
131 id: str
132 object: str = "provider"
133 created: int
134 url: Optional[str]
135 label: Optional[str]
136
137 class ProviderResponseDetailModel(ProviderResponseModel):
138 models: list[str]
139 image_models: list[str]
140 vision_models: list[str]
141 params: list[str]
142
143 class ModelResponseModel(BaseModel):
144 id: str
145 object: str = "model"
146 created: int
147 owned_by: Optional[str]
148
149 class ErrorResponseModel(BaseModel):
150 error: ErrorResponseMessageModel
151 model: Optional[str] = None
152 provider: Optional[str] = None
153
154 class ErrorResponseMessageModel(BaseModel):
155 message: str
156
157 class FileResponseModel(BaseModel):
158 filename: str
159
160 99 class ErrorResponse(Response):
161 100 media_type = "application/json"
162 101
@@ -198,7 +137,7 @@ class Api:
198 137 security = HTTPBearer(auto_error=False)
199 138 basic_security = HTTPBasic()
200 139
201 async def get_username(self, request: Request):
140 async def get_username(self, request: Request) -> str:
202 141 credentials = await self.basic_security(request)
203 142 current_password_bytes = credentials.password.encode()
204 143 is_correct_password = secrets.compare_digest(
@@ -222,13 +161,13 @@ class Api:
222 161 user_g4f_api_key = await self.get_g4f_api_key(request)
223 162 except HTTPException:
224 163 user_g4f_api_key = None
225 if request.url.path.startswith("/v1"):
164 path = request.url.path
165 if path.startswith("/v1"):
226 166 if user_g4f_api_key is None:
227 167 return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED)
228 168 if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
229 169 return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
230 170 else:
231 path = request.url.path
232 171 if user_g4f_api_key is not None and path.startswith("/images/"):
233 172 if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
234 173 return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
@@ -261,6 +200,8 @@ class Api:
261 200 def register_routes(self):
262 201 @self.app.get("/")
263 202 async def read_root():
203 if AppConfig.gui:
204 return RedirectResponse("/chat/", 302)
264 205 return RedirectResponse("/v1", 302)
265 206
266 207 @self.app.get("/v1")
@@ -336,6 +277,7 @@ class Api:
336 277 except ValueError as e:
337 278 example = json.dumps({"images": [["data:image/jpeg;base64,...", "filename"]]})
338 279 return ErrorResponse.from_message(f'The image you send must be a data URI. Example: {example}', status_code=HTTP_422_UNPROCESSABLE_ENTITY)
280
339 281 # Create the completion response
340 282 response = self.client.chat.completions.create(
341 283 **filter_none(
@@ -379,7 +321,7 @@ class Api:
379 321 except (ModelNotFoundError, ProviderNotFoundError) as e:
380 322 logger.exception(e)
381 323 return ErrorResponse.from_exception(e, config, HTTP_404_NOT_FOUND)
382 except MissingAuthError as e:
324 except (MissingAuthError, NoValidHarFileError) as e:
383 325 logger.exception(e)
384 326 return ErrorResponse.from_exception(e, config, HTTP_401_UNAUTHORIZED)
385 327 except Exception as e:
@@ -392,7 +334,6 @@ class Api:
392 334 HTTP_404_NOT_FOUND: {"model": ErrorResponseModel},
393 335 HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponseModel},
394 336 }
395
396 337 @self.app.post("/v1/images/generate", responses=responses)
397 338 @self.app.post("/v1/images/generations", responses=responses)
398 339 async def generate_image(
@@ -564,4 +505,4 @@ def run_api(
564 505 use_colors=use_colors,
565 506 factory=True,
566 507 reload=reload
567 )
508 )
Added g4f/api/stubs.py +63 -0
@@ -0,0 +1,63 @@
1 from __future__ import annotations
2
3 from pydantic import BaseModel, Field
4 from typing import Union, Optional
5
6 from g4f.typing import Messages
7
8 class ChatCompletionsConfig(BaseModel):
9 messages: Messages = Field(examples=[[{"role": "system", "content": ""}, {"role": "user", "content": ""}]])
10 model: str = Field(default="")
11 provider: Optional[str] = None
12 stream: bool = False
13 image: Optional[str] = None
14 image_name: Optional[str] = None
15 images: Optional[list[tuple[str, str]]] = None
16 temperature: Optional[float] = None
17 max_tokens: Optional[int] = None
18 stop: Union[list[str], str, None] = None
19 api_key: Optional[str] = None
20 web_search: Optional[bool] = None
21 proxy: Optional[str] = None
22 conversation_id: Optional[str] = None
23 history_disabled: Optional[bool] = None
24 auto_continue: Optional[bool] = None
25 timeout: Optional[int] = None
26
27 class ImageGenerationConfig(BaseModel):
28 prompt: str
29 model: Optional[str] = None
30 provider: Optional[str] = None
31 response_format: Optional[str] = None
32 api_key: Optional[str] = None
33 proxy: Optional[str] = None
34
35 class ProviderResponseModel(BaseModel):
36 id: str
37 object: str = "provider"
38 created: int
39 url: Optional[str]
40 label: Optional[str]
41
42 class ProviderResponseDetailModel(ProviderResponseModel):
43 models: list[str]
44 image_models: list[str]
45 vision_models: list[str]
46 params: list[str]
47
48 class ModelResponseModel(BaseModel):
49 id: str
50 object: str = "model"
51 created: int
52 owned_by: Optional[str]
53
54 class ErrorResponseModel(BaseModel):
55 error: ErrorResponseMessageModel
56 model: Optional[str] = None
57 provider: Optional[str] = None
58
59 class ErrorResponseMessageModel(BaseModel):
60 message: str
61
62 class FileResponseModel(BaseModel):
63 filename: str
Modified g4f/client/__init__.py +10 -8
@@ -16,7 +16,6 @@ from ..errors import NoImageResponseError
16 16 from ..providers.retry_provider import IterListProvider
17 17 from ..providers.asyncio import to_sync_generator, async_generator_to_list
18 18 from ..Provider.needs_auth import BingCreateImages, OpenaiAccount
19 from ..image import to_bytes
20 19 from .stubs import ChatCompletion, ChatCompletionChunk, Image, ImagesResponse
21 20 from .image_models import ImageModels
22 21 from .types import IterResponse, ImageProvider, Client as BaseClient
@@ -59,7 +58,7 @@ def iter_response(
59 58 elif isinstance(chunk, BaseConversation):
60 59 yield chunk
61 60 continue
62 elif isinstance(chunk, SynthesizeData) or chunk is None:
61 elif isinstance(chunk, SynthesizeData) or not chunk:
63 62 continue
64 63
65 64 chunk = str(chunk)
@@ -122,7 +121,7 @@ async def async_iter_response(
122 121 elif isinstance(chunk, BaseConversation):
123 122 yield chunk
124 123 continue
125 elif isinstance(chunk, SynthesizeData) or chunk is None:
124 elif isinstance(chunk, SynthesizeData) or not chunk:
126 125 continue
127 126
128 127 chunk = str(chunk)
@@ -214,6 +213,8 @@ class Completions:
214 213 stop = [stop] if isinstance(stop, str) else stop
215 214 if image is not None:
216 215 kwargs["images"] = [(image, image_name)]
216 if ignore_stream:
217 kwargs["ignore_stream"] = True
217 218 response = provider.create_completion(
218 219 model,
219 220 messages,
@@ -330,7 +331,6 @@ class Images:
330 331 model: str,
331 332 prompt: str,
332 333 prompt_prefix: str = "Generate a image: ",
333 image: ImageType = None,
334 334 **kwargs
335 335 ) -> ImageResponse:
336 336 messages = [{"role": "user", "content": f"{prompt_prefix}{prompt}"}]
@@ -341,7 +341,6 @@ class Images:
341 341 messages,
342 342 stream=True,
343 343 prompt=prompt,
344 image=image,
345 344 **kwargs
346 345 ):
347 346 if isinstance(item, ImageResponse):
@@ -353,7 +352,6 @@ class Images:
353 352 messages,
354 353 True,
355 354 prompt=prompt,
356 image=image,
357 355 **kwargs
358 356 ):
359 357 if isinstance(item, ImageResponse):
@@ -389,20 +387,22 @@ class Images:
389 387 if proxy is None:
390 388 proxy = self.client.proxy
391 389 prompt = "create a variation of this image"
390 if image is not None:
391 kwargs["images"] = [(image, None)]
392 392
393 393 e = None
394 394 response = None
395 395 if isinstance(provider_handler, IterListProvider):
396 396 for provider in provider_handler.providers:
397 397 try:
398 response = await self._generate_image_response(provider, provider.__name__, model, prompt, image=image, **kwargs)
398 response = await self._generate_image_response(provider, provider.__name__, model, prompt, **kwargs)
399 399 if response is not None:
400 400 provider_name = provider.__name__
401 401 break
402 402 except Exception as e:
403 403 debug.log(f"Image provider {provider.__name__}: {e}")
404 404 else:
405 response = await self._generate_image_response(provider_handler, provider_name, model, prompt, image=image, **kwargs)
405 response = await self._generate_image_response(provider_handler, provider_name, model, prompt, **kwargs)
406 406
407 407 if isinstance(response, ImageResponse):
408 408 return await self._process_image_response(response, response_format, proxy, model, provider_name)
@@ -494,6 +494,8 @@ class AsyncCompletions:
494 494 stop = [stop] if isinstance(stop, str) else stop
495 495 if image is not None:
496 496 kwargs["images"] = [(image, image_name)]
497 if ignore_stream:
498 kwargs["ignore_stream"] = True
497 499 if hasattr(provider, "create_async_generator"):
498 500 create_handler = provider.create_async_generator
499 501 else:
Modified g4f/cookies.py +1 -0
@@ -59,6 +59,7 @@ DOMAINS = [
59 59 "chat.reka.ai",
60 60 "chatgpt.com",
61 61 ".cerebras.ai",
62 "github.com",
62 63 ]
63 64
64 65 if has_browser_cookie3 and os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
Modified g4f/gui/client/index.html +7 -0
Modified g4f/gui/client/static/css/style.css +39 -66
Modified g4f/gui/client/static/js/chat.v1.js +15 -1
Modified g4f/gui/server/api.py +3 -2
Modified g4f/models.py +4 -3
Modified g4f/providers/helper.py +15 -0
Modified g4f/providers/retry_provider.py +4 -2
Modified g4f/requests/raise_for_status.py +22 -11