Skip to content

Conversation

@MengqinShen
Copy link
Contributor

Description here... Help the reviewer by:

  • linking to an issue that includes more details
  • if it's a new feature include samples of how to use the new feature
  • (optional if issue link is provided) if you fixed a bug include basic bug details

Checklist (if applicable):

@gemini-code-assist
Copy link

Summary of Changes

Hello @MengqinShen, 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 refines the prompt management system within the Genkit Python framework. It introduces robust capabilities for defining and loading prompts from external files, supporting advanced features like prompt variants and reusable partials. The core ExecutablePrompt class has been enhanced to facilitate lazy loading and expose prompts as distinct actions for rendering and execution. These changes aim to streamline prompt development and integration, making the system more flexible and performant. Accompanying these functional updates are comprehensive tests to ensure reliability and minor adjustments to project dependencies and build configurations.

Highlights

  • Enhanced Prompt Management: Introduced new functionalities for loading and managing prompts from .prompt files, including support for variants and partials. This allows for more organized and reusable prompt definitions.
  • Lazy Loading of Prompts: Implemented lazy loading for file-based prompts, meaning they are only fully processed and loaded into memory when first accessed, improving startup performance.
  • Dual Action Registration for Prompts: File-based prompts now register two distinct actions: a PROMPT action that returns a GenerateRequest (for rendering) and an EXECUTABLE_PROMPT action that returns GenerateActionOptions (for execution), providing clearer separation of concerns.
  • New prompt() Method in Genkit: Added an async def prompt method to the Genkit class, enabling direct lookup and retrieval of ExecutablePrompt instances by name and optional variant, aligning with JavaScript API behavior.
  • Comprehensive Prompt Testing: Extensive new test cases have been added to validate the new prompt loading, variant handling, partial inclusion, message list processing, tool integration, output schema preservation, and configuration merging behaviors.
  • Dependency and Build System Updates: Updated uv.lock to support Python 3.14 and manage grpcio versions conditionally. Also added opentelemetry-exporter-gcp-monitoring as a new dependency.
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.

@MengqinShen
Copy link
Contributor Author

This branch includes some previous changes that have been merged already. Will create a new branch and new PR.

Copy link

@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 significant new functionality for defining, loading, and looking up prompts, including support for file-based prompts, variants, and partials. It also adds comprehensive tests for these new features. My review focuses on a critical bug in the lazy-loading implementation for prompts which could lead to memory leaks and incorrect behavior, as well as some documentation inconsistencies. The changes are otherwise well-structured and the addition of tests is a great practice.

Comment on lines +1159 to +1185
if action:
# First check if we've stored the ExecutablePrompt directly
if hasattr(action, '_executable_prompt') and action._executable_prompt is not None:
return action._executable_prompt
elif hasattr(action, '_async_factory'):
# Otherwise, create it from the factory (lazy loading)
# This will also set _executable_prompt on the action for future lookups
executable_prompt = await action._async_factory()
# Store it on the action for future lookups (if not already stored)
if not hasattr(action, '_executable_prompt') or action._executable_prompt is None:
action._executable_prompt = executable_prompt
return executable_prompt
else:
# Fallback: try to get from metadata
factory = action.metadata.get('_async_factory')
if factory:
executable_prompt = await factory()
# Store it on the action for future lookups
if not hasattr(action, '_executable_prompt') or action._executable_prompt is None:
action._executable_prompt = executable_prompt
return executable_prompt
# Last resort: this shouldn't happen if prompts are loaded correctly
raise GenkitError(
status='INTERNAL',
message=f'Prompt action found but no ExecutablePrompt available for {name}',
)

Choose a reason for hiding this comment

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

critical

The implementation of lookup_prompt has a critical bug related to weakref that can cause memory leaks and return incorrect objects.

  1. return action._executable_prompt returns the weakref.ref object itself, not the ExecutablePrompt instance it refers to. You should call the weakref object to get the underlying prompt: action._executable_prompt().
  2. The code that re-assigns action._executable_prompt = executable_prompt after creating it from the factory creates a strong reference, which defeats the purpose of using weakref and will lead to a memory leak due to a circular reference. The factory function create_prompt_from_file already correctly sets the weakref.

This suggested change corrects the weakref handling and simplifies the logic to prevent these issues.

    if action:
        # First check if we have a live weak reference to the ExecutablePrompt
        if hasattr(action, '_executable_prompt') and action._executable_prompt:
            prompt = action._executable_prompt()
            if prompt:
                return prompt

        # If not, create it from the factory (lazy loading).
        # The factory is responsible for setting the weakref on the action.
        if hasattr(action, '_async_factory'):
            return await action._async_factory()

        # Fallback for safety, though _async_factory should exist.
        factory = action.metadata.get('_async_factory')
        if factory:
            return await factory()

        # Last resort: this shouldn't happen if prompts are loaded correctly
        raise GenkitError(
            status='INTERNAL',
            message=f'Prompt action found but no ExecutablePrompt available for {name}',
        )

Comment on lines +626 to +629
registry: The registry to look up the prompt from.
name: The name of the prompt.
variant: Optional variant name.
dir: Optional directory parameter (accepted for compatibility but not used).

Choose a reason for hiding this comment

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

high

The docstring for the prompt method is inaccurate. It lists registry and dir as parameters, but they are not part of the method's signature. The method uses self.registry instead. This can be misleading for developers using this method. Please update the docstring to match the method signature.

Suggested change
registry: The registry to look up the prompt from.
name: The name of the prompt.
variant: Optional variant name.
dir: Optional directory parameter (accepted for compatibility but not used).
name: The name of the prompt.
variant: Optional variant name.

if self._name is None:
raise GenkitError(
status='FAILED_PRECONDITION',
message='Prompt name not available. This prompt was not created via define_prompt_async() or load_prompt().',

Choose a reason for hiding this comment

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

medium

The error message here mentions define_prompt_async(), but there is no such function defined in the codebase. It seems this should refer to define_prompt() instead.

Suggested change
message='Prompt name not available. This prompt was not created via define_prompt_async() or load_prompt().',
message='Prompt name not available. This prompt was not created via define_prompt() or load_prompt().',

Comment on lines +531 to +533
Exception: If a tool name provided in options cannot be found in
the registry.
GenkitError: If the options do not contain any messages.

Choose a reason for hiding this comment

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

medium

The docstring for to_generate_request indicates that it raises an Exception if a tool is not found. However, the implementation raises a GenkitError. It's best to keep the docstring consistent with the implementation and consolidate the Raises section.

Suggested change
Exception: If a tool name provided in options cannot be found in
the registry.
GenkitError: If the options do not contain any messages.
GenkitError: If a tool name provided in options cannot be found in
the registry, or if the options do not contain any messages.

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants