DragonCard/ChuanhuChatGPT
0
1# -*- coding:utf-8 -*-2from __future__ import annotations3from typing import TYPE_CHECKING, List4 5import logging6import json7import os8import requests9 10from tqdm import tqdm11import colorama12from duckduckgo_search import ddg13 14from presets import *15from llama_func import *16from utils import *17 18# logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s")19 20if TYPE_CHECKING:21 from typing import TypedDict22 23 class DataframeData(TypedDict):24 headers: List[str]25 data: List[List[str | int | bool]]26 27 28initial_prompt = "You are a helpful assistant."29API_URL = "https://api.openai.com/v1/chat/completions"30HISTORY_DIR = "history"31TEMPLATES_DIR = "templates"32 33def get_response(34 openai_api_key, system_prompt, history, temperature, top_p, stream, selected_model35):36 headers = {37 "Content-Type": "application/json",38 "Authorization": f"Bearer {openai_api_key}",39 }40 41 history = [construct_system(system_prompt), *history]42 43 payload = {44 "model": selected_model,45 "messages": history, # [{"role": "user", "content": f"{inputs}"}],46 "temperature": temperature, # 1.0,47 "top_p": top_p, # 1.0,48 "n": 1,49 "stream": stream,50 "presence_penalty": 0,51 "frequency_penalty": 0,52 }53 if stream:54 timeout = timeout_streaming55 else:56 timeout = timeout_all57 58 # 获取环境变量中的代理设置59 http_proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")60 https_proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")61 62 # 如果存在代理设置,使用它们63 proxies = {}64 if http_proxy:65 logging.info(f"Using HTTP proxy: {http_proxy}")66 proxies["http"] = http_proxy67 if https_proxy:68 logging.info(f"Using HTTPS proxy: {https_proxy}")69 proxies["https"] = https_proxy70 71 # 如果有代理,使用代理发送请求,否则使用默认设置发送请求72 if proxies:73 response = requests.post(74 API_URL,75 headers=headers,76 json=payload,77 stream=True,78 timeout=timeout,79 proxies=proxies,80 )81 else:82 response = requests.post(83 API_URL,84 headers=headers,85 json=payload,86 stream=True,87 timeout=timeout,88 )89 return response90 91 92def stream_predict(93 openai_api_key,94 system_prompt,95 history,96 inputs,97 chatbot,98 all_token_counts,99 top_p,100 temperature,101 selected_model,102):103 def get_return_value():104 return chatbot, history, status_text, all_token_counts105 106 logging.info("实时回答模式")107 partial_words = ""108 counter = 0109 status_text = "开始实时传输回答……"110 history.append(construct_user(inputs))111 history.append(construct_assistant(""))112 chatbot.append((parse_text(inputs), ""))113 user_token_count = 0114 if len(all_token_counts) == 0:115 system_prompt_token_count = count_token(construct_system(system_prompt))116 user_token_count = (117 count_token(construct_user(inputs)) + system_prompt_token_count118 )119 else:120 user_token_count = count_token(construct_user(inputs))121 all_token_counts.append(user_token_count)122 logging.info(f"输入token计数: {user_token_count}")123 yield get_return_value()124 try:125 response = get_response(126 openai_api_key,127 system_prompt,128 history,129 temperature,130 top_p,131 True,132 selected_model,133 )134 except requests.exceptions.ConnectTimeout:135 status_text = (136 standard_error_msg + connection_timeout_prompt + error_retrieve_prompt137 )138 yield get_return_value()139 return140 except requests.exceptions.ReadTimeout:141 status_text = standard_error_msg + read_timeout_prompt + error_retrieve_prompt142 yield get_return_value()143 return144 145 yield get_return_value()146 error_json_str = ""147 148 for chunk in tqdm(response.iter_lines()):149 if counter == 0:150 counter += 1151 continue152 counter += 1153 # check whether each line is non-empty154 if chunk:155 chunk = chunk.decode()156 chunklength = len(chunk)157 try:158 chunk = json.loads(chunk[6:])159 except json.JSONDecodeError:160 logging.info(chunk)161 error_json_str += chunk162 status_text = f"JSON解析错误。请重置对话。收到的内容: {error_json_str}"163 yield get_return_value()164 continue165 # decode each line as response data is in bytes166 if chunklength > 6 and "delta" in chunk["choices"][0]:167 finish_reason = chunk["choices"][0]["finish_reason"]168 status_text = construct_token_message(169 sum(all_token_counts), stream=True170 )171 if finish_reason == "stop":172 yield get_return_value()173 break174 try:175 partial_words = (176 partial_words + chunk["choices"][0]["delta"]["content"]177 )178 except KeyError:179 status_text = (180 standard_error_msg181 + "API回复中找不到内容。很可能是Token计数达到上限了。请重置对话。当前Token计数: "182 + str(sum(all_token_counts))183 )184 yield get_return_value()185 break186 history[-1] = construct_assistant(partial_words)187 chatbot[-1] = (parse_text(inputs), parse_text(partial_words))188 all_token_counts[-1] += 1189 yield get_return_value()190 191 192def predict_all(193 openai_api_key,194 system_prompt,195 history,196 inputs,197 chatbot,198 all_token_counts,199 top_p,200 temperature,201 selected_model,202):203 logging.info("一次性回答模式")204 history.append(construct_user(inputs))205 history.append(construct_assistant(""))206 chatbot.append((parse_text(inputs), ""))207 all_token_counts.append(count_token(construct_user(inputs)))208 try:209 response = get_response(210 openai_api_key,211 system_prompt,212 history,213 temperature,214 top_p,215 False,216 selected_model,217 )218 except requests.exceptions.ConnectTimeout:219 status_text = (220 standard_error_msg + connection_timeout_prompt + error_retrieve_prompt221 )222 return chatbot, history, status_text, all_token_counts223 except requests.exceptions.ProxyError:224 status_text = standard_error_msg + proxy_error_prompt + error_retrieve_prompt225 return chatbot, history, status_text, all_token_counts226 except requests.exceptions.SSLError:227 status_text = standard_error_msg + ssl_error_prompt + error_retrieve_prompt228 return chatbot, history, status_text, all_token_counts229 response = json.loads(response.text)230 content = response["choices"][0]["message"]["content"]231 history[-1] = construct_assistant(content)232 chatbot[-1] = (parse_text(inputs), parse_text(content))233 total_token_count = response["usage"]["total_tokens"]234 all_token_counts[-1] = total_token_count - sum(all_token_counts)235 status_text = construct_token_message(total_token_count)236 return chatbot, history, status_text, all_token_counts237 238 239def predict(240 openai_api_key,241 system_prompt,242 history,243 inputs,244 chatbot,245 all_token_counts,246 top_p,247 temperature,248 stream=False,249 selected_model=MODELS[0],250 use_websearch_checkbox=False,251 files = None,252 should_check_token_count=True,253): # repetition_penalty, top_k254 logging.info("输入为:" + colorama.Fore.BLUE + f"{inputs}" + colorama.Style.RESET_ALL)255 if files:256 msg = "构建索引中……(这可能需要比较久的时间)"257 logging.info(msg)258 yield chatbot, history, msg, all_token_counts259 index = construct_index(openai_api_key, file_src=files)260 msg = "索引构建完成,获取回答中……"261 yield chatbot, history, msg, all_token_counts262 history, chatbot, status_text = chat_ai(openai_api_key, index, inputs, history, chatbot)263 yield chatbot, history, status_text, all_token_counts264 return265 if use_websearch_checkbox:266 results = ddg(inputs, max_results=3)267 web_results = []268 for idx, result in enumerate(results):269 logging.info(f"搜索结果{idx + 1}:{result}")270 web_results.append(f'[{idx+1}]"{result["body"]}"\nURL: {result["href"]}')271 web_results = "\n\n".join(web_results)272 inputs = (273 replace_today(WEBSEARCH_PTOMPT_TEMPLATE)274 .replace("{query}", inputs)275 .replace("{web_results}", web_results)276 )277 if len(openai_api_key) != 51:278 status_text = standard_error_msg + no_apikey_msg279 logging.info(status_text)280 chatbot.append((parse_text(inputs), ""))281 if len(history) == 0:282 history.append(construct_user(inputs))283 history.append("")284 all_token_counts.append(0)285 else:286 history[-2] = construct_user(inputs)287 yield chatbot, history, status_text, all_token_counts288 return289 if stream:290 yield chatbot, history, "开始生成回答……", all_token_counts291 if stream:292 logging.info("使用流式传输")293 iter = stream_predict(294 openai_api_key,295 system_prompt,296 history,297 inputs,298 chatbot,299 all_token_counts,300 top_p,301 temperature,302 selected_model,303 )304 for chatbot, history, status_text, all_token_counts in iter:305 yield chatbot, history, status_text, all_token_counts306 else:307 logging.info("不使用流式传输")308 chatbot, history, status_text, all_token_counts = predict_all(309 openai_api_key,310 system_prompt,311 history,312 inputs,313 chatbot,314 all_token_counts,315 top_p,316 temperature,317 selected_model,318 )319 yield chatbot, history, status_text, all_token_counts320 logging.info(f"传输完毕。当前token计数为{all_token_counts}")321 if len(history) > 1 and history[-1]["content"] != inputs:322 logging.info(323 "回答为:"324 + colorama.Fore.BLUE325 + f"{history[-1]['content']}"326 + colorama.Style.RESET_ALL327 )328 if stream:329 max_token = max_token_streaming330 else:331 max_token = max_token_all332 if sum(all_token_counts) > max_token and should_check_token_count:333 status_text = f"精简token中{all_token_counts}/{max_token}"334 logging.info(status_text)335 yield chatbot, history, status_text, all_token_counts336 iter = reduce_token_size(337 openai_api_key,338 system_prompt,339 history,340 chatbot,341 all_token_counts,342 top_p,343 temperature,344 stream=False,345 selected_model=selected_model,346 hidden=True,347 )348 for chatbot, history, status_text, all_token_counts in iter:349 status_text = f"Token 达到上限,已自动降低Token计数至 {status_text}"350 yield chatbot, history, status_text, all_token_counts351 352 353def retry(354 openai_api_key,355 system_prompt,356 history,357 chatbot,358 token_count,359 top_p,360 temperature,361 stream=False,362 selected_model=MODELS[0],363):364 logging.info("重试中……")365 if len(history) == 0:366 yield chatbot, history, f"{standard_error_msg}上下文是空的", token_count367 return368 history.pop()369 inputs = history.pop()["content"]370 token_count.pop()371 iter = predict(372 openai_api_key,373 system_prompt,374 history,375 inputs,376 chatbot,377 token_count,378 top_p,379 temperature,380 stream=stream,381 selected_model=selected_model,382 )383 logging.info("重试完毕")384 for x in iter:385 yield x386 387 388def reduce_token_size(389 openai_api_key,390 system_prompt,391 history,392 chatbot,393 token_count,394 top_p,395 temperature,396 stream=False,397 selected_model=MODELS[0],398 hidden=False,399):400 logging.info("开始减少token数量……")401 iter = predict(402 openai_api_key,403 system_prompt,404 history,405 summarize_prompt,406 chatbot,407 token_count,408 top_p,409 temperature,410 stream=stream,411 selected_model=selected_model,412 should_check_token_count=False,413 )414 logging.info(f"chatbot: {chatbot}")415 for chatbot, history, status_text, previous_token_count in iter:416 history = history[-2:]417 token_count = previous_token_count[-1:]418 if hidden:419 chatbot.pop()420 yield chatbot, history, construct_token_message(421 sum(token_count), stream=stream422 ), token_count423 logging.info("减少token数量完毕")