支持自然语言修改项目本身的配置

This commit is contained in:
qingxu fu 2023-08-29 17:37:41 +08:00
parent 2fa3d47887
commit 31ff6e1e7a
5 changed files with 294 additions and 138 deletions

View File

@ -25,7 +25,7 @@ def get_crazy_functions():
from crazy_functions.对话历史存档 import 载入对话历史存档
from crazy_functions.对话历史存档 import 删除所有本地对话历史记录
from crazy_functions.辅助功能 import 清除缓存
from crazy_functions.批量Markdown翻译 import Markdown英译中
function_plugins = {
"解析整个Python项目": {
@ -34,11 +34,11 @@ def get_crazy_functions():
},
"载入对话历史存档(先上传存档或输入路径)": {
"Color": "stop",
"AsButton":False,
"AsButton": False,
"Function": HotReload(载入对话历史存档)
},
"删除所有本地对话历史记录(请谨慎操作)": {
"AsButton":False,
"AsButton": False,
"Function": HotReload(删除所有本地对话历史记录)
},
"清除所有缓存文件(请谨慎操作)": {
@ -48,10 +48,10 @@ def get_crazy_functions():
},
"解析Jupyter Notebook文件": {
"Color": "stop",
"AsButton":False,
"AsButton": False,
"Function": HotReload(解析ipynb文件),
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "若输入0则不解析notebook中的Markdown块", # 高级参数输入区的显示提示
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "若输入0则不解析notebook中的Markdown块", # 高级参数输入区的显示提示
},
"批量总结Word文档": {
"Color": "stop",
@ -255,8 +255,8 @@ def get_crazy_functions():
"解析项目源代码(手动指定和筛选源代码文件类型)": {
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "输入时用逗号隔开, *代表通配符, 加了^代表不匹配; 不输入代表全部匹配。例如: \"*.c, ^*.cpp, config.toml, ^*.toml\"", # 高级参数输入区的显示提示
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "输入时用逗号隔开, *代表通配符, 加了^代表不匹配; 不输入代表全部匹配。例如: \"*.c, ^*.cpp, config.toml, ^*.toml\"", # 高级参数输入区的显示提示
"Function": HotReload(解析任意code项目)
},
})
@ -269,8 +269,8 @@ def get_crazy_functions():
"询问多个GPT模型手动指定询问哪些模型": {
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "支持任意数量的llm接口用&符号分隔。例如chatglm&gpt-3.5-turbo&api2d-gpt-4", # 高级参数输入区的显示提示
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "支持任意数量的llm接口用&符号分隔。例如chatglm&gpt-3.5-turbo&api2d-gpt-4", # 高级参数输入区的显示提示
"Function": HotReload(同时问询_指定模型)
},
})
@ -283,8 +283,8 @@ def get_crazy_functions():
"图片生成先切换模型到openai或api2d": {
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "在这里输入分辨率, 如256x256默认", # 高级参数输入区的显示提示
"AdvancedArgs": True, # 调用时唤起高级参数输入区默认False
"ArgsReminder": "在这里输入分辨率, 如256x256默认", # 高级参数输入区的显示提示
"Function": HotReload(图片生成)
},
})
@ -358,7 +358,7 @@ def get_crazy_functions():
})
except:
print('Load function plugin failed')
try:
from crazy_functions.交互功能函数模板 import 交互功能模板函数
function_plugins.update({
@ -402,9 +402,10 @@ def get_crazy_functions():
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " + 'If the term "agent" is used in this section, it should be translated to "智能体". ',
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 " +
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " +
'If the term "agent" is used in this section, it should be translated to "智能体". ',
"Function": HotReload(Latex翻译中文并重新编译PDF)
}
})
@ -413,16 +414,16 @@ def get_crazy_functions():
"Color": "stop",
"AsButton": False,
"AdvancedArgs": True,
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 "+
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " + 'If the term "agent" is used in this section, it should be translated to "智能体". ',
"ArgsReminder":
"如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 " +
"例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " +
'If the term "agent" is used in this section, it should be translated to "智能体". ',
"Function": HotReload(Latex翻译中文并重新编译PDF)
}
})
except:
print('Load function plugin failed')
try:
from toolbox import get_conf
ENABLE_AUDIO, = get_conf('ENABLE_AUDIO')
@ -437,19 +438,17 @@ def get_crazy_functions():
})
except:
print('Load function plugin failed')
# try:
# from crazy_functions.虚空终端 import 终端
# function_plugins.update({
# "超级终端": {
# "Color": "stop",
# "AsButton": False,
# # "AdvancedArgs": True,
# # "ArgsReminder": "",
# "Function": HotReload(终端)
# }
# })
# except:
# print('Load function plugin failed')
try:
from crazy_functions.虚空终端 import 自动终端
function_plugins.update({
"自动终端": {
"Color": "stop",
"AsButton": False,
"Function": HotReload(自动终端)
}
})
except:
print('Load function plugin failed')
return function_plugins

View File

@ -0,0 +1,100 @@
"""
https://github.com/langchain-ai/langchain/blob/master/docs/extras/modules/model_io/output_parsers/pydantic.ipynb
Example 1.
# Define your desired data structure.
class Joke(BaseModel):
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")
# You can add custom validation logic easily with Pydantic.
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field
Example 2.
# Here's another example, but with a compound typed field.
class Actor(BaseModel):
name: str = Field(description="name of an actor")
film_names: List[str] = Field(description="list of names of films they starred in")
"""
import json, re, logging
PYDANTIC_FORMAT_INSTRUCTIONS = """The output should be formatted as a JSON instance that conforms to the JSON schema below.
As an example, for the schema {{"properties": {{"foo": {{"title": "Foo", "description": "a list of strings", "type": "array", "items": {{"type": "string"}}}}}}, "required": ["foo"]}}
the object {{"foo": ["bar", "baz"]}} is a well-formatted instance of the schema. The object {{"properties": {{"foo": ["bar", "baz"]}}}} is not well-formatted.
Here is the output schema:
```
{schema}
```"""
class GptJsonIO():
def __init__(self, schema):
self.pydantic_object = schema
self.format_instructions = self.generate_format_instructions()
def generate_format_instructions(self):
schema = self.pydantic_object.schema()
# Remove extraneous fields.
reduced_schema = schema
if "title" in reduced_schema:
del reduced_schema["title"]
if "type" in reduced_schema:
del reduced_schema["type"]
# Ensure json in context is well-formed with double quotes.
schema_str = json.dumps(reduced_schema)
return PYDANTIC_FORMAT_INSTRUCTIONS.format(schema=schema_str)
def generate_output(self, text):
# Greedy search for 1st json candidate.
match = re.search(
r"\{.*\}", text.strip(), re.MULTILINE | re.IGNORECASE | re.DOTALL
)
json_str = ""
if match: json_str = match.group()
json_object = json.loads(json_str, strict=False)
final_object = self.pydantic_object.parse_obj(json_object)
return final_object
def generate_repair_prompt(self, broken_json, error):
prompt = "Fix a broken json string.\n\n" + \
"(1) The broken json string need to fix is: \n\n" + \
"```" + "\n" + \
broken_json + "\n" + \
"```" + "\n\n" + \
"(2) The error message is: \n\n" + \
error + "\n\n" + \
"Now, fix this json string. \n\n"
return prompt
def generate_output_auto_repair(self, response, gpt_gen_fn):
"""
response: string containing canidate json
gpt_gen_fn: gpt_gen_fn(inputs, sys_prompt)
"""
try:
result = self.generate_output(response)
except Exception as e:
try:
logging.info(f'Repairing json{response}')
repair_prompt = self.generate_repair_prompt(broken_json = response, error=repr(e))
result = self.generate_output(gpt_gen_fn(repair_prompt, self.generate_format_instructions()))
logging.info('Repaire json success.')
except Exception as e:
# 没辙了,放弃治疗
logging.info('Repaire json fail.')
raise RuntimeError('Cannot repair json.', str(e))
return result

View File

@ -0,0 +1,66 @@
from pydantic import BaseModel, Field
from typing import List
from toolbox import update_ui_lastest_msg
from request_llm.bridge_all import predict_no_ui_long_connection
from crazy_functions.json_fns.pydantic_io import GptJsonIO
import copy, json, pickle, os, sys
def modify_configuration_hot(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention):
import config
# ⭐ ⭐ ⭐ 读取可配置项目条目
from enum import Enum
names = {}
for k, v in config.__dict__.items():
if k.startswith('__'): continue
names.update({k:k})
# if len(names) > 20: break # 限制最多前10个配置项如果太多了会导致gpt无法理解
ConfigOptions = Enum('ConfigOptions', names)
class ModifyConfigurationIntention(BaseModel):
which_config_to_modify: ConfigOptions = Field(description="the name of the configuration to modify, you must choose from one of the ConfigOptions enum.", default=None)
new_option_value: str = Field(description="the new value of the option", default=None)
# ⭐ ⭐ ⭐ 分析用户意图
yield from update_ui_lastest_msg(lastmsg=f"正在执行任务: {txt}\n\n读取新配置中", chatbot=chatbot, history=history, delay=0)
gpt_json_io = GptJsonIO(ModifyConfigurationIntention)
inputs = "Analyze how to change configuration according to following user input, answer me with json: \n\n" + \
">>" + txt + '\n\n' + \
gpt_json_io.format_instructions
run_gpt_fn = lambda inputs, sys_prompt: predict_no_ui_long_connection(
inputs=inputs, llm_kwargs=llm_kwargs, history=[], sys_prompt=sys_prompt, observe_window=[])
user_intention = gpt_json_io.generate_output_auto_repair(run_gpt_fn(inputs, ""), run_gpt_fn)
explicit_conf = user_intention.which_config_to_modify.value
ok = (explicit_conf in txt)
if ok:
yield from update_ui_lastest_msg(
lastmsg=f"正在执行任务: {txt}\n\n新配置{explicit_conf}={user_intention.new_option_value}",
chatbot=chatbot, history=history, delay=1
)
yield from update_ui_lastest_msg(
lastmsg=f"正在执行任务: {txt}\n\n新配置{explicit_conf}={user_intention.new_option_value}\n\n正在修改配置中",
chatbot=chatbot, history=history, delay=2
)
# ⭐ ⭐ ⭐ 立即应用配置
from toolbox import set_conf
set_conf(explicit_conf, user_intention.new_option_value)
yield from update_ui_lastest_msg(
lastmsg=f"正在执行任务: {txt}\n\n配置修改完成,重新页面即可生效。", chatbot=chatbot, history=history, delay=1
)
else:
yield from update_ui_lastest_msg(
lastmsg=f"失败,如果需要配置{explicit_conf},您需要明确说明并在指令中提到它。", chatbot=chatbot, history=history, delay=5
)
def modify_configuration_reboot(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention):
yield from modify_configuration_hot(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention)
yield from update_ui_lastest_msg(
lastmsg=f"正在执行任务: {txt}\n\n配置修改完成,五秒后即将重启!若出现报错请无视即可。", chatbot=chatbot, history=history, delay=5
)
os.execl(sys.executable, sys.executable, *sys.argv)

View File

@ -1,119 +1,108 @@
from pydantic import BaseModel, Field
from typing import List
from toolbox import CatchException, update_ui, gen_time_str
from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
from .crazy_utils import input_clipping
import copy, json
from toolbox import update_ui_lastest_msg
from request_llm.bridge_all import predict_no_ui_long_connection
from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
from crazy_functions.crazy_utils import input_clipping
from crazy_functions.json_fns.pydantic_io import GptJsonIO
from crazy_functions.vt_fns.vt_modify_config import modify_configuration_hot
from crazy_functions.vt_fns.vt_modify_config import modify_configuration_reboot
from enum import Enum
import copy, json, pickle, os, sys
def get_fn_lib():
return {
"BatchTranslatePDFDocuments_MultiThreaded": {
"module": "crazy_functions.批量翻译PDF文档_多线程",
"function": "批量翻译PDF文档",
"description": "Translate PDF Documents",
"arg_1_description": "A path containing pdf files.",
},
"SummarizingWordDocuments": {
"module": "crazy_functions.总结word文档",
"function": "总结word文档",
"description": "Summarize Word Documents",
"arg_1_description": "A path containing Word files.",
},
"ImageGeneration": {
"module": "crazy_functions.图片生成",
"function": "图片生成",
"description": "Generate a image that satisfies some description.",
"arg_1_description": "Descriptions about the image to be generated.",
},
"TranslateMarkdownFromEnglishToChinese": {
"module": "crazy_functions.批量Markdown翻译",
"function": "Markdown中译英",
"description": "Translate Markdown Documents from English to Chinese.",
"arg_1_description": "A path containing Markdown files.",
},
"SummaryAudioVideo": {
"module": "crazy_functions.总结音视频",
"function": "总结音视频",
"description": "Get text from a piece of audio and summarize this audio.",
"arg_1_description": "A path containing audio files.",
},
}
class IntentionEnum(str, Enum):
ModifyConfiguration = 'ModifyConfiguration'
ExecutePlugin = 'ExecutePlugin'
Chat = 'Chat'
functions = [
{
"name": k,
"description": v['description'],
"parameters": {
"type": "object",
"properties": {
"plugin_arg_1": {
"type": "string",
"description": v['arg_1_description'],
},
},
"required": ["plugin_arg_1"],
},
} for k, v in get_fn_lib().items()
]
class UserIntention(BaseModel):
user_prompt: str = Field(description="the content of user input", default="")
intention_type: IntentionEnum = Field(description="the type of user intention", default=IntentionEnum.Chat)
user_provide_file: bool = Field(description="whether the user provides a path to a file", default=False)
user_provide_url: bool = Field(description="whether the user provides a url", default=False)
def inspect_dependency(chatbot, history):
return True
def execute_plugin(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention):
# 没写完
pass
def eval_code(code, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
import importlib
try:
tmp = get_fn_lib()[code['name']]
fp, fn = tmp['module'], tmp['function']
fn_plugin = getattr(importlib.import_module(fp, fn), fn)
arg = json.loads(code['arguments'])['plugin_arg_1']
yield from fn_plugin(arg, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port)
except:
from toolbox import trimmed_format_exc
chatbot.append(["执行错误", f"\n```\n{trimmed_format_exc()}\n```\n"])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
def get_code_block(reply):
import re
pattern = r"```([\s\S]*?)```" # regex pattern to match code blocks
matches = re.findall(pattern, reply) # find all code blocks in text
if len(matches) != 1:
raise RuntimeError("GPT is not generating proper code.")
return matches[0].strip('python') # code block
def chat(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention):
gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=txt, inputs_show_user=txt,
llm_kwargs=llm_kwargs, chatbot=chatbot, history=[],
sys_prompt=system_prompt
)
chatbot[-1] = [txt, gpt_say]
history.extend([txt, gpt_say])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
pass
@CatchException
def 终端(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
def 自动终端(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
"""
txt 输入栏用户输入的文本, 例如需要翻译的一段话, 再例如一个包含了待处理文件的路径
txt 输入栏用户输入的文本例如需要翻译的一段话再例如一个包含了待处理文件的路径
llm_kwargs gpt模型参数, 如温度和top_p等, 一般原样传递下去就行
plugin_kwargs 插件模型的参数, 暂时没有用武之地
chatbot 聊天显示框的句柄, 用于显示给用户
history 聊天历史, 前情提要
plugin_kwargs 插件模型的参数, 如温度和top_p等, 一般原样传递下去就行
chatbot 聊天显示框的句柄用于显示给用户
history 聊天历史前情提要
system_prompt 给gpt的静默提醒
web_port 当前软件运行的端口号
"""
# 清空历史, 以免输入溢出
history = []
# 基本信息:功能、贡献者
chatbot.append(["虚空终端插件的功能?", "根据自然语言的描述, 执行任意插件的命令."])
history = [] # 清空历史,以免输入溢出
chatbot.append(("自动终端状态: ", f"正在执行任务: {txt}"))
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# 输入
i_say = txt
# 开始
llm_kwargs_function_call = copy.deepcopy(llm_kwargs)
llm_kwargs_function_call['llm_model'] = 'gpt-call-fn' # 修改调用函数
gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
inputs=i_say, inputs_show_user=txt,
llm_kwargs=llm_kwargs_function_call, chatbot=chatbot, history=[],
sys_prompt=functions
)
# 将代码转为动画
res = json.loads(gpt_say)['choices'][0]
if res['finish_reason'] == 'function_call':
code = json.loads(gpt_say)['choices'][0]
yield from eval_code(code['message']['function_call'], llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port)
else:
chatbot.append(["无法调用相关功能", res])
yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# 初始化插件状态
state = chatbot._cookies.get('plugin_state', None)
if state is not None: state = pickle.loads(state)
else: state = {}
def update_vt_state():
# 赋予插件锁定 锁定插件回调路径,当下一次用户提交时,会直接转到该函数
chatbot._cookies['lock_plugin'] = 'crazy_functions.虚空终端->自动终端'
chatbot._cookies['vt_state'] = pickle.dumps(state)
# ⭐ ⭐ ⭐ 分析用户意图
yield from update_ui_lastest_msg(lastmsg=f"正在执行任务: {txt}\n\n分析用户意图中", chatbot=chatbot, history=history, delay=0)
gpt_json_io = GptJsonIO(UserIntention)
inputs = "Analyze the intention of the user according to following user input: \n\n" + txt + '\n\n' + gpt_json_io.format_instructions
run_gpt_fn = lambda inputs, sys_prompt: predict_no_ui_long_connection(
inputs=inputs, llm_kwargs=llm_kwargs, history=[], sys_prompt=sys_prompt, observe_window=[])
user_intention = gpt_json_io.generate_output_auto_repair(run_gpt_fn(inputs, ""), run_gpt_fn)
# 用户意图: 修改本项目的配置
if user_intention.intention_type == IntentionEnum.ModifyConfiguration:
yield from modify_configuration_reboot(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention)
# 用户意图: 调度插件
if user_intention.intention_type == IntentionEnum.ExecutePlugin:
yield from execute_plugin(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention)
# 用户意图: 聊天
if user_intention.intention_type == IntentionEnum.Chat:
yield from chat(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_intention)
# update_vt_state()
return
# # if state == 'wait_user_keyword':
# # chatbot._cookies['lock_plugin'] = None # 解除插件锁定,避免遗忘导致死锁
# # chatbot._cookies['plugin_state_0001'] = None # 解除插件状态,避免遗忘导致死锁
# # # 解除插件锁定
# # chatbot.append((f"获取关键词:{txt}", ""))
# # yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# # inputs=inputs_show_user=f"Extract all image urls in this html page, pick the first 5 images and show them with markdown format: \n\n {page_return}"
# # gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
# # inputs=inputs, inputs_show_user=inputs_show_user,
# # llm_kwargs=llm_kwargs, chatbot=chatbot, history=[],
# # sys_prompt="When you want to show an image, use markdown format. e.g. ![image_description](image_url). If there are no image url provided, answer 'no image url provided'"
# # )
# # chatbot[-1] = [chatbot[-1][0], gpt_say]
# yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
# return

View File

@ -9,6 +9,8 @@ validate_path() # 返回项目根路径
from tests.test_utils import plugin_test
if __name__ == "__main__":
plugin_test(plugin='crazy_functions.虚空终端->自动终端', main_input='修改api-key为sk-jhoejriotherjep')
# plugin_test(plugin='crazy_functions.命令行助手->命令行助手', main_input='查看当前的docker容器列表')
# plugin_test(plugin='crazy_functions.解析项目源代码->解析一个Python项目', main_input="crazy_functions/test_project/python/dqn")
@ -19,7 +21,7 @@ if __name__ == "__main__":
# plugin_test(plugin='crazy_functions.批量Markdown翻译->Markdown中译英', main_input="README.md")
plugin_test(plugin='crazy_functions.批量翻译PDF文档_多线程->批量翻译PDF文档', main_input='crazy_functions/test_project/pdf_and_word/aaai.pdf')
# plugin_test(plugin='crazy_functions.批量翻译PDF文档_多线程->批量翻译PDF文档', main_input='crazy_functions/test_project/pdf_and_word/aaai.pdf')
# plugin_test(plugin='crazy_functions.谷歌检索小助手->谷歌检索小助手', main_input="https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=auto+reinforcement+learning&btnG=")