summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md30
-rw-r--r--g4f/Provider/BingCreateImages.py40
-rw-r--r--g4f/Provider/Blackbox.py10
-rw-r--r--g4f/Provider/bing/create_images.py30
-rw-r--r--g4f/Provider/needs_auth/OpenaiChat.py77
-rw-r--r--g4f/Provider/openai/har_file.py6
-rw-r--r--g4f/Provider/you/har_file.py18
-rw-r--r--g4f/cookies.py37
-rw-r--r--g4f/gui/client/index.html25
-rw-r--r--g4f/gui/client/static/css/style.css13
-rw-r--r--g4f/gui/client/static/js/chat.v1.js23
-rw-r--r--g4f/image.py4
12 files changed, 139 insertions, 174 deletions
diff --git a/README.md b/README.md
index 3fc3cddb..970206f7 100644
--- a/README.md
+++ b/README.md
@@ -236,19 +236,39 @@ set_cookies(".google.com", {
})
```
-Alternatively, you can place your .har and cookie files in the `/har_and_cookies` directory. To export a cookie file, use the EditThisCookie extension available on the Chrome Web Store: [EditThisCookie Extension](https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg).
+#### Using .har and Cookie Files
-You can also create .har files to capture cookies. If you need further assistance, refer to the next section.
+You can place `.har` and cookie files in the default `./har_and_cookies` directory. To export a cookie file, use the [EditThisCookie Extension](https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg) available on the Chrome Web Store.
-```bash
-python -m g4f.cli api --debug
+#### Creating .har Files to Capture Cookies
+
+To capture cookies, you can also create `.har` files. For more details, refer to the next section.
+
+#### Changing the Cookies Directory and Loading Cookie Files in Python
+
+You can change the cookies directory and load cookie files in your Python environment. To set the cookies directory relative to your Python file, use the following code:
+
+```python
+import os.path
+from g4f.cookies import set_cookies_dir, read_cookie_files
+
+import g4f.debug
+g4f.debug.logging = True
+
+cookies_dir = os.path.join(os.path.dirname(__file__), "har_and_cookies")
+set_cookies_dir(cookies_dir)
+read_cookie_files(cookies_dir)
```
+
+### Debug Mode
+
+If you enable debug mode, you will see logs similar to the following:
+
```
Read .har file: ./har_and_cookies/you.com.har
Cookies added: 10 from .you.com
Read cookie file: ./har_and_cookies/google.json
Cookies added: 16 from .google.com
-Starting server... [g4f v-0.0.0] (debug)
```
#### .HAR File for OpenaiChat Provider
diff --git a/g4f/Provider/BingCreateImages.py b/g4f/Provider/BingCreateImages.py
index 79dc5665..7a206c8f 100644
--- a/g4f/Provider/BingCreateImages.py
+++ b/g4f/Provider/BingCreateImages.py
@@ -1,18 +1,14 @@
from __future__ import annotations
-import asyncio
-import os
-from typing import Iterator, Union
-
from ..cookies import get_cookies
from ..image import ImageResponse
-from ..errors import MissingRequirementsError, MissingAuthError
+from ..errors import MissingAuthError
from ..typing import AsyncResult, Messages, Cookies
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from .bing.create_images import create_images, create_session, get_cookies_from_browser
+from .bing.create_images import create_images, create_session
class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
- label = "Microsoft Designer"
+ label = "Microsoft Designer in Bing"
parent = "Bing"
url = "https://www.bing.com/images/create"
working = True
@@ -38,30 +34,9 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
**kwargs
) -> AsyncResult:
session = BingCreateImages(cookies, proxy, api_key)
- yield await session.create_async(messages[-1]["content"])
-
- def create(self, prompt: str) -> Iterator[Union[ImageResponse, str]]:
- """
- Generator for creating imagecompletion based on a prompt.
-
- Args:
- prompt (str): Prompt to generate images.
-
- Yields:
- Generator[str, None, None]: The final output as markdown formatted string with images.
- """
- cookies = self.cookies or get_cookies(".bing.com", False)
- if cookies is None or "_U" not in cookies:
- login_url = os.environ.get("G4F_LOGIN_URL")
- if login_url:
- yield f"Please login: [Bing]({login_url})\n\n"
- try:
- self.cookies = get_cookies_from_browser(self.proxy)
- except MissingRequirementsError as e:
- raise MissingAuthError(f'Missing "_U" cookie. {e}')
- yield asyncio.run(self.create_async(prompt))
+ yield await session.generate(messages[-1]["content"])
- async def create_async(self, prompt: str) -> ImageResponse:
+ async def generate(self, prompt: str) -> ImageResponse:
"""
Asynchronously creates a markdown formatted string with images based on the prompt.
@@ -74,7 +49,6 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
cookies = self.cookies or get_cookies(".bing.com", False)
if cookies is None or "_U" not in cookies:
raise MissingAuthError('Missing "_U" cookie')
- proxy = self.proxy or os.environ.get("G4F_PROXY")
- async with create_session(cookies, proxy) as session:
- images = await create_images(session, prompt, proxy)
+ async with create_session(cookies, self.proxy) as session:
+ images = await create_images(session, prompt)
return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"} if len(images) > 1 else {}) \ No newline at end of file
diff --git a/g4f/Provider/Blackbox.py b/g4f/Provider/Blackbox.py
index 6e43a7a4..6e1e3949 100644
--- a/g4f/Provider/Blackbox.py
+++ b/g4f/Provider/Blackbox.py
@@ -4,7 +4,8 @@ import uuid
import secrets
from aiohttp import ClientSession
-from ..typing import AsyncResult, Messages
+from ..typing import AsyncResult, Messages, ImageType
+from ..image import to_data_uri
from .base_provider import AsyncGeneratorProvider
class Blackbox(AsyncGeneratorProvider):
@@ -17,8 +18,15 @@ class Blackbox(AsyncGeneratorProvider):
model: str,
messages: Messages,
proxy: str = None,
+ image: ImageType = None,
+ image_name: str = None,
**kwargs
) -> AsyncResult:
+ if image is not None:
+ messages[-1]["data"] = {
+ "fileText": image_name,
+ "imageBase64": to_data_uri(image)
+ }
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Accept": "*/*",
diff --git a/g4f/Provider/bing/create_images.py b/g4f/Provider/bing/create_images.py
index 44303c21..7a08ddfe 100644
--- a/g4f/Provider/bing/create_images.py
+++ b/g4f/Provider/bing/create_images.py
@@ -1,7 +1,3 @@
-"""
-This module provides functionalities for creating and managing images using Bing's service.
-It includes functions for user login, session creation, image creation, and processing.
-"""
from __future__ import annotations
import asyncio
@@ -17,9 +13,7 @@ try:
except ImportError:
has_requirements = False
-from ...providers.create_images import CreateImagesProvider
from ..helper import get_connector
-from ...providers.types import ProviderType
from ...errors import MissingRequirementsError, RateLimitError
from ...webdriver import WebDriver, get_driver_cookies, get_browser
@@ -101,7 +95,7 @@ def create_session(cookies: Dict[str, str], proxy: str = None, connector: BaseCo
headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
return ClientSession(headers=headers, connector=get_connector(connector, proxy))
-async def create_images(session: ClientSession, prompt: str, proxy: str = None, timeout: int = TIMEOUT_IMAGE_CREATION) -> List[str]:
+async def create_images(session: ClientSession, prompt: str, timeout: int = TIMEOUT_IMAGE_CREATION) -> List[str]:
"""
Creates images based on a given prompt using Bing's service.
@@ -132,7 +126,7 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
raise RuntimeError(f"Create images failed: {error}")
if response.status != 302:
url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=3&FORM=GENCRE"
- async with session.post(url, allow_redirects=False, proxy=proxy, timeout=timeout) as response:
+ async with session.post(url, allow_redirects=False, timeout=timeout) as response:
if response.status != 302:
raise RuntimeError(f"Create images failed. Code: {response.status}")
@@ -185,22 +179,4 @@ def read_images(html_content: str) -> List[str]:
raise RuntimeError("Bad images found")
if not images:
raise RuntimeError("No images found")
- return images
-
-def patch_provider(provider: ProviderType) -> CreateImagesProvider:
- """
- Patches a provider to include image creation capabilities.
-
- Args:
- provider (ProviderType): The provider to be patched.
-
- Returns:
- CreateImagesProvider: The patched provider with image creation capabilities.
- """
- from ..BingCreateImages import BingCreateImages
- service = BingCreateImages()
- return CreateImagesProvider(
- provider,
- service.create,
- service.create_async
- ) \ No newline at end of file
+ return images \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index a202f45e..d8ea4fad 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -26,7 +26,7 @@ from ...webdriver import get_browser
from ...typing import AsyncResult, Messages, Cookies, ImageType, AsyncIterator
from ...requests import get_args_from_browser, raise_for_status
from ...requests.aiohttp import StreamSession
-from ...image import to_image, to_bytes, ImageResponse, ImageRequest
+from ...image import ImageResponse, ImageRequest, to_image, to_bytes, is_accepted_format
from ...errors import MissingAuthError, ResponseError
from ...providers.conversation import BaseConversation
from ..helper import format_cookies
@@ -138,23 +138,22 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
An ImageRequest object that contains the download URL, file name, and other data
"""
# Convert the image to a PIL Image object and get the extension
- image = to_image(image)
- extension = image.format.lower()
- # Convert the image to a bytes object and get the size
data_bytes = to_bytes(image)
+ image = to_image(data_bytes)
+ extension = image.format.lower()
data = {
- "file_name": image_name if image_name else f"{image.width}x{image.height}.{extension}",
+ "file_name": "" if image_name is None else image_name,
"file_size": len(data_bytes),
"use_case": "multimodal"
}
# Post the image data to the service and get the image data
async with session.post(f"{cls.url}/backend-api/files", json=data, headers=headers) as response:
- cls._update_request_args()
+ cls._update_request_args(session)
await raise_for_status(response)
image_data = {
**data,
**await response.json(),
- "mime_type": f"image/{extension}",
+ "mime_type": is_accepted_format(data_bytes),
"extension": extension,
"height": image.height,
"width": image.width
@@ -275,7 +274,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
first_part = line["message"]["content"]["parts"][0]
if "asset_pointer" not in first_part or "metadata" not in first_part:
return
- if first_part["metadata"] is None:
+ if first_part["metadata"] is None or first_part["metadata"]["dalle"] is None:
return
prompt = first_part["metadata"]["dalle"]["prompt"]
file_id = first_part["asset_pointer"].split("file-service://", 1)[1]
@@ -365,49 +364,17 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
) as session:
if cls._expires is not None and cls._expires < time.time():
cls._headers = cls._api_key = None
- if cls._headers is None or cookies is not None:
- cls._create_request_args(cookies)
- api_key = kwargs["access_token"] if "access_token" in kwargs else api_key
- if api_key is not None:
- cls._set_api_key(api_key)
-
- if cls.default_model is None and (not cls.needs_auth or cls._api_key is not None):
- if cls._api_key is None:
- cls._create_request_args(cookies)
- async with session.get(
- f"{cls.url}/",
- headers=DEFAULT_HEADERS
- ) as response:
- cls._update_request_args(session)
- await raise_for_status(response)
- try:
- if not model:
- cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
- else:
- cls.default_model = cls.get_model(model)
- except MissingAuthError:
- pass
- except Exception as e:
- api_key = cls._api_key = None
- cls._create_request_args()
- if debug.logging:
- print("OpenaiChat: Load default model failed")
- print(f"{e.__class__.__name__}: {e}")
-
arkose_token = None
proofTokens = None
- if cls.default_model is None:
- error = None
- try:
- arkose_token, api_key, cookies, headers, proofTokens = 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(proxy)
+ try:
+ arkose_token, api_key, cookies, headers, proofTokens = await getArkoseAndAccessToken(proxy)
+ cls._create_request_args(cookies, headers)
+ cls._set_api_key(api_key)
+ except NoValidHarFileError as e:
if cls._api_key is None and cls.needs_auth:
- raise error
+ raise e
+
+ if cls.default_model is None:
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
try:
@@ -461,7 +428,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
)
ws = None
if need_arkose:
- async with session.post("https://chatgpt.com/backend-api/register-websocket", headers=cls._headers) as response:
+ async with session.post(f"{cls.url}/backend-api/register-websocket", headers=cls._headers) as response:
wss_url = (await response.json()).get("wss_url")
if wss_url:
ws = await session.ws_connect(wss_url)
@@ -490,7 +457,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
if proofofwork is not None:
headers["Openai-Sentinel-Proof-Token"] = proofofwork
async with session.post(
- f"{cls.url}/backend-anon/conversation" if cls._api_key is None else
+ f"{cls.url}/backend-anon/conversation"
+ if cls._api_key is None else
f"{cls.url}/backend-api/conversation",
json=data,
headers=headers
@@ -580,12 +548,9 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
raise RuntimeError(line["error"])
if "message_type" not in line["message"]["metadata"]:
return
- try:
- image_response = await cls.get_generated_image(session, cls._headers, line)
- if image_response is not None:
- yield image_response
- except Exception as e:
- yield e
+ image_response = await cls.get_generated_image(session, cls._headers, line)
+ if image_response is not None:
+ yield image_response
if line["message"]["author"]["role"] != "assistant":
return
if line["message"]["content"]["content_type"] != "text":
diff --git a/g4f/Provider/openai/har_file.py b/g4f/Provider/openai/har_file.py
index 8dcbe44e..2287d6a6 100644
--- a/g4f/Provider/openai/har_file.py
+++ b/g4f/Provider/openai/har_file.py
@@ -12,6 +12,7 @@ from copy import deepcopy
from .crypt import decrypt, encrypt
from ...requests import StreamSession
+from ...cookies import get_cookies_dir
from ... import debug
class NoValidHarFileError(Exception):
@@ -36,17 +37,14 @@ proofTokens: list = []
def readHAR():
global proofTokens
- dirPath = "./"
harPath = []
chatArks = []
accessToken = None
cookies = {}
- for root, dirs, files in os.walk(dirPath):
+ for root, dirs, files in os.walk(get_cookies_dir()):
for file in files:
if file.endswith(".har"):
harPath.append(os.path.join(root, file))
- if harPath:
- break
if not harPath:
raise NoValidHarFileError("No .har file found")
for path in harPath:
diff --git a/g4f/Provider/you/har_file.py b/g4f/Provider/you/har_file.py
index 870c388e..71d741fd 100644
--- a/g4f/Provider/you/har_file.py
+++ b/g4f/Provider/you/har_file.py
@@ -7,6 +7,7 @@ import random
import logging
from ...requests import StreamSession, raise_for_status
+from ...cookies import get_cookies_dir
from ...errors import MissingRequirementsError
from ... import debug
@@ -28,10 +29,9 @@ public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
chatArks: list = None
def readHAR():
- dirPath = "./"
harPath = []
chatArks = []
- for root, dirs, files in os.walk(dirPath):
+ for root, dirs, files in os.walk(get_cookies_dir()):
for file in files:
if file.endswith(".har"):
harPath.append(os.path.join(root, file))
@@ -65,16 +65,10 @@ def parseHAREntry(entry) -> arkReq:
return tmpArk
async def sendRequest(tmpArk: arkReq, proxy: str = None):
- try:
- 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()
- except RuntimeError as e:
- if str(e) == "Event loop is closed":
- print("Event loop is closed error occurred in sendRequest.")
- else:
- raise
+ 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 create_telemetry_id(proxy: str = None):
global chatArks
diff --git a/g4f/cookies.py b/g4f/cookies.py
index 9dfe0ca5..0a25c41e 100644
--- a/g4f/cookies.py
+++ b/g4f/cookies.py
@@ -23,8 +23,9 @@ from .typing import Dict, Cookies
from .errors import MissingRequirementsError
from . import debug
-# Global variable to store cookies
-_cookies: Dict[str, Cookies] = {}
+class CookiesConfig():
+ cookies: Dict[str, Cookies] = {}
+ cookies_dir: str = "./har_and_cookies"
DOMAINS = [
".bing.com",
@@ -48,20 +49,18 @@ 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]
+ if domain_name in CookiesConfig.cookies:
+ return CookiesConfig.cookies[domain_name]
cookies = load_cookies_from_browsers(domain_name, raise_requirements_error, single_browser)
- _cookies[domain_name] = cookies
+ CookiesConfig.cookies[domain_name] = cookies
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:
- _cookies.pop(domain_name)
+ CookiesConfig.cookies[domain_name] = cookies
+ elif domain_name in CookiesConfig.cookies:
+ CookiesConfig.cookies.pop(domain_name)
def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool = True, single_browser: bool = False) -> Cookies:
"""
@@ -96,7 +95,13 @@ 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"):
+def set_cookies_dir(dir: str) -> None:
+ CookiesConfig.cookies_dir = dir
+
+def get_cookies_dir() -> str:
+ return CookiesConfig.cookies_dir
+
+def read_cookie_files(dirPath: str = None):
def get_domain(v: dict) -> str:
host = [h["value"] for h in v['request']['headers'] if h["name"].lower() in ("host", ":authority")]
if not host:
@@ -106,16 +111,16 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
if d in host:
return d
- global _cookies
harFiles = []
cookieFiles = []
- for root, dirs, files in os.walk(dirPath):
+ for root, dirs, files in os.walk(CookiesConfig.cookies_dir if dirPath is None else 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 = {}
+
+ CookiesConfig.cookies = {}
for path in harFiles:
with open(path, 'rb') as file:
try:
@@ -134,7 +139,7 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
for c in v['request']['cookies']:
v_cookies[c['name']] = c['value']
if len(v_cookies) > 0:
- _cookies[domain] = v_cookies
+ CookiesConfig.cookies[domain] = v_cookies
new_cookies[domain] = len(v_cookies)
if debug.logging:
for domain, new_values in new_cookies.items():
@@ -159,7 +164,7 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
for domain, new_values in new_cookies.items():
if debug.logging:
print(f"Cookies added: {len(new_values)} from {domain}")
- _cookies[domain] = new_values
+ CookiesConfig.cookies[domain] = new_values
def _g4f(domain_name: str) -> list:
"""
diff --git a/g4f/gui/client/index.html b/g4f/gui/client/index.html
index 064e4594..d3cddd3c 100644
--- a/g4f/gui/client/index.html
+++ b/g4f/gui/client/index.html
@@ -93,22 +93,22 @@
<div class="paper">
<h3>Settings</h3>
<div class="field">
- <span class="label">Web Access</span>
+ <span class="label">Web Access with DuckDuckGo</span>
<input type="checkbox" id="switch" />
<label for="switch" class="toogle" title="Add the pages of the first 5 search results to the query."></label>
</div>
<div class="field">
- <span class="label">Disable History</span>
+ <span class="label">Disable Conversation History</span>
<input type="checkbox" id="history" />
<label for="history" class="toogle" title="To improve the reaction time or if you have trouble with large conversations."></label>
</div>
<div class="field">
- <span class="label">Hide System prompt</span>
+ <span class="label">Hide System-prompt</span>
<input type="checkbox" id="hide-systemPrompt" />
<label for="hide-systemPrompt" class="toogle" title="For more space on phones"></label>
</div>
<div class="field">
- <span class="label">Auto continue</span>
+ <span class="label">Auto continue in ChatGPT</span>
<input id="auto_continue" type="checkbox" name="auto_continue" checked/>
<label for="auto_continue" class="toogle" title="Continue large responses in OpenaiChat"></label>
</div>
@@ -121,8 +121,8 @@
<input type="text" id="recognition-language" value="" placeholder="navigator.language"/>
</div>
<div class="field box">
- <label for="Bing-api_key" class="label" title="">Bing:</label>
- <textarea id="Bing-api_key" name="Bing[api_key]" class="BingCreateImages-api_key" placeholder="&quot;_U&quot; cookie"></textarea>
+ <label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
+ <textarea id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"></textarea>
</div>
<div class="field box">
<label for="DeepInfra-api_key" class="label" title="">DeepInfra:</label>
@@ -145,14 +145,14 @@
<textarea id="Openai-api_key" name="Openai[api_key]" placeholder="api_key"></textarea>
</div>
<div class="field box">
- <label for="OpenaiAccount-api_key" class="label" title="">OpenAI ChatGPT:</label>
- <textarea id="OpenaiAccount-api_key" name="OpenaiAccount[api_key]" class="OpenaiChat-api_key" placeholder="access_key"></textarea>
- </div>
- <div class="field box">
<label for="OpenRouter-api_key" class="label" title="">OpenRouter:</label>
<textarea id="OpenRouter-api_key" name="OpenRouter[api_key]" placeholder="api_key"></textarea>
</div>
<div class="field box">
+ <label for="PerplexityApi-api_key" class="label" title="">Perplexity API:</label>
+ <textarea id="PerplexityApi-api_key" name="PerplexityApi[api_key]" placeholder="api_key"></textarea>
+ </div>
+ <div class="field box">
<label for="Replicate-api_key" class="label" title="">Replicate:</label>
<textarea id="Replicate-api_key" name="Replicate[api_key]" class="ReplicateImage-api_key" placeholder="api_key"></textarea>
</div>
@@ -173,7 +173,10 @@
<div id="messages" class="box"></div>
<div class="toolbar">
<div id="input-count" class="">
- &nbsp;
+ <button class="hide-input">
+ <i class="fa-solid fa-angles-down"></i>
+ </button>
+ <span class="text"></span>
</div>
<div class="stop_generating stop_generating-hidden">
<button id="cancelButton">
diff --git a/g4f/gui/client/static/css/style.css b/g4f/gui/client/static/css/style.css
index 01bc17fa..005cb8bf 100644
--- a/g4f/gui/client/static/css/style.css
+++ b/g4f/gui/client/static/css/style.css
@@ -457,7 +457,11 @@ body {
#input-count {
width: fit-content;
font-size: 12px;
- padding: 6px var(--inner-gap);
+ padding: 6px 6px;
+}
+
+#input-count .text {
+ padding: 0 4px;
}
.stop_generating, .toolbar .regenerate {
@@ -491,6 +495,13 @@ body {
animation: show_popup 0.4s;
}
+.toolbar .hide-input {
+ background: transparent;
+ border: none;
+ color: var(--colour-3);
+ cursor: pointer;
+}
+
@keyframes show_popup {
from {
opacity: 0;
diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js
index a0178e63..46d5039e 100644
--- a/g4f/gui/client/static/js/chat.v1.js
+++ b/g4f/gui/client/static/js/chat.v1.js
@@ -11,7 +11,7 @@ const imageInput = document.getElementById("image");
const cameraInput = document.getElementById("camera");
const fileInput = document.getElementById("file");
const microLabel = document.querySelector(".micro-label");
-const inputCount = document.getElementById("input-count");
+const inputCount = document.getElementById("input-count").querySelector(".text");
const providerSelect = document.getElementById("provider");
const modelSelect = document.getElementById("model");
const modelProvider = document.getElementById("model2");
@@ -41,9 +41,7 @@ appStorage = window.localStorage || {
length: 0
}
-const markdown = window.markdownit({
- html: true,
-});
+const markdown = window.markdownit();
const markdown_render = (content) => {
return markdown.render(content
.replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
@@ -813,6 +811,17 @@ document.getElementById("regenerateButton").addEventListener("click", async () =
await ask_gpt();
});
+const hide_input = document.querySelector(".toolbar .hide-input");
+hide_input.addEventListener("click", async (e) => {
+ const icon = hide_input.querySelector("i");
+ const func = icon.classList.contains("fa-angles-down") ? "add" : "remove";
+ const remv = icon.classList.contains("fa-angles-down") ? "remove" : "add";
+ icon.classList[func]("fa-angles-up");
+ icon.classList[remv]("fa-angles-down");
+ document.querySelector(".conversation .user-input").classList[func]("hidden");
+ document.querySelector(".conversation .buttons").classList[func]("hidden");
+});
+
const uuid = () => {
return `xxxxxxxx-xxxx-4xxx-yxxx-${Date.now().toString(16)}`.replace(
/[xy]/g,
@@ -1016,7 +1025,7 @@ const count_input = async () => {
if (countFocus.value) {
inputCount.innerText = count_words_and_tokens(countFocus.value, get_selected_model());
} else {
- inputCount.innerHTML = "&nbsp;"
+ inputCount.innerText = "";
}
}, 100);
};
@@ -1060,6 +1069,8 @@ async function on_api() {
messageInput.addEventListener("keydown", async (evt) => {
if (prompt_lock) return;
+ // If not mobile
+ if (!window.matchMedia("(pointer:coarse)").matches)
if (evt.keyCode === 13 && !evt.shiftKey) {
evt.preventDefault();
console.log("pressed enter");
@@ -1262,6 +1273,7 @@ async function load_provider_models(providerIndex=null) {
if (!providerIndex) {
providerIndex = providerSelect.selectedIndex;
}
+ modelProvider.innerHTML = '';
const provider = providerSelect.options[providerIndex].value;
if (!provider) {
modelProvider.classList.add("hidden");
@@ -1269,7 +1281,6 @@ async function load_provider_models(providerIndex=null) {
return;
}
const models = await api('models', provider);
- modelProvider.innerHTML = '';
if (models.length > 0) {
modelSelect.classList.add("hidden");
modelProvider.classList.remove("hidden");
diff --git a/g4f/image.py b/g4f/image.py
index 3d339266..a677e8df 100644
--- a/g4f/image.py
+++ b/g4f/image.py
@@ -210,8 +210,8 @@ def format_images_markdown(images: Union[str, list], alt: str, preview: Union[st
if not isinstance(preview, list):
preview = [preview.replace('{image}', image) if preview else image for image in images]
result = "\n".join(
- #f"[![#{idx+1} {alt}]({preview[idx]})]({image})"
- f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
+ f"[![#{idx+1} {alt}]({preview[idx]})]({image})"
+ #f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
for idx, image in enumerate(images)
)
start_flag = "<!-- generated images start -->\n"