Merge pull request #121 from wugren/MVP

Support multimodal input
This commit is contained in:
Liu Zhicong
2024-02-27 10:31:36 -08:00
committed by GitHub
9 changed files with 211 additions and 150 deletions
+12 -10
View File
@@ -16,6 +16,8 @@ Only clearly specifying the task you completed can be completed independently.
type="AgentMessageProcess" type="AgentMessageProcess"
# TODO: 是否应该自动记录 inner function和action的执行细节 # TODO: 是否应该自动记录 inner function和action的执行细节
mutil_model="gpt-4-vision-preview" mutil_model="gpt-4-vision-preview"
asr_model="openai-whisper"
tts_model="tts-1"
process_description=""" process_description="""
1. Based on your role and the existing information, please think and then make a brief and efficient reply. 1. Based on your role and the existing information, please think and then make a brief and efficient reply.
@@ -30,7 +32,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
{ {
think:'$think step-by-step to be sure you have the right reply.' think:'$think step-by-step to be sure you have the right reply.'
resp: '$What you want to reply', resp: '$What you want to reply',
actions: [{ actions: [{
name: '$action_name', name: '$action_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}] }]
@@ -63,7 +65,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
{ {
think:'$think step-by-step to be sure you can triage tasks well.' think:'$think step-by-step to be sure you can triage tasks well.'
resp : '$determine, summary what you do', resp : '$determine, summary what you do',
actions: [{ actions: [{
name: '$action_name', name: '$action_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}] }]
@@ -89,7 +91,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
{ {
think:'$thinking step by step to ensure the accurate and efficient processing task.', think:'$thinking step by step to ensure the accurate and efficient processing task.',
resp:'$determine, summary what you do' resp:'$determine, summary what you do'
actions: [{ actions: [{
name: '$action_name', name: '$action_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}] }]
@@ -114,7 +116,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
{ {
think:'$think step-by-step to be sure you have the right result.', think:'$think step-by-step to be sure you have the right result.',
resp : '$determine, summary what you will do', resp : '$determine, summary what you will do',
actions: [{ actions: [{
name: '$action_name', name: '$action_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}] }]
@@ -124,11 +126,11 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
llm_context.actions.enable = ["agent.workspace.cancel_task","agent.workspace.update_task"] llm_context.actions.enable = ["agent.workspace.cancel_task","agent.workspace.update_task"]
context="Your Principal now in {location}, time: {now}, weather: {weather}." context="Your Principal now in {location}, time: {now}, weather: {weather}."
[behavior.do] [behavior.do]
# do TODO # do TODO
type="AgentDo" type="AgentDo"
process_description=""" process_description="""
The input is a TODO comes from a Task. The input is a TODO comes from a Task.
1. Your task is to combine your role definition, tools on hand, known information, and complete a certain Todo.After completing the Todo, you will get a tip of $ 200. 1. Your task is to combine your role definition, tools on hand, known information, and complete a certain Todo.After completing the Todo, you will get a tip of $ 200.
2. 8000 word limit for short term memory. Your short term memory is short, so immediately save important information to files. 2. 8000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.
3. In the process of completing Todo, you should think first and then execute. During the execution, you can use functions to access the results of the front steps. 3. In the process of completing Todo, you should think first and then execute. During the execution, you can use functions to access the results of the front steps.
@@ -141,10 +143,10 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
{ {
think:'$think step by step, how to complete the todo', think:'$think step by step, how to complete the todo',
resp: '$simport report about what you do', resp: '$simport report about what you do',
actions: [{ actions: [{
name: '$action1_name', name: '$action1_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}, ... }, ...
] ]
} }
""" """
@@ -168,7 +170,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
resp:'$think step by step, how to check the todo', resp:'$think step by step, how to check the todo',
name: '$action1_name', name: '$action1_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}, ... }, ...
] ]
} }
""" """
@@ -197,7 +199,7 @@ The Response must be directly parsed by `python json.loads`. Here is an example:
resp:'$Summary in one sentence', resp:'$Summary in one sentence',
name: '$action1_name', name: '$action1_name',
$param_name: '$parm' #Optional, fill in only if the action has parameters. $param_name: '$parm' #Optional, fill in only if the action has parameters.
}, ... }, ...
] ]
} }
""" """
+29 -26
View File
@@ -84,7 +84,7 @@ class AIAgent(BaseAIAgent):
todo_prompts = {} todo_prompts = {}
todo_prompts[TodoListType.TO_WORK] = { todo_prompts[TodoListType.TO_WORK] = {
"do": None, "do": None,
"check": None, "check": None,
"review": None, "review": None,
} }
todo_prompts[TodoListType.TO_LEARN] = { todo_prompts[TodoListType.TO_LEARN] = {
@@ -103,12 +103,12 @@ class AIAgent(BaseAIAgent):
self.prviate_workspace : AgentWorkspace = None self.prviate_workspace : AgentWorkspace = None
self.behaviors:Dict[str,BaseLLMProcess] = {} self.behaviors:Dict[str,BaseLLMProcess] = {}
async def initial(self,params:Dict = None): async def initial(self,params:Dict = None):
self.base_dir = f"{AIStorage.get_instance().get_myai_dir()}/agent_data/{self.agent_id}" self.base_dir = f"{AIStorage.get_instance().get_myai_dir()}/agent_data/{self.agent_id}"
memory_base_dir = f"{self.base_dir}/memory" memory_base_dir = f"{self.base_dir}/memory"
self.memory = AgentMemory(self.agent_id,memory_base_dir) self.memory = AgentMemory(self.agent_id,memory_base_dir)
self.prviate_workspace = AgentWorkspace(self.agent_id) self.prviate_workspace = AgentWorkspace(self.agent_id)
init_params = {} init_params = {}
init_params["memory"] = self.memory init_params["memory"] = self.memory
init_params["workspace"] = self.prviate_workspace init_params["workspace"] = self.prviate_workspace
@@ -117,7 +117,7 @@ class AIAgent(BaseAIAgent):
if init_result is False: if init_result is False:
logger.error(f"llm process {process_name} initial failed! initial return False") logger.error(f"llm process {process_name} initial failed! initial return False")
return False return False
self.wake_up() self.wake_up()
return True return True
@@ -151,7 +151,7 @@ class AIAgent(BaseAIAgent):
self.enable_timestamp = bool(config["enable_timestamp"]) self.enable_timestamp = bool(config["enable_timestamp"])
if config.get("history_len"): if config.get("history_len"):
self.history_len = int(config.get("history_len")) self.history_len = int(config.get("history_len"))
#load all LLMProcess #load all LLMProcess
self.behaviors = {} self.behaviors = {}
behaviors = config.get("behavior") behaviors = config.get("behavior")
@@ -201,12 +201,12 @@ class AIAgent(BaseAIAgent):
context_info["owner"] = AIStorage.get_instance().get_user_config().get_value("username") context_info["owner"] = AIStorage.get_instance().get_user_config().get_value("username")
return context_info return context_info
async def llm_process_msg(self,msg:AgentMsg) -> AgentMsg: async def llm_process_msg(self,msg:AgentMsg) -> AgentMsg:
need_process:bool = True need_process:bool = True
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG: if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
need_process = False need_process = False
session_topic = msg.target + "#" + msg.topic session_topic = msg.target + "#" + msg.topic
chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.memory.memory_db) chatsession = AIChatSession.get_session(self.agent_id,session_topic,self.memory.memory_db)
if msg.mentions is not None: if msg.mentions is not None:
@@ -218,7 +218,7 @@ class AIAgent(BaseAIAgent):
chatsession.append(msg) chatsession.append(msg)
resp_msg = msg.create_group_resp_msg(self.agent_id,"") resp_msg = msg.create_group_resp_msg(self.agent_id,"")
return resp_msg return resp_msg
context_info = await self._get_context_info() context_info = await self._get_context_info()
input_parms = { input_parms = {
"msg":msg, "msg":msg,
@@ -232,8 +232,11 @@ class AIAgent(BaseAIAgent):
elif llm_result.state == LLMResultStates.IGNORE: elif llm_result.state == LLMResultStates.IGNORE:
return None return None
else: # OK else: # OK
resp_msg = llm_result.raw_result.get("_resp_msg") if llm_result.raw_result is not None:
return resp_msg resp_msg = llm_result.raw_result.get("_resp_msg")
return resp_msg
else:
return msg.create_resp_msg(llm_result.resp)
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg: async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
return await self.llm_process_msg(msg) return await self.llm_process_msg(msg)
@@ -264,7 +267,7 @@ class AIAgent(BaseAIAgent):
else: else:
logger.info(f"llm process self thinking ok!,think is:{llm_result.resp}") logger.info(f"llm process self thinking ok!,think is:{llm_result.resp}")
self.memory.set_last_think_time(time.time()) self.memory.set_last_think_time(time.time())
self.agent_energy -= 2 self.agent_energy -= 2
return return
async def llm_triage_tasklist(self): async def llm_triage_tasklist(self):
@@ -273,7 +276,7 @@ class AIAgent(BaseAIAgent):
if self.prviate_workspace: if self.prviate_workspace:
filter = {} filter = {}
filter["state"] = AgentTaskState.TASK_STATE_WAIT filter["state"] = AgentTaskState.TASK_STATE_WAIT
tasklist:List[AgentTask]= await self.prviate_workspace.task_mgr.list_task(filter) tasklist:List[AgentTask]= await self.prviate_workspace.task_mgr.list_task(filter)
@@ -281,8 +284,8 @@ class AIAgent(BaseAIAgent):
if len(tasklist) > 0: if len(tasklist) > 0:
simple_list:List[Dict] = [] simple_list:List[Dict] = []
for task in tasklist: for task in tasklist:
simple_list.append(task.to_simple_dict()) simple_list.append(task.to_simple_dict())
input_parms = { input_parms = {
"tasklist":simple_list, "tasklist":simple_list,
"context_info": await self._get_context_info() "context_info": await self._get_context_info()
@@ -294,7 +297,7 @@ class AIAgent(BaseAIAgent):
logger.info(f"llm process triage_tasks ignore!") logger.info(f"llm process triage_tasks ignore!")
else: else:
logger.info(f"llm process triage_tasks ok!,think is:{llm_result.resp}") logger.info(f"llm process triage_tasks ok!,think is:{llm_result.resp}")
self.agent_energy -= 3 self.agent_energy -= 3
# for agent_task in tasklist: # for agent_task in tasklist:
# if self.agent_energy <= 0: # if self.agent_energy <= 0:
@@ -314,7 +317,7 @@ class AIAgent(BaseAIAgent):
# else: # else:
# determine = llm_result.raw_result.get("determine") # determine = llm_result.raw_result.get("determine")
# logger.info(f"llm process review_task ok!,think is:{determine}") # logger.info(f"llm process review_task ok!,think is:{determine}")
# self.agent_energy -= 1 # self.agent_energy -= 1
async def llm_do_todo(self, todo: AgentTodo): async def llm_do_todo(self, todo: AgentTodo):
llm_process : BaseLLMProcess = self.behaviors.get("do") llm_process : BaseLLMProcess = self.behaviors.get("do")
@@ -350,7 +353,7 @@ class AIAgent(BaseAIAgent):
logger.info(f"llm process check_todo ok!,think is:{llm_result.resp}") logger.info(f"llm process check_todo ok!,think is:{llm_result.resp}")
self.agent_energy -= 1 self.agent_energy -= 1
return return
async def llm_plan_task(self,task:AgentTask): async def llm_plan_task(self,task:AgentTask):
llm_process : BaseLLMProcess = self.behaviors.get("plan_task") llm_process : BaseLLMProcess = self.behaviors.get("plan_task")
@@ -398,7 +401,7 @@ class AIAgent(BaseAIAgent):
async def _on_timer(self): async def _on_timer(self):
await asyncio.sleep(5) await asyncio.sleep(5)
while True: while True:
try: try:
now = time.time() now = time.time()
if self.last_recover_time is None: if self.last_recover_time is None:
@@ -419,7 +422,7 @@ class AIAgent(BaseAIAgent):
#filter["state"] = AgentTaskState.TASK_STATE_WAIT #filter["state"] = AgentTaskState.TASK_STATE_WAIT
filter = None filter = None
task_list:List[AgentTask] = await self.prviate_workspace.task_mgr.list_task(filter) task_list:List[AgentTask] = await self.prviate_workspace.task_mgr.list_task(filter)
for task in task_list: for task in task_list:
if self.agent_energy <= 0: if self.agent_energy <= 0:
break break
@@ -456,18 +459,18 @@ class AIAgent(BaseAIAgent):
task = await self.prviate_workspace.task_mgr.get_task(task.task_id) task = await self.prviate_workspace.task_mgr.get_task(task.task_id)
if task.state == AgentTaskState.TASK_STATE_WAITING_REVIEW: if task.state == AgentTaskState.TASK_STATE_WAITING_REVIEW:
await self.llm_review_task(task) await self.llm_review_task(task)
await self._self_imporve() await self._self_imporve()
except Exception as e: except Exception as e:
tb_str = traceback.format_exc() tb_str = traceback.format_exc()
logger.error(f"agent {self.agent_id} on timer error:{e},{tb_str}") logger.error(f"agent {self.agent_id} on timer error:{e},{tb_str}")
# Because the LLM itself is very slow, the accuracy of the system processing task is in minutes. # Because the LLM itself is very slow, the accuracy of the system processing task is in minutes.
await asyncio.sleep(30) await asyncio.sleep(30)
+104 -66
View File
@@ -1,5 +1,8 @@
# Old name is behavior, I belive new name "llm_process" is better # Old name is behavior, I belive new name "llm_process" is better
# pylint:disable=E0402 # pylint:disable=E0402
import os.path
from .chatsession import AIChatSession
from ..utils import video_utils,image_utils from ..utils import video_utils,image_utils
from ..proto.compute_task import LLMPrompt,LLMResult,ComputeTaskResult,ComputeTaskResultCode from ..proto.compute_task import LLMPrompt,LLMResult,ComputeTaskResult,ComputeTaskResultCode
@@ -31,11 +34,11 @@ class BaseLLMProcess(ABC):
self.goal:str = None #目标 self.goal:str = None #目标
self.input_example:str= None #输入样例 self.input_example:str= None #输入样例
self.result_example:str = None #llm_result样例 self.result_example:str = None #llm_result样例
self.enable_json_resp = False self.enable_json_resp = False
#None means system default, #None means system default,
# TODO: support abcstract model name like: local-hight,local-low,local-medium,remote-hight,remote-low,remote-medium # TODO: support abcstract model name like: local-hight,local-low,local-medium,remote-hight,remote-low,remote-medium
self.model_name = None self.model_name = None
self.max_token = 1000 # result_token self.max_token = 1000 # result_token
self.max_prompt_token = 1000 # not include input prompt self.max_prompt_token = 1000 # not include input prompt
self.timeout = 1800 # 30 min self.timeout = 1800 # 30 min
@@ -55,8 +58,8 @@ class BaseLLMProcess(ABC):
@abstractmethod @abstractmethod
def prepare_inner_function_context_for_exec(self,inner_func_name:str,parameters:Dict): def prepare_inner_function_context_for_exec(self,inner_func_name:str,parameters:Dict):
return return
@abstractmethod @abstractmethod
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool: async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
pass pass
@@ -76,14 +79,14 @@ class BaseLLMProcess(ABC):
self.max_token = config.get("max_token") self.max_token = config.get("max_token")
if config.get("timeout"): if config.get("timeout"):
self.timeout = config.get("timeout") self.timeout = config.get("timeout")
return True return True
@abstractmethod @abstractmethod
async def initial(self,params:Dict = None) -> bool: async def initial(self,params:Dict = None) -> bool:
pass pass
def _format_content_by_env_value(self,content:str,env)->str: def _format_content_by_env_value(self,content:str,env)->str:
return content.format_map(env) return content.format_map(env)
@@ -120,12 +123,12 @@ class BaseLLMProcess(ABC):
task_result.result_code = ComputeTaskResultCode.ERROR task_result.result_code = ComputeTaskResultCode.ERROR
task_result.error_str = f"prompt too long,can not predict" task_result.error_str = f"prompt too long,can not predict"
return task_result return task_result
if stack_limit > 0: if stack_limit > 0:
inner_functions=prompt.inner_functions inner_functions=prompt.inner_functions
else: else:
inner_functions = None inner_functions = None
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion( task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
prompt, prompt,
@@ -140,7 +143,7 @@ class BaseLLMProcess(ABC):
return task_result return task_result
inner_func_call_node = None inner_func_call_node = None
result_message : dict = task_result.result.get("message") result_message : dict = task_result.result.get("message")
if result_message: if result_message:
inner_func_call_node = result_message.get("function_call") inner_func_call_node = result_message.get("function_call")
@@ -163,10 +166,10 @@ class BaseLLMProcess(ABC):
# Action define in prompt, will be execute after llm compute # Action define in prompt, will be execute after llm compute
prompt = await self.prepare_prompt(input) prompt = await self.prepare_prompt(input)
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt,self.model_name) max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt,self.get_llm_model_name())
#if max_result_token < MIN_PREDICT_TOKEN_LEN: #if max_result_token < MIN_PREDICT_TOKEN_LEN:
# return LLMResult.from_error_str(f"prompt too long,can not predict") # return LLMResult.from_error_str(f"prompt too long,can not predict")
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion( task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
prompt, prompt,
resp_mode=resp_mode, resp_mode=resp_mode,
@@ -174,12 +177,12 @@ class BaseLLMProcess(ABC):
max_token=max_result_token, max_token=max_result_token,
inner_functions=prompt.inner_functions, #NOTICE: inner_function in prompt can be a subset of get_inner_function inner_functions=prompt.inner_functions, #NOTICE: inner_function in prompt can be a subset of get_inner_function
timeout=self.timeout)) timeout=self.timeout))
if task_result.result_code != ComputeTaskResultCode.OK: if task_result.result_code != ComputeTaskResultCode.OK:
err_str = f"do_llm_completion error:{task_result.error_str}" err_str = f"do_llm_completion error:{task_result.error_str}"
logger.error(err_str) logger.error(err_str)
return LLMResult.from_error_str(err_str) return LLMResult.from_error_str(err_str)
result_message = task_result.result.get("message") result_message = task_result.result.get("message")
inner_func_call_node = None inner_func_call_node = None
if result_message: if result_message:
@@ -194,7 +197,11 @@ class BaseLLMProcess(ABC):
# parse task_result to LLM Result # parse task_result to LLM Result
if self.enable_json_resp: if self.enable_json_resp:
llm_result = LLMResult.from_json_str(task_result.result_str) try:
llm_result = LLMResult.from_json_str(task_result.result_str)
except Exception as e:
logger.error(f"parse llm result error:{e}")
llm_result = LLMResult.from_str(task_result.result_str)
else: else:
llm_result = LLMResult.from_str(task_result.result_str) llm_result = LLMResult.from_str(task_result.result_str)
@@ -202,7 +209,7 @@ class BaseLLMProcess(ABC):
await self.post_llm_process(llm_result.action_list,input,llm_result) await self.post_llm_process(llm_result.action_list,input,llm_result)
return llm_result return llm_result
class LLMAgentBaseProcess(BaseLLMProcess): class LLMAgentBaseProcess(BaseLLMProcess):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
@@ -211,11 +218,11 @@ class LLMAgentBaseProcess(BaseLLMProcess):
self.process_description:str = None self.process_description:str = None
self.reply_format:str = None self.reply_format:str = None
self.context : str = None self.context : str = None
self.workspace : AgentWorkspace = None # If Workspace is not none , enable Agent Tasklist self.workspace : AgentWorkspace = None # If Workspace is not none , enable Agent Tasklist
self.memory : AgentMemory = None self.memory : AgentMemory = None
self.enable_kb : bool = False self.enable_kb : bool = False
self.kb = None self.kb = None
async def initial(self,params:Dict = None) -> bool: async def initial(self,params:Dict = None) -> bool:
self.memory = params.get("memory") self.memory = params.get("memory")
@@ -227,23 +234,23 @@ class LLMAgentBaseProcess(BaseLLMProcess):
return True return True
async def load_default_config(self) -> bool: async def load_default_config(self) -> bool:
return True return True
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]: async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
if is_load_default: if is_load_default:
await self.load_default_config() await self.load_default_config()
if await super().load_from_config(config) is False: if await super().load_from_config(config) is False:
return False return False
self.role_description = config.get("role_desc") self.role_description = config.get("role_desc")
if self.role_description is None: if self.role_description is None:
logger.error(f"role_description not found in config") logger.error(f"role_description not found in config")
return False return False
if config.get("process_description"): if config.get("process_description"):
self.process_description = config.get("process_description") self.process_description = config.get("process_description")
if config.get("reply_format"): if config.get("reply_format"):
self.reply_format = config.get("reply_format") self.reply_format = config.get("reply_format")
@@ -282,7 +289,7 @@ class LLMAgentBaseProcess(BaseLLMProcess):
return system_prompt_dict return system_prompt_dict
def prepare_inner_function_context_for_exec(self,inner_func_name:str,parameters:Dict): def prepare_inner_function_context_for_exec(self,inner_func_name:str,parameters:Dict):
parameters["_workspace"] = self.workspace parameters["_workspace"] = self.workspace
def get_action_desc(self) -> Dict: def get_action_desc(self) -> Dict:
result = {} result = {}
@@ -290,17 +297,17 @@ class LLMAgentBaseProcess(BaseLLMProcess):
for action in actions_list: for action in actions_list:
result[action.get_name()] = action.get_description() result[action.get_name()] = action.get_description()
return result return result
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction: async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
return self.llm_context.get_ai_function(func_name) return self.llm_context.get_ai_function(func_name)
async def _execute_actions(self,actions:List[ActionNode],action_params:Dict): async def _execute_actions(self,actions:List[ActionNode],action_params:Dict):
for action_item in actions: for action_item in actions:
op : AIAction = self.llm_context.get_ai_action(action_item.name) op : AIAction = self.llm_context.get_ai_action(action_item.name)
if op: if op:
if action_item.parms is None: if action_item.parms is None:
action_item.parms = {} action_item.parms = {}
real_parms = {**action_params,**action_item.parms} real_parms = {**action_params,**action_item.parms}
action_item.parms["_result"] = await op.execute(real_parms) action_item.parms["_result"] = await op.execute(real_parms)
@@ -309,17 +316,19 @@ class LLMAgentBaseProcess(BaseLLMProcess):
logger.warn(f"action {action_item.name} not found") logger.warn(f"action {action_item.name} not found")
return False return False
class AgentMessageProcess(LLMAgentBaseProcess): class AgentMessageProcess(LLMAgentBaseProcess):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.mutil_model = None self.mutil_model = None
self.enable_media2text = False self.enable_media2text = False
self.is_mutil_model = False self.is_mutil_model = False
self.asr_model = None
self.tts_model = None
async def load_default_config(self) -> bool: async def load_default_config(self) -> bool:
return True return True
async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]: async def load_from_config(self, config: dict,is_load_default=True) -> Coroutine[Any, Any, bool]:
if is_load_default: if is_load_default:
await self.load_default_config() await self.load_default_config()
@@ -331,23 +340,26 @@ class AgentMessageProcess(LLMAgentBaseProcess):
if config.get("mutil_model"): if config.get("mutil_model"):
self.mutil_model = config.get("mutil_model") self.mutil_model = config.get("mutil_model")
self.asr_model = config.get("asr_model")
self.tts_model = config.get("tts_model")
def get_llm_model_name(self) -> str: def get_llm_model_name(self) -> str:
if self.is_mutil_model: if self.is_mutil_model:
return self.mutil_model return self.mutil_model
else: else:
return self.model_name return self.model_name
def check_and_to_base64(self, image_path: str) -> str: def check_and_to_base64(self, image_path: str) -> str:
if image_utils.is_file(image_path): if image_utils.is_file(image_path):
return image_utils.to_base64(image_path, (1024, 1024)) return image_utils.to_base64(image_path, (1024, 1024))
else: else:
return image_path return image_path
async def get_prompt_from_msg(self,msg:AgentMsg) -> LLMPrompt: async def get_prompt_from_msg(self,msg:AgentMsg) -> LLMPrompt:
msg_prompt = LLMPrompt() msg_prompt = LLMPrompt()
self.is_mutil_model = False self.is_mutil_model = False
if msg.is_image_msg(): if msg.is_image_msg():
if self.enable_media2text: if self.enable_media2text:
logger.error(f"enable_media2text is not supported yet") logger.error(f"enable_media2text is not supported yet")
else: else:
@@ -358,35 +370,60 @@ class AgentMessageProcess(LLMAgentBaseProcess):
content = [{"type": "text", "text": image_prompt}] content = [{"type": "text", "text": image_prompt}]
content.extend([{"type": "image_url", "image_url": {"url": self.check_and_to_base64(image)}} for image in images]) content.extend([{"type": "image_url", "image_url": {"url": self.check_and_to_base64(image)}} for image in images])
msg_prompt.messages = [{"role": "user", "content": content}] msg_prompt.messages = [{"role": "user", "content": content}]
if self.mutil_model: if self.mutil_model:
self.is_mutil_model = True self.is_mutil_model = True
else: else:
logger.warning(f"mutil_model is not set!") logger.warning(f"mutil_model is not set!")
elif msg.is_video_msg(): elif msg.is_video_msg():
video_prompt, video = msg.get_video_body() if self.enable_media2text:
frames = video_utils.extract_frames(video, (1024, 1024)) logger.error(f"enable_media2text is not supported yet")
if video_prompt is None:
msg_prompt.messages = [{"role": "user", "content": [{"type": "image_url", "image_url": {"url": frame}} for frame in frames]}]
else: else:
content = [{"type": "text", "text": video_prompt}] video_prompt, video = msg.get_video_body()
frames = video_utils.extract_frames(video, (1024, 1024))
audio_file = os.path.splitext(video)[0] + ".mp3"
video_utils.extract_audio(video, audio_file)
voice_content = None
if self.asr_model is not None:
resp = await (ComputeKernel.get_instance().do_speech_to_text(audio_file, model=self.asr_model, prompt=None, response_format="text"))
if resp.result_code == ComputeTaskResultCode.OK:
voice_content = resp.result_str
content = []
if video_prompt is not None:
content.append({"type": "text", "text": video_prompt})
if voice_content is not None and voice_content != "":
content.append({"type": "text", "text": f"Voice content in video:{voice_content}"})
content.extend([{"type": "image_url", "image_url": {"url": frame}} for frame in frames]) content.extend([{"type": "image_url", "image_url": {"url": frame}} for frame in frames])
msg_prompt.messages = [{"role": "user", "content": content}] msg_prompt.messages = [{"role": "user", "content": content}]
if self.mutil_model:
self.is_mutil_model = True
else:
logger.warning(f"mutil_model is not set!")
elif msg.is_audio_msg(): elif msg.is_audio_msg():
audio_file = msg.body if self.enable_media2text:
resp = await (ComputeKernel.get_instance().do_speech_to_text(audio_file, None, prompt=None, response_format="text")) logger.error(f"enable_media2text is not supported yet")
if resp.result_code != ComputeTaskResultCode.OK:
error_resp = msg.create_error_resp(resp.error_str)
return error_resp
else: else:
msg.body = resp.result_str prompt, audio_file = msg.get_audio_body()
msg_prompt.messages = [{"role":"user","content":resp.result_str}] resp = await (ComputeKernel.get_instance().do_speech_to_text(audio_file, model=self.asr_model, prompt=None, response_format="text"))
if resp.result_code != ComputeTaskResultCode.OK:
error_resp = msg.create_error_resp(resp.error_str)
return error_resp
else:
if prompt == "":
msg.body = resp.result_str
msg_prompt.messages = [{"role":"user","content":resp.result_str}]
else:
msg.body = f"{prompt}\nVoice content:{resp.result_str}"
msg_prompt.messages = [{"role":"user","content": prompt}, {"role": "user", "content": f"Voice content:{resp.result_str}"}]
else: else:
msg_prompt.messages = [{"role":"user","content":msg.body}] msg_prompt.messages = [{"role":"user","content":msg.body}]
return msg_prompt return msg_prompt
async def sender_info(self,msg:AgentMsg)->str: async def sender_info(self,msg:AgentMsg)->str:
sender_id = msg.sender sender_id = msg.sender
#TODO Is sender an agent? #TODO Is sender an agent?
@@ -400,14 +437,14 @@ class AgentMessageProcess(LLMAgentBaseProcess):
async def get_log_summary(self,msg:AgentMsg)->str: async def get_log_summary(self,msg:AgentMsg)->str:
return None return None
async def get_extend_known_info(self,msg:AgentMsg,prompt:LLMPrompt)->str: async def get_extend_known_info(self,msg:AgentMsg,prompt:LLMPrompt)->str:
return None return None
async def prepare_prompt(self,input:Dict) -> LLMPrompt: async def prepare_prompt(self,input:Dict) -> LLMPrompt:
prompt = LLMPrompt() prompt = LLMPrompt()
# User Prompt # User Prompt
## Input Msg ## Input Msg
msg : AgentMsg = input.get("msg") msg : AgentMsg = input.get("msg")
context_info = input.get("context_info") context_info = input.get("context_info")
@@ -422,8 +459,8 @@ class AgentMessageProcess(LLMAgentBaseProcess):
## 通用的角色相关的系统提示词 ## 通用的角色相关的系统提示词
system_prompt_dict = self.prepare_role_system_prompt(context_info) system_prompt_dict = self.prepare_role_system_prompt(context_info)
## 已知信息 ## 已知信息
known_info = {} known_info = {}
#prompt.append_system_message(self.known_info_tips) #prompt.append_system_message(self.known_info_tips)
### 信息发送者资料 ### 信息发送者资料
@@ -442,23 +479,23 @@ class AgentMessageProcess(LLMAgentBaseProcess):
known_info["summary"] = summary known_info["summary"] = summary
#prompt.append_system_message(await self.get_log_summary(self,msg)) #prompt.append_system_message(await self.get_log_summary(self,msg))
system_prompt_dict["known_info"] = known_info system_prompt_dict["known_info"] = known_info
prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions()) prompt.inner_functions =LLMProcessContext.aifunctions_to_inner_functions(self.llm_context.get_all_ai_functions())
if self.workspace: if self.workspace:
#TODO eanble workspace functions? #TODO eanble workspace functions?
logger.info(f"workspace is not none,enable workspace functions") logger.info(f"workspace is not none,enable workspace functions")
## 给予查询KB的权限 ## 给予查询KB的权限
if self.enable_kb: if self.enable_kb:
logger.info(f"enable kb") logger.info(f"enable kb")
prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False)) prompt.append_system_message(json.dumps(system_prompt_dict,ensure_ascii=False))
## 扩展已知信息 (这可能是一个LLM过程) ## 扩展已知信息 (这可能是一个LLM过程)
prompt.append_system_message(await self.get_extend_known_info(msg,prompt)) prompt.append_system_message(await self.get_extend_known_info(msg,prompt))
return prompt return prompt
async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool: async def post_llm_process(self,actions:List[ActionNode],input:Dict,llm_result:LLMResult) -> bool:
msg:AgentMsg = input.get("msg") msg:AgentMsg = input.get("msg")
@@ -466,14 +503,15 @@ class AgentMessageProcess(LLMAgentBaseProcess):
resp_msg = msg.create_group_resp_msg(self.memory.agent_id,llm_result.resp) resp_msg = msg.create_group_resp_msg(self.memory.agent_id,llm_result.resp)
else: else:
resp_msg = msg.create_resp_msg(llm_result.resp) resp_msg = msg.create_resp_msg(llm_result.resp)
llm_result.raw_result["_resp_msg"] = resp_msg if llm_result.raw_result is not None:
llm_result.raw_result["_resp_msg"] = resp_msg
action_params = {} action_params = {}
action_params["_input"] = input action_params["_input"] = input
action_params["_memory"] = self.memory action_params["_memory"] = self.memory
action_params["_workspace"] = self.workspace action_params["_workspace"] = self.workspace
action_params["_resp_msg"] = resp_msg action_params["_resp_msg"] = resp_msg
action_params["_llm_result"] = llm_result action_params["_llm_result"] = llm_result
action_params["_agentid"] = self.memory.agent_id action_params["_agentid"] = self.memory.agent_id
action_params["_start_at"] = datetime.now() action_params["_start_at"] = datetime.now()
@@ -482,7 +520,7 @@ class AgentMessageProcess(LLMAgentBaseProcess):
chatsession = self.memory.get_session_from_msg(msg) chatsession = self.memory.get_session_from_msg(msg)
chatsession.append(msg) chatsession.append(msg)
chatsession.append(resp_msg) chatsession.append(resp_msg)
return True return True
@@ -567,11 +605,11 @@ class AgentSelfThinking(LLMAgentBaseProcess):
record_list = input.get("record_list") record_list = input.get("record_list")
context_info = input.get("context_info") context_info = input.get("context_info")
if record_list is None: if record_list is None:
logger.error(f"AgentSelfThinking prepare_prompt failed! input not found") logger.error(f"AgentSelfThinking prepare_prompt failed! input not found")
return None return None
prompt.append_user_message(json.dumps(record_list,ensure_ascii=False)) prompt.append_user_message(json.dumps(record_list,ensure_ascii=False))
system_prompt_dict = self.prepare_role_system_prompt(context_info) system_prompt_dict = self.prepare_role_system_prompt(context_info)
@@ -594,7 +632,7 @@ class AgentSelfThinking(LLMAgentBaseProcess):
if known_experience_list: if known_experience_list:
known_info["known_experience_list"] = known_experience_list known_info["known_experience_list"] = known_experience_list
have_known_info = True have_known_info = True
if have_known_info: if have_known_info:
system_prompt_dict["known_info"] = known_info system_prompt_dict["known_info"] = known_info
@@ -626,7 +664,7 @@ class AgentSelfLearning(BaseLLMProcess):
async def prepare_prompt(self) -> LLMPrompt: async def prepare_prompt(self) -> LLMPrompt:
prompt = LLMPrompt() prompt = LLMPrompt()
pass pass
async def get_inner_function_for_exec(self,func_name:str) -> AIFunction: async def get_inner_function_for_exec(self,func_name:str) -> AIFunction:
pass pass
@@ -636,7 +674,7 @@ class AgentSelfLearning(BaseLLMProcess):
class AgentSelfImprove(BaseLLMProcess): class AgentSelfImprove(BaseLLMProcess):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
+22 -14
View File
@@ -80,16 +80,16 @@ class LLMPrompt:
def append_system_message(self,content:str): def append_system_message(self,content:str):
if content is None: if content is None:
return return
if self.system_message is None: if self.system_message is None:
self.system_message = {"role":"system","content":content} self.system_message = {"role":"system","content":content}
else: else:
self.system_message["content"] += content self.system_message["content"] += content
def append_user_message(self,content:str): def append_user_message(self,content:str):
if content is None: if content is None:
return return
self.messages.append({"role":"user","content":content}) self.messages.append({"role":"user","content":content})
def as_str(self)->str: def as_str(self)->str:
@@ -109,13 +109,13 @@ class LLMPrompt:
result.append(self.system_message) result.append(self.system_message)
result.extend(self.messages) result.extend(self.messages)
return result return result
def append(self,prompt:'LLMPrompt'): def append(self,prompt:'LLMPrompt'):
if prompt is None: if prompt is None:
return return
if prompt.inner_functions: if prompt.inner_functions:
if self.inner_functions is None: if self.inner_functions is None:
self.inner_functions = copy.deepcopy(prompt.inner_functions) self.inner_functions = copy.deepcopy(prompt.inner_functions)
@@ -164,8 +164,8 @@ class LLMResult:
@classmethod @classmethod
def from_error_str(self,error_str:str) -> 'LLMResult': def from_error_str(self,error_str:str) -> 'LLMResult':
r = LLMResult() r = LLMResult()
r.state = "error" r.state = LLMResultStates.ERROR
r.compute_error_str = error_str r.error_str = error_str
return r return r
@classmethod @classmethod
@@ -177,7 +177,7 @@ class LLMResult:
if llm_json_str == "**IGNORE**": if llm_json_str == "**IGNORE**":
r.state = LLMResultStates.IGNORE r.state = LLMResultStates.IGNORE
return r return r
r.state = LLMResultStates.OK r.state = LLMResultStates.OK
llm_json = json.loads(llm_json_str) llm_json = json.loads(llm_json_str)
@@ -198,7 +198,7 @@ class LLMResult:
func_name = str_list[0] func_name = str_list[0]
params = str_list[1:] params = str_list[1:]
return func_name, params return func_name, params
@classmethod @classmethod
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult': def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
r = LLMResult() r = LLMResult()
@@ -210,8 +210,14 @@ class LLMResult:
r.state = LLMResultStates.IGNORE r.state = LLMResultStates.IGNORE
return r return r
if llm_result_str[0] == "{": try:
return LLMResult.from_json_str(llm_result_str) if llm_result_str[0] == "{":
return LLMResult.from_json_str(llm_result_str)
if llm_result_str.lstrip().rstrip().startswith("```json"):
return LLMResult.from_json_str(llm_result_str[7:-3])
except:
pass
lines = llm_result_str.splitlines() lines = llm_result_str.splitlines()
is_need_wait = False is_need_wait = False
@@ -226,10 +232,10 @@ class LLMResult:
target_id = action_item.args[0] target_id = action_item.args[0]
msg_content = action_item.body msg_content = action_item.body
new_msg.set("",target_id,msg_content) new_msg.set("",target_id,msg_content)
return True return True
return False return False
@@ -255,6 +261,8 @@ class LLMResult:
r.resp += current_action.dumps() r.resp += current_action.dumps()
else: else:
r.action_list.append(current_action) r.action_list.append(current_action)
r.state = LLMResultStates.OK
return r return r
class ComputeTask: class ComputeTask:
+6
View File
@@ -3,6 +3,7 @@ from typing import List, Tuple
import cv2 import cv2
import numpy as np import numpy as np
import moviepy.editor as mp
def precess_image(image): def precess_image(image):
@@ -120,3 +121,8 @@ def extract_frames(video_path: str, resize: Tuple[int, int] = None, smooth=False
i += 1 i += 1
vidcap.release() vidcap.release()
return frames return frames
def extract_audio(video_path: str, audio_path: str):
my_clip = mp.VideoFileClip(video_path)
my_clip.audio.write_audiofile(audio_path)
@@ -13,6 +13,7 @@ import PyPDF2
import datetime import datetime
from typing import Optional, List from typing import Optional, List
from aios import * from aios import *
from aios.environment.workspace_env import TodoListEnvironment, TodoListType
from .local_file_system import FilesystemEnvironment from .local_file_system import FilesystemEnvironment
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -21,7 +22,7 @@ class MetaDatabase:
def __init__(self,db_path:str): def __init__(self,db_path:str):
self.db_path = db_path self.db_path = db_path
self._get_conn() self._get_conn()
def _get_conn(self): def _get_conn(self):
""" get db connection """ """ get db connection """
local = threading.local() local = threading.local()
@@ -43,7 +44,7 @@ class MetaDatabase:
self._create_tables(conn) self._create_tables(conn)
return conn return conn
def _create_tables(self,conn): def _create_tables(self,conn):
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(''' cursor.execute('''
@@ -68,7 +69,7 @@ class MetaDatabase:
create_time TEXT create_time TEXT
) )
''') ''')
cursor.execute(''' cursor.execute('''
CREATE INDEX IF NOT EXISTS idx_documents_doc_hash CREATE INDEX IF NOT EXISTS idx_documents_doc_hash
ON documents (doc_hash) ON documents (doc_hash)
@@ -110,7 +111,7 @@ class MetaDatabase:
WHERE doc_path = ? WHERE doc_path = ?
''', (doc_hash, doc_path)) ''', (doc_hash, doc_path))
conn.commit() conn.commit()
def get_docs_without_hash(self,limit:int=1024) -> List[str]: def get_docs_without_hash(self,limit:int=1024) -> List[str]:
conn = self._get_conn() conn = self._get_conn()
cursor = conn.cursor() cursor = conn.cursor()
@@ -186,7 +187,7 @@ class MetaDatabase:
row = cursor.fetchone() row = cursor.fetchone()
if row is None: if row is None:
return None return None
# get doc path # get doc path
cursor.execute(''' cursor.execute('''
SELECT doc_path SELECT doc_path
@@ -197,7 +198,7 @@ class MetaDatabase:
if row2 is None: if row2 is None:
return None return None
doc_path = row2[0] doc_path = row2[0]
return { return {
"full_path": doc_path, "full_path": doc_path,
@@ -261,7 +262,7 @@ class LearningCache:
def remove(self, key): def remove(self, key):
with self.cache_lock: with self.cache_lock:
return self.cache.pop(key, None) return self.cache.pop(key, None)
class LocalKnowledgeBase(CompositeEnvironment): class LocalKnowledgeBase(CompositeEnvironment):
def __init__(self, workspace: str) -> None: def __init__(self, workspace: str) -> None:
@@ -275,10 +276,10 @@ class LocalKnowledgeBase(CompositeEnvironment):
async def learn(op:dict): async def learn(op:dict):
full_path = op.get("original_path") full_path = op.get("original_path")
if not full_path: if not full_path:
return return
meta = self.learning_cache.get(full_path) meta = self.learning_cache.get(full_path)
meta.update(op) meta.update(op)
self.add_ai_operation(SimpleAIAction( self.add_ai_operation(SimpleAIAction(
op="learn", op="learn",
description="update knowledge llm summary", description="update knowledge llm summary",
@@ -287,16 +288,16 @@ class LocalKnowledgeBase(CompositeEnvironment):
self.fs = FilesystemEnvironment(self.root_path) self.fs = FilesystemEnvironment(self.root_path)
self.add_env(self.fs) self.add_env(self.fs)
async def get_knowledege_catalog(self,path:str=None,only_dir =True,max_depth:int=5)->str: async def get_knowledege_catalog(self,path:str=None,only_dir =True,max_depth:int=5)->str:
if path: if path:
full_path = f"{self.root_path}/{path}" full_path = f"{self.root_path}/{path}"
else: else:
full_path = self.root_path full_path = self.root_path
catlogs,file_count = await self.get_directory_structure(full_path,max_depth,only_dir) catlogs,file_count = await self.get_directory_structure(full_path,max_depth,only_dir)
return catlogs return catlogs
async def get_directory_structure(self,root_dir, max_depth:int=4, only_dir=True, indent=1): async def get_directory_structure(self,root_dir, max_depth:int=4, only_dir=True, indent=1):
file_count = 0 file_count = 0
structure_str = '' structure_str = ''
@@ -315,11 +316,11 @@ class LocalKnowledgeBase(CompositeEnvironment):
if only_dir is False: if only_dir is False:
for file_name in sub_files: for file_name in sub_files:
structure_str = structure_str + ' ' * (indent+1) + file_name + '\n' structure_str = structure_str + ' ' * (indent+1) + file_name + '\n'
dir_name = os.path.basename(root_dir) dir_name = os.path.basename(root_dir)
dir_info = f"{dir_name} <count: {file_count}>" dir_info = f"{dir_name} <count: {file_count}>"
structure_str = ' ' * indent + dir_info + '\n' + structure_str structure_str = ' ' * indent + dir_info + '\n' + structure_str
@@ -328,7 +329,7 @@ class LocalKnowledgeBase(CompositeEnvironment):
else: else:
return structure_str, file_count return structure_str, file_count
# inner_function # inner_function
async def get_knowledge_meta(self,path:str) -> str: async def get_knowledge_meta(self,path:str) -> str:
full_path = f"{self.root_path}/{path}" full_path = f"{self.root_path}/{path}"
if os.islink(full_path): if os.islink(full_path):
@@ -336,9 +337,9 @@ class LocalKnowledgeBase(CompositeEnvironment):
hash = self.meta_db.get_hash_by_doc_path(org_path) hash = self.meta_db.get_hash_by_doc_path(org_path)
if hash: if hash:
return self.meta_db.get_knowledge(org_path) return self.meta_db.get_knowledge(org_path)
return "not found" return "not found"
async def load_knowledge_content(self,path:str,pos:int=0,length:int=None) -> str: async def load_knowledge_content(self,path:str,pos:int=0,length:int=None) -> str:
if path.endswith("pdf"): if path.endswith("pdf"):
logger.info("load_knowledge_content:pdf") logger.info("load_knowledge_content:pdf")
@@ -367,12 +368,12 @@ class ScanLocalDocument:
workspace = string.Template(config["workspace"]).substitute(myai_dir=AIStorage.get_instance().get_myai_dir()) workspace = string.Template(config["workspace"]).substitute(myai_dir=AIStorage.get_instance().get_myai_dir())
path = string.Template(config["path"]).substitute(myai_dir=AIStorage.get_instance().get_myai_dir()) path = string.Template(config["path"]).substitute(myai_dir=AIStorage.get_instance().get_myai_dir())
self.knowledge_base = LocalKnowledgeBase(workspace) self.knowledge_base = LocalKnowledgeBase(workspace)
self.path = path self.path = path
def _support_file(self,file_name:str) -> bool: def _support_file(self,file_name:str) -> bool:
if file_name.startswith("."): if file_name.startswith("."):
return False return False
if file_name.endswith(".pdf"): if file_name.endswith(".pdf"):
return True return True
if file_name.endswith(".md"): if file_name.endswith(".md"):
@@ -380,7 +381,7 @@ class ScanLocalDocument:
if file_name.endswith(".txt"): if file_name.endswith(".txt"):
return True return True
return False return False
async def next(self): async def next(self):
while True: while True:
for root, dirs, files in os.walk(self.path): for root, dirs, files in os.walk(self.path):
@@ -391,10 +392,10 @@ class ScanLocalDocument:
if self.knowledge_base.meta_db.is_doc_exist(full_path): if self.knowledge_base.meta_db.is_doc_exist(full_path):
continue continue
yield(full_path, full_path) yield(full_path, full_path)
else: else:
continue continue
yield(None, None) yield(None, None)
class ParseLocalDocument: class ParseLocalDocument:
@@ -425,7 +426,7 @@ class ParseLocalDocument:
await self.knowledge_base.fs.symlink(full_path, new_path) await self.knowledge_base.fs.symlink(full_path, new_path)
logger.info(f"create soft link {full_path} -> {new_path}") logger.info(f"create soft link {full_path} -> {new_path}")
return full_path return full_path
async def _get_meta_prompt(self,meta: dict,temp_meta = None,need_catalogs = False) -> str: async def _get_meta_prompt(self,meta: dict,temp_meta = None,need_catalogs = False) -> str:
kb_tree = await self.knowledge_base.get_knowledege_catalog() kb_tree = await self.knowledge_base.get_knowledege_catalog()
@@ -473,7 +474,7 @@ class ParseLocalDocument:
full_content_len = self._token_len(full_content) full_content_len = self._token_len(full_content)
full_path = meta["original_path"] full_path = meta["original_path"]
self.knowledge_base.learning_cache.add(full_path, meta) self.knowledge_base.learning_cache.add(full_path, meta)
if full_content_len < self.token_limit: if full_content_len < self.token_limit:
# 短文章不用总结catalog # 短文章不用总结catalog
@@ -521,7 +522,7 @@ class ParseLocalDocument:
if item.title: if item.title:
new_item = {} new_item = {}
new_item["page"] = item.page.idnum new_item["page"] = item.page.idnum
new_item["title"] = item.title new_item["title"] = item.title
my_childs = [] my_childs = []
if item.childs: if item.childs:
if len(item.childs) > 0: if len(item.childs) > 0:
@@ -573,7 +574,7 @@ class ParseLocalDocument:
return {} return {}
def _parse_md(self,doc_path:str): def _parse_md(self,doc_path:str):
metadata = {} metadata = {}
cur_encode = "utf-8" cur_encode = "utf-8"
with open(doc_path,'rb') as f: with open(doc_path,'rb') as f:
cur_encode = chardet.detect(f.read(1024))['encoding'] cur_encode = chardet.detect(f.read(1024))['encoding']
@@ -588,7 +589,7 @@ class ParseLocalDocument:
toc = md.toc toc = md.toc
if toc: if toc:
metadata['catalogs'] = toc metadata['catalogs'] = toc
return metadata return metadata
def _parse_document(self,doc_path:str): def _parse_document(self,doc_path:str):
@@ -614,5 +615,4 @@ class ParseLocalDocument:
meta_data["title"] = title meta_data["title"] = title
logger.info("parse document %s!",doc_path) logger.info("parse document %s!",doc_path)
return hash_result, meta_data return hash_result, meta_data
+7 -5
View File
@@ -206,7 +206,7 @@ class OpenAI_ComputeNode(ComputeNode):
if mode_name == "gpt-4-vision-preview": if mode_name == "gpt-4-vision-preview":
response_format = NOT_GIVEN response_format = NOT_GIVEN
llm_inner_functions = None llm_inner_functions = None
if max_token_size > 4096: if max_token_size > 4096 or max_token_size < 50:
result_token = 4096 result_token = 4096
else: else:
result_token = -1 result_token = -1
@@ -216,14 +216,16 @@ class OpenAI_ComputeNode(ComputeNode):
client = AsyncOpenAI(api_key=self.openai_api_key) client = AsyncOpenAI(api_key=self.openai_api_key)
try: try:
if llm_inner_functions is None or len(llm_inner_functions) == 0: if llm_inner_functions is None or len(llm_inner_functions) == 0:
logger.info(f"call openai {mode_name} prompts: {prompts}") if mode_name != "gpt-4-vision-preview":
logger.info(f"call openai {mode_name} prompts: {prompts}")
resp = await client.chat.completions.create(model=mode_name, resp = await client.chat.completions.create(model=mode_name,
messages=prompts, messages=prompts,
response_format = response_format, response_format = response_format,
max_tokens=result_token, max_tokens=result_token,
) )
else: else:
logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions,ensure_ascii=False)}") if mode_name != "gpt-4-vision-preview":
logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions,ensure_ascii=False)}")
resp = await client.chat.completions.create(model=mode_name, resp = await client.chat.completions.create(model=mode_name,
messages=prompts, messages=prompts,
response_format = response_format, response_format = response_format,
@@ -239,7 +241,7 @@ class OpenAI_ComputeNode(ComputeNode):
#logger.info(f"openai response: {resp}") #logger.info(f"openai response: {resp}")
#TODO: gpt-4v api is image_2_text ? #TODO: gpt-4v api is image_2_text ?
if mode_name == "gpt-4-vision-preview": if mode_name == "gpt-4-vision-preview":
status_code = resp.choices[0].finish_reason status_code = resp.choices[0].finish_reason
if status_code is None: if status_code is None:
status_code = resp.choices[0].finish_details['type'] status_code = resp.choices[0].finish_details['type']
@@ -267,7 +269,7 @@ class OpenAI_ComputeNode(ComputeNode):
if token_usage: if token_usage:
result.result_refers["token_usage"] = token_usage result.result_refers["token_usage"] = token_usage
logger.info(f"openai success response: {result.result_str}") logger.info(f"openai success response: {result.result_str}")
return result return result
case _: case _:
+1
View File
@@ -119,6 +119,7 @@ class SlackTunnel(AgentTunnel):
continue continue
await download_file(file_info["file"]["url_private_download"], file_path, self.token) await download_file(file_info["file"]["url_private_download"], file_path, self.token)
mime_type = file["mimetype"]
if file["mimetype"].startswith("image/"): if file["mimetype"].startswith("image/"):
if file_type is None: if file_type is None:
file_type = "image" file_type = "image"
+1
View File
@@ -156,3 +156,4 @@ opencv-python
discord.py discord.py
slack_bolt slack_bolt
wget wget
moviepy