Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ancool-ovo_dify-on-wechat-ipad.json

1 line
20 KiB
JSON

{"repo": "ancool-ovo/dify-on-wechat-ipad", "files": {"config.py": {"68c96e04b26b2bfdffd4626b94c9dd44dd413cc7": {"t": 1785674175.8523896, "n": 16016, "h": "e18bd744c551b2e6", "c": "# encoding:utf-8\n\nimport json\nimport logging\nimport os\nimport pickle\nimport copy\n\nfrom common.log import logger\n\n# 将所有可用的配置项写在字典里, 请使用小写字母\n# 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到config.json中\navailable_setting = {\n # webui配置\n \"web_ui_port\": 7860,\n \"web_ui_username\": \"dow\",\n \"web_ui_password\": \"dify-on-wechat\",\n # 错误回复消息\n \"error_reply\": \"我暂时遇到了一些问题,请您稍后重试~\",\n # openai api配置\n \"open_ai_api_key\": \"\", # openai api key\n # openai apibase,当use_azure_chatgpt为true时,需要设置对应的api base\n \"open_ai_api_base\": \"https://api.openai.com/v1\",\n \"proxy\": \"\", # openai使用的代理\n # chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称\n \"model\": \"gpt-3.5-turbo\", # 可选择: gpt-4o, pt-4o-mini, gpt-4-turbo, claude-3-sonnet, wenxin, moonshot, qwen-turbo, xunfei, glm-4, minimax, gemini等模型,全部可选模型详见common/const.py文件\n \"bot_type\": \"\", # 可选配置,使用兼容openai格式的三方服务时候,需填\"chatGPT\"。bot具体名称详见common/const.py文件列出的bot_type,如不填根据model名称判断,\n \"use_azure_chatgpt\": False, # 是否使用azure的chatgpt\n \"azure_deployment_id\": \"\", # azure 模型部署名称\n \"azure_api_version\": \"\", # azure api版本\n # Bot触发配置\n \"single_chat_prefix\": [\"bot\", \"@bot\"], # 私聊时文本需要包含该前缀才能触发机器人回复\n \"single_chat_reply_prefix\": \"[bot] \", # 私聊时自动回复的前缀,用于区分真人\n \"single_chat_reply_suffix\": \"\", # 私聊时自动回复的后缀,\\n 可以换行\n \"accept_friend_commands\": [\"加好友\"], # 自动接受好友请求的申请信息\n \"group_chat_prefix\": [\"@bot\"], # 群聊时包含该前缀则会触发机器人回复\n \"no_need_at\": False, # 群聊回复时是否不需要艾特\n \"group_chat_reply_prefix\": \"\", # 群聊时自动回复的前缀\n \"group_chat_reply_suffix\": \"\", # 群聊时自动回复的后缀,\\n 可以换行\n \"group_chat_keyword\": [], # 群聊时包含该关键词则会触发机器人回复\n \"group_at_off\": False, # 是否关闭群聊时@bot的触发\n \"group_name_white_list\": [\"ChatGPT测试群\", \"ChatGPT测试群2\"], # 开启自动回复的群名称列表\n \"group_name_keyword_white_list\": [], # 开启自动回复的群名称关键词列表\n \"group_chat_in_one_session\": [\"ChatGPT测试群\"], # 支持会话上下文共享的群名称\n \"nick_name_black_list\": [], # 用户昵称黑名单\n \"group_welcome_msg\": \"\", # 配置新人进群固定欢迎语,不配置则使用随机风格欢迎\n \"trigger_by_self\": False, # 是否允许机器人触发\n \"text_to_image\": \"dall-e-2\", # 图片生成模型,可选 dall-e-2, dall-e-3\n # Azure OpenAI dall-e-3 配置\n \"dalle3_image_style\": \"vivid\", # 图片生成dalle3的风格,可选有 vivid, natural\n \"dalle3_image_quality\": \"hd\", # 图片生成dalle3的质量,可选有 standard, hd\n # Azure OpenAI DALL-E API 配置, 当use_azure_chatgpt为true时,用于将文字回复的资源和Dall-E的资源分开.\n \"azure_openai_dalle_api_base\": \"\", # [可选] azure openai 用于回复图片的资源 endpoint,默认使用 open_ai_api_base\n \"azure_openai_dalle_api_key\": \"\", # [可选] azure openai 用于回复图片的资源 key,默认使用 open_ai_api_key\n \"azure_openai_dalle_deployment_id\":\"\", # [可选] azure openai 用于回复图片的资源 deployment id,默认使用 text_to_image\n \"image_proxy\": True, # 是否需要图片代理,国内访问LinkAI时需要\n \"image_create_prefix\": [\"画\", \"看\", \"找\"], # 开启图片回复的前缀\n \"concurrency_in_session\": 1, # 同一会话最多有多少条消息在处理中,大于1可能乱序\n \"image_create_size\": \"256x256\", # 图片大小,可选有 256x256, 512x512, 1024x1024 (dall-e-3默认为1024x1024)\n \"group_chat_exit_group\": False,\n \"group_exit_msg\": \"\", # 退出群聊的消息\n \"accept_friend_msg\": \"\", # 接受好友请求后发送的消息\n # chatgpt会话参数\n \"expires_in_seconds\": 3600, # 无操作会话的过期时间\n # 人格描述\n \"character_desc\": \"你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。\",\n \"conversation_max_tokens\": 1000, # 支持上下文记忆的最多字符数\n # chatgpt限流配置\n \"rate_limit_chatgpt\": 20, # chatgpt的调用频率限制\n \"rate_limit_dalle\": 50, # openai dalle的调用频率限制\n # chatgpt api参数 参考https://platform.openai.com/docs/api-reference/chat/create\n \"temperature\": 0.9,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"request_timeout\": 180, # chatgpt请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间\n \"timeout\": 120, # chatgpt重试超时时间,在这个时间内,将会自动重试\n # Baidu 文心一言参数\n \"baidu_wenxin_model\": \"eb-instant\", # 默认使用ERNIE-Bot-turbo模型\n \"baidu_wenxin_api_key\": \"\", # Baidu api key\n \"baidu_wenxin_secret_key\": \"\", # Baidu secret key\n \"baidu_wenxin_prompt_enabled\": False, # Enable prompt if you are using ernie character model\n # 讯飞星火API\n \"xunfei_app_id\": \"\", # 讯飞应用ID\n \"xunfei_api_key\": \"\", # 讯飞 API key\n \"xunfei_api_secret\": \"\", # 讯飞 API secret\n \"xunfei_domain\": \"\", # 讯飞模型对应的domain参数,Spark4.0 Ultra为 4.0Ultra,其他模型详见: https://www.xfyun.cn/doc/spark/Web.html\n \"xunfei_spark_url\": \"\", # 讯飞模型对应的请求地址,Spark4.0 Ultra为 wss://spark-api.xf-yun.com/v4.0/chat,其他模型参考详见: https://www.xfyun.cn/doc/spark/Web.html\n # claude 配置\n \"claude_api_cookie\": \"\",\n \"claude_uuid\": \"\",\n # claude api key\n \"claude_api_key\": \"\",\n # 通义千问API, 获取方式查看文档 https://help.aliyun.com/document_detail/2587494.html\n \"qwen_access_key_id\": \"\",\n \"qwen_access_key_secret\": \"\",\n \"qwen_agent_key\": \"\",\n \"qwen_app_id\": \"\",\n \"qwen_node_id\": \"\", # 流程编排模型用到的id,如果没有用到qwen_node_id,请务必保持为空字符串\n # 阿里灵积(通义新版sdk)模型api key\n \"dashscope_api_key\": \"\",\n # Google Gemini Api Key\n \"gemini_api_key\": \"\",\n # dify配置\n \"dify_api_base\": \"https://api.dify.ai/v1\",\n \"dify_api_key\": \"app-xxx\",\n \"dify_app_type\": \"chatbot\", # dify助手类型 chatbot(对应聊天助手或对话流)/agent(对应Agent)/workflow(对应工作流,则默认为chatbot\n \"dify_conversation_max_messages\": 5, # dify目前不支持设置历史消息长度,暂时使用超过最大消息数清空会话的策略,缺点是没有滑动窗口,会突然丢失历史消息,当设置的值小于等于0,则不限制历史消息长度\n \"dify_error_reply\": \"\", # dify bot错误时给用户的回复\n # coze配置\n \"coze_api_base\": \"https://api.coze.cn\",\n \"coze_api_key\": \"xxx\",\n \"coze_bot_id\": \"xxx\",\n \"coze_return_show_img\": \"false\",\n \"coze_conversation_max_messages\" : 20, # coze目前不支持设置历史消息长度,暂时使用超过最大消息数清空会话的策略,缺点是没有滑动窗口,会突然丢失历史消息\n # wework的通用配置\n \"wework_smart\": True, # 配置wework是否使用已登录的企业微信,False为多开\n # 语音设置\n \"speech_recognition\": False, # 是否开启语音识别\n \"group_speech_recognition\": False, # 是否开启群组语音识别\n \"voice_reply_voice\": False, # 是否使用语音回复语音,需要设置对应语音合成引擎的api key\n \"always_reply_voice\": False, # 是否一直使用语音回复\n \"voice_to_text\": \"openai\", # 语音识别引擎,支持openai,baidu,google,azure,xunfei,ali\n \"text_to_voice\": \"openai\", # 语音合成引擎,支持openai,baidu,google,azure,xunfei,ali,pytts(offline),elevenlabs,edge(online)\n \"text_to_voice_model\": \"tts-1\",\n \"tts_voice_id\": \"alloy\",\n # baidu 语音api配置, 使用百度语音识别和语音合成时需要\n \"baidu_app_id\": \"\",\n \"baidu_api_key\": \"\",\n \"baidu_secret_key\": \"\",\n # 1536普通话(支持简单的英文识别) 1737英语 1637粤语 1837四川话 1936普通话远场\n \"baidu_dev_pid\": 1536,\n # azure 语音api配置, 使用azure语音识别和语音合成时需要\n \"azure_voice_api_key\": \"\",\n \"azure_voice_region\": \"japaneast\",\n # elevenlabs 语音api配置\n \"xi_api_key\": \"\", #获取ap的方法可以参考https://docs.elevenlabs.io/api-reference/quick-start/authentication\n \"xi_voice_id\": \"\", #ElevenLabs提供了9种英式、美式等英语发音id,分别是“Adam/Antoni/Arnold/Bella/Domi/Elli/Josh/Rachel/Sam”\n # 图像模型设置\n \"image_recognition\": False, # 是否开启图片识别\n # 服务时间限制,目前支持itchat\n \"chat_time_module\": False, # 是否开启服务时间限制\n \"chat_start_time\": \"00:00\", # 服务开始时间\n \"chat_stop_time\": \"24:00\", # 服务结束时间\n # 翻译api\n \"translate\": \"baidu\", # 翻译api,支持baidu\n # baidu翻译api的配置\n \"baidu_translate_app_id\": \"\", # 百度翻译api的appid\n \"baidu_translate_app_key\": \"\", # 百度翻译api的秘钥\n # itchat的配置\n \"hot_reload\": False, # 是否开启热重载\n # wechaty的配置\n \"wechaty_puppet_service_token\": \"\", # wechaty的token\n # wechatmp的配置\n \"wechatmp_token\": \"\", # 微信公众平台的Token\n \"wechatmp_port\": 8080, # 微信公众平台的端口,需要端口转发到80或443\n \"wechatmp_app_id\": \"\", # 微信公众平台的appID\n \"wechatmp_app_secret\": \"\", # 微信公众平台的appsecret\n \"wechatmp_aes_key\": \"\", # 微信公众平台的EncodingAESKey,加密模式需要\n # wechatcom的通用配置\n \"wechatcom_corp_id\": \"\", # 企业微信公司的corpID\n # wechatcomapp的配置\n \"wechatcomapp_token\": \"\", # 企业微信app的token\n \"wechatcomapp_port\": 9898, # 企业微信app的服务端口,不需要端口转发\n \"wechatcomapp_secret\": \"\", # 企业微信app的secret\n \"wechatcomapp_agent_id\": \"\", # 企业微信app的agent_id\n \"wechatcomapp_aes_key\": \"\", # 企业微信app的aes_key\n # 飞书配置\n \"feishu_port\": 80, # 飞书bot监听端口\n \"feishu_app_id\": \"\", # 飞书机器人应用APP Id\n \"feishu_app_secret\": \"\", # 飞书机器人APP secret\n \"feishu_token\": \"\", # 飞书 verification token\n \"feishu_bot_name\": \"\", # 飞书机器人的名字\n # 钉钉配置\n \"dingtalk_client_id\": \"\", # 钉钉机器人Client ID \n \"dingtalk_client_secret\": \"\", # 钉钉机器人Client Secret\n \"dingtalk_card_enabled\": False,\n ## gewechat配置\n \"gewechat_base_url\": \"\",\n \"gewechat_download_url\": \"\",\n \"gewechat_token\": \"\",\n \"gewechat_app_id\": \"\",\n \"gewechat_callback_url\": \"\", # 回调地址,示例:http://172.17.0.1:9919/v2/api/callback/collect\n \n # 微信849协议配置\n \"wx849_api_host\": \"127.0.0.1\", # 微信849协议API地址\n \"wx849_api_port\": 9000, # 微信849协议API端口\n \"wx849_protocol_version\": \"849\", # 微信849协议版本,可选: \"849\", \"855\", \"ipad\"\n \"log_level\": \"INFO\",\n \"wx849_wxid\": \"\",\n \"wx849_device_name\": \"DoW微信机器人\",\n \"wx849_device_id\": \"\",\n\n # chatgpt指令自定义触发词\n \"clear_memory_commands\": [\"#清除记忆\"], # 重置会话指令,必须以#开头\n # channel配置\n \"channel_type\": \"\", # 通道类型,支持:{wx,wxy,terminal,wechatmp,wechatmp_service,wechatcom_app,dingtalk}\n \"subscribe_msg\": \"\", # 订阅消息, 支持: wechatmp, wechatmp_service, wechatcom_app\n \"debug\": False, # 是否开启debug模式,开启后会打印更多日志\n \"appdata_dir\": \"\", # 数据目录\n # 插件配置\n \"plugin_trigger_prefix\": \"$\", # 规范插件提供聊天相关指令的前缀,建议不要和管理员指令前缀\"#\"冲突\n # 是否使用全局插件配置\n \"use_global_plugin_config\": False,\n \"max_media_send_count\": 3, # 单次最大发送媒体资源的个数\n \"media_send_interval\": 1, # 发送图片的事件间隔,单位秒\n # 智谱AI 平台配置\n \"zhipu_ai_api_key\": \"\",\n \"zhipu_ai_api_base\": \"https://open.bigmodel.cn/api/paas/v4\",\n \"moonshot_api_key\": \"\",\n \"moonshot_base_url\": \"https://api.moonshot.cn/v1/chat/completions\",\n #魔搭社区 平台配置\n \"modelscope_api_key\": \"\",\n \"modelscope_base_url\": \"https://api-inference.modelscope.cn/v1/chat/completions\",\n # LinkAI平台配置\n \"use_linkai\": False,\n \"linkai_api_key\": \"\",\n \"linkai_app_code\": \"\",\n \"linkai_api_base\": \"https://api.link-ai.tech\", # linkAI服务地址\n \"Minimax_api_key\": \"\",\n \"Minimax_group_id\": \"\",\n \"Minimax_base_url\": \"\",\n \"web_port\": 9899,\n}\n\n\nclass Config(dict):\n def __init__(self, d=None):\n super().__init__()\n if d is None:\n d = {}\n for k, v in d.items():\n self[k] = v\n # user_datas: 用户数据,key为用户名,value为用户数据,也是dict\n self.user_datas = {}\n\n def __getitem__(self, key):\n if key not in available_setting:\n raise Exception(\"key {} not in available_setting\".format(key))\n return super().__getitem__(key)\n\n def __setitem__(self, key, value):\n if key not in available_setting:\n raise Exception(\"key {} not in available_setting\".format(key))\n return super().__setitem__(key, value)\n\n def get(self, key, default=None):\n try:\n return self[key]\n except KeyError as e:\n return default\n except Exception as e:\n raise e\n \n def set(self, key, value):\n try:\n self[key] = value\n except Exception as e:\n raise e\n\n # Make sure to return a dictionary to ensure atomic\n def get_user_data(self, user) -> dict:\n if self.user_datas.get(user) is None:\n self.user_datas[user] = {}\n return self.user_datas[user]\n\n def load_user_datas(self):\n try:\n with open(os.path.join(get_appdata_dir(), \"user_datas.pkl\"), \"rb\") as f:\n self.user_datas = pickle.load(f)\n logger.info(\"[Config] User datas loaded.\")\n except FileNotFoundError as e:\n logger.info(\"[Config] User datas file not found, ignore.\")\n except Exception as e:\n logger.info(\"[Config] User datas error: {}\".format(e))\n self.user_datas = {}\n\n def save_user_datas(self):\n try:\n with open(os.path.join(get_appdata_dir(), \"user_datas.pkl\"), \"wb\") as f:\n pickle.dump(self.user_datas, f)\n logger.info(\"[Config] User datas saved.\")\n except Exception as e:\n logger.info(\"[Config] User datas error: {}\".format(e))\n\n\nconfig = Config()\n\n\ndef drag_sensitive(config):\n try:\n if isinstance(config, str):\n conf_dict: dict = json.loads(config)\n conf_dict_copy = copy.deepcopy(conf_dict)\n for key in conf_dict_copy:\n if \"key\" in key or \"secret\" in key:\n if isinstance(conf_dict_copy[key], str):\n conf_dict_copy[key] = conf_dict_copy[key][0:3] + \"*\" * 5 + conf_dict_copy[key][-3:]\n return json.dumps(conf_dict_copy, indent=4)\n\n elif isinstance(config, dict):\n config_copy = copy.deepcopy(config)\n for key in config:\n if \"key\" in key or \"secret\" in key:\n if isinstance(config_copy[key], str):\n config_copy[key] = config_copy[key][0:3] + \"*\" * 5 + config_copy[key][-3:]\n return config_copy\n except Exception as e:\n logger.exception(e)\n return config\n return config\n\n\ndef load_config():\n global config\n config_path = \"./config.json\"\n if not os.path.exists(config_path):\n logger.info(\"配置文件不存在,将使用config-template.json模板\")\n config_path = \"./config-template.json\"\n\n config_str = read_file(config_path)\n logger.debug(\"[INIT] config str: {}\".format(drag_sensitive(config_str)))\n\n # 将json字符串反序列化为dict类型\n config = Config(json.loads(config_str))\n\n # override config with environment variables.\n # Some online deployment platforms (e.g. Railway) deploy project from github directly. So you shouldn't put your secrets like api key in a config file, instead use environment variables to override the default config.\n for name, value in os.environ.items():\n name = name.lower()\n if name in available_setting:\n logger.info(\"[INIT] override config by environ args: {}={}\".format(name, value))\n try:\n config[name] = eval(value)\n except:\n if value == \"false\":\n config[name] = False\n elif value == \"true\":\n config[name] = True\n else:\n config[name] = value\n\n if config.get(\"debug\", False):\n logger.setLevel(logging.DEBUG)\n logger.debug(\"[INIT] set log level to DEBUG\")\n\n logger.info(\"[INIT] load config: {}\".format(drag_sensitive(config)))\n\n config.load_user_datas()\n\ndef save_config():\n global config\n config_path = \"./config.json\"\n try:\n config_dict = dict(config) # 将Config对象转换为普通字典\n # 创建一个按键排序的有序字典\n sorted_config = {key: config_dict[key] for key in sorted(config_dict.keys())}\n with open(config_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(sorted_config, f, indent=4, ensure_ascii=False)\n logger.info(\"[Config] Configuration saved.\")\n except Exception as e:\n logger.error(f\"[Config] Save configuration error: {e}\")\n\n\ndef get_root():\n return os.path.dirname(os.path.abspath(__file__))\n\n\ndef read_file(path):\n with open(path, mode=\"r\", encoding=\"utf-8\") as f:\n return f.read()\n\n\ndef conf():\n return config\n\n\ndef get_appdata_dir():\n data_path = os.path.join(get_root(), conf().get(\"appdata_dir\", \"\"))\n if not os.path.exists(data_path):\n logger.info(\"[INIT] data path not exists, create it: {}\".format(data_path))\n os.makedirs(data_path)\n return data_path\n\n\ndef subscribe_msg():\n trigger_prefix = conf().get(\"single_chat_prefix\", [\"\"])[0]\n msg = conf().get(\"subscribe_msg\", \"\")\n return msg.format(trigger_prefix=trigger_prefix)\n\n\n# global plugin config\nplugin_config = {}\n\n\ndef write_plugin_config(pconf: dict):\n \"\"\"\n 写入插件全局配置\n :param pconf: 全量插件配置\n \"\"\"\n global plugin_config\n for k in pconf:\n plugin_config[k.lower()] = pconf[k]\n\ndef remove_plugin_config(name: str):\n \"\"\"\n 移除待重新加载的插件全局配置\n :param name: 待重载的插件名\n \"\"\"\n global plugin_config\n plugin_config.pop(name.lower(), None)\n\n\ndef pconf(plugin_name: str) -> dict:\n \"\"\"\n 根据插件名称获取配置\n :param plugin_name: 插件名称\n :return: 该插件的配置项\n \"\"\"\n # 如果插件名称作为key获取不到,则尝试使用小写名称\n return plugin_config.get(plugin_name) or plugin_config.get(plugin_name.lower())\n\n\n# 全局配置,用于存放全局生效的状态\nglobal_config = {\"admin_users\": []}\n"}}}}