How to Add User Confirmation to Tool Call Requests#

Prerequisites

This guide assumes familiarity with:

WayFlow Agents can be equipped with Tools to enhance their capabilities. However, end users may want to confirm or deny tool call requests emitted from the agent.

This guide shows you how to achieve this with the ClientTool.

Basic implementation#

In this example, you will build a simple Agent equipped with three tools:

  • A tool to add numbers

  • A tool to subtract numbers

  • A tool to multiply numbers

This guide requires the use of an LLM. WayFlow supports several LLM API providers. Select an LLM from the options below:

from wayflowcore.models import OCIGenAIModel

if __name__ == "__main__":

    llm = OCIGenAIModel(
        model_id="provider.model-id",
        service_endpoint="https://url-to-service-endpoint.com",
        compartment_id="compartment-id",
        auth_type="API_KEY",
    )

To learn more about the different LLM providers, read the guide on How to Use LLMs from Different Providers.

Creating the tools#

Sometimes you will want to ask for user confirmation before executing certain tools. To do this you use a ClientTool.

from wayflowcore.tools import ClientTool

add_numbers_tool = ClientTool(
    name="add_numbers",
    description="Add two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)
subtract_numbers_tool = ClientTool(
    name="subtract_numbers",
    description="Subtract two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)
multiply_numbers_tool = ClientTool(
    name="multiply_numbers",
    description="Multiply two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)

Creating the client-side execution logic#

To enable users to accept or deny tool call requests, you add simple validation logic before executing the tools requested by the agent.

from typing import Any, List

from wayflowcore.tools import ToolRequest


def _add_numbers(number1: float, number2: float) -> float:
    return number1 + number2


def _subtract_numbers(number1: float, number2: float) -> float:
    return number1 - number2


def _multiply_numbers(number1: float, number2: float) -> float:
    return number1 * number2


def _ask_for_user_confirmation(tool_request: ToolRequest) -> bool:
    import json

    message = (
        "---\nThe Agent requests the following Tool Call:\n"
        f"Name: `{tool_request.name}`\n"
        f"Args: {json.dumps(tool_request.args, indent=2)}\n---\n"
        f"Do you accept this tool call request? (Y/N)\n"
        ">>> "
    )
    while True:
        user_response = input(message).strip()
        if user_response == "Y":
            return True
        elif user_response == "N":
            return False
        print(f"Unrecognized option: `{user_response}`.")


def execute_client_tool_from_tool_request(
    tool_request: ToolRequest, tools_requiring_confirmation: List[str]
) -> Any:
    if tool_request.name in tools_requiring_confirmation:
        is_confirmed = _ask_for_user_confirmation(tool_request)
        if not is_confirmed:
            return (
                f"The user denied the tool request for the tool {tool_request.name} at this time."
            )

    if tool_request.name == "add_numbers":
        return _add_numbers(**tool_request.args)
    elif tool_request.name == "subtract_numbers":
        return _subtract_numbers(**tool_request.args)
    elif tool_request.name == "multiply_numbers":
        return _multiply_numbers(**tool_request.args)
    else:
        raise ValueError(f"Tool name {tool_request.name} is not recognized")


Here, you simply loop until the user answers whether to accept the tool request (with Y) or reject it (with N).

Creating the agent#

Finally, you create a simple Agent to test the execution code written in the previous section.

from wayflowcore.agent import Agent

assistant = Agent(
    llm=llm,
    tools=[add_numbers_tool, subtract_numbers_tool, multiply_numbers_tool],
    custom_instruction="Use the tools at your disposal to answer the user requests.",
)

Running the agent in an execution loop#

Now, you create a simple execution loop to test the agent. In this loop, you specify that only the subtract_numbers and multiply_numbers tools require user confirmation, while the add_numbers tool can be executed autonomously by the agent.

from wayflowcore.executors.executionstatus import (
    FinishedStatus, UserMessageRequestStatus, ToolRequestStatus
)
from wayflowcore.messagelist import Message, MessageType
from wayflowcore.models import VllmModel
from wayflowcore.tools import ToolResult

TOOLS_REQUIRING_CONFIRMATION = ["subtract_numbers", "multiply_numbers"]

conversation_inputs = {}
conversation = assistant.start_conversation(inputs=conversation_inputs)

while True:
    status = conversation.execute()
    assistant_reply = conversation.get_last_message()
    if assistant_reply:
        print(f"Assistant>>> {assistant_reply.content}\n")

    if isinstance(status, FinishedStatus):
        print(f"Finished assistant execution. Output values:\n{status.output_values}",)
        break
    elif isinstance(status, UserMessageRequestStatus):
        user_input = input("User>>> ")
        print("\n")
        conversation.append_user_message(user_input)
    elif isinstance(status, ToolRequestStatus):
        tool_request = status.tool_requests[0]
        tool_result = execute_client_tool_from_tool_request(tool_request, TOOLS_REQUIRING_CONFIRMATION)
        print(f"{tool_result!r}")
        conversation.append_message(
            Message(
                tool_result=ToolResult(content=tool_result, tool_request_id=tool_request.tool_request_id),
                message_type=MessageType.TOOL_RESULT,
            )
        )
    else:
        raise ValueError(f"Unsupported execution status: '{status}'")

Recap#

In this guide, you learned how to support client-side confirmation for tool call requests.

Below is the complete code from this guide.
from wayflowcore.tools import ClientTool

add_numbers_tool = ClientTool(
    name="add_numbers",
    description="Add two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)
subtract_numbers_tool = ClientTool(
    name="subtract_numbers",
    description="Subtract two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)
multiply_numbers_tool = ClientTool(
    name="multiply_numbers",
    description="Multiply two numbers",
    parameters={
        "number1": {"description": "the first number", "type": "number"},
        "number2": {"description": "the second number", "type": "number"},
    },
    output={"type": "number"},
)
from typing import Any, List

from wayflowcore.tools import ToolRequest


def _add_numbers(number1: float, number2: float) -> float:
    return number1 + number2


def _subtract_numbers(number1: float, number2: float) -> float:
    return number1 - number2


def _multiply_numbers(number1: float, number2: float) -> float:
    return number1 * number2


def _ask_for_user_confirmation(tool_request: ToolRequest) -> bool:
    import json

    message = (
        "---\nThe Agent requests the following Tool Call:\n"
        f"Name: `{tool_request.name}`\n"
        f"Args: {json.dumps(tool_request.args, indent=2)}\n---\n"
        f"Do you accept this tool call request? (Y/N)\n"
        ">>> "
    )
    while True:
        user_response = input(message).strip()
        if user_response == "Y":
            return True
        elif user_response == "N":
            return False
        print(f"Unrecognized option: `{user_response}`.")


def execute_client_tool_from_tool_request(
    tool_request: ToolRequest, tools_requiring_confirmation: List[str]
) -> Any:
    if tool_request.name in tools_requiring_confirmation:
        is_confirmed = _ask_for_user_confirmation(tool_request)
        if not is_confirmed:
            return (
                f"The user denied the tool request for the tool {tool_request.name} at this time."
            )

    if tool_request.name == "add_numbers":
        return _add_numbers(**tool_request.args)
    elif tool_request.name == "subtract_numbers":
        return _subtract_numbers(**tool_request.args)
    elif tool_request.name == "multiply_numbers":
        return _multiply_numbers(**tool_request.args)
    else:
        raise ValueError(f"Tool name {tool_request.name} is not recognized")


from wayflowcore.agent import Agent

assistant = Agent(
    llm=llm,
    tools=[add_numbers_tool, subtract_numbers_tool, multiply_numbers_tool],
    custom_instruction="Use the tools at your disposal to answer the user requests.",
)

from wayflowcore.executors.executionstatus import (
    FinishedStatus, UserMessageRequestStatus, ToolRequestStatus
)
from wayflowcore.messagelist import Message, MessageType
from wayflowcore.models import VllmModel
from wayflowcore.tools import ToolResult

TOOLS_REQUIRING_CONFIRMATION = ["subtract_numbers", "multiply_numbers"]

conversation_inputs = {}
conversation = assistant.start_conversation(inputs=conversation_inputs)

while True:
    status = conversation.execute()
    assistant_reply = conversation.get_last_message()
    if assistant_reply:
        print(f"Assistant>>> {assistant_reply.content}\n")

    if isinstance(status, FinishedStatus):
        print(f"Finished assistant execution. Output values:\n{status.output_values}",)
        break
    elif isinstance(status, UserMessageRequestStatus):
        user_input = input("User>>> ")
        print("\n")
        conversation.append_user_message(user_input)
    elif isinstance(status, ToolRequestStatus):
        tool_request = status.tool_requests[0]
        tool_result = execute_client_tool_from_tool_request(tool_request, TOOLS_REQUIRING_CONFIRMATION)
        print(f"{tool_result!r}")
        conversation.append_message(
            Message(
                tool_result=ToolResult(content=tool_result, tool_request_id=tool_request.tool_request_id),
                message_type=MessageType.TOOL_RESULT,
            )
        )
    else:
        raise ValueError(f"Unsupported execution status: '{status}'")

Next steps#

Having learned how to add user confirmation for tool calls, you may now proceed to: