Skip to content

Conversation

@cs168898
Copy link

@cs168898 cs168898 commented Dec 2, 2025

Filter Function Calls

Developers using ADK-go can now add filters when they create a new LLM agent as such:

	agent, err := llmagent.New(llmagent.Config{
		Name:        "agent",
		Description: "math agent",
		Model:       model,
		Instruction: "only use the tools provided to output results",
		// TODO(hakim): set to false when autoflow is implemented.
		DisallowTransferToParent: true,
		DisallowTransferToPeers:  true,
		Tools:                    []tool.Tool{sumTool},
		Filter:                   map[string]bool{sumTool.Name(): false},  <================ Added filter field
	})

The Filter field accepts a map where the Key is the tool's name and the Value is a boolean on whether the tool is allowed or not.
True = Tool is allowed.
False = Tool is NOT allowed.

  • Added Filter field to the Config struct.
  • Added filter field to the State struct.
  • Implemented filter logic in base_flow.go
  • Added tests for testing if filtered tools are still called.

Notes

I have decided to start on developer declared filter first as the use cases can include tool prohibition based on user roles.
For example, if a user has no premium access account, developers could create a conditional check and initialize the agent without access to specific tools.
The current implementation will impose a permanent tool filter on all requests after agent initialization, meaning we cannot change the filter once the agent has been initialized.

TODO

  • Implement filter changes per request.
  • Agent defined filters to pass to sub-agents.

This PR references this Issue

cs168898 and others added 3 commits December 2, 2025 09:49
Users of the ADK can now define the tools they want to filter through the config struct when creating a new llmagent.
The api key has been completely deleted from existence.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @cs168898, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the LLM agent configuration by allowing developers to define a static filter for tools. This feature enables fine-grained control over an agent's capabilities, supporting scenarios such as restricting tool access based on user roles or premium features. The filtering is applied during agent initialization, providing a clear mechanism to manage tool availability.

Highlights

  • New Filter Field: A new Filter field (map[string]bool) has been added to the llmagent.Config and internal/llminternal.State structs, allowing explicit control over which tools an LLM agent can use.
  • Tool Filtering Logic: The core logic for filtering tools has been implemented in internal/llminternal/base_flow.go, ensuring that only allowed tools are presented to the LLM agent during processing.
  • New Test Case: A new test, TestToolFiltering, has been added to llmagent_test.go to verify that tools marked as disallowed in the filter are indeed not invoked by the agent.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a filtering mechanism for tools in LLM agents, which is a great addition for controlling agent capabilities. The implementation is mostly solid, with a new Filter field in the agent configuration and corresponding logic to apply it. The new test case effectively validates the filtering of a disallowed tool.

My main feedback is around the filter's logic and implementation. The current approach acts as a strict allowlist, which can be inconvenient for use cases like tool prohibition. I've suggested changing it to a denylist-by-default behavior, which should be more intuitive. I also noticed some code duplication in the filtering logic that could be refactored for better maintainability. Additionally, there's a minor formatting issue.

Overall, this is a valuable feature, and with a few adjustments, it will be even better.

Comment on lines 223 to 259
tools := []tool.Tool{}
baseTools := Reveal(llmAgent).Tools

if hasFilter {
for _, t := range baseTools {
if use, ok := filter[t.Name()]; ok && use {
tools = append(tools, t)
}
}
} else {
tools = append(tools, baseTools...)
}

// run processors for tools.
tools := Reveal(llmAgent).Tools
for _, toolSet := range Reveal(llmAgent).Toolsets {

tsTools, err := toolSet.Tools(icontext.NewReadonlyContext(ctx))
if err != nil {
return fmt.Errorf("failed to extract tools from the tool set %q: %w", toolSet.Name(), err)
}

tools = append(tools, tsTools...)
// if filter contains something then filter accordingly
if hasFilter {
for _, tool := range tsTools {
use, found := filter[tool.Name()]
if !found {
continue
}
if use {
tools = append(tools, tool)
}
}
} else {
// else just append the original toolset
tools = append(tools, tsTools...)
}
}

return toolPreprocess(ctx, req, tools)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current filter implementation acts as a strict allowlist. This means if a filter map is provided, only tools explicitly set to true are included. This can be cumbersome if the user wants to disallow only a few tools, as they would have to list all other tools as true. A more intuitive approach for tool prohibition is to treat the filter as a denylist by default: if a tool is not in the filter, it's allowed.

Additionally, the logic for filtering baseTools and tools from Toolsets is duplicated. This can be refactored for better readability and maintainability by first collecting all tools and then applying the filter once.

Here is a suggested refactoring that implements the denylist behavior and removes code duplication:

  // run processors for tools and filter
  var allTools []tool.Tool
  allTools = append(allTools, Reveal(llmAgent).Tools...)
  for _, toolSet := range Reveal(llmAgent).Toolsets {
    tsTools, err := toolSet.Tools(icontext.NewReadonlyContext(ctx))
    if err != nil {
      return fmt.Errorf("failed to extract tools from the tool set %q: %w", toolSet.Name(), err)
    }
    allTools = append(allTools, tsTools...)
  }

  if !hasFilter {
    return toolPreprocess(ctx, req, allTools)
  }

  var filteredTools []tool.Tool
  for _, t := range allTools {
    // If a tool is not in the filter, it is allowed by default.
    // This makes the filter act as a denylist, which is more intuitive for prohibiting tools.
    if use, ok := filter[t.Name()]; !ok || use {
      filteredTools = append(filteredTools, t)
    }
  }
  return toolPreprocess(ctx, req, filteredTools)


Tools []tool.Tool
Toolsets []tool.Toolset
Filter map[string]bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a minor formatting issue here. A tab character (\t) is used for alignment instead of spaces. It's better to use spaces for consistent formatting with the rest of the struct fields.

Suggested change
Filter map[string]bool
Filter map[string]bool

@cs168898 cs168898 changed the title Cs168898/filter function calls Filter function calls Dec 3, 2025
The current implementation now ignores the value of the Filter map and if the tool is NOT found in the Filter map, it will be included into the available tools to use. Also cleaned up tab and spacing issues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant