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

XFEstudio/gpt4free

Add system message input to gui Improve OpenaiChat provider

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

代码差异

4 个文件 +134 -103
Modified g4f/Provider/needs_auth/OpenaiChat.py +45 -43
@@ -10,11 +10,10 @@ from aiohttp import ClientWebSocketResponse
10 10
11 11 try:
12 12 from py_arkose_generator.arkose import get_values_for_request
13 from async_property import async_cached_property
14 has_requirements = True
13 has_arkose_generator = True
15 14 except ImportError:
16 async_cached_property = property
17 has_requirements = False
15 has_arkose_generator = False
16
18 17 try:
19 18 from selenium.webdriver.common.by import By
20 19 from selenium.webdriver.support.ui import WebDriverWait
@@ -34,7 +33,7 @@ from ... import debug
34 33
35 34 class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
36 35 """A class for creating and managing conversations with OpenAI chat service"""
37
36
38 37 url = "https://chat.openai.com"
39 38 working = True
40 39 needs_auth = True
@@ -81,7 +80,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
81 80 A Response object that contains the generator, action, messages, and options
82 81 """
83 82 # Add the user input to the messages list
84 if prompt:
83 if prompt is not None:
85 84 messages.append({
86 85 "role": "user",
87 86 "content": prompt
@@ -103,7 +102,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
103 102 messages,
104 103 kwargs
105 104 )
106
105
107 106 @classmethod
108 107 async def upload_image(
109 108 cls,
@@ -163,7 +162,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
163 162 response.raise_for_status()
164 163 image_data["download_url"] = (await response.json())["download_url"]
165 164 return ImageRequest(image_data)
166
165
167 166 @classmethod
168 167 async def get_default_model(cls, session: StreamSession, headers: dict):
169 168 """
@@ -186,7 +185,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
186 185 return cls.default_model
187 186 raise RuntimeError(f"Response: {data}")
188 187 return cls.default_model
189
188
190 189 @classmethod
191 190 def create_messages(cls, messages: Messages, image_request: ImageRequest = None):
192 191 """
@@ -335,9 +334,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
335 334 Raises:
336 335 RuntimeError: If an error occurs during processing.
337 336 """
338 if not has_requirements:
339 raise MissingRequirementsError('Install "py-arkose-generator" and "async_property" package')
340 if not parent_id:
337 if parent_id is None:
341 338 parent_id = str(uuid.uuid4())
342 339
343 340 # Read api_key from arguments
@@ -349,7 +346,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
349 346 timeout=timeout
350 347 ) as session:
351 348 # Read api_key and cookies from cache / browser config
352 if cls._headers is None or time.time() > cls._expires:
349 if cls._headers is None or cls._expires is None or time.time() > cls._expires:
353 350 if api_key is None:
354 351 # Read api_key from cookies
355 352 cookies = get_cookies("chat.openai.com", False) if cookies is None else cookies
@@ -358,8 +355,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
358 355 else:
359 356 api_key = cls._api_key if api_key is None else api_key
360 357 # Read api_key with session cookies
361 if api_key is None and cookies:
362 api_key = await cls.fetch_access_token(session, cls._headers)
358 #if api_key is None and cookies:
359 # api_key = await cls.fetch_access_token(session, cls._headers)
363 360 # Load default model
364 361 if cls.default_model is None and api_key is not None:
365 362 try:
@@ -385,6 +382,19 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
385 382 else:
386 383 cls._set_api_key(api_key)
387 384
385 async with session.post(
386 f"{cls.url}/backend-api/sentinel/chat-requirements",
387 json={"conversation_mode_kind": "primary_assistant"},
388 headers=cls._headers
389 ) as response:
390 response.raise_for_status()
391 data = await response.json()
392 need_arkose = data["arkose"]["required"]
393 chat_token = data["token"]
394
395 if need_arkose and not has_arkose_generator:
396 raise MissingRequirementsError('Install "py-arkose-generator" package')
397
388 398 try:
389 399 image_request = await cls.upload_image(session, cls._headers, image, image_name) if image else None
390 400 except Exception as e:
@@ -395,12 +405,10 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
395 405 model = cls.get_model(model).replace("gpt-3.5-turbo", "text-davinci-002-render-sha")
396 406 fields = ResponseFields()
397 407 while fields.finish_reason is None:
398 arkose_token = await cls.get_arkose_token(session)
399 408 conversation_id = conversation_id if fields.conversation_id is None else fields.conversation_id
400 409 parent_id = parent_id if fields.message_id is None else fields.message_id
401 410 data = {
402 411 "action": action,
403 "arkose_token": arkose_token,
404 412 "conversation_mode": {"kind": "primary_assistant"},
405 413 "force_paragen": False,
406 414 "force_rate_limit": False,
@@ -418,7 +426,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
418 426 json=data,
419 427 headers={
420 428 "Accept": "text/event-stream",
421 "OpenAI-Sentinel-Arkose-Token": arkose_token,
429 **({"OpenAI-Sentinel-Arkose-Token": await cls.get_arkose_token(session)} if need_arkose else {}),
430 "OpenAI-Sentinel-Chat-Requirements-Token": chat_token,
422 431 **cls._headers
423 432 }
424 433 ) as response:
@@ -471,6 +480,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
471 480 if not line.startswith(b"data: "):
472 481 return
473 482 elif line.startswith(b"data: [DONE]"):
483 if fields.finish_reason is None:
484 fields.finish_reason = "error"
474 485 return
475 486 try:
476 487 line = json.loads(line[6:])
@@ -600,16 +611,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
600 611 def _update_cookie_header(cls):
601 612 cls._headers["Cookie"] = cls._format_cookies(cls._cookies)
602 613
603 class EndTurn:
604 """
605 Class to represent the end of a conversation turn.
606 """
607 def __init__(self):
608 self.is_end = False
609
610 def end(self):
611 self.is_end = True
612
613 614 class ResponseFields:
614 615 """
615 616 Class to encapsulate response fields.
@@ -638,8 +639,8 @@ class Response():
638 639 self._options = options
639 640 self._fields = None
640 641
641 async def generator(self):
642 if self._generator:
642 async def generator(self) -> AsyncIterator:
643 if self._generator is not None:
643 644 self._generator = None
644 645 chunks = []
645 646 async for chunk in self._generator:
@@ -649,27 +650,29 @@ class Response():
649 650 yield chunk
650 651 chunks.append(str(chunk))
651 652 self._message = "".join(chunks)
652 if not self._fields:
653 if self._fields is None:
653 654 raise RuntimeError("Missing response fields")
654 self.is_end = self._fields.end_turn
655 self.is_end = self._fields.finish_reason == "stop"
655 656
656 657 def __aiter__(self):
657 658 return self.generator()
658 659
659 @async_cached_property
660 async def message(self) -> str:
660 async def get_message(self) -> str:
661 661 await self.generator()
662 662 return self._message
663 663
664 async def get_fields(self):
664 async def get_fields(self) -> dict:
665 665 await self.generator()
666 return {"conversation_id": self._fields.conversation_id, "parent_id": self._fields.message_id}
666 return {
667 "conversation_id": self._fields.conversation_id,
668 "parent_id": self._fields.message_id
669 }
667 670
668 async def next(self, prompt: str, **kwargs) -> Response:
671 async def create_next(self, prompt: str, **kwargs) -> Response:
669 672 return await OpenaiChat.create(
670 673 **self._options,
671 674 prompt=prompt,
672 messages=await self.messages,
675 messages=await self.get_messages(),
673 676 action="next",
674 677 **await self.get_fields(),
675 678 **kwargs
@@ -681,13 +684,13 @@ class Response():
681 684 raise RuntimeError("Can't continue message. Message already finished.")
682 685 return await OpenaiChat.create(
683 686 **self._options,
684 messages=await self.messages,
687 messages=await self.get_messages(),
685 688 action="continue",
686 689 **fields,
687 690 **kwargs
688 691 )
689 692
690 async def variant(self, **kwargs) -> Response:
693 async def create_variant(self, **kwargs) -> Response:
691 694 if self.action != "next":
692 695 raise RuntimeError("Can't create variant from continue or variant request.")
693 696 return await OpenaiChat.create(
@@ -698,8 +701,7 @@ class Response():
698 701 **kwargs
699 702 )
700 703
701 @async_cached_property
702 async def messages(self):
704 async def get_messages(self) -> list:
703 705 messages = self._messages
704 messages.append({"role": "assistant", "content": await self.message})
706 messages.append({"role": "assistant", "content": await self.message()})
705 707 return messages
Modified g4f/gui/client/css/style.css +16 -5
@@ -65,6 +65,7 @@
65 65 :root {
66 66 --font-1: "Inter", sans-serif;
67 67 --section-gap: 25px;
68 --inner-gap: 15px;
68 69 --border-radius-1: 8px;
69 70 }
70 71
@@ -222,7 +223,7 @@ body {
222 223 overflow-wrap: break-word;
223 224 display: flex;
224 225 gap: var(--section-gap);
225 padding: var(--section-gap);
226 padding: var(--inner-gap) var(--section-gap);
226 227 padding-bottom: 0;
227 228 }
228 229
@@ -393,7 +394,7 @@ body {
393 394 #input-count {
394 395 width: fit-content;
395 396 font-size: 12px;
396 padding: 6px 15px;
397 padding: 6px var(--inner-gap);
397 398 }
398 399
399 400 .stop_generating, .regenerate {
@@ -417,7 +418,7 @@ body {
417 418 background-color: var(--blur-bg);
418 419 border-radius: var(--border-radius-1);
419 420 border: 1px solid var(--blur-border);
420 padding: 5px 15px;
421 padding: 5px var(--inner-gap);
421 422 color: var(--colour-3);
422 423 display: flex;
423 424 justify-content: center;
@@ -601,7 +602,7 @@ select {
601 602 .input-box {
602 603 display: flex;
603 604 align-items: center;
604 padding-right: 15px;
605 padding-right: var(--inner-gap);
605 606 cursor: pointer;
606 607 }
607 608
@@ -785,7 +786,7 @@ a:-webkit-any-link {
785 786 font-size: 15px;
786 787 width: 100%;
787 788 height: 100%;
788 padding: 12px 15px;
789 padding: 12px var(--inner-gap);
789 790 background: none;
790 791 border: none;
791 792 outline: none;
@@ -997,3 +998,13 @@ a:-webkit-any-link {
997 998 #send-button:hover {
998 999 border: 1px solid #e4d4ffc9;
999 1000 }
1001
1002 #systemPrompt {
1003 font-size: 15px;
1004 width: 100%;
1005 color: var(--colour-3);
1006 height: 50px;
1007 outline: none;
1008 padding: var(--inner-gap) var(--section-gap);
1009 resize: vertical;
1010 }
Modified g4f/gui/client/html/index.html +1 -0
@@ -116,6 +116,7 @@
116 116 </div>
117 117 </div>
118 118 <div class="conversation">
119 <textarea id="systemPrompt" class="box" placeholder="System prompt"></textarea>
119 120 <div id="messages" class="box"></div>
120 121 <div class="toolbar">
121 122 <div id="input-count" class="">
Modified g4f/gui/client/js/chat.v1.js +72 -55
@@ -13,6 +13,7 @@ const cameraInput = document.getElementById("camera");
13 13 const fileInput = document.getElementById("file");
14 14 const inputCount = document.getElementById("input-count")
15 15 const modelSelect = document.getElementById("model");
16 const systemPrompt = document.getElementById("systemPrompt")
16 17
17 18 let prompt_lock = false;
18 19
@@ -135,7 +136,7 @@ const remove_cancel_button = async () => {
135 136 }, 300);
136 137 };
137 138
138 const filter_messages = (messages, filter_last_message = true) => {
139 const prepare_messages = (messages, filter_last_message = true) => {
139 140 // Removes none user messages at end
140 141 if (filter_last_message) {
141 142 let last_message;
@@ -147,7 +148,7 @@ const filter_messages = (messages, filter_last_message = true) => {
147 148 }
148 149 }
149 150
150 // Remove history, if it is selected
151 // Remove history, if it's selected
151 152 if (document.getElementById('history')?.checked) {
152 153 if (filter_last_message) {
153 154 messages = [messages.pop()];
@@ -160,7 +161,7 @@ const filter_messages = (messages, filter_last_message = true) => {
160 161 for (i in messages) {
161 162 new_message = messages[i];
162 163 // Remove generated images from history
163 new_message["content"] = new_message["content"].replaceAll(
164 new_message.content = new_message.content.replaceAll(
164 165 /<!-- generated images start -->[\s\S]+<!-- generated images end -->/gm,
165 166 ""
166 167 )
@@ -171,6 +172,15 @@ const filter_messages = (messages, filter_last_message = true) => {
171 172 }
172 173 }
173 174
175 // Add system message
176 system_content = systemPrompt?.value;
177 if (system_content) {
178 new_messages.unshift({
179 "role": "system",
180 "content": system_content
181 });
182 }
183
174 184 return new_messages;
175 185 }
176 186
@@ -179,7 +189,7 @@ const ask_gpt = async () => {
179 189 messages = await get_messages(window.conversation_id);
180 190 total_messages = messages.length;
181 191
182 messages = filter_messages(messages);
192 messages = prepare_messages(messages);
183 193
184 194 window.scrollTo(0, 0);
185 195 window.controller = new AbortController();
@@ -192,8 +202,6 @@ const ask_gpt = async () => {
192 202
193 203 message_box.scrollTop = message_box.scrollHeight;
194 204 window.scrollTo(0, 0);
195 await new Promise((r) => setTimeout(r, 500));
196 window.scrollTo(0, 0);
197 205
198 206 el = message_box.querySelector('.count_total');
199 207 el ? el.parentElement.removeChild(el) : null;
@@ -218,6 +226,8 @@ const ask_gpt = async () => {
218 226
219 227 message_box.scrollTop = message_box.scrollHeight;
220 228 window.scrollTo(0, 0);
229
230 error = provider_result = null;
221 231 try {
222 232 let body = JSON.stringify({
223 233 id: window.token,
@@ -241,18 +251,14 @@ const ask_gpt = async () => {
241 251 } else {
242 252 headers['content-type'] = 'application/json';
243 253 }
254
244 255 const response = await fetch(`/backend-api/v2/conversation`, {
245 256 method: 'POST',
246 257 signal: window.controller.signal,
247 258 headers: headers,
248 259 body: body
249 260 });
250
251 await new Promise((r) => setTimeout(r, 1000));
252 window.scrollTo(0, 0);
253
254 261 const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
255 error = provider = null;
256 262 while (true) {
257 263 const { value, done } = await reader.read();
258 264 if (done) break;
@@ -262,12 +268,12 @@ const ask_gpt = async () => {
262 268 if (message.type == "content") {
263 269 text += message.content;
264 270 } else if (message["type"] == "provider") {
265 provider = message.provider
271 provider_result = message.provider
266 272 content.querySelector('.provider').innerHTML = `
267 <a href="${provider.url}" target="_blank">
268 ${provider.name}
273 <a href="${provider_result.url}" target="_blank">
274 ${provider_result.name}
269 275 </a>
270 ${provider.model ? ' with ' + provider.model : ''}
276 ${provider_result.model ? ' with ' + provider_result.model : ''}
271 277 `
272 278 } else if (message["type"] == "error") {
273 279 error = message["error"];
@@ -292,7 +298,7 @@ const ask_gpt = async () => {
292 298 html = html.substring(0, lastIndex) + '<span id="cursor"></span>' + lastElement;
293 299 }
294 300 content_inner.innerHTML = html;
295 content_count.innerText = count_words_and_tokens(text, provider?.model);
301 content_count.innerText = count_words_and_tokens(text, provider_result?.model);
296 302 highlight(content_inner);
297 303 }
298 304
@@ -324,19 +330,19 @@ const ask_gpt = async () => {
324 330 }
325 331 }
326 332 if (!error) {
327 await add_message(window.conversation_id, "assistant", text, provider);
333 await add_message(window.conversation_id, "assistant", text, provider_result);
328 334 await load_conversation(window.conversation_id);
329 335 } else {
330 336 let cursorDiv = document.getElementById(`cursor`);
331 337 if (cursorDiv) cursorDiv.parentNode.removeChild(cursorDiv);
332 338 }
339 window.scrollTo(0, 0);
333 340 message_box.scrollTop = message_box.scrollHeight;
334 341 await remove_cancel_button();
335 342 await register_remove_message();
336 343 prompt_lock = false;
337 window.scrollTo(0, 0);
338 344 await load_conversations();
339 regenerate.classList.remove(`regenerate-hidden`);
345 regenerate.classList.remove("regenerate-hidden");
340 346 };
341 347
342 348 const clear_conversations = async () => {
@@ -362,6 +368,10 @@ const clear_conversation = async () => {
362 368 while (messages.length > 0) {
363 369 message_box.removeChild(messages[0]);
364 370 }
371
372 if (systemPrompt) {
373 systemPrompt.value = "";
374 }
365 375 };
366 376
367 377 const show_option = async (conversation_id) => {
@@ -418,17 +428,22 @@ const new_conversation = async () => {
418 428 };
419 429
420 430 const load_conversation = async (conversation_id) => {
421 let messages = await get_messages(conversation_id);
431 let conversation = await get_conversation(conversation_id);
432 let messages = conversation?.items || [];
433
434 if (systemPrompt) {
435 systemPrompt.value = conversation.system || "";
436 }
422 437
423 438 let elements = "";
424 439 let last_model = null;
425 440 for (i in messages) {
426 441 let item = messages[i];
427 last_model = item?.provider?.model;
442 last_model = item.provider?.model;
428 443 let next_i = parseInt(i) + 1;
429 444 let next_provider = item.provider ? item.provider : (messages.length > next_i ? messages[next_i].provider : null);
430 445
431 let provider_link = item.provider?.name ? `<a href="${item.provider?.url}" target="_blank">${item.provider.name}</a>` : "";
446 let provider_link = item.provider?.name ? `<a href="${item.provider.url}" target="_blank">${item.provider.name}</a>` : "";
432 447 let provider = provider_link ? `
433 448 <div class="provider">
434 449 ${provider_link}
@@ -454,7 +469,7 @@ const load_conversation = async (conversation_id) => {
454 469 `;
455 470 }
456 471
457 const filtered = filter_messages(messages, false);
472 const filtered = prepare_messages(messages, false);
458 473 if (filtered.length > 0) {
459 474 last_model = last_model?.startsWith("gpt-4") ? "gpt-4" : "gpt-3.5-turbo"
460 475 let count_total = GPTTokenizer_cl100k_base?.encodeChat(filtered, last_model).length
@@ -493,19 +508,26 @@ function count_words_and_tokens(text, model) {
493 508 return countWords ? `(${countWords(text)} words${tokens_append})` : "";
494 509 }
495 510
496 const get_conversation = async (conversation_id) => {
511 async function get_conversation(conversation_id) {
497 512 let conversation = await JSON.parse(
498 513 localStorage.getItem(`conversation:${conversation_id}`)
499 514 );
500 515 return conversation;
501 };
516 }
517
518 async function save_conversation(conversation_id, conversation) {
519 localStorage.setItem(
520 `conversation:${conversation_id}`,
521 JSON.stringify(conversation)
522 );
523 }
502 524
503 const get_messages = async (conversation_id) => {
525 async function get_messages(conversation_id) {
504 526 let conversation = await get_conversation(conversation_id);
505 527 return conversation?.items || [];
506 };
528 }
507 529
508 const add_conversation = async (conversation_id, content) => {
530 async function add_conversation(conversation_id, content) {
509 531 if (content.length > 17) {
510 532 title = content.substring(0, 17) + '...'
511 533 } else {
@@ -513,18 +535,23 @@ const add_conversation = async (conversation_id, content) => {
513 535 }
514 536
515 537 if (localStorage.getItem(`conversation:${conversation_id}`) == null) {
516 localStorage.setItem(
517 `conversation:${conversation_id}`,
518 JSON.stringify({
519 id: conversation_id,
520 title: title,
521 items: [],
522 })
523 );
538 await save_conversation(conversation_id, {
539 id: conversation_id,
540 title: title,
541 system: systemPrompt?.value,
542 items: [],
543 });
524 544 }
525 545
526 546 history.pushState({}, null, `/chat/${conversation_id}`);
527 };
547 }
548
549 async function save_system_message() {
550 if (!window.conversation_id) return;
551 const conversation = await get_conversation(window.conversation_id);
552 conversation.system = systemPrompt?.value;
553 await save_conversation(window.conversation_id, conversation);
554 }
528 555
529 556 const hide_last_message = async (conversation_id) => {
530 557 const conversation = await get_conversation(conversation_id)
@@ -533,11 +560,7 @@ const hide_last_message = async (conversation_id) => {
533 560 last_message["regenerate"] = true;
534 561 }
535 562 conversation.items.push(last_message);
536
537 localStorage.setItem(
538 `conversation:${conversation_id}`,
539 JSON.stringify(conversation)
540 );
563 await save_conversation(conversation_id, conversation);
541 564 };
542 565
543 566 const remove_message = async (conversation_id, index) => {
@@ -552,10 +575,7 @@ const remove_message = async (conversation_id, index) => {
552 575 }
553 576 }
554 577 conversation.items = new_items;
555 localStorage.setItem(
556 `conversation:${conversation_id}`,
557 JSON.stringify(conversation)
558 );
578 await save_conversation(conversation_id, conversation);
559 579 };
560 580
561 581 const add_message = async (conversation_id, role, content, provider) => {
@@ -566,12 +586,7 @@ const add_message = async (conversation_id, role, content, provider) => {
566 586 content: content,
567 587 provider: provider
568 588 });
569
570 localStorage.setItem(
571 `conversation:${conversation_id}`,
572 JSON.stringify(conversation)
573 );
574
589 await save_conversation(conversation_id, conversation);
575 590 return conversation.items.length - 1;
576 591 };
577 592
@@ -754,9 +769,7 @@ window.onload = async () => {
754 769 say_hello()
755 770 }
756 771
757 setTimeout(() => {
758 load_conversations();
759 }, 1);
772 load_conversations();
760 773
761 774 message_input.addEventListener("keydown", async (evt) => {
762 775 if (prompt_lock) return;
@@ -875,4 +888,8 @@ fileInput.addEventListener('change', async (event) => {
875 888 } else {
876 889 delete fileInput.dataset.text;
877 890 }
891 });
892
893 systemPrompt?.addEventListener("blur", async () => {
894 await save_system_message();
878 895 });