Advertisement
Amiminoru

Biba fix

Aug 18th, 2023
1,004
0
Never
2
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 23.09 KB | None | 0 0
  1. from EdgeGPT.EdgeGPT import Chatbot
  2. from aiohttp import web
  3. import time
  4. import random
  5. import string
  6. import json
  7. import re
  8. import sys
  9. import tiktoken
  10. import config
  11. import requests
  12. import aiohttp
  13. from urllib.parse import urlparse
  14.  
  15. PORT = config.PORT
  16. HOST = config.HOST
  17.  
  18. CONCATENATE_RESPONSES = config.CONCATENATE_RESPONSES
  19. CONCATENATE_RESPONSES_STRING = config.CONCATENATE_RESPONSES_STRING
  20. DESIRED_TOKENS = config.DESIRED_TOKENS
  21. CONTINUATION_QUERY = config.CONTINUATION_QUERY
  22.  
  23. MARKUP_FIX = config.MARKUP_FIX
  24.  
  25. COOKIE_NAME = config.COOKIE_NAME
  26.  
  27. USER_MESSAGE_WORKAROUND = config.USER_MESSAGE_WORKAROUND
  28. USER_MESSAGE = config.USER_MESSAGE
  29.  
  30. REDIRECT_PROXY = config.REDIRECT_PROXY
  31. REDIRECT_API_KEY = config.REDIRECT_API_KEY
  32. REDIRECT_API_MODEL = config.REDIRECT_API_MODEL
  33. REDIRECT_COMMAND = config.REDIRECT_COMMAND
  34. REDIRECT_TEMPERATURE = config.REDIRECT_TEMPERATURE
  35. REDIRECT_USE_CONTEXT = config.REDIRECT_USE_CONTEXT
  36. REDIRECT_CONTEXT_TOKENS = config.REDIRECT_CONTEXT_TOKENS
  37.  
  38. try:
  39.     cookies = json.loads(open(f"./{COOKIE_NAME}", encoding="utf-8").read())
  40. except:
  41.     cookies = None
  42.  
  43. class LinkPlaceholderReplacer:
  44.     def __init__(self):
  45.         self.placeholder_wrap = ""
  46.         self.i = 0
  47.         self.urls = []
  48.         self.stash = ""
  49.         self.regex = r'\^(\d+)\^'
  50.  
  51.     def process(self, content, urls):
  52.  
  53.         if "[" not in content and self.i == 0:
  54.             return content
  55.  
  56.         self.stash += content
  57.  
  58.         if "[" in content:
  59.             self.i = 1
  60.             return ""
  61.         elif self.i == 1 and re.search(self.regex, self.stash):
  62.             self.i = 2
  63.             return ""
  64.         elif self.i == 1 and not re.search(self.regex, self.stash):
  65.             self.i = 0
  66.             result = self.stash
  67.             self.stash = ""
  68.             return result
  69.         elif self.i == 2:
  70.             result = re.sub(r'\[\^(\d+)\^\]', lambda match: transform_into_hyperlink(match, urls), self.stash)
  71.             self.i = 0
  72.             self.stash = ""
  73.             return result
  74.        
  75.         self.stash = ""
  76.  
  77.  
  78. class OpenaiResponse:
  79.     def __init__(self, id, created, end=False, content="", stream=True):
  80.         self.id = id
  81.         self.created = created
  82.         self.end = end
  83.         self.content = content
  84.         self.stream = stream
  85.  
  86.     def dict(self):
  87.         if self.stream:
  88.             data = {
  89.                 "id": self.id,
  90.                 "object": "chat.completion.chunk",
  91.                 "created": self.created,
  92.                 "model": "gpt-4",
  93.                 "choices": [
  94.                     {
  95.                         "delta": {},
  96.                         "index": 0,
  97.                         "finish_reason": "null"
  98.                     }
  99.                 ]
  100.             }
  101.             if self.end: data["choices"][0]["finish_reason"] = "stop"
  102.             if self.content: data["choices"][0]["delta"] = {"content": self.content}
  103.             return data
  104.         else:
  105.             data = {
  106.                 "id": self.id,
  107.                 "created": self.created,
  108.                 "object": "chat.completion",
  109.                 "model": "gpt-4",
  110.                 "choices": [{
  111.                     "message": {
  112.                         "role": 'assistant',
  113.                         "content": self.content
  114.                     },
  115.                     'finish_reason': 'stop',
  116.                     'index': 0,
  117.                 }]
  118.             }
  119.             return data
  120.  
  121.  
  122. def transform_into_hyperlink(match, urls):
  123.     index = int(match.group(1)) - 1
  124.     return f" [{urlparse(urls[index]).hostname}]({urls[index]})"
  125.  
  126.  
  127. def prepare_response(id, created, filter=False, content="", end=False, done=False, stream=True):
  128.  
  129.     response = b""
  130.  
  131.     if stream:
  132.         if filter:
  133.             OAIResponse = OpenaiResponse(id, created, content="Отфильтровано.", stream=stream)
  134.             response += b"data: " + json.dumps(OAIResponse.dict()).encode() + b"\n\n"
  135.         if content:
  136.             OAIResponse = OpenaiResponse(id, created, content=content, stream=stream)
  137.             response += b"data: " + json.dumps(OAIResponse.dict()).encode() + b"\n\n"
  138.         if end:
  139.             OAIResponse = OpenaiResponse(id, created, end=True, stream=stream)
  140.             response += b"data: " + json.dumps(OAIResponse.dict()).encode() + b"\n\n"
  141.         if done:
  142.             response += b"data: " + b"[DONE]" + b"\n\n"
  143.     else:
  144.         response = json.dumps(OpenaiResponse(id, created, content=content, stream=stream).dict()).encode()
  145.  
  146.     return response
  147.  
  148.  
  149. def transform_message(message):
  150.     role = message["role"]
  151.     content = message["content"]
  152.     anchor = "#additional_instructions" if role == "system" else "#message"
  153.     return f"[{role}]({anchor})\n{content}\n\n"
  154.  
  155.  
  156. def process_messages(messages):
  157.     transformed_messages = [transform_message(message) for message in messages]
  158.     return "".join(transformed_messages)+"\n"
  159.  
  160.  
  161. class SSEHandler(web.View):
  162.  
  163.  
  164.     async def get(self):
  165.         data = {
  166.                    "object": "list",
  167.                    "data": [
  168.                        {
  169.                         "id": "gpt-4",
  170.                         "object": "model",
  171.                         "created": str(int(time.time())),
  172.                         "owned_by": "OpenAI",
  173.                         "permissions": [],
  174.                         "root": 'gpt-4',
  175.                         "parent": None
  176.                        }
  177.                    ]
  178.                }
  179.  
  180.         return web.json_response(data)
  181.  
  182.     async def post(self):
  183.  
  184.         self.id = "chatcmpl-" + ''.join(random.choices(string.ascii_letters + string.digits, k=29))
  185.         self.created = str(int(time.time()))
  186.         self.responseWasFiltered = False
  187.         self.responseWasFilteredInLoop = False
  188.         self.fullResponse = ""
  189.  
  190.         async def streamCallback(self, data):
  191.             self.fullResponse += data
  192.             if stream and not redirect:
  193.                 await self.response.write(b"data: " + json.dumps({
  194.                     "id": self.id,
  195.                     "object": "chat.completion.chunk",
  196.                     "created": self.created,
  197.                     "model": "gpt-4",
  198.                     "choices": [
  199.                         {
  200.                             "delta": { "content": data },
  201.                             "index": 0,
  202.                             "finish_reason": "null"
  203.                         }
  204.                     ]
  205.                 }).encode() + b"\n\n")
  206.  
  207.         request_data = await self.request.json()
  208.  
  209.         messages = request_data.get('messages', [])
  210.         if USER_MESSAGE_WORKAROUND:
  211.             prompt = USER_MESSAGE
  212.             context = process_messages(messages)
  213.         else:
  214.             prompt = messages[-1]['content']
  215.             context = process_messages(messages[:-1])
  216.         stream = request_data.get('stream', [])
  217.         self.response = web.StreamResponse(
  218.             status=200,
  219.             headers={
  220.                 'Content-Type': 'application/json',
  221.             }
  222.         )
  223.         await self.response.prepare(self.request)
  224.  
  225.  
  226.         conversation_style = self.request.path.split('/')[1]
  227.         if conversation_style not in ["creative", "balanced", "precise"]:
  228.             conversation_style = "creative"
  229.  
  230.         if self.request.path.split('/')[1] == "suggestion":
  231.             redirect = True
  232.  
  233.         if self.request.path.split('/')[2] == "suggestion":
  234.             suggestion = True
  235.         else:
  236.             suggestion = False
  237.  
  238.         if self.request.path.split('/')[2] == "redirect":
  239.             redirect = True
  240.         else:
  241.             redirect = False
  242.  
  243.         async def output(self, streamCallback, nsfwMode=False):
  244.             self.responseText = ""
  245.  
  246.             try:
  247.                 chatbot = await Chatbot.create(cookies=cookies)
  248.             except Exception as e:
  249.                 if str(e) == "[Errno 11001] getaddrinfo failed":
  250.                     print("Нет интернет-соединения.")
  251.                     return
  252.                 print("Ошибка запуска чатбота.", str(e))
  253.                 return
  254.            
  255.             print("\nФормируется запрос...")
  256.             link_placeholder_replacer = LinkPlaceholderReplacer()
  257.             wrote = 0
  258.  
  259.             async for final, response in chatbot.ask_stream(
  260.                     prompt=prompt,
  261.                     raw=True,
  262.                     webpage_context=context,
  263.                     conversation_style=conversation_style,
  264.                     search_result=True,
  265.             ):
  266.  
  267.                 if not final and response["type"] == 1 and "messages" in response["arguments"][0]:
  268.                     message = response["arguments"][0]["messages"][0]
  269.                     match message.get("messageType"):
  270.                         case "InternalSearchQuery":
  271.                             print(f"Поиск в Бинге:", message['hiddenText'])
  272.                         case "InternalSearchResult":
  273.                             if 'hiddenText' in message:
  274.                                 search = message['hiddenText'] = message['hiddenText'][len("```json\n"):]
  275.                                 search = search[:-len("```")]
  276.                                 print(f"search: {search}")
  277.                                 urls = []
  278.                                 try:
  279.                                     search = json.loads(search)
  280.                                     urls = []
  281.                                     if "question_answering_results" in search:
  282.                                         for result in search["question_answering_results"]:
  283.                                             urls.append(result["url"])
  284.                                     if "web_search_results" in search:
  285.                                         for result in search["web_search_results"]:
  286.                                             urls.append(result["url"])
  287.                                 except json.JSONDecodeError as e:
  288.                                     print(f"Error decoding JSON: {e}")
  289.                         case None:
  290.                             if "cursor" in response["arguments"][0]:
  291.                                 print("\nОтвет от сервера:\n")
  292.                             if message.get("contentOrigin") == "Apology":
  293.                                 if stream and wrote == 0:
  294.                                     await streamCallback(self, "Отфильтровано.")
  295.                                     if nsfwMode:
  296.                                         self.responseWasFilteredInLoop = True
  297.                                     break
  298.  
  299.                                 if MARKUP_FIX:
  300.                                     if self.responseText.count("*") % 2 == 1 or self.responseText.count("*") == 1:
  301.                                         await streamCallback(self, "*")
  302.                                         self.responseText += "*"
  303.                                     if self.responseText.count("\"") % 2 == 1 or self.responseText.count("\"") == 1:
  304.                                         await streamCallback(self, "\"")
  305.                                         self.responseText += "\""
  306.  
  307.                                 self.responseWasFiltered = True
  308.  
  309.                                 print("\nОтвет отозван во время стрима.")
  310.                                 break
  311.                             else:
  312.                                 streaming_content_chunk = message['text'][wrote:]
  313.                                 streaming_content_chunk = streaming_content_chunk.replace('\\"', '\"')
  314.  
  315.  
  316.                                 if 'urls' in vars():
  317.                                     if urls:
  318.                                         streaming_content_chunk = link_placeholder_replacer.process(streaming_content_chunk, urls)
  319.  
  320.                                 self.responseText += streaming_content_chunk
  321.  
  322.                                 await streamCallback(self, streaming_content_chunk)
  323.  
  324.                                 print(message["text"][wrote:], end="")
  325.                                 sys.stdout.flush()
  326.                                 wrote = len(message["text"])
  327.  
  328.                                 if "suggestedResponses" in message:
  329.                                     suggested_responses = '\n'.join(x["text"] for x in message["suggestedResponses"])
  330.                                     suggested_responses = "\n```" + suggested_responses + "```"
  331.                                     if suggestion and not nsfwMode:
  332.                                         await streamCallback(self, suggested_responses)
  333.                                     break
  334.                 if final and not response["item"]["messages"][-1].get("text"):
  335.                     print("Сработал фильтр.")
  336.                     if nsfwMode:
  337.                         print("Выходим из цикла.\n")
  338.                         self.responseWasFilteredInLoop = True
  339.  
  340.             await chatbot.close()
  341.  
  342.            
  343.            
  344.         try:
  345.             if stream and not redirect:
  346.                 await self.response.write(b"data: " + json.dumps({
  347.                     "id": self.id,
  348.                     "object": "chat.completion.chunk",
  349.                     "created": self.created,
  350.                     "model": "gpt-4",
  351.                     "choices": [
  352.                         {
  353.                             "delta": { "role": 'assistant' },
  354.                             "index": 0,
  355.                             "finish_reason": "null"
  356.                         }
  357.                     ]
  358.                 }).encode() + b"\n\n")
  359.             await output(self, streamCallback)
  360.             encoding = tiktoken.get_encoding("cl100k_base")
  361.             if self.responseWasFiltered and CONCATENATE_RESPONSES:
  362.                 tokens_total = len(encoding.encode(self.fullResponse))
  363.                 if USER_MESSAGE_WORKAROUND:
  364.                     prompt = CONTINUATION_QUERY
  365.                     context += f"[assistant](#message)\n{self.responseText}\n"
  366.                 else:
  367.                     context+=f"[{messages[-1]['role']}](#message)\n{prompt}\n\n[assistant](#message)\n{self.responseText}\n"
  368.                     prompt=CONTINUATION_QUERY
  369.                 self.fullResponse += CONCATENATE_RESPONSES_STRING
  370.                 print("Токенов в ответе:",tokens_total)
  371.                 while tokens_total < DESIRED_TOKENS and not self.responseWasFilteredInLoop:
  372.                     if stream and not redirect:
  373.                         await self.response.write(b"data: " + json.dumps({
  374.                             "id": self.id,
  375.                             "object": "chat.completion.chunk",
  376.                             "created": self.created,
  377.                             "model": "gpt-4",
  378.                             "choices": [
  379.                                 {
  380.                                     "delta": { "content": CONCATENATE_RESPONSES_STRING },
  381.                                     "index": 0,
  382.                                     "finish_reason": "null"
  383.                                 }
  384.                             ]
  385.                         }).encode() + b"\n\n")
  386.                     await output(self, streamCallback, nsfwMode=True)
  387.                     context+=self.responseText + CONCATENATE_RESPONSES_STRING
  388.                     self.fullResponse += CONCATENATE_RESPONSES_STRING
  389.                     tokens_response = len(encoding.encode(self.responseText))
  390.                     tokens_total = len(encoding.encode(self.fullResponse))
  391.                     print(f"\nТокенов в ответе: {tokens_response}")
  392.                     print(f"Токенов всего: {tokens_total}")
  393.             if redirect:
  394.                 async with aiohttp.ClientSession() as session:
  395.                     messages_token_count = len(encoding.encode(f"{self.fullResponse}\n\n{REDIRECT_COMMAND}"))
  396.                     redirect_messages = [{"role": "user", "content": f"{self.fullResponse}\n\n{REDIRECT_COMMAND}"}]
  397.                     if REDIRECT_USE_CONTEXT:
  398.                         for message in reversed(messages):
  399.                             if (messages_token_count + len(message["content"])) > REDIRECT_CONTEXT_TOKENS: break
  400.                             messages_token_count += len(message["content"])
  401.                             redirect_messages.insert(0, message)
  402.                     headers = {"Content-Type": "application/json","Authorization": f"Bearer {REDIRECT_API_KEY}"}
  403.                     body = {
  404.                         "model": REDIRECT_API_MODEL,
  405.                         "messages": redirect_messages,
  406.                         "temperature": REDIRECT_TEMPERATURE,
  407.                         "stream": stream
  408.                     }
  409.                     if REDIRECT_PROXY.endswith("v1/chat/completions") or REDIRECT_PROXY.endswith("v1/chat/completions/"):
  410.                         url = REDIRECT_PROXY
  411.                     elif REDIRECT_PROXY.endswith("/"):
  412.                         url = f"{REDIRECT_PROXY}v1/chat/completions"
  413.                     else:
  414.                         url = f"{REDIRECT_PROXY}/v1/chat/completions"
  415.                     async with session.post(url, headers=headers, json=body) as response:
  416.                         async for chunk in response.content.iter_chunked(1024):
  417.                             chunk_str = chunk.decode("utf-8")
  418.                             if stream and not chunk_str.startswith("data: ") and chunk_str != "\n: joining queue\n\n":
  419.                                 oai_response = prepare_response(self.id, self.created, content="```\n" + chunk_str + "\n```", end=True, done=True, stream=True)
  420.                                 await self.response.write(oai_response)
  421.                             elif not stream and not "choices" in json.loads(chunk.decode("utf-8")) and chunk.decode("utf-8") != "\n: joining queue\n\n":
  422.                                 oai_response = prepare_response(self.id, self.created, content="```\n" + chunk_str + "\n```", stream=False)
  423.                                 await self.response.write(oai_response)
  424.                             else: await self.response.write(chunk)
  425.             else:
  426.                 if stream:
  427.                     await self.response.write(b"data: " + json.dumps({
  428.                             "id": self.id,
  429.                             "created": self.created,
  430.                             "object": 'chat.completion.chunk',
  431.                             "model": "gpt-4",
  432.                             "choices": [{
  433.                                 "delta": {},
  434.                                 "finish_reason": 'stop',
  435.                                 "index": 0,
  436.                             }],
  437.                         }).encode() + b"\n\n")
  438.                 else:
  439.                     await self.response.write(json.dumps({
  440.                             "id": self.id,
  441.                             "created": self.created,
  442.                             "object": "chat.completion",
  443.                             "model": "gpt-4",
  444.                             "choices": [{
  445.                                 "message": {
  446.                                     "role": 'assistant',
  447.                                     "content": self.fullResponse
  448.                                 },
  449.                                 'finish_reason': 'stop',
  450.                                 'index': 0,
  451.                             }]
  452.                         }).encode())
  453.             return self.response
  454.         except Exception as e:
  455.             error = f"Ошибка: {str(e)}."
  456.             error_text = ""
  457.             if str(e) == "'messages'":
  458.                 error_text = "\nПроблема с учеткой. Возможные причины: \n```\n " \
  459.                              "  Бан. Фикс: регистрация по новой. \n " \
  460.                              "  Куки слетели. Фикс: собрать их снова. \n " \
  461.                              "  Достигнут лимит сообщений Бинга. Фикс: попробовать разлогиниться и собрать куки, либо собрать их с новой учетки и/или айпи. \n " \
  462.                              "  Возможно Бинг барахлит/троттлит запросы и нужно просто сделать реген/свайп. \n```\n " \
  463.                              "Чтобы узнать подробности можно зайти в сам чат Бинга и отправить сообщение."
  464.                 print(error, error_text)
  465.             elif str(e) == " " or str(e) == "":
  466.                 error_text = "Таймаут."
  467.                 print(error, error_text)
  468.             elif str(e) == "received 1000 (OK); then sent 1000 (OK)" or str(e) == "'int' object has no attribute 'split'":
  469.                 error_text = "Слишком много токенов. Больше 14000 токенов не принимает."
  470.                 print(error, error_text)
  471.             elif str(e) == "'contentOrigin'":
  472.                 error_text = "Ошибка связанная с размером промпта. \n " \
  473.                              "Возможно последнее сообщение в отправленном промпте (джейл или сообщение пользователя/ассистента) " \
  474.                              "на сервер слишком большое. \n"
  475.                 print(error, error_text)
  476.             else:
  477.                 print(error)
  478.             if not self.fullResponse:
  479.                 if stream:
  480.                     oai_response = prepare_response(self.id, self.created, content=error + error_text, end=True, done=True, stream=True)
  481.                 else:
  482.                     oai_response = prepare_response(self.id, self.created, content=error + error_text, stream=False)
  483.             else:
  484.                 if stream:
  485.                     oai_response = prepare_response(self.id, self.created, end=True, done=True, stream=True)
  486.                 else:
  487.                     oai_response = prepare_response(self.id, self.created, content=self.fullResponse, stream=False)
  488.             await self.response.write(oai_response)
  489.         return self.response        
  490.  
  491.  
  492.  
  493. app = web.Application()
  494. app.router.add_routes([
  495.     web.route('*', '/{tail:.*}', SSEHandler),
  496. ])
  497.  
  498. if __name__ == '__main__':
  499.     print(f"Есть несколько режимов (разнятся температурой):\n"
  500.           f"По дефолту стоит creative: http://{HOST}:{PORT}/\n"
  501.           f"Режим creative: http://{HOST}:{PORT}/creative\n"
  502.           f"Режим precise:  http://{HOST}:{PORT}/precise\n"
  503.           f"Режим balanced: http://{HOST}:{PORT}/balanced\n"
  504.           f"Есть режим подсказок от Бинга. Чтобы его включить, нужно добавить /suggestion после выбранного режима.\n"
  505.           f"И еще есть режим переброса, нужный для того чтобы победить шиканье креативной Сидни. Включается добавлением /redirect после режима.")
  506.     web.run_app(app, host=HOST, port=PORT, print=None)
Advertisement
Comments
Add Comment
Please, Sign In to add comment
Advertisement