CoolFace
Apppublic

Danielzero/GPT3.5

sourceHugging Facegpl-3.0updated 3y agoView on Hugging Face
1likes
base_model.py562 linesDownload Raw Back to modules
1from __future__ import annotations2from typing import TYPE_CHECKING, List3 4import logging5import json6import commentjson as cjson7import os8import sys9import requests10import urllib311import traceback12 13from tqdm import tqdm14import colorama15from duckduckgo_search import ddg16import asyncio17import aiohttp18from enum import Enum19 20from .presets import *21from .llama_func import *22from .utils import *23from . import shared24from .config import retrieve_proxy25 26 27class ModelType(Enum):28    Unknown = -129    OpenAI = 030    ChatGLM = 131    LLaMA = 232    XMChat = 333 34    @classmethod35    def get_type(cls, model_name: str):36        model_type = None37        model_name_lower = model_name.lower()38        if "gpt" in model_name_lower:39            model_type = ModelType.OpenAI40        elif "chatglm" in model_name_lower:41            model_type = ModelType.ChatGLM42        elif "llama" in model_name_lower or "alpaca" in model_name_lower:43            model_type = ModelType.LLaMA44        elif "xmchat" in model_name_lower:45            model_type = ModelType.XMChat46        else:47            model_type = ModelType.Unknown48        return model_type49 50 51class BaseLLMModel:52    def __init__(53        self,54        model_name,55        system_prompt="",56        temperature=1.0,57        top_p=1.0,58        n_choices=1,59        stop=None,60        max_generation_token=None,61        presence_penalty=0,62        frequency_penalty=0,63        logit_bias=None,64        user="",65    ) -> None:66        self.history = []67        self.all_token_counts = []68        self.model_name = model_name69        self.model_type = ModelType.get_type(model_name)70        try:71            self.token_upper_limit = MODEL_TOKEN_LIMIT[model_name]72        except KeyError:73            self.token_upper_limit = DEFAULT_TOKEN_LIMIT74        self.interrupted = False75        self.system_prompt = system_prompt76        self.api_key = None77        self.need_api_key = False78        self.single_turn = False79 80        self.temperature = temperature81        self.top_p = top_p82        self.n_choices = n_choices83        self.stop_sequence = stop84        self.max_generation_token = None85        self.presence_penalty = presence_penalty86        self.frequency_penalty = frequency_penalty87        self.logit_bias = logit_bias88        self.user_identifier = user89 90    def get_answer_stream_iter(self):91        """stream predict, need to be implemented92        conversations are stored in self.history, with the most recent question, in OpenAI format93        should return a generator, each time give the next word (str) in the answer94        """95        logging.warning("stream predict not implemented, using at once predict instead")96        response, _ = self.get_answer_at_once()97        yield response98 99    def get_answer_at_once(self):100        """predict at once, need to be implemented101        conversations are stored in self.history, with the most recent question, in OpenAI format102        Should return:103        the answer (str)104        total token count (int)105        """106        logging.warning("at once predict not implemented, using stream predict instead")107        response_iter = self.get_answer_stream_iter()108        count = 0109        for response in response_iter:110            count += 1111        return response, sum(self.all_token_counts) + count112 113    def billing_info(self):114        """get billing infomation, inplement if needed"""115        logging.warning("billing info not implemented, using default")116        return BILLING_NOT_APPLICABLE_MSG117 118    def count_token(self, user_input):119        """get token count from input, implement if needed"""120        logging.warning("token count not implemented, using default")121        return len(user_input)122 123    def stream_next_chatbot(self, inputs, chatbot, fake_input=None, display_append=""):124        def get_return_value():125            return chatbot, status_text126 127        status_text = i18n("开始实时传输回答……")128        if fake_input:129            chatbot.append((fake_input, ""))130        else:131            chatbot.append((inputs, ""))132 133        user_token_count = self.count_token(inputs)134        self.all_token_counts.append(user_token_count)135        logging.debug(f"输入token计数: {user_token_count}")136 137        stream_iter = self.get_answer_stream_iter()138 139        for partial_text in stream_iter:140            chatbot[-1] = (chatbot[-1][0], partial_text + display_append)141            self.all_token_counts[-1] += 1142            status_text = self.token_message()143            yield get_return_value()144            if self.interrupted:145                self.recover()146                break147        self.history.append(construct_assistant(partial_text))148 149    def next_chatbot_at_once(self, inputs, chatbot, fake_input=None, display_append=""):150        if fake_input:151            chatbot.append((fake_input, ""))152        else:153            chatbot.append((inputs, ""))154        if fake_input is not None:155            user_token_count = self.count_token(fake_input)156        else:157            user_token_count = self.count_token(inputs)158        self.all_token_counts.append(user_token_count)159        ai_reply, total_token_count = self.get_answer_at_once()160        self.history.append(construct_assistant(ai_reply))161        if fake_input is not None:162            self.history[-2] = construct_user(fake_input)163        chatbot[-1] = (chatbot[-1][0], ai_reply + display_append)164        if fake_input is not None:165            self.all_token_counts[-1] += count_token(construct_assistant(ai_reply))166        else:167            self.all_token_counts[-1] = total_token_count - sum(self.all_token_counts)168        status_text = self.token_message()169        return chatbot, status_text170 171    def handle_file_upload(self, files, chatbot):172        """if the model accepts multi modal input, implement this function"""173        status = gr.Markdown.update()174        if files:175            construct_index(self.api_key, file_src=files)176            status = "索引构建完成"177        return gr.Files.update(), chatbot, status178 179    def prepare_inputs(self, real_inputs, use_websearch, files, reply_language, chatbot):180        fake_inputs = None181        display_append = []182        limited_context = False183        fake_inputs = real_inputs184        if files:185            from llama_index.indices.vector_store.base_query import GPTVectorStoreIndexQuery186            from llama_index.indices.query.schema import QueryBundle187            from langchain.embeddings.huggingface import HuggingFaceEmbeddings188            from langchain.chat_models import ChatOpenAI189            from llama_index import (190                GPTSimpleVectorIndex,191                ServiceContext,192                LangchainEmbedding,193                OpenAIEmbedding,194            )195            limited_context = True196            msg = "加载索引中……"197            logging.info(msg)198            # yield chatbot + [(inputs, "")], msg199            index = construct_index(self.api_key, file_src=files)200            assert index is not None, "获取索引失败"201            msg = "索引获取成功,生成回答中……"202            logging.info(msg)203            if local_embedding or self.model_type != ModelType.OpenAI:204                embed_model = LangchainEmbedding(HuggingFaceEmbeddings(model_name = "sentence-transformers/distiluse-base-multilingual-cased-v2"))205            else:206                embed_model = OpenAIEmbedding()207            # yield chatbot + [(inputs, "")], msg208            with retrieve_proxy():209                prompt_helper = PromptHelper(210                    max_input_size=4096,211                    num_output=5,212                    max_chunk_overlap=20,213                    chunk_size_limit=600,214                )215                from llama_index import ServiceContext216 217                service_context = ServiceContext.from_defaults(218                    prompt_helper=prompt_helper, embed_model=embed_model219                )220                query_object = GPTVectorStoreIndexQuery(221                    index.index_struct,222                    service_context=service_context,223                    similarity_top_k=5,224                    vector_store=index._vector_store,225                    docstore=index._docstore,226                )227                query_bundle = QueryBundle(real_inputs)228                nodes = query_object.retrieve(query_bundle)229            reference_results = [n.node.text for n in nodes]230            reference_results = add_source_numbers(reference_results, use_source=False)231            display_append = add_details(reference_results)232            display_append = "\n\n" + "".join(display_append)233            real_inputs = (234                replace_today(PROMPT_TEMPLATE)235                .replace("{query_str}", real_inputs)236                .replace("{context_str}", "\n\n".join(reference_results))237                .replace("{reply_language}", reply_language)238            )239        elif use_websearch:240            limited_context = True241            search_results = ddg(real_inputs, max_results=5)242            reference_results = []243            for idx, result in enumerate(search_results):244                logging.debug(f"搜索结果{idx + 1}:{result}")245                domain_name = urllib3.util.parse_url(result["href"]).host246                reference_results.append([result["body"], result["href"]])247                display_append.append(248                    # f"{idx+1}. [{domain_name}]({result['href']})\n"249                    f"<li><a href=\"{result['href']}\" target=\"_blank\">{domain_name}</a></li>\n"250                )251            reference_results = add_source_numbers(reference_results)252            display_append = "<ol>\n\n" + "".join(display_append) + "</ol>"253            real_inputs = (254                replace_today(WEBSEARCH_PTOMPT_TEMPLATE)255                .replace("{query}", real_inputs)256                .replace("{web_results}", "\n\n".join(reference_results))257                .replace("{reply_language}", reply_language)258            )259        else:260            display_append = ""261        return limited_context, fake_inputs, display_append, real_inputs, chatbot262 263    def predict(264        self,265        inputs,266        chatbot,267        stream=False,268        use_websearch=False,269        files=None,270        reply_language="中文",271        should_check_token_count=True,272    ):  # repetition_penalty, top_k273 274        status_text = "开始生成回答……"275        logging.info(276            "输入为:" + colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL277        )278        if should_check_token_count:279            yield chatbot + [(inputs, "")], status_text280        if reply_language == "跟随问题语言(不稳定)":281            reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."282 283        limited_context, fake_inputs, display_append, inputs, chatbot = self.prepare_inputs(real_inputs=inputs, use_websearch=use_websearch, files=files, reply_language=reply_language, chatbot=chatbot)284        yield chatbot + [(fake_inputs, "")], status_text285 286        if (287            self.need_api_key and288            self.api_key is None289            and not shared.state.multi_api_key290        ):291            status_text = STANDARD_ERROR_MSG + NO_APIKEY_MSG292            logging.info(status_text)293            chatbot.append((inputs, ""))294            if len(self.history) == 0:295                self.history.append(construct_user(inputs))296                self.history.append("")297                self.all_token_counts.append(0)298            else:299                self.history[-2] = construct_user(inputs)300            yield chatbot + [(inputs, "")], status_text301            return302        elif len(inputs.strip()) == 0:303            status_text = STANDARD_ERROR_MSG + NO_INPUT_MSG304            logging.info(status_text)305            yield chatbot + [(inputs, "")], status_text306            return307 308        if self.single_turn:309            self.history = []310            self.all_token_counts = []311        self.history.append(construct_user(inputs))312 313        try:314            if stream:315                logging.debug("使用流式传输")316                iter = self.stream_next_chatbot(317                    inputs,318                    chatbot,319                    fake_input=fake_inputs,320                    display_append=display_append,321                )322                for chatbot, status_text in iter:323                    yield chatbot, status_text324            else:325                logging.debug("不使用流式传输")326                chatbot, status_text = self.next_chatbot_at_once(327                    inputs,328                    chatbot,329                    fake_input=fake_inputs,330                    display_append=display_append,331                )332                yield chatbot, status_text333        except Exception as e:334            traceback.print_exc()335            status_text = STANDARD_ERROR_MSG + str(e)336            yield chatbot, status_text337 338        if len(self.history) > 1 and self.history[-1]["content"] != inputs:339            logging.info(340                "回答为:"341                + colorama.Fore.BLUE342                + f"{self.history[-1]['content']}"343                + colorama.Style.RESET_ALL344            )345 346        if limited_context:347            # self.history = self.history[-4:]348            # self.all_token_counts = self.all_token_counts[-2:]349            self.history = []350            self.all_token_counts = []351 352        max_token = self.token_upper_limit - TOKEN_OFFSET353 354        if sum(self.all_token_counts) > max_token and should_check_token_count:355            count = 0356            while (357                sum(self.all_token_counts)358                > self.token_upper_limit * REDUCE_TOKEN_FACTOR359                and sum(self.all_token_counts) > 0360            ):361                count += 1362                del self.all_token_counts[0]363                del self.history[:2]364            logging.info(status_text)365            status_text = f"为了防止token超限,模型忘记了早期的 {count} 轮对话"366            yield chatbot, status_text367 368    def retry(369        self,370        chatbot,371        stream=False,372        use_websearch=False,373        files=None,374        reply_language="中文",375    ):376        logging.debug("重试中……")377        if len(self.history) > 0:378            inputs = self.history[-2]["content"]379            del self.history[-2:]380            self.all_token_counts.pop()381        elif len(chatbot) > 0:382            inputs = chatbot[-1][0]383        else:384            yield chatbot, f"{STANDARD_ERROR_MSG}上下文是空的"385            return386 387        iter = self.predict(388            inputs,389            chatbot,390            stream=stream,391            use_websearch=use_websearch,392            files=files,393            reply_language=reply_language,394        )395        for x in iter:396            yield x397        logging.debug("重试完毕")398 399    # def reduce_token_size(self, chatbot):400    #     logging.info("开始减少token数量……")401    #     chatbot, status_text = self.next_chatbot_at_once(402    #         summarize_prompt,403    #         chatbot404    #     )405    #     max_token_count = self.token_upper_limit * REDUCE_TOKEN_FACTOR406    #     num_chat = find_n(self.all_token_counts, max_token_count)407    #     logging.info(f"previous_token_count: {self.all_token_counts}, keeping {num_chat} chats")408    #     chatbot = chatbot[:-1]409    #     self.history = self.history[-2*num_chat:] if num_chat > 0 else []410    #     self.all_token_counts = self.all_token_counts[-num_chat:] if num_chat > 0 else []411    #     msg = f"保留了最近{num_chat}轮对话"412    #     logging.info(msg)413    #     logging.info("减少token数量完毕")414    #     return chatbot, msg + "," + self.token_message(self.all_token_counts if len(self.all_token_counts) > 0 else [0])415 416    def interrupt(self):417        self.interrupted = True418 419    def recover(self):420        self.interrupted = False421 422    def set_token_upper_limit(self, new_upper_limit):423        self.token_upper_limit = new_upper_limit424        print(f"token上限设置为{new_upper_limit}")425 426    def set_temperature(self, new_temperature):427        self.temperature = new_temperature428 429    def set_top_p(self, new_top_p):430        self.top_p = new_top_p431 432    def set_n_choices(self, new_n_choices):433        self.n_choices = new_n_choices434 435    def set_stop_sequence(self, new_stop_sequence: str):436        new_stop_sequence = new_stop_sequence.split(",")437        self.stop_sequence = new_stop_sequence438 439    def set_max_tokens(self, new_max_tokens):440        self.max_generation_token = new_max_tokens441 442    def set_presence_penalty(self, new_presence_penalty):443        self.presence_penalty = new_presence_penalty444 445    def set_frequency_penalty(self, new_frequency_penalty):446        self.frequency_penalty = new_frequency_penalty447 448    def set_logit_bias(self, logit_bias):449        logit_bias = logit_bias.split()450        bias_map = {}451        encoding = tiktoken.get_encoding("cl100k_base")452        for line in logit_bias:453            word, bias_amount = line.split(":")454            if word:455                for token in encoding.encode(word):456                    bias_map[token] = float(bias_amount)457        self.logit_bias = bias_map458 459    def set_user_identifier(self, new_user_identifier):460        self.user_identifier = new_user_identifier461 462    def set_system_prompt(self, new_system_prompt):463        self.system_prompt = new_system_prompt464 465    def set_key(self, new_access_key):466        self.api_key = new_access_key.strip()467        msg = i18n("API密钥更改为了") + hide_middle_chars(self.api_key)468        logging.info(msg)469        return self.api_key, msg470 471    def set_single_turn(self, new_single_turn):472        self.single_turn = new_single_turn473 474    def reset(self):475        self.history = []476        self.all_token_counts = []477        self.interrupted = False478        return [], self.token_message([0])479 480    def delete_first_conversation(self):481        if self.history:482            del self.history[:2]483            del self.all_token_counts[0]484        return self.token_message()485 486    def delete_last_conversation(self, chatbot):487        if len(chatbot) > 0 and STANDARD_ERROR_MSG in chatbot[-1][1]:488            msg = "由于包含报错信息,只删除chatbot记录"489            chatbot.pop()490            return chatbot, self.history491        if len(self.history) > 0:492            self.history.pop()493            self.history.pop()494        if len(chatbot) > 0:495            msg = "删除了一组chatbot对话"496            chatbot.pop()497        if len(self.all_token_counts) > 0:498            msg = "删除了一组对话的token计数记录"499            self.all_token_counts.pop()500        msg = "删除了一组对话"501        return chatbot, msg502 503    def token_message(self, token_lst=None):504        if token_lst is None:505            token_lst = self.all_token_counts506        token_sum = 0507        for i in range(len(token_lst)):508            token_sum += sum(token_lst[: i + 1])509        return i18n("Token 计数: ") + f"{sum(token_lst)}" + i18n(",本次对话累计消耗了 ") + f"{token_sum} tokens"510 511    def save_chat_history(self, filename, chatbot, user_name):512        if filename == "":513            return514        if not filename.endswith(".json"):515            filename += ".json"516        return save_file(filename, self.system_prompt, self.history, chatbot, user_name)517 518    def export_markdown(self, filename, chatbot, user_name):519        if filename == "":520            return521        if not filename.endswith(".md"):522            filename += ".md"523        return save_file(filename, self.system_prompt, self.history, chatbot, user_name)524 525    def load_chat_history(self, filename, chatbot, user_name):526        logging.debug(f"{user_name} 加载对话历史中……")527        if type(filename) != str:528            filename = filename.name529        try:530            with open(os.path.join(HISTORY_DIR, user_name, filename), "r") as f:531                json_s = json.load(f)532            try:533                if type(json_s["history"][0]) == str:534                    logging.info("历史记录格式为旧版,正在转换……")535                    new_history = []536                    for index, item in enumerate(json_s["history"]):537                        if index % 2 == 0:538                            new_history.append(construct_user(item))539                        else:540                            new_history.append(construct_assistant(item))541                    json_s["history"] = new_history542                    logging.info(new_history)543            except:544                # 没有对话历史545                pass546            logging.debug(f"{user_name} 加载对话历史完毕")547            self.history = json_s["history"]548            return filename, json_s["system"], json_s["chatbot"]549        except FileNotFoundError:550            logging.warning(f"{user_name} 没有找到对话历史文件,不执行任何操作")551            return filename, self.system_prompt, chatbot552 553    def like(self):554        """like the last response, implement if needed555        """556        return gr.update()557 558    def dislike(self):559        """dislike the last response, implement if needed560        """561        return gr.update()562