1) Fix bugs.

2) openai_node support async/await.
This commit is contained in:
Liu Zhicong
2023-11-05 15:21:27 -08:00
parent 575d37486c
commit 7fd3749a40
7 changed files with 61 additions and 30 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ The types of TODO I can handle include:
I receive a TODO described in json format, I will handle it according to the following rules: I receive a TODO described in json format, I will handle it according to the following rules:
- Determine whether I have the ability to handle the TODO independently. If not, I will try to break the TODO down into smaller sub-TODOs, or hand it over to someone more suitable that I know. - Determine whether I have the ability to handle the TODO independently. If not, I will try to break the TODO down into smaller sub-TODOs, or hand it over to someone more suitable that I know.
- I will plan the steps to solve the TODO in combination with known information, and break down the generalized TODO into more specific sub-todos. The title of the sub-todo should contain step number like #1, #2 - I will plan the steps to solve the TODO in combination with known information, and break down the generalized TODO into more specific sub-todos. The title of the sub-todo should contain step number like #1, #2
- sub-todo must set parent - Sub-todo must set parent, The maximum depth of sub-todo is 4.
- A specific sub-todo refers to a task that can be completed in one execution within my ability range. - A specific sub-todo refers to a task that can be completed in one execution within my ability range.
- After each execution, I will decide whether to update the status of the TODO. And use op:'update_todo' to update when necessary. - After each execution, I will decide whether to update the status of the TODO. And use op:'update_todo' to update when necessary.
+9 -5
View File
@@ -1,3 +1,4 @@
import traceback
from typing import Optional from typing import Optional
from asyncio import Queue from asyncio import Queue
@@ -9,6 +10,7 @@ import json
import shlex import shlex
import datetime import datetime
import copy import copy
import sys
from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt,AgentReport,AgentTodo,AgentGoal,AgentTodoResult,AgentWorkLog from .agent_base import AgentMsg, AgentMsgStatus, AgentMsgType,FunctionItem,LLMResult,AgentPrompt,AgentReport,AgentTodo,AgentGoal,AgentTodoResult,AgentWorkLog
from .chatsession import AIChatSession from .chatsession import AIChatSession
@@ -659,6 +661,7 @@ class AIAgent:
# 尝试完成自己的TOOD (不依赖任何其他Agnet) # 尝试完成自己的TOOD (不依赖任何其他Agnet)
async def do_my_work(self) -> None: async def do_my_work(self) -> None:
workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None) workspace : WorkspaceEnvironment = self.get_workspace_by_msg(None)
logger.info(f"agent {self.agent_id} do my work start!")
# review todo能更整体的思考一次todo的优先级 # review todo能更整体的思考一次todo的优先级
if await self.need_review_todos(): if await self.need_review_todos():
@@ -681,6 +684,8 @@ class AIAgent:
await self._llm_check_todo(todo,workspace) await self._llm_check_todo(todo,workspace)
self.agent_energy -= 1 self.agent_energy -= 1
logger.info(f"agent {self.agent_id} do my work done!")
def get_review_todo_prompt(self) -> AgentPrompt: def get_review_todo_prompt(self) -> AgentPrompt:
return self.review_todo_prompt return self.review_todo_prompt
@@ -981,9 +986,6 @@ class AIAgent:
return task_result return task_result
async def execute_op_list(self,oplist:list,workspace:WorkspaceEnvironment):
pass
def need_work(self) -> bool: def need_work(self) -> bool:
return True return True
@@ -1002,7 +1004,7 @@ class AIAgent:
# agent loop # agent loop
async def _on_timer(self): async def _on_timer(self):
while True: while True:
await asyncio.sleep(1) await asyncio.sleep(15)
try: try:
now = time.time() now = time.time()
if self.last_recover_time is None: if self.last_recover_time is None:
@@ -1030,8 +1032,10 @@ class AIAgent:
# #
if self.need_self_learn(): if self.need_self_learn():
await self.do_self_learn() await self.do_self_learn()
except Exception as e: except Exception as e:
logger.error(f"agent {self.agent_id} on timer error:{e}") tb_str = traceback.format_exc()
logger.error(f"agent {self.agent_id} on timer error:{tb_str},{e}")
continue continue
+11 -6
View File
@@ -374,12 +374,13 @@ class AgentTodo:
self.title = None self.title = None
self.detail = None self.detail = None
self.todo_path = None # get parent todo,sub todo by path self.todo_path = None # get parent todo,sub todo by path
#self.parent = None
self.create_time = time.time() self.create_time = time.time()
self.due_date = time.time() + 3600 * 24 * 2 self.due_date = time.time() + 3600 * 24 * 2
self.state = "pending" self.state = "pending"
self.depend_todo_ids = [] self.depend_todo_ids = []
self.sub_todos = [] self.sub_todos = {}
self.need_check = True self.need_check = True
self.result : ComputeTaskResult = None self.result : ComputeTaskResult = None
@@ -406,7 +407,7 @@ class AgentTodo:
due_date = json_obj.get("due_date") due_date = json_obj.get("due_date")
if due_date: if due_date:
todo.due_date = datetime.fromisoformat(due_date).timestamp() todo.due_date = datetime.fromisoformat(due_date).timestamp()
#todo.todo_path = json_obj.get("todo_path") todo.todo_path = json_obj.get("todo_path")
todo.depend_todo_ids = json_obj.get("depend_todo_ids") todo.depend_todo_ids = json_obj.get("depend_todo_ids")
todo.need_check = json_obj.get("need_check") todo.need_check = json_obj.get("need_check")
#todo.result = json_obj.get("result") #todo.result = json_obj.get("result")
@@ -414,7 +415,9 @@ class AgentTodo:
todo.worker = json_obj.get("worker") todo.worker = json_obj.get("worker")
todo.checker = json_obj.get("checker") todo.checker = json_obj.get("checker")
todo.createor = json_obj.get("createor") todo.createor = json_obj.get("createor")
#todo.retry_count = json_obj.get("retry_count") if json_obj.get("retry_count"):
todo.retry_count = json_obj.get("retry_count")
todo.raw_obj = json_obj todo.raw_obj = json_obj
return todo return todo
@@ -422,6 +425,7 @@ class AgentTodo:
def to_dict(self) -> dict: def to_dict(self) -> dict:
result = {} result = {}
result["id"] = self.todo_id result["id"] = self.todo_id
result["todo_path"] = self.todo_path
#result["parent_id"] = self.parent_id #result["parent_id"] = self.parent_id
result["title"] = self.title result["title"] = self.title
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat() result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
@@ -436,9 +440,10 @@ class AgentTodo:
return result return result
def can_do(self) -> bool: def can_do(self) -> bool:
for sub_todo in self.sub_todos: for sub_todo in self.sub_todos.values():
if sub_todo.state != "done": if sub_todo.state:
return False if sub_todo.state != "done":
return False
now = datetime.now().timestamp() now = datetime.now().timestamp()
time_diff = now - self.due_date time_diff = now - self.due_date
+3 -1
View File
@@ -151,7 +151,9 @@ class ComputeKernel:
time_out_result = ComputeTaskResult() time_out_result = ComputeTaskResult()
time_out_result.result_code = ComputeTaskResultCode.TIMEOUT time_out_result.result_code = ComputeTaskResultCode.TIMEOUT
time_out_result.set_from_task(task_req) time_out_result.set_from_task(task_req)
## craete timeout task_result task_req.result = time_out_result
return time_out_result
async def do_llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0, inner_functions = None) -> str: async def do_llm_completion(self, prompt: AgentPrompt, mode_name: Optional[str] = None, max_token: int = 0, inner_functions = None) -> str:
task_req = self.llm_completion(prompt, mode_name, max_token,inner_functions) task_req = self.llm_completion(prompt, mode_name, max_token,inner_functions)
+8 -4
View File
@@ -4,11 +4,13 @@ import asyncio
from asyncio import Queue from asyncio import Queue
import logging import logging
import json import json
import aiohttp
from .compute_task import ComputeTask, ComputeTaskResult, ComputeTaskState, ComputeTaskType,ComputeTaskResultCode from .compute_task import ComputeTask, ComputeTaskResult, ComputeTaskState, ComputeTaskType,ComputeTaskResultCode
from .compute_node import ComputeNode from .compute_node import ComputeNode
from .storage import AIStorage,UserConfig from .storage import AIStorage,UserConfig
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -57,7 +59,8 @@ class OpenAI_ComputeNode(ComputeNode):
async def remove_task(self, task_id: str): async def remove_task(self, task_id: str):
pass pass
def _run_task(self, task: ComputeTask):
async def _run_task(self, task: ComputeTask):
task.state = ComputeTaskState.RUNNING task.state = ComputeTaskState.RUNNING
result = ComputeTaskResult() result = ComputeTaskResult()
@@ -110,16 +113,17 @@ class OpenAI_ComputeNode(ComputeNode):
max_token_size = 4000 max_token_size = 4000
result_token = max_token_size result_token = max_token_size
try: try:
if llm_inner_functions is None: if llm_inner_functions is None:
logger.info(f"call openai {mode_name} prompts: {prompts}") logger.info(f"call openai {mode_name} prompts: {prompts}")
resp = openai.ChatCompletion.create(model=mode_name, resp = await openai.ChatCompletion.acreate(model=mode_name,
messages=prompts, messages=prompts,
#max_tokens=result_token, #max_tokens=result_token,
temperature=0.7) temperature=0.7)
else: else:
logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions)}") logger.info(f"call openai {mode_name} prompts: \n\t {prompts} \nfunctions: \n\t{json.dumps(llm_inner_functions)}")
resp = openai.ChatCompletion.create(model=mode_name, resp = await openai.ChatCompletion.acreate(model=mode_name,
messages=prompts, messages=prompts,
functions=llm_inner_functions, functions=llm_inner_functions,
#max_tokens=result_token, #max_tokens=result_token,
@@ -171,7 +175,7 @@ class OpenAI_ComputeNode(ComputeNode):
while True: while True:
task = await self.task_queue.get() task = await self.task_queue.get()
logger.info(f"openai_node get task: {task.display()}") logger.info(f"openai_node get task: {task.display()}")
result = self._run_task(task) result = await self._run_task(task)
if result is not None: if result is not None:
task.state = ComputeTaskState.DONE task.state = ComputeTaskState.DONE
task.result = result task.result = result
+25 -9
View File
@@ -58,7 +58,10 @@ class WorkspaceEnvironment(Environment):
if op["op"] == "create": if op["op"] == "create":
return await self.create(op["path"],op["content"]) return await self.create(op["path"],op["content"])
elif op["op"] == "write_file": elif op["op"] == "write_file":
return await self.write(op["path"],op["content"],op["mode"]) is_append = op.get("is_append")
if is_append is None:
is_append = False
return await self.write(op["path"],op["content"],is_append)
elif op["op"] == "delete": elif op["op"] == "delete":
return await self.delete(op["path"]) return await self.delete(op["path"])
elif op["op"] == "rename": elif op["op"] == "rename":
@@ -173,13 +176,14 @@ class WorkspaceEnvironment(Environment):
continue continue
todo_count = todo_count + 1 todo_count = todo_count + 1
str_result = str_result + f"{' '*(4-deep)}└─{entry.name}\n" str_result = str_result + f"{' '*(4-deep)}{entry.name}\n"
await scan_dir(entry.path,deep-1) await scan_dir(entry.path,deep-1)
await scan_dir(directory_path,deep) await scan_dir(directory_path,deep)
return str_result,todo_count return str_result,todo_count
async def get_todo_list(self,agent_id:str,path:str = None)->List[AgentTodo]: async def get_todo_list(self,agent_id:str,path:str = None)->List[AgentTodo]:
logger.info("get_todo_list:%s,%s",agent_id,path)
if path: if path:
directory_path = self.root_path + "/todos/" + path directory_path = self.root_path + "/todos/" + path
else: else:
@@ -208,7 +212,7 @@ class WorkspaceEnvironment(Environment):
if todo: if todo:
if parent: if parent:
parent.sub_todos.append(todo) parent.sub_todos[todo.todo_id] = todo
result_list.append(todo) result_list.append(todo)
await scan_dir(entry.path,deep + 1,todo) await scan_dir(entry.path,deep + 1,todo)
@@ -218,11 +222,11 @@ class WorkspaceEnvironment(Environment):
await scan_dir(directory_path,0) await scan_dir(directory_path,0)
#sort by rank #sort by rank
result_list.sort(key=lambda x:(x.rank,x.title)) result_list.sort(key=lambda x:(x.rank,x.title))
logger.info("get_todo_list return,todolist.length() is %d",len(result_list))
return result_list return result_list
async def get_todo_by_fullpath(self,path:str) -> AgentTodo: async def get_todo_by_fullpath(self,path:str) -> AgentTodo:
logger.info("get_todo_by_fullpath:%s",path) #logger.info("get_todo_by_fullpath:%s",path)
detail_path = path + "/detail" detail_path = path + "/detail"
async with aiofiles.open(detail_path, mode='r', encoding="utf-8") as f: async with aiofiles.open(detail_path, mode='r', encoding="utf-8") as f:
@@ -231,7 +235,10 @@ class WorkspaceEnvironment(Environment):
todo_dict = json.loads(content) todo_dict = json.loads(content)
result_todo = AgentTodo.from_dict(todo_dict) result_todo = AgentTodo.from_dict(todo_dict)
if result_todo: if result_todo:
result_todo.todo_path = path relative_path = os.path.relpath(path, self.root_path + "/todos/")
if not relative_path.startswith('/'):
relative_path = '/' + relative_path
result_todo.todo_path = relative_path
self.known_todo[result_todo.todo_id] = result_todo self.known_todo[result_todo.todo_id] = result_todo
else: else:
logger.error("get_todo_by_path:%s,parse failed!",path) logger.error("get_todo_by_path:%s,parse failed!",path)
@@ -243,16 +250,25 @@ class WorkspaceEnvironment(Environment):
async def create_todo(self,parent_id:str,todo:AgentTodo) -> None: async def create_todo(self,parent_id:str,todo:AgentTodo) -> None:
if parent_id: if parent_id:
if parent_id not in self.known_todo:
logger.error("create_todo failed: parent_id not found!")
return False
parent_path = self.known_todo.get(parent_id).todo_path parent_path = self.known_todo.get(parent_id).todo_path
dir_path = f"{parent_path}/{todo.title}" todo_path = f"{parent_path}/{todo.title}"
else: else:
dir_path = f"{self.root_path}/todos/{todo.title}" todo_path = todo.title
dir_path = f"{self.root_path}/todos/{todo_path}"
os.makedirs(dir_path) os.makedirs(dir_path)
detail_path = f"{dir_path}/detail" detail_path = f"{dir_path}/detail"
if todo.todo_path is None:
todo.todo_path = todo_path
logger.info("create_todo %s",detail_path) logger.info("create_todo %s",detail_path)
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f: async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
await f.write(json.dumps(todo.to_dict())) await f.write(json.dumps(todo.to_dict()))
self.known_todo[todo.todo_id] = todo
return True return True
async def update_todo(self,todo_id:str,new_stat:str)->bool: async def update_todo(self,todo_id:str,new_stat:str)->bool:
@@ -263,7 +279,7 @@ class WorkspaceEnvironment(Environment):
todo.raw_obj = todo.to_dict() todo.raw_obj = todo.to_dict()
todo.raw_obj["status"] = new_stat todo.raw_obj["status"] = new_stat
detail_path = todo.todo_path + "/detail" detail_path = f"{self.root_path}/todos/{todo.todo_path}/detail"
async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f: async with aiofiles.open(detail_path, mode='w', encoding="utf-8") as f:
await f.write(json.dumps(todo.raw_obj)) await f.write(json.dumps(todo.raw_obj))
return True return True
+2 -2
View File
@@ -19,7 +19,7 @@ click>=8.1.7
colorama>=0.4.6 colorama>=0.4.6
coloredlogs>=15.0.1 coloredlogs>=15.0.1
decorator>=4.4.2 decorator>=4.4.2
fastapi>=0.99.1 fastapi
filelock>=3.12.3 filelock>=3.12.3
flatbuffers>=23.5.26 flatbuffers>=23.5.26
frozenlist>=1.4.0 frozenlist>=1.4.0
@@ -59,7 +59,7 @@ proto-plus>=1.22.3
pulsar-client>=3.3.0 pulsar-client>=3.3.0
pyasn1>=0.5.0 pyasn1>=0.5.0
pyasn1-modules>=0.3.0 pyasn1-modules>=0.3.0
pydantic>=1.10.12 pydantic
PyPika>=0.48.9 PyPika>=0.48.9
pyreadline3>=3.4.1 pyreadline3>=3.4.1
python-dateutil>=2.8.2 python-dateutil>=2.8.2