返回提交历史
Modified
g4f/Provider/Copilot.py
+6
-9
Modified
g4f/Provider/PollinationsImage.py
+5
-4
Modified
g4f/Provider/__init__.py
+1
-2
Modified
g4f/Provider/hf/HuggingFaceInference.py
+7
-7
Modified
g4f/Provider/hf/models.py
+3
-2
Modified
g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
+2
-2
Modified
g4f/Provider/hf_space/G4F.py
+8
-7
Modified
g4f/Provider/hf_space/Janus_Pro_7B.py
+7
-11
Modified
g4f/gui/client/demo.html
+12
-4
Modified
g4f/gui/client/home.html
+4
-4
Modified
g4f/gui/client/index.html
+25
-8
Modified
g4f/gui/client/static/css/style.css
+7
-7
Modified
g4f/gui/client/static/img/site.webmanifest
+1
-0
Modified
g4f/gui/client/static/js/chat.v1.js
+221
-215
Modified
g4f/gui/server/backend_api.py
+1
-3
Modified
g4f/models.py
+12
-5
Modified
g4f/providers/base_provider.py
+5
-5
Modified
g4f/requests/__init__.py
+6
-2
Modified
g4f/tools/web_search.py
+1
-1
XFEstudio/gpt4free
Improve model list in HuggingFace WakeLook and disable count tokens for performance Export and import settings in UI Some styling improvments in UI Fix curl_cffi updated bugs
b84f35f4
代码差异
19 个文件
+334
-298
@@ -4,11 +4,11 @@ import os
4
4
import json
5
5
import asyncio
6
6
import base64
7
from http.cookiejar import CookieJar
8
7
from urllib.parse import quote
9
8
10
9
try:
11
from curl_cffi.requests import Session, CurlWsFlag
10
from curl_cffi.requests import Session
11
from curl_cffi import CurlWsFlag
12
12
has_curl_cffi = True
13
13
except ImportError:
14
14
has_curl_cffi = False
@@ -55,7 +55,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
55
55
conversation_url = f"{url}/c/api/conversations"
56
56
57
57
_access_token: str = None
58
_cookies: CookieJar = None
58
_cookies: dict = None
59
59
60
60
@classmethod
61
61
def create_completion(
@@ -86,9 +86,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
86
86
except NoValidHarFileError as h:
87
87
debug.log(f"Copilot: {h}")
88
88
if has_nodriver:
89
login_url = os.environ.get("G4F_LOGIN_URL")
90
if login_url:
91
yield RequestLogin(cls.label, login_url)
89
yield RequestLogin(cls.label, os.environ.get("G4F_LOGIN_URL", ""))
92
90
get_running_loop(check_nested=True)
93
91
cls._access_token, cls._cookies = asyncio.run(get_access_token_and_cookies(cls.url, proxy))
94
92
else:
@@ -104,7 +102,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
104
102
cookies=cls._cookies,
105
103
) as session:
106
104
if cls._access_token is not None:
107
cls._cookies = session.cookies.jar
105
cls._cookies = session.cookies.jar if hasattr(session.cookies, "jar") else session.cookies
108
106
# if cls._access_token is None:
109
107
# try:
110
108
# url = "https://copilot.microsoft.com/cl/eus-sc/collect"
@@ -203,8 +201,7 @@ class Copilot(AbstractProvider, ProviderModelMixin):
203
201
if not is_started:
204
202
raise RuntimeError(f"Invalid response: {last_msg}")
205
203
finally:
206
yield Parameters(**{"conversation": conversation.get_dict(), "user": user, "prompt": prompt})
207
yield Parameters(**{"cookies": {c.name: c.value for c in session.cookies.jar}})
204
wss.close()
208
205
209
206
async def get_access_token_and_cookies(url: str, proxy: str = None, target: str = "ChatAI",):
210
207
browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
@@ -7,16 +7,17 @@ from ..typing import AsyncResult, Messages
7
7
from .PollinationsAI import PollinationsAI
8
8
9
9
class PollinationsImage(PollinationsAI):
10
label = "Pollinations AI (Image)"
11
10
default_model = "flux"
12
11
default_vision_model = None
13
12
default_image_model = default_model
13
image_models = [default_image_model]
14
14
15
15
@classmethod
16
16
def get_models(cls, **kwargs):
17
if not cls.image_models:
18
cls.image_models = list(dict.fromkeys([*cls.image_models, *cls.extra_image_models]))
19
return cls.image_models
17
if not cls.models:
18
super().get_models(**kwargs)
19
cls.models = cls.image_models
20
return cls.models
20
21
21
22
@classmethod
22
23
async def create_async_generator(
@@ -10,7 +10,7 @@ from .needs_auth import *
10
10
from .not_working import *
11
11
from .local import *
12
12
from .hf import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference
13
from .hf_space import HuggingSpace
13
from .hf_space import *
14
14
from .mini_max import HailuoAI, MiniMax
15
15
from .template import OpenaiTemplate, BackendApi
16
16
@@ -53,7 +53,6 @@ __providers__: list[ProviderType] = [
53
53
if isinstance(provider, type)
54
54
and issubclass(provider, BaseProvider)
55
55
]
56
__providers__ = __providers__ + HuggingSpace.providers
57
56
__all__: list[str] = [
58
57
provider.__name__ for provider in __providers__
59
58
]
@@ -11,7 +11,7 @@ from ...errors import ModelNotSupportedError, ResponseError
11
11
from ...requests import StreamSession, raise_for_status
12
12
from ...providers.response import FinishReason, ImageResponse
13
13
from ..helper import format_image_prompt, get_last_user_message
14
from .models import default_model, default_image_model, model_aliases, fallback_models, image_models
14
from .models import default_model, default_image_model, model_aliases, text_models, image_models, vision_models
15
15
from ... import debug
16
16
17
17
class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin):
@@ -29,18 +29,18 @@ class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin):
29
29
@classmethod
30
30
def get_models(cls) -> list[str]:
31
31
if not cls.models:
32
models = fallback_models.copy()
32
models = text_models.copy()
33
33
url = "https://huggingface.co/api/models?inference=warm&pipeline_tag=text-generation"
34
34
response = requests.get(url)
35
35
if response.ok:
36
extra_models = [model["id"] for model in response.json()]
37
extra_models.sort()
38
models.extend([model for model in extra_models if model not in models])
36
extra_models = [model["id"] for model in response.json() if model.get("trendingScore", 0) >= 10]
37
models = extra_models + vision_models + [model for model in models if model not in extra_models]
39
38
url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
40
39
response = requests.get(url)
40
cls.image_models = image_models.copy()
41
41
if response.ok:
42
cls.image_models = [model["id"] for model in response.json() if model.get("trendingScore", 0) >= 20]
43
cls.image_models.sort()
42
extra_models = [model["id"] for model in response.json() if model.get("trendingScore", 0) >= 20]
43
cls.image_models.extend([model for model in extra_models if model not in cls.image_models])
44
44
models.extend([model for model in cls.image_models if model not in models])
45
45
cls.models = models
46
46
return cls.models
@@ -4,7 +4,7 @@ image_models = [
4
4
default_image_model,
5
5
"black-forest-labs/FLUX.1-schnell",
6
6
]
7
fallback_models = [
7
text_models = [
8
8
default_model,
9
9
'meta-llama/Llama-3.3-70B-Instruct',
10
10
'CohereForAI/c4ai-command-r-plus-08-2024',
@@ -15,7 +15,8 @@ fallback_models = [
15
15
'meta-llama/Llama-3.2-11B-Vision-Instruct',
16
16
'mistralai/Mistral-Nemo-Instruct-2407',
17
17
'microsoft/Phi-3.5-mini-instruct',
18
] + image_models
18
]
19
fallback_models = text_models + image_models
19
20
model_aliases = {
20
21
### Chat ###
21
22
"qwen-2.5-72b": "Qwen/Qwen2.5-Coder-32B-Instruct",
@@ -62,7 +62,7 @@ class BlackForestLabsFlux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
62
62
seed: int = 0,
63
63
randomize_seed: bool = True,
64
64
cookies: dict = None,
65
zerogpu_token: str = None,
65
api_key: str = None,
66
66
zerogpu_uuid: str = "[object Object]",
67
67
**kwargs
68
68
) -> AsyncResult:
@@ -70,7 +70,7 @@ class BlackForestLabsFlux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
70
70
async with StreamSession(impersonate="chrome", proxy=proxy) as session:
71
71
prompt = format_image_prompt(messages, prompt)
72
72
data = [prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
73
conversation = JsonConversation(zerogpu_token=zerogpu_token, zerogpu_uuid=zerogpu_uuid, session_hash=uuid.uuid4().hex)
73
conversation = JsonConversation(zerogpu_token=api_key, zerogpu_uuid=zerogpu_uuid, session_hash=uuid.uuid4().hex)
74
74
if conversation.zerogpu_token is None:
75
75
conversation.zerogpu_uuid, conversation.zerogpu_token = await get_zerogpu_token(cls.space, session, conversation, cookies)
76
76
async with cls.run(f"post", session, conversation, data) as response:
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
3
3
from aiohttp import ClientSession
4
4
import time
5
import random
5
6
import asyncio
6
7
7
8
from ...typing import AsyncResult, Messages
@@ -40,7 +41,7 @@ class G4F(Janus_Pro_7B):
40
41
height: int = 1024,
41
42
seed: int = None,
42
43
cookies: dict = None,
43
zerogpu_token: str = None,
44
api_key: str = None,
44
45
zerogpu_uuid: str = "[object Object]",
45
46
**kwargs
46
47
) -> AsyncResult:
@@ -53,7 +54,7 @@ class G4F(Janus_Pro_7B):
53
54
height=height,
54
55
seed=seed,
55
56
cookies=cookies,
56
zerogpu_token=zerogpu_token,
57
api_key=api_key,
57
58
zerogpu_uuid=zerogpu_uuid,
58
59
**kwargs
59
60
):
@@ -66,7 +67,7 @@ class G4F(Janus_Pro_7B):
66
67
prompt=prompt,
67
68
seed=seed,
68
69
cookies=cookies,
69
zerogpu_token=zerogpu_token,
70
api_key=api_key,
70
71
zerogpu_uuid=zerogpu_uuid,
71
72
**kwargs
72
73
):
@@ -79,7 +80,7 @@ class G4F(Janus_Pro_7B):
79
80
if prompt is None:
80
81
prompt = format_image_prompt(messages)
81
82
if seed is None:
82
seed = int(time.time())
83
seed = random.randint(9999, 2**32 - 1)
83
84
84
85
payload = {
85
86
"data": [
@@ -96,11 +97,11 @@ class G4F(Janus_Pro_7B):
96
97
"trigger_id": 10
97
98
}
98
99
async with ClientSession() as session:
99
if zerogpu_token is None:
100
if api_key is None:
100
101
yield Reasoning(status="Acquiring GPU Token")
101
zerogpu_uuid, zerogpu_token = await get_zerogpu_token(cls.space, session, JsonConversation(), cookies)
102
zerogpu_uuid, api_key = await get_zerogpu_token(cls.space, session, JsonConversation(), cookies)
102
103
headers = {
103
"x-zerogpu-token": zerogpu_token,
104
"x-zerogpu-token": api_key,
104
105
"x-zerogpu-uuid": zerogpu_uuid,
105
106
}
106
107
headers = {k: v for k, v in headers.items() if v is not None}
@@ -71,17 +71,13 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
71
71
prompt: str = None,
72
72
proxy: str = None,
73
73
cookies: Cookies = None,
74
zerogpu_token: str = None,
74
api_key: str = None,
75
75
zerogpu_uuid: str = "[object Object]",
76
76
return_conversation: bool = False,
77
77
conversation: JsonConversation = None,
78
78
seed: int = None,
79
79
**kwargs
80
80
) -> AsyncResult:
81
def generate_session_hash():
82
"""Generate a unique session hash."""
83
return str(uuid.uuid4()).replace('-', '')[:12]
84
85
81
method = "post"
86
82
if model == cls.default_image_model or prompt is not None:
87
83
method = "image"
@@ -90,14 +86,14 @@ class Janus_Pro_7B(AsyncGeneratorProvider, ProviderModelMixin):
90
86
if seed is None:
91
87
seed = random.randint(1000, 999999)
92
88
93
session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
89
session_hash = uuid.uuid4().hex if conversation is None else getattr(conversation, "session_hash", uuid.uuid4().hex)
94
90
async with StreamSession(proxy=proxy, impersonate="chrome") as session:
95
session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
96
if zerogpu_token is None:
97
zerogpu_uuid, zerogpu_token = await get_zerogpu_token(cls.space, session, conversation, cookies)
91
if api_key is None:
92
zerogpu_uuid, api_key = await get_zerogpu_token(cls.space, session, conversation, cookies)
98
93
if conversation is None or not hasattr(conversation, "session_hash"):
99
conversation = JsonConversation(session_hash=session_hash, zerogpu_token=zerogpu_token, zerogpu_uuid=zerogpu_uuid)
100
conversation.zerogpu_token = zerogpu_token
94
conversation = JsonConversation(session_hash=session_hash, zerogpu_token=api_key, zerogpu_uuid=zerogpu_uuid)
95
else:
96
conversation.zerogpu_token = api_key
101
97
if return_conversation:
102
98
yield conversation
103
99
@@ -183,13 +183,21 @@
183
183
const isIframe = window.self !== window.top;
184
184
const backendUrl = "{{backend_url}}";
185
185
let url = new URL(window.location.href)
186
let params = new URLSearchParams(url.search);
186
187
if (isIframe && backendUrl) {
187
window.location.replace(url.search ? `${backendUrl}?${url.search}` : backendUrl);
188
if (params.get("get_gpu_token")) {
189
window.addEventListener('DOMContentLoaded', async function() {
190
const link = document.getElementById("new_window");
191
link.href = `${backendUrl}${url.search}`;
192
link.click();
193
});
194
} else {
195
window.location.replace(`${backendUrl}${url.search}`);
196
}
188
197
return;
189
198
}
190
let params = new URLSearchParams(url.search);
191
199
if (params.get("__sign")) {
192
localStorage.setItem("zerogpu_token", params.get("__sign"));
200
localStorage.setItem("HuggingSpace-api_key", params.get("__sign"));
193
201
if (!isIframe) {
194
202
window.location.replace("/");
195
203
}
@@ -226,7 +234,7 @@
226
234
<form action="/chat/">
227
235
<input type="text" name="token" class="input-field" placeholder="Enter an Access Token..." autocomplete="off">
228
236
<div class="button-container">
229
<a href="" class="button hidden" target="_blank">New Window</a>
237
<a id="new_window" href="" class="button hidden" target="_blank">New Window</a>
230
238
<button class="button">Submit</button>
231
239
</div>
232
240
<p>
@@ -224,15 +224,15 @@
224
224
</div>
225
225
<script>
226
226
const iframe = document.querySelector('.stream-widget');
227
const rand_idx = Math.floor(Math.random() * 9)
227
const rand_idx = Math.floor(Math.random() * 12)
228
228
if (rand_idx < 3) {
229
229
search = "xtekky/gpt4free releases";
230
} else if (rand_idx < 5) {
230
} else if (rand_idx < 6) {
231
231
search = "developer news";
232
232
} else {
233
233
search = (navigator.language == "de" ? "news in deutsch" : navigator.language == "en" ? "world news" : `news in ${navigator.language}`);
234
234
}
235
const summary_prompt = "Give a summary of the provided text in ```markdown``` format. Add maybe one or more images.";
235
const summary_prompt = "Present the news from the search results in a clear and organized markdown format. Include a headline, a brief summary, key points, and one or more relevant images with proper attribution. Ensure the content is concise, well-structured, and visually appealing.";
236
236
const url = `/backend-api/v2/create?prompt=${summary_prompt}&stream=1&web_search=${search}`;
237
237
iframe.src = url;
238
238
const message = "Loading...";
@@ -265,7 +265,7 @@
265
265
if (!i.contentWindow || !i.contentDocument) {
266
266
return;
267
267
}
268
clientHeight = i.contentDocument.body.scrollHeight;
268
clientHeight = i.contentDocument.body?.scrollHeight;
269
269
i.contentWindow.scrollTo(0, clientHeight);
270
270
if (clientHeight - i.contentWindow.scrollY < 2 * clientHeight) {
271
271
setTimeout(scroll_to_bottom_callback, 1000);
@@ -33,14 +33,22 @@
33
33
};
34
34
</script>
35
35
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
36
<script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
37
import mistralTokenizer from "mistral-tokenizer-js"
38
</script>
39
<script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
40
import llamaTokenizer from "llama-tokenizer-js"
36
<template>
37
<script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
38
import mistralTokenizer from "mistral-tokenizer-js"
39
</script>
40
<script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
41
import llamaTokenizer from "llama-tokenizer-js"
42
</script>
43
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
44
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/o200k_base.js" async></script>
45
</template>
46
<script>
47
if (localStorage.getItem("countTokens") != "false") {
48
const template = document.head.querySelector('template');
49
document.head.appendChild(template.content);
50
}
41
51
</script>
42
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
43
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/o200k_base.js" async></script>
44
52
<script type="module" async>
45
53
import PhotoSwipeLightbox from 'https://cdn.jsdelivr.net/npm/photoswipe/dist/photoswipe-lightbox.esm.js';
46
54
const lightbox = new PhotoSwipeLightbox({
@@ -168,6 +176,11 @@
168
176
<input type="checkbox" id="report_error" checked/>
169
177
<label for="report_error" class="toogle" title=""></label>
170
178
</div>
179
<div class="field">
180
<span class="label">Count words and tokens</span>
181
<input type="checkbox" id="countTokens" checked/>
182
<label for="countTokens" class="toogle" title=""></label>
183
</div>
171
184
<div class="field box">
172
185
<label for="systemPrompt" class="label">System prompt</label>
173
186
<textarea id="systemPrompt" placeholder="You are a helpful assistant." data-example="If you need to generate images, you can use the following format: . This will enable the use of an image generation tool."></textarea>
@@ -193,7 +206,7 @@
193
206
<label for="mem0-api_key" class="label" title="">Mem0 API:</label>
194
207
<input type="text" id="mem0-api_key" name="mem0[api_key]" placeholder="api_key"/>
195
208
</div>
196
<div class="field box">
209
<div class="field box hidden">
197
210
<label for="Custom-api_base" class="label" title="">Custom Provider (Base Url):</label>
198
211
<input type="text" id="Custom-api_base" name="Custom[api_base]" placeholder="http://localhost:8080/v1"/>
199
212
</div>
@@ -215,6 +228,10 @@
215
228
<i class="fa-solid fa-download"></i>
216
229
<a href="" onclick="return false;">Export Conversations</a>
217
230
</button>
231
<button onclick="save_storage(true)">
232
<i class="fa-solid fa-pencil"></i>
233
<a href="" onclick="return false;">Export Settings</a>
234
</button>
218
235
<button id="showLog">
219
236
<i class="fa-solid fa-terminal"></i>
220
237
<a href="" onclick="return false;">Show log</a>
@@ -768,11 +768,10 @@ form input:checked+label {
768
768
}
769
769
770
770
.settings .bottom_buttons {
771
flex-direction: column;
771
padding-bottom: 50px;
772
772
}
773
773
774
774
.settings .bottom_buttons button {
775
display: inline-block;
776
775
max-width: 210px;
777
776
width: 100%;
778
777
}
@@ -858,6 +857,10 @@ button.regenerate_button, button.continue_button, button.options_button {
858
857
backdrop-filter: none;
859
858
}
860
859
860
button.options_button {
861
margin-left: auto;
862
}
863
861
864
.buttons button.pinned span {
862
865
max-width: 160px;
863
866
overflow: hidden;
@@ -1026,6 +1029,7 @@ input.model:hover
1026
1029
.mem0 button span {
1027
1030
color: var(--colour-3);
1028
1031
font-weight: 500;
1032
text-decoration: none;
1029
1033
}
1030
1034
1031
1035
.conversations .top {
@@ -1145,7 +1149,7 @@ ul {
1145
1149
}
1146
1150
1147
1151
.settings h3 {
1148
padding-left: 54px;
1152
padding-left: 14px;
1149
1153
padding-top: 18px;
1150
1154
}
1151
1155
@@ -1164,10 +1168,6 @@ ul {
1164
1168
padding-left: 60px;
1165
1169
}
1166
1170
1167
.settings h3 {
1168
text-align: center;
1169
}
1170
1171
1171
.field.collapsible {
1172
1172
flex-direction: column;
1173
1173
}