Skip to content

Conversation

@huangjeff5
Copy link
Contributor

No description provided.

@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 introduces a fundamental refactoring of the plugin system, transitioning to an asynchronous, lazy-loading architecture. This change streamlines how external functionalities are integrated and managed within the framework, improving efficiency and flexibility by resolving actions on demand rather than during initial setup. The update impacts the core registry, reflection mechanisms, and all existing plugins, ensuring a consistent and modern approach to extensibility.

Highlights

  • Asynchronous Plugin API (PluginV2): The core plugin system has been refactored to introduce a new asynchronous API. The old synchronous genkit.ai._plugin.Plugin class has been removed and replaced with genkit.core.plugin.Plugin, which defines asynchronous init, resolve, and list_actions methods. This enables lazy loading and dynamic resolution of plugin actions.
  • Registry Updates for Async Resolution: The genkit.core.registry.Registry class has been significantly updated to support the new asynchronous plugin model. Old synchronous methods like lookup_action and lookup_action_by_key have been replaced with async resolve_action and async resolve_action_by_key. Plugin registration now uses register_plugin instead of separate resolver registrations, and includes mechanisms for ensuring plugins are initialized only once.
  • Migration of Existing Plugins: All existing plugins (Anthropic, OpenAI-Compat, DeepSeek, Dev Local Vector Store, Evaluators, Firebase, Google GenAI, Ollama, Vertex AI, xAI) have been updated to conform to the new asynchronous PluginV2 API. This involves changing their initialize and resolve_action methods to async init and async resolve, respectively, and updating internal action resolution calls to use the new await registry.resolve_action pattern.
  • Reflection Server Enhancements: The reflection server (genkit.core.reflection.py) has been updated to leverage the new asynchronous plugin API for listing and running actions. Streaming responses have been enhanced with asyncio.Queue for better chunk handling and now use text/plain media type with Transfer-Encoding: chunked to match the Go implementation.
  • Runtime File Naming and Metadata: Runtime files now include the process ID (PID) and a more precise timestamp (milliseconds) in their names to ensure uniqueness across restarts. Additionally, the Genkit version is now included in the runtime metadata.
  • Code Cleanup and Type Hinting: Various files across the codebase have undergone minor cleanup, including removal of unused imports (sys, asyncio, typing.Type, Awaitable, etc.), simplification of StrEnum imports, and updates to type hints for consistency (e.g., Type[BaseModel] to type[BaseModel]). Docstrings have also been refined in several places.

🧠 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.

self._runtime_dir.mkdir(parents=True, exist_ok=True)
runtime_file_path = _create_and_write_runtime_file(self._runtime_dir, self.spec)
_register_atexit_cleanup_handler(runtime_file_path)
self._runtime_file_path = _create_and_write_runtime_file(self._runtime_dir, self.spec)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

investigate some potential weirdness in runtime file
seems like first time we start (no runtime file) there is some weird slowness


from pydantic import BaseModel, ConfigDict, Field

if sys.version_info < (3, 11):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why did we erase this? not sure if needed.

from strenum import StrEnum
else:
from enum import StrEnum
from enum import StrEnum
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why did we erase this? not sure if needed.

try:
key = f'/{meta.kind.value}/{meta.name}'
except Exception:
# Defensive: skip unexpected plugin metadata objects.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe log warn

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 is a major refactoring of the plugin system to a new asynchronous "V2" API. The changes are extensive, touching core registry logic, all existing plugins, and tests. The new plugin interface is cleaner and async-first. Most of the changes correctly adapt the codebase to this new model, including significant improvements to streaming logic in the reflection server and bug fixes in request handling. However, the refactoring appears to be incomplete for the VertexAIVectorSearch plugin, as its new init method is empty, effectively disabling its functionality. This is a critical issue that needs to be addressed.

Comment on lines +92 to +101
async def init(self) -> list:
"""Initialize plugin with the retriever specified.
Register actions with the registry making them available for use in the Genkit framework.
Returns:
Empty list (using lazy loading via resolve).
"""
return []

Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The refactoring of the VertexAIVectorSearch plugin to the new V2 API appears to be incomplete. The previous initialize method, which was responsible for registering the retriever action, has been replaced with an init method that currently returns an empty list. This means the plugin no longer registers its retriever and is effectively non-functional.

To fix this, the init method should create and return the retriever Action. However, this requires access to the Genkit instance (for embedding), which is not currently passed to the plugin's __init__. A similar plugin, FirestoreVectorStore, was updated to accept the ai instance in its constructor. A similar change is likely needed here.

Assuming the __init__ is updated to accept and store an ai instance as self.ai, the init method should be implemented like this:

    async def init(self) -> list:
        """Initialize plugin with the retriever specified.

        Creates and returns the retriever action.
        """
        from genkit.core.action import Action
        from genkit.core.action.types import ActionKind
        from genkit.plugins.vertex_ai.vector_search.retriever import RetrieverOptionsSchema
        from genkit.core.schema import to_json_schema

        retriever = self.retriever_cls(
            ai=self.ai,
            name=self.name,
            match_service_client_generator=self._match_service_client_generator,
            embedder=self.embedder,
            embedder_options=self.embedder_options,
            **self.retriever_extra_args,
        )

        return [
            Action(
                kind=ActionKind.RETRIEVER,
                name=vertexai_name(self.name),
                fn=retriever.retrieve,
                metadata={
                    'retriever': {
                        'customOptions': to_json_schema(RetrieverOptionsSchema),
                    }
                },
            )
        ]


def send_chunk(chunk):
chunk_json = dump_json(chunk)
logger.info(f'📤 Sending chunk: {chunk_json[:200]}...')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove

)
self.wfile.write(bytes('\n', encoding))
self.wfile.flush() # Flush immediately for streaming
logger.info('✅ Chunk flushed')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove log

def send_chunk(chunk):
"""Callback that puts chunks on the queue."""
out = dump_json(chunk)
chunk_queue.put_nowait(f'{out}\n')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure waht put_nowait does

self._entries[action.kind] = {}
self._entries[action.kind][name] = action

async def resolve_action(self, kind: ActionKind, name: str) -> Action | None:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

does this method skip registered actions? seems like it

True if the query flag is enabled, False otherwise.
"""
return query_params.get(flag, ['false'])[0] == 'true'
return query_params.get(flag, 'false') == 'true'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

think this is right but double check

)

override_messages = [
[
Copy link
Contributor Author

Choose a reason for hiding this comment

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

?? is this dead code


got = registry.list_actions({}, allowed_kind)
assert got == expected
# NOTE: This test is disabled because the old synchronous list_actions method
Copy link
Contributor Author

Choose a reason for hiding this comment

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

delete this

resolver_calls.append([kind, name])
resolver_calls.append([action_type, name])

from genkit.core.action import Action
Copy link
Contributor Author

Choose a reason for hiding this comment

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

import at top level

"""
model = AnthropicModel(model_name=model_name, client=self._anthropic_client)
model_info = get_model_info(model_name)
from genkit.core.action import Action
Copy link
Contributor Author

Choose a reason for hiding this comment

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

import at top

Returns:
List of ActionMetadata for all supported models.
"""
from genkit.blocks.model import model_action_metadata
Copy link
Contributor Author

Choose a reason for hiding this comment

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

import at top

The fully qualified OpenAi-Compat action name.
"""
return f'openai/{name}'
return f'openai-compat/{name}'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

keep ti as openai

Action object for the model.
"""
from genkit.core.action import Action
from genkit.core.schema import to_json_schema
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mports to top

model=clean_name,
api_key=self.api_key,
registry=ai,
registry=None, # We don't need registry anymore
Copy link
Contributor Author

Choose a reason for hiding this comment

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

see to remove registry parameter from this

# Create retriever action
retriever = DevLocalVectorStoreRetriever(
ai=ai,
ai=None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why pass ai=None

embedder_options: dict[str, Any] | None = None,
distance_measure: DistanceMeasure = DistanceMeasure.COSINE,
metadata_fields: list[str] | MetadataTransformFn | None = None,
ai: Genkit | None = None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why ai gets passedi n now?

location=self.location,
project_id=self.project_id,
registry=ai,
registry=None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why registry is an arg here

response = await ai.generate(
prompt=f'Generate and run code for the task: {task}',
config=GeminiConfigSchema(temperature=1, code_execution=True),
config={'temperature': 1, 'code_execution': True},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why did we get rid fo this again

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant