返回提交历史
Modified
g4f/api/__init__.py
+3
-1
Modified
g4f/gui/client/css/style.css
+24
-6
Modified
g4f/gui/client/html/index.html
+17
-13
Modified
g4f/gui/client/js/chat.v1.js
+62
-29
XFEstudio/gpt4free
Ignore empty auth header in api Add live token count in gui
3b96c27e
代码差异
4 个文件
+106
-49
@@ -85,7 +85,9 @@ class Api:
85
85
if config.api_key is None and request is not None:
86
86
auth_header = request.headers.get("Authorization")
87
87
if auth_header is not None:
88
config.api_key = auth_header.split(None, 1)[-1]
88
auth_header = auth_header.split(None, 1)[-1]
89
if auth_header and auth_header != "Bearer":
90
config.api_key = auth_header
89
91
response = self.client.chat.completions.create(
90
92
**config.dict(exclude_none=True),
91
93
ignored=self.list_ignored_providers
@@ -119,7 +119,7 @@ body {
119
119
width: 100%;
120
120
display: flex;
121
121
flex-direction: column;
122
gap: 15px;
122
gap: 5px;
123
123
}
124
124
125
125
.conversation #messages {
@@ -129,11 +129,12 @@ body {
129
129
flex-direction: column;
130
130
overflow: auto;
131
131
overflow-wrap: break-word;
132
padding-bottom: 20px;
132
padding-bottom: 10px;
133
133
}
134
134
135
135
.conversation .user-input {
136
136
max-height: 200px;
137
margin-bottom: 10px;
137
138
}
138
139
139
140
.conversation .user-input input {
@@ -385,12 +386,29 @@ body {
385
386
font-size: 14px;
386
387
}
387
388
389
.toolbar {
390
position: relative;
391
}
392
393
#input-count {
394
width: fit-content;
395
font-size: 12px;
396
padding: 6px 15px;
397
}
398
388
399
.stop_generating, .regenerate {
389
400
position: absolute;
390
bottom: 122px;
391
left: 50%;
392
transform: translateX(-50%);
393
401
z-index: 1000000;
402
top: 0;
403
right: 0;
404
}
405
406
@media only screen and (min-width: 40em) {
407
.stop_generating, .regenerate {
408
left: 50%;
409
transform: translateX(-50%);
410
right: auto;
411
}
394
412
}
395
413
396
414
.stop_generating button, .regenerate button{
@@ -399,7 +417,7 @@ body {
399
417
background-color: var(--blur-bg);
400
418
border-radius: var(--border-radius-1);
401
419
border: 1px solid var(--blur-border);
402
padding: 10px 15px;
420
padding: 5px 15px;
403
421
color: var(--colour-3);
404
422
display: flex;
405
423
justify-content: center;
@@ -112,19 +112,23 @@
112
112
</div>
113
113
</div>
114
114
<div class="conversation">
115
<div class="stop_generating stop_generating-hidden">
116
<button id="cancelButton">
117
<span>Stop Generating</span>
118
<i class="fa-regular fa-stop"></i>
119
</button>
120
</div>
121
<div class="regenerate regenerate-hidden">
122
<button id="regenerateButton">
123
<span>Regenerate</span>
124
<i class="fa-solid fa-rotate"></i>
125
</button>
126
</div>
127
<div class="box" id="messages">
115
<div id="messages" class="box"></div>
116
<div class="toolbar">
117
<div id="input-count" class="">
118
119
</div>
120
<div class="stop_generating stop_generating-hidden">
121
<button id="cancelButton">
122
<span>Stop Generating</span>
123
<i class="fa-regular fa-stop"></i>
124
</button>
125
</div>
126
<div class="regenerate regenerate-hidden">
127
<button id="regenerateButton">
128
<span>Regenerate</span>
129
<i class="fa-solid fa-rotate"></i>
130
</button>
131
</div>
128
132
</div>
129
133
<div class="user-input">
130
134
<div class="box input-box">
@@ -5,10 +5,12 @@ const message_input = document.getElementById(`message-input`);
5
5
const box_conversations = document.querySelector(`.top`);
6
6
const stop_generating = document.querySelector(`.stop_generating`);
7
7
const regenerate = document.querySelector(`.regenerate`);
8
const send_button = document.querySelector(`#send-button`);
9
const imageInput = document.querySelector('#image');
10
const cameraInput = document.querySelector('#camera');
11
const fileInput = document.querySelector('#file');
8
const send_button = document.getElementById("send-button");
9
const imageInput = document.getElementById("image");
10
const cameraInput = document.getElementById("camera");
11
const fileInput = document.getElementById("file");
12
const inputCount = document.getElementById("input-count")
13
const modelSelect = document.getElementById("model");
12
14
13
15
let prompt_lock = false;
14
16
@@ -75,6 +77,7 @@ const handle_ask = async () => {
75
77
if (message.length > 0) {
76
78
message_input.value = '';
77
79
prompt_lock = true;
80
count_input()
78
81
await add_conversation(window.conversation_id, message);
79
82
if ("text" in fileInput.dataset) {
80
83
message += '\n```' + fileInput.dataset.type + '\n';
@@ -89,6 +92,7 @@ const handle_ask = async () => {
89
92
if (input.files.length > 0) imageInput.dataset.src = URL.createObjectURL(input.files[0]);
90
93
else delete imageInput.dataset.src
91
94
95
model = modelSelect.options[modelSelect.selectedIndex].value
92
96
message_box.innerHTML += `
93
97
<div class="message" data-index="${message_index}">
94
98
<div class="user">
@@ -97,11 +101,14 @@ const handle_ask = async () => {
97
101
<i class="fa-regular fa-phone-arrow-up-right"></i>
98
102
</div>
99
103
<div class="content" id="user_${token}">
104
<div class="content_inner">
100
105
${markdown_render(message)}
101
106
${imageInput.dataset.src
102
107
? '<img src="' + imageInput.dataset.src + '" alt="Image upload">'
103
108
: ''
104
109
}
110
</div>
111
<div class="count">${count_words_and_tokens(message, model)}</div>
105
112
</div>
106
113
</div>
107
114
`;
@@ -120,19 +127,25 @@ const remove_cancel_button = async () => {
120
127
}, 300);
121
128
};
122
129
123
const filter_messages = (messages) => {
130
const filter_messages = (messages, filter_last_message = true) => {
124
131
// Removes none user messages at end
125
let last_message;
126
while (last_message = messages.pop()) {
127
if (last_message["role"] == "user") {
128
messages.push(last_message);
129
break;
132
if (filter_last_message) {
133
let last_message;
134
while (last_message = messages.pop()) {
135
if (last_message["role"] == "user") {
136
messages.push(last_message);
137
break;
138
}
130
139
}
131
140
}
132
141
133
142
// Remove history, if it is selected
134
143
if (document.getElementById('history')?.checked) {
135
messages = [messages[messages.length-1]];
144
if (filter_last_message) {
145
messages = [messages.pop()];
146
} else {
147
messages = [messages.pop(), messages.pop()];
148
}
136
149
}
137
150
138
151
let new_messages = [];
@@ -165,7 +178,6 @@ const ask_gpt = async () => {
165
178
166
179
jailbreak = document.getElementById("jailbreak");
167
180
provider = document.getElementById("provider");
168
model = document.getElementById("model");
169
181
window.text = '';
170
182
171
183
stop_generating.classList.remove(`stop_generating-hidden`);
@@ -188,11 +200,13 @@ const ask_gpt = async () => {
188
200
<div class="content" id="gpt_${window.token}">
189
201
<div class="provider"></div>
190
202
<div class="content_inner"><span id="cursor"></span></div>
203
<div class="count"></div>
191
204
</div>
192
205
</div>
193
206
`;
194
207
content = document.getElementById(`gpt_${window.token}`);
195
208
content_inner = content.querySelector('.content_inner');
209
content_count = content.querySelector('.count');
196
210
197
211
message_box.scrollTop = message_box.scrollHeight;
198
212
window.scrollTo(0, 0);
@@ -200,7 +214,7 @@ const ask_gpt = async () => {
200
214
let body = JSON.stringify({
201
215
id: window.token,
202
216
conversation_id: window.conversation_id,
203
model: model.options[model.selectedIndex].value,
217
model: modelSelect.options[modelSelect.selectedIndex].value,
204
218
jailbreak: jailbreak.options[jailbreak.selectedIndex].value,
205
219
web_search: document.getElementById(`switch`).checked,
206
220
provider: provider.options[provider.selectedIndex].value,
@@ -270,6 +284,7 @@ const ask_gpt = async () => {
270
284
html = html.substring(0, lastIndex) + '<span id="cursor"></span>' + lastElement;
271
285
}
272
286
content_inner.innerHTML = html;
287
content_count.innerText = count_words_and_tokens(text, provider?.model);
273
288
highlight(content_inner);
274
289
}
275
290
@@ -397,13 +412,13 @@ const load_conversation = async (conversation_id) => {
397
412
let messages = await get_messages(conversation_id);
398
413
399
414
let elements = "";
415
let last_model = null;
400
416
for (i in messages) {
401
417
let item = messages[i];
418
last_model = item?.provider?.model;
402
419
let next_i = parseInt(i) + 1;
403
420
let next_provider = item.provider ? item.provider : (messages.length > next_i ? messages[next_i].provider : null);
404
let tokens_count = next_provider?.model ? count_tokens(next_provider.model, item.content) : "";
405
let append_count = tokens_count ? `, ${tokens_count} tokens` : "";
406
let words_count = `(${count_words(item.content)} words${append_count})`
421
407
422
let provider_link = item?.provider?.name ? `<a href="${item?.provider?.url}" target="_blank">${item.provider.name}</a>` : "";
408
423
let provider = provider_link ? `
409
424
<div class="provider">
@@ -424,17 +439,18 @@ const load_conversation = async (conversation_id) => {
424
439
<div class="content">
425
440
${provider}
426
441
<div class="content_inner">${markdown_render(item.content)}</div>
427
<div class="count">${words_count}</div>
442
<div class="count">${count_words_and_tokens(item.content, next_provider?.model)}</div>
428
443
</div>
429
444
</div>
430
445
`;
431
446
}
432
447
433
const filtered = filter_messages(messages);
448
const filtered = filter_messages(messages, false);
434
449
if (filtered.length > 0) {
435
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, "gpt-3.5-turbo").length
450
last_model = last_model?.startsWith("gpt-4") ? "gpt-4" : "gpt-3.5-turbo"
451
let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
436
452
if (count_total > 0) {
437
elements += `<div class="count_total">(${count_total} tokens used)</div>`;
453
elements += `<div class="count_total">(${count_total} tokens total)</div>`;
438
454
}
439
455
}
440
456
@@ -467,6 +483,11 @@ function count_tokens(model, text) {
467
483
}
468
484
}
469
485
486
function count_words_and_tokens(text, model) {
487
const tokens_count = model ? `, ${count_tokens(model, text)} tokens` : "";
488
return `(${count_words(text)} words${tokens_count})`
489
}
490
470
491
const get_conversation = async (conversation_id) => {
471
492
let conversation = await JSON.parse(
472
493
localStorage.getItem(`conversation:${conversation_id}`)
@@ -703,6 +724,16 @@ colorThemes.forEach((themeOption) => {
703
724
});
704
725
});
705
726
727
const count_input = async () => {
728
if (message_input.value) {
729
model = modelSelect.options[modelSelect.selectedIndex].value;
730
inputCount.innerText = count_words_and_tokens(message_input.value, model);
731
} else {
732
inputCount.innerHTML = " "
733
}
734
};
735
message_input.addEventListener("keyup", count_input);
736
706
737
window.onload = async () => {
707
738
setTheme();
708
739
@@ -713,18 +744,21 @@ window.onload = async () => {
713
744
}
714
745
}
715
746
716
await setTimeout(() => {
717
load_conversations();
718
}, 1);
747
count_input();
719
748
720
749
if (/\/chat\/.+/.test(window.location.href)) {
721
await load_conversation(window.conversation_id);
750
load_conversation(window.conversation_id);
722
751
} else {
723
await say_hello()
752
say_hello()
724
753
}
725
726
message_input.addEventListener(`keydown`, async (evt) => {
754
755
setTimeout(() => {
756
load_conversations();
757
}, 1);
758
759
message_input.addEventListener("keydown", async (evt) => {
727
760
if (prompt_lock) return;
761
728
762
if (evt.keyCode === 13 && !evt.shiftKey) {
729
763
evt.preventDefault();
730
764
console.log("pressed enter");
@@ -768,12 +802,11 @@ observer.observe(message_input, { attributes: true });
768
802
(async () => {
769
803
response = await fetch('/backend-api/v2/models')
770
804
models = await response.json()
771
let select = document.getElementById('model');
772
805
773
806
for (model of models) {
774
807
let option = document.createElement('option');
775
808
option.value = option.text = model;
776
select.appendChild(option);
809
modelSelect.appendChild(option);
777
810
}
778
811
779
812
response = await fetch('/backend-api/v2/providers')