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

XFEstudio/gpt4free

Ignore empty auth header in api Add live token count in gui

3b96c27e
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

4 个文件 +106 -49
Modified g4f/api/__init__.py +3 -1
@@ -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
Modified g4f/gui/client/css/style.css +24 -6
@@ -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;
Modified g4f/gui/client/html/index.html +17 -13
@@ -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 &nbsp;
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">
Modified g4f/gui/client/js/chat.v1.js +62 -29
@@ -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 = "&nbsp;"
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')