"""命令行解析器"""
import copy
import re
from typing import Any
from apps.llm.patterns.json_gen import Json
from apps.scheduler.call.cmd.assembler import CommandlineAssembler
class Solver:
"""解析命令行生成器"""
@staticmethod
async def _get_option(agent_input: str, collection_name: str, binary_name: str, subcmd_name: str, spec: dict[str, Any]) -> tuple[str, str]:
"""选择最匹配的命令行参数"""
global_options = CommandlineAssembler.get_data("global_option", agent_input, collection_name, binary_name, num=2)
options = CommandlineAssembler.get_data("option", agent_input, collection_name, binary_name, subcmd_name, 3)
choices = options + global_options
result, result_desc = await CommandlineAssembler.select_option(agent_input, choices)
option_type = ""
if not option_type:
for opt in global_options:
if result == opt["name"]:
option_type = "global_option"
break
if not option_type:
for opt in options:
if result == opt["name"]:
option_type = "option"
break
if option_type == "global_option":
spec = spec[binary_name][1][result]
elif option_type == "option":
spec = spec[binary_name][2][subcmd_name][2][result]
else:
err = "No option found."
raise ValueError(err)
return result, spec, result_desc
@staticmethod
async def _get_value(question: str, description: str, spec: dict[str, Any]) -> dict[str, Any]:
"""根据用户目标和JSON Schema,生成命令行参数"""
gen_input = f"""
用户的目标为: [[{question}]]
依照JSON Schema,生成下列参数:
{description}
严格按照JSON Schema格式输出,不要添加或编造字段。""".format(objective=question, description=description)
return await Json().generate(question=gen_input, background="Empty.", spec=spec)
@staticmethod
async def process_output(output: str, question: str, collection_name: str, binary_name: str, subcmd_name: str, spec: dict[str, Any]) -> tuple[str, str]:
"""对规划器输出的evidence进行解析,生成命令行参数"""
spec_template = {
"type": "object",
"properties": {},
}
opt_spec = copy.deepcopy(spec_template)
full_opt_desc = ""
arg_spec = copy.deepcopy(spec_template)
full_arg_desc = ""
lines = output.split("\n")
for line in lines:
if not line.startswith("Plan:"):
continue
evidence = re.search(r"#E.*", line)
if not evidence:
continue
evidence = evidence.group(0)
if "Option" in evidence:
action_input = re.search(r"\[.*\]", evidence)
if not action_input:
continue
action_input = action_input.group(0)
action_input = action_input.rstrip("]").lstrip("[")
opt_name, single_opt_spec, opt_desc = await Solver._get_option(action_input, collection_name, binary_name, subcmd_name, spec)
opt_spec["properties"].update({opt_name: single_opt_spec})
full_opt_desc += f"- {opt_name}: {opt_desc}\n"
elif "Argument" in evidence:
name = re.search(r"\[.*\]", evidence)
if not name:
continue
name = name.group(0)
name = name.rstrip("]").lstrip("[")
name = name.lower()
if name not in spec[binary_name][2][subcmd_name][3]:
continue
value = re.search(r"<.*>", evidence)
if not value:
continue
value = value.group(0)
value = value.rstrip(">").lstrip("<")
arg_spec["properties"].update({name: spec[binary_name][2][subcmd_name][3][name]})
arg_desc = spec[binary_name][2][subcmd_name][3][name]["description"]
full_arg_desc += f"- {name}: {arg_desc}. 可能的值: {value}.\n"
result_dict = {
"opts": {},
"args": {},
}
result_dict["opts"].update(await Solver._get_value(question, full_opt_desc, opt_spec))
result_dict["args"].update(await Solver._get_value(question, full_arg_desc, arg_spec))
result_cmd = CommandlineAssembler.convert_dict_to_cmdline(result_dict, spec[binary_name][2][subcmd_name][1])
full_description = "各命令行标志的描述为:\n" + full_opt_desc + "\n\n各参数的描述为:\n" + full_arg_desc
return result_cmd, full_description