Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions py/packages/genkit/src/genkit/_core/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,15 +757,24 @@ async def resolve_embedder(self, name: str) -> Action[EmbedRequest, EmbedRespons
return cast(Action[EmbedRequest, EmbedResponse, Never], action)

async def resolve_model(self, name: str) -> Action[ModelRequest, ModelResponse, ModelResponseChunk] | None:
"""Resolve a model action by name with full type information.
"""Resolve a model action by name.

Looks up a normal model first, then a background start action, so a
name registered only with ``define_background_model`` is findable
under the same string callers already pass.

Args:
name: The model name (e.g., "gemini-pro" or "plugin/model").

Returns:
A fully typed model action, or None if not found.
The MODEL action, or the BACKGROUND_MODEL start action, or None.
"""
action = await self.resolve_action(ActionKind.MODEL, name)
if action is None:
# Models registered with define_background_model live under this
# kind. Callers still pass the same name they would for a normal
# model.
action = await self.resolve_action(ActionKind.BACKGROUND_MODEL, name)
if action is None:
return None
return cast(
Expand Down
77 changes: 75 additions & 2 deletions py/packages/genkit/tests/genkit/core/registry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
import pytest

from genkit import Genkit, Plugin
from genkit._core._action import Action, ActionKind, create_action_key
from genkit._core._action import Action, ActionKind, ActionRunContext, create_action_key
from genkit._core._dap import DapValue, define_dynamic_action_provider
from genkit._core._model import ModelRequest, ModelResponse
from genkit._core._registry import Registry
from genkit._core._typing import ActionMetadata
from genkit._core._typing import ActionMetadata, Operation


async def _identity(x: object) -> object:
Expand Down Expand Up @@ -438,3 +439,75 @@ def test_registry_satisfies_registry_like() -> None:
from genkit._core._registry import Registry

assert isinstance(Registry(None), RegistryLike)


async def _bg_start(_request: ModelRequest, _ctx: ActionRunContext) -> Operation:
return Operation(id='bg-op', done=False)


async def _bg_check(op: Operation) -> Operation:
return op


@pytest.mark.asyncio
async def test_resolve_model_finds_background_model() -> None:
"""A name registered only as a background model is still findable."""
ai = Genkit()
action = ai.define_background_model(name='bg-model', start=_bg_start, check=_bg_check)

got = await ai.registry.resolve_model('bg-model')

assert got is not None
assert got is action.start_action
assert got.kind == ActionKind.BACKGROUND_MODEL


@pytest.mark.asyncio
async def test_resolve_model_prefers_foreground_when_both_exist() -> None:
"""A normal model of the same name wins. The fallback is only for names that have no MODEL."""

async def fg(_request: ModelRequest, _ctx: ActionRunContext) -> ModelResponse:
return ModelResponse()

ai = Genkit()
foreground = ai.define_model(name='same-name', fn=fg)
ai.define_background_model(name='same-name', start=_bg_start, check=_bg_check)

got = await ai.registry.resolve_model('same-name')

assert got is not None
assert got is foreground
assert got.kind == ActionKind.MODEL


@pytest.mark.asyncio
async def test_resolve_model_missing_is_none() -> None:
"""Unknown names stay None. This is not NOT_FOUND — callers decide the error."""
ai = Genkit()
assert await ai.registry.resolve_model('no-such-model') is None


@pytest.mark.asyncio
async def test_resolve_model_finds_plugin_background_model() -> None:
"""A plugin MODEL miss still lets the BACKGROUND_MODEL start action through."""

class VeoPlugin(Plugin):
name = 'plug'

async def init(self) -> list[Action]:
return []

async def list_actions(self) -> list[ActionMetadata]:
return []

async def resolve(self, action_type: ActionKind, name: str) -> Action | None:
if action_type != ActionKind.BACKGROUND_MODEL:
return None
return Action(name=name, kind=ActionKind.BACKGROUND_MODEL, fn=_bg_start)

ai = Genkit(plugins=[VeoPlugin()])
got = await ai.registry.resolve_model('plug/veo-2.0-generate-001')

assert got is not None
assert got.kind == ActionKind.BACKGROUND_MODEL
assert got.name == 'plug/veo-2.0-generate-001'
Loading