Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method_tool Functionality #94

Open
nathanmargaglio opened this issue Mar 12, 2025 · 6 comments · May be fixed by #137
Open

Add method_tool Functionality #94

nathanmargaglio opened this issue Mar 12, 2025 · 6 comments · May be fixed by #137
Labels
enhancement New feature or request

Comments

@nathanmargaglio
Copy link

I group my tools into classes, and they leverage attributes of the instantiated object to work properly. The function_tool decorator doesn't seem to support this. For example, the following works (as expected):

@function_tool
def get_random_number() -> int:
    """Get a random number."""
    return random.randint(0, 100)

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant",
    tools=[get_random_number]
)
result = Runner.run_sync(agent, input="Get me a random number")
print(result.final_output)

But the following doesn't:

class Tools:
    @function_tool
    def get_random_number(self) -> int:
        """Get a random number."""
        return random.randint(0, 100)

tools = Tools()

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant",
    tools=[tools.get_random_number]
)
result = Runner.run_sync(agent, input="Get me a random number")
print(result.final_output)

Running this produces an error like:

openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid schema for function 'get_random_number': In context=('properties', 'self'), schema must have a 'type' key.", 'type': 'invalid_request_error', 'param': 'tools[0].parameters', 'code': 'invalid_function_parameters'}}

i.e., the self parameter isn't accounted for.

There's probably a ton of different ways of approaching this, but I've been utilizing the class-based tool pattern quite extensively and I've found it works pretty well for keeping tooling clean and simple. I'm happy to consider alternative approaches, but if there was just a way to ignore the self parameter, then I think everything else would just work, no?

@nathanmargaglio nathanmargaglio added the enhancement New feature or request label Mar 12, 2025
@rm-openai
Copy link
Collaborator

I'll take a look. For now there's a pretty simple workaround:

class Tools:
    def get_random_number(self) -> int:
        """Get a random number."""
        return random.randint(0, 100)

tools = Tools()

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant",
    tools=[function_tool(tools.get_random_number)]
)
result = Runner.run_sync(agent, input="Get me a random number")
print(result.final_output)

@Shehryar718
Copy link

Hi @nathanmargaglio and @rm-openai, I've created pull request #137, which addresses the issue and includes tests to verify the changes. Please take a look and let me know if you have any feedback. Thanks!

@WSQsGithub
Copy link

WSQsGithub commented Mar 14, 2025

I have the similar problem. I need to pass chat_id to some of the tool functions for searching purpose.

Currently I am doing this and it fails. I wonder if there's any better way that the tool functions can get chat_id

class Tools:
    def __init__(self, chat_uuid: str):
        self.chat_uuid = chat_uuid

    @function_tool(name_override="tell_time_tool", description_override="A tool that tells the current time.")
    async def tell_time_tool(self) -> str:
        return tell_time()


    @function_tool(
        name_override="ask_for_clarification_tool",
        description_override="A tool that asks for clarification if user's request doesn't make sense.",
    )
    async def ask_for_clarification_tool(self) -> str:
        return ask_for_clarification()

And this is how I implement the agent.

class TriageAgent(Agent):
    def __init__(self, chat_uuid: str, input: any) -> None: 
        self.chat_uuid = chat_uuid
        self.input = input
        self.tool_collections = Tools(chat_uuid=chat_uuid)

        self.tools = [self.tool_collections.tell_time_tool, self.tool_collections.ask_for_clarification_tool]
        self.handoffs = []

        self.agent = Agent(
            name="Triage Agent",
            instructions=INSTRUCTIONS,
            tools=self.tools,
            handoffs=self.handoffs,
        )


    async def run(self) -> None:
        with trace(workflow_name="Triage Handling"):
            # the chat pipeline starts with the triage agent
            result = await Runner.run(starting_agent=self.triage_agent, input=self.input)
            return result.final_output

@Shehryar718
Copy link

@WSQsGithub could you please share the error that you're getting?

@WSQsGithub
Copy link

@WSQsGithub could you please share the error that you're getting?
Here is what I'm getting. I guess it would be fixed once @Shehryar718's pr is approved.

openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid schema for function 'tell_time_tool': In context=('properties', 'self'), schema must have a 'type' key.", 'type': 'invalid_request_error', 'param': 'tools[0].parameters', 'code': 'invalid_function_parameters'}}

@Shehryar718
Copy link

@WSQsGithub could you please share the error that you're getting?
Here is what I'm getting. I guess it would be fixed once @Shehryar718's pr is approved.

openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid schema for function 'tell_time_tool': In context=('properties', 'self'), schema must have a 'type' key.", 'type': 'invalid_request_error', 'param': 'tools[0].parameters', 'code': 'invalid_function_parameters'}}

Yeah my pull request fixes it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
4 participants