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

XFEstudio/gpt4free

Improve image generation in OpenaiChat and Gemini

78c20c08
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

7 个文件 +98 -85
Modified g4f/Provider/needs_auth/Gemini.py +18 -26
@@ -76,9 +76,8 @@ class Gemini(AsyncGeneratorProvider):
76 76 page = await browser.get(f"{cls.url}/app")
77 77 await page.select("div.ql-editor.textarea", 240)
78 78 cookies = {}
79 for c in await page.browser.cookies.get_all():
80 if c.domain.endswith(".google.com"):
81 cookies[c.name] = c.value
79 for c in await page.send(nodriver.cdp.network.get_cookies([cls.url])):
80 cookies[c.name] = c.value
82 81 await page.close()
83 82 cls._cookies = cookies
84 83
@@ -92,7 +91,6 @@ class Gemini(AsyncGeneratorProvider):
92 91 connector: BaseConnector = None,
93 92 image: ImageType = None,
94 93 image_name: str = None,
95 response_format: str = None,
96 94 return_conversation: bool = False,
97 95 conversation: Conversation = None,
98 96 language: str = "en",
@@ -113,7 +111,7 @@ class Gemini(AsyncGeneratorProvider):
113 111 async for chunk in cls.nodriver_login(proxy):
114 112 yield chunk
115 113 except Exception as e:
116 raise MissingAuthError('Missing "__Secure-1PSID" cookie', e)
114 raise MissingAuthError('Missing or invalid "__Secure-1PSID" cookie', e)
117 115 if not cls._snlm0e:
118 116 if cls._cookies is None or "__Secure-1PSID" not in cls._cookies:
119 117 raise MissingAuthError('Missing "__Secure-1PSID" cookie')
@@ -153,7 +151,7 @@ class Gemini(AsyncGeneratorProvider):
153 151 ) as response:
154 152 await raise_for_status(response)
155 153 image_prompt = response_part = None
156 last_content_len = 0
154 last_content = ""
157 155 async for line in response.content:
158 156 try:
159 157 try:
@@ -171,32 +169,26 @@ class Gemini(AsyncGeneratorProvider):
171 169 yield Conversation(response_part[1][0], response_part[1][1], response_part[4][0][0])
172 170 content = response_part[4][0][1][0]
173 171 except (ValueError, KeyError, TypeError, IndexError) as e:
174 print(f"{cls.__name__}:{e.__class__.__name__}:{e}")
172 debug.log(f"{cls.__name__}:{e.__class__.__name__}:{e}")
175 173 continue
176 174 match = re.search(r'\[Imagen of (.*?)\]', content)
177 175 if match:
178 176 image_prompt = match.group(1)
179 177 content = content.replace(match.group(0), '')
180 yield content[last_content_len:]
181 last_content_len = len(content)
182 if image_prompt:
183 try:
184 images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
185 if response_format == "b64_json":
178 pattern = r"http://googleusercontent.com/image_generation_content/\d+"
179 content = re.sub(pattern, "", content)
180 if last_content and content.startswith(last_content):
181 yield content[len(last_content):]
182 else:
183 yield content
184 last_content = content
185 if image_prompt:
186 try:
187 images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
188 image_prompt = image_prompt.replace("a fake image", "")
186 189 yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
187 else:
188 resolved_images = []
189 preview = []
190 for image in images:
191 async with client.get(image, allow_redirects=False) as fetch:
192 image = fetch.headers["location"]
193 async with client.get(image, allow_redirects=False) as fetch:
194 image = fetch.headers["location"]
195 resolved_images.append(image)
196 preview.append(image.replace('=s512', '=s200'))
197 yield ImageResponse(resolved_images, image_prompt, {"orginal_links": images, "preview": preview})
198 except TypeError:
199 pass
190 except TypeError:
191 pass
200 192
201 193 @classmethod
202 194 async def synthesize(cls, params: dict, proxy: str = None) -> AsyncIterator[bytes]:
Modified g4f/Provider/needs_auth/OpenaiChat.py +30 -25
@@ -184,7 +184,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
184 184 "content": {"content_type": "text", "parts": [message["content"]]},
185 185 "id": str(uuid.uuid4()),
186 186 "create_time": int(time.time()),
187 "id": str(uuid.uuid4()),
188 187 "metadata": {"serialization_metadata": {"custom_symbol_offsets": []}, "system_hints": system_hints},
189 188 } for message in messages]
190 189
@@ -295,8 +294,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
295 294 Raises:
296 295 RuntimeError: If an error occurs during processing.
297 296 """
298 if model == cls.default_image_model:
299 model = cls.default_model
300 297 if cls.needs_auth:
301 298 await cls.login(proxy)
302 299
@@ -308,9 +305,9 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
308 305 if not cls.needs_auth:
309 306 cls._create_request_args(cookies)
310 307 RequestConfig.proof_token = get_config(cls._headers.get("user-agent"))
311 async with session.get(cls.url, headers=INIT_HEADERS) as response:
312 cls._update_request_args(session)
313 await raise_for_status(response)
308 async with session.get(cls.url, headers=INIT_HEADERS) as response:
309 cls._update_request_args(session)
310 await raise_for_status(response)
314 311 try:
315 312 image_request = await cls.upload_image(session, cls._headers, image, image_name) if image else None
316 313 except Exception as e:
@@ -318,6 +315,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
318 315 debug.log("OpenaiChat: Upload image failed")
319 316 debug.log(f"{e.__class__.__name__}: {e}")
320 317 model = cls.get_model(model)
318 if model == cls.default_image_model:
319 model = cls.default_vision_model
321 320 if conversation is None:
322 321 conversation = Conversation(conversation_id, str(uuid.uuid4()) if parent_id is None else parent_id)
323 322 else:
@@ -363,13 +362,22 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
363 362 "messages": None,
364 363 "parent_message_id": conversation.message_id,
365 364 "model": model,
366 "paragen_cot_summary_display_override": "allow",
365 "timezone_offset_min":-60,
366 "timezone":"Europe/Berlin",
367 367 "history_and_training_disabled": history_disabled and not auto_continue and not return_conversation,
368 "conversation_mode": {"kind":"primary_assistant"},
368 "conversation_mode":{"kind":"primary_assistant","plugin_ids":None},
369 "force_paragen":False,
370 "force_paragen_model_slug":"",
371 "force_rate_limit":False,
372 "reset_rate_limits":False,
369 373 "websocket_request_id": str(uuid.uuid4()),
370 "supported_encodings": ["v1"],
371 "supports_buffering": True,
372 "system_hints": ["search"] if web_search else None
374 "system_hints": ["search"] if web_search else None,
375 "supported_encodings":["v1"],
376 "conversation_origin":None,
377 "client_contextual_info":{"is_dark_mode":False,"time_since_loaded":14,"page_height":578,"page_width":1850,"pixel_ratio":1,"screen_height":1080,"screen_width":1920},
378 "paragen_stream_type_override":None,
379 "paragen_cot_summary_display_override":"allow",
380 "supports_buffering":True
373 381 }
374 382 if conversation.conversation_id is not None:
375 383 data["conversation_id"] = conversation.conversation_id
@@ -408,7 +416,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
408 416 async for line in response.iter_lines():
409 417 async for chunk in cls.iter_messages_line(session, line, conversation):
410 418 yield chunk
411 if not history_disabled:
419 if not history_disabled and RequestConfig.access_token is not None:
412 420 yield SynthesizeData(cls.__name__, {
413 421 "conversation_id": conversation.conversation_id,
414 422 "message_id": conversation.message_id,
@@ -493,9 +501,12 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
493 501 await get_request_config(proxy)
494 502 cls._create_request_args(RequestConfig.cookies, RequestConfig.headers)
495 503 cls._set_api_key(RequestConfig.access_token)
504 if RequestConfig.proof_token is None:
505 RequestConfig.proof_token = get_config(cls._headers.get("user-agent"))
496 506 except NoValidHarFileError:
497 507 if has_nodriver:
498 await cls.nodriver_auth(proxy)
508 if RequestConfig.access_token is None:
509 await cls.nodriver_auth(proxy)
499 510 else:
500 511 raise
501 512
@@ -527,23 +538,17 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
527 538 await page.send(nodriver.cdp.network.enable())
528 539 page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
529 540 page = await browser.get(cls.url)
530 try:
531 if RequestConfig.access_request_id is not None:
532 body = await page.send(get_response_body(RequestConfig.access_request_id))
533 if isinstance(body, tuple) and body:
534 body = body[0]
535 if body:
536 match = re.search(r'"accessToken":"(.*?)"', body)
537 if match:
538 RequestConfig.access_token = match.group(1)
539 except KeyError:
540 pass
541 body = await page.evaluate("JSON.stringify(window.__remixContext)")
542 if body:
543 match = re.search(r'"accessToken":"(.*?)"', body)
544 if match:
545 RequestConfig.access_token = match.group(1)
541 546 for c in await page.send(nodriver.cdp.network.get_cookies([cls.url])):
542 547 RequestConfig.cookies[c.name] = c.value
543 548 user_agent = await page.evaluate("window.navigator.userAgent")
544 549 await page.select("#prompt-textarea", 240)
545 550 while True:
546 if RequestConfig.proof_token:
551 if RequestConfig.access_token:
547 552 break
548 553 await asyncio.sleep(1)
549 554 await page.close()
Modified g4f/api/__init__.py +4 -4
@@ -112,7 +112,7 @@ class ImageGenerationConfig(BaseModel):
112 112 prompt: str
113 113 model: Optional[str] = None
114 114 provider: Optional[str] = None
115 response_format: str = "url"
115 response_format: Optional[str] = None
116 116 api_key: Optional[str] = None
117 117 proxy: Optional[str] = None
118 118
@@ -370,9 +370,9 @@ class Api:
370 370 model=config.model,
371 371 provider=AppConfig.image_provider if config.provider is None else config.provider,
372 372 **filter_none(
373 response_format = config.response_format,
374 api_key = config.api_key,
375 proxy = config.proxy
373 response_format=config.response_format,
374 api_key=config.api_key,
375 proxy=config.proxy
376 376 )
377 377 )
378 378 for image in response.data:
Modified g4f/client/__init__.py +21 -16
@@ -292,6 +292,7 @@ class Images:
292 292 if proxy is None:
293 293 proxy = self.client.proxy
294 294
295 e = None
295 296 response = None
296 297 if isinstance(provider_handler, IterListProvider):
297 298 for provider in provider_handler.providers:
@@ -300,7 +301,7 @@ class Images:
300 301 if response is not None:
301 302 provider_name = provider.__name__
302 303 break
303 except (MissingAuthError, NoValidHarFileError) as e:
304 except Exception as e:
304 305 debug.log(f"Image provider {provider.__name__}: {e}")
305 306 else:
306 307 response = await self._generate_image_response(provider_handler, provider_name, model, prompt, **kwargs)
@@ -314,6 +315,8 @@ class Images:
314 315 provider_name
315 316 )
316 317 if response is None:
318 if e is not None:
319 raise e
317 320 raise NoImageResponseError(f"No image response from {provider_name}")
318 321 raise NoImageResponseError(f"Unexpected response type: {type(response)}")
319 322
@@ -362,7 +365,7 @@ class Images:
362 365 image: ImageType,
363 366 model: str = None,
364 367 provider: Optional[ProviderType] = None,
365 response_format: str = "url",
368 response_format: Optional[str] = None,
366 369 **kwargs
367 370 ) -> ImagesResponse:
368 371 return asyncio.run(self.async_create_variation(
@@ -374,7 +377,7 @@ class Images:
374 377 image: ImageType,
375 378 model: Optional[str] = None,
376 379 provider: Optional[ProviderType] = None,
377 response_format: str = "url",
380 response_format: Optional[str] = None,
378 381 proxy: Optional[str] = None,
379 382 **kwargs
380 383 ) -> ImagesResponse:
@@ -384,6 +387,7 @@ class Images:
384 387 proxy = self.client.proxy
385 388 prompt = "create a variation of this image"
386 389
390 e = None
387 391 response = None
388 392 if isinstance(provider_handler, IterListProvider):
389 393 # File pointer can be read only once, so we need to convert it to bytes
@@ -394,7 +398,7 @@ class Images:
394 398 if response is not None:
395 399 provider_name = provider.__name__
396 400 break
397 except (MissingAuthError, NoValidHarFileError) as e:
401 except Exception as e:
398 402 debug.log(f"Image provider {provider.__name__}: {e}")
399 403 else:
400 404 response = await self._generate_image_response(provider_handler, provider_name, model, prompt, image=image, **kwargs)
@@ -402,10 +406,11 @@ class Images:
402 406 if isinstance(response, ImageResponse):
403 407 return await self._process_image_response(response, response_format, proxy, model, provider_name)
404 408 if response is None:
409 if e is not None:
410 raise e
405 411 raise NoImageResponseError(f"No image response from {provider_name}")
406 412 raise NoImageResponseError(f"Unexpected response type: {type(response)}")
407 413
408
409 414 async def _process_image_response(
410 415 self,
411 416 response: ImageResponse,
@@ -414,21 +419,21 @@ class Images:
414 419 model: Optional[str] = None,
415 420 provider: Optional[str] = None
416 421 ) -> ImagesResponse:
422 last_provider = get_last_provider(True)
417 423 if response_format == "url":
418 424 # Return original URLs without saving locally
419 425 images = [Image.construct(url=image, revised_prompt=response.alt) for image in response.get_list()]
420 elif response_format == "b64_json":
421 images = await copy_images(response.get_list(), response.options.get("cookies"), proxy)
422 async def process_image_item(image_file: str) -> Image:
423 with open(os.path.join(images_dir, os.path.basename(image_file)), "rb") as file:
424 image_data = base64.b64encode(file.read()).decode()
425 return Image.construct(b64_json=image_data, revised_prompt=response.alt)
426 images = await asyncio.gather(*[process_image_item(image) for image in images])
427 426 else:
428 427 # Save locally for None (default) case
429 images = await copy_images(response.get_list(), response.options.get("cookies"), proxy)
430 images = [Image.construct(url=f"/images/{os.path.basename(image)}", revised_prompt=response.alt) for image in images]
431 last_provider = get_last_provider(True)
428 images = await copy_images(response.get_list(), response.get("cookies"), proxy)
429 if response_format == "b64_json":
430 async def process_image_item(image_file: str) -> Image:
431 with open(os.path.join(images_dir, os.path.basename(image_file)), "rb") as file:
432 image_data = base64.b64encode(file.read()).decode()
433 return Image.construct(b64_json=image_data, revised_prompt=response.alt)
434 images = await asyncio.gather(*[process_image_item(image) for image in images])
435 else:
436 images = [Image.construct(url=f"/images/{os.path.basename(image)}", revised_prompt=response.alt) for image in images]
432 437 return ImagesResponse.construct(
433 438 created=int(time.time()),
434 439 data=images,
@@ -529,7 +534,7 @@ class AsyncImages(Images):
529 534 image: ImageType,
530 535 model: str = None,
531 536 provider: ProviderType = None,
532 response_format: str = "url",
537 response_format: Optional[str] = None,
533 538 **kwargs
534 539 ) -> ImagesResponse:
535 540 return await self.async_create_variation(
Modified g4f/gui/server/api.py +4 -2
@@ -110,8 +110,10 @@ class Api:
110 110 def _create_response_stream(self, kwargs: dict, conversation_id: str, provider: str, download_images: bool = True) -> Iterator:
111 111 def log_handler(text: str):
112 112 debug.logs.append(text)
113 print(text)
113 if debug.logging:
114 print(text)
114 115 debug.log_handler = log_handler
116 proxy = os.environ.get("G4F_PROXY")
115 117 try:
116 118 result = ChatCompletion.create(**kwargs)
117 119 first = True
@@ -139,7 +141,7 @@ class Api:
139 141 elif isinstance(chunk, ImageResponse):
140 142 images = chunk
141 143 if download_images:
142 images = asyncio.run(copy_images(chunk.get_list(), chunk.options.get("cookies")))
144 images = asyncio.run(copy_images(chunk.get_list(), chunk.get("cookies"), proxy))
143 145 images = ImageResponse(images, chunk.alt)
144 146 yield self._format_json("content", str(images))
145 147 elif isinstance(chunk, SynthesizeData):
Modified g4f/image.py +17 -10
@@ -7,8 +7,7 @@ import uuid
7 7 from io import BytesIO
8 8 import base64
9 9 import asyncio
10 from aiohttp import ClientSession
11
10 from aiohttp import ClientSession, ClientError
12 11 try:
13 12 from PIL.Image import open as open_image, new as new_image
14 13 from PIL.Image import FLIP_LEFT_RIGHT, ROTATE_180, ROTATE_270, ROTATE_90
@@ -20,6 +19,7 @@ from .typing import ImageType, Union, Image, Optional, Cookies
20 19 from .errors import MissingRequirementsError
21 20 from .providers.response import ResponseType
22 21 from .requests.aiohttp import get_connector
22 from . import debug
23 23
24 24 ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'}
25 25
@@ -277,12 +277,14 @@ def ensure_images_dir():
277 277 if not os.path.exists(images_dir):
278 278 os.makedirs(images_dir)
279 279
280 async def copy_images(images: list[str], cookies: Optional[Cookies] = None, proxy: Optional[str] = None):
280 async def copy_images(
281 images: list[str],
282 cookies: Optional[Cookies] = None,
283 proxy: Optional[str] = None
284 ):
281 285 ensure_images_dir()
282 286 async with ClientSession(
283 connector=get_connector(
284 proxy=os.environ.get("G4F_PROXY") if proxy is None else proxy
285 ),
287 connector=get_connector(proxy=proxy),
286 288 cookies=cookies
287 289 ) as session:
288 290 async def copy_image(image: str) -> str:
@@ -291,10 +293,15 @@ async def copy_images(images: list[str], cookies: Optional[Cookies] = None, prox
291 293 with open(target, "wb") as f:
292 294 f.write(extract_data_uri(image))
293 295 else:
294 async with session.get(image) as response:
295 with open(target, "wb") as f:
296 async for chunk in response.content.iter_chunked(4096):
297 f.write(chunk)
296 try:
297 async with session.get(image) as response:
298 response.raise_for_status()
299 with open(target, "wb") as f:
300 async for chunk in response.content.iter_chunked(4096):
301 f.write(chunk)
302 except ClientError as e:
303 debug.log(f"copy_images failed: {e.__class__.__name__}: {e}")
304 return image
298 305 with open(target, "rb") as f:
299 306 extension = is_accepted_format(f.read(12)).split("/")[-1]
300 307 extension = "jpg" if extension == "jpeg" else extension
Modified g4f/requests/raise_for_status.py +4 -2
@@ -18,7 +18,7 @@ def is_cloudflare(text: str) -> bool:
18 18 return '<div id="cf-please-wait">' in text or "<title>Just a moment...</title>" in text
19 19
20 20 def is_openai(text: str) -> bool:
21 return "<p>Unable to load site</p>" in text
21 return "<p>Unable to load site</p>" in text or 'id="challenge-error-text"' in text
22 22
23 23 async def raise_for_status_async(response: Union[StreamResponse, ClientResponse], message: str = None):
24 24 if response.status in (429, 402):
@@ -27,8 +27,10 @@ async def raise_for_status_async(response: Union[StreamResponse, ClientResponse]
27 27 if response.status == 403 and is_cloudflare(message):
28 28 raise CloudflareError(f"Response {response.status}: Cloudflare detected")
29 29 elif response.status == 403 and is_openai(message):
30 raise ResponseStatusError(f"Response {response.status}: Bot are detected")
30 raise ResponseStatusError(f"Response {response.status}: OpenAI Bot detected")
31 31 elif not response.ok:
32 if "<html>" in message:
33 message = "HTML content"
32 34 raise ResponseStatusError(f"Response {response.status}: {message}")
33 35
34 36 def raise_for_status(response: Union[Response, StreamResponse, ClientResponse, RequestsResponse], message: str = None):