From 7624f40862d4c56d064df106e309e7eb50d44fc9 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 20 Aug 2026 13:32:28 -0500 Subject: [PATCH 1/3] feat(py): fallback to BACKGROUND_MODEL in resolve_model --- .../genkit/src/genkit/_core/_registry.py | 5 ++ .../genkit/tests/genkit/core/registry_test.py | 49 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/py/packages/genkit/src/genkit/_core/_registry.py b/py/packages/genkit/src/genkit/_core/_registry.py index 52ce070a47..d6c1b89c26 100644 --- a/py/packages/genkit/src/genkit/_core/_registry.py +++ b/py/packages/genkit/src/genkit/_core/_registry.py @@ -766,6 +766,11 @@ async def resolve_model(self, name: str) -> Action[ModelRequest, ModelResponse, A fully typed model action, or None if not found. """ 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( diff --git a/py/packages/genkit/tests/genkit/core/registry_test.py b/py/packages/genkit/tests/genkit/core/registry_test.py index 628a237d82..5ddb42c874 100644 --- a/py/packages/genkit/tests/genkit/core/registry_test.py +++ b/py/packages/genkit/tests/genkit/core/registry_test.py @@ -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: @@ -438,3 +439,47 @@ 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 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 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 From adaa13904ca73427da25e25fc030e08a1fe96401 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 21 Aug 2026 13:05:13 -0500 Subject: [PATCH 2/3] update docstring --- py/packages/genkit/src/genkit/_core/_registry.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/py/packages/genkit/src/genkit/_core/_registry.py b/py/packages/genkit/src/genkit/_core/_registry.py index d6c1b89c26..4123777ed9 100644 --- a/py/packages/genkit/src/genkit/_core/_registry.py +++ b/py/packages/genkit/src/genkit/_core/_registry.py @@ -757,13 +757,17 @@ 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: From 41899d2d544dda931b4758fbece1f2e0375ddf39 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 21 Aug 2026 15:59:44 -0500 Subject: [PATCH 3/3] add test --- .../genkit/tests/genkit/core/registry_test.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/py/packages/genkit/tests/genkit/core/registry_test.py b/py/packages/genkit/tests/genkit/core/registry_test.py index 5ddb42c874..069a19a4ce 100644 --- a/py/packages/genkit/tests/genkit/core/registry_test.py +++ b/py/packages/genkit/tests/genkit/core/registry_test.py @@ -457,6 +457,7 @@ async def test_resolve_model_finds_background_model() -> None: 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 @@ -474,6 +475,7 @@ async def fg(_request: ModelRequest, _ctx: ActionRunContext) -> ModelResponse: got = await ai.registry.resolve_model('same-name') + assert got is not None assert got is foreground assert got.kind == ActionKind.MODEL @@ -483,3 +485,29 @@ 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'