返回提交历史
Modified
README.md
+8
-2
Modified
g4f/__init__.py
+5
-1
Modified
g4f/gui/client/css/style.css
+7
-11
Modified
g4f/gui/client/html/index.html
+6
-0
Modified
g4f/gui/client/js/chat.v1.js
+68
-83
Modified
g4f/gui/server/backend.py
+27
-25
XFEstudio/gpt4free
Add G4F_PROXY environment Add regenerate button in gui
bf41cfc5
代码差异
6 个文件
+121
-122
@@ -22,7 +22,7 @@ docker pull hlohaus789/g4f
22
22
23
23
## 📚 Table of Contents
24
24
25
- [🆕 What's New](#-what-s-new)
25
- [🆕 What's New](#-whats-new)
26
26
- [📚 Table of Contents](#-table-of-contents)
27
27
- [🛠️ Getting Started](#-getting-started)
28
28
+ [Docker container](#docker-container)
@@ -253,7 +253,7 @@ for message in response:
253
253
254
254
##### Using Browser
255
255
256
Some providers using a a browser to bypass the bot protection. They using the selenium webdriver to control the browser. The browser settings and the login data are saved in a custom directory. If the headless mode is enabled, the browser windows are loaded invisibly. For performance reasons, it is recommended to reuse the browser instances and close them yourself at the end:
256
Some providers using a browser to bypass the bot protection. They using the selenium webdriver to control the browser. The browser settings and the login data are saved in a custom directory. If the headless mode is enabled, the browser windows are loaded invisibly. For performance reasons, it is recommended to reuse the browser instances and close them yourself at the end:
257
257
258
258
```python
259
259
import g4f
@@ -335,6 +335,12 @@ response = g4f.ChatCompletion.create(
335
335
print(f"Result:", response)
336
336
```
337
337
338
You can also set a proxy globally via an environment variable:
339
340
```sh
341
export G4F_PROXY="http://host:port"
342
```
343
338
344
### Interference openai-proxy API (Use with openai python package)
339
345
340
346
#### Run interference API from PyPi package
@@ -96,6 +96,10 @@ class ChatCompletion:
96
96
97
97
if auth:
98
98
kwargs['auth'] = auth
99
100
proxy = os.environ.get("G4F_PROXY")
101
if proxy and "proxy" not in kwargs:
102
kwargs['proxy'] = proxy
99
103
100
104
result = provider.create_completion(model.name, messages, stream, **kwargs)
101
105
return result if stream else ''.join(result)
@@ -112,7 +116,7 @@ class ChatCompletion:
112
116
if stream:
113
117
if isinstance(provider, type) and issubclass(provider, AsyncGeneratorProvider):
114
118
return await provider.create_async_generator(model.name, messages, **kwargs)
115
raise ValueError(f'{provider.__name__} does not support "stream" argument')
119
raise ValueError(f'{provider.__name__} does not support "stream" argument in "create_async"')
116
120
117
121
return await provider.create_async(model.name, messages, **kwargs)
118
122
@@ -301,6 +301,9 @@ body {
301
301
font-size: 15px;
302
302
line-height: 1.3;
303
303
}
304
.message .content pre {
305
white-space: pre-wrap;
306
}
304
307
305
308
.message .user i {
306
309
position: absolute;
@@ -338,19 +341,15 @@ body {
338
341
font-size: 14px;
339
342
}
340
343
341
342
.stop_generating {
344
.stop_generating, .regenerate {
343
345
position: absolute;
344
bottom: 118px;
345
/* left: 10px;
346
bottom: 125px;
347
right: 8px; */
346
bottom: 158px;
348
347
left: 50%;
349
348
transform: translateX(-50%);
350
349
z-index: 1000000;
351
350
}
352
351
353
.stop_generating button {
352
.stop_generating button, .regenerate button{
354
353
backdrop-filter: blur(20px);
355
354
-webkit-backdrop-filter: blur(20px);
356
355
background-color: var(--blur-bg);
@@ -380,11 +379,8 @@ body {
380
379
}
381
380
}
382
381
383
.stop_generating-hiding button {
382
.stop_generating-hidden #cancelButton, .regenerate-hidden #regenerateButton {
384
383
animation: hide_popup 0.4s;
385
}
386
387
.stop_generating-hidden button {
388
384
display: none;
389
385
}
390
386
@@ -101,6 +101,12 @@
101
101
<i class="fa-regular fa-stop"></i>
102
102
</button>
103
103
</div>
104
<div class="regenerate regenerate-hidden">
105
<button id="regenerateButton">
106
<span>Regenerate</span>
107
<i class="fa-solid fa-rotate"></i>
108
</button>
109
</div>
104
110
<div class="box" id="messages">
105
111
</div>
106
112
<div class="user-input">
@@ -5,15 +5,12 @@ const message_input = document.getElementById(`message-input`);
5
5
const box_conversations = document.querySelector(`.top`);
6
6
const spinner = box_conversations.querySelector(".spinner");
7
7
const stop_generating = document.querySelector(`.stop_generating`);
8
const regenerate = document.querySelector(`.regenerate`);
8
9
const send_button = document.querySelector(`#send-button`);
9
10
let prompt_lock = false;
10
11
11
12
hljs.addPlugin(new CopyButtonPlugin());
12
13
13
const format = (text) => {
14
return text.replace(/(?:\r\n|\r|\n)/g, "<br>");
15
};
16
17
14
message_input.addEventListener("blur", () => {
18
15
window.scrollTo(0, 0);
19
16
});
@@ -22,6 +19,10 @@ message_input.addEventListener("focus", () => {
22
19
document.documentElement.scrollTop = document.documentElement.scrollHeight;
23
20
});
24
21
22
const markdown_render = (content) => {
23
return markdown.render(content).replace("<a href=", '<a target="_blank" href=').replace('<code>', '<code class="language-plaintext">')
24
}
25
25
26
const delete_conversations = async () => {
26
27
localStorage.clear();
27
28
await new_conversation();
@@ -30,38 +31,25 @@ const delete_conversations = async () => {
30
31
const handle_ask = async () => {
31
32
message_input.style.height = `80px`;
32
33
message_input.focus();
33
34
let txtMsgs = [];
35
const divTags = document.getElementsByClassName("message");
36
for(let i=0;i<divTags.length;i++){
37
if(!divTags[i].children[1].classList.contains("welcome-message")){
38
if(divTags[i].children[0].className == "assistant"){
39
const msg = {
40
role: "assistant",
41
content: divTags[i].children[1].textContent+" "
42
};
43
txtMsgs.push(msg);
44
}else{
45
const msg = {
46
role: "user",
47
content: divTags[i].children[1].textContent+" "
48
};
49
txtMsgs.push(msg);
50
}
51
}
52
}
53
54
34
window.scrollTo(0, 0);
55
let message = message_input.value;
56
const msg = {
57
role: "user",
58
content: message
59
};
60
txtMsgs.push(msg);
61
35
message = message_input.value
62
36
if (message.length > 0) {
63
message_input.value = ``;
64
await ask_gpt(txtMsgs);
37
message_input.value = '';
38
await add_conversation(window.conversation_id, message);
39
await add_message(window.conversation_id, "user", message);
40
window.token = message_id();
41
message_box.innerHTML += `
42
<div class="message">
43
<div class="user">
44
${user_image}
45
<i class="fa-regular fa-phone-arrow-up-right"></i>
46
</div>
47
<div class="content" id="user_${token}">
48
${markdown_render(message)}
49
</div>
50
</div>
51
`;
52
await ask_gpt();
65
53
}
66
54
};
67
55
@@ -74,13 +62,10 @@ const remove_cancel_button = async () => {
74
62
}, 300);
75
63
};
76
64
77
const ask_gpt = async (txtMsgs) => {
65
const ask_gpt = async () => {
66
regenerate.classList.add(`regenerate-hidden`);
67
messages = await get_messages(window.conversation_id);
78
68
try {
79
message_input.value = ``;
80
message_input.innerHTML = ``;
81
message_input.innerText = ``;
82
83
add_conversation(window.conversation_id, txtMsgs[0].content);
84
69
window.scrollTo(0, 0);
85
70
window.controller = new AbortController();
86
71
@@ -89,22 +74,9 @@ const ask_gpt = async (txtMsgs) => {
89
74
model = document.getElementById("model");
90
75
prompt_lock = true;
91
76
window.text = ``;
92
window.token = message_id();
93
77
94
78
stop_generating.classList.remove(`stop_generating-hidden`);
95
79
96
message_box.innerHTML += `
97
<div class="message">
98
<div class="user">
99
${user_image}
100
<i class="fa-regular fa-phone-arrow-up-right"></i>
101
</div>
102
<div class="content" id="user_${token}">
103
${format(txtMsgs[txtMsgs.length-1].content)}
104
</div>
105
</div>
106
`;
107
108
80
message_box.scrollTop = message_box.scrollHeight;
109
81
window.scrollTo(0, 0);
110
82
await new Promise((r) => setTimeout(r, 500));
@@ -138,14 +110,13 @@ const ask_gpt = async (txtMsgs) => {
138
110
action: `_ask`,
139
111
model: model.options[model.selectedIndex].value,
140
112
jailbreak: jailbreak.options[jailbreak.selectedIndex].value,
113
internet_access: document.getElementById(`switch`).checked,
141
114
provider: provider.options[provider.selectedIndex].value,
142
115
meta: {
143
116
id: window.token,
144
117
content: {
145
conversation: await get_conversation(window.conversation_id),
146
internet_access: document.getElementById(`switch`).checked,
147
118
content_type: `text`,
148
parts: txtMsgs,
119
parts: messages,
149
120
},
150
121
},
151
122
}),
@@ -161,7 +132,7 @@ const ask_gpt = async (txtMsgs) => {
161
132
162
133
text += chunk;
163
134
164
document.getElementById(`gpt_${window.token}`).innerHTML = markdown.render(text).replace("<a href=", '<a target="_blank" href=');
135
document.getElementById(`gpt_${window.token}`).innerHTML = markdown_render(text);
165
136
document.querySelectorAll(`code`).forEach((el) => {
166
137
hljs.highlightElement(el);
167
138
});
@@ -171,10 +142,9 @@ const ask_gpt = async (txtMsgs) => {
171
142
}
172
143
173
144
if (text.includes(`G4F_ERROR`)) {
174
document.getElementById(`gpt_${window.token}`).innerHTML = "An error occured, please try again, if the problem persists, please reload / refresh cache or use a differnet browser";
145
document.getElementById(`gpt_${window.token}`).innerHTML = "An error occured, please try again, if the problem persists, please use a other model or provider";
175
146
}
176
147
177
add_message(window.conversation_id, "user", txtMsgs[txtMsgs.length-1].content);
178
148
add_message(window.conversation_id, "assistant", text);
179
149
180
150
message_box.scrollTop = message_box.scrollHeight;
@@ -185,7 +155,6 @@ const ask_gpt = async (txtMsgs) => {
185
155
window.scrollTo(0, 0);
186
156
187
157
} catch (e) {
188
add_message(window.conversation_id, "user", txtMsgs[txtMsgs.length-1].content);
189
158
190
159
message_box.scrollTop = message_box.scrollHeight;
191
160
await remove_cancel_button();
@@ -210,6 +179,7 @@ const ask_gpt = async (txtMsgs) => {
210
179
211
180
window.scrollTo(0, 0);
212
181
}
182
regenerate.classList.remove(`regenerate-hidden`);
213
183
};
214
184
215
185
const clear_conversations = async () => {
@@ -280,7 +250,6 @@ const set_conversation = async (conversation_id) => {
280
250
};
281
251
282
252
const new_conversation = async () => {
283
284
253
history.pushState({}, null, `/chat/`);
285
254
window.conversation_id = uuid();
286
255
@@ -291,12 +260,9 @@ const new_conversation = async () => {
291
260
};
292
261
293
262
const load_conversation = async (conversation_id) => {
294
let conversation = await JSON.parse(
295
localStorage.getItem(`conversation:${conversation_id}`)
296
);
297
console.log(conversation, conversation_id);
263
let messages = await get_messages(conversation_id);
298
264
299
for (item of conversation.items) {
265
for (item of messages) {
300
266
message_box.innerHTML += `
301
267
<div class="message">
302
268
<div class=${item.role == "assistant" ? "assistant" : "user"}>
@@ -308,7 +274,7 @@ const load_conversation = async (conversation_id) => {
308
274
</div>
309
275
<div class="content">
310
276
${item.role == "assistant"
311
? markdown.render(item.content).replace("<a href=", '<a target="_blank" href=')
277
? markdown_render(item.content)
312
278
: item.content
313
279
}
314
280
</div>
@@ -331,6 +297,11 @@ const get_conversation = async (conversation_id) => {
331
297
let conversation = await JSON.parse(
332
298
localStorage.getItem(`conversation:${conversation_id}`)
333
299
);
300
return conversation;
301
};
302
303
const get_messages = async (conversation_id) => {
304
let conversation = await get_conversation(conversation_id);
334
305
return conversation.items;
335
306
};
336
307
@@ -351,21 +322,32 @@ const add_conversation = async (conversation_id, content) => {
351
322
})
352
323
);
353
324
}
325
326
history.pushState({}, null, `/chat/${conversation_id}`);
354
327
};
355
328
356
const add_message = async (conversation_id, role, content) => {
357
before_adding = JSON.parse(
358
localStorage.getItem(`conversation:${conversation_id}`)
329
const remove_last_message = async (conversation_id) => {
330
const conversation = await get_conversation(conversation_id)
331
332
conversation.items.pop();
333
334
localStorage.setItem(
335
`conversation:${conversation_id}`,
336
JSON.stringify(conversation)
359
337
);
338
};
339
340
const add_message = async (conversation_id, role, content) => {
341
const conversation = await get_conversation(conversation_id);
360
342
361
before_adding.items.push({
343
conversation.items.push({
362
344
role: role,
363
345
content: content,
364
346
});
365
347
366
348
localStorage.setItem(
367
349
`conversation:${conversation_id}`,
368
JSON.stringify(before_adding)
350
JSON.stringify(conversation)
369
351
);
370
352
};
371
353
@@ -404,6 +386,12 @@ document.getElementById(`cancelButton`).addEventListener(`click`, async () => {
404
386
console.log(`aborted ${window.conversation_id}`);
405
387
});
406
388
389
document.getElementById(`regenerateButton`).addEventListener(`click`, async () => {
390
await remove_last_message(window.conversation_id);
391
window.token = message_id();
392
await ask_gpt();
393
});
394
407
395
const uuid = () => {
408
396
return `xxxxxxxx-xxxx-4xxx-yxxx-${Date.now().toString(16)}`.replace(
409
397
/[xy]/g,
@@ -485,17 +473,16 @@ const say_hello = async () => {
485
473
${gpt_image}
486
474
<i class="fa-regular fa-phone-arrow-down-left"></i>
487
475
</div>
488
<div class="content welcome-message">
476
<div class="content">
477
<p class=" welcome-message"></p>
489
478
</div>
490
479
</div>
491
480
`;
492
481
493
content = ``
494
482
to_modify = document.querySelector(`.welcome-message`);
495
483
for (token of tokens) {
496
484
await new Promise(resolve => setTimeout(resolve, (Math.random() * (100 - 200) + 100)))
497
content += token;
498
to_modify.innerHTML = markdown.render(content);
485
to_modify.textContent += token;
499
486
}
500
487
}
501
488
@@ -542,14 +529,12 @@ window.onload = async () => {
542
529
load_conversations(20, 0);
543
530
}, 1);
544
531
545
if (!window.location.href.endsWith(`#`)) {
546
if (/\/chat\/.+/.test(window.location.href)) {
547
await load_conversation(window.conversation_id);
548
}
532
if (/\/chat\/.+/.test(window.location.href)) {
533
await load_conversation(window.conversation_id);
534
} else {
535
await say_hello()
549
536
}
550
551
await say_hello()
552
537
553
538
message_input.addEventListener(`keydown`, async (evt) => {
554
539
if (prompt_lock) return;
555
540
if (evt.keyCode === 13 && !evt.shiftKey) {
@@ -1,14 +1,15 @@
1
1
import g4f
2
2
from g4f.Provider import __providers__
3
3
4
from flask import request
4
import json
5
from flask import request, Flask
5
6
from .internet import get_search_message
6
7
7
8
g4f.debug.logging = True
8
9
9
10
class Backend_Api:
10
def __init__(self, app) -> None:
11
self.app = app
11
def __init__(self, app: Flask) -> None:
12
self.app: Flask = app
12
13
self.routes = {
13
14
'/backend-api/v2/models': {
14
15
'function': self.models,
@@ -61,19 +62,18 @@ class Backend_Api:
61
62
}
62
63
63
64
def _conversation(self):
64
try:
65
#jailbreak = request.json['jailbreak']
66
web_search = request.json['meta']['content']['internet_access']
67
messages = request.json['meta']['content']['parts']
68
if web_search:
69
messages[-1]["content"] = get_search_message(messages[-1]["content"])
70
model = request.json.get('model')
71
model = model if model else g4f.models.default
72
provider = request.json.get('provider').replace('g4f.Provider.', '')
73
provider = provider if provider and provider != "Auto" else None
74
if provider != None:
75
provider = g4f.Provider.ProviderUtils.convert.get(provider)
76
65
#jailbreak = request.json['jailbreak']
66
messages = request.json['meta']['content']['parts']
67
if request.json['internet_access']:
68
messages[-1]["content"] = get_search_message(messages[-1]["content"])
69
model = request.json.get('model')
70
model = model if model else g4f.models.default
71
provider = request.json.get('provider').replace('g4f.Provider.', '')
72
provider = provider if provider and provider != "Auto" else None
73
if provider != None:
74
provider = g4f.Provider.ProviderUtils.convert.get(provider)
75
76
def try_response():
77
77
response = g4f.ChatCompletion.create(
78
78
model=model,
79
79
provider=provider,
@@ -81,13 +81,15 @@ class Backend_Api:
81
81
stream=True,
82
82
ignore_stream_and_auth=True
83
83
)
84
try:
85
yield from response
86
except Exception as e:
87
print(e)
88
yield json.dumps({
89
'code' : 'G4F_ERROR',
90
'_action': '_ask',
91
'success': False,
92
'error' : f'an error occurred {str(e)}'
93
})
84
94
85
return self.app.response_class(response, mimetype='text/event-stream')
86
87
except Exception as e:
88
print(e)
89
return {
90
'code' : 'G4F_ERROR',
91
'_action': '_ask',
92
'success': False,
93
'error' : f'an error occurred {str(e)}'}, 400
95
return self.app.response_class(try_response(), mimetype='text/event-stream')