mirror of
https://github.com/temporal-community/temporal-ai-agent.git
synced 2026-03-15 14:08:08 +01:00
Merge branch 'development' of https://github.com/joshmsmith/temporal-ai-agent into development
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -8,5 +8,7 @@ talk through the pieces
|
||||
|
||||
explain elements
|
||||
|
||||
tools do determinism
|
||||
|
||||
# Adding features
|
||||
link to how to LLM interactions/how to change
|
||||
@@ -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.WARN)
|
||||
|
||||
|
||||
|
||||
# Run the worker
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as activity_executor:
|
||||
|
||||
1
todo.md
1
todo.md
@@ -16,6 +16,7 @@
|
||||
[ ] how to add more scenarios, tools <br />
|
||||
|
||||
[ ] create tests<br />
|
||||
[ ] fix logging statements not to be all warn, maybe set logging level to info
|
||||
|
||||
[ ] create people management scenarios <br />
|
||||
[x] 1. Schedule PTO goal
|
||||
|
||||
@@ -77,55 +77,32 @@ 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
|
||||
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):
|
||||
self.add_message("user", prompt)
|
||||
|
||||
# Validate the prompt before proceeding
|
||||
@@ -144,7 +121,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)
|
||||
@@ -171,7 +148,8 @@ 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:
|
||||
args = tool_data.get("args", {})
|
||||
@@ -212,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)
|
||||
|
||||
@@ -222,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
|
||||
@@ -283,5 +260,61 @@ 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.info("Chat-end signal received. Chat ending.")
|
||||
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.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()
|
||||
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.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.info("setting goal to goal_choose_agent_type")
|
||||
self.change_goal("goal_choose_agent_type")
|
||||
return waiting_for_confirm
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user