Skip to content

Commit ba9c5c7

Browse files
authored
feat: PluginV2 refactor (#4132)
1 parent 92524ce commit ba9c5c7

148 files changed

Lines changed: 2879 additions & 3019 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

py/bin/sanitize_schema_typing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@
4242

4343
import ast
4444
import sys
45-
from _ast import AST
4645
from datetime import datetime
4746
from pathlib import Path
48-
from typing import Any, Type, cast
47+
from typing import Any, cast
4948

5049

5150
class ClassTransformer(ast.NodeTransformer):

py/packages/genkit/src/genkit/ai/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
from genkit.core import GENKIT_CLIENT_HEADER, GENKIT_VERSION
3636
from genkit.core.action import ActionRunContext
3737
from genkit.core.action.types import ActionKind
38+
from genkit.core.plugin import Plugin
3839

3940
from ._aio import Genkit
40-
from ._plugin import Plugin
4141
from ._registry import FlowWrapper, GenkitRegistry
4242

4343
__all__ = [

py/packages/genkit/src/genkit/ai/_aio.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ class while customizing it with any plugins.
4343
from genkit.blocks.retriever import IndexerRef, IndexerRequest, RetrieverRef
4444
from genkit.core.action import ActionRunContext
4545
from genkit.core.action.types import ActionKind
46+
from genkit.core.plugin import Plugin
4647
from genkit.core.typing import (
4748
BaseDataPoint,
48-
BaseEvalDataPoint,
4949
EmbedRequest,
5050
EmbedResponse,
5151
EvalRequest,
@@ -61,8 +61,7 @@ class while customizing it with any plugins.
6161
ToolChoice,
6262
)
6363

64-
from ._base import GenkitBase
65-
from ._plugin import Plugin
64+
from ._base_async import GenkitBase
6665
from ._server import ServerSpec
6766

6867

@@ -328,7 +327,7 @@ async def retrieve(
328327
Args:
329328
retriever: Optional retriever name or reference to use.
330329
query: Text query or a DocumentData containing query text.
331-
options: retriever options
330+
options: Optional retriever-specific options.
332331
333332
Returns:
334333
The generated response with documents.
@@ -349,11 +348,20 @@ async def retrieve(
349348
if isinstance(query, str):
350349
query = Document.from_text(query)
351350

352-
final_options = {**(retriever_config or {}), **(options or {})}
351+
request_options = {**(retriever_config or {}), **(options or {})}
353352

354-
retrieve_action = self.registry.lookup_action(ActionKind.RETRIEVER, retriever_name)
353+
retrieve_action = await self.registry.resolve_action(ActionKind.RETRIEVER, retriever_name)
354+
if retrieve_action is None:
355+
raise ValueError(f'Retriever "{retriever_name}" not found')
355356

356-
return (await retrieve_action.arun(RetrieverRequest(query=query, options=final_options))).response
357+
return (
358+
await retrieve_action.arun(
359+
RetrieverRequest(
360+
query=query,
361+
options=request_options if request_options else None,
362+
)
363+
)
364+
).response
357365

358366
async def index(
359367
self,
@@ -366,7 +374,7 @@ async def index(
366374
Args:
367375
indexer: Optional indexer name or reference to use.
368376
documents: Documents to index.
369-
options: indexer options
377+
options: Optional indexer-specific options.
370378
"""
371379
indexer_name: str
372380
indexer_config: dict[str, Any] = {}
@@ -381,11 +389,18 @@ async def index(
381389
else:
382390
raise ValueError('Indexer must be specified as a string name or an IndexerRef.')
383391

384-
final_options = {**(indexer_config or {}), **(options or {})}
392+
req_options = {**(indexer_config or {}), **(options or {})}
385393

386-
index_action = self.registry.lookup_action(ActionKind.INDEXER, indexer_name)
394+
index_action = await self.registry.resolve_action(ActionKind.INDEXER, indexer_name)
395+
if index_action is None:
396+
raise ValueError(f'Indexer "{indexer_name}" not found')
387397

388-
await index_action.arun(IndexerRequest(documents=documents, options=final_options))
398+
await index_action.arun(
399+
IndexerRequest(
400+
documents=documents,
401+
options=req_options if req_options else None,
402+
)
403+
)
389404

390405
async def embed(
391406
self,
@@ -410,7 +425,9 @@ async def embed(
410425
# Merge options passed to embed() with config from EmbedderRef
411426
final_options = {**(embedder_config or {}), **(options or {})}
412427

413-
embed_action = self.registry.lookup_action(ActionKind.EMBEDDER, embedder_name)
428+
embed_action = await self.registry.resolve_action(ActionKind.EMBEDDER, embedder_name)
429+
if embed_action is None:
430+
raise ValueError(f'Embedder "{embedder_name}" not found')
414431

415432
return (await embed_action.arun(EmbedRequest(input=documents, options=final_options))).response
416433

@@ -445,7 +462,9 @@ async def evaluate(
445462

446463
final_options = {**(evaluator_config or {}), **(options or {})}
447464

448-
eval_action = self.registry.lookup_action(ActionKind.EVALUATOR, evaluator_name)
465+
eval_action = await self.registry.resolve_action(ActionKind.EVALUATOR, evaluator_name)
466+
if eval_action is None:
467+
raise ValueError(f'Evaluator "{evaluator_name}" not found')
449468

450469
if not eval_run_id:
451470
eval_run_id = str(uuid.uuid4())

py/packages/genkit/src/genkit/ai/_base.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
from genkit.blocks.formats import built_in_formats
3030
from genkit.blocks.generate import define_generate_action
3131
from genkit.core.environment import is_dev_environment
32+
from genkit.core.plugin import Plugin
3233
from genkit.core.reflection import make_reflection_server
3334
from genkit.web.manager import find_free_port_sync
3435

35-
from ._plugin import Plugin
3636
from ._registry import GenkitRegistry
3737
from ._server import ServerSpec, init_default_runtime
3838

@@ -121,19 +121,7 @@ def _initialize_registry(self, model: str | None, plugins: list[Plugin] | None)
121121
else:
122122
for plugin in plugins:
123123
if isinstance(plugin, Plugin):
124-
plugin.initialize(ai=self)
125-
126-
def resolver(kind, name, plugin=plugin):
127-
return plugin.resolve_action(self, kind, name)
128-
129-
def action_resolver(plugin=plugin):
130-
if isinstance(plugin.list_actions, list):
131-
return plugin.list_actions
132-
else:
133-
return plugin.list_actions()
134-
135-
self.registry.register_action_resolver(plugin.plugin_name(), resolver)
136-
self.registry.register_list_actions_resolver(plugin.plugin_name(), action_resolver)
124+
self.registry.register_plugin(plugin)
137125
else:
138126
raise ValueError(f'Invalid {plugin=} provided to Genkit: must be of type `genkit.ai.Plugin`')
139127

py/packages/genkit/src/genkit/ai/_base_async.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525

2626
from genkit.aio.loop import run_loop
2727
from genkit.blocks.formats import built_in_formats
28+
from genkit.blocks.generate import define_generate_action
2829
from genkit.core.environment import is_dev_environment
30+
from genkit.core.plugin import Plugin
2931
from genkit.core.reflection import create_reflection_asgi_app
3032
from genkit.web.manager import find_free_port_sync
3133

32-
from ._plugin import Plugin
3334
from ._registry import GenkitRegistry
3435
from ._runtime import RuntimeManager
3536
from ._server import ServerSpec
@@ -59,6 +60,8 @@ def __init__(
5960
super().__init__()
6061
self._reflection_server_spec = reflection_server_spec
6162
self._initialize_registry(model, plugins)
63+
# Ensure the default generate action is registered for async usage.
64+
define_generate_action(self.registry)
6265

6366
def _initialize_registry(self, model: str | None, plugins: list[Plugin] | None) -> None:
6467
"""Initialize the registry for the Genkit instance.
@@ -82,12 +85,7 @@ def _initialize_registry(self, model: str | None, plugins: list[Plugin] | None)
8285
else:
8386
for plugin in plugins:
8487
if isinstance(plugin, Plugin):
85-
plugin.initialize(ai=self)
86-
87-
def resolver(kind, name, plugin=plugin):
88-
return plugin.resolve_action(self, kind, name)
89-
90-
self.registry.register_action_resolver(plugin.plugin_name(), resolver)
88+
self.registry.register_plugin(plugin)
9189
else:
9290
raise ValueError(f'Invalid {plugin=} provided to Genkit: must be of type `genkit.ai.Plugin`')
9391

py/packages/genkit/src/genkit/ai/_plugin.py

Lines changed: 0 additions & 89 deletions
This file was deleted.

py/packages/genkit/src/genkit/ai/_registry.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import uuid
4444
from collections.abc import AsyncIterator, Callable
4545
from functools import wraps
46-
from typing import TYPE_CHECKING, Any, Callable, Type
46+
from typing import TYPE_CHECKING, Any
4747

4848
if TYPE_CHECKING:
4949
from genkit.blocks.resource import ResourceFn, ResourceOptions
@@ -429,6 +429,7 @@ def define_reranker(
429429
config_schema=reranker_meta['reranker'].get('customOptions'),
430430
label=reranker_meta['reranker'].get('label'),
431431
),
432+
description=reranker_description,
432433
)
433434

434435
async def rerank(
@@ -636,7 +637,7 @@ def define_model(
636637
self,
637638
name: str,
638639
fn: ModelFn,
639-
config_schema: Type[BaseModel] | dict[str, Any] | None = None,
640+
config_schema: type[BaseModel] | dict[str, Any] | None = None,
640641
metadata: dict[str, Any] | None = None,
641642
info: ModelInfo | None = None,
642643
description: str | None = None,
@@ -820,7 +821,6 @@ async def prompt(
820821
Raises:
821822
GenkitError: If the prompt is not found.
822823
"""
823-
824824
return await lookup_prompt(
825825
registry=self.registry,
826826
name=name,

py/packages/genkit/src/genkit/ai/_runtime.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
import structlog
3030

31+
from genkit.core.constants import DEFAULT_GENKIT_VERSION
32+
3133
from ._server import ServerSpec
3234

3335
logger = structlog.get_logger(__name__)
@@ -87,15 +89,24 @@ def _create_and_write_runtime_file(runtime_dir: Path, spec: ServerSpec) -> Path:
8789
The Path object of the created file.
8890
"""
8991
current_datetime = datetime.now()
90-
runtime_file_name = f'{current_datetime.isoformat()}.json'
92+
timestamp_ms = int(current_datetime.timestamp() * 1000)
93+
pid = os.getpid()
94+
95+
# Build a unique runtime ID from the process ID and port
96+
port = spec.port if spec.port else ''
97+
runtime_id = f'{pid}-{port}' if port else f'{pid}'
98+
99+
# Include timestamp in filename to avoid collisions across restarts
100+
runtime_file_name = f'{runtime_id}-{timestamp_ms}.json'
91101
runtime_file_path = runtime_dir / runtime_file_name
92102

93103
metadata = json.dumps({
94104
'reflectionApiSpecVersion': 1,
95-
'id': f'{os.getpid()}',
96-
'pid': os.getpid(),
105+
'id': runtime_id,
106+
'pid': pid,
107+
'genkitVersion': 'py/' + DEFAULT_GENKIT_VERSION,
97108
'reflectionServerUrl': spec.url,
98-
'timestamp': f'{current_datetime.isoformat()}',
109+
'timestamp': current_datetime.isoformat(),
99110
})
100111

101112
logger.debug(f'Writing runtime file: {runtime_file_path}')
@@ -156,8 +167,8 @@ async def __aenter__(self) -> RuntimeManager:
156167
try:
157168
await logger.adebug(f'Ensuring runtime directory exists: {self._runtime_dir}')
158169
self._runtime_dir.mkdir(parents=True, exist_ok=True)
159-
runtime_file_path = _create_and_write_runtime_file(self._runtime_dir, self.spec)
160-
_register_atexit_cleanup_handler(runtime_file_path)
170+
self._runtime_file_path = _create_and_write_runtime_file(self._runtime_dir, self.spec)
171+
_register_atexit_cleanup_handler(self._runtime_file_path)
161172

162173
except Exception as e:
163174
logger.error(f'Failed to initialize runtime file: {e}', exc_info=True)

0 commit comments

Comments
 (0)