{"repo": "b-lala/gpt-v1.0", "files": {"toolbox.py": {"cf3f571b334e0f6d47a3d7ad9522a38c39d966cc": {"t": 1785768952.798555, "n": 46921, "h": "ce3433cbb5a987ca", "c": "import markdown\nimport importlib\nimport time\nimport inspect\nimport re\nimport os\nimport base64\nimport gradio\nimport shutil\nimport glob\nimport math\nfrom latex2mathml.converter import convert as tex2mathml\nfrom functools import wraps, lru_cache\npj = os.path.join\ndefault_user_name = 'default_user'\n\"\"\"\n========================================================================\n第一部分\n函数插件输入输出接驳区\n - ChatBotWithCookies: 带Cookies的Chatbot类,为实现更多强大的功能做基础\n - ArgsGeneralWrapper: 装饰器函数,用于重组输入参数,改变输入参数的顺序与结构\n - update_ui: 刷新界面用 yield from update_ui(chatbot, history)\n - CatchException: 将插件中出的所有问题显示在界面上\n - HotReload: 实现插件的热更新\n - trimmed_format_exc: 打印traceback,为了安全而隐藏绝对地址\n========================================================================\n\"\"\"\n\nclass ChatBotWithCookies(list):\n def __init__(self, cookie):\n \"\"\"\n cookies = {\n 'top_p': top_p,\n 'temperature': temperature,\n 'lock_plugin': bool,\n \"files_to_promote\": [\"file1\", \"file2\"],\n \"most_recent_uploaded\": {\n \"path\": \"uploaded_path\",\n \"time\": time.time(),\n \"time_str\": \"timestr\",\n }\n }\n \"\"\"\n self._cookies = cookie\n\n def write_list(self, list):\n for t in list:\n self.append(t)\n\n def get_list(self):\n return [t for t in self]\n\n def get_cookies(self):\n return self._cookies\n\n\ndef ArgsGeneralWrapper(f):\n \"\"\"\n 装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。\n \"\"\"\n def decorated(request: gradio.Request, cookies, max_length, llm_model, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg, *args):\n txt_passon = txt\n if txt == \"\" and txt2 != \"\": txt_passon = txt2\n # 引入一个有cookie的chatbot\n if request.username is not None:\n user_name = request.username\n else:\n user_name = default_user_name\n cookies.update({\n 'top_p':top_p,\n 'api_key': cookies['api_key'],\n 'llm_model': llm_model,\n 'temperature':temperature,\n 'user_name': user_name,\n })\n llm_kwargs = {\n 'api_key': cookies['api_key'],\n 'llm_model': llm_model,\n 'top_p':top_p,\n 'max_length': max_length,\n 'temperature':temperature,\n 'client_ip': request.client.host,\n 'most_recent_uploaded': cookies.get('most_recent_uploaded')\n }\n plugin_kwargs = {\n \"advanced_arg\": plugin_advanced_arg,\n }\n chatbot_with_cookie = ChatBotWithCookies(cookies)\n chatbot_with_cookie.write_list(chatbot)\n \n if cookies.get('lock_plugin', None) is None:\n # 正常状态\n if len(args) == 0: # 插件通道\n yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, request)\n else: # 对话通道,或者基础功能通道\n yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args)\n else:\n # 处理少数情况下的特殊插件的锁定状态\n module, fn_name = cookies['lock_plugin'].split('->')\n f_hot_reload = getattr(importlib.import_module(module, fn_name), fn_name)\n yield from f_hot_reload(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, request)\n # 判断一下用户是否错误地通过对话通道进入,如果是,则进行提醒\n final_cookies = chatbot_with_cookie.get_cookies()\n # len(args) != 0 代表“提交”键对话通道,或者基础功能通道\n if len(args) != 0 and 'files_to_promote' in final_cookies and len(final_cookies['files_to_promote']) > 0:\n chatbot_with_cookie.append([\"检测到**滞留的缓存文档**,请及时处理。\", \"请及时点击“**保存当前对话**”获取所有滞留文档。\"])\n yield from update_ui(chatbot_with_cookie, final_cookies['history'], msg=\"检测到被滞留的缓存文档\")\n return decorated\n\n\ndef update_ui(chatbot, history, msg='正常', **kwargs): # 刷新界面\n \"\"\"\n 刷新用户界面\n \"\"\"\n assert isinstance(chatbot, ChatBotWithCookies), \"在传递chatbot的过程中不要将其丢弃。必要时, 可用clear将其清空, 然后用for+append循环重新赋值。\"\n cookies = chatbot.get_cookies()\n # 备份一份History作为记录\n cookies.update({'history': history})\n # 解决插件锁定时的界面显示问题\n if cookies.get('lock_plugin', None):\n label = cookies.get('llm_model', \"\") + \" | \" + \"正在锁定插件\" + cookies.get('lock_plugin', None)\n chatbot_gr = gradio.update(value=chatbot, label=label)\n if cookies.get('label', \"\") != label: cookies['label'] = label # 记住当前的label\n elif cookies.get('label', None):\n chatbot_gr = gradio.update(value=chatbot, label=cookies.get('llm_model', \"\"))\n cookies['label'] = None # 清空label\n else:\n chatbot_gr = chatbot\n\n yield cookies, chatbot_gr, history, msg\n\ndef update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面\n \"\"\"\n 刷新用户界面\n \"\"\"\n if len(chatbot) == 0: chatbot.append([\"update_ui_last_msg\", lastmsg])\n chatbot[-1] = list(chatbot[-1])\n chatbot[-1][-1] = lastmsg\n yield from update_ui(chatbot=chatbot, history=history)\n time.sleep(delay)\n\n\ndef trimmed_format_exc():\n import os, traceback\n str = traceback.format_exc()\n current_path = os.getcwd()\n replace_path = \".\"\n return str.replace(current_path, replace_path)\n\ndef CatchException(f):\n \"\"\"\n 装饰器函数,捕捉函数f中的异常并封装到一个生成器中返回,并显示到聊天当中。\n \"\"\"\n\n @wraps(f)\n def decorated(main_input, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, *args, **kwargs):\n try:\n yield from f(main_input, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, *args, **kwargs)\n except Exception as e:\n from check_proxy import check_proxy\n from toolbox import get_conf\n proxies = get_conf('proxies')\n tb_str = '```\\n' + trimmed_format_exc() + '```'\n if len(chatbot_with_cookie) == 0:\n chatbot_with_cookie.clear()\n chatbot_with_cookie.append([\"插件调度异常\", \"异常原因\"])\n chatbot_with_cookie[-1] = (chatbot_with_cookie[-1][0],\n f\"[Local Message] 插件调用出错: \\n\\n{tb_str} \\n\\n当前代理可用性: \\n\\n{check_proxy(proxies)}\")\n yield from update_ui(chatbot=chatbot_with_cookie, history=history, msg=f'异常 {e}') # 刷新界面\n return decorated\n\n\ndef HotReload(f):\n \"\"\"\n HotReload的装饰器函数,用于实现Python函数插件的热更新。\n 函数热更新是指在不停止程序运行的情况下,更新函数代码,从而达到实时更新功能。\n 在装饰器内部,使用wraps(f)来保留函数的元信息,并定义了一个名为decorated的内部函数。\n 内部函数通过使用importlib模块的reload函数和inspect模块的getmodule函数来重新加载并获取函数模块,\n 然后通过getattr函数获取函数名,并在新模块中重新加载函数。\n 最后,使用yield from语句返回重新加载过的函数,并在被装饰的函数上执行。\n 最终,装饰器函数返回内部函数。这个内部函数可以将函数的原始定义更新为最新版本,并执行函数的新版本。\n \"\"\"\n if get_conf('PLUGIN_HOT_RELOAD'):\n @wraps(f)\n def decorated(*args, **kwargs):\n fn_name = f.__name__\n f_hot_reload = getattr(importlib.reload(inspect.getmodule(f)), fn_name)\n yield from f_hot_reload(*args, **kwargs)\n return decorated\n else:\n return f\n\n\n\"\"\"\n========================================================================\n第二部分\n其他小工具:\n - write_history_to_file: 将结果写入markdown文件中\n - regular_txt_to_markdown: 将普通文本转换为Markdown格式的文本。\n - report_exception: 向chatbot中添加简单的意外错误信息\n - text_divide_paragraph: 将文本按照段落分隔符分割开,生成带有段落标签的HTML代码。\n - markdown_convertion: 用多种方式组合,将markdown转化为好看的html\n - format_io: 接管gradio默认的markdown处理方式\n - on_file_uploaded: 处理文件的上传(自动解压)\n - on_report_generated: 将生成的报告自动投射到文件上传区\n - clip_history: 当历史上下文过长时,自动截断\n - get_conf: 获取设置\n - select_api_key: 根据当前的模型类别,抽取可用的api-key\n========================================================================\n\"\"\"\n\ndef get_reduce_token_percent(text):\n \"\"\"\n * 此函数未来将被弃用\n \"\"\"\n try:\n # text = \"maximum context length is 4097 tokens. However, your messages resulted in 4870 tokens\"\n pattern = r\"(\\d+)\\s+tokens\\b\"\n match = re.findall(pattern, text)\n EXCEED_ALLO = 500 # 稍微留一点余地,否则在回复时会因余量太少出问题\n max_limit = float(match[0]) - EXCEED_ALLO\n current_tokens = float(match[1])\n ratio = max_limit/current_tokens\n assert ratio > 0 and ratio < 1\n return ratio, str(int(current_tokens-max_limit))\n except:\n return 0.5, '不详'\n\n\ndef write_history_to_file(history, file_basename=None, file_fullname=None, auto_caption=True):\n \"\"\"\n 将对话记录history以Markdown格式写入文件中。如果没有指定文件名,则使用当前时间生成文件名。\n \"\"\"\n import os\n import time\n if file_fullname is None:\n if file_basename is not None:\n file_fullname = pj(get_log_folder(), file_basename)\n else:\n file_fullname = pj(get_log_folder(), f'GPT-Academic-{gen_time_str()}.md')\n os.makedirs(os.path.dirname(file_fullname), exist_ok=True)\n with open(file_fullname, 'w', encoding='utf8') as f:\n f.write('# GPT-Academic Report\\n')\n for i, content in enumerate(history):\n try: \n if type(content) != str: content = str(content)\n except:\n continue\n if i % 2 == 0 and auto_caption:\n f.write('## ')\n try:\n f.write(content)\n except:\n # remove everything that cannot be handled by utf8\n f.write(content.encode('utf-8', 'ignore').decode())\n f.write('\\n\\n')\n res = os.path.abspath(file_fullname)\n return res\n\n\ndef regular_txt_to_markdown(text):\n \"\"\"\n 将普通文本转换为Markdown格式的文本。\n \"\"\"\n text = text.replace('\\n', '\\n\\n')\n text = text.replace('\\n\\n\\n', '\\n\\n')\n text = text.replace('\\n\\n\\n', '\\n\\n')\n return text\n\n\n\n\ndef report_exception(chatbot, history, a, b):\n \"\"\"\n 向chatbot中添加错误信息\n \"\"\"\n chatbot.append((a, b))\n history.extend([a, b])\n\n\ndef text_divide_paragraph(text):\n \"\"\"\n 将文本按照段落分隔符分割开,生成带有段落标签的HTML代码。\n \"\"\"\n pre = '
'\n suf = '
'\n if text.startswith(pre) and text.endswith(suf):\n return text\n \n if '```' in text:\n # careful input\n return text\n elif '' in text:\n # careful input\n return text\n else:\n # whatever input\n lines = text.split(\"\\n\")\n for i, line in enumerate(lines):\n lines[i] = lines[i].replace(\" \", \" \")\n text = \"
\".join(lines)\n return pre + text + suf\n\n\n@lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度\ndef markdown_convertion(txt):\n \"\"\"\n 将Markdown格式的文本转换为HTML格式。如果包含数学公式,则先将公式转换为HTML格式。\n \"\"\"\n pre = '
'\n suf = '
'\n if txt.startswith(pre) and txt.endswith(suf):\n # print('警告,输入了已经经过转化的字符串,二次转化可能出问题')\n return txt # 已经被转化过,不需要再次转化\n \n markdown_extension_configs = {\n 'mdx_math': {\n 'enable_dollar_delimiter': True,\n 'use_gitlab_delimiters': False,\n },\n }\n find_equation_pattern = r''\n\n def tex2mathml_catch_exception(content, *args, **kwargs):\n try:\n content = tex2mathml(content, *args, **kwargs)\n except:\n content = content\n return content\n\n def replace_math_no_render(match):\n content = match.group(1)\n if 'mode=display' in match.group(0):\n content = content.replace('\\n', '
')\n return f\"$${content}$$\"\n else:\n return f\"${content}$\"\n\n def replace_math_render(match):\n content = match.group(1)\n if 'mode=display' in match.group(0):\n if '\\\\begin{aligned}' in content:\n content = content.replace('\\\\begin{aligned}', '\\\\begin{array}')\n content = content.replace('\\\\end{aligned}', '\\\\end{array}')\n content = content.replace('&', ' ')\n content = tex2mathml_catch_exception(content, display=\"block\")\n return content\n else:\n return tex2mathml_catch_exception(content)\n\n def markdown_bug_hunt(content):\n \"\"\"\n 解决一个mdx_math的bug(单$包裹begin命令时多余\\n', '')\n return content\n\n def is_equation(txt):\n \"\"\"\n 判定是否为公式 | 测试1 写出洛伦兹定律,使用tex格式公式 测试2 给出柯西不等式,使用latex格式 测试3 写出麦克斯韦方程组\n \"\"\"\n if '```' in txt and '```reference' not in txt: return False\n if '$' not in txt and '\\\\[' not in txt: return False\n mathpatterns = {\n r'(?= one_minute_ago:\n if os.path.isdir(file_path):\n continue\n recent_files.append(file_path)\n\n return recent_files\n\n\ndef file_already_in_downloadzone(file, user_path):\n try:\n parent_path = os.path.abspath(user_path)\n child_path = os.path.abspath(file)\n if os.path.samefile(os.path.commonpath([parent_path, child_path]), parent_path):\n return True\n else:\n return False\n except:\n return False\n\ndef promote_file_to_downloadzone(file, rename_file=None, chatbot=None):\n # 将文件复制一份到下载区\n import shutil\n if chatbot is not None:\n user_name = get_user(chatbot)\n else:\n user_name = default_user_name\n if not os.path.exists(file):\n raise FileNotFoundError(f'文件{file}不存在')\n user_path = get_log_folder(user_name, plugin_name=None)\n if file_already_in_downloadzone(file, user_path):\n new_path = file\n else:\n user_path = get_log_folder(user_name, plugin_name='downloadzone')\n if rename_file is None: rename_file = f'{gen_time_str()}-{os.path.basename(file)}'\n new_path = pj(user_path, rename_file)\n # 如果已经存在,先删除\n if os.path.exists(new_path) and not os.path.samefile(new_path, file): os.remove(new_path)\n # 把文件复制过去\n if not os.path.exists(new_path): shutil.copyfile(file, new_path)\n # 将文件添加到chatbot cookie中\n if chatbot is not None:\n if 'files_to_promote' in chatbot._cookies: current = chatbot._cookies['files_to_promote']\n else: current = []\n chatbot._cookies.update({'files_to_promote': [new_path] + current})\n return new_path\n\n\ndef disable_auto_promotion(chatbot):\n chatbot._cookies.update({'files_to_promote': []})\n return\n\n\ndef del_outdated_uploads(outdate_time_seconds, target_path_base=None):\n if target_path_base is None:\n user_upload_dir = get_conf('PATH_PRIVATE_UPLOAD')\n else:\n user_upload_dir = target_path_base\n current_time = time.time()\n one_hour_ago = current_time - outdate_time_seconds\n # Get a list of all subdirectories in the user_upload_dir folder\n # Remove subdirectories that are older than one hour\n for subdirectory in glob.glob(f'{user_upload_dir}/*'):\n subdirectory_time = os.path.getmtime(subdirectory)\n if subdirectory_time < one_hour_ago:\n try: shutil.rmtree(subdirectory)\n except: pass\n return\n\n\ndef html_local_file(file):\n base_path = os.path.dirname(__file__) # 项目目录\n if os.path.exists(str(file)):\n file = f'file={file.replace(base_path, \".\")}'\n return file\n\n\ndef html_local_img(__file, layout='left', max_width=None, max_height=None, md=True):\n style = ''\n if max_width is not None:\n style += f\"max-width: {max_width};\"\n if max_height is not None:\n style += f\"max-height: {max_height};\"\n __file = html_local_file(__file)\n a = f'
'\n if md:\n a = f'![{__file}]({__file})'\n return a\n\ndef file_manifest_filter_type(file_list, filter_: list = None):\n new_list = []\n if not filter_: filter_ = ['png', 'jpg', 'jpeg']\n for file in file_list:\n if str(os.path.basename(file)).split('.')[-1] in filter_:\n new_list.append(html_local_img(file, md=False))\n else:\n new_list.append(file)\n return new_list\n\ndef to_markdown_tabs(head: list, tabs: list, alignment=':---:', column=False):\n \"\"\"\n Args:\n head: 表头:[]\n tabs: 表值:[[列1], [列2], [列3], [列4]]\n alignment: :--- 左对齐, :---: 居中对齐, ---: 右对齐\n column: True to keep data in columns, False to keep data in rows (default).\n Returns:\n A string representation of the markdown table.\n \"\"\"\n if column:\n transposed_tabs = list(map(list, zip(*tabs)))\n else:\n transposed_tabs = tabs\n # Find the maximum length among the columns\n max_len = max(len(column) for column in transposed_tabs)\n\n tab_format = \"| %s \"\n tabs_list = \"\".join([tab_format % i for i in head]) + '|\\n'\n tabs_list += \"\".join([tab_format % alignment for i in head]) + '|\\n'\n\n for i in range(max_len):\n row_data = [tab[i] if i < len(tab) else '' for tab in transposed_tabs]\n row_data = file_manifest_filter_type(row_data, filter_=None)\n tabs_list += \"\".join([tab_format % i for i in row_data]) + '|\\n'\n\n return tabs_list\n\ndef on_file_uploaded(request: gradio.Request, files, chatbot, txt, txt2, checkboxes, cookies):\n \"\"\"\n 当文件被上传时的回调函数\n \"\"\"\n if len(files) == 0:\n return chatbot, txt\n\n # 创建工作路径\n user_name = default_user_name if not request.username else request.username\n time_tag = gen_time_str()\n target_path_base = get_upload_folder(user_name, tag=time_tag)\n os.makedirs(target_path_base, exist_ok=True)\n \n # 移除过时的旧文件从而节省空间&保护隐私\n outdate_time_seconds = 3600 # 一小时\n del_outdated_uploads(outdate_time_seconds, get_upload_folder(user_name))\n\n # 逐个文件转移到目标路径\n upload_msg = ''\n for file in files:\n file_origin_name = os.path.basename(file.orig_name)\n this_file_path = pj(target_path_base, file_origin_name)\n shutil.move(file.name, this_file_path)\n upload_msg += extract_archive(file_path=this_file_path, dest_dir=this_file_path+'.extract')\n\n if \"浮动输入区\" in checkboxes: \n txt, txt2 = \"\", target_path_base\n else:\n txt, txt2 = target_path_base, \"\"\n\n # 整理文件集合 输出消息\n moved_files = [fp for fp in glob.glob(f'{target_path_base}/**/*', recursive=True)]\n moved_files_str = to_markdown_tabs(head=['文件'], tabs=[moved_files])\n chatbot.append(['我上传了文件,请查收', \n f'[Local Message] 收到以下文件: \\n\\n{moved_files_str}' +\n f'\\n\\n调用路径参数已自动修正到: \\n\\n{txt}' +\n f'\\n\\n现在您点击任意函数插件时,以上文件将被作为输入参数'+upload_msg])\n \n # 记录近期文件\n cookies.update({\n 'most_recent_uploaded': {\n 'path': target_path_base,\n 'time': time.time(),\n 'time_str': time_tag\n }})\n return chatbot, txt, txt2, cookies\n\n\ndef on_report_generated(cookies, files, chatbot):\n # from toolbox import find_recent_files\n # PATH_LOGGING = get_conf('PATH_LOGGING')\n if 'files_to_promote' in cookies:\n report_files = cookies['files_to_promote']\n cookies.pop('files_to_promote')\n else:\n report_files = []\n # report_files = find_recent_files(PATH_LOGGING)\n if len(report_files) == 0:\n return cookies, None, chatbot\n # files.extend(report_files)\n file_links = ''\n for f in report_files: file_links += f'
{f}'\n chatbot.append(['报告如何远程获取?', f'报告已经添加到右侧“文件上传区”(可能处于折叠状态),请查收。{file_links}'])\n return cookies, report_files, chatbot\n\ndef load_chat_cookies():\n API_KEY, LLM_MODEL, AZURE_API_KEY = get_conf('API_KEY', 'LLM_MODEL', 'AZURE_API_KEY')\n AZURE_CFG_ARRAY, NUM_CUSTOM_BASIC_BTN = get_conf('AZURE_CFG_ARRAY', 'NUM_CUSTOM_BASIC_BTN')\n\n # deal with azure openai key\n if is_any_api_key(AZURE_API_KEY):\n if is_any_api_key(API_KEY): API_KEY = API_KEY + ',' + AZURE_API_KEY\n else: API_KEY = AZURE_API_KEY\n if len(AZURE_CFG_ARRAY) > 0:\n for azure_model_name, azure_cfg_dict in AZURE_CFG_ARRAY.items():\n if not azure_model_name.startswith('azure'): \n raise ValueError(\"AZURE_CFG_ARRAY中配置的模型必须以azure开头\")\n AZURE_API_KEY_ = azure_cfg_dict[\"AZURE_API_KEY\"]\n if is_any_api_key(AZURE_API_KEY_):\n if is_any_api_key(API_KEY): API_KEY = API_KEY + ',' + AZURE_API_KEY_\n else: API_KEY = AZURE_API_KEY_\n\n customize_fn_overwrite_ = {}\n for k in range(NUM_CUSTOM_BASIC_BTN):\n customize_fn_overwrite_.update({ \n \"自定义按钮\" + str(k+1):{\n \"Title\": r\"\",\n \"Prefix\": r\"请在自定义菜单中定义提示词前缀.\",\n \"Suffix\": r\"请在自定义菜单中定义提示词后缀\",\n }\n })\n return {'api_key': API_KEY, 'llm_model': LLM_MODEL, 'customize_fn_overwrite': customize_fn_overwrite_}\n\ndef is_openai_api_key(key):\n CUSTOM_API_KEY_PATTERN = get_conf('CUSTOM_API_KEY_PATTERN')\n if len(CUSTOM_API_KEY_PATTERN) != 0:\n API_MATCH_ORIGINAL = re.match(CUSTOM_API_KEY_PATTERN, key)\n else:\n API_MATCH_ORIGINAL = re.match(r\"sk-[a-zA-Z0-9]{48}$\", key)\n return bool(API_MATCH_ORIGINAL)\n\ndef is_azure_api_key(key):\n API_MATCH_AZURE = re.match(r\"[a-zA-Z0-9]{32}$\", key)\n return bool(API_MATCH_AZURE)\n\ndef is_api2d_key(key):\n API_MATCH_API2D = re.match(r\"fk[a-zA-Z0-9]{6}-[a-zA-Z0-9]{32}$\", key)\n return bool(API_MATCH_API2D)\n\ndef is_any_api_key(key):\n if ',' in key:\n keys = key.split(',')\n for k in keys:\n if is_any_api_key(k): return True\n return False\n else:\n return is_openai_api_key(key) or is_api2d_key(key) or is_azure_api_key(key)\n\ndef what_keys(keys):\n avail_key_list = {'OpenAI Key':0, \"Azure Key\":0, \"API2D Key\":0}\n key_list = keys.split(',')\n\n for k in key_list:\n if is_openai_api_key(k): \n avail_key_list['OpenAI Key'] += 1\n\n for k in key_list:\n if is_api2d_key(k): \n avail_key_list['API2D Key'] += 1\n\n for k in key_list:\n if is_azure_api_key(k): \n avail_key_list['Azure Key'] += 1\n\n return f\"检测到: OpenAI Key {avail_key_list['OpenAI Key']} 个, Azure Key {avail_key_list['Azure Key']} 个, API2D Key {avail_key_list['API2D Key']} 个\"\n\ndef select_api_key(keys, llm_model):\n import random\n avail_key_list = []\n key_list = keys.split(',')\n\n if llm_model.startswith('gpt-'):\n for k in key_list:\n if is_openai_api_key(k): avail_key_list.append(k)\n\n if llm_model.startswith('api2d-'):\n for k in key_list:\n if is_api2d_key(k): avail_key_list.append(k)\n\n if llm_model.startswith('azure-'):\n for k in key_list:\n if is_azure_api_key(k): avail_key_list.append(k)\n\n if len(avail_key_list) == 0:\n raise RuntimeError(f\"您提供的api-key不满足要求,不包含任何可用于{llm_model}的api-key。您可能选择了错误的模型或请求源(右下角更换模型菜单中可切换openai,azure,claude,api2d等请求源)。\")\n\n api_key = random.choice(avail_key_list) # 随机负载均衡\n return api_key\n\ndef read_env_variable(arg, default_value):\n \"\"\"\n 环境变量可以是 `GPT_ACADEMIC_CONFIG`(优先),也可以直接是`CONFIG`\n 例如在windows cmd中,既可以写:\n set USE_PROXY=True\n set API_KEY=sk-j7caBpkRoxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n set proxies={\"http\":\"http://127.0.0.1:10085\", \"https\":\"http://127.0.0.1:10085\",}\n set AVAIL_LLM_MODELS=[\"gpt-3.5-turbo\", \"chatglm\"]\n set AUTHENTICATION=[(\"username\", \"password\"), (\"username2\", \"password2\")]\n 也可以写:\n set GPT_ACADEMIC_USE_PROXY=True\n set GPT_ACADEMIC_API_KEY=sk-j7caBpkRoxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n set GPT_ACADEMIC_proxies={\"http\":\"http://127.0.0.1:10085\", \"https\":\"http://127.0.0.1:10085\",}\n set GPT_ACADEMIC_AVAIL_LLM_MODELS=[\"gpt-3.5-turbo\", \"chatglm\"]\n set GPT_ACADEMIC_AUTHENTICATION=[(\"username\", \"password\"), (\"username2\", \"password2\")]\n \"\"\"\n from colorful import print亮红, print亮绿\n arg_with_prefix = \"GPT_ACADEMIC_\" + arg \n if arg_with_prefix in os.environ: \n env_arg = os.environ[arg_with_prefix]\n elif arg in os.environ: \n env_arg = os.environ[arg]\n else:\n raise KeyError\n print(f\"[ENV_VAR] 尝试加载{arg},默认值:{default_value} --> 修正值:{env_arg}\")\n try:\n if isinstance(default_value, bool):\n env_arg = env_arg.strip()\n if env_arg == 'True': r = True\n elif env_arg == 'False': r = False\n else: print('enter True or False, but have:', env_arg); r = default_value\n elif isinstance(default_value, int):\n r = int(env_arg)\n elif isinstance(default_value, float):\n r = float(env_arg)\n elif isinstance(default_value, str):\n r = env_arg.strip()\n elif isinstance(default_value, dict):\n r = eval(env_arg)\n elif isinstance(default_value, list):\n r = eval(env_arg)\n elif default_value is None:\n assert arg == \"proxies\"\n r = eval(env_arg)\n else:\n print亮红(f\"[ENV_VAR] 环境变量{arg}不支持通过环境变量设置! \")\n raise KeyError\n except:\n print亮红(f\"[ENV_VAR] 环境变量{arg}加载失败! \")\n raise KeyError(f\"[ENV_VAR] 环境变量{arg}加载失败! \")\n\n print亮绿(f\"[ENV_VAR] 成功读取环境变量{arg}\")\n return r\n\n@lru_cache(maxsize=128)\ndef read_single_conf_with_lru_cache(arg):\n from colorful import print亮红, print亮绿, print亮蓝\n try:\n # 优先级1. 获取环境变量作为配置\n default_ref = getattr(importlib.import_module('config'), arg) # 读取默认值作为数据类型转换的参考\n r = read_env_variable(arg, default_ref) \n except:\n try:\n # 优先级2. 获取config_private中的配置\n r = getattr(importlib.import_module('config_private'), arg)\n except:\n # 优先级3. 获取config中的配置\n r = getattr(importlib.import_module('config'), arg)\n\n # 在读取API_KEY时,检查一下是不是忘了改config\n if arg == 'API_URL_REDIRECT':\n oai_rd = r.get(\"https://api.openai.com/v1/chat/completions\", None) # API_URL_REDIRECT填写格式是错误的,请阅读`https://github.com/binary-husky/gpt_academic/wiki/项目配置说明`\n if oai_rd and not oai_rd.endswith('/completions'):\n print亮红( \"\\n\\n[API_URL_REDIRECT] API_URL_REDIRECT填错了。请阅读`https://github.com/binary-husky/gpt_academic/wiki/项目配置说明`。如果您确信自己没填错,无视此消息即可。\")\n time.sleep(5)\n if arg == 'API_KEY':\n print亮蓝(f\"[API_KEY] 本项目现已支持OpenAI和Azure的api-key。也支持同时填写多个api-key,如API_KEY=\\\"openai-key1,openai-key2,azure-key3\\\"\")\n print亮蓝(f\"[API_KEY] 您既可以在config.py中修改api-key(s),也可以在问题输入区输入临时的api-key(s),然后回车键提交后即可生效。\")\n if is_any_api_key(r):\n print亮绿(f\"[API_KEY] 您的 API_KEY 是: {r[:15]}*** API_KEY 导入成功\")\n else:\n print亮红( \"[API_KEY] 您的 API_KEY 不满足任何一种已知的密钥格式,请在config文件中修改API密钥之后再运行。\")\n if arg == 'proxies':\n if not read_single_conf_with_lru_cache('USE_PROXY'): r = None # 检查USE_PROXY,防止proxies单独起作用\n if r is None:\n print亮红('[PROXY] 网络代理状态:未配置。无代理状态下很可能无法访问OpenAI家族的模型。建议:检查USE_PROXY选项是否修改。')\n else:\n print亮绿('[PROXY] 网络代理状态:已配置。配置信息如下:', r)\n assert isinstance(r, dict), 'proxies格式错误,请注意proxies选项的格式,不要遗漏括号。'\n return r\n\n\n@lru_cache(maxsize=128)\ndef get_conf(*args):\n \"\"\"\n 本项目的所有配置都集中在config.py中。 修改配置有三种方法,您只需要选择其中一种即可:\n - 直接修改config.py\n - 创建并修改config_private.py\n - 修改环境变量(修改docker-compose.yml等价于修改容器内部的环境变量)\n\n 注意:如果您使用docker-compose部署,请修改docker-compose(等价于修改容器内部的环境变量)\n \"\"\"\n res = []\n for arg in args:\n r = read_single_conf_with_lru_cache(arg)\n res.append(r)\n if len(res) == 1: return res[0]\n return res\n\n\ndef clear_line_break(txt):\n txt = txt.replace('\\n', ' ')\n txt = txt.replace(' ', ' ')\n txt = txt.replace(' ', ' ')\n return txt\n\n\nclass DummyWith():\n \"\"\"\n 这段代码定义了一个名为DummyWith的空上下文管理器,\n 它的作用是……额……就是不起作用,即在代码结构不变得情况下取代其他的上下文管理器。\n 上下文管理器是一种Python对象,用于与with语句一起使用,\n 以确保一些资源在代码块执行期间得到正确的初始化和清理。\n 上下文管理器必须实现两个方法,分别为 __enter__()和 __exit__()。\n 在上下文执行开始的情况下,__enter__()方法会在代码块被执行前被调用,\n 而在上下文执行结束时,__exit__()方法则会被调用。\n \"\"\"\n def __enter__(self):\n return self\n\n def __exit__(self, exc_type, exc_value, traceback):\n return\n\ndef run_gradio_in_subpath(demo, auth, port, custom_path):\n \"\"\"\n 把gradio的运行地址更改到指定的二次路径上\n \"\"\"\n def is_path_legal(path: str)->bool:\n '''\n check path for sub url\n path: path to check\n return value: do sub url wrap\n '''\n if path == \"/\": return True\n if len(path) == 0:\n print(\"ilegal custom path: {}\\npath must not be empty\\ndeploy on root url\".format(path))\n return False\n if path[0] == '/':\n if path[1] != '/':\n print(\"deploy on sub-path {}\".format(path))\n return True\n return False\n print(\"ilegal custom path: {}\\npath should begin with \\'/\\'\\ndeploy on root url\".format(path))\n return False\n\n if not is_path_legal(custom_path): raise RuntimeError('Ilegal custom path')\n import uvicorn\n import gradio as gr\n from fastapi import FastAPI\n app = FastAPI()\n if custom_path != \"/\":\n @app.get(\"/\")\n def read_main(): \n return {\"message\": f\"Gradio is running at: {custom_path}\"}\n app = gr.mount_gradio_app(app, demo, path=custom_path)\n uvicorn.run(app, host=\"0.0.0.0\", port=port) # , auth=auth\n\n\ndef clip_history(inputs, history, tokenizer, max_token_limit):\n \"\"\"\n reduce the length of history by clipping.\n this function search for the longest entries to clip, little by little,\n until the number of token of history is reduced under threshold.\n 通过裁剪来缩短历史记录的长度。 \n 此函数逐渐地搜索最长的条目进行剪辑,\n 直到历史记录的标记数量降低到阈值以下。\n \"\"\"\n import numpy as np\n from request_llms.bridge_all import model_info\n def get_token_num(txt): \n return len(tokenizer.encode(txt, disallowed_special=()))\n input_token_num = get_token_num(inputs)\n\n if max_token_limit < 5000: output_token_expect = 256 # 4k & 2k models\n elif max_token_limit < 9000: output_token_expect = 512 # 8k models\n else: output_token_expect = 1024 # 16k & 32k models\n\n if input_token_num < max_token_limit * 3 / 4:\n # 当输入部分的token占比小于限制的3/4时,裁剪时\n # 1. 把input的余量留出来\n max_token_limit = max_token_limit - input_token_num\n # 2. 把输出用的余量留出来\n max_token_limit = max_token_limit - output_token_expect\n # 3. 如果余量太小了,直接清除历史\n if max_token_limit < output_token_expect:\n history = []\n return history\n else:\n # 当输入部分的token占比 > 限制的3/4时,直接清除历史\n history = []\n return history\n\n everything = ['']\n everything.extend(history)\n n_token = get_token_num('\\n'.join(everything))\n everything_token = [get_token_num(e) for e in everything]\n\n # 截断时的颗粒度\n delta = max(everything_token) // 16\n\n while n_token > max_token_limit:\n where = np.argmax(everything_token)\n encoded = tokenizer.encode(everything[where], disallowed_special=())\n clipped_encoded = encoded[:len(encoded)-delta]\n everything[where] = tokenizer.decode(clipped_encoded)[:-1] # -1 to remove the may-be illegal char\n everything_token[where] = get_token_num(everything[where])\n n_token = get_token_num('\\n'.join(everything))\n\n history = everything[1:]\n return history\n\n\"\"\"\n========================================================================\n第三部分\n其他小工具:\n - zip_folder: 把某个路径下所有文件压缩,然后转移到指定的另一个路径中(gpt写的)\n - gen_time_str: 生成时间戳\n - ProxyNetworkActivate: 临时地启动代理网络(如果有)\n - objdump/objload: 快捷的调试函数\n========================================================================\n\"\"\"\n\ndef zip_folder(source_folder, dest_folder, zip_name):\n import zipfile\n import os\n # Make sure the source folder exists\n if not os.path.exists(source_folder):\n print(f\"{source_folder} does not exist\")\n return\n\n # Make sure the destination folder exists\n if not os.path.exists(dest_folder):\n print(f\"{dest_folder} does not exist\")\n return\n\n # Create the name for the zip file\n zip_file = pj(dest_folder, zip_name)\n\n # Create a ZipFile object\n with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) as zipf:\n # Walk through the source folder and add files to the zip file\n for foldername, subfolders, filenames in os.walk(source_folder):\n for filename in filenames:\n filepath = pj(foldername, filename)\n zipf.write(filepath, arcname=os.path.relpath(filepath, source_folder))\n\n # Move the zip file to the destination folder (if it wasn't already there)\n if os.path.dirname(zip_file) != dest_folder:\n os.rename(zip_file, pj(dest_folder, os.path.basename(zip_file)))\n zip_file = pj(dest_folder, os.path.basename(zip_file))\n\n print(f\"Zip file created at {zip_file}\")\n\ndef zip_result(folder):\n t = gen_time_str()\n zip_folder(folder, get_log_folder(), f'{t}-result.zip')\n return pj(get_log_folder(), f'{t}-result.zip')\n\ndef gen_time_str():\n import time\n return time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.localtime())\n\ndef get_log_folder(user=default_user_name, plugin_name='shared'):\n if user is None: user = default_user_name\n PATH_LOGGING = get_conf('PATH_LOGGING')\n if plugin_name is None:\n _dir = pj(PATH_LOGGING, user)\n else:\n _dir = pj(PATH_LOGGING, user, plugin_name)\n if not os.path.exists(_dir): os.makedirs(_dir)\n return _dir\n\ndef get_upload_folder(user=default_user_name, tag=None):\n PATH_PRIVATE_UPLOAD = get_conf('PATH_PRIVATE_UPLOAD')\n if user is None: user = default_user_name\n if tag is None or len(tag)==0:\n target_path_base = pj(PATH_PRIVATE_UPLOAD, user)\n else:\n target_path_base = pj(PATH_PRIVATE_UPLOAD, user, tag)\n return target_path_base\n\ndef is_the_upload_folder(string):\n PATH_PRIVATE_UPLOAD = get_conf('PATH_PRIVATE_UPLOAD')\n pattern = r'^PATH_PRIVATE_UPLOAD[\\\\/][A-Za-z0-9_-]+[\\\\/]\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2}$'\n pattern = pattern.replace('PATH_PRIVATE_UPLOAD', PATH_PRIVATE_UPLOAD)\n if re.match(pattern, string): return True\n else: return False\n\ndef get_user(chatbotwithcookies):\n return chatbotwithcookies._cookies.get('user_name', default_user_name)\n\nclass ProxyNetworkActivate():\n \"\"\"\n 这段代码定义了一个名为TempProxy的空上下文管理器, 用于给一小段代码上代理\n \"\"\"\n def __init__(self, task=None) -> None:\n self.task = task\n if not task:\n # 不给定task, 那么我们默认代理生效\n self.valid = True\n else:\n # 给定了task, 我们检查一下\n from toolbox import get_conf\n WHEN_TO_USE_PROXY = get_conf('WHEN_TO_USE_PROXY')\n self.valid = (task in WHEN_TO_USE_PROXY)\n\n def __enter__(self):\n if not self.valid: return self\n from toolbox import get_conf\n proxies = get_conf('proxies')\n if 'no_proxy' in os.environ: os.environ.pop('no_proxy')\n if proxies is not None:\n if 'http' in proxies: os.environ['HTTP_PROXY'] = proxies['http']\n if 'https' in proxies: os.environ['HTTPS_PROXY'] = proxies['https']\n return self\n\n def __exit__(self, exc_type, exc_value, traceback):\n os.environ['no_proxy'] = '*'\n if 'HTTP_PROXY' in os.environ: os.environ.pop('HTTP_PROXY')\n if 'HTTPS_PROXY' in os.environ: os.environ.pop('HTTPS_PROXY')\n return\n\ndef objdump(obj, file='objdump.tmp'):\n import pickle\n with open(file, 'wb+') as f:\n pickle.dump(obj, f)\n return\n\ndef objload(file='objdump.tmp'):\n import pickle, os\n if not os.path.exists(file): \n return\n with open(file, 'rb') as f:\n return pickle.load(f)\n \ndef Singleton(cls):\n \"\"\"\n 一个单实例装饰器\n \"\"\"\n _instance = {}\n \n def _singleton(*args, **kargs):\n if cls not in _instance:\n _instance[cls] = cls(*args, **kargs)\n return _instance[cls]\n \n return _singleton\n\n\"\"\"\n========================================================================\n第四部分\n接驳void-terminal:\n - set_conf: 在运行过程中动态地修改配置\n - set_multi_conf: 在运行过程中动态地修改多个配置\n - get_plugin_handle: 获取插件的句柄\n - get_plugin_default_kwargs: 获取插件的默认参数\n - get_chat_handle: 获取简单聊天的句柄\n - get_chat_default_kwargs: 获取简单聊天的默认参数\n========================================================================\n\"\"\"\n\ndef set_conf(key, value):\n from toolbox import read_single_conf_with_lru_cache, get_conf\n read_single_conf_with_lru_cache.cache_clear()\n get_conf.cache_clear()\n os.environ[key] = str(value)\n altered = get_conf(key)\n return altered\n\ndef set_multi_conf(dic):\n for k, v in dic.items(): set_conf(k, v)\n return\n\ndef get_plugin_handle(plugin_name):\n \"\"\"\n e.g. plugin_name = 'crazy_functions.批量Markdown翻译->Markdown翻译指定语言'\n \"\"\"\n import importlib\n assert '->' in plugin_name, \\\n \"Example of plugin_name: crazy_functions.批量Markdown翻译->Markdown翻译指定语言\"\n module, fn_name = plugin_name.split('->')\n f_hot_reload = getattr(importlib.import_module(module, fn_name), fn_name)\n return f_hot_reload\n\ndef get_chat_handle():\n \"\"\"\n \"\"\"\n from request_llms.bridge_all import predict_no_ui_long_connection\n return predict_no_ui_long_connection\n\ndef get_plugin_default_kwargs():\n \"\"\"\n \"\"\"\n from toolbox import ChatBotWithCookies\n cookies = load_chat_cookies()\n llm_kwargs = {\n 'api_key': cookies['api_key'],\n 'llm_model': cookies['llm_model'],\n 'top_p':1.0, \n 'max_length': None,\n 'temperature':1.0,\n }\n chatbot = ChatBotWithCookies(llm_kwargs)\n\n # txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port\n DEFAULT_FN_GROUPS_kwargs = {\n \"main_input\": \"./README.md\",\n \"llm_kwargs\": llm_kwargs,\n \"plugin_kwargs\": {},\n \"chatbot_with_cookie\": chatbot,\n \"history\": [],\n \"system_prompt\": \"You are a good AI.\", \n \"web_port\": None\n }\n return DEFAULT_FN_GROUPS_kwargs\n\ndef get_chat_default_kwargs():\n \"\"\"\n \"\"\"\n cookies = load_chat_cookies()\n llm_kwargs = {\n 'api_key': cookies['api_key'],\n 'llm_model': cookies['llm_model'],\n 'top_p':1.0, \n 'max_length': None,\n 'temperature':1.0,\n }\n default_chat_kwargs = {\n \"inputs\": \"Hello there, are you ready?\",\n \"llm_kwargs\": llm_kwargs,\n \"history\": [],\n \"sys_prompt\": \"You are AI assistant\",\n \"observe_window\": None,\n \"console_slience\": False,\n }\n\n return default_chat_kwargs\n\n\ndef get_pictures_list(path):\n file_manifest = [f for f in glob.glob(f'{path}/**/*.jpg', recursive=True)]\n file_manifest += [f for f in glob.glob(f'{path}/**/*.jpeg', recursive=True)]\n file_manifest += [f for f in glob.glob(f'{path}/**/*.png', recursive=True)]\n return file_manifest\n\n\ndef have_any_recent_upload_image_files(chatbot):\n _5min = 5 * 60\n if chatbot is None: return False, None # chatbot is None\n most_recent_uploaded = chatbot._cookies.get(\"most_recent_uploaded\", None)\n if not most_recent_uploaded: return False, None # most_recent_uploaded is None\n if time.time() - most_recent_uploaded[\"time\"] < _5min:\n most_recent_uploaded = chatbot._cookies.get(\"most_recent_uploaded\", None)\n path = most_recent_uploaded['path']\n file_manifest = get_pictures_list(path)\n if len(file_manifest) == 0: return False, None\n return True, file_manifest # most_recent_uploaded is new\n else:\n return False, None # most_recent_uploaded is too old\n\n\n# Function to encode the image\ndef encode_image(image_path):\n with open(image_path, \"rb\") as image_file:\n return base64.b64encode(image_file.read()).decode('utf-8')\n\n\ndef get_max_token(llm_kwargs):\n from request_llms.bridge_all import model_info\n return model_info[llm_kwargs['llm_model']]['max_token']\n\ndef check_packages(packages=[]):\n import importlib.util\n for p in packages:\n spam_spec = importlib.util.find_spec(p)\n if spam_spec is None: raise ModuleNotFoundError"}}, "config.py": {"cf3f571b334e0f6d47a3d7ad9522a38c39d966cc": {"t": 1785768954.043027, "n": 9303, "h": "ac32e50ef9065f4d", "c": "\"\"\"\n 以下所有配置也都支持利用环境变量覆写,环境变量配置格式见docker-compose.yml。\n 读取优先级:环境变量 > config_private.py > config.py\n --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n All the following configurations also support using environment variables to override, \n and the environment variable configuration format can be seen in docker-compose.yml. \n Configuration reading priority: environment variable > config_private.py > config.py\n\"\"\"\n\n# [step 1]>> API_KEY = \"sk-123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789\"。极少数情况下,还需要填写组织(格式如org-123456789abcdefghijklmno的),请向下翻,找 API_ORG 设置项\nAPI_KEY = \"sk-rwefH6RwYUnwCOuUD27071672b65402aA6D52d8b8e359b9c\" # 可同时填写多个API-KEY,用英文逗号分割,例如API_KEY = \"sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey3,azure-apikey4\"\n\n\n# [step 2]>> 改为True应用代理,如果直接在海外服务器部署,此处不修改;如果使用本地或无地域限制的大模型时,此处也不需要修改\nUSE_PROXY = False\nif USE_PROXY:\n \"\"\"\n 代理网络的地址,打开你的代理软件查看代理协议(socks5h / http)、地址(localhost)和端口(11284)\n 填写格式是 [协议]:// [地址] :[端口],填写之前不要忘记把USE_PROXY改成True,如果直接在海外服务器部署,此处不修改\n <配置教程&视频教程> https://github.com/binary-husky/gpt_academic/issues/1>\n [协议] 常见协议无非socks5h/http; 例如 v2**y 和 ss* 的默认本地协议是socks5h; 而cl**h 的默认本地协议是http\n [地址] 填localhost或者127.0.0.1(localhost意思是代理软件安装在本机上)\n [端口] 在代理软件的设置里找。虽然不同的代理软件界面不一样,但端口号都应该在最显眼的位置上\n \"\"\"\n proxies = {\n # [协议]:// [地址] :[端口]\n \"http\": \"http://114.55.245.137:1478\", # 再例如 \"http\": \"http://127.0.0.1:7890\",\n \"https\": \"http://114.55.245.137:1478\", # 再例如 \"https\": \"http://127.0.0.1:7890\",\n }\nelse:\n proxies = None\n\n# ------------------------------------ 以下配置可以优化体验, 但大部分场合下并不需要修改 ------------------------------------\n\n# 重新URL重新定向,实现更换API_URL的作用(高危设置! 常规情况下不要修改! 通过修改此设置,您将把您的API-KEY和对话隐私完全暴露给您设定的中间人!)\n# 格式: API_URL_REDIRECT = {\"https://api.openai.com/v1/chat/completions\": \"在这里填写重定向的api.openai.com的URL\"} \n# 举例: API_URL_REDIRECT = {\"https://api.openai.com/v1/chat/completions\": \"https://reverse-proxy-url/v1/chat/completions\"}\nAPI_URL_REDIRECT = {\"https://api.openai.com/v1/chat/completions\": \"https://api.kwwai.top/v1/chat/completions\"}\n\n\n# 多线程函数插件中,默认允许多少路线程同时访问OpenAI。Free trial users的限制是每分钟3次,Pay-as-you-go users的限制是每分钟3500次\n# 一言以蔽之:免费(5刀)用户填3,OpenAI绑了信用卡的用户可以填 16 或者更高。提高限制请查询:https://platform.openai.com/docs/guides/rate-limits/overview\nDEFAULT_WORKER_NUM = 3\n\n\n# 色彩主题, 可选 [\"Default\", \"Chuanhu-Small-and-Beautiful\", \"High-Contrast\"]\n# 更多主题, 请查阅Gradio主题商店: https://huggingface.co/spaces/gradio/theme-gallery 可选 [\"Gstaff/Xkcd\", \"NoCrypt/Miku\", ...]\nTHEME = \"Default\"\nAVAIL_THEMES = [\"Default\", \"Chuanhu-Small-and-Beautiful\", \"High-Contrast\", \"Gstaff/Xkcd\", \"NoCrypt/Miku\"]\n\n\n# 默认的系统提示词(system prompt)\nINIT_SYS_PROMPT = \"Serve me as a writing and programming assistant.\"\n\n\n# 对话窗的高度 (仅在LAYOUT=\"TOP-DOWN\"时生效)\nCHATBOT_HEIGHT = 1115\n\n\n# 代码高亮\nCODE_HIGHLIGHT = True\n\n\n# 窗口布局\nLAYOUT = \"LEFT-RIGHT\" # \"LEFT-RIGHT\"(左右布局) # \"TOP-DOWN\"(上下布局)\n\n\n# 暗色模式 / 亮色模式\nDARK_MODE = True \n\n\n# 发送请求到OpenAI后,等待多久判定为超时\nTIMEOUT_SECONDS = 30\n\n\n# 网页的端口, -1代表随机端口\nWEB_PORT = 1245\n\n\n# 如果OpenAI不响应(网络卡顿、代理失败、KEY失效),重试的次数限制\nMAX_RETRY = 2\n\n\n# 插件分类默认选项\nDEFAULT_FN_GROUPS = ['对话', '编程', '学术', '智能体']\n\n\n# 模型选择是 (注意: LLM_MODEL是默认选中的模型, 它*必须*被包含在AVAIL_LLM_MODELS列表中 )\nLLM_MODEL = \"gpt-3.5-turbo\" # 可选 ↓↓↓\nAVAIL_LLM_MODELS = [\"gpt-3.5-turbo-1106\",\"gpt-4-1106-preview\",\"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-16k\", \"gpt-3.5-turbo\", \"azure-gpt-3.5\",\n \"api2d-gpt-3.5-turbo\", 'api2d-gpt-3.5-turbo-16k',\n \"gpt-4\", \"gpt-4-32k\", \"azure-gpt-4\", \"api2d-gpt-4\",\n \"chatglm3\", \"moss\", \"claude-2\"]\n# P.S. 其他可用的模型还包括 [\"zhipuai\", \"qianfan\", \"deepseekcoder\", \"llama2\", \"qwen\", \"gpt-3.5-turbo-0613\", \"gpt-3.5-turbo-16k-0613\", \"gpt-3.5-random\"\n# \"spark\", \"sparkv2\", \"sparkv3\", \"chatglm_onnx\", \"claude-1-100k\", \"claude-2\", \"internlm\", \"jittorllms_pangualpha\", \"jittorllms_llama\"]\n\n\n# 定义界面上“询问多个GPT模型”插件应该使用哪些模型,请从AVAIL_LLM_MODELS中选择,并在不同模型之间用`&`间隔,例如\"gpt-3.5-turbo&chatglm3&azure-gpt-4\"\nMULTI_QUERY_LLM_MODELS = \"gpt-3.5-turbo&chatglm3\"\n\n\n# 选择本地模型变体(只有当AVAIL_LLM_MODELS包含了对应本地模型时,才会起作用)\n# 如果你选择Qwen系列的模型,那么请在下面的QWEN_MODEL_SELECTION中指定具体的模型\n# 也可以是具体的模型路径\nQWEN_MODEL_SELECTION = \"Qwen/Qwen-1_8B-Chat-Int8\"\n\n\n# 百度千帆(LLM_MODEL=\"qianfan\")\nBAIDU_CLOUD_API_KEY = ''\nBAIDU_CLOUD_SECRET_KEY = ''\nBAIDU_CLOUD_QIANFAN_MODEL = 'ERNIE-Bot' # 可选 \"ERNIE-Bot-4\"(文心大模型4.0), \"ERNIE-Bot\"(文心一言), \"ERNIE-Bot-turbo\", \"BLOOMZ-7B\", \"Llama-2-70B-Chat\", \"Llama-2-13B-Chat\", \"Llama-2-7B-Chat\"\n\n\n# 如果使用ChatGLM2微调模型,请把 LLM_MODEL=\"chatglmft\",并在此处指定模型路径\nCHATGLM_PTUNING_CHECKPOINT = \"\" # 例如\"/home/hmp/ChatGLM2-6B/ptuning/output/6b-pt-128-1e-2/checkpoint-100\"\n\n\n# 本地LLM模型如ChatGLM的执行方式 CPU/GPU\nLOCAL_MODEL_DEVICE = \"cpu\" # 可选 \"cuda\"\nLOCAL_MODEL_QUANT = \"FP16\" # 默认 \"FP16\" \"INT4\" 启用量化INT4版本 \"INT8\" 启用量化INT8版本\n\n# 设置gradio的并行线程数(不需要修改)\nCONCURRENT_COUNT = 100\n\n\n# 是否在提交时自动清空输入框\nAUTO_CLEAR_TXT = False\n\n\n# 加一个live2d装饰\nADD_WAIFU = False\n\n\n# 设置用户名和密码(不需要修改)(相关功能不稳定,与gradio版本和网络都相关,如果本地使用不建议加这个)\n# [(\"username\", \"password\"), (\"username2\", \"password2\"), ...]\nAUTHENTICATION = [(\"user\", \"1\"),(\"user2\", \"2\"),(\"user3\", \"3\"),(\"wa\",\"1\")]\n\n\n# 如果需要在二级路径下运行(常规情况下,不要修改!!)(需要配合修改main.py才能生效!)\nCUSTOM_PATH = \"/\"\n\n\n# HTTPS 秘钥和证书(不需要修改)\nSSL_KEYFILE = \"\"\nSSL_CERTFILE = \"\"\n\n\n# 极少数情况下,openai的官方KEY需要伴随组织编码(格式如org-xxxxxxxxxxxxxxxxxxxxxxxx)使用\nAPI_ORG = \"\"\n\n\n# 如果需要使用Slack Claude,使用教程详情见 request_llms/README.md\nSLACK_CLAUDE_BOT_ID = '' \nSLACK_CLAUDE_USER_TOKEN = ''\n\n\n# 如果需要使用AZURE(方法一:单个azure模型部署)详情请见额外文档 docs\\use_azure.md\nAZURE_ENDPOINT = \"https://你亲手写的api名称.openai.azure.com/\"\nAZURE_API_KEY = \"填入azure openai api的密钥\" # 建议直接在API_KEY处填写,该选项即将被弃用\nAZURE_ENGINE = \"填入你亲手写的部署名\" # 读 docs\\use_azure.md\n\n\n# 如果需要使用AZURE(方法二:多个azure模型部署+动态切换)详情请见额外文档 docs\\use_azure.md\nAZURE_CFG_ARRAY = {}\n\n\n# 使用Newbing (不推荐使用,未来将删除)\nNEWBING_STYLE = \"creative\" # [\"creative\", \"balanced\", \"precise\"]\nNEWBING_COOKIES = \"\"\"\nput your new bing cookies here\n\"\"\"\n\n\n# 阿里云实时语音识别 配置难度较高 仅建议高手用户使用 参考 https://github.com/binary-husky/gpt_academic/blob/master/docs/use_audio.md\nENABLE_AUDIO = False\nALIYUN_TOKEN=\"\" # 例如 f37f30e0f9934c34a992f6f64f7eba4f\nALIYUN_APPKEY=\"\" # 例如 RoPlZrM88DnAFkZK\nALIYUN_ACCESSKEY=\"\" # (无需填写)\nALIYUN_SECRET=\"\" # (无需填写)\n\n\n# 接入讯飞星火大模型 https://console.xfyun.cn/services/iat\nXFYUN_APPID = \"00000000\"\nXFYUN_API_SECRET = \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\nXFYUN_API_KEY = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\n\n# 接入智谱大模型\nZHIPUAI_API_KEY = \"\"\nZHIPUAI_MODEL = \"chatglm_turbo\"\n\n\n# Claude API KEY\nANTHROPIC_API_KEY = \"\"\n\n\n# 自定义API KEY格式\nCUSTOM_API_KEY_PATTERN = \"\"\n\n\n# HUGGINGFACE的TOKEN,下载LLAMA时起作用 https://huggingface.co/docs/hub/security-tokens\nHUGGINGFACE_ACCESS_TOKEN = \"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\"\n\n\n# GROBID服务器地址(填写多个可以均衡负载),用于高质量地读取PDF文档\n# 获取方法:复制以下空间https://huggingface.co/spaces/qingxu98/grobid,设为public,然后GROBID_URL = \"https://(你的hf用户名如qingxu98)-(你的填写的空间名如grobid).hf.space\"\nGROBID_URLS = [\n \"https://qingxu98-grobid.hf.space\",\"https://qingxu98-grobid2.hf.space\",\"https://qingxu98-grobid3.hf.space\",\n \"https://qingxu98-grobid4.hf.space\",\"https://qingxu98-grobid5.hf.space\", \"https://qingxu98-grobid6.hf.space\", \n \"https://qingxu98-grobid7.hf.space\", \"https://qingxu98-grobid8.hf.space\", \n]\n\n\n# 是否允许通过自然语言描述修改本页的配置,该功能具有一定的危险性,默认关闭\nALLOW_RESET_CONFIG = False\n\n\n# 在使用AutoGen插件时,是否使用Docker容器运行代码\nAUTOGEN_USE_DOCKER = False\n\n\n# 临时的上传文件夹位置,请勿修改\nPATH_PRIVATE_UPLOAD = \"private_upload\"\n\n\n# 日志文件夹的位置,请勿修改\nPATH_LOGGING = \"gpt_log\"\n\n\n# 除了连接OpenAI之外,还有哪些场合允许使用代理,请勿修改\nWHEN_TO_USE_PROXY = [\"Download_LLM\", \"Download_Gradio_Theme\", \"Connect_Grobid\", \n \"Warmup_Modules\", \"Nougat_Download\", \"AutoGen\"]\n\n\n# *实验性功能*: 自动检测并屏蔽失效的KEY,请勿使用\nBLOCK_INVALID_APIKEY = False\n\n\n# 启用插件热加载\nPLUGIN_HOT_RELOAD = False\n\n\n# 自定义按钮的最大数量限制\nNUM_CUSTOM_BASIC_BTN = 4\n\n\"\"\"\n在线大模型配置关联关系示意图\n│\n├── \"gpt-3.5-turbo\" 等openai模型\n│ ├── API_KEY\n│ ├── CUSTOM_API_KEY_PATTERN(不常用)\n│ ├── API_ORG(不常用)\n│ └── API_URL_REDIRECT(不常用)\n│\n├── \"azure-gpt-3.5\" 等azure模型(单个azure模型,不需要动态切换)\n│ ├── API_KEY\n│ ├── AZURE_ENDPOINT\n│ ├── AZURE_API_KEY\n│ ├── AZURE_ENGINE\n│ └── API_URL_REDIRECT\n│\n├── \"azure-gpt-3.5\" 等azure模型(多个azure模型,需要动态切换,高优先级)\n│ └── AZURE_CFG_ARRAY\n│\n├── \"spark\" 星火认知大模型 spark & sparkv2\n│ ├── XFYUN_APPID\n│ ├── XFYUN_API_SECRET\n│ └── XFYUN_API_KEY\n│\n├── \"claude-1-100k\" 等claude模型\n│ └── ANTHROPIC_API_KEY\n│\n├── \"stack-claude\"\n│ ├── SLACK_CLAUDE_BOT_ID\n│ └── SLACK_CLAUDE_USER_TOKEN\n│\n├── \"qianfan\" 百度千帆大模型库\n│ ├── BAIDU_CLOUD_QIANFAN_MODEL\n│ ├── BAIDU_CLOUD_API_KEY\n│ └── BAIDU_CLOUD_SECRET_KEY\n│\n├── \"zhipuai\" 智谱AI大模型chatglm_turbo\n│ ├── ZHIPUAI_API_KEY\n│ └── ZHIPUAI_MODEL\n│\n└── \"newbing\" Newbing接口不再稳定,不推荐使用\n ├── NEWBING_STYLE\n └── NEWBING_COOKIES\n\n \n本地大模型示意图\n│\n├── \"chatglm3\"\n├── \"chatglm\"\n├── \"chatglm_onnx\"\n├── \"chatglmft\"\n├── \"internlm\"\n├── \"moss\"\n├── \"jittorllms_pangualpha\"\n├── \"jittorllms_llama\"\n├── \"deepseekcoder\"\n├── \"qwen\"\n├── RWKV的支持见Wiki\n└── \"llama2\"\n\n\n用户图形界面布局依赖关系示意图\n│\n├── CHATBOT_HEIGHT 对话窗的高度\n├── CODE_HIGHLIGHT 代码高亮\n├── LAYOUT 窗口布局\n├── DARK_MODE 暗色模式 / 亮色模式\n├── DEFAULT_FN_GROUPS 插件分类默认选项\n├── THEME 色彩主题\n├── AUTO_CLEAR_TXT 是否在提交时自动清空输入框\n├── ADD_WAIFU 加一个live2d装饰\n└── ALLOW_RESET_CONFIG 是否允许通过自然语言描述修改本页的配置,该功能具有一定的危险性\n\n\n插件在线服务配置依赖关系示意图\n│\n├── 语音功能\n│ ├── ENABLE_AUDIO\n│ ├── ALIYUN_TOKEN\n│ ├── ALIYUN_APPKEY\n│ ├── ALIYUN_ACCESSKEY\n│ └── ALIYUN_SECRET\n│\n└── PDF文档精准解析\n └── GROBID_URLS\n\n\"\"\"\n"}}}}