summaryrefslogtreecommitdiffstats
path: root/g4f
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--g4f/Provider/Bing.py2
-rw-r--r--g4f/Provider/DeepInfraImage.py74
-rw-r--r--g4f/Provider/You.py11
-rw-r--r--g4f/Provider/__init__.py1
-rw-r--r--g4f/Provider/needs_auth/OpenRouter.py31
-rw-r--r--g4f/Provider/needs_auth/Openai.py13
-rw-r--r--g4f/Provider/needs_auth/OpenaiChat.py2
-rw-r--r--g4f/Provider/needs_auth/__init__.py3
-rw-r--r--g4f/api/__init__.py2
-rw-r--r--g4f/gui/client/index.html8
-rw-r--r--g4f/gui/client/static/css/style.css10
-rw-r--r--g4f/gui/client/static/js/chat.v1.js32
-rw-r--r--g4f/gui/server/api.py4
-rw-r--r--g4f/providers/helper.py9
14 files changed, 164 insertions, 38 deletions
diff --git a/g4f/Provider/Bing.py b/g4f/Provider/Bing.py
index 1e462084..955717a2 100644
--- a/g4f/Provider/Bing.py
+++ b/g4f/Provider/Bing.py
@@ -47,7 +47,7 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
proxy: str = None,
timeout: int = 900,
api_key: str = None,
- cookies: Cookies = None,
+ cookies: Cookies = {},
connector: BaseConnector = None,
tone: str = None,
image: ImageType = None,
diff --git a/g4f/Provider/DeepInfraImage.py b/g4f/Provider/DeepInfraImage.py
new file mode 100644
index 00000000..6099b793
--- /dev/null
+++ b/g4f/Provider/DeepInfraImage.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+import requests
+
+from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..typing import AsyncResult, Messages
+from ..requests import StreamSession, raise_for_status
+from ..image import ImageResponse
+
+class DeepInfraImage(AsyncGeneratorProvider, ProviderModelMixin):
+ url = "https://deepinfra.com"
+ working = True
+ default_model = 'stability-ai/sdxl'
+
+ @classmethod
+ def get_models(cls):
+ if not cls.models:
+ 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"]
+ return cls.models
+
+ @classmethod
+ async def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ **kwargs
+ ) -> AsyncResult:
+ yield await cls.create_async(messages[-1]["content"], model, **kwargs)
+
+ @classmethod
+ async def create_async(
+ cls,
+ prompt: str,
+ model: str,
+ api_key: str = None,
+ api_base: str = "https://api.deepinfra.com/v1/inference",
+ proxy: str = None,
+ timeout: int = 180,
+ extra_data: dict = {},
+ **kwargs
+ ) -> ImageResponse:
+ headers = {
+ 'Accept-Encoding': 'gzip, deflate, br',
+ 'Accept-Language': 'en-US',
+ 'Connection': 'keep-alive',
+ 'Origin': 'https://deepinfra.com',
+ 'Referer': 'https://deepinfra.com/',
+ 'Sec-Fetch-Dest': 'empty',
+ 'Sec-Fetch-Mode': 'cors',
+ 'Sec-Fetch-Site': 'same-site',
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
+ 'X-Deepinfra-Source': 'web-embed',
+ 'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
+ 'sec-ch-ua-mobile': '?0',
+ 'sec-ch-ua-platform': '"macOS"',
+ }
+ if api_key is not None:
+ headers["Authorization"] = f"Bearer {api_key}"
+ async with StreamSession(
+ proxies={"all": proxy},
+ headers=headers,
+ timeout=timeout
+ ) as session:
+ model = cls.get_model(model)
+ data = {"prompt": prompt, **extra_data}
+ data = {"input": data} if model == cls.default_model else data
+ async with session.post(f"{api_base.rstrip('/')}/{model}", json=data) as response:
+ await raise_for_status(response)
+ data = await response.json()
+ images = data["output"] if "output" in data else data["images"]
+ images = images[0] if len(images) == 1 else images
+ return ImageResponse(images, prompt) \ No newline at end of file
diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py
index 6256cda9..cfa2c7bf 100644
--- a/g4f/Provider/You.py
+++ b/g4f/Provider/You.py
@@ -8,8 +8,9 @@ import uuid
from ..typing import AsyncResult, Messages, ImageType, Cookies
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import format_prompt
-from ..image import to_bytes, ImageResponse
+from ..image import ImageResponse, to_bytes, is_accepted_format
from ..requests import StreamSession, FormData, raise_for_status
+from ..errors import MissingRequirementsError
from .you.har_file import get_dfp_telemetry_id
@@ -46,6 +47,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
image: ImageType = None,
image_name: str = None,
proxy: str = None,
+ timeout: int = 240,
chat_mode: str = "default",
**kwargs,
) -> AsyncResult:
@@ -55,12 +57,14 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
...
elif model.startswith("dall-e"):
chat_mode = "create"
+ messages = [messages[-1]]
else:
chat_mode = "custom"
model = cls.get_model(model)
async with StreamSession(
proxies={"all": proxy},
- impersonate="chrome"
+ 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 ""
@@ -73,7 +77,6 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
"q": format_prompt(messages),
"domain": "youchat",
"selectedChatMode": chat_mode,
- #"chat": json.dumps(chat),
}
params = {
"userFiles": upload,
@@ -113,7 +116,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
await raise_for_status(response)
upload_nonce = await response.text()
data = FormData()
- data.add_field('file', file, filename=filename)
+ data.add_field('file', file, content_type=is_accepted_format(file), filename=filename)
async with client.post(
f"{cls.url}/api/upload",
data=data,
diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py
index b567305c..7a39d023 100644
--- a/g4f/Provider/__init__.py
+++ b/g4f/Provider/__init__.py
@@ -21,6 +21,7 @@ from .ChatgptFree import ChatgptFree
from .ChatgptNext import ChatgptNext
from .ChatgptX import ChatgptX
from .DeepInfra import DeepInfra
+from .DeepInfraImage import DeepInfraImage
from .DuckDuckGo import DuckDuckGo
from .FlowGpt import FlowGpt
from .FreeChatgpt import FreeChatgpt
diff --git a/g4f/Provider/needs_auth/OpenRouter.py b/g4f/Provider/needs_auth/OpenRouter.py
new file mode 100644
index 00000000..e5f87076
--- /dev/null
+++ b/g4f/Provider/needs_auth/OpenRouter.py
@@ -0,0 +1,31 @@
+from __future__ import annotations
+
+import requests
+
+from .Openai import Openai
+from ...typing import AsyncResult, Messages
+
+class OpenRouter(Openai):
+ url = "https://openrouter.ai"
+ working = True
+ default_model = "openrouter/auto"
+
+ @classmethod
+ def get_models(cls):
+ if not cls.models:
+ url = 'https://openrouter.ai/api/v1/models'
+ models = requests.get(url).json()["data"]
+ cls.models = [model['id'] for model in models]
+ return cls.models
+
+ @classmethod
+ def create_async_generator(
+ cls,
+ model: str,
+ messages: Messages,
+ api_base: str = "https://openrouter.ai/api/v1",
+ **kwargs
+ ) -> AsyncResult:
+ return super().create_async_generator(
+ model, messages, api_base=api_base, **kwargs
+ ) \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/Openai.py b/g4f/Provider/needs_auth/Openai.py
index 6cd2cf86..ea09e950 100644
--- a/g4f/Provider/needs_auth/Openai.py
+++ b/g4f/Provider/needs_auth/Openai.py
@@ -2,10 +2,10 @@ from __future__ import annotations
import json
+from ..helper import filter_none
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, FinishReason
from ...typing import Union, Optional, AsyncResult, Messages
-from ...requests.raise_for_status import raise_for_status
-from ...requests import StreamSession
+from ...requests import StreamSession, raise_for_status
from ...errors import MissingAuthError, ResponseError
class Openai(AsyncGeneratorProvider, ProviderModelMixin):
@@ -98,11 +98,4 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
else {}
),
**({} if headers is None else headers)
- }
-
-def filter_none(**kwargs) -> dict:
- return {
- key: value
- for key, value in kwargs.items()
- if value is not None
- } \ No newline at end of file
+ } \ No newline at end of file
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index 64e3aeac..7491725f 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -334,7 +334,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
RuntimeError: If an error occurs during processing.
"""
async with StreamSession(
- proxies={"https": proxy},
+ proxies={"all": proxy},
impersonate="chrome",
timeout=timeout
) as session:
diff --git a/g4f/Provider/needs_auth/__init__.py b/g4f/Provider/needs_auth/__init__.py
index 581335e1..7b793223 100644
--- a/g4f/Provider/needs_auth/__init__.py
+++ b/g4f/Provider/needs_auth/__init__.py
@@ -5,4 +5,5 @@ from .ThebApi import ThebApi
from .OpenaiChat import OpenaiChat
from .Poe import Poe
from .Openai import Openai
-from .Groq import Groq \ No newline at end of file
+from .Groq import Groq
+from .OpenRouter import OpenRouter \ No newline at end of file
diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py
index 579090fe..383e22be 100644
--- a/g4f/api/__init__.py
+++ b/g4f/api/__init__.py
@@ -76,7 +76,7 @@ class Api:
@self.app.get("/v1/models")
async def models():
model_list = dict(
- (model, g4f.ModelUtils.convert[model])
+ (model, g4f.models.ModelUtils.convert[model])
for model in g4f.Model.__all__()
)
model_list = [{
diff --git a/g4f/gui/client/index.html b/g4f/gui/client/index.html
index d6ad5241..463eb650 100644
--- a/g4f/gui/client/index.html
+++ b/g4f/gui/client/index.html
@@ -133,9 +133,17 @@
<textarea id="GeminiPro-api_key" name="GeminiPro[api_key]" placeholder="..."></textarea>
</div>
<div class="field box">
+ <label for="OpenRouter-api_key" class="label" title="">OpenRouter: api_key</label>
+ <textarea id="OpenRouter-api_key" name="OpenRouter[api_key]" placeholder="..."></textarea>
+ </div>
+ <div class="field box">
<label for="HuggingFace-api_key" class="label" title="">HuggingFace: api_key</label>
<textarea id="HuggingFace-api_key" name="HuggingFace[api_key]" placeholder="..."></textarea>
</div>
+ <div class="field box">
+ <label for="DeepInfra-api_key" class="label" title="">DeepInfra: api_key</label>
+ <textarea id="DeepInfra-api_key" name="DeepInfra[api_key]" placeholder="..."></textarea>
+ </div>
</div>
<div class="bottom_buttons">
<button onclick="delete_conversations()">
diff --git a/g4f/gui/client/static/css/style.css b/g4f/gui/client/static/css/style.css
index 32fff3db..8e967806 100644
--- a/g4f/gui/client/static/css/style.css
+++ b/g4f/gui/client/static/css/style.css
@@ -109,7 +109,7 @@ body {
}
.conversations {
- max-width: 280px;
+ max-width: 300px;
padding: var(--section-gap);
overflow: auto;
flex-shrink: 0;
@@ -207,9 +207,9 @@ body {
gap: 4px;
}
-.conversations .convo .fa-trash {
+.conversations .convo .fa-ellipsis-vertical {
position: absolute;
- right: 8px;
+ right: 14px;
}
.conversations .convo .choise {
@@ -1075,6 +1075,10 @@ a:-webkit-any-link {
resize: vertical;
}
+.settings textarea {
+ height: 51px;
+}
+
.settings {
width: 100%;
display: flex;
diff --git a/g4f/gui/client/static/js/chat.v1.js b/g4f/gui/client/static/js/chat.v1.js
index 5036a93b..628d0682 100644
--- a/g4f/gui/client/static/js/chat.v1.js
+++ b/g4f/gui/client/static/js/chat.v1.js
@@ -42,7 +42,7 @@ appStorage = window.localStorage || {
const markdown = window.markdownit();
const markdown_render = (content) => {
return markdown.render(content
- .replaceAll(/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, "")
+ .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
.replaceAll(/<img data-prompt="[^>]+">/gm, "")
)
.replaceAll("<a href=", '<a target="_blank" href=')
@@ -127,9 +127,6 @@ const register_message_buttons = async () => {
sound.controls = 'controls';
sound.src = url;
sound.type = 'audio/wav';
- if (ended && !stopped) {
- sound.autoplay = true;
- }
sound.onended = function() {
ended = true;
};
@@ -140,6 +137,9 @@ const register_message_buttons = async () => {
container.classList.add("audio");
container.appendChild(sound);
content_el.appendChild(container);
+ if (ended && !stopped) {
+ sound.play();
+ }
}
if (lines.length < 1 || stopped) {
el.classList.remove("active");
@@ -608,12 +608,11 @@ async function get_messages(conversation_id) {
}
async function add_conversation(conversation_id, content) {
- if (content.length > 17) {
- title = content.substring(0, 17) + '...'
+ if (content.length > 18) {
+ title = content.substring(0, 18) + '...'
} else {
- title = content + '&nbsp;'.repeat(19 - content.length)
+ title = content + '&nbsp;'.repeat(20 - content.length)
}
-
if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
await save_conversation(conversation_id, {
id: conversation_id,
@@ -623,7 +622,6 @@ async function add_conversation(conversation_id, content) {
items: [],
});
}
-
history.pushState({}, null, `/chat/${conversation_id}`);
}
@@ -695,27 +693,31 @@ const load_conversations = async () => {
await clear_conversations();
- for (conversation of conversations) {
+ conversations.sort((a, b) => (b.updated||0)-(a.updated||0));
+
+ let html = "";
+ conversations.forEach((conversation) => {
let updated = "";
if (conversation.updated) {
const date = new Date(conversation.updated);
updated = date.toLocaleString('en-GB', {dateStyle: 'short', timeStyle: 'short', monthStyle: 'short'});
updated = updated.replace("/" + date.getFullYear(), "")
}
- box_conversations.innerHTML += `
+ html += `
<div class="convo" id="convo-${conversation.id}">
<div class="left" onclick="set_conversation('${conversation.id}')">
<i class="fa-regular fa-comments"></i>
<span class="convo-title"><span class="datetime">${updated}</span> ${conversation.title}</span>
</div>
- <i onclick="show_option('${conversation.id}')" class="fa-regular fa-trash" id="conv-${conversation.id}"></i>
+ <i onclick="show_option('${conversation.id}')" class="fa-solid fa-ellipsis-vertical" id="conv-${conversation.id}"></i>
<div id="cho-${conversation.id}" class="choise" style="display:none;">
- <i onclick="delete_conversation('${conversation.id}')" class="fa-regular fa-check"></i>
+ <i onclick="delete_conversation('${conversation.id}')" class="fa-regular fa-trash"></i>
<i onclick="hide_option('${conversation.id}')" class="fa-regular fa-x"></i>
</div>
</div>
`;
- }
+ });
+ box_conversations.innerHTML = html;
};
document.getElementById("cancelButton").addEventListener("click", async () => {
@@ -804,6 +806,7 @@ const register_settings_storage = async () => {
appStorage.setItem(element.id, element.selectedIndex);
break;
case "text":
+ case "number":
appStorage.setItem(element.id, element.value);
break;
default:
@@ -828,6 +831,7 @@ const load_settings_storage = async () => {
element.selectedIndex = parseInt(value);
break;
case "text":
+ case "number":
case "textarea":
element.value = value;
break;
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index 2b3f2fb6..bbae6066 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -8,7 +8,7 @@ from g4f import version, models
from g4f import get_last_provider, ChatCompletion
from g4f.errors import VersionNotFoundError
from g4f.Provider import ProviderType, __providers__, __map__
-from g4f.providers.base_provider import ProviderModelMixin
+from g4f.providers.base_provider import ProviderModelMixin, FinishReason
from g4f.providers.conversation import BaseConversation
conversations: dict[dict[str, BaseConversation]] = {}
@@ -134,7 +134,7 @@ class Api():
elif isinstance(chunk, Exception):
logging.exception(chunk)
yield self._format_json("message", get_error_message(chunk))
- else:
+ elif not isinstance(chunk, FinishReason):
yield self._format_json("content", str(chunk))
except Exception as e:
logging.exception(e)
diff --git a/g4f/providers/helper.py b/g4f/providers/helper.py
index 5f3b4fb6..0ec9aac2 100644
--- a/g4f/providers/helper.py
+++ b/g4f/providers/helper.py
@@ -49,4 +49,11 @@ def get_random_hex(length: int = 32) -> str:
return ''.join(
random.choice("abcdef" + string.digits)
for _ in range(length)
- ) \ No newline at end of file
+ )
+
+def filter_none(**kwargs) -> dict:
+ return {
+ key: value
+ for key, value in kwargs.items()
+ if value is not None
+ } \ No newline at end of file