summaryrefslogtreecommitdiffstats
path: root/g4f
diff options
context:
space:
mode:
authorH Lohaus <hlohaus@users.noreply.github.com>2024-11-17 18:32:51 +0100
committerGitHub <noreply@github.com>2024-11-17 18:32:51 +0100
commit275574d71ece22975de7df0e226d466a2056605b (patch)
tree3f113ea8beb7c43920871019512aeb8de9d1b4f7 /g4f
parentFix api streaming, fix AsyncClient (#2357) (diff)
parentAdd nodriver to Gemini provider, (diff)
downloadgpt4free-275574d71ece22975de7df0e226d466a2056605b.tar
gpt4free-275574d71ece22975de7df0e226d466a2056605b.tar.gz
gpt4free-275574d71ece22975de7df0e226d466a2056605b.tar.bz2
gpt4free-275574d71ece22975de7df0e226d466a2056605b.tar.lz
gpt4free-275574d71ece22975de7df0e226d466a2056605b.tar.xz
gpt4free-275574d71ece22975de7df0e226d466a2056605b.tar.zst
gpt4free-275574d71ece22975de7df0e226d466a2056605b.zip
Diffstat (limited to 'g4f')
-rw-r--r--g4f/Provider/Cloudflare.py15
-rw-r--r--g4f/Provider/HuggingChat.py34
-rw-r--r--g4f/Provider/needs_auth/Gemini.py80
-rw-r--r--g4f/Provider/needs_auth/GeminiPro.py4
-rw-r--r--g4f/Provider/needs_auth/HuggingFace.py29
-rw-r--r--g4f/Provider/needs_auth/MetaAI.py3
-rw-r--r--g4f/Provider/needs_auth/MetaAIAccount.py2
-rw-r--r--g4f/Provider/needs_auth/__init__.py1
-rw-r--r--g4f/gui/server/api.py32
-rw-r--r--g4f/image.py2
10 files changed, 80 insertions, 122 deletions
diff --git a/g4f/Provider/Cloudflare.py b/g4f/Provider/Cloudflare.py
index 825c5027..7d477d57 100644
--- a/g4f/Provider/Cloudflare.py
+++ b/g4f/Provider/Cloudflare.py
@@ -7,6 +7,7 @@ import uuid
from ..typing import AsyncResult, Messages, Cookies
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin, get_running_loop
from ..requests import Session, StreamSession, get_args_from_nodriver, raise_for_status, merge_cookies
+from ..errors import ResponseStatusError
class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin):
label = "Cloudflare AI"
@@ -42,10 +43,14 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin):
cls._args = asyncio.run(args)
with Session(**cls._args) as session:
response = session.get(cls.models_url)
- raise_for_status(response)
+ cls._args["cookies"] = merge_cookies(cls._args["cookies"] , response)
+ try:
+ raise_for_status(response)
+ except ResponseStatusError as e:
+ cls._args = None
+ raise e
json_data = response.json()
cls.models = [model.get("name") for model in json_data.get("models")]
- cls._args["cookies"] = merge_cookies(cls._args["cookies"] , response)
return cls.models
@classmethod
@@ -74,8 +79,12 @@ class Cloudflare(AsyncGeneratorProvider, ProviderModelMixin):
cls.api_endpoint,
json=data,
) as response:
- await raise_for_status(response)
cls._args["cookies"] = merge_cookies(cls._args["cookies"] , response)
+ try:
+ await raise_for_status(response)
+ except ResponseStatusError as e:
+ cls._args = None
+ raise e
async for line in response.iter_lines():
if line.startswith(b'data: '):
if line == b'data: [DONE]':
diff --git a/g4f/Provider/HuggingChat.py b/g4f/Provider/HuggingChat.py
index 509a7f16..2481aa31 100644
--- a/g4f/Provider/HuggingChat.py
+++ b/g4f/Provider/HuggingChat.py
@@ -4,12 +4,13 @@ import json
import requests
try:
- from curl_cffi import requests as cf_reqs
+ from curl_cffi import Session
has_curl_cffi = True
except ImportError:
has_curl_cffi = False
from ..typing import CreateResult, Messages
from ..errors import MissingRequirementsError
+from ..requests.raise_for_status import raise_for_status
from .base_provider import ProviderModelMixin, AbstractProvider
from .helper import format_prompt
@@ -18,7 +19,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
working = True
supports_stream = True
default_model = "meta-llama/Meta-Llama-3.1-70B-Instruct"
-
+
models = [
'meta-llama/Meta-Llama-3.1-70B-Instruct',
'CohereForAI/c4ai-command-r-plus-08-2024',
@@ -30,7 +31,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
'mistralai/Mistral-Nemo-Instruct-2407',
'microsoft/Phi-3.5-mini-instruct',
]
-
+
model_aliases = {
"llama-3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct",
"command-r-plus": "CohereForAI/c4ai-command-r-plus-08-2024",
@@ -44,15 +45,6 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
}
@classmethod
- def get_model(cls, model: str) -> str:
- if model in cls.models:
- return model
- elif model in cls.model_aliases:
- return cls.model_aliases[model]
- else:
- return cls.default_model
-
- @classmethod
def create_completion(
cls,
model: str,
@@ -65,7 +57,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
model = cls.get_model(model)
if model in cls.models:
- session = cf_reqs.Session()
+ session = Session()
session.headers = {
'accept': '*/*',
'accept-language': 'en',
@@ -82,20 +74,18 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
}
-
json_data = {
'model': model,
}
-
response = session.post('https://huggingface.co/chat/conversation', json=json_data)
- if response.status_code != 200:
- raise RuntimeError(f"Request failed with status code: {response.status_code}, response: {response.text}")
+ raise_for_status(response)
conversationId = response.json().get('conversationId')
# Get the data response and parse it properly
response = session.get(f'https://huggingface.co/chat/conversation/{conversationId}/__data.json?x-sveltekit-invalidated=11')
-
+ raise_for_status(response)
+
# Split the response content by newlines and parse each line as JSON
try:
json_data = None
@@ -156,6 +146,7 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
headers=headers,
files=files,
)
+ raise_for_status(response)
full_response = ""
for line in response.iter_lines():
@@ -182,9 +173,4 @@ class HuggingChat(AbstractProvider, ProviderModelMixin):
full_response = full_response.replace('<|im_end|', '').replace('\u0000', '').strip()
if not stream:
- yield full_response
-
- @classmethod
- def supports_model(cls, model: str) -> bool:
- """Check if the model is supported by the provider."""
- return model in cls.models or model in cls.model_aliases
+ yield full_response \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py
index dad54c84..781aa410 100644
--- a/g4f/Provider/needs_auth/Gemini.py
+++ b/g4f/Provider/needs_auth/Gemini.py
@@ -6,24 +6,20 @@ import random
import re
from aiohttp import ClientSession, BaseConnector
-
-from ..helper import get_connector
-
try:
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
+ import nodriver
+ has_nodriver = True
except ImportError:
- pass
+ has_nodriver = False
from ... import debug
from ...typing import Messages, Cookies, ImageType, AsyncResult, AsyncIterator
from ..base_provider import AsyncGeneratorProvider, BaseConversation
from ..helper import format_prompt, get_cookies
from ...requests.raise_for_status import raise_for_status
-from ...errors import MissingAuthError, MissingRequirementsError
+from ...requests.aiohttp import get_connector
+from ...errors import MissingAuthError
from ...image import ImageResponse, to_bytes
-from ...webdriver import get_browser, get_driver_cookies
REQUEST_HEADERS = {
"authority": "gemini.google.com",
@@ -64,9 +60,9 @@ class Gemini(AsyncGeneratorProvider):
@classmethod
async def nodriver_login(cls, proxy: str = None) -> AsyncIterator[str]:
- try:
- import nodriver as uc
- except ImportError:
+ if not has_nodriver:
+ if debug.logging:
+ print("Skip nodriver login in Gemini provider")
return
try:
from platformdirs import user_config_dir
@@ -75,7 +71,7 @@ class Gemini(AsyncGeneratorProvider):
user_data_dir = None
if debug.logging:
print(f"Open nodriver with user_dir: {user_data_dir}")
- browser = await uc.start(
+ browser = await nodriver.start(
user_data_dir=user_data_dir,
browser_args=None if proxy is None else [f"--proxy-server={proxy}"],
)
@@ -92,30 +88,6 @@ class Gemini(AsyncGeneratorProvider):
cls._cookies = cookies
@classmethod
- async def webdriver_login(cls, proxy: str) -> AsyncIterator[str]:
- driver = None
- try:
- driver = get_browser(proxy=proxy)
- try:
- driver.get(f"{cls.url}/app")
- WebDriverWait(driver, 5).until(
- EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea"))
- )
- except:
- login_url = os.environ.get("G4F_LOGIN_URL")
- if login_url:
- yield f"Please login: [Google Gemini]({login_url})\n\n"
- WebDriverWait(driver, 240).until(
- EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea"))
- )
- cls._cookies = get_driver_cookies(driver)
- except MissingRequirementsError:
- pass
- finally:
- if driver:
- driver.close()
-
- @classmethod
async def create_async_generator(
cls,
model: str,
@@ -143,9 +115,6 @@ class Gemini(AsyncGeneratorProvider):
if not cls._snlm0e:
async for chunk in cls.nodriver_login(proxy):
yield chunk
- if cls._cookies is None:
- async for chunk in cls.webdriver_login(proxy):
- yield chunk
if not cls._snlm0e:
if cls._cookies is None or "__Secure-1PSID" not in cls._cookies:
raise MissingAuthError('Missing "__Secure-1PSID" cookie')
@@ -211,20 +180,23 @@ class Gemini(AsyncGeneratorProvider):
yield content[last_content_len:]
last_content_len = len(content)
if image_prompt:
- images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
- if response_format == "b64_json":
- yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
- else:
- resolved_images = []
- preview = []
- for image in images:
- async with client.get(image, allow_redirects=False) as fetch:
- image = fetch.headers["location"]
- async with client.get(image, allow_redirects=False) as fetch:
- image = fetch.headers["location"]
- resolved_images.append(image)
- preview.append(image.replace('=s512', '=s200'))
- yield ImageResponse(resolved_images, image_prompt, {"orginal_links": images, "preview": preview})
+ try:
+ images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
+ if response_format == "b64_json":
+ yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
+ else:
+ resolved_images = []
+ preview = []
+ for image in images:
+ async with client.get(image, allow_redirects=False) as fetch:
+ image = fetch.headers["location"]
+ async with client.get(image, allow_redirects=False) as fetch:
+ image = fetch.headers["location"]
+ resolved_images.append(image)
+ preview.append(image.replace('=s512', '=s200'))
+ yield ImageResponse(resolved_images, image_prompt, {"orginal_links": images, "preview": preview})
+ except TypeError:
+ pass
def build_request(
prompt: str,
diff --git a/g4f/Provider/needs_auth/GeminiPro.py b/g4f/Provider/needs_auth/GeminiPro.py
index 7e52a194..a7f1e0aa 100644
--- a/g4f/Provider/needs_auth/GeminiPro.py
+++ b/g4f/Provider/needs_auth/GeminiPro.py
@@ -16,9 +16,9 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
working = True
supports_message_history = True
needs_auth = True
- default_model = "gemini-1.5-pro-latest"
+ default_model = "gemini-1.5-pro"
default_vision_model = default_model
- models = [default_model, "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"]
+ models = [default_model, "gemini-pro", "gemini-1.5-flash", "gemini-1.5-flash-8b"]
@classmethod
async def create_async_generator(
diff --git a/g4f/Provider/needs_auth/HuggingFace.py b/g4f/Provider/needs_auth/HuggingFace.py
index ecc75d1c..35270e60 100644
--- a/g4f/Provider/needs_auth/HuggingFace.py
+++ b/g4f/Provider/needs_auth/HuggingFace.py
@@ -1,13 +1,11 @@
from __future__ import annotations
import json
-from aiohttp import ClientSession, BaseConnector
from ...typing import AsyncResult, Messages
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import get_connector
-from ...errors import RateLimitError, ModelNotFoundError
-from ...requests.raise_for_status import raise_for_status
+from ...errors import ModelNotFoundError
+from ...requests import StreamSession, raise_for_status
from ..HuggingChat import HuggingChat
@@ -21,22 +19,12 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
model_aliases = HuggingChat.model_aliases
@classmethod
- def get_model(cls, model: str) -> str:
- if model in cls.models:
- return model
- elif model in cls.model_aliases:
- return cls.model_aliases[model]
- else:
- return cls.default_model
-
- @classmethod
async def create_async_generator(
cls,
model: str,
messages: Messages,
stream: bool = True,
proxy: str = None,
- connector: BaseConnector = None,
api_base: str = "https://api-inference.huggingface.co",
api_key: str = None,
max_new_tokens: int = 1024,
@@ -62,7 +50,6 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
}
if api_key is not None:
headers["Authorization"] = f"Bearer {api_key}"
-
params = {
"return_full_text": False,
"max_new_tokens": max_new_tokens,
@@ -70,10 +57,9 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
**kwargs
}
payload = {"inputs": format_prompt(messages), "parameters": params, "stream": stream}
-
- async with ClientSession(
+ async with StreamSession(
headers=headers,
- connector=get_connector(connector, proxy)
+ proxy=proxy
) as session:
async with session.post(f"{api_base.rstrip('/')}/models/{model}", json=payload) as response:
if response.status == 404:
@@ -81,7 +67,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
await raise_for_status(response)
if stream:
first = True
- async for line in response.content:
+ async for line in response.iter_lines():
if line.startswith(b"data:"):
data = json.loads(line[5:])
if not data["token"]["special"]:
@@ -89,7 +75,8 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
if first:
first = False
chunk = chunk.lstrip()
- yield chunk
+ if chunk:
+ yield chunk
else:
yield (await response.json())[0]["generated_text"].strip()
@@ -101,4 +88,4 @@ def format_prompt(messages: Messages) -> str:
for idx, message in enumerate(messages)
if message["role"] == "assistant"
])
- return f"{history}<s>[INST] {question} [/INST]"
+ return f"{history}<s>[INST] {question} [/INST]" \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/MetaAI.py b/g4f/Provider/needs_auth/MetaAI.py
index 4b730abd..568de701 100644
--- a/g4f/Provider/needs_auth/MetaAI.py
+++ b/g4f/Provider/needs_auth/MetaAI.py
@@ -79,7 +79,6 @@ class MetaAI(AsyncGeneratorProvider, ProviderModelMixin):
self.access_token = None
if self.access_token is None and cookies is None:
await self.update_access_token()
-
if self.access_token is None:
url = "https://www.meta.ai/api/graphql/"
payload = {"lsd": self.lsd, 'fb_dtsg': self.dtsg}
@@ -128,6 +127,8 @@ class MetaAI(AsyncGeneratorProvider, ProviderModelMixin):
json_line = json.loads(line)
except json.JSONDecodeError:
continue
+ if json_line.get("errors"):
+ raise RuntimeError("\n".join([error.get("message") for error in json_line.get("errors")]))
bot_response_message = json_line.get("data", {}).get("node", {}).get("bot_response_message", {})
streaming_state = bot_response_message.get("streaming_state")
fetch_id = bot_response_message.get("fetch_id") or fetch_id
diff --git a/g4f/Provider/needs_auth/MetaAIAccount.py b/g4f/Provider/needs_auth/MetaAIAccount.py
index 2d54f3e0..0a586006 100644
--- a/g4f/Provider/needs_auth/MetaAIAccount.py
+++ b/g4f/Provider/needs_auth/MetaAIAccount.py
@@ -2,7 +2,7 @@ from __future__ import annotations
from ...typing import AsyncResult, Messages, Cookies
from ..helper import format_prompt, get_cookies
-from ..MetaAI import MetaAI
+from .MetaAI import MetaAI
class MetaAIAccount(MetaAI):
needs_auth = True
diff --git a/g4f/Provider/needs_auth/__init__.py b/g4f/Provider/needs_auth/__init__.py
index 26c50c0a..ace53876 100644
--- a/g4f/Provider/needs_auth/__init__.py
+++ b/g4f/Provider/needs_auth/__init__.py
@@ -11,6 +11,7 @@ from .GeminiPro import GeminiPro
from .Groq import Groq
from .HuggingFace import HuggingFace
from .MetaAI import MetaAI
+from .MetaAIAccount import MetaAIAccount
from .OpenaiAPI import OpenaiAPI
from .OpenaiChat import OpenaiChat
from .PerplexityApi import PerplexityApi
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index dafcb5d4..f03d2048 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -6,14 +6,14 @@ import uuid
import asyncio
import time
from aiohttp import ClientSession
-from typing import Iterator, Optional, AsyncIterator, Union
+from typing import Iterator, Optional
from flask import send_from_directory
from g4f import version, models
from g4f import get_last_provider, ChatCompletion
from g4f.errors import VersionNotFoundError
from g4f.typing import Cookies
-from g4f.image import ImagePreview, ImageResponse, is_accepted_format
+from g4f.image import ImagePreview, ImageResponse, is_accepted_format, extract_data_uri
from g4f.requests.aiohttp import get_connector
from g4f.Provider import ProviderType, __providers__, __map__
from g4f.providers.base_provider import ProviderModelMixin, FinishReason
@@ -31,7 +31,6 @@ def ensure_images_dir():
conversations: dict[dict[str, BaseConversation]] = {}
-
class Api:
@staticmethod
def get_models() -> list[str]:
@@ -176,18 +175,22 @@ class Api:
connector=get_connector(None, os.environ.get("G4F_PROXY")),
cookies=cookies
) as session:
- async def copy_image(image):
- async with session.get(image) as response:
- target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
+ async def copy_image(image: str) -> str:
+ target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
+ if image.startswith("data:"):
with open(target, "wb") as f:
- async for chunk in response.content.iter_any():
- f.write(chunk)
- with open(target, "rb") as f:
- extension = is_accepted_format(f.read(12)).split("/")[-1]
- extension = "jpg" if extension == "jpeg" else extension
- new_target = f"{target}.{extension}"
- os.rename(target, new_target)
- return f"/images/{os.path.basename(new_target)}"
+ f.write(extract_data_uri(image))
+ else:
+ async with session.get(image) as response:
+ with open(target, "wb") as f:
+ async for chunk in response.content.iter_any():
+ f.write(chunk)
+ with open(target, "rb") as f:
+ extension = is_accepted_format(f.read(12)).split("/")[-1]
+ extension = "jpg" if extension == "jpeg" else extension
+ new_target = f"{target}.{extension}"
+ os.rename(target, new_target)
+ return f"/images/{os.path.basename(new_target)}"
return await asyncio.gather(*[copy_image(image) for image in images])
@@ -197,7 +200,6 @@ class Api:
response_type: content
}
-
def get_error_message(exception: Exception) -> str:
message = f"{type(exception).__name__}: {exception}"
provider = get_last_provider()
diff --git a/g4f/image.py b/g4f/image.py
index 556ec43d..8a3d7a74 100644
--- a/g4f/image.py
+++ b/g4f/image.py
@@ -133,7 +133,7 @@ def extract_data_uri(data_uri: str) -> bytes:
Returns:
bytes: The extracted binary data.
"""
- data = data_uri.split(",")[1]
+ data = data_uri.split(",")[-1]
data = base64.b64decode(data)
return data