Skip to content

Conversation

@huangjeff5
Copy link
Contributor

  • Implemented PluginV2 interface (init, resolve, list)
  • Refactored Anthropic plugin
  • Tested manually using Anthropic plugin

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @huangjeff5, 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 significantly upgrades the plugin architecture within Genkit by introducing a PluginV2 interface. This new design promotes greater modularity and independence for plugins, allowing them to define and manage their actions (like models and embedders) more autonomously. The framework now intelligently handles both the legacy v1 plugins and the new v2 plugins, ensuring a smooth transition and continued functionality. The Anthropic plugin serves as an early adopter, showcasing how to leverage the init, resolve, and list methods of the PluginV2 interface for more flexible and testable integrations.

Highlights

  • New PluginV2 Interface: Introduced a new PluginV2 abstract base class, providing a decoupled way for plugins to define and return Action objects (models, embedders, retrievers) rather than directly mutating the framework's registry. This enhances standalone usage and testability.
  • Backward Compatibility: The core Genkit class has been updated to seamlessly support both the existing Plugin (v1) and the new PluginV2 interfaces, ensuring that older plugins continue to function as expected.
  • Anthropic Plugin Refactoring: The Anthropic plugin has been refactored to implement the new PluginV2 interface, demonstrating its usage for eager action initialization (init), lazy action resolution (resolve), and action listing (list).
  • Action Factory Functions: New factory functions (model, embedder, retriever) have been added to genkit.blocks to create Action objects without immediate registration, facilitating the v2 plugin design where plugins return these actions.
  • Dynamic Action Resolution: The registry's action resolver now correctly strips the plugin prefix from action names before calling a v2 plugin's resolve method, allowing plugins to handle unprefixed action names internally.
  • Utility Functions: Added is_plugin_v1 and is_plugin_v2 helper functions for runtime plugin type detection, and get_func_description to extract function descriptions from docstrings or provided arguments.
  • Comprehensive Testing: A new test file (test_plugin_v2.py) has been added, covering various aspects of the PluginV2 functionality, including standalone operation, framework integration, lazy loading, and automatic namespacing.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

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 significant and valuable architectural improvement with the PluginV2 interface, which decouples plugins from the registry and improves testability and standalone usage. The refactoring of the Anthropic plugin serves as a great example of the new pattern. The addition of factory functions for models, embedders, and retrievers is a clean way to support this new interface.

However, there are a few critical and medium-severity issues that should be addressed. The most critical is the incorrect use of asyncio.run in an async context, which will lead to runtime errors. There are also opportunities to improve encapsulation in the plugin registration logic and a minor regression in the Anthropic plugin's configuration options. Addressing these points will make the new infrastructure more robust and maintainable.

Comment on lines +119 to +121
if inspect.iscoroutinefunction(plugin.init):
resolved_actions = asyncio.run(plugin.init())
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

asyncio.run() will raise a RuntimeError if called from within a running event loop. Since this code is in an async-focused module, it's likely that Genkit() will be instantiated within an async function, which would cause the application to crash. This same issue exists for plugin.resolve on line 134.

The initialization logic for V2 plugins needs to be async-aware. The initialization process should be made asynchronous. A common pattern is to have a lightweight __init__ and a separate async def _async_init(self) method that performs the actual async setup. Other methods like generate would then need to ensure _async_init has been called. This avoids blocking the event loop and prevents runtime errors.

Comment on lines 190 to 201
if action.name.startswith(f"{plugin.name}/"):
namespaced_name = action.name
else:
namespaced_name = f"{plugin.name}/{action.name}"
action._name = namespaced_name

# Register the action directly in the registry's entries
# (Don't use register_action() as that would create a new Action and double-wrap)
with self.registry._lock:
if action.kind not in self.registry._entries:
self.registry._entries[action.kind] = {}
self.registry._entries[action.kind][namespaced_name] = action
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This method directly accesses and modifies private members of both the Action object (action._name) and the GenkitRegistry object (self.registry._lock, self.registry._entries). This breaks encapsulation, making the code more brittle and harder to maintain. This issue is also present in _base_async.py.

To improve encapsulation, I suggest adding a public method to GenkitRegistry for registering a pre-constructed Action object, for example add_action(self, action: Action). For re-namespacing, the Action class could have a method to return a new instance with a modified name (e.g., action.with_name(new_name)) to promote immutability and avoid direct mutation of private attributes.

Comment on lines 61 to 64
def __init__(
self,
models: list[str] | None = None,
**anthropic_params: str,
) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The refactored __init__ method no longer accepts a models parameter. The previous implementation allowed users to specify a list of model names for eager initialization. The new init method now unconditionally initializes all supported Anthropic models. This is a regression in functionality, as users can no longer control which models are eagerly loaded to manage memory usage or startup time.

def __init__(
    self,
    models: list[str] | None = None,
    **anthropic_params: str,
) -> None:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Python

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant