Add OpenRouter and DeepInfraImage Provider (#1814)

This commit is contained in:
H Lohaus 2024-04-10 08:14:50 +02:00 committed by GitHub
parent 84475b4159
commit 00951eb791
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 164 additions and 38 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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
)

View File

@ -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
}
}

View File

@ -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:

View File

@ -5,4 +5,5 @@ from .ThebApi import ThebApi
from .OpenaiChat import OpenaiChat
from .Poe import Poe
from .Openai import Openai
from .Groq import Groq
from .Groq import Groq
from .OpenRouter import OpenRouter

View File

@ -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 = [{

View File

@ -132,10 +132,18 @@
<label for="GeminiPro-api_key" class="label" title="">GeminiPro: api_key</label>
<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()">

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -49,4 +49,11 @@ def get_random_hex(length: int = 32) -> str:
return ''.join(
random.choice("abcdef" + string.digits)
for _ in range(length)
)
)
def filter_none(**kwargs) -> dict:
return {
key: value
for key, value in kwargs.items()
if value is not None
}