From 4cfe472ca0996ddf4721565a1d4705c6392fb21c Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:10:43 -0400 Subject: [PATCH 1/7] notes about tools --- adding-goals-and-tools.md | 1 + 1 file changed, 1 insertion(+) diff --git a/adding-goals-and-tools.md b/adding-goals-and-tools.md index c878229..e1f4437 100644 --- a/adding-goals-and-tools.md +++ b/adding-goals-and-tools.md @@ -41,6 +41,7 @@ description="Help the user gather args for these tools in order: " - The file name and function name will be the same as each other and should also be the same as the name of the tool, without "tool" - so future_pto_tool would be future_pto.py with a function named future_pto within it. - The function should have `args: dict` as the input and also return a `dict` - The return dict should match the output format you specified in the goal's `example_conversation_history` +- tools are where the user input+model output becomes deterministic. Add validation here to make sure what the system is doing is valid and acceptable #### Add to `tools/__init__.py` - In `tools/__init__.py`, add an import statement for each new tool as well as an applicable return statement in `get_handler`. The tool name here should match the tool name as described in the goal's `description` field. From 72fe6384852c3c15402a991caf1449ec6e4eacc1 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:11:33 -0400 Subject: [PATCH 2/7] more notes about tools --- architecture.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/architecture.md b/architecture.md index f1a5d7b..a97c02d 100644 --- a/architecture.md +++ b/architecture.md @@ -8,5 +8,7 @@ talk through the pieces explain elements +tools do determinism + # Adding features link to how to LLM interactions/how to change \ No newline at end of file From f767cfdc510caea3515feb22a8778e661e24c4d4 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:11:45 -0400 Subject: [PATCH 3/7] todo/logging --- todo.md | 1 + 1 file changed, 1 insertion(+) diff --git a/todo.md b/todo.md index 624805e..87fd312 100644 --- a/todo.md +++ b/todo.md @@ -16,6 +16,7 @@ [ ] how to add more scenarios, tools
[ ] create tests
+[ ] fix logging statements not to be all warn, maybe set logging level to info [ ] create people management scenarios
[x] 1. Schedule PTO goal From 7153c5308a8ef619236905c8831132197cdafa3b Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:12:09 -0400 Subject: [PATCH 4/7] set logging level (to Info) --- scripts/run_worker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/run_worker.py b/scripts/run_worker.py index 7b9549e..c30c953 100644 --- a/scripts/run_worker.py +++ b/scripts/run_worker.py @@ -2,6 +2,7 @@ import asyncio import concurrent.futures import os from dotenv import load_dotenv +import logging from temporalio.worker import Worker @@ -48,6 +49,9 @@ async def main(): print("===========================================================\n") print("Worker ready to process tasks!") + logging.basicConfig(level=logging.INFO) + + # Run the worker with concurrent.futures.ThreadPoolExecutor(max_workers=100) as activity_executor: From c8a0feaa1b65979d84b6503f4c56596dfaee6fe7 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:12:27 -0400 Subject: [PATCH 5/7] logging level to WARN --- scripts/run_worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_worker.py b/scripts/run_worker.py index c30c953..c5b280e 100644 --- a/scripts/run_worker.py +++ b/scripts/run_worker.py @@ -49,7 +49,7 @@ async def main(): print("===========================================================\n") print("Worker ready to process tasks!") - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.WARN) From 36894c91f9eec1015519137a7db560ebc4470ba1 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:13:07 -0400 Subject: [PATCH 6/7] cleaning up workflow code --- workflows/agent_goal_workflow.py | 109 +++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/workflows/agent_goal_workflow.py b/workflows/agent_goal_workflow.py index 3f73866..745f2b5 100644 --- a/workflows/agent_goal_workflow.py +++ b/workflows/agent_goal_workflow.py @@ -77,55 +77,34 @@ class AgentGoalWorkflow: # This is the main interactive loop. Main responsibilities: # - Selecting and changing goals as directed by the user # - reacting to user input (from signals) - # - calling activities to determine next steps and prompts - # - executing the selected tools + # - validating user input to make sure it makes sense with the current goal and tools + # - calling the LLM through activities to determine next steps and prompts + # - executing the selected tools via activities while True: # wait indefinitely for input from signals - user_prompt, end_chat, or confirm as defined below await workflow.wait_condition( lambda: bool(self.prompt_queue) or self.chat_ended or self.confirm ) - # handle chat-end signal - if self.chat_ended: - workflow.logger.warning(f"workflow step: chat-end signal received, ending") - workflow.logger.info("Chat ended.") + # handle chat should end. When chat ends, push conversation history to workflow results. + if self.chat_should_end(): return f"{self.conversation_history}" # Execute the tool - if self.confirm and waiting_for_confirm and current_tool and self.tool_data: - workflow.logger.warning(f"workflow step: user has confirmed, executing the tool {current_tool}") - self.confirm = False - waiting_for_confirm = False - - confirmed_tool_data = self.tool_data.copy() - confirmed_tool_data["next"] = "user_confirmed_tool_run" - self.add_message("user_confirmed_tool_run", confirmed_tool_data) - - # execute the tool by key as defined in tools/__init__.py - await helpers.handle_tool_execution( - current_tool, - self.tool_data, - self.tool_results, - self.add_message, - self.prompt_queue - ) - - #set new goal if we should - if len(self.tool_results) > 0: - if "ChangeGoal" in self.tool_results[-1].values() and "new_goal" in self.tool_results[-1].keys(): - new_goal = self.tool_results[-1].get("new_goal") - workflow.logger.warning(f"Booya new goal!: {new_goal}") - self.change_goal(new_goal) - elif "ListAgents" in self.tool_results[-1].values() and self.goal.id != "goal_choose_agent_type": - workflow.logger.warning("setting goal to goal_choose_agent_type") - self.change_goal("goal_choose_agent_type") + if self.ready_for_tool_execution(waiting_for_confirm, current_tool): + waiting_for_confirm = await self.execute_tool(current_tool) continue - # if we've received messages to be processed on the prompt queue... + # process forward on the prompt queue if any if self.prompt_queue: + # get most recent prompt prompt = self.prompt_queue.popleft() workflow.logger.warning(f"workflow step: processing message on the prompt queue, message is {prompt}") - if not prompt.startswith("###"): #if the message isn't from the LLM but is instead from the user + + + + # Validate user-provided prompts + if self.is_user_prompt(prompt): self.add_message("user", prompt) # Validate the prompt before proceeding @@ -144,7 +123,7 @@ class AgentGoalWorkflow: ), ) - #If validation fails, provide that feedback to the user - i.e., "your words make no sense, puny human" end this iteration of processing + # If validation fails, provide that feedback to the user - i.e., "your words make no sense, puny human" end this iteration of processing if not validation_result.validationResult: workflow.logger.warning(f"Prompt validation failed: {validation_result.validationFailedReason}") self.add_message("agent", validation_result.validationFailedReason) @@ -172,6 +151,7 @@ class AgentGoalWorkflow: current_tool = tool_data.get("tool") workflow.logger.warning(f"next_step: {next_step}, current tool is {current_tool}") + #if the next step is to confirm... if next_step == "confirm" and current_tool: args = tool_data.get("args", {}) @@ -285,3 +265,60 @@ class AgentGoalWorkflow: # self.goal = goals.get(goal) workflow.logger.warning("Changed goal to " + goal) #todo reset goal or tools if this doesn't work or whatever + + # workflow function that defines if chat should end + def chat_should_end(self) -> bool: + if self.chat_ended: + workflow.logger.warning(f"workflow step: chat-end signal received, ending") + workflow.logger.info("Chat ended.") + return True + else: + return False + + # define if we're ready for tool execution + def ready_for_tool_execution(self, waiting_for_confirm: bool, current_tool: Any) -> bool: + if self.confirm and waiting_for_confirm and current_tool and self.tool_data: + return True + else: + return False + + # LLM-tagged prompts start with "###" + # all others are from the user + def is_user_prompt(self, prompt) -> bool: + if prompt.startswith("###"): + return False + else: + return True + + # execute the tool - return False if we're not waiting for confirm anymore (always the case if it works successfully) + # + async def execute_tool(self, current_tool: str)->bool: + workflow.logger.warning(f"workflow step: user has confirmed, executing the tool {current_tool}") + self.confirm = False + waiting_for_confirm = False + confirmed_tool_data = self.tool_data.copy() + confirmed_tool_data["next"] = "user_confirmed_tool_run" + self.add_message("user_confirmed_tool_run", confirmed_tool_data) + + # execute the tool by key as defined in tools/__init__.py + await helpers.handle_tool_execution( + current_tool, + self.tool_data, + self.tool_results, + self.add_message, + self.prompt_queue + ) + + #set new goal if we should + if len(self.tool_results) > 0: + if "ChangeGoal" in self.tool_results[-1].values() and "new_goal" in self.tool_results[-1].keys(): + new_goal = self.tool_results[-1].get("new_goal") + workflow.logger.warning(f"Booya new goal!: {new_goal}") + self.change_goal(new_goal) + elif "ListAgents" in self.tool_results[-1].values() and self.goal.id != "goal_choose_agent_type": + workflow.logger.warning("setting goal to goal_choose_agent_type") + self.change_goal("goal_choose_agent_type") + return waiting_for_confirm + + + From 9ead0078496b40d512bfefdd907acbc6435aa84b Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 14 Mar 2025 10:16:16 -0400 Subject: [PATCH 7/7] change logging to info unless there needs to be a warning --- workflows/agent_goal_workflow.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/workflows/agent_goal_workflow.py b/workflows/agent_goal_workflow.py index 745f2b5..d52e9ab 100644 --- a/workflows/agent_goal_workflow.py +++ b/workflows/agent_goal_workflow.py @@ -99,9 +99,7 @@ class AgentGoalWorkflow: if self.prompt_queue: # get most recent prompt prompt = self.prompt_queue.popleft() - workflow.logger.warning(f"workflow step: processing message on the prompt queue, message is {prompt}") - - + workflow.logger.info(f"workflow step: processing message on the prompt queue, message is {prompt}") # Validate user-provided prompts if self.is_user_prompt(prompt): @@ -150,7 +148,7 @@ class AgentGoalWorkflow: next_step = tool_data.get("next") current_tool = tool_data.get("tool") - workflow.logger.warning(f"next_step: {next_step}, current tool is {current_tool}") + workflow.logger.info(f"next_step: {next_step}, current tool is {current_tool}") #if the next step is to confirm... if next_step == "confirm" and current_tool: @@ -192,9 +190,9 @@ class AgentGoalWorkflow: @workflow.signal async def user_prompt(self, prompt: str) -> None: """Signal handler for receiving user prompts.""" - workflow.logger.warning(f"signal received: user_prompt, prompt is {prompt}") + workflow.logger.info(f"signal received: user_prompt, prompt is {prompt}") if self.chat_ended: - workflow.logger.warning(f"Message dropped due to chat closed: {prompt}") + workflow.logger.info(f"Message dropped due to chat closed: {prompt}") return self.prompt_queue.append(prompt) @@ -202,15 +200,14 @@ class AgentGoalWorkflow: @workflow.signal async def confirm(self) -> None: """Signal handler for user confirmation of tool execution.""" - workflow.logger.info("Received user confirmation") - workflow.logger.warning(f"signal recieved: confirm") + workflow.logger.info("Received user signal: confirmation") self.confirm = True #Signal that comes from api/main.py via a post to /end-chat @workflow.signal async def end_chat(self) -> None: """Signal handler for ending the chat session.""" - workflow.logger.warning("signal received: end_chat") + workflow.logger.info("signal received: end_chat") self.chat_ended = True @workflow.query @@ -263,14 +260,13 @@ class AgentGoalWorkflow: if listed_goal.id == goal: self.goal = listed_goal # self.goal = goals.get(goal) - workflow.logger.warning("Changed goal to " + goal) + workflow.logger.info("Changed goal to " + goal) #todo reset goal or tools if this doesn't work or whatever # workflow function that defines if chat should end def chat_should_end(self) -> bool: if self.chat_ended: - workflow.logger.warning(f"workflow step: chat-end signal received, ending") - workflow.logger.info("Chat ended.") + workflow.logger.info("Chat-end signal received. Chat ending.") return True else: return False @@ -293,7 +289,7 @@ class AgentGoalWorkflow: # execute the tool - return False if we're not waiting for confirm anymore (always the case if it works successfully) # async def execute_tool(self, current_tool: str)->bool: - workflow.logger.warning(f"workflow step: user has confirmed, executing the tool {current_tool}") + workflow.logger.info(f"workflow step: user has confirmed, executing the tool {current_tool}") self.confirm = False waiting_for_confirm = False confirmed_tool_data = self.tool_data.copy() @@ -313,10 +309,10 @@ class AgentGoalWorkflow: if len(self.tool_results) > 0: if "ChangeGoal" in self.tool_results[-1].values() and "new_goal" in self.tool_results[-1].keys(): new_goal = self.tool_results[-1].get("new_goal") - workflow.logger.warning(f"Booya new goal!: {new_goal}") + workflow.logger.info(f"Booya new goal!: {new_goal}") self.change_goal(new_goal) elif "ListAgents" in self.tool_results[-1].values() and self.goal.id != "goal_choose_agent_type": - workflow.logger.warning("setting goal to goal_choose_agent_type") + workflow.logger.info("setting goal to goal_choose_agent_type") self.change_goal("goal_choose_agent_type") return waiting_for_confirm