diff --git a/g4f/Provider/Bing.py b/g4f/Provider/Bing.py index ca431355..9a067b0f 100644 --- a/g4f/Provider/Bing.py +++ b/g4f/Provider/Bing.py @@ -10,13 +10,13 @@ from datetime import datetime from aiohttp import ClientSession, ClientTimeout, BaseConnector, WSMsgType from ..typing import AsyncResult, Messages, ImageType, Cookies -from ..image import ImageResponse, ImageRequest +from ..image import ImageRequest from ..errors import ResponseStatusError from .base_provider import AsyncGeneratorProvider from .helper import get_connector, get_random_hex from .bing.upload_image import upload_image -from .bing.create_images import create_images from .bing.conversation import Conversation, create_conversation, delete_conversation +from .BingCreateImages import BingCreateImages from .. import debug class Tones: @@ -71,7 +71,7 @@ class Bing(AsyncGeneratorProvider): gpt4_turbo = True if model.startswith("gpt-4-turbo") else False - return stream_generate(prompt, tone, image, context, cookies, get_connector(connector, proxy, True), web_search, gpt4_turbo, timeout) + return stream_generate(prompt, tone, image, context, cookies, get_connector(connector, proxy, True), proxy, web_search, gpt4_turbo, timeout) def create_context(messages: Messages) -> str: """ @@ -307,6 +307,7 @@ async def stream_generate( context: str = None, cookies: dict = None, connector: BaseConnector = None, + proxy: str = None, web_search: bool = False, gpt4_turbo: bool = False, timeout: int = 900, @@ -387,8 +388,9 @@ async def stream_generate( elif message.get('contentType') == "IMAGE": prompt = message.get('text') try: - image_response = ImageResponse(await create_images(session, prompt), prompt, {"preview": "{image}?w=200&h=200"}) - except: + image_client = BingCreateImages(cookies, proxy) + image_response = await image_client.create_async(prompt) + except Exception as e: response_txt += f"\nhttps://www.bing.com/images/create?q={parse.quote(prompt)}" do_read = False if response_txt.startswith(returned_text): @@ -415,4 +417,4 @@ async def stream_generate( await asyncio.sleep(sleep_retry) break return - await delete_conversation(session, headers, conversation) + await delete_conversation(session, conversation, headers) diff --git a/g4f/Provider/BingCreateImages.py b/g4f/Provider/BingCreateImages.py index c465c1d8..f9c4f3b3 100644 --- a/g4f/Provider/BingCreateImages.py +++ b/g4f/Provider/BingCreateImages.py @@ -7,14 +7,15 @@ from typing import Iterator, Union from ..cookies import get_cookies from ..image import ImageResponse from ..errors import MissingRequirementsError, MissingAuthError +from ..typing import Cookies from .bing.create_images import create_images, create_session, get_cookies_from_browser class BingCreateImages: """A class for creating images using Bing.""" - def __init__(self, cookies: dict[str, str] = {}, proxy: str = None) -> None: - self.cookies = cookies - self.proxy = proxy + def __init__(self, cookies: Cookies = None, proxy: str = None) -> None: + self.cookies: Cookies = cookies + self.proxy: str = proxy def create(self, prompt: str) -> Iterator[Union[ImageResponse, str]]: """ diff --git a/g4f/gui/client/js/chat.v1.js b/g4f/gui/client/js/chat.v1.js index 16b4acb9..d07597ea 100644 --- a/g4f/gui/client/js/chat.v1.js +++ b/g4f/gui/client/js/chat.v1.js @@ -27,6 +27,13 @@ messageInput.addEventListener("focus", () => { document.documentElement.scrollTop = document.documentElement.scrollHeight; }); +appStorage = window.localStorage || { + setItem: (key, value) => self[key] = value, + getItem: (key) => self[key], + removeItem: (key) => delete self[key], + length: 0 +} + const markdown_render = (content) => { return markdown.render(content .replaceAll(//gm, "") @@ -67,10 +74,10 @@ const register_remove_message = async () => { } const delete_conversations = async () => { - for (let i = 0; i < localStorage.length; i++){ - let key = localStorage.key(i); + for (let i = 0; i < appStorage.length; i++){ + let key = appStorage.key(i); if (key.startsWith("conversation:")) { - localStorage.removeItem(key); + appStorage.removeItem(key); } } hide_sidebar(); @@ -261,6 +268,7 @@ const ask_gpt = async () => { body: body }); const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + let buffer = "" while (true) { const { value, done } = await reader.read(); if (done) break; @@ -268,7 +276,14 @@ const ask_gpt = async () => { if (!line) { continue; } - const message = JSON.parse(line); + let message; + try { + message = JSON.parse(buffer + line); + buffer = ""; + } catch { + buffer += line + continue; + } if (message.type == "content") { text += message.content; } else if (message.type == "provider") { @@ -389,7 +404,7 @@ const hide_option = async (conversation_id) => { }; const delete_conversation = async (conversation_id) => { - localStorage.removeItem(`conversation:${conversation_id}`); + appStorage.removeItem(`conversation:${conversation_id}`); const conversation = document.getElementById(`convo-${conversation_id}`); conversation.remove(); @@ -491,13 +506,13 @@ const load_conversation = async (conversation_id, scroll = true) => { async function get_conversation(conversation_id) { let conversation = await JSON.parse( - localStorage.getItem(`conversation:${conversation_id}`) + appStorage.getItem(`conversation:${conversation_id}`) ); return conversation; } async function save_conversation(conversation_id, conversation) { - localStorage.setItem( + appStorage.setItem( `conversation:${conversation_id}`, JSON.stringify(conversation) ); @@ -515,7 +530,7 @@ async function add_conversation(conversation_id, content) { title = content + ' '.repeat(19 - content.length) } - if (localStorage.getItem(`conversation:${conversation_id}`) == null) { + if (appStorage.getItem(`conversation:${conversation_id}`) == null) { await save_conversation(conversation_id, { id: conversation_id, title: title, @@ -577,9 +592,9 @@ const add_message = async (conversation_id, role, content, provider) => { const load_conversations = async () => { let conversations = []; - for (let i = 0; i < localStorage.length; i++) { - if (localStorage.key(i).startsWith("conversation:")) { - let conversation = localStorage.getItem(localStorage.key(i)); + for (let i = 0; i < appStorage.length; i++) { + if (appStorage.key(i).startsWith("conversation:")) { + let conversation = appStorage.getItem(appStorage.key(i)); conversations.push(JSON.parse(conversation)); } } @@ -657,10 +672,10 @@ const register_settings_localstorage = async () => { element.addEventListener('change', async (event) => { switch (event.target.type) { case "checkbox": - localStorage.setItem(id, event.target.checked); + appStorage.setItem(id, event.target.checked); break; case "select-one": - localStorage.setItem(id, event.target.selectedIndex); + appStorage.setItem(id, event.target.selectedIndex); break; default: console.warn("Unresolved element type"); @@ -672,7 +687,7 @@ const register_settings_localstorage = async () => { const load_settings_localstorage = async () => { for (id of ["switch", "model", "jailbreak", "patch", "provider", "history"]) { element = document.getElementById(id); - value = localStorage.getItem(element.id); + value = appStorage.getItem(element.id); if (value) { switch (element.type) { case "checkbox": @@ -712,12 +727,12 @@ const say_hello = async () => { // Theme storage for recurring viewers const storeTheme = function (theme) { - localStorage.setItem("theme", theme); + appStorage.setItem("theme", theme); }; // set theme when visitor returns const setTheme = function () { - const activeTheme = localStorage.getItem("theme"); + const activeTheme = appStorage.getItem("theme"); colorThemes.forEach((themeOption) => { if (themeOption.id === activeTheme) { themeOption.checked = true; diff --git a/g4f/gui/webview.py b/g4f/gui/webview.py new file mode 100644 index 00000000..5a4263dc --- /dev/null +++ b/g4f/gui/webview.py @@ -0,0 +1,24 @@ +import webview +from functools import partial +from platformdirs import user_config_dir + +from g4f.gui import run_gui +from g4f.gui.run import gui_parser +import g4f.version +import g4f.debug + +def run_webview(host: str = "0.0.0.0", port: int = 8080, debug: bool = True): + webview.create_window(f"g4f - {g4f.version.utils.current_version}", f"http://{host}:{port}/") + if debug: + g4f.debug.logging = True + webview.start( + partial(run_gui, host, port), + private_mode=False, + storage_path=user_config_dir("g4f-webview"), + debug=debug + ) + +if __name__ == "__main__": + parser = gui_parser() + args = parser.parse_args() + run_webview(args.host, args.port, args.debug) \ No newline at end of file diff --git a/g4f/providers/create_images.py b/g4f/providers/create_images.py index 29a2a041..29db9435 100644 --- a/g4f/providers/create_images.py +++ b/g4f/providers/create_images.py @@ -6,6 +6,7 @@ import asyncio from .. import debug from ..typing import CreateResult, Messages from .types import BaseProvider, ProviderType +from ..image import ImageResponse system_message = """ You can generate images, pictures, photos or img with the DALL-E 3 image generator. @@ -92,7 +93,9 @@ class CreateImagesProvider(BaseProvider): messages.insert(0, {"role": "system", "content": self.system_message}) buffer = "" for chunk in self.provider.create_completion(model, messages, stream, **kwargs): - if isinstance(chunk, str) and buffer or "<" in chunk: + if isinstance(chunk, ImageResponse): + yield chunk + elif isinstance(chunk, str) and buffer or "<" in chunk: buffer += chunk if ">" in buffer: match = re.search(r'', buffer)