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

XFEstudio/gpt4free

Read FinishReason and Usage from Gemini API Add "Custom Provider": Set API Url in the settings Remove Discord link from result, add them to attr: Jmuz Fix Bug: File content are added to the prompt Changed response from /v1/models API Disable Pizzagpt Provider

2df2d6b0
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

11 个文件 +81 -57
Modified g4f/Provider/Jmuz.py +13 -11
@@ -5,7 +5,7 @@ from .needs_auth.OpenaiAPI import OpenaiAPI
5 5
6 6 class Jmuz(OpenaiAPI):
7 7 label = "Jmuz"
8 url = "https://jmuz.me"
8 url = "https://discord.gg/qXfu24JmsB"
9 9 login_url = None
10 10 api_base = "https://jmuz.me/gpt/api/v2"
11 11 api_key = "prod"
@@ -15,7 +15,7 @@ class Jmuz(OpenaiAPI):
15 15 supports_stream = True
16 16 supports_system_message = False
17 17
18 default_model = 'gpt-4o'
18 default_model = "gpt-4o"
19 19 model_aliases = {
20 20 "gemini": "gemini-exp",
21 21 "deepseek-chat": "deepseek-2.5",
@@ -29,13 +29,7 @@ class Jmuz(OpenaiAPI):
29 29 return cls.models
30 30
31 31 @classmethod
32 def get_model(cls, model: str, **kwargs) -> str:
33 if model in cls.get_models():
34 return model
35 return cls.default_model
36
37 @classmethod
38 def create_async_generator(
32 async def create_async_generator(
39 33 cls,
40 34 model: str,
41 35 messages: Messages,
@@ -52,7 +46,8 @@ class Jmuz(OpenaiAPI):
52 46 "cache-control": "no-cache",
53 47 "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
54 48 }
55 return super().create_async_generator(
49 started = False
50 async for chunk in super().create_async_generator(
56 51 model=model,
57 52 messages=messages,
58 53 api_base=cls.api_base,
@@ -60,4 +55,11 @@ class Jmuz(OpenaiAPI):
60 55 stream=cls.supports_stream,
61 56 headers=headers,
62 57 **kwargs
63 )
58 ):
59 if isinstance(chunk, str) and cls.url in chunk:
60 continue
61 if isinstance(chunk, str) and not started:
62 chunk = chunk.lstrip()
63 if chunk:
64 started = True
65 yield chunk
Modified g4f/Provider/Pizzagpt.py +2 -2
@@ -10,7 +10,7 @@ from .helper import format_prompt
10 10 class Pizzagpt(AsyncGeneratorProvider, ProviderModelMixin):
11 11 url = "https://www.pizzagpt.it"
12 12 api_endpoint = "/api/chatx-completion"
13 working = True
13 working = False
14 14 default_model = 'gpt-4o-mini'
15 15
16 16 @classmethod
@@ -46,6 +46,6 @@ class Pizzagpt(AsyncGeneratorProvider, ProviderModelMixin):
46 46 response_json = await response.json()
47 47 content = response_json.get("answer", response_json).get("content")
48 48 if content:
49 if "misuse detected. please get in touch" in content:
49 if "Misuse detected. please get in touch" in content:
50 50 raise ValueError(content)
51 51 yield content
Modified g4f/Provider/needs_auth/Custom.py +4 -3
@@ -3,9 +3,10 @@ from __future__ import annotations
3 3 from .OpenaiAPI import OpenaiAPI
4 4
5 5 class Custom(OpenaiAPI):
6 label = "Custom"
6 label = "Custom Provider"
7 7 url = None
8 login_url = "http://localhost:8080"
8 login_url = None
9 9 working = True
10 10 api_base = "http://localhost:8080/v1"
11 needs_auth = False
11 needs_auth = False
12 sort_models = False
Modified g4f/Provider/needs_auth/GeminiPro.py +16 -1
@@ -3,12 +3,14 @@ from __future__ import annotations
3 3 import base64
4 4 import json
5 5 import requests
6 from typing import Optional
6 7 from aiohttp import ClientSession, BaseConnector
7 8
8 9 from ...typing import AsyncResult, Messages, ImagesType
9 10 from ...image import to_bytes, is_accepted_format
10 11 from ...errors import MissingAuthError
11 12 from ...requests.raise_for_status import raise_for_status
13 from ...providers.response import Usage, FinishReason
12 14 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
13 15 from ..helper import get_connector
14 16 from ... import debug
@@ -62,6 +64,7 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
62 64 api_base: str = api_base,
63 65 use_auth_header: bool = False,
64 66 images: ImagesType = None,
67 tools: Optional[list] = None,
65 68 connector: BaseConnector = None,
66 69 **kwargs
67 70 ) -> AsyncResult:
@@ -104,7 +107,10 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
104 107 "maxOutputTokens": kwargs.get("max_tokens"),
105 108 "topP": kwargs.get("top_p"),
106 109 "topK": kwargs.get("top_k"),
107 }
110 },
111 "tools": [{
112 "functionDeclarations": tools
113 }] if tools else None
108 114 }
109 115 system_prompt = "\n".join(
110 116 message["content"]
@@ -128,6 +134,15 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
128 134 data = b"".join(lines)
129 135 data = json.loads(data)
130 136 yield data["candidates"][0]["content"]["parts"][0]["text"]
137 if "finishReason" in data["candidates"][0]:
138 yield FinishReason(data["candidates"][0]["finishReason"].lower())
139 usage = data.get("usageMetadata")
140 if usage:
141 yield Usage(
142 prompt_tokens=usage.get("promptTokenCount"),
143 completion_tokens=usage.get("candidatesTokenCount"),
144 total_tokens=usage.get("totalTokenCount")
145 )
131 146 except:
132 147 data = data.decode(errors="ignore") if isinstance(data, bytes) else data
133 148 raise RuntimeError(f"Read chunk failed: {data}")
Modified g4f/Provider/needs_auth/OpenaiAPI.py +6 -2
@@ -23,6 +23,7 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin):
23 23 supports_system_message = True
24 24 default_model = ""
25 25 fallback_models = []
26 sort_models = True
26 27
27 28 @classmethod
28 29 def get_models(cls, api_key: str = None, api_base: str = None) -> list[str]:
@@ -36,8 +37,11 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin):
36 37 response = requests.get(f"{api_base}/models", headers=headers)
37 38 raise_for_status(response)
38 39 data = response.json()
39 cls.models = [model.get("id") for model in (data.get("data") if isinstance(data, dict) else data)]
40 cls.models.sort()
40 data = data.get("data") if isinstance(data, dict) else data
41 cls.image_models = [model.get("id") for model in data if model.get("image")]
42 cls.models = [model.get("id") for model in data]
43 if cls.sort_models:
44 cls.models.sort()
41 45 except Exception as e:
42 46 debug.log(e)
43 47 cls.models = cls.fallback_models
Modified g4f/api/__init__.py +10 -6
@@ -215,12 +215,16 @@ class Api:
215 215 HTTP_200_OK: {"model": List[ModelResponseModel]},
216 216 })
217 217 async def models():
218 return [{
219 'id': model_id,
220 'object': 'model',
221 'created': 0,
222 'owned_by': model.base_provider
223 } for model_id, model in g4f.models.ModelUtils.convert.items()]
218 return {
219 "object": "list",
220 "data": [{
221 "id": model_id,
222 "object": "model",
223 "created": 0,
224 "owned_by": model.base_provider,
225 "image": isinstance(model, g4f.models.ImageModel),
226 } for model_id, model in g4f.models.ModelUtils.convert.items()]
227 }
224 228
225 229 @self.app.get("/v1/models/{model_name}", responses={
226 230 HTTP_200_OK: {"model": ModelResponseModel},
Modified g4f/gui/client/index.html +9 -4
@@ -143,7 +143,7 @@
143 143 <label for="refine" class="toogle" title=""></label>
144 144 </div>
145 145 <div class="field box">
146 <label for="systemPrompt" class="label" title="">Default for System prompt</label>
146 <label for="systemPrompt" class="label" title="">System prompt</label>
147 147 <textarea id="systemPrompt" placeholder="You are a helpful assistant."></textarea>
148 148 </div>
149 149 <div class="field box">
@@ -157,6 +157,14 @@
157 157 document.getElementById('recognition-language').placeholder = navigator.language;
158 158 </script>
159 159 </div>
160 <div class="field box">
161 <label for="Custom-api_base" class="label" title="">Custom Provider (Base Url):</label>
162 <input type="text" id="Custom-api_base" name="Custom[api_base]" placeholder="http://localhost:8080/v1"/>
163 </div>
164 <div class="field box hidden">
165 <label for="Custom-api_key" class="label" title="">Custom Provider:</label>
166 <input type="text" id="Custom-api_key" name="Custom[api_key]" placeholder="api_key"/>
167 </div>
160 168 <div class="field box hidden">
161 169 <label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
162 170 <input type="text" id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"/>
@@ -254,10 +262,7 @@
254 262 <option value="gpt-4o">gpt-4o</option>
255 263 <option value="gpt-4o-mini">gpt-4o-mini</option>
256 264 <option value="llama-3.1-70b">llama-3.1-70b</option>
257 <option value="llama-3.1-405b">llama-3.1-405b</option>
258 265 <option value="mixtral-8x7b">mixtral-8x7b</option>
259 <option value="gemini-pro">gemini-pro</option>
260 <option value="gemini-flash">gemini-flash</option>
261 266 <option value="claude-3.5-sonnet">claude-3.5-sonnet</option>
262 267 <option value="flux">flux (Image Generation)</option>
263 268 <option value="dall-e-3">dall-e-3 (Image Generation)</option>
Modified g4f/gui/client/static/js/chat.v1.js +13 -22
@@ -351,11 +351,6 @@ const handle_ask = async () => {
351 351 await count_input()
352 352 await add_conversation(window.conversation_id);
353 353
354 if ("text" in fileInput.dataset) {
355 message += '\n```' + fileInput.dataset.type + '\n';
356 message += fileInput.dataset.text;
357 message += '\n```'
358 }
359 354 let message_index = await add_message(window.conversation_id, "user", message);
360 355 let message_id = get_message_id();
361 356
@@ -799,6 +794,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
799 794 const files = input && input.files.length > 0 ? input.files : null;
800 795 const download_images = document.getElementById("download_images")?.checked;
801 796 const api_key = get_api_key_by_provider(provider);
797 const api_base = provider == "Custom" ? document.getElementById(`${provider}-api_base`).value : null;
802 798 const ignored = Array.from(settings.querySelectorAll("input.provider:not(:checked)")).map((el)=>el.value);
803 799 await api("conversation", {
804 800 id: message_id,
@@ -811,6 +807,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
811 807 action: action,
812 808 download_images: download_images,
813 809 api_key: api_key,
810 api_base: api_base,
814 811 ignored: ignored,
815 812 }, files, message_id, scroll);
816 813 content_map.update_timeouts.forEach((timeoutId)=>clearTimeout(timeoutId));
@@ -1066,7 +1063,7 @@ const load_conversation = async (conversation_id, scroll=true) => {
1066 1063 }
1067 1064 buffer = buffer.replace(/ \[aborted\]$/g, "").replace(/ \[error\]$/g, "");
1068 1065 new_content = item.content.replace(/ \[aborted\]$/g, "").replace(/ \[error\]$/g, "");
1069 buffer += merge_messages(buffer, new_content);
1066 buffer = merge_messages(buffer, new_content);
1070 1067 last_model = item.provider?.model;
1071 1068 providers.push(item.provider?.name);
1072 1069 let next_i = parseInt(i) + 1;
@@ -1176,9 +1173,6 @@ const load_conversation = async (conversation_id, scroll=true) => {
1176 1173 if (window.GPTTokenizer_cl100k_base) {
1177 1174 const filtered = prepare_messages(messages, null, true, false);
1178 1175 if (filtered.length > 0) {
1179 if (GPTTokenizer_o200k_base && last_model?.startsWith("gpt-4o") || last_model?.startsWith("o1")) {
1180 return GPTTokenizer_o200k_base?.encodeChat(filtered, last_model).length;
1181 }
1182 1176 last_model = last_model?.startsWith("gpt-3") ? "gpt-3.5-turbo" : "gpt-4"
1183 1177 let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
1184 1178 if (count_total > 0) {
@@ -1890,7 +1884,6 @@ setTimeout(load_version, 100);
1890 1884
1891 1885 fileInput.addEventListener('click', async (event) => {
1892 1886 fileInput.value = '';
1893 delete fileInput.dataset.text;
1894 1887 });
1895 1888
1896 1889 async function upload_cookies() {
@@ -1920,7 +1913,6 @@ function formatFileSize(bytes) {
1920 1913 async function upload_files(fileInput) {
1921 1914 const paperclip = document.querySelector(".user-input .fa-paperclip");
1922 1915 const bucket_id = uuid();
1923 delete fileInput.dataset.text;
1924 1916 paperclip.classList.add("blink");
1925 1917
1926 1918 const formData = new FormData();
@@ -1980,8 +1972,7 @@ fileInput.addEventListener('change', async (event) => {
1980 1972 if (type == "json") {
1981 1973 const reader = new FileReader();
1982 1974 reader.addEventListener('load', async (event) => {
1983 fileInput.dataset.text = event.target.result;
1984 const data = JSON.parse(fileInput.dataset.text);
1975 const data = JSON.parse(event.target.result);
1985 1976 if (data.options && "g4f" in data.options) {
1986 1977 let count = 0;
1987 1978 Object.keys(data).forEach(key => {
@@ -1990,7 +1981,6 @@ fileInput.addEventListener('change', async (event) => {
1990 1981 count += 1;
1991 1982 }
1992 1983 });
1993 delete fileInput.dataset.text;
1994 1984 await load_conversations();
1995 1985 fileInput.value = "";
1996 1986 inputCount.innerText = `${count} Conversations were imported successfully`;
@@ -2012,8 +2002,6 @@ fileInput.addEventListener('change', async (event) => {
2012 2002 });
2013 2003 reader.readAsText(fileInput.files[0]);
2014 2004 }
2015 } else {
2016 delete fileInput.dataset.text;
2017 2005 }
2018 2006 });
2019 2007
@@ -2033,16 +2021,19 @@ function get_selected_model() {
2033 2021 }
2034 2022
2035 2023 async function api(ressource, args=null, files=null, message_id=null, scroll=true) {
2036 let api_key;
2024 const headers = {};
2037 2025 if (ressource == "models" && args) {
2038 2026 api_key = get_api_key_by_provider(args);
2027 if (api_key) {
2028 headers.x_api_key = api_key;
2029 }
2030 api_base = args == "Custom" ? document.getElementById(`${args}-api_base`).value : null;
2031 if (api_base) {
2032 headers.x_api_base = api_base;
2033 }
2039 2034 ressource = `${ressource}/${args}`;
2040 2035 }
2041 2036 const url = `/backend-api/v2/${ressource}`;
2042 const headers = {};
2043 if (api_key) {
2044 headers.x_api_key = api_key;
2045 }
2046 2037 if (ressource == "conversation") {
2047 2038 let body = JSON.stringify(args);
2048 2039 headers.accept = 'text/event-stream';
@@ -2224,7 +2215,7 @@ if (SpeechRecognition) {
2224 2215 };
2225 2216 recognition.onend = function() {
2226 2217 messageInput.value = `${startValue ? startValue + "\n" : ""}${buffer}`;
2227 if (!microLabel.classList.contains("recognition")) {
2218 if (microLabel.classList.contains("recognition")) {
2228 2219 recognition.start();
2229 2220 } else {
2230 2221 messageInput.readOnly = false;
Modified g4f/gui/server/api.py +5 -2
@@ -37,12 +37,12 @@ class Api:
37 37 for model, providers in models.__models__.values()]
38 38
39 39 @staticmethod
40 def get_provider_models(provider: str, api_key: str = None):
40 def get_provider_models(provider: str, api_key: str = None, api_base: str = None):
41 41 if provider in ProviderUtils.convert:
42 42 provider = ProviderUtils.convert[provider]
43 43 if issubclass(provider, ProviderModelMixin):
44 44 if api_key is not None and "api_key" in signature(provider.get_models).parameters:
45 models = provider.get_models(api_key=api_key)
45 models = provider.get_models(api_key=api_key, api_base=api_base)
46 46 else:
47 47 models = provider.get_models()
48 48 return [
@@ -90,6 +90,9 @@ class Api:
90 90 api_key = json_data.get("api_key")
91 91 if api_key is not None:
92 92 kwargs["api_key"] = api_key
93 api_base = json_data.get("api_base")
94 if api_base is not None:
95 kwargs["api_base"] = api_base
93 96 kwargs["tool_calls"] = [{
94 97 "function": {
95 98 "name": "bucket_tool"
Modified g4f/gui/server/backend_api.py +2 -1
@@ -303,7 +303,8 @@ class Backend_Api(Api):
303 303
304 304 def get_provider_models(self, provider: str):
305 305 api_key = request.headers.get("x_api_key")
306 models = super().get_provider_models(provider, api_key)
306 api_base = request.headers.get("x_api_base")
307 models = super().get_provider_models(provider, api_key, api_base)
307 308 if models is None:
308 309 return "Provider not found", 404
309 310 return models
Modified g4f/models.py +1 -3
@@ -36,7 +36,6 @@ from .Provider import (
36 36 OpenaiAccount,
37 37 PerplexityLabs,
38 38 Pi,
39 Pizzagpt,
40 39 PollinationsAI,
41 40 Reka,
42 41 ReplicateHome,
@@ -72,7 +71,6 @@ default = Model(
72 71 base_provider = "",
73 72 best_provider = IterListProvider([
74 73 DDG,
75 Pizzagpt,
76 74 Blackbox,
77 75 Copilot,
78 76 ChatGptEs,
@@ -118,7 +116,7 @@ gpt_4o = Model(
118 116 gpt_4o_mini = Model(
119 117 name = 'gpt-4o-mini',
120 118 base_provider = 'OpenAI',
121 best_provider = IterListProvider([DDG, Pizzagpt, ChatGptEs, ChatGptt, Jmuz, ChatGpt, RubiksAI, Liaobots, OpenaiChat])
119 best_provider = IterListProvider([DDG, ChatGptEs, ChatGptt, Jmuz, ChatGpt, RubiksAI, Liaobots, OpenaiChat])
122 120 )
123 121
124 122 # o1