diff --git a/py/packages/genkit/src/genkit/ai/_plugin.py b/py/packages/genkit/src/genkit/ai/_plugin.py index d90a0f30e1..691657ca20 100644 --- a/py/packages/genkit/src/genkit/ai/_plugin.py +++ b/py/packages/genkit/src/genkit/ai/_plugin.py @@ -24,6 +24,7 @@ from genkit.core.registry import ActionKind +from ..core.action import ActionMetadata from ._registry import GenkitRegistry @@ -75,14 +76,14 @@ def initialize(self, ai: GenkitRegistry) -> None: """ pass - def list_actions(self) -> list[dict[str, str]]: + def list_actions(self) -> list[ActionMetadata]: """Generate a list of available actions or models. Returns: - list of actions dicts with the following shape: - { - 'name': str, - 'kind': ActionKind, - } + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. """ return [] diff --git a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py index 5d27540ba8..6016ddef43 100644 --- a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py +++ b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py @@ -16,13 +16,19 @@ """OpenAI OpenAI API Compatible Plugin for Genkit.""" +from functools import cached_property from typing import Any, Callable -from openai import Client, OpenAI as OpenAIClient +from openai import OpenAI as OpenAIClient +from openai.types import Embedding, Model from genkit.ai._plugin import Plugin from genkit.ai._registry import GenkitRegistry +from genkit.blocks.embedding import embedder_action_metadata +from genkit.blocks.model import model_action_metadata +from genkit.core.action import ActionMetadata from genkit.core.action.types import ActionKind +from genkit.core.typing import GenerationCommonConfig from genkit.plugins.compat_oai.models import ( SUPPORTED_OPENAI_COMPAT_MODELS, SUPPORTED_OPENAI_MODELS, @@ -165,6 +171,53 @@ def _define_openai_model(self, ai: GenkitRegistry, name: str) -> None: }, ) + @cached_property + def list_actions(self) -> list[ActionMetadata]: + """Generate a list of available actions or models. + + Returns: + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. + """ + + actions = [] + models_ = self._openai_client.models.list() + models: list[Model] = models_.data + # Print each model + for model in models: + _name = model.id + if 'embed' in _name: + actions.append( + embedder_action_metadata( + name=open_ai_name(_name), + config_schema=Embedding, + info={ + 'label': f'OpenAI Embedding - {_name}', + 'dimensions': None, + 'supports': { + 'input': ['text'], + }, + }, + ) + ) + else: + actions.append( + model_action_metadata( + name=open_ai_name(_name), + config_schema=GenerationCommonConfig, + info={ + 'label': f'OpenAI - {_name}', + 'multiturn': True, + 'system_role': True, + 'tools': False, + }, + ) + ) + return actions + diff --git a/py/plugins/compat-oai/tests/test_plugin.py b/py/plugins/compat-oai/tests/test_plugin.py index cc750f1949..68f07bc784 100644 --- a/py/plugins/compat-oai/tests/test_plugin.py +++ b/py/plugins/compat-oai/tests/test_plugin.py @@ -17,8 +17,10 @@ from unittest.mock import ANY, MagicMock, patch import pytest +from openai.types import Model from genkit.ai._aio import Genkit +from genkit.core.action import ActionMetadata from genkit.core.action.types import ActionKind from genkit.plugins.compat_oai import OpenAIConfig from genkit.plugins.compat_oai.models.model_info import SUPPORTED_OPENAI_MODELS @@ -76,6 +78,34 @@ def test_openai_plugin_resolve_action(kind, name): ) +def test_openai_plugin_list_actions() -> None: + entries = [ + Model(id='gpt-4-0613', created=1686588896, object='model', owned_by='openai'), + Model(id='gpt-4', created=1687882411, object='model', owned_by='openai'), + Model(id='gpt-3.5-turbo', created=1677610602, object='model', owned_by='openai'), + Model(id='o4-mini-deep-research-2025-06-26', created=1750866121, object='model', owned_by='system'), + Model(id='codex-mini-latest', created=1746673257, object='model', owned_by='system'), + Model(id='text-embedding-ada-002', created=1671217299, object='model', owned_by='openai-internal') + ] + plugin = OpenAI(api_key='test-key') + mock_client = MagicMock() + + mock_result_ = MagicMock() + mock_result_.data = entries + mock_client.models.list.return_value = mock_result_ + + plugin._openai_client = mock_client + + actions: list[ActionMetadata ] = plugin.list_actions + mock_client.models.list.assert_called_once() + _ = plugin.list_actions + mock_client.models.list.assert_called_once() + + assert len(actions) == len(entries) + assert actions[0].name == 'openai/gpt-4-0613' + assert actions[-1].name == 'openai/text-embedding-ada-002' + + @pytest.mark.parametrize( 'kind, name', [ diff --git a/py/plugins/google-genai/src/genkit/plugins/google_genai/google.py b/py/plugins/google-genai/src/genkit/plugins/google_genai/google.py index bf2b74644b..36433d9eeb 100644 --- a/py/plugins/google-genai/src/genkit/plugins/google_genai/google.py +++ b/py/plugins/google-genai/src/genkit/plugins/google_genai/google.py @@ -26,6 +26,7 @@ from genkit.ai import GENKIT_CLIENT_HEADER, GenkitRegistry, Plugin from genkit.blocks.embedding import embedder_action_metadata from genkit.blocks.model import model_action_metadata +from genkit.core.action import ActionMetadata from genkit.core.registry import ActionKind from genkit.plugins.google_genai.models.embedder import ( Embedder, @@ -218,15 +219,15 @@ def _resolve_embedder(self, ai: GenkitRegistry, name: str) -> None: ) @cached_property - def list_actions(self) -> list[dict[str, str]]: + def list_actions(self) -> list[ActionMetadata]: """Generate a list of available actions or models. Returns: - list of actions dicts with the following shape: - { - 'name': str, - 'kind': ActionKind, - } + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. """ actions_list = list() for m in self._client.models.list(): @@ -413,15 +414,15 @@ def _resolve_embedder(self, ai: GenkitRegistry, name: str) -> None: ) @cached_property - def list_actions(self) -> list[dict[str, str]]: + def list_actions(self) -> list[ActionMetadata]: """Generate a list of available actions or models. Returns: - list of actions dicts with the following shape: - { - 'name': str, - 'kind': ActionKind, - } + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. """ actions_list = list() for m in self._client.models.list(): diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/plugin_api.py b/py/plugins/ollama/src/genkit/plugins/ollama/plugin_api.py index 947271885e..472d1c0a7b 100644 --- a/py/plugins/ollama/src/genkit/plugins/ollama/plugin_api.py +++ b/py/plugins/ollama/src/genkit/plugins/ollama/plugin_api.py @@ -209,7 +209,15 @@ def _define_ollama_embedder(self, ai: GenkitRegistry, embedder_ref: EmbeddingDef @cached_property def list_actions(self) -> list[dict[str, str]]: - """.""" + """Generate a list of available actions or models. + + Returns: + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. + """ try: loop = asyncio.get_running_loop() except RuntimeError: diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/model_garden/modelgarden_plugin.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/model_garden/modelgarden_plugin.py index 4746fefee6..48eac737cc 100644 --- a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/model_garden/modelgarden_plugin.py +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/model_garden/modelgarden_plugin.py @@ -21,6 +21,7 @@ from genkit.ai import GenkitRegistry, Plugin from genkit.blocks.model import model_action_metadata +from genkit.core.action import ActionMetadata from genkit.core.action.types import ActionKind from genkit.plugins.compat_oai.models import SUPPORTED_OPENAI_COMPAT_MODELS from genkit.plugins.compat_oai.typing import OpenAIConfig @@ -120,16 +121,17 @@ def _resolve_model(self, ai: GenkitRegistry, name: str) -> None: model_proxy.define_model() @cached_property - def list_actions(self) -> list[dict[str, str]]: + def list_actions(self) -> list[ActionMetadata]: """Generate a list of available actions or models. Returns: - list of actions dicts with the following shape: - { - 'name': str, - 'kind': ActionKind, - } + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. """ + actions_list = [] for model, model_info in SUPPORTED_OPENAI_COMPAT_MODELS.items(): actions_list.append(