Migrate from Genkit Python 0.12 to 0.13
Genkit Python 0.13 makes the API feel native to Python ahead of 1.0, and it has breaking changes. For the design rationale and full changelog, see the 0.13.0 release notes.
This guide covers every change you need to make, with old and new code for each. Most apps only touch a few sections. Skim the headings and apply the ones whose old code appears in your project.
Upgrade the packages
Section titled “Upgrade the packages”Upgrade genkit and every genkit-* package to 0.13 together. Keep them all on the same version.
uv add 'genkit==0.13.*' 'genkit-google-genai==0.13.*' # plus each genkit-* package you useIf you call Claude or Llama through Vertex AI Model Garden, install the extra for it:
uv add 'genkit-vertexai[anthropic]' # Claudeuv add 'genkit-vertexai[openai]' # Llama, Mistral and other OpenAI-compatible modelsUsing a coding agent
Section titled “Using a coding agent”You can point a coding agent at this page:
Migrate this project from Genkit Python 0.12 to 0.13 by following https://genkit.dev/docs/python/migrating-from-0-12/ (markdown: https://github.com/genkit-ai/genkit/blob/main/py/docs/migrating-from-0.12.md).Apply every section whose old code appears in this codebase, then run the type checker and tests and list anything you couldn't migrate.After migrating, run your type checker (pyright, mypy or ty). Most of the renames in this guide show up there as errors.
Model and embedder helpers
Section titled “Model and embedder helpers”Model helpers drop the _model suffix, and .embedding() is now .embedder().
Old:
GoogleAI.gemini_model('gemini-flash-latest')GoogleAI.gemini_image_model('gemini-2.5-flash-image')GoogleAI.gemini_tts_model('gemini-2.5-flash-preview-tts')GoogleAI.embedding('gemini-embedding-001')VertexAI.veo_model('veo-3.1-generate-001')Anthropic.claude_model('claude-sonnet-4-6')New:
GoogleAI.gemini('gemini-flash-latest')GoogleAI.gemini_image('gemini-2.5-flash-image')GoogleAI.gemini_tts('gemini-2.5-flash-preview-tts')GoogleAI.embedder('gemini-embedding-001')VertexAI.veo('veo-3.1-generate-001')Anthropic.claude('claude-sonnet-4-6')Imports
Section titled “Imports”genkit now exports only what apps use. Every public name lives in exactly one namespace, and each namespace’s __init__.py lists its exports in __all__. If an import breaks, use the layout and lookup below to find its new home instead of guessing.
The layout
genkit app code: Genkit, Message, Part, Document, FlowContext, ToolContext, GenkitError, Failure, ...genkit.model defining or inspecting models: Model, ModelRef, ModelRequest, ModelInfo, ModelSupports, ...genkit.embedder defining or inspecting embedders: Embedder, EmbedderRef, EmbedRequest, ...genkit.evaluator evaluator typesgenkit.middleware writing middlewaregenkit.formats custom output formatsgenkit.plugin_api writing plugins: Plugin, Action, GenkitConfig, provider_error, ...genkit.web serving flows: RequestData, ContextProvider, to_ssegenkit.telemetry tracing backendsgenkit.testing fake models for testsgenkit.exp experimental Genkit class (adds define_agent), plus current_sessiongenkit.exp.agent experimental agent APIsThe rule: if an application uses it, it’s in genkit. If you need it to define a model, embedder, evaluator, middleware or format, it’s in that resource’s namespace. Plugin-only primitives are in genkit.plugin_api. Plugin packages export everything from their top-level module, such as genkit_google_genai; their submodules are private.
Find where a name went
# list one namespace's exportspython -c "import genkit.model as m; print(m.__all__)"
# find which namespace exports a namepython - <<'EOF'import importlibname = 'ModelSupports'for mod in ['genkit', 'genkit.model', 'genkit.embedder', 'genkit.evaluator', 'genkit.middleware', 'genkit.formats', 'genkit.plugin_api', 'genkit.web', 'genkit.telemetry', 'genkit.testing', 'genkit.exp', 'genkit.exp.agent']: if name in importlib.import_module(mod).__all__: print(mod)EOFIf a name isn’t exported anywhere, it was renamed or removed. Check the renames below and “Removed names”.
Renamed while moving
Old:
from genkit import Supports, Stage, ModelUsage, Constrained, ExecutablePrompt, RuntimeErrorReasonNew:
from genkit import Prompt, ErrorReasonfrom genkit.model import ModelSupports, ModelStage, GenerationUsage, ConstrainedSupportInterrupts
Section titled “Interrupts”restart_tool and respond_to_interrupt are now methods on the interrupt part.
Old:
from genkit import restart_tool, respond_to_interrupt
restart = restart_tool(interrupt=interrupt, resumed_metadata={'approved': True})reply = respond_to_interrupt({'status': 'declined'}, interrupt=interrupt)New:
restart = interrupt.restart(resumed_metadata={'approved': True})reply = interrupt.respond({'status': 'declined'})Multipart tool responses
Section titled “Multipart tool responses”Old:
from genkit import response
return response({'ok': True}, parts=[png])New:
from genkit import MultipartToolResponse
return MultipartToolResponse(output={'ok': True}, content=[png])Evaluators
Section titled “Evaluators”Evaluator types are renamed, evaluation is a list, and define_evaluator takes the name and function positionally.
Old:
from genkit.evaluator import BaseDataPoint, Details, EvalFnResponse, EvalStatusEnum, Score
async def accuracy(dp: BaseDataPoint, _options=None) -> EvalFnResponse: return EvalFnResponse( test_case_id=dp.test_case_id or '', evaluation=Score(score=1.0, status=EvalStatusEnum.PASS, details=Details(reasoning=why)), )
ai.define_evaluator(name='byo/accuracy', display_name='Accuracy', definition='Matches the reference.', fn=accuracy)New:
from genkit.evaluator import BaseDataPoint, EvaluatorResponse, Score, ScoreDetails, ScoreStatus
async def accuracy(dp: BaseDataPoint, _options=None) -> EvaluatorResponse: return EvaluatorResponse( test_case_id=dp.test_case_id or '', evaluation=[Score(score=1.0, status=ScoreStatus.PASS, details=ScoreDetails(reasoning=why))], )
ai.define_evaluator('byo/accuracy', accuracy, display_name='Accuracy', definition='Matches the reference.')Removed names
Section titled “Removed names”Strings instead of enums. Where 0.12 had enum classes you had to import (GoogleAIGeminiVersion, KnownGpt, KnownClaude, …), 0.13 takes plain strings. Model helpers and config fields are typed as Literal unions of the known values, so your IDE and type checker autocomplete and check them with no import. A model name the plugin doesn’t list yet still works as a plain string.
Old:
from genkit_google_genai import GoogleAIGeminiVersion
GoogleAI.gemini_model(GoogleAIGeminiVersion.GEMINI_3_FLASH_PREVIEW)New:
GoogleAI.gemini('gemini-3-flash-preview') # autocompletes; any other string is still acceptedGeminiConfig(thinking_level='HIGH') # checked against 'MINIMAL' | 'LOW' | 'MEDIUM' | 'HIGH'If you’re a coding agent, replace each enum member with its string value, which you can read from the 0.12 source or the Literal in the 0.13 signature.
Every removed name, and what to use instead:
- Model enums (
KnownGemini*,KnownGpt,KnownClaude,*Version,GeminiEmbeddingModels,VertexEmbeddingModels): pass the model name as a string, such as'openai/gpt-4o'. - Name helpers (
bedrock_name,anthropic_name,ollama_name,openai_model,genkit_eval_name,package_name): use the full'plugin/model'string. Candidate: use the response’smessage.Document.data,Document.data_type,Document.from_data(): build aDocumentwith its constructor.ai.registry: use the lookup methods onGenkit.- Plugin submodules such as
genkit_openai.models: import from the plugin’s top-level package.
Keyword-only arguments
Section titled “Keyword-only arguments”Constructors, plugin constructors, and every option after an action’s input are keyword-only. If you see TypeError: takes N positional arguments, name the argument.
Old:
ai = Genkit([GoogleAI()], GoogleAI.gemini_model('gemini-flash-latest'))await my_flow.run(data, on_chunk, ctx)New:
ai = Genkit(plugins=[GoogleAI()], model=GoogleAI.gemini('gemini-flash-latest'))await my_flow.run(data, on_chunk=on_chunk, context=ctx)Plugin constructor arguments
Section titled “Plugin constructor arguments”Google Cloud plugins take project=, matching the google-cloud-* clients. Every plugin that talks to a server takes one base_url=.
Old:
ModelGarden(project_id='my-proj')Ollama(server_address='http://localhost:11434')New:
ModelGarden(project='my-proj')Ollama(base_url='http://localhost:11434')Config class names
Section titled “Config class names”Old:
from genkit_google_genai import GeminiConfigSchema, GeminiImageConfigSchema, GeminiTtsConfigSchema, GemmaConfigSchemaNew:
from genkit_google_genai import GeminiConfig, GeminiImageConfig, GeminiTtsConfig, GemmaConfigEmbeddingTaskType and VertexAIEvaluationMetricType are no longer exported. Pass the string value instead.
Gemini config
Section titled “Gemini config”Gemini config is flat. A nested key such as thinking_config now raises an error that names the flat field to use.
Old:
config={'thinking_config': {'thinking_level': 'HIGH', 'include_thoughts': True}}config={'google_search_retrieval': True}config={'speech_config': {'voice_config': {'prebuilt_voice_config': {'voice_name': 'Kore'}}}}New:
config=GeminiConfig(thinking_level='HIGH', include_thoughts=True)config=GeminiConfig(google_search=True)config=GeminiTtsConfig(voice='Kore')Anthropic config
Section titled “Anthropic config”AnthropicConfig is flat. ThinkingConfig, OutputConfig, TaskBudget, RequestMetadata and the *ToolChoice types are removed.
Old:
config={'thinking': {'enabled': True, 'budgetTokens': 1024}}New:
config=AnthropicConfig(thinking='enabled', thinking_budget=1024)
# effort, task_budget and user_id are flat fields tooconfig=AnthropicConfig(thinking='adaptive', effort='high', task_budget=20000, user_id='diner-42')Tool choice
Section titled “Tool choice”Tool choice moves out of provider config and onto generate.
Old:
await ai.generate(prompt=p, tools=[lookup], config={'tool_choice': {'type': 'any'}})New:
await ai.generate(prompt=p, tools=[lookup], tool_choice='required') # 'auto' | 'required' | 'none'Provider settings without a field
Section titled “Provider settings without a field”Settings a config class doesn’t declare go in extra.
Old:
config={'some_provider_option': 1}New:
config=BedrockConfig(extra={'some_provider_option': 1})OpenAIConfig no longer has a user field.
options= on embed and evaluate
Section titled “options= on embed and evaluate”Old:
await ai.embed(embedder=e, content=doc, options={'task_type': 'RETRIEVAL_DOCUMENT'})result = await ai.evaluate(evaluator=ev, dataset=rows, options={...})New:
await ai.embed(embedder=e, content=doc, config={'task_type': 'RETRIEVAL_DOCUMENT'})results = await ai.evaluate(evaluator=ev, dataset=rows, config={...}) # now returns a listUnknown config keys
Section titled “Unknown config keys”An unknown config key now raises before the model is called. In 0.12 it was silently dropped, so this can surface typos you didn’t know about.
await ai.generate(prompt=p, config={'temprature': 0.2})# raises GenkitError (INVALID_ARGUMENT) naming 'temprature'Config defaults merge key by key: the model ref’s default, then the prompt’s config, then the call’s. Pass None to clear an inherited value.
API keys
Section titled “API keys”An API key in model config now raises in every plugin. Move it to the plugin or to context.
Old:
await ai.generate(model=m, prompt=p, config={'api_key': user_key})New:
# one key for the whole appai = Genkit(plugins=[OpenAI(api_key=os.environ['OPENAI_API_KEY'])])
# or a different key per requestawait ai.generate(model=m, prompt=p, context={'secrets': {'api_key': user_key}})Vertex AI Model Garden authenticates with ADC and rejects a per-request key. ai.embed() takes no context, so embedders always use the plugin’s key.
Flow contexts
Section titled “Flow contexts”ActionRunContext is replaced by a typed context for each kind of action. The chunk type moves from @ai.flow(chunk_type=...) into the context’s type parameter.
Old:
from genkit import ActionRunContext
@ai.flow(chunk_type=Status)async def run(n: int, ctx: ActionRunContext) -> str: ctx.send_chunk(Status(step='search'))New:
from genkit import FlowContext
@ai.flow()async def run(n: int, ctx: FlowContext[Status]) -> str: ctx.send_chunk(Status(step='search'))Tool contexts
Section titled “Tool contexts”Tools can now stream typed chunks too.
Old:
from genkit import ToolRunContext
@ai.tool()async def prepare(dish: str, ctx: ToolRunContext) -> str: ...New:
from genkit import ToolContext
@ai.tool()async def prepare(dish: str, ctx: ToolContext[Progress]) -> str: ctx.send_chunk(Progress(stage='searing', percent=40))Model contexts
Section titled “Model contexts”If you implement a model yourself:
Old:
from genkit import ActionRunContext
async def generate(request: ModelRequest, ctx: ActionRunContext) -> ModelResponse: ...New:
from genkit.model import ModelContext
async def generate(request: ModelRequest, ctx: ModelContext) -> ModelResponse: ...Genkit finds the context by its type annotation, and an action takes exactly one input argument.
Cancellation
Section titled “Cancellation”ctx.abort_signal is removed. Cancellation is now standard asyncio.CancelledError, and it reaches the model call, any retry backoff, and every running tool.
Old:
@ai.tool()async def slow(x: dict, ctx: ToolRunContext) -> dict: while not done: if ctx.abort_signal.is_set(): raise GenkitError(status='ABORTED', message='aborted') await step()New:
@ai.tool()async def slow(x: dict) -> dict: while not done: await step() # cancellation raises asyncio.CancelledError hereTo cancel a call, cancel its task:
task = asyncio.create_task(ai.generate(prompt=p))task.cancel()To stop a stream early, use async with (or call aclose()). A bare break without it leaves the flow running.
stream = my_flow.stream(x)async with stream: async for chunk in stream: if enough(chunk): break # cancels the flow; stream.response raises CANCELLEDCancellation can now land at any await in your tool, so put cleanup for partial work in finally. If you catch asyncio.CancelledError yourself, re-raise it.
@ai.tool()async def book_trip(trip: Trip) -> Booking: hold = await reserve_seats(trip) try: return await confirm(hold) finally: if not hold.confirmed: await release(hold)Returned errors
Section titled “Returned errors”response.error (and .error on operations and agent results) is now typed Failure, a public type you can import. It has the same fields as before: status, message, details and reason. The reason enum is renamed from RuntimeErrorReason to ErrorReason.
Old:
from genkit import RuntimeErrorReason
if res.error and res.error.reason == RuntimeErrorReason.INVALID_OUTPUT: ...New:
from genkit import ErrorReason, Failure
if res.error and res.error.reason == ErrorReason.INVALID_OUTPUT: ...
def report(err: Failure) -> None: ...Raised errors
Section titled “Raised errors”GenkitError keeps fewer attributes, and its constructor is keyword-only.
Old:
from genkit import GenkitError
try: ...except GenkitError as e: log(e.original_message, e.http_code, e.trace_id, e.cause)New:
from genkit import GenkitError
try: ...except GenkitError as e: log(e.message, e.status, e.__cause__)Plugin exception types
Section titled “Plugin exception types”Plugin-specific exceptions such as OllamaConnectionError are gone. Check the status on res.error instead.
Old:
try: ...except GenkitError as e: if isinstance(e.cause, OllamaConnectionError): ...New:
res = await ai.generate(model=m, prompt=p)if res.error and res.error.status == 'UNAVAILABLE': ...Interrupts and except Exception
Section titled “Interrupts and except Exception”Interrupt now subclasses BaseException, so except Exception: no longer catches it. If your code catches interrupts on purpose, catch Interrupt by name.
Old:
try: require_approval(order)except Exception as e: # caught Interrupt too ...New:
try: require_approval(order)except Interrupt: raise # only if you caught it on purposeexcept Exception as e: ...Context providers
Section titled “Context providers”RequestData moves to genkit.web and exposes method, headers and input directly, the same way on Flask, FastAPI and Django.
Old:
from genkit.plugin_api import RequestData
async def auth(req: RequestData[HttpRequest]) -> dict: return {'user': req.request.META.get('HTTP_AUTHORIZATION')}New:
from genkit.web import RequestData
async def auth(req: RequestData) -> dict: return {'user': req.headers.get('authorization')}Errors from served flows
Section titled “Errors from served flows”This only affects flows served over HTTP: genkit_flask_handler, the FastAPI and Django handlers, or genkit.web in your own route. Calling a flow or model in process (await my_flow.run(...), await ai.generate(...)) is unchanged, and you still get the full error.
One error, two views
Section titled “One error, two views”- In process, you get everything: a
GenkitErrorwithstatus,message,detailsand__cause__, or your own exception, unwrapped. Yourtry/except, middleware, tests and the Dev UI (with stack trace and trace link) all see it. - Over HTTP, the client gets a body built from that error by these rules:
| What was raised | HTTP status | What the client gets |
|---|---|---|
PublicError(status, message, details) | Matches the status, such as 404 for NOT_FOUND | Its status, message and details |
| Invalid input to the served flow | 400 | Invalid input for flow 'save': qty: Input should be a valid integer: field names and Pydantic’s built-in messages, never the values sent |
Anything else: other GenkitErrors, provider errors, your own exceptions | 500 | {"status": "INTERNAL", "message": "Internal Error"} |
Streaming responses use the same body inside a data: {"error": {...}} SSE frame. Every failure is still logged on the server, tagged with the flow name: 5xx errors with the full traceback, 4xx errors as warnings.
In 0.12, the client got the message and details of any GenkitError, and the text and stack trace of any other exception.
Errors your clients rely on
Section titled “Errors your clients rely on”If a client shows an error’s message or branches on its status, raise PublicError for it.
Old:
@ai.flow()async def get_order(order_id: str) -> Order: order = await db.find(order_id) if order is None: raise GenkitError(status='NOT_FOUND', message=f'Order {order_id} not found') # 0.13: client gets a 500 return orderNew:
from genkit import PublicError
@ai.flow()async def get_order(order_id: str) -> Order: order = await db.find(order_id) if order is None: raise PublicError('NOT_FOUND', f'Order {order_id} not found') # client gets 404 and this message return orderEverything in a PublicError’s message and details goes to the client as written, so keep secrets, IDs from other tenants, and internal details out of it.
Auth in context providers
Section titled “Auth in context providers”A PublicError raised in a context provider is handled the same way, which is how you return a 401 or 403.
from genkit import PublicErrorfrom genkit.web import RequestData
async def auth(req: RequestData) -> dict: token = req.headers.get('authorization') if not token: raise PublicError('UNAUTHENTICATED', 'Missing bearer token') # 401 return {'user': await verify(token)}Validation messages
Section titled “Validation messages”For invalid input, the client sees Pydantic’s built-in messages. A custom validator’s own message shows as Invalid value, because it might contain server data. To show the caller your validator’s message, raise PublicError from the validator.
Custom routes
Section titled “Custom routes”If you serve a flow from your own route, use the same rules with error_body and error_status:
from genkit.web import error_body, error_status
try: result = await my_flow.run(data)except Exception as e: return JSONResponse(error_body(e), status_code=error_status(e))Google Cloud telemetry
Section titled “Google Cloud telemetry”Old:
add_gcp_telemetry(project_id='my-project', force_dev_export=True)New:
enable_google_cloud_telemetry(project='my-project', force_dev_export=True)The FIREBASE_PROJECT_ID fallback is gone, so set project= or GOOGLE_CLOUD_PROJECT.
Custom trace exporters
Section titled “Custom trace exporters”Old:
from genkit.plugin_api import tracer, add_custom_exporter, AdjustingTraceExporterNew:
from genkit.telemetry import Instrumentation, configure_instrumentationImagen
Section titled “Imagen”The Imagen catalog is retired. Use a Gemini image model.
Old:
GoogleAI.imagen_model('imagen-4.0-generate-001')New:
GoogleAI.gemini_image('gemini-2.5-flash-image')Vertex AI Model Garden
Section titled “Vertex AI Model Garden”Old:
from genkit_vertexai.model_garden import ModelGardenPlugin
ai = Genkit(plugins=[ModelGardenPlugin()])New:
from genkit_vertexai import ModelGarden
ai = Genkit(plugins=[ModelGarden()])Install the [anthropic] or [openai] extra for the models you call (see the top of this guide).
OpenAI
Section titled “OpenAI”Old:
from genkit_openai import openai_model
model = openai_model('gpt-4o')New:
model = 'openai/gpt-4o'Ollama
Section titled “Ollama”Ollama() reads your pulled models and their capabilities from the server, so you no longer list them.
Old:
from genkit_ollama import EmbeddingDefinition, ModelDefinition, Ollama
Ollama( models=[ModelDefinition(name='llama3.2')], embedders=[EmbeddingDefinition(name='nomic-embed-text')], server_address=os.getenv('OLLAMA_HOST'),)New:
from genkit_ollama import Ollama
Ollama(base_url=os.getenv('OLLAMA_HOST'))Ollama no longer reads media from local file paths.
Amazon Bedrock
Section titled “Amazon Bedrock”Old:
from genkit_amazon_bedrock import Bedrock, BedrockImageConfig, ModelDefinition
Bedrock(models=[ModelDefinition(name='us.amazon.nova-lite-v1:0')])New:
from genkit_amazon_bedrock import Bedrock, StabilityImageConfig
Bedrock(models=['us.amazon.nova-lite-v1:0'])genkit-flask no longer installs genkit-google-genai. Add it yourself if you use it.
uv add genkit-google-genaiMedia size limit
Section titled “Media size limit”Google GenAI and Ollama now fail on media URLs larger than 20 MB instead of downloading them.
Templates in generate
Section titled “Templates in generate”ai.generate(prompt=...) and system= are sent exactly as written. Handlebars templates only render in define_prompt and .prompt files. This change raises no error, so search your code for {{ inside generate calls.
Old:
await ai.generate(prompt='Say hi to {{name}}')New:
greet = ai.define_prompt(name='greet', prompt='Say hi to {{name}}')await greet({'name': 'Ana'})Prompt call options
Section titled “Prompt call options”Calling a prompt takes the same keyword names as generate. The output schema is fixed by the prompt and can’t be overridden per call. Call config merges over the prompt’s config instead of replacing it.
recipe = ai.prompt('recipe')await recipe({'dish': 'pie'}, config={'temperature': 0.2})Prompt input is now validated, and defaults from .prompt files are filled in.
Truncated structured output
Section titled “Truncated structured output”If a JSON reply is cut off, response.output is now None and response.error reports INVALID_OUTPUT. Before, you could get a partial object. Check for None where you read output.
res = await ai.generate(prompt=p, output_schema=City)if res.output is None: ... # the reply was incomplete; see res.errorFlow input and output validation
Section titled “Flow input and output validation”Flow input and output are now validated against their type annotations. A flow that returns the wrong shape now raises. For served flows, {"data": null} means “use the default”.
Exceptions from actions
Section titled “Exceptions from actions”An action now raises the exception its body raised, unwrapped. If you caught GenkitError around flow.run() to handle your own exceptions, catch those types directly. Retry and Fallback only retry failures a plugin classified, such as UNAVAILABLE.
For plugin and middleware authors
Section titled “For plugin and middleware authors”Skip these if you only use Genkit in an app.
Middleware
Section titled “Middleware”Old:
from genkit.middleware import BaseMiddleware, GenerateMiddlewareContext
@ai.middleware(name='pii_redact')class PiiRedact(BaseMiddleware[Cfg]): async def model(self, params, ctx: GenerateMiddlewareContext, next_fn): ...New:
from genkit.middleware import Middleware, MiddlewareContext
@ai.middlewareclass PiiRedact(Middleware[Cfg]): name = 'pii_redact' async def model(self, params, ctx: MiddlewareContext, next_fn): ...tools() returns Tool handles. GenerateMiddleware, MiddlewareRef, MiddlewarePlugin and new_middleware are removed.
Defining models and embedders
Section titled “Defining models and embedders”Old:
from genkit.plugin_api import model, model_ref, background_model, embedder, embedder_ref, ModelConfigNew:
from genkit.model import Model, ModelRef, BackgroundModel, ModelConfigfrom genkit.embedder import Embedder, EmbedderReffrom genkit.plugin_api import GenkitConfigResolving models
Section titled “Resolving models”resolve() now receives the model ID without the plugin prefix.
Old:
async def resolve(self, kind, name): model_id = name.removeprefix('myplugin/')New:
async def resolve(self, kind, name): model_id = nameProvider errors, clients and logging
Section titled “Provider errors, clients and logging”Old:
from genkit.plugin_api import ( wrap_http_error, from_http_code, parse_retry_after_ms, get_cached_client, GENKIT_CLIENT_HEADER, GENKIT_VERSION, tracer, add_custom_exporter,)New:
from genkit import get_loggerfrom genkit.plugin_api import provider_error, loop_local_client, GENKIT_USER_AGENTfrom genkit.telemetry import Instrumentation, configure_instrumentationAlso removed: Plugin.model(), Plugin.embedder() and Supports.context. generate_operation receives only what a background start uses. Custom formatters import Formatter, FormatterConfig and FormatHandler from genkit.formats.
Experimental APIs
Section titled “Experimental APIs”APIs under an exp module can change in any release.
Old:
from genkit.exp import Agent, Session, InMemorySessionStore, AgentErrorfrom genkit_fastapi import serve_agentfrom genkit_middleware import ArtifactsNew:
from genkit.exp.agent import Agent, Session, InMemorySessionStorefrom genkit_fastapi.exp import serve_agentfrom genkit_middleware.exp import ArtifactsAgentError is removed. A failed turn returns res.error, and a call that produces no turn raises GenkitError. Tools in a detached turn are cancelled with await task.abort() instead of checking abort_signal.
Check your upgrade
Section titled “Check your upgrade”- Run your type checker. Most renames in this guide surface as errors.
- Run your tests.
- Search for
{{insideai.generate(calls, and for reads ofresponse.outputthat assume it’s neverNone. Neither change raises an error.