Skip to content
Draft
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
3 changes: 1 addition & 2 deletions py/bin/run_python_tests
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ PYTHON_VERSIONS=(

#"pypy3.12" # TODO: Enable when it is released.

# TODO: Wait for https://github.com/PyO3/pyo3/issues/5000 to be fixed.
#"python3.14" # Next version to catch breakages early.
"python3.14" # Next version to catch breakages early.
)

# WORKAROUND:
Expand Down
2 changes: 1 addition & 1 deletion py/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
'3.11',
'3.12',
'3.13',
#'3.14', # This still fails.
'3.14',
]


Expand Down
1 change: 1 addition & 0 deletions py/packages/genkit/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
2 changes: 1 addition & 1 deletion py/packages/genkit/src/genkit/aio/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async def wait_for_310(fut: asyncio.Future[T], timeout: float | None = None) ->
raise TimeoutError() from e


if sys.version_info < (3, 11):
if sys.version_info < (3, 14):
wait_for = wait_for_310
else:
wait_for = asyncio.wait_for
248 changes: 244 additions & 4 deletions py/packages/genkit/src/genkit/blocks/embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,68 @@
"""Embedding actions."""

from collections.abc import Callable
from typing import Any
from typing import Any, TypedDict, Union, TYPE_CHECKING

from genkit.ai import ActionKind
from genkit.core.action import ActionMetadata
from genkit.core.action.types import ActionKind
from genkit.core.action import ActionMetadata,Action
from genkit.core.schema import to_json_schema
from genkit.core.typing import EmbedRequest, EmbedResponse
from genkit.core.typing import EmbedRequest, EmbedResponse,Embedding, DocumentData
from genkit.core.registry import Registry

# type EmbedderFn = Callable[[EmbedRequest], EmbedResponse]
EmbedderFn = Callable[[EmbedRequest], EmbedResponse]

class EmbedderOptions(TypedDict):
"""Options for defining an embedder."""
name:str
config_schema: Any | None
info: dict[str, Any] | None

class EmbedderParams(TypedDict):
"""Parameters for embedding a single piece of content."""
embedder: 'EmbedderArgument' # Must be one of : str, EmbedderAction,or EmbedderReference
content: Union[str, DocumentData] # Must be string or DocumentData
metadata: dict[str, Any] | None # Optional metadata dict
options: Any | None # Optional options

class EmbedManyParams(TypedDict):
"""Parameters for embedding multiple pieces of content."""
embedder: 'EmbedderArgument'
content: list[Union[str, DocumentData]]
metadata: dict[str, Any] | None
options: Any | None

class EmbedderReference(TypedDict):
"""Reference to an embedder with configuration."""
name: str
config_schema: Any | None
info: dict[str, Any] | None
config: dict[str, Any] | None
version: str | None

class EmbedderReferenceOptions(TypedDict):
"""Options for creating an embedder reference."""
name: str
config_schema: Any | None
info: dict[str, Any] | None
config: dict[str, Any] | None
version: str | None
namespace: str | None

# Union type for embedder arguments
EmbedderArgument = Union[str, 'EmbedderAction', EmbedderReference]

# Type alias for embedder action
if TYPE_CHECKING:
EmbedderAction = Action
else:
EmbedderAction = Any

class ResolvedEmbedder(TypedDict):
"""Resolved embedder with action, config, and version."""
embedder_action: EmbedderAction
config: dict[str, Any] | None
version: str | None

def embedder_action_metadata(
name: str,
Expand All @@ -42,3 +94,191 @@ def embedder_action_metadata(
output_json_schema=to_json_schema(EmbedResponse),
metadata={'embedder': {**info, 'customOptions': to_json_schema(config_schema) if config_schema else None}},
)


def embedder(
options: EmbedderOptions,
runner: EmbedderFn,
) -> EmbedderAction:
"""Creates embedder model for the provided EmbedderFn model implementation.
Unlike define_embedder this function does not register the embedder in the registry."""
# Create metadata
embedder_meta: dict[str, Any] = {}
if 'embedder' not in embedder_meta:
embedder_meta['embedder'] = {}

if options.get('config_schema'):
embedder_meta['embedder']['customOptions'] = to_json_schema(options['config_schema'])

if options.get('info'):
embedder_meta['embedder'].update(options['info'])

return Action(
kind=ActionKind.EMBEDDER,
name=options['name'],
fn=runner,
metadata=embedder_meta,
)


def define_embedder(
registry: 'Registry',
options: EmbedderOptions,
runner: EmbedderFn,
) -> EmbedderAction:
"""Creates embedder model for the provided EmbedderFn model implementation.

This function registers the embedder in the registry."""

# Create metadata
embedder_meta: dict[str, Any] = {}
if 'embedder' not in embedder_meta:
embedder_meta['embedder'] = {}

if options.get('config_schema'):
embedder_meta['embedder']['customOptions'] = to_json_schema(options['config_schema'])

if options.get('info'):
embedder_meta['embedder'].update(options['info'])

# Register the action in the registry
return registry.register_action(
kind=ActionKind.EMBEDDER,
name=options['name'],
fn=runner,
metadata=embedder_meta,
)


async def embed(
registry: 'Registry',
params: EmbedderParams,
) -> list[Embedding]:
"""Single embedding function"""
resolved = await _resolve_embedder(registry, params['embedder'])

# Convert content to DocumentData if it's a string
if isinstance(params['content'], str):
from genkit.blocks.document import Document
document = Document.from_text(params['content'], params.get('metadata'))
input_docs = [document.model_dump()]
else:
input_docs = [params['content']]

# Merge options: reference config/version + caller-provided options
merged_options: dict[str, Any] | None = None
if resolved.get('version') is not None or resolved.get('config'):
merged_options = {}
if resolved.get('version') is not None:
merged_options['version'] = resolved['version']
if resolved.get('config'):
merged_options.update(resolved['config'])
if params.get('options'):
merged_options.update(params['options'])
else:
merged_options = params.get('options')

# Create embed request
request = EmbedRequest(
input=input_docs,
options=merged_options,
)

response = await resolved['embedder_action'].arun(input=request)
return response.response.embeddings

async def embed_many(
registry: 'Registry',
params: EmbedManyParams,
) -> list[Embedding]:
"""Batch embedding function."""
resolved = await _resolve_embedder(registry, params['embedder'])

# Convert content to DocumentData if it's strings
input_docs = []
for content in params['content']:
if isinstance(content, str):
from genkit.blocks.document import Document
document = Document.from_text(content, params.get('metadata'))
input_docs.append(document.model_dump())
else:
input_docs.append(content)

# Merge options: reference config/version + caller-provided options
merged_options: dict[str, Any] | None = None
if resolved.get('version') is not None or resolved.get('config'):
merged_options = {}
if resolved.get('version') is not None:
merged_options['version'] = resolved['version']
if resolved.get('config'):
merged_options.update(resolved['config'])
if params.get('options'):
merged_options.update(params['options'])
else:
merged_options = params.get('options')

# Create embed request
request = EmbedRequest(
input=input_docs,
options=merged_options,
)

# Execute the embedder
response = await resolved['embedder_action'].arun(input=request)
return response.response.embeddings


def embedder_ref(
options: EmbedderReferenceOptions,
) -> EmbedderReference:
"""Helper method to configure an EmbedderReference."""

name = options['name']
if options.get('namespace') and not name.startswith(options['namespace'] + '/'):
name = f"{options['namespace']}/{name}"

return EmbedderReference(
name=name,
config_schema=options.get('config_schema'),
info=options.get('info'),
config=options.get('config'),
version=options.get('version'),
)


async def _resolve_embedder(
registry: 'Registry',
embedder_arg: EmbedderArgument,
) -> ResolvedEmbedder:
"""Resolve embedder from registry based on embedder argument"""

if isinstance(embedder_arg, str):
# Look up by name in registry
action = await registry.lookup_action(f"/embedder/{embedder_arg}")
if action is None:
raise ValueError(f"Unable to resolve embedder {embedder_arg}")
return ResolvedEmbedder(
embedder_action=action,
config=None,
version=None,
)
elif hasattr(embedder_arg, 'name'):
# It's an EmbedderAction
return ResolvedEmbedder(
embedder_action=embedder_arg,
config=None,
version=None,
)
elif isinstance(embedder_arg, dict) and 'name' in embedder_arg:
# It's an EmbedderReference - extract config and version
ref = embedder_arg
action = await registry.lookup_action(f"/embedder/{ref['name']}")
if action is None:
raise ValueError(f"Unable to resolve embedder {ref['name']}")
return ResolvedEmbedder(
embedder_action=action,
config=ref.get('config'),
version=ref.get('version'),
)
else:
raise ValueError(f"Failed to resolve embedder {embedder_arg}")
1 change: 1 addition & 0 deletions py/plugins/compat-oai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/dev-local-vectorstore/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/evaluators/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
Expand Down
1 change: 1 addition & 0 deletions py/plugins/firebase/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/flask/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/google-cloud/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/google-genai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/ollama/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/plugins/vertex-ai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
1 change: 1 addition & 0 deletions py/tests/smoke/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries",
]
Expand Down
Loading