diff options
Diffstat (limited to 'g4f/gui/server')
-rw-r--r-- | g4f/gui/server/api.py | 182 | ||||
-rw-r--r-- | g4f/gui/server/internet.py | 2 | ||||
-rw-r--r-- | g4f/gui/server/website.py | 6 |
3 files changed, 75 insertions, 115 deletions
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py index 3da0fe17..7aac650a 100644 --- a/g4f/gui/server/api.py +++ b/g4f/gui/server/api.py @@ -2,12 +2,11 @@ from __future__ import annotations import logging import os -import os.path import uuid import asyncio import time from aiohttp import ClientSession -from typing import Iterator, Optional +from typing import Iterator, Optional, AsyncIterator, Union from flask import send_from_directory from g4f import version, models @@ -20,21 +19,20 @@ from g4f.Provider import ProviderType, __providers__, __map__ from g4f.providers.base_provider import ProviderModelMixin, FinishReason from g4f.providers.conversation import BaseConversation -conversations: dict[dict[str, BaseConversation]] = {} +# Define the directory for generated images images_dir = "./generated_images" -class Api(): +# Function to ensure the images directory exists +def ensure_images_dir(): + if not os.path.exists(images_dir): + os.makedirs(images_dir) - @staticmethod - def get_models() -> list[str]: - """ - Return a list of all models. +conversations: dict[dict[str, BaseConversation]] = {} - Fetches and returns a list of all available models in the system. - Returns: - List[str]: A list of model names. - """ +class Api: + @staticmethod + def get_models() -> list[str]: return models._all_models @staticmethod @@ -42,14 +40,11 @@ class Api(): if provider in __map__: provider: ProviderType = __map__[provider] if issubclass(provider, ProviderModelMixin): - return [{"model": model, "default": model == provider.default_model} for model in provider.get_models()] - elif provider.supports_gpt_35_turbo or provider.supports_gpt_4: return [ - *([{"model": "gpt-4", "default": not provider.supports_gpt_4}] if provider.supports_gpt_4 else []), - *([{"model": "gpt-3.5-turbo", "default": not provider.supports_gpt_4}] if provider.supports_gpt_35_turbo else []) + {"model": model, "default": model == provider.default_model} + for model in provider.get_models() ] - else: - return []; + return [] @staticmethod def get_image_models() -> list[dict]: @@ -71,7 +66,7 @@ class Api(): "image_model": model, "vision_model": parent.default_vision_model if hasattr(parent, "default_vision_model") else None }) - index.append(parent.__name__) + index.append(parent.__name__) elif hasattr(provider, "default_vision_model") and provider.__name__ not in index: image_models.append({ "provider": provider.__name__, @@ -85,31 +80,20 @@ class Api(): @staticmethod def get_providers() -> list[str]: - """ - Return a list of all working providers. - """ return { - provider.__name__: (provider.label - if hasattr(provider, "label") - else provider.__name__) + - (" (WebDriver)" - if "webdriver" in provider.get_parameters() - else "") + - (" (Auth)" - if provider.needs_auth - else "") + provider.__name__: ( + provider.label if hasattr(provider, "label") else provider.__name__ + ) + ( + " (WebDriver)" if "webdriver" in provider.get_parameters() else "" + ) + ( + " (Auth)" if provider.needs_auth else "" + ) for provider in __providers__ if provider.working } @staticmethod def get_version(): - """ - Returns the current and latest version of the application. - - Returns: - dict: A dictionary containing the current and latest version. - """ try: current_version = version.utils.current_version except VersionNotFoundError: @@ -120,18 +104,10 @@ class Api(): } def serve_images(self, name): + ensure_images_dir() return send_from_directory(os.path.abspath(images_dir), name) def _prepare_conversation_kwargs(self, json_data: dict, kwargs: dict): - """ - Prepares arguments for chat completion based on the request data. - - Reads the request and prepares the necessary arguments for handling - a chat completion request. - - Returns: - dict: Arguments prepared for chat completion. - """ model = json_data.get('model') or models.default provider = json_data.get('provider') messages = json_data['messages'] @@ -139,7 +115,7 @@ class Api(): if api_key is not None: kwargs["api_key"] = api_key if json_data.get('web_search'): - if provider in ("Bing", "HuggingChat"): + if provider: kwargs['web_search'] = True else: from .internet import get_search_message @@ -160,89 +136,69 @@ class Api(): } def _create_response_stream(self, kwargs: dict, conversation_id: str, provider: str) -> Iterator: - """ - Creates and returns a streaming response for the conversation. - - Args: - kwargs (dict): Arguments for creating the chat completion. - - Yields: - str: JSON formatted response chunks for the stream. - - Raises: - Exception: If an error occurs during the streaming process. - """ try: + result = ChatCompletion.create(**kwargs) first = True - for chunk in ChatCompletion.create(**kwargs): + if isinstance(result, ImageResponse): if first: first = False yield self._format_json("provider", get_last_provider(True)) - if isinstance(chunk, BaseConversation): - if provider not in conversations: - conversations[provider] = {} - conversations[provider][conversation_id] = chunk - yield self._format_json("conversation", conversation_id) - elif isinstance(chunk, Exception): - logging.exception(chunk) - yield self._format_json("message", get_error_message(chunk)) - elif isinstance(chunk, ImagePreview): - yield self._format_json("preview", chunk.to_string()) - elif isinstance(chunk, ImageResponse): - async def copy_images(images: list[str], cookies: Optional[Cookies] = None): - async with ClientSession( - 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())}") - 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]) - images = asyncio.run(copy_images(chunk.get_list(), chunk.options.get("cookies"))) - yield self._format_json("content", str(ImageResponse(images, chunk.alt))) - elif not isinstance(chunk, FinishReason): - yield self._format_json("content", str(chunk)) + yield self._format_json("content", str(result)) + else: + for chunk in result: + if first: + first = False + yield self._format_json("provider", get_last_provider(True)) + if isinstance(chunk, BaseConversation): + if provider not in conversations: + conversations[provider] = {} + conversations[provider][conversation_id] = chunk + yield self._format_json("conversation", conversation_id) + elif isinstance(chunk, Exception): + logging.exception(chunk) + yield self._format_json("message", get_error_message(chunk)) + elif isinstance(chunk, ImagePreview): + yield self._format_json("preview", chunk.to_string()) + elif isinstance(chunk, ImageResponse): + images = asyncio.run(self._copy_images(chunk.get_list(), chunk.options.get("cookies"))) + yield self._format_json("content", str(ImageResponse(images, chunk.alt))) + elif not isinstance(chunk, FinishReason): + yield self._format_json("content", str(chunk)) except Exception as e: logging.exception(e) yield self._format_json('error', get_error_message(e)) - def _format_json(self, response_type: str, content): - """ - Formats and returns a JSON response. - - Args: - response_type (str): The type of the response. - content: The content to be included in the response. + async def _copy_images(self, images: list[str], cookies: Optional[Cookies] = None): + ensure_images_dir() + async with ClientSession( + 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())}") + 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]) - Returns: - str: A JSON formatted string. - """ + def _format_json(self, response_type: str, content): return { 'type': response_type, response_type: content } -def get_error_message(exception: Exception) -> str: - """ - Generates a formatted error message from an exception. - Args: - exception (Exception): The exception to format. - - Returns: - str: A formatted error message string. - """ +def get_error_message(exception: Exception) -> str: message = f"{type(exception).__name__}: {exception}" provider = get_last_provider() if provider is None: return message - return f"{provider.__name__}: {message}"
\ No newline at end of file + return f"{provider.__name__}: {message}" diff --git a/g4f/gui/server/internet.py b/g4f/gui/server/internet.py index a1fafa7d..78bea0ca 100644 --- a/g4f/gui/server/internet.py +++ b/g4f/gui/server/internet.py @@ -101,7 +101,7 @@ async def search(query: str, n_results: int = 5, max_words: int = 2500, add_text raise MissingRequirementsError('Install "duckduckgo-search" and "beautifulsoup4" package') async with AsyncDDGS() as ddgs: results = [] - for result in await ddgs.text( + for result in await ddgs.atext( query, region="wt-wt", safesearch="moderate", diff --git a/g4f/gui/server/website.py b/g4f/gui/server/website.py index 5e633674..3cabcdf3 100644 --- a/g4f/gui/server/website.py +++ b/g4f/gui/server/website.py @@ -27,6 +27,10 @@ class Website: 'function': redirect_home, 'methods': ['GET', 'POST'] }, + '/images/': { + 'function': redirect_home, + 'methods': ['GET', 'POST'] + }, } def _chat(self, conversation_id): @@ -35,4 +39,4 @@ class Website: return render_template('index.html', chat_id=conversation_id) def _index(self): - return render_template('index.html', chat_id=str(uuid.uuid4()))
\ No newline at end of file + return render_template('index.html', chat_id=str(uuid.uuid4())) |