Skip to content

Support tool definition via decoration #948

@thoraxe

Description

@thoraxe

🚀 Describe the new functionality needed

Many other agentic frameworks make it easy to define tools by using decorators on a function. In looking at the existing mechanism for defining custom tools for local execution in a client, there is a lot of boilerplate code that would be repeated over and over again when you have a large number of tools.

For example, from the stack-apps repo:

class TickerDataTool(SingleMessageClientTool):
    """Tool to get finance data using yfinance apis"""

    def get_name(self) -> str:
        return "get_ticker_data"

    def get_description(self) -> str:
        return "Get yearly closing prices for a given ticker symbol"

    def get_params_definition(self) -> Dict[str, Parameter]:
        return {
            "ticker_symbol": Parameter(
                name="ticker_symbol",
                parameter_type="str",
                description="The ticker symbol for which to get the data. eg. '^GSPC'",
                required=True,
            ),
            "start": Parameter(
                name="start",
                parameter_type="str",
                description="Start date, eg. '2021-01-01'",
                required=True,
            ),
            "end": Parameter(
                name="end",
                parameter_type="str",
                description="End date, eg. '2024-12-31'",
                required=True,
            ),
        }

    def run_impl(self, ticker_symbol: str, start: str, end: str):
        data = yf.download(ticker_symbol, start=start, end=end)

        data["Year"] = data.index.year
        annual_close = data.groupby("Year")["Close"].last().reset_index()
        annual_close_json = annual_close.to_json(orient="records", date_format="iso")

        return annual_close_json

Comparing to something like Pydantic:

@routing_agent.tool
async def knowledge_tool(ctx: RunContext[str], original_query: str) -> str:
    """A tool for answering general OpenShift and Kubernetes knowledge
    questions. Use for obtaining how-to, documentation, and similar answers.

    Args:
        original_query: the question to get an answer for
    """

    logger.debug(ctx)
    r = await knowledge_agent.run(original_query, usage=ctx.usage)

    # pprint(r.all_messages)
    return r.data

There is a lot of prior art (codebase) from other frameworks for how to implement decorations like this, which make it trivial to define custom tools.

💡 Why is this needed? What if we don't build it?

Not having a decoration mechanism for more trivial tool definition use cases makes llama-stack much harder to use and requires a lot more boilerplate code to be re-used, which increases codebase sizes and also means that any future vulnerabilities that might be found would require a lot more touchpoints to fix.

Other thoughts

#234 is somewhat related, but not entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions