CoolFace
Apppublic

cathub/LocalChatGPT

sourceHugging Facegpl-3.0updated 3y agoView on Hugging Face
0likes
chat_func.py514 linesDownload Raw Back to modules
1# -*- coding:utf-8 -*-2from __future__ import annotations3from typing import TYPE_CHECKING, List4 5import logging6import json7import os8import requests9import urllib310 11from tqdm import tqdm12import colorama13from duckduckgo_search import ddg14import asyncio15import aiohttp16from llama_index.indices.query.vector_store import GPTVectorStoreIndexQuery17from llama_index.indices.query.schema import QueryBundle18from langchain.llms import OpenAIChat19 20from modules.presets import *21from modules.llama_func import *22from modules.utils import *23import modules.shared as shared24 25# logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s")26 27if TYPE_CHECKING:28    from typing import TypedDict29 30    class DataframeData(TypedDict):31        headers: List[str]32        data: List[List[str | int | bool]]33 34 35initial_prompt = "You are a helpful assistant."36HISTORY_DIR = "history"37TEMPLATES_DIR = "templates"38 39def get_response(40    openai_api_key, system_prompt, history, temperature, top_p, stream, selected_model41):42    headers = {43        "Content-Type": "application/json",44        "Authorization": f"Bearer {openai_api_key}",45    }46 47    history = [construct_system(system_prompt), *history]48 49    payload = {50        "model": selected_model,51        "messages": history,  # [{"role": "user", "content": f"{inputs}"}],52        "temperature": temperature,  # 1.0,53        "top_p": top_p,  # 1.0,54        "n": 1,55        "stream": stream,56        "presence_penalty": 0,57        "frequency_penalty": 0,58    }59    if stream:60        timeout = timeout_streaming61    else:62        timeout = timeout_all63 64    proxies = get_proxies()65 66    # 如果有自定义的api-url,使用自定义url发送请求,否则使用默认设置发送请求67    if shared.state.api_url != API_URL:68        logging.info(f"使用自定义API URL: {shared.state.api_url}")69 70    response = requests.post(71        shared.state.api_url,72        headers=headers,73        json=payload,74        stream=True,75        timeout=timeout,76        proxies=proxies,77    )78 79    return response80 81 82def stream_predict(83    openai_api_key,84    system_prompt,85    history,86    inputs,87    chatbot,88    all_token_counts,89    top_p,90    temperature,91    selected_model,92    fake_input=None,93    display_append=""94):95    def get_return_value():96        return chatbot, history, status_text, all_token_counts97 98    logging.info("实时回答模式")99    partial_words = ""100    counter = 0101    status_text = "开始实时传输回答……"102    history.append(construct_user(inputs))103    history.append(construct_assistant(""))104    if fake_input:105        chatbot.append((fake_input, ""))106    else:107        chatbot.append((inputs, ""))108    user_token_count = 0109    if fake_input is not None:110        input_token_count = count_token(construct_user(fake_input))111    else:112        input_token_count = count_token(construct_user(inputs))113    if len(all_token_counts) == 0:114        system_prompt_token_count = count_token(construct_system(system_prompt))115        user_token_count = (116            input_token_count + system_prompt_token_count117        )118    else:119        user_token_count = input_token_count120    all_token_counts.append(user_token_count)121    logging.info(f"输入token计数: {user_token_count}")122    yield get_return_value()123    try:124        response = get_response(125            openai_api_key,126            system_prompt,127            history,128            temperature,129            top_p,130            True,131            selected_model,132        )133    except requests.exceptions.ConnectTimeout:134        status_text = (135            standard_error_msg + connection_timeout_prompt + error_retrieve_prompt136        )137        yield get_return_value()138        return139    except requests.exceptions.ReadTimeout:140        status_text = standard_error_msg + read_timeout_prompt + error_retrieve_prompt141        yield get_return_value()142        return143 144    yield get_return_value()145    error_json_str = ""146 147    if fake_input is not None:148        history[-2] = construct_user(fake_input)149    for chunk in response.iter_lines():150        if counter == 0:151            counter += 1152            continue153        counter += 1154        # check whether each line is non-empty155        if chunk:156            chunk = chunk.decode()157            chunklength = len(chunk)158            try:159                chunk = json.loads(chunk[6:])160            except json.JSONDecodeError:161                logging.info(chunk)162                error_json_str += chunk163                status_text = f"JSON解析错误。请重置对话。收到的内容: {error_json_str}"164                yield get_return_value()165                continue166            # decode each line as response data is in bytes167            if chunklength > 6 and "delta" in chunk["choices"][0]:168                finish_reason = chunk["choices"][0]["finish_reason"]169                status_text = construct_token_message(170                    sum(all_token_counts), stream=True171                )172                if finish_reason == "stop":173                    yield get_return_value()174                    break175                try:176                    partial_words = (177                        partial_words + chunk["choices"][0]["delta"]["content"]178                    )179                except KeyError:180                    status_text = (181                        standard_error_msg182                        + "API回复中找不到内容。很可能是Token计数达到上限了。请重置对话。当前Token计数: "183                        + str(sum(all_token_counts))184                    )185                    yield get_return_value()186                    break187                history[-1] = construct_assistant(partial_words)188                chatbot[-1] = (chatbot[-1][0], partial_words+display_append)189                all_token_counts[-1] += 1190                yield get_return_value()191 192 193def predict_all(194    openai_api_key,195    system_prompt,196    history,197    inputs,198    chatbot,199    all_token_counts,200    top_p,201    temperature,202    selected_model,203    fake_input=None,204    display_append=""205):206    logging.info("一次性回答模式")207    history.append(construct_user(inputs))208    history.append(construct_assistant(""))209    if fake_input:210        chatbot.append((fake_input, ""))211    else:212        chatbot.append((inputs, ""))213    if fake_input is not None:214        all_token_counts.append(count_token(construct_user(fake_input)))215    else:216        all_token_counts.append(count_token(construct_user(inputs)))217    try:218        response = get_response(219            openai_api_key,220            system_prompt,221            history,222            temperature,223            top_p,224            False,225            selected_model,226        )227    except requests.exceptions.ConnectTimeout:228        status_text = (229            standard_error_msg + connection_timeout_prompt + error_retrieve_prompt230        )231        return chatbot, history, status_text, all_token_counts232    except requests.exceptions.ProxyError:233        status_text = standard_error_msg + proxy_error_prompt + error_retrieve_prompt234        return chatbot, history, status_text, all_token_counts235    except requests.exceptions.SSLError:236        status_text = standard_error_msg + ssl_error_prompt + error_retrieve_prompt237        return chatbot, history, status_text, all_token_counts238    response = json.loads(response.text)239    if fake_input is not None:240        history[-2] = construct_user(fake_input)241    try:242        content = response["choices"][0]["message"]["content"]243        history[-1] = construct_assistant(content)244        chatbot[-1] = (chatbot[-1][0], content+display_append)245        total_token_count = response["usage"]["total_tokens"]246        if fake_input is not None:247            all_token_counts[-1] += count_token(construct_assistant(content))248        else:249            all_token_counts[-1] = total_token_count - sum(all_token_counts)250        status_text = construct_token_message(total_token_count)251        return chatbot, history, status_text, all_token_counts252    except KeyError:253        status_text = standard_error_msg + str(response)254        return chatbot, history, status_text, all_token_counts255 256def is_repeated_string(s):257    n = len(s)258    for i in range(1, n // 2 + 1):259        if n % i == 0:260            sub = s[:i]261            if sub * (n // i) == s:262                return True263    return False264 265def predict(266    openai_api_key,267    system_prompt,268    history,269    inputs,270    chatbot,271    all_token_counts,272    top_p,273    temperature,274    stream=False,275    selected_model=MODELS[0],276    use_websearch=False,277    files = None,278    reply_language="中文",279    should_check_token_count=True,280):  # repetition_penalty, top_k281    logging.info("输入为:" + colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL)282    if is_repeated_string(inputs):283        print("================== 有人来浪费了 ======================")284        yield chatbot+[(inputs, "🖕️🖕️🖕️🖕️🖕️看不起你")], history, "🖕️🖕️🖕️🖕️🖕️🖕️", all_token_counts285        return286    if should_check_token_count:287        yield chatbot+[(inputs, "")], history, "开始生成回答……", all_token_counts288    if reply_language == "跟随问题语言(不稳定)":289        reply_language = "the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch."290    old_inputs = None291    display_reference = []292    limited_context = False293    if files:294        limited_context = True295        old_inputs = inputs296        msg = "加载索引中……(这可能需要几分钟)"297        logging.info(msg)298        yield chatbot+[(inputs, "")], history, msg, all_token_counts299        index = construct_index(openai_api_key, file_src=files)300        msg = "索引构建完成,获取回答中……"301        logging.info(msg)302        yield chatbot+[(inputs, "")], history, msg, all_token_counts303        llm_predictor = LLMPredictor(llm=OpenAIChat(temperature=0, model_name=selected_model))304        prompt_helper = PromptHelper(max_input_size = 4096, num_output = 5, max_chunk_overlap = 20, chunk_size_limit=600)305        service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, prompt_helper=prompt_helper)306        query_object = GPTVectorStoreIndexQuery(index.index_struct, service_context=service_context, similarity_top_k=5, vector_store=index._vector_store, docstore=index._docstore)307        query_bundle = QueryBundle(inputs)308        nodes = query_object.retrieve(query_bundle)309        reference_results = [n.node.text for n in nodes]310        reference_results = add_source_numbers(reference_results, use_source=False)311        display_reference = add_details(reference_results)312        display_reference = "\n\n" + "".join(display_reference)313        inputs = (314            replace_today(PROMPT_TEMPLATE)315            .replace("{query_str}", inputs)316            .replace("{context_str}", "\n\n".join(reference_results))317            .replace("{reply_language}", reply_language )318        )319    elif use_websearch:320        limited_context = True321        search_results = ddg(inputs, max_results=5)322        old_inputs = inputs323        reference_results = []324        for idx, result in enumerate(search_results):325            logging.info(f"搜索结果{idx + 1}:{result}")326            domain_name = urllib3.util.parse_url(result["href"]).host327            reference_results.append([result["body"], result["href"]])328            display_reference.append(f"{idx+1}. [{domain_name}]({result['href']})\n")329        reference_results = add_source_numbers(reference_results)330        display_reference = "\n\n" + "".join(display_reference)331        inputs = (332            replace_today(WEBSEARCH_PTOMPT_TEMPLATE)333            .replace("{query}", inputs)334            .replace("{web_results}", "\n\n".join(reference_results))335            .replace("{reply_language}", reply_language )336        )337    else:338        display_reference = ""339 340    if len(openai_api_key) != 51:341        status_text = standard_error_msg + no_apikey_msg342        logging.info(status_text)343        chatbot.append((inputs, ""))344        if len(history) == 0:345            history.append(construct_user(inputs))346            history.append("")347            all_token_counts.append(0)348        else:349            history[-2] = construct_user(inputs)350        yield chatbot+[(inputs, "")], history, status_text, all_token_counts351        return352    elif len(inputs.strip()) == 0:353        status_text = standard_error_msg + no_input_msg354        logging.info(status_text)355        yield chatbot+[(inputs, "")], history, status_text, all_token_counts356        return357 358    if stream:359        logging.info("使用流式传输")360        iter = stream_predict(361            openai_api_key,362            system_prompt,363            history,364            inputs,365            chatbot,366            all_token_counts,367            top_p,368            temperature,369            selected_model,370            fake_input=old_inputs,371            display_append=display_reference372        )373        for chatbot, history, status_text, all_token_counts in iter:374            if shared.state.interrupted:375                shared.state.recover()376                return377            yield chatbot, history, status_text, all_token_counts378    else:379        logging.info("不使用流式传输")380        chatbot, history, status_text, all_token_counts = predict_all(381            openai_api_key,382            system_prompt,383            history,384            inputs,385            chatbot,386            all_token_counts,387            top_p,388            temperature,389            selected_model,390            fake_input=old_inputs,391            display_append=display_reference392        )393        yield chatbot, history, status_text, all_token_counts394 395    logging.info(f"传输完毕。当前token计数为{all_token_counts}")396    if len(history) > 1 and history[-1]["content"] != inputs:397        logging.info(398            "回答为:"399            + colorama.Fore.BLUE400            + f"{history[-1]['content']}"401            + colorama.Style.RESET_ALL402        )403 404    if limited_context:405        history = history[-4:]406        all_token_counts = all_token_counts[-2:]407        yield chatbot, history, status_text, all_token_counts408 409    if stream:410        max_token = MODEL_SOFT_TOKEN_LIMIT[selected_model]["streaming"]411    else:412        max_token = MODEL_SOFT_TOKEN_LIMIT[selected_model]["all"]413 414    if sum(all_token_counts) > max_token and should_check_token_count:415        status_text = f"精简token中{all_token_counts}/{max_token}"416        logging.info(status_text)417        yield chatbot, history, status_text, all_token_counts418        iter = reduce_token_size(419            openai_api_key,420            system_prompt,421            history,422            chatbot,423            all_token_counts,424            top_p,425            temperature,426            max_token//2,427            selected_model=selected_model,428        )429        for chatbot, history, status_text, all_token_counts in iter:430            status_text = f"Token 达到上限,已自动降低Token计数至 {status_text}"431            yield chatbot, history, status_text, all_token_counts432 433 434def retry(435    openai_api_key,436    system_prompt,437    history,438    chatbot,439    token_count,440    top_p,441    temperature,442    stream=False,443    selected_model=MODELS[0],444    reply_language="中文",445):446    logging.info("重试中……")447    if len(history) == 0:448        yield chatbot, history, f"{standard_error_msg}上下文是空的", token_count449        return450    history.pop()451    inputs = history.pop()["content"]452    token_count.pop()453    iter = predict(454        openai_api_key,455        system_prompt,456        history,457        inputs,458        chatbot,459        token_count,460        top_p,461        temperature,462        stream=stream,463        selected_model=selected_model,464        reply_language=reply_language,465    )466    logging.info("重试中……")467    for x in iter:468        yield x469    logging.info("重试完毕")470 471 472def reduce_token_size(473    openai_api_key,474    system_prompt,475    history,476    chatbot,477    token_count,478    top_p,479    temperature,480    max_token_count,481    selected_model=MODELS[0],482    reply_language="中文",483):484    logging.info("开始减少token数量……")485    iter = predict(486        openai_api_key,487        system_prompt,488        history,489        summarize_prompt,490        chatbot,491        token_count,492        top_p,493        temperature,494        selected_model=selected_model,495        should_check_token_count=False,496        reply_language=reply_language,497    )498    logging.info(f"chatbot: {chatbot}")499    flag = False500    for chatbot, history, status_text, previous_token_count in iter:501        num_chat = find_n(previous_token_count, max_token_count)502        logging.info(f"previous_token_count: {previous_token_count}, keeping {num_chat} chats")503        if flag:504            chatbot = chatbot[:-1]505        flag = True506        history = history[-2*num_chat:] if num_chat > 0 else []507        token_count = previous_token_count[-num_chat:] if num_chat > 0 else []508        msg = f"保留了最近{num_chat}轮对话"509        yield chatbot, history, msg + "," + construct_token_message(510            sum(token_count) if len(token_count) > 0 else 0,511        ), token_count512    logging.info(msg)513    logging.info("减少token数量完毕")514