summaryrefslogtreecommitdiffstats
path: root/g4f
diff options
context:
space:
mode:
authorH Lohaus <hlohaus@users.noreply.github.com>2024-04-22 01:35:07 +0200
committerGitHub <noreply@github.com>2024-04-22 01:35:07 +0200
commit4b4d1f08b5c75c8c8932b5edfbb0d020f8e029a7 (patch)
tree6ed0cfc6cd53a3ab32565d6199a929ac1ea6ad80 /g4f
parentMerge pull request #1869 from hlohaus/carst (diff)
parentAdd vision models to readme (diff)
downloadgpt4free-0.3.0.4.tar
gpt4free-0.3.0.4.tar.gz
gpt4free-0.3.0.4.tar.bz2
gpt4free-0.3.0.4.tar.lz
gpt4free-0.3.0.4.tar.xz
gpt4free-0.3.0.4.tar.zst
gpt4free-0.3.0.4.zip
Diffstat (limited to 'g4f')
-rw-r--r--g4f/Provider/Bing.py3
-rw-r--r--g4f/Provider/BingCreateImages.py2
-rw-r--r--g4f/Provider/DeepInfra.py33
-rw-r--r--g4f/Provider/DeepInfraImage.py3
-rw-r--r--g4f/Provider/MetaAI.py7
-rw-r--r--g4f/Provider/MetaAIAccount.py2
-rw-r--r--g4f/Provider/Replicate.py84
-rw-r--r--g4f/Provider/ReplicateImage.py2
-rw-r--r--g4f/Provider/You.py37
-rw-r--r--g4f/Provider/__init__.py2
-rw-r--r--g4f/Provider/bing/conversation.py2
-rw-r--r--g4f/Provider/needs_auth/Gemini.py88
-rw-r--r--g4f/Provider/needs_auth/OpenaiAccount.py4
-rw-r--r--g4f/Provider/needs_auth/OpenaiChat.py46
-rw-r--r--g4f/Provider/openai/crypt.py2
-rw-r--r--g4f/Provider/openai/har_file.py20
-rw-r--r--g4f/Provider/unfinished/Replicate.py78
-rw-r--r--g4f/Provider/you/har_file.py67
-rw-r--r--g4f/api/__init__.py46
-rw-r--r--g4f/cli.py34
-rw-r--r--g4f/cookies.py58
-rw-r--r--g4f/gui/__init__.py3
-rw-r--r--g4f/gui/gui_parser.py1
-rw-r--r--g4f/gui/run.py6
-rw-r--r--g4f/gui/server/api.py32
-rw-r--r--g4f/gui/server/backend.py4
-rw-r--r--g4f/image.py9
-rw-r--r--g4f/providers/base_provider.py4
-rw-r--r--g4f/providers/helper.py7
29 files changed, 431 insertions, 255 deletions
diff --git a/g4f/Provider/Bing.py b/g4f/Provider/Bing.py
index 1fe94359..bfd74f8c 100644
--- a/g4f/Provider/Bing.py
+++ b/g4f/Provider/Bing.py
@@ -38,8 +38,9 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
supports_message_history = True
supports_gpt_4 = True
default_model = "Balanced"
+ default_vision_model = "gpt-4-vision"
models = [getattr(Tones, key) for key in Tones.__dict__ if not key.startswith("__")]
-
+
@classmethod
def create_async_generator(
cls,
diff --git a/g4f/Provider/BingCreateImages.py b/g4f/Provider/BingCreateImages.py
index f29126b6..69bf1e92 100644
--- a/g4f/Provider/BingCreateImages.py
+++ b/g4f/Provider/BingCreateImages.py
@@ -13,9 +13,11 @@ from .bing.create_images import create_images, create_session, get_cookies_from_
class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
label = "Microsoft Designer"
+ parent = "Bing"
url = "https://www.bing.com/images/create"
working = True
needs_auth = True
+ image_models = ["dall-e"]
def __init__(self, cookies: Cookies = None, proxy: str = None) -> None:
self.cookies: Cookies = cookies
diff --git a/g4f/Provider/DeepInfra.py b/g4f/Provider/DeepInfra.py
index 971424b7..35ff84a1 100644
--- a/g4f/Provider/DeepInfra.py
+++ b/g4f/Provider/DeepInfra.py
@@ -1,17 +1,22 @@
from __future__ import annotations
import requests
-from ..typing import AsyncResult, Messages
+from ..typing import AsyncResult, Messages, ImageType
+from ..image import to_data_uri
from .needs_auth.Openai import Openai
class DeepInfra(Openai):
label = "DeepInfra"
url = "https://deepinfra.com"
working = True
- needs_auth = False
+ has_auth = True
supports_stream = True
supports_message_history = True
- default_model = 'HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1'
+ default_model = "meta-llama/Meta-Llama-3-70b-instruct"
+ default_vision_model = "llava-hf/llava-1.5-7b-hf"
+ model_aliases = {
+ 'mixtral-8x22b': 'HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1'
+ }
@classmethod
def get_models(cls):
@@ -27,19 +32,12 @@ class DeepInfra(Openai):
model: str,
messages: Messages,
stream: bool,
+ image: ImageType = None,
api_base: str = "https://api.deepinfra.com/v1/openai",
temperature: float = 0.7,
max_tokens: int = 1028,
**kwargs
) -> AsyncResult:
-
- if not '/' in model:
- models = {
- 'mixtral-8x22b': 'HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1',
- 'dbrx-instruct': 'databricks/dbrx-instruct',
- }
- model = models.get(model, model)
-
headers = {
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US',
@@ -55,6 +53,19 @@ class DeepInfra(Openai):
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
}
+ if image is not None:
+ if not model:
+ model = cls.default_vision_model
+ messages[-1]["content"] = [
+ {
+ "type": "image_url",
+ "image_url": {"url": to_data_uri(image)}
+ },
+ {
+ "type": "text",
+ "text": messages[-1]["content"]
+ }
+ ]
return super().create_async_generator(
model, messages,
stream=stream,
diff --git a/g4f/Provider/DeepInfraImage.py b/g4f/Provider/DeepInfraImage.py
index ce09533b..46a5c2e2 100644
--- a/g4f/Provider/DeepInfraImage.py
+++ b/g4f/Provider/DeepInfraImage.py
@@ -9,8 +9,10 @@ from ..image import ImageResponse
class DeepInfraImage(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://deepinfra.com"
+ parent = "DeepInfra"
working = True
default_model = 'stability-ai/sdxl'
+ image_models = [default_model]
@classmethod
def get_models(cls):
@@ -18,6 +20,7 @@ class DeepInfraImage(AsyncGeneratorProvider, ProviderModelMixin):
url = 'https://api.deepinfra.com/models/featured'
models = requests.get(url).json()
cls.models = [model['model_name'] for model in models if model["reported_type"] == "text-to-image"]
+ cls.image_models = cls.models
return cls.models
@classmethod
diff --git a/g4f/Provider/MetaAI.py b/g4f/Provider/MetaAI.py
index fb7790f9..caed7778 100644
--- a/g4f/Provider/MetaAI.py
+++ b/g4f/Provider/MetaAI.py
@@ -13,7 +13,7 @@ from ..requests import raise_for_status, DEFAULT_HEADERS
from ..image import ImageResponse, ImagePreview
from ..errors import ResponseError
from .base_provider import AsyncGeneratorProvider
-from .helper import format_prompt, get_connector
+from .helper import format_prompt, get_connector, format_cookies
class Sources():
def __init__(self, link_list: List[Dict[str, str]]) -> None:
@@ -48,7 +48,6 @@ class MetaAI(AsyncGeneratorProvider):
async def update_access_token(self, birthday: str = "1999-01-01"):
url = "https://www.meta.ai/api/graphql/"
-
payload = {
"lsd": self.lsd,
"fb_api_caller_class": "RelayModern",
@@ -90,7 +89,7 @@ class MetaAI(AsyncGeneratorProvider):
headers = {}
headers = {
'content-type': 'application/x-www-form-urlencoded',
- 'cookie': "; ".join([f"{k}={v}" for k, v in cookies.items()]),
+ 'cookie': format_cookies(self.cookies),
'origin': 'https://www.meta.ai',
'referer': 'https://www.meta.ai/',
'x-asbd-id': '129477',
@@ -194,7 +193,7 @@ class MetaAI(AsyncGeneratorProvider):
**headers
}
async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
- await raise_for_status(response)
+ await raise_for_status(response, "Fetch sources failed")
text = await response.text()
if "<h1>Something Went Wrong</h1>" in text:
raise ResponseError("Response: Something Went Wrong")
diff --git a/g4f/Provider/MetaAIAccount.py b/g4f/Provider/MetaAIAccount.py
index 8be2318e..369b3f2f 100644
--- a/g4f/Provider/MetaAIAccount.py
+++ b/g4f/Provider/MetaAIAccount.py
@@ -6,6 +6,8 @@ from .MetaAI import MetaAI
class MetaAIAccount(MetaAI):
needs_auth = True
+ parent = "MetaAI"
+ image_models = ["meta"]
@classmethod
async def create_async_generator(
diff --git a/g4f/Provider/Replicate.py b/g4f/Provider/Replicate.py
new file mode 100644
index 00000000..593fd04d
--- /dev/null
+++ b/g4f/Provider/Replicate.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from .helper import format_prompt, filter_none
+from ..typing import AsyncResult, Messages
+from ..requests import raise_for_status
+from ..requests.aiohttp import StreamSession
+from ..errors import ResponseError, MissingAuthError
+
+class Replicate(AsyncGeneratorProvider, ProviderModelMixin):
+ url = "https://replicate.com"
+ working = True
+ default_model = "meta/meta-llama-3-70b-instruct"
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ api_key: str = None,
+ proxy: str = None,
+ timeout: int = 180,
+ system_prompt: str = None,
+ max_new_tokens: int = None,
+ temperature: float = None,
+ top_p: float = None,
+ top_k: float = None,
+ stop: list = None,
+ extra_data: dict = {},
+ headers: dict = {
+ "accept": "application/json",
+ },
+ **kwargs
+ ) -> AsyncResult:
+ model = cls.get_model(model)
+ if cls.needs_auth and api_key is None:
+ raise MissingAuthError("api_key is missing")
+ if api_key is not None:
+ headers["Authorization"] = f"Bearer {api_key}"
+ api_base = "https://api.replicate.com/v1/models/"
+ else:
+ api_base = "https://replicate.com/api/models/"
+ async with StreamSession(
+ proxy=proxy,
+ headers=headers,
+ timeout=timeout
+ ) as session:
+ data = {
+ "stream": True,
+ "input": {
+ "prompt": format_prompt(messages),
+ **filter_none(
+ system_prompt=system_prompt,
+ max_new_tokens=max_new_tokens,
+ temperature=temperature,
+ top_p=top_p,
+ top_k=top_k,
+ stop_sequences=",".join(stop) if stop else None
+ ),
+ **extra_data
+ },
+ }
+ url = f"{api_base.rstrip('/')}/{model}/predictions"
+ async with session.post(url, json=data) as response:
+ message = "Model not found" if response.status == 404 else None
+ await raise_for_status(response, message)
+ result = await response.json()
+ if "id" not in result:
+ raise ResponseError(f"Invalid response: {result}")
+ async with session.get(result["urls"]["stream"], headers={"Accept": "text/event-stream"}) as response:
+ await raise_for_status(response)
+ event = None
+ async for line in response.iter_lines():
+ if line.startswith(b"event: "):
+ event = line[7:]
+ if event == b"done":
+ break
+ elif event == b"output":
+ if line.startswith(b"data: "):
+ new_text = line[6:].decode()
+ if new_text:
+ yield new_text
+ else:
+ yield "\n" \ No newline at end of file
diff --git a/g4f/Provider/ReplicateImage.py b/g4f/Provider/ReplicateImage.py
index 3e423c1a..cc3943d7 100644
--- a/g4f/Provider/ReplicateImage.py
+++ b/g4f/Provider/ReplicateImage.py
@@ -11,12 +11,14 @@ from ..errors import ResponseError
class ReplicateImage(AsyncGeneratorProvider, ProviderModelMixin):
url = "https://replicate.com"
+ parent = "Replicate"
working = True
default_model = 'stability-ai/sdxl'
default_versions = [
"39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
"2b017d9b67edd2ee1401238df49d75da53c523f36e363881e057f5dc3ed3c5b2"
]
+ image_models = [default_model]
@classmethod
async def create_async_generator(
diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py
index e90365ce..a9e7834c 100644
--- a/g4f/Provider/You.py
+++ b/g4f/Provider/You.py
@@ -8,19 +8,22 @@ import uuid
from ..typing import AsyncResult, Messages, ImageType, Cookies
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt
-from ..image import ImageResponse, to_bytes, is_accepted_format
+from ..image import ImageResponse, ImagePreview, to_bytes, is_accepted_format
from ..requests import StreamSession, FormData, raise_for_status
from .you.har_file import get_telemetry_ids
from .. import debug
class You(AsyncGeneratorProvider, ProviderModelMixin):
+ label = "You.com"
url = "https://you.com"
working = True
supports_gpt_35_turbo = True
supports_gpt_4 = True
default_model = "gpt-3.5-turbo"
+ default_vision_model = "agent"
+ image_models = ["dall-e"]
models = [
- "gpt-3.5-turbo",
+ default_model,
"gpt-4",
"gpt-4-turbo",
"claude-instant",
@@ -29,7 +32,8 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
"claude-3-sonnet",
"gemini-pro",
"zephyr",
- "dall-e",
+ default_vision_model,
+ *image_models
]
model_aliases = {
"claude-v2": "claude-2"
@@ -51,7 +55,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
chat_mode: str = "default",
**kwargs,
) -> AsyncResult:
- if image is not None:
+ if image is not None or model == cls.default_vision_model:
chat_mode = "agent"
elif not model or model == cls.default_model:
...
@@ -62,13 +66,18 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
chat_mode = "custom"
model = cls.get_model(model)
async with StreamSession(
- proxies={"all": proxy},
+ proxy=proxy,
impersonate="chrome",
timeout=(30, timeout)
) as session:
cookies = await cls.get_cookies(session) if chat_mode != "default" else None
-
- upload = json.dumps([await cls.upload_file(session, cookies, to_bytes(image), image_name)]) if image else ""
+ upload = ""
+ if image is not None:
+ upload_file = await cls.upload_file(
+ session, cookies,
+ to_bytes(image), image_name
+ )
+ upload = json.dumps([upload_file])
headers = {
"Accept": "text/event-stream",
"Referer": f"{cls.url}/search?fromSearchBar=true&tbm=youchat",
@@ -102,11 +111,17 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
if event == "youChatToken" and event in data:
yield data[event]
elif event == "youChatUpdate" and "t" in data and data["t"] is not None:
- match = re.search(r"!\[fig\]\((.+?)\)", data["t"])
- if match:
- yield ImageResponse(match.group(1), messages[-1]["content"])
+ if chat_mode == "create":
+ match = re.search(r"!\[(.+?)\]\((.+?)\)", data["t"])
+ if match:
+ if match.group(1) == "fig":
+ yield ImagePreview(match.group(2), messages[-1]["content"])
+ else:
+ yield ImageResponse(match.group(2), match.group(1))
+ else:
+ yield data["t"]
else:
- yield data["t"]
+ yield data["t"]
@classmethod
async def upload_file(cls, client: StreamSession, cookies: Cookies, file: bytes, filename: str = None) -> dict:
diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py
index 27c14672..d2d9bfda 100644
--- a/g4f/Provider/__init__.py
+++ b/g4f/Provider/__init__.py
@@ -9,7 +9,6 @@ from .deprecated import *
from .not_working import *
from .selenium import *
from .needs_auth import *
-from .unfinished import *
from .Aichatos import Aichatos
from .Aura import Aura
@@ -46,6 +45,7 @@ from .MetaAI import MetaAI
from .MetaAIAccount import MetaAIAccount
from .PerplexityLabs import PerplexityLabs
from .Pi import Pi
+from .Replicate import Replicate
from .ReplicateImage import ReplicateImage
from .Vercel import Vercel
from .WhiteRabbitNeo import WhiteRabbitNeo
diff --git a/g4f/Provider/bing/conversation.py b/g4f/Provider/bing/conversation.py
index 4cfeef8e..a4195fa4 100644
--- a/g4f/Provider/bing/conversation.py
+++ b/g4f/Provider/bing/conversation.py
@@ -41,6 +41,8 @@ async def create_conversation(session: StreamSession, headers: dict, tone: str)
raise RateLimitError("Response 404: Do less requests and reuse conversations")
await raise_for_status(response, "Failed to create conversation")
data = await response.json()
+ if not data:
+ raise RuntimeError('Empty response: Failed to create conversation')
conversationId = data.get('conversationId')
clientId = data.get('clientId')
conversationSignature = response.headers.get('X-Sydney-Encryptedconversationsignature')
diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py
index ebf5f413..209c2e91 100644
--- a/g4f/Provider/needs_auth/Gemini.py
+++ b/g4f/Provider/needs_auth/Gemini.py
@@ -16,6 +16,7 @@ try:
except ImportError:
pass
+from ... import debug
from ...typing import Messages, Cookies, ImageType, AsyncResult
from ..base_provider import AsyncGeneratorProvider
from ..helper import format_prompt, get_cookies
@@ -53,6 +54,56 @@ class Gemini(AsyncGeneratorProvider):
url = "https://gemini.google.com"
needs_auth = True
working = True
+ image_models = ["gemini"]
+ default_vision_model = "gemini"
+ _cookies: Cookies = None
+
+ @classmethod
+ async def nodriver_login(cls) -> Cookies:
+ try:
+ import nodriver as uc
+ except ImportError:
+ return
+ try:
+ from platformdirs import user_config_dir
+ user_data_dir = user_config_dir("g4f-nodriver")
+ except:
+ user_data_dir = None
+ if debug.logging:
+ print(f"Open nodriver with user_dir: {user_data_dir}")
+ browser = await uc.start(user_data_dir=user_data_dir)
+ page = await browser.get(f"{cls.url}/app")
+ await page.select("div.ql-editor.textarea", 240)
+ cookies = {}
+ for c in await page.browser.cookies.get_all():
+ if c.domain.endswith(".google.com"):
+ cookies[c.name] = c.value
+ await page.close()
+ return cookies
+
+ @classmethod
+ async def webdriver_login(cls, proxy: 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(
@@ -72,47 +123,30 @@ class Gemini(AsyncGeneratorProvider):
if cookies is None:
cookies = {}
cookies["__Secure-1PSID"] = api_key
- cookies = cookies if cookies else get_cookies(".google.com", False, True)
+ cls._cookies = cookies or cls._cookies or get_cookies(".google.com", False, True)
base_connector = get_connector(connector, proxy)
async with ClientSession(
headers=REQUEST_HEADERS,
connector=base_connector
) as session:
- snlm0e = await cls.fetch_snlm0e(session, cookies) if cookies else None
+ snlm0e = await cls.fetch_snlm0e(session, cls._cookies) if cls._cookies else None
if not snlm0e:
- 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"))
- )
- cookies = get_driver_cookies(driver)
- except MissingRequirementsError:
- pass
- finally:
- if driver:
- driver.close()
+ cls._cookies = await cls.nodriver_login();
+ if cls._cookies is None:
+ async for chunk in cls.webdriver_login(proxy):
+ yield chunk
if not snlm0e:
- if "__Secure-1PSID" not in cookies:
+ if "__Secure-1PSID" not in cls._cookies:
raise MissingAuthError('Missing "__Secure-1PSID" cookie')
- snlm0e = await cls.fetch_snlm0e(session, cookies)
+ snlm0e = await cls.fetch_snlm0e(session, cls._cookies)
if not snlm0e:
- raise RuntimeError("Invalid auth. SNlM0e not found")
+ raise RuntimeError("Invalid cookies. SNlM0e not found")
image_url = await cls.upload_image(base_connector, to_bytes(image), image_name) if image else None
async with ClientSession(
- cookies=cookies,
+ cookies=cls._cookies,
headers=REQUEST_HEADERS,
connector=base_connector,
) as client:
diff --git a/g4f/Provider/needs_auth/OpenaiAccount.py b/g4f/Provider/needs_auth/OpenaiAccount.py
index 7be60c86..16bfff66 100644
--- a/g4f/Provider/needs_auth/OpenaiAccount.py
+++ b/g4f/Provider/needs_auth/OpenaiAccount.py
@@ -3,4 +3,6 @@ from __future__ import annotations
from .OpenaiChat import OpenaiChat
class OpenaiAccount(OpenaiChat):
- needs_auth = True \ No newline at end of file
+ needs_auth = True
+ parent = "OpenaiChat"
+ image_models = ["dall-e"] \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index 36b8bd3c..515230f0 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -29,6 +29,7 @@ from ...requests.aiohttp import StreamSession
from ...image import to_image, to_bytes, ImageResponse, ImageRequest
from ...errors import MissingAuthError, ResponseError
from ...providers.conversation import BaseConversation
+from ..helper import format_cookies
from ..openai.har_file import getArkoseAndAccessToken, NoValidHarFileError
from ... import debug
@@ -43,8 +44,14 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
supports_message_history = True
supports_system_message = True
default_model = None
+ default_vision_model = "gpt-4-vision"
models = ["gpt-3.5-turbo", "gpt-4", "gpt-4-gizmo"]
- model_aliases = {"text-davinci-002-render-sha": "gpt-3.5-turbo", "": "gpt-3.5-turbo", "gpt-4-turbo-preview": "gpt-4"}
+ model_aliases = {
+ "text-davinci-002-render-sha": "gpt-3.5-turbo",
+ "": "gpt-3.5-turbo",
+ "gpt-4-turbo-preview": "gpt-4",
+ "dall-e": "gpt-4",
+ }
_api_key: str = None
_headers: dict = None
_cookies: Cookies = None
@@ -334,9 +341,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
Raises:
RuntimeError: If an error occurs during processing.
"""
-
async with StreamSession(
- proxies={"all": proxy},
+ proxy=proxy,
impersonate="chrome",
timeout=timeout
) as session:
@@ -358,26 +364,27 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
api_key = cls._api_key = None
cls._create_request_args()
if debug.logging:
- print("OpenaiChat: Load default_model failed")
+ print("OpenaiChat: Load default model failed")
print(f"{e.__class__.__name__}: {e}")
arkose_token = None
if cls.default_model is None:
+ error = None
try:
- arkose_token, api_key, cookies = await getArkoseAndAccessToken(proxy)
- cls._create_request_args(cookies)
+ arkose_token, api_key, cookies, headers = await getArkoseAndAccessToken(proxy)
+ cls._create_request_args(cookies, headers)
cls._set_api_key(api_key)
except NoValidHarFileError as e:
- ...
+ error = e
if cls._api_key is None:
await cls.nodriver_access_token()
if cls._api_key is None and cls.needs_auth:
- raise e
+ raise error
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
async with session.post(
f"{cls.url}/backend-anon/sentinel/chat-requirements"
- if not cls._api_key else
+ if cls._api_key is None else
f"{cls.url}/backend-api/sentinel/chat-requirements",
json={"conversation_mode_kind": "primary_assistant"},
headers=cls._headers
@@ -393,8 +400,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
print(f'Arkose: {need_arkose} Turnstile: {data["turnstile"]["required"]}')
if need_arkose and arkose_token is None:
- arkose_token, api_key, cookies = await getArkoseAndAccessToken(proxy)
- cls._create_request_args(cookies)
+ arkose_token, api_key, cookies, headers = await getArkoseAndAccessToken(proxy)
+ cls._create_request_args(cookies, headers)
cls._set_api_key(api_key)
if arkose_token is None:
raise MissingAuthError("No arkose token found in .har file")
@@ -406,7 +413,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
print("OpenaiChat: Upload image failed")
print(f"{e.__class__.__name__}: {e}")
- model = cls.get_model(model).replace("gpt-3.5-turbo", "text-davinci-002-render-sha")
+ model = cls.get_model(model)
+ model = "text-davinci-002-render-sha" if model == "gpt-3.5-turbo" else model
if conversation is None:
conversation = Conversation(conversation_id, str(uuid.uuid4()) if parent_id is None else parent_id)
else:
@@ -613,7 +621,7 @@ this.fetch = async (url, options) => {
cookies[c.name] = c.value
user_agent = await page.evaluate("window.navigator.userAgent")
await page.close()
- cls._create_request_args(cookies, user_agent)
+ cls._create_request_args(cookies, user_agent=user_agent)
cls._set_api_key(api_key)
@classmethod
@@ -667,16 +675,12 @@ this.fetch = async (url, options) => {
"oai-language": "en-US",
}
- @staticmethod
- def _format_cookies(cookies: Cookies):
- return "; ".join(f"{k}={v}" for k, v in cookies.items() if k != "access_token")
-
@classmethod
- def _create_request_args(cls, cookies: Cookies = None, user_agent: str = None):
- cls._headers = cls.get_default_headers()
+ def _create_request_args(cls, cookies: Cookies = None, headers: dict = None, user_agent: str = None):
+ cls._headers = cls.get_default_headers() if headers is None else headers
if user_agent is not None:
cls._headers["user-agent"] = user_agent
- cls._cookies = {} if cookies is None else cookies
+ cls._cookies = {} if cookies is None else {k: v for k, v in cookies.items() if k != "access_token"}
cls._update_cookie_header()
@classmethod
@@ -693,7 +697,7 @@ this.fetch = async (url, options) => {
@classmethod
def _update_cookie_header(cls):
- cls._headers["cookie"] = cls._format_cookies(cls._cookies)
+ cls._headers["cookie"] = format_cookies(cls._cookies)
class Conversation(BaseConversation):
"""
diff --git a/g4f/Provider/openai/crypt.py b/g4f/Provider/openai/crypt.py
index e7f35190..43156f3d 100644
--- a/g4f/Provider/openai/crypt.py
+++ b/g4f/Provider/openai/crypt.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import json
import base64
import hashlib
diff --git a/g4f/Provider/openai/har_file.py b/g4f/Provider/openai/har_file.py
index 52cd8260..da25b637 100644
--- a/g4f/Provider/openai/har_file.py
+++ b/g4f/Provider/openai/har_file.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import base64
import json
import os
@@ -59,17 +61,21 @@ def readHAR():
except KeyError:
continue
cookies = {c['name']: c['value'] for c in v['request']['cookies']}
+ headers = get_headers(v)
if not accessToken:
raise NoValidHarFileError("No accessToken found in .har files")
if not chatArks:
- return None, accessToken, cookies
- return chatArks.pop(), accessToken, cookies
+ return None, accessToken, cookies, headers
+ return chatArks.pop(), accessToken, cookies, headers
+
+def get_headers(entry) -> dict:
+ return {h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')}
def parseHAREntry(entry) -> arkReq:
tmpArk = arkReq(
arkURL=entry['request']['url'],
arkBx="",
- arkHeader={h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')},
+ arkHeader=get_headers(entry),
arkBody={p['name']: unquote(p['value']) for p in entry['request']['postData']['params'] if p['name'] not in ['rnd']},
arkCookies={c['name']: c['value'] for c in entry['request']['cookies']},
userAgent=""
@@ -123,11 +129,11 @@ def getN() -> str:
timestamp = str(int(time.time()))
return base64.b64encode(timestamp.encode()).decode()
-async def getArkoseAndAccessToken(proxy: str):
+async def getArkoseAndAccessToken(proxy: str) -> tuple[str, str, dict, dict]:
global chatArk, accessToken, cookies
if chatArk is None or accessToken is None:
- chatArk, accessToken, cookies = readHAR()
+ chatArk, accessToken, cookies, headers = readHAR()
if chatArk is None:
- return None, accessToken, cookies
+ return None, accessToken, cookies, headers
newReq = genArkReq(chatArk)
- return await sendRequest(newReq, proxy), accessToken, cookies
+ return await sendRequest(newReq, proxy), accessToken, cookies, headers
diff --git a/g4f/Provider/unfinished/Replicate.py b/g4f/Provider/unfinished/Replicate.py
deleted file mode 100644
index aaaf31b3..00000000
--- a/g4f/Provider/unfinished/Replicate.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-
-from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import format_prompt, filter_none
-from ...typing import AsyncResult, Messages
-from ...requests import StreamSession, raise_for_status
-from ...image import ImageResponse
-from ...errors import ResponseError, MissingAuthError
-
-class Replicate(AsyncGeneratorProvider, ProviderModelMixin):
- url = "https://replicate.com"
- working = True
- default_model = "mistralai/mixtral-8x7b-instruct-v0.1"
- api_base = "https://api.replicate.com/v1/models/"
-
- @classmethod
- async def create_async_generator(
- cls,
- model: str,
- messages: Messages,
- api_key: str = None,
- proxy: str = None,
- timeout: int = 180,
- system_prompt: str = None,
- max_new_tokens: int = None,
- temperature: float = None,
- top_p: float = None,
- top_k: float = None,
- stop: list = None,
- extra_data: dict = {},
- headers: dict = {},
- **kwargs
- ) -> AsyncResult:
- model = cls.get_model(model)
- if api_key is None:
- raise MissingAuthError("api_key is missing")
- headers["Authorization"] = f"Bearer {api_key}"
- async with StreamSession(
- proxies={"all": proxy},
- headers=headers,
- timeout=timeout
- ) as session:
- data = {
- "stream": True,
- "input": {
- "prompt": format_prompt(messages),
- **filter_none(
- system_prompt=system_prompt,
- max_new_tokens=max_new_tokens,
- temperature=temperature,
- top_p=top_p,
- top_k=top_k,
- stop_sequences=",".join(stop) if stop else None
- ),
- **extra_data
- },
- }
- url = f"{cls.api_base.rstrip('/')}/{model}/predictions"
- async with session.post(url, json=data) as response:
- await raise_for_status(response)
- result = await response.json()
- if "id" not in result:
- raise ResponseError(f"Invalid response: {result}")
- async with session.get(result["urls"]["stream"], headers={"Accept": "text/event-stream"}) as response:
- await raise_for_status(response)
- event = None
- async for line in response.iter_lines():
- if line.startswith(b"event: "):
- event = line[7:]
- elif event == b"output":
- if line.startswith(b"data: "):
- yield line[6:].decode()
- elif not line.startswith(b"id: "):
- continue#yield "+"+line.decode()
- elif event == b"done":
- break \ No newline at end of file
diff --git a/g4f/Provider/you/har_file.py b/g4f/Provider/you/har_file.py
index 18a7144b..e58e44d6 100644
--- a/g4f/Provider/you/har_file.py
+++ b/g4f/Provider/you/har_file.py
@@ -4,7 +4,6 @@ import json
import os
import os.path
import random
-import requests
from ...requests import StreamSession, raise_for_status
from ...errors import MissingRequirementsError
@@ -21,7 +20,8 @@ class arkReq:
self.arkCookies = arkCookies
self.userAgent = userAgent
-arkPreURL = "https://telemetry.stytch.com/submit"
+telemetry_url = "https://telemetry.stytch.com/submit"
+public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
chatArks: list = None
def readHAR():
@@ -44,7 +44,7 @@ def readHAR():
# Error: not a HAR file!
continue
for v in harFile['log']['entries']:
- if arkPreURL in v['request']['url']:
+ if v['request']['url'] == telemetry_url:
chatArks.append(parseHAREntry(v))
if not chatArks:
raise NoValidHarFileError("No telemetry in .har files found")
@@ -62,24 +62,29 @@ def parseHAREntry(entry) -> arkReq:
return tmpArk
async def sendRequest(tmpArk: arkReq, proxy: str = None):
- async with StreamSession(headers=tmpArk.arkHeaders, cookies=tmpArk.arkCookies, proxies={"all": proxy}) as session:
+ async with StreamSession(headers=tmpArk.arkHeaders, cookies=tmpArk.arkCookies, proxy=proxy) as session:
async with session.post(tmpArk.arkURL, data=tmpArk.arkBody) as response:
await raise_for_status(response)
return await response.text()
-async def get_dfp_telemetry_id(proxy: str = None):
+async def create_telemetry_id(proxy: str = None):
global chatArks
if chatArks is None:
chatArks = readHAR()
return await sendRequest(random.choice(chatArks), proxy)
async def get_telemetry_ids(proxy: str = None) -> list:
+ try:
+ return [await create_telemetry_id(proxy)]
+ except NoValidHarFileError as e:
+ if debug.logging:
+ print(e)
if debug.logging:
print('Getting telemetry_id for you.com with nodriver')
try:
from nodriver import start
except ImportError:
- raise MissingRequirementsError('Install "nodriver" package | pip install -U nodriver')
+ raise MissingRequirementsError('Add .har file from you.com or install "nodriver" package | pip install -U nodriver')
try:
browser = await start()
tab = browser.main_tab
@@ -89,49 +94,11 @@ async def get_telemetry_ids(proxy: str = None) -> list:
await tab.sleep(1)
async def get_telemetry_id():
- public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
- telemetry_url = "https://telemetry.stytch.com/submit"
- return await tab.evaluate(f'this.GetTelemetryID("{public_token}", "{telemetry_url}");', await_promise=True)
-
- # for _ in range(500):
- # with open("hardir/you.com_telemetry_ids.txt", "a") as f:
- # f.write((await get_telemetry_id()) + "\n")
+ return await tab.evaluate(
+ f'this.GetTelemetryID("{public_token}", "{telemetry_url}");',
+ await_promise=True
+ )
- return [await get_telemetry_id() for _ in range(4)]
+ return [await get_telemetry_id() for _ in range(1)]
finally:
- try:
- await tab.close()
- except Exception as e:
- print(f"Error occurred while closing tab: {str(e)}")
- try:
- await browser.stop()
- except Exception as e:
- pass
-
- headers = {
- 'Accept': '*/*',
- 'Accept-Language': 'en,fr-FR;q=0.9,fr;q=0.8,es-ES;q=0.7,es;q=0.6,en-US;q=0.5,am;q=0.4,de;q=0.3',
- 'Connection': 'keep-alive',
- 'Content-type': 'application/x-www-form-urlencoded',
- 'Origin': 'https://you.com',
- 'Referer': 'https://you.com/',
- 'Sec-Fetch-Dest': 'empty',
- 'Sec-Fetch-Mode': 'cors',
- 'Sec-Fetch-Site': 'cross-site',
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
- 'sec-ch-ua': '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
- 'sec-ch-ua-mobile': '?0',
- 'sec-ch-ua-platform': '"macOS"',
- }
-
- proxies = {
- 'http': proxy,
- 'https': proxy} if proxy else None
-
- response = requests.post('https://telemetry.stytch.com/submit',
- headers=headers, data=payload, proxies=proxies)
-
- if '-' in response.text:
- print(f'telemetry generated: {response.text}')
-
- return (response.text)
+ await browser.stop() \ No newline at end of file
diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py
index ed39fc58..aaac1827 100644
--- a/g4f/api/__init__.py
+++ b/g4f/api/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import logging
import json
import uvicorn
@@ -8,14 +10,19 @@ from fastapi.exceptions import RequestValidationError
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
-from typing import List, Union, Optional
+from typing import Union, Optional
import g4f
import g4f.debug
from g4f.client import AsyncClient
from g4f.typing import Messages
-app = FastAPI()
+def create_app() -> FastAPI:
+ app = FastAPI()
+ api = Api(app)
+ api.register_routes()
+ api.register_validation_exception_handler()
+ return app
class ChatCompletionsConfig(BaseModel):
messages: Messages
@@ -29,16 +36,19 @@ class ChatCompletionsConfig(BaseModel):
web_search: Optional[bool] = None
proxy: Optional[str] = None
+list_ignored_providers: list[str] = None
+
+def set_list_ignored_providers(ignored: list[str]):
+ global list_ignored_providers
+ list_ignored_providers = ignored
+
class Api:
- def __init__(self, list_ignored_providers: List[str] = None) -> None:
- self.list_ignored_providers = list_ignored_providers
+ def __init__(self, app: FastAPI) -> None:
+ self.app = app
self.client = AsyncClient()
-
- def set_list_ignored_providers(self, list: list):
- self.list_ignored_providers = list
def register_validation_exception_handler(self):
- @app.exception_handler(RequestValidationError)
+ @self.app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
details = exc.errors()
modified_details = []
@@ -54,17 +64,17 @@ class Api:
)
def register_routes(self):
- @app.get("/")
+ @self.app.get("/")
async def read_root():
return RedirectResponse("/v1", 302)
- @app.get("/v1")
+ @self.app.get("/v1")
async def read_root_v1():
return HTMLResponse('g4f API: Go to '
'<a href="/v1/chat/completions">chat/completions</a> '
'or <a href="/v1/models">models</a>.')
- @app.get("/v1/models")
+ @self.app.get("/v1/models")
async def models():
model_list = dict(
(model, g4f.models.ModelUtils.convert[model])
@@ -78,7 +88,7 @@ class Api:
} for model_id, model in model_list.items()]
return JSONResponse(model_list)
- @app.get("/v1/models/{model_name}")
+ @self.app.get("/v1/models/{model_name}")
async def model_info(model_name: str):
try:
model_info = g4f.models.ModelUtils.convert[model_name]
@@ -91,7 +101,7 @@ class Api:
except:
return JSONResponse({"error": "The model does not exist."})
- @app.post("/v1/chat/completions")
+ @self.app.post("/v1/chat/completions")
async def chat_completions(config: ChatCompletionsConfig, request: Request = None, provider: str = None):
try:
config.provider = provider if config.provider is None else config.provider
@@ -103,7 +113,7 @@ class Api:
config.api_key = auth_header
response = self.client.chat.completions.create(
**config.dict(exclude_none=True),
- ignored=self.list_ignored_providers
+ ignored=list_ignored_providers
)
except Exception as e:
logging.exception(e)
@@ -125,14 +135,10 @@ class Api:
return StreamingResponse(streaming(), media_type="text/event-stream")
- @app.post("/v1/completions")
+ @self.app.post("/v1/completions")
async def completions():
return Response(content=json.dumps({'info': 'Not working yet.'}, indent=4), media_type="application/json")
-api = Api()
-api.register_routes()
-api.register_validation_exception_handler()
-
def format_exception(e: Exception, config: ChatCompletionsConfig) -> str:
last_provider = g4f.get_last_provider(True)
return json.dumps({
@@ -156,4 +162,4 @@ def run_api(
host, port = bind.split(":")
if debug:
g4f.debug.logging = True
- uvicorn.run("g4f.api:app", host=host, port=int(port), workers=workers, use_colors=use_colors)# \ No newline at end of file
+ uvicorn.run("g4f.api:create_app", host=host, port=int(port), workers=workers, use_colors=use_colors, factory=True)# \ No newline at end of file
diff --git a/g4f/cli.py b/g4f/cli.py
index 6b39091d..86fc2dbb 100644
--- a/g4f/cli.py
+++ b/g4f/cli.py
@@ -1,7 +1,11 @@
+from __future__ import annotations
+
import argparse
from g4f import Provider
from g4f.gui.run import gui_parser, run_gui_args
+from g4f.cookies import read_cookie_files
+from g4f import debug
def main():
parser = argparse.ArgumentParser(description="Run gpt4free")
@@ -10,28 +14,36 @@ def main():
api_parser.add_argument("--bind", default="0.0.0.0:1337", help="The bind string.")
api_parser.add_argument("--debug", action="store_true", help="Enable verbose logging.")
api_parser.add_argument("--workers", type=int, default=None, help="Number of workers.")
- api_parser.add_argument("--disable_colors", action="store_true", help="Don't use colors.")
+ api_parser.add_argument("--disable-colors", action="store_true", help="Don't use colors.")
+ api_parser.add_argument("--ignore-cookie-files", action="store_true", help="Don't read .har and cookie files.")
api_parser.add_argument("--ignored-providers", nargs="+", choices=[provider for provider in Provider.__map__],
default=[], help="List of providers to ignore when processing request.")
subparsers.add_parser("gui", parents=[gui_parser()], add_help=False)
args = parser.parse_args()
if args.mode == "api":
- import g4f.api
- g4f.api.api.set_list_ignored_providers(
- args.ignored_providers
- )
- g4f.api.run_api(
- bind=args.bind,
- debug=args.debug,
- workers=args.workers,
- use_colors=not args.disable_colors
- )
+ run_api_args(args)
elif args.mode == "gui":
run_gui_args(args)
else:
parser.print_help()
exit(1)
+def run_api_args(args):
+ if args.debug:
+ debug.logging = True
+ if not args.ignore_cookie_files:
+ read_cookie_files()
+ import g4f.api
+ g4f.api.set_list_ignored_providers(
+ args.ignored_providers
+ )
+ g4f.api.run_api(
+ bind=args.bind,
+ debug=args.debug,
+ workers=args.workers,
+ use_colors=not args.disable_colors
+ )
+
if __name__ == "__main__":
main()
diff --git a/g4f/cookies.py b/g4f/cookies.py
index 578be8db..efd6e969 100644
--- a/g4f/cookies.py
+++ b/g4f/cookies.py
@@ -2,6 +2,7 @@ from __future__ import annotations
import os
import time
+import json
try:
from platformdirs import user_config_dir
@@ -38,6 +39,7 @@ def get_cookies(domain_name: str = '', raise_requirements_error: bool = True, si
Returns:
Dict[str, str]: A dictionary of cookie names and values.
"""
+ global _cookies
if domain_name in _cookies:
return _cookies[domain_name]
@@ -46,6 +48,7 @@ def get_cookies(domain_name: str = '', raise_requirements_error: bool = True, si
return cookies
def set_cookies(domain_name: str, cookies: Cookies = None) -> None:
+ global _cookies
if cookies:
_cookies[domain_name] = cookies
elif domain_name in _cookies:
@@ -84,6 +87,61 @@ def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool
print(f"Error reading cookies from {cookie_fn.__name__} for {domain_name}: {e}")
return cookies
+def read_cookie_files(dirPath: str = "./har_and_cookies"):
+ global _cookies
+ harFiles = []
+ cookieFiles = []
+ for root, dirs, files in os.walk(dirPath):
+ for file in files:
+ if file.endswith(".har"):
+ harFiles.append(os.path.join(root, file))
+ elif file.endswith(".json"):
+ cookieFiles.append(os.path.join(root, file))
+ _cookies = {}
+ for path in harFiles:
+ with open(path, 'rb') as file:
+ try:
+ harFile = json.load(file)
+ except json.JSONDecodeError:
+ # Error: not a HAR file!
+ continue
+ if debug.logging:
+ print("Read .har file:", path)
+ new_cookies = {}
+ for v in harFile['log']['entries']:
+ v_cookies = {}
+ for c in v['request']['cookies']:
+ if c['domain'] not in v_cookies:
+ v_cookies[c['domain']] = {}
+ v_cookies[c['domain']][c['name']] = c['value']
+ for domain, c in v_cookies.items():
+ _cookies[domain] = c
+ new_cookies[domain] = len(c)
+ if debug.logging:
+ for domain, new_values in new_cookies.items():
+ print(f"Cookies added: {new_values} from {domain}")
+ for path in cookieFiles:
+ with open(path, 'rb') as file:
+ try:
+ cookieFile = json.load(file)
+ except json.JSONDecodeError:
+ # Error: not a json file!
+ continue
+ if not isinstance(cookieFile, list):
+ continue
+ if debug.logging:
+ print("Read cookie file:", path)
+ new_cookies = {}
+ for c in cookieFile:
+ if isinstance(c, dict) and "domain" in c:
+ if c["domain"] not in new_cookies:
+ new_cookies[c["domain"]] = {}
+ new_cookies[c["domain"]][c["name"]] = c["value"]
+ for domain, new_values in new_cookies.items():
+ if debug.logging:
+ print(f"Cookies added: {len(new_values)} from {domain}")
+ _cookies[domain] = new_values
+
def _g4f(domain_name: str) -> list:
"""
Load cookies from the 'g4f' browser (if exists).
diff --git a/g4f/gui/__init__.py b/g4f/gui/__init__.py
index f5e448ad..930a2aa0 100644
--- a/g4f/gui/__init__.py
+++ b/g4f/gui/__init__.py
@@ -12,9 +12,6 @@ def run_gui(host: str = '0.0.0.0', port: int = 8080, debug: bool = False) -> Non
if import_error is not None:
raise MissingRequirementsError(f'Install "gui" requirements | pip install -U g4f[gui]\n{import_error}')
- if debug:
- from g4f import debug
- debug.logging = True
config = {
'host' : host,
'port' : port,
diff --git a/g4f/gui/gui_parser.py b/g4f/gui/gui_parser.py
index ad458f5c..9fd70bef 100644
--- a/g4f/gui/gui_parser.py
+++ b/g4f/gui/gui_parser.py
@@ -5,4 +5,5 @@ def gui_parser():
parser.add_argument("-host", type=str, default="0.0.0.0", help="hostname")
parser.add_argument("-port", type=int, default=8080, help="port")
parser.add_argument("-debug", action="store_true", help="debug mode")
+ parser.add_argument("--ignore-cookie-files", action="store_true", help="Don't read .har and cookie files.")
return parser \ No newline at end of file
diff --git a/g4f/gui/run.py b/g4f/gui/run.py
index 91314d2d..9b1c527c 100644
--- a/g4f/gui/run.py
+++ b/g4f/gui/run.py
@@ -1,6 +1,12 @@
from .gui_parser import gui_parser
+from ..cookies import read_cookie_files
+import g4f.debug
def run_gui_args(args):
+ if args.debug:
+ g4f.debug.logging = True
+ if not args.ignore_cookie_files:
+ read_cookie_files()
from g4f.gui import run_gui
host = args.host
port = args.port
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index 47c25eb7..3d9f6a1c 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -16,7 +16,8 @@ conversations: dict[dict[str, BaseConversation]] = {}
class Api():
- def get_models(self) -> list[str]:
+ @staticmethod
+ def get_models() -> list[str]:
"""
Return a list of all models.
@@ -27,7 +28,8 @@ class Api():
"""
return models._all_models
- def get_provider_models(self, provider: str) -> list[dict]:
+ @staticmethod
+ def get_provider_models(provider: str) -> list[dict]:
if provider in __map__:
provider: ProviderType = __map__[provider]
if issubclass(provider, ProviderModelMixin):
@@ -40,7 +42,28 @@ class Api():
else:
return [];
- def get_providers(self) -> list[str]:
+ @staticmethod
+ def get_image_models() -> list[dict]:
+ image_models = []
+ for provider in __providers__:
+ if hasattr(provider, "image_models"):
+ if hasattr(provider, "get_models"):
+ provider.get_models()
+ parent = provider
+ if hasattr(provider, "parent"):
+ parent = __map__[provider.parent]
+ for model in provider.image_models:
+ image_models.append({
+ "provider": parent.__name__,
+ "url": parent.url,
+ "label": parent.label if hasattr(parent, "label") else None,
+ "image_model": model,
+ "vision_model": parent.default_vision_model if hasattr(parent, "default_vision_model") else None
+ })
+ return image_models
+
+ @staticmethod
+ def get_providers() -> list[str]:
"""
Return a list of all working providers.
"""
@@ -58,7 +81,8 @@ class Api():
if provider.working
}
- def get_version(self):
+ @staticmethod
+ def get_version():
"""
Returns the current and latest version of the application.
diff --git a/g4f/gui/server/backend.py b/g4f/gui/server/backend.py
index d30b97d9..d9e31c0e 100644
--- a/g4f/gui/server/backend.py
+++ b/g4f/gui/server/backend.py
@@ -31,6 +31,10 @@ class Backend_Api(Api):
'function': self.get_provider_models,
'methods': ['GET']
},
+ '/backend-api/v2/image_models': {
+ 'function': self.get_image_models,
+ 'methods': ['GET']
+ },
'/backend-api/v2/providers': {
'function': self.get_providers,
'methods': ['GET']
diff --git a/g4f/image.py b/g4f/image.py
index ed8af103..270b59ad 100644
--- a/g4f/image.py
+++ b/g4f/image.py
@@ -86,7 +86,7 @@ def is_data_uri_an_image(data_uri: str) -> bool:
if image_format not in ALLOWED_EXTENSIONS and image_format != "svg+xml":
raise ValueError("Invalid image format (from mime file type).")
-def is_accepted_format(binary_data: bytes) -> bool:
+def is_accepted_format(binary_data: bytes) -> str:
"""
Checks if the given binary data represents an image with an accepted format.
@@ -241,6 +241,13 @@ def to_bytes(image: ImageType) -> bytes:
else:
return image.read()
+def to_data_uri(image: ImageType) -> str:
+ if not isinstance(image, str):
+ data = to_bytes(image)
+ data_base64 = base64.b64encode(data).decode()
+ return f"data:{is_accepted_format(data)};base64,{data_base64}"
+ return image
+
class ImageResponse:
def __init__(
self,
diff --git a/g4f/providers/base_provider.py b/g4f/providers/base_provider.py
index 685a6f98..5624c9e1 100644
--- a/g4f/providers/base_provider.py
+++ b/g4f/providers/base_provider.py
@@ -271,13 +271,13 @@ class AsyncGeneratorProvider(AsyncProvider):
raise NotImplementedError()
class ProviderModelMixin:
- default_model: str
+ default_model: str = None
models: list[str] = []
model_aliases: dict[str, str] = {}
@classmethod
def get_models(cls) -> list[str]:
- if not cls.models:
+ if not cls.models and cls.default_model is not None:
return [cls.default_model]
return cls.models
diff --git a/g4f/providers/helper.py b/g4f/providers/helper.py
index 0ec9aac2..7ebe0d02 100644
--- a/g4f/providers/helper.py
+++ b/g4f/providers/helper.py
@@ -3,7 +3,7 @@ from __future__ import annotations
import random
import string
-from ..typing import Messages
+from ..typing import Messages, Cookies
def format_prompt(messages: Messages, add_special_tokens=False) -> str:
"""
@@ -56,4 +56,7 @@ def filter_none(**kwargs) -> dict:
key: value
for key, value in kwargs.items()
if value is not None
- } \ No newline at end of file
+ }
+
+def format_cookies(cookies: Cookies) -> str:
+ return "; ".join([f"{k}={v}" for k, v in cookies.items()]) \ No newline at end of file