返回提交历史
Modified
g4f/Provider/Jmuz.py
+13
-11
Modified
g4f/Provider/Pizzagpt.py
+2
-2
Modified
g4f/Provider/needs_auth/Custom.py
+4
-3
Modified
g4f/Provider/needs_auth/GeminiPro.py
+16
-1
Modified
g4f/Provider/needs_auth/OpenaiAPI.py
+6
-2
Modified
g4f/api/__init__.py
+10
-6
Modified
g4f/gui/client/index.html
+9
-4
Modified
g4f/gui/client/static/js/chat.v1.js
+13
-22
Modified
g4f/gui/server/api.py
+5
-2
Modified
g4f/gui/server/backend_api.py
+2
-1
Modified
g4f/models.py
+1
-3
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
代码差异
11 个文件
+81
-57
@@ -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
@@ -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
@@ -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
@@ -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}")
@@ -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
@@ -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},
@@ -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=""_U" 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>
@@ -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;
@@ -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"
@@ -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
@@ -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