Support multimodal input
This commit is contained in:
@@ -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.
|
||||||
}, ...
|
}, ...
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# 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 ..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 +33,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 +57,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 +78,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 +122,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 +142,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")
|
||||||
@@ -166,7 +168,7 @@ class BaseLLMProcess(ABC):
|
|||||||
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.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 +176,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:
|
||||||
@@ -202,7 +204,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 +213,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 +229,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 +284,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 +292,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 +311,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 +335,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 +365,56 @@ 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
|
audio_file = msg.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:
|
||||||
|
msg.body = resp.result_str
|
||||||
|
msg_prompt.messages = [{"role":"user","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 +428,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 +450,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 +470,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 +494,14 @@ 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
|
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 +510,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 +595,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 +622,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 +654,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 +664,7 @@ class AgentSelfLearning(BaseLLMProcess):
|
|||||||
|
|
||||||
class AgentSelfImprove(BaseLLMProcess):
|
class AgentSelfImprove(BaseLLMProcess):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -226,10 +226,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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 _:
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -156,3 +156,4 @@ opencv-python
|
|||||||
discord.py
|
discord.py
|
||||||
slack_bolt
|
slack_bolt
|
||||||
wget
|
wget
|
||||||
|
moviepy
|
||||||
|
|||||||
Reference in New Issue
Block a user