XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

Add OpenRouter and DeepInfraImage Provider (#1814)

00951eb7
H Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

14 个文件 +164 -38
Modified g4f/Provider/Bing.py +1 -1
@@ -47,7 +47,7 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
47 47 proxy: str = None,
48 48 timeout: int = 900,
49 49 api_key: str = None,
50 cookies: Cookies = None,
50 cookies: Cookies = {},
51 51 connector: BaseConnector = None,
52 52 tone: str = None,
53 53 image: ImageType = None,
Added g4f/Provider/DeepInfraImage.py +74 -0
@@ -0,0 +1,74 @@
1 from __future__ import annotations
2
3 import requests
4
5 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
6 from ..typing import AsyncResult, Messages
7 from ..requests import StreamSession, raise_for_status
8 from ..image import ImageResponse
9
10 class DeepInfraImage(AsyncGeneratorProvider, ProviderModelMixin):
11 url = "https://deepinfra.com"
12 working = True
13 default_model = 'stability-ai/sdxl'
14
15 @classmethod
16 def get_models(cls):
17 if not cls.models:
18 url = 'https://api.deepinfra.com/models/featured'
19 models = requests.get(url).json()
20 cls.models = [model['model_name'] for model in models if model["reported_type"] == "text-to-image"]
21 return cls.models
22
23 @classmethod
24 async def create_async_generator(
25 cls,
26 model: str,
27 messages: Messages,
28 **kwargs
29 ) -> AsyncResult:
30 yield await cls.create_async(messages[-1]["content"], model, **kwargs)
31
32 @classmethod
33 async def create_async(
34 cls,
35 prompt: str,
36 model: str,
37 api_key: str = None,
38 api_base: str = "https://api.deepinfra.com/v1/inference",
39 proxy: str = None,
40 timeout: int = 180,
41 extra_data: dict = {},
42 **kwargs
43 ) -> ImageResponse:
44 headers = {
45 'Accept-Encoding': 'gzip, deflate, br',
46 'Accept-Language': 'en-US',
47 'Connection': 'keep-alive',
48 'Origin': 'https://deepinfra.com',
49 'Referer': 'https://deepinfra.com/',
50 'Sec-Fetch-Dest': 'empty',
51 'Sec-Fetch-Mode': 'cors',
52 'Sec-Fetch-Site': 'same-site',
53 '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',
54 'X-Deepinfra-Source': 'web-embed',
55 'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
56 'sec-ch-ua-mobile': '?0',
57 'sec-ch-ua-platform': '"macOS"',
58 }
59 if api_key is not None:
60 headers["Authorization"] = f"Bearer {api_key}"
61 async with StreamSession(
62 proxies={"all": proxy},
63 headers=headers,
64 timeout=timeout
65 ) as session:
66 model = cls.get_model(model)
67 data = {"prompt": prompt, **extra_data}
68 data = {"input": data} if model == cls.default_model else data
69 async with session.post(f"{api_base.rstrip('/')}/{model}", json=data) as response:
70 await raise_for_status(response)
71 data = await response.json()
72 images = data["output"] if "output" in data else data["images"]
73 images = images[0] if len(images) == 1 else images
74 return ImageResponse(images, prompt)
Modified g4f/Provider/You.py +7 -4
@@ -8,8 +8,9 @@ import uuid
8 8 from ..typing import AsyncResult, Messages, ImageType, Cookies
9 9 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 10 from .helper import format_prompt
11 from ..image import to_bytes, ImageResponse
11 from ..image import ImageResponse, to_bytes, is_accepted_format
12 12 from ..requests import StreamSession, FormData, raise_for_status
13 from ..errors import MissingRequirementsError
13 14
14 15 from .you.har_file import get_dfp_telemetry_id
15 16
@@ -46,6 +47,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
46 47 image: ImageType = None,
47 48 image_name: str = None,
48 49 proxy: str = None,
50 timeout: int = 240,
49 51 chat_mode: str = "default",
50 52 **kwargs,
51 53 ) -> AsyncResult:
@@ -55,12 +57,14 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
55 57 ...
56 58 elif model.startswith("dall-e"):
57 59 chat_mode = "create"
60 messages = [messages[-1]]
58 61 else:
59 62 chat_mode = "custom"
60 63 model = cls.get_model(model)
61 64 async with StreamSession(
62 65 proxies={"all": proxy},
63 impersonate="chrome"
66 impersonate="chrome",
67 timeout=(30, timeout)
64 68 ) as session:
65 69 cookies = await cls.get_cookies(session) if chat_mode != "default" else None
66 70 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):
73 77 "q": format_prompt(messages),
74 78 "domain": "youchat",
75 79 "selectedChatMode": chat_mode,
76 #"chat": json.dumps(chat),
77 80 }
78 81 params = {
79 82 "userFiles": upload,
@@ -113,7 +116,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
113 116 await raise_for_status(response)
114 117 upload_nonce = await response.text()
115 118 data = FormData()
116 data.add_field('file', file, filename=filename)
119 data.add_field('file', file, content_type=is_accepted_format(file), filename=filename)
117 120 async with client.post(
118 121 f"{cls.url}/api/upload",
119 122 data=data,
Modified g4f/Provider/__init__.py +1 -0
@@ -21,6 +21,7 @@ from .ChatgptFree import ChatgptFree
21 21 from .ChatgptNext import ChatgptNext
22 22 from .ChatgptX import ChatgptX
23 23 from .DeepInfra import DeepInfra
24 from .DeepInfraImage import DeepInfraImage
24 25 from .DuckDuckGo import DuckDuckGo
25 26 from .FlowGpt import FlowGpt
26 27 from .FreeChatgpt import FreeChatgpt
Added g4f/Provider/needs_auth/OpenRouter.py +31 -0
@@ -0,0 +1,31 @@
1 from __future__ import annotations
2
3 import requests
4
5 from .Openai import Openai
6 from ...typing import AsyncResult, Messages
7
8 class OpenRouter(Openai):
9 url = "https://openrouter.ai"
10 working = True
11 default_model = "openrouter/auto"
12
13 @classmethod
14 def get_models(cls):
15 if not cls.models:
16 url = 'https://openrouter.ai/api/v1/models'
17 models = requests.get(url).json()["data"]
18 cls.models = [model['id'] for model in models]
19 return cls.models
20
21 @classmethod
22 def create_async_generator(
23 cls,
24 model: str,
25 messages: Messages,
26 api_base: str = "https://openrouter.ai/api/v1",
27 **kwargs
28 ) -> AsyncResult:
29 return super().create_async_generator(
30 model, messages, api_base=api_base, **kwargs
31 )
Modified g4f/Provider/needs_auth/Openai.py +3 -10
@@ -2,10 +2,10 @@ from __future__ import annotations
2 2
3 3 import json
4 4
5 from ..helper import filter_none
5 6 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, FinishReason
6 7 from ...typing import Union, Optional, AsyncResult, Messages
7 from ...requests.raise_for_status import raise_for_status
8 from ...requests import StreamSession
8 from ...requests import StreamSession, raise_for_status
9 9 from ...errors import MissingAuthError, ResponseError
10 10
11 11 class Openai(AsyncGeneratorProvider, ProviderModelMixin):
@@ -98,11 +98,4 @@ class Openai(AsyncGeneratorProvider, ProviderModelMixin):
98 98 else {}
99 99 ),
100 100 **({} if headers is None else headers)
101 }
102
103 def filter_none(**kwargs) -> dict:
104 return {
105 key: value
106 for key, value in kwargs.items()
107 if value is not None
108 }
101 }
Modified g4f/Provider/needs_auth/OpenaiChat.py +1 -1
@@ -334,7 +334,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
334 334 RuntimeError: If an error occurs during processing.
335 335 """
336 336 async with StreamSession(
337 proxies={"https": proxy},
337 proxies={"all": proxy},
338 338 impersonate="chrome",
339 339 timeout=timeout
340 340 ) as session:
Modified g4f/Provider/needs_auth/__init__.py +2 -1
@@ -5,4 +5,5 @@ from .ThebApi import ThebApi
5 5 from .OpenaiChat import OpenaiChat
6 6 from .Poe import Poe
7 7 from .Openai import Openai
8 from .Groq import Groq
8 from .Groq import Groq
9 from .OpenRouter import OpenRouter
Modified g4f/api/__init__.py +1 -1
@@ -76,7 +76,7 @@ class Api:
76 76 @self.app.get("/v1/models")
77 77 async def models():
78 78 model_list = dict(
79 (model, g4f.ModelUtils.convert[model])
79 (model, g4f.models.ModelUtils.convert[model])
80 80 for model in g4f.Model.__all__()
81 81 )
82 82 model_list = [{
Modified g4f/gui/client/index.html +8 -0
@@ -132,10 +132,18 @@
132 132 <label for="GeminiPro-api_key" class="label" title="">GeminiPro: api_key</label>
133 133 <textarea id="GeminiPro-api_key" name="GeminiPro[api_key]" placeholder="..."></textarea>
134 134 </div>
135 <div class="field box">
136 <label for="OpenRouter-api_key" class="label" title="">OpenRouter: api_key</label>
137 <textarea id="OpenRouter-api_key" name="OpenRouter[api_key]" placeholder="..."></textarea>
138 </div>
135 139 <div class="field box">
136 140 <label for="HuggingFace-api_key" class="label" title="">HuggingFace: api_key</label>
137 141 <textarea id="HuggingFace-api_key" name="HuggingFace[api_key]" placeholder="..."></textarea>
138 142 </div>
143 <div class="field box">
144 <label for="DeepInfra-api_key" class="label" title="">DeepInfra: api_key</label>
145 <textarea id="DeepInfra-api_key" name="DeepInfra[api_key]" placeholder="..."></textarea>
146 </div>
139 147 </div>
140 148 <div class="bottom_buttons">
141 149 <button onclick="delete_conversations()">
Modified g4f/gui/client/static/css/style.css +7 -3
@@ -109,7 +109,7 @@ body {
109 109 }
110 110
111 111 .conversations {
112 max-width: 280px;
112 max-width: 300px;
113 113 padding: var(--section-gap);
114 114 overflow: auto;
115 115 flex-shrink: 0;
@@ -207,9 +207,9 @@ body {
207 207 gap: 4px;
208 208 }
209 209
210 .conversations .convo .fa-trash {
210 .conversations .convo .fa-ellipsis-vertical {
211 211 position: absolute;
212 right: 8px;
212 right: 14px;
213 213 }
214 214
215 215 .conversations .convo .choise {
@@ -1075,6 +1075,10 @@ a:-webkit-any-link {
1075 1075 resize: vertical;
1076 1076 }
1077 1077
1078 .settings textarea {
1079 height: 51px;
1080 }
1081
1078 1082 .settings {
1079 1083 width: 100%;
1080 1084 display: flex;
Modified g4f/gui/client/static/js/chat.v1.js +18 -14
@@ -42,7 +42,7 @@ appStorage = window.localStorage || {
42 42 const markdown = window.markdownit();
43 43 const markdown_render = (content) => {
44 44 return markdown.render(content
45 .replaceAll(/<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm, "")
45 .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
46 46 .replaceAll(/<img data-prompt="[^>]+">/gm, "")
47 47 )
48 48 .replaceAll("<a href=", '<a target="_blank" href=')
@@ -127,9 +127,6 @@ const register_message_buttons = async () => {
127 127 sound.controls = 'controls';
128 128 sound.src = url;
129 129 sound.type = 'audio/wav';
130 if (ended && !stopped) {
131 sound.autoplay = true;
132 }
133 130 sound.onended = function() {
134 131 ended = true;
135 132 };
@@ -140,6 +137,9 @@ const register_message_buttons = async () => {
140 137 container.classList.add("audio");
141 138 container.appendChild(sound);
142 139 content_el.appendChild(container);
140 if (ended && !stopped) {
141 sound.play();
142 }
143 143 }
144 144 if (lines.length < 1 || stopped) {
145 145 el.classList.remove("active");
@@ -608,12 +608,11 @@ async function get_messages(conversation_id) {
608 608 }
609 609
610 610 async function add_conversation(conversation_id, content) {
611 if (content.length > 17) {
612 title = content.substring(0, 17) + '...'
611 if (content.length > 18) {
612 title = content.substring(0, 18) + '...'
613 613 } else {
614 title = content + '&nbsp;'.repeat(19 - content.length)
614 title = content + '&nbsp;'.repeat(20 - content.length)
615 615 }
616
617 616 if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
618 617 await save_conversation(conversation_id, {
619 618 id: conversation_id,
@@ -623,7 +622,6 @@ async function add_conversation(conversation_id, content) {
623 622 items: [],
624 623 });
625 624 }
626
627 625 history.pushState({}, null, `/chat/${conversation_id}`);
628 626 }
629 627
@@ -695,27 +693,31 @@ const load_conversations = async () => {
695 693
696 694 await clear_conversations();
697 695
698 for (conversation of conversations) {
696 conversations.sort((a, b) => (b.updated||0)-(a.updated||0));
697
698 let html = "";
699 conversations.forEach((conversation) => {
699 700 let updated = "";
700 701 if (conversation.updated) {
701 702 const date = new Date(conversation.updated);
702 703 updated = date.toLocaleString('en-GB', {dateStyle: 'short', timeStyle: 'short', monthStyle: 'short'});
703 704 updated = updated.replace("/" + date.getFullYear(), "")
704 705 }
705 box_conversations.innerHTML += `
706 html += `
706 707 <div class="convo" id="convo-${conversation.id}">
707 708 <div class="left" onclick="set_conversation('${conversation.id}')">
708 709 <i class="fa-regular fa-comments"></i>
709 710 <span class="convo-title"><span class="datetime">${updated}</span> ${conversation.title}</span>
710 711 </div>
711 <i onclick="show_option('${conversation.id}')" class="fa-regular fa-trash" id="conv-${conversation.id}"></i>
712 <i onclick="show_option('${conversation.id}')" class="fa-solid fa-ellipsis-vertical" id="conv-${conversation.id}"></i>
712 713 <div id="cho-${conversation.id}" class="choise" style="display:none;">
713 <i onclick="delete_conversation('${conversation.id}')" class="fa-regular fa-check"></i>
714 <i onclick="delete_conversation('${conversation.id}')" class="fa-regular fa-trash"></i>
714 715 <i onclick="hide_option('${conversation.id}')" class="fa-regular fa-x"></i>
715 716 </div>
716 717 </div>
717 718 `;
718 }
719 });
720 box_conversations.innerHTML = html;
719 721 };
720 722
721 723 document.getElementById("cancelButton").addEventListener("click", async () => {
@@ -804,6 +806,7 @@ const register_settings_storage = async () => {
804 806 appStorage.setItem(element.id, element.selectedIndex);
805 807 break;
806 808 case "text":
809 case "number":
807 810 appStorage.setItem(element.id, element.value);
808 811 break;
809 812 default:
@@ -828,6 +831,7 @@ const load_settings_storage = async () => {
828 831 element.selectedIndex = parseInt(value);
829 832 break;
830 833 case "text":
834 case "number":
831 835 case "textarea":
832 836 element.value = value;
833 837 break;
Modified g4f/gui/server/api.py +2 -2
Modified g4f/providers/helper.py +8 -1