Creating Genkit plugins
Genkit’s capabilities are designed to be extended by plugins. Genkit plugins are configurable modules that can provide models, retrievers, trace stores, and more. You’ve already seen plugins in action just by using Genkit:
import (
"github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googlegenai" "github.com/firebase/genkit/go/plugins/server")g := genkit.Init(ctx, genkit.WithPlugins( &googlegenai.GoogleAI{APIKey: ...}, &googlegenai.VertexAI{ProjectID: "my-project", Location: "us-central1"}, ),)The Vertex AI plugin takes configuration (such as the user’s Google Cloud project ID) and registers a variety of new models, embedders, and more with the Genkit registry. The registry serves as a lookup service for named actions at runtime, and powers Genkit’s local UI for running and inspecting models, prompts, and more.
Creating a plugin
Section titled “Creating a plugin”In Go, a Genkit plugin is a type that implements the api.Plugin interface. A
single module can contain several plugins.
Provider ID
Section titled “Provider ID”Every plugin must have a unique identifier string that distinguishes it from other plugins. Genkit uses this identifier as a namespace for every resource your plugin defines, to prevent naming conflicts with other plugins.
For example, if your plugin has an ID yourplugin and provides a model called
text-generator, the full model identifier will be yourplugin/text-generator.
This provider ID needs to be exported and you should define it once for your plugin and use it consistently when required by a Genkit function.
package yourplugin
const providerID = "yourplugin"Standard exports
Section titled “Standard exports”Every plugin exports a struct type that encapsulates all of the configuration
options accepted by the plugin, and implements Name() and Init() on it.
For any plugin options that are secret values, such as API keys, you should offer both a config option and a default environment variable to configure it. This lets your plugin take advantage of the secret-management features offered by many hosting providers, such as Cloud Secret Manager, which you can use with Cloud Run.
package myplugin
import ( "context" "os"
"github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/core/api")
const providerID = "myprovider"
// MyPlugin holds every option the plugin accepts.type MyPlugin struct { // APIKey is the credential. If empty, MYPROVIDER_API_KEY is consulted. APIKey string
// Models overrides what the plugin knows about a model, keyed by model ID. // Fields left at their zero value keep what the plugin resolved. Models map[string]ai.ModelOptions
client *apiClient}
// Name returns the provider ID.func (p *MyPlugin) Name() string { return providerID }
// Init creates shared resources and returns the actions to register.func (p *MyPlugin) Init(ctx context.Context) []api.Action { apiKey := p.APIKey if apiKey == "" { apiKey = os.Getenv("MYPROVIDER_API_KEY") } if apiKey == "" { panic("myprovider plugin initialization failed: apiKey is required") } p.client = newAPIClient(apiKey)
return []api.Action{ p.newModel("my-model"), }}
var _ api.Plugin = (*MyPlugin)(nil)Init is called automatically during genkit.Init() when the user passes the
plugin into the WithPlugins() option. In it:
- Confirm that any required configuration values are specified and assign default values to any unspecified optional settings.
- Verify that the given configuration options are valid together.
- Create any shared resources required by the rest of your plugin. For example, create clients for any services your plugin accesses.
- Construct your actions and return them. The framework registers whatever
Initreturns, so a plugin never reaches into the registry itself.
To the extent possible, the resources provided by your plugin shouldn’t assume that any other plugins have been installed before this one.
Resolving actions on demand
Section titled “Resolving actions on demand”A provider that serves more models than are worth registering up front can also
implement api.DynamicPlugin, which adds two methods. ListActions advertises
everything the plugin could serve, so the Developer UI can list it, and
ResolveAction builds one on demand when a name is looked up and nothing is
registered under it.
// ListActions advertises every model the plugin could serve.func (p *MyPlugin) ListActions(ctx context.Context) []api.ActionDesc { var descs []api.ActionDesc for _, id := range p.client.listModels(ctx) { descs = append(descs, p.newModel(id).Desc()) } return descs}
// ResolveAction builds a model on demand. id arrives bare: the registry has// already matched the provider prefix to this plugin.func (p *MyPlugin) ResolveAction(atype api.ActionType, id string) api.Action { if atype != api.ActionTypeModel { return nil } return p.newModel(id)}Because an unknown ID still resolves, a curated catalog is a source of capability metadata rather than an allowlist.
How the constructors are shaped
Section titled “How the constructors are shaped”Genkit has two audiences and two matching styles, and it is worth knowing which one you are writing against.
The plugin-facing surface, core.New*Of and ai.New*Action, reads identity,
then descriptor, then implementation. Identity is positional because it is
always required: core.New*Of takes the action type and then the name, and
ai.New*Action takes the name. One options struct follows and carries the
descriptor. For core.New*Of it is core.ActionOptions, holding the
description, the metadata, and each schema; for ai.New*Action it is a
primitive-specific struct such as ai.ModelOptions or ai.RetrieverOptions,
holding the label, the capabilities, and the config schema. The implementation
function is positional, and optional lifecycle hooks are fields on the options
struct. Shaped this way, a new descriptor slot or a new hook is never a
signature break.
The app-facing surface is genkit.Define* and the ai.With* request options.
The request options, and the tool and prompt definers, stay variadic functional
options, because an application names only the few things it cares about. Each
genkit.Define*Action wrapper takes the same options struct as the plugin-side
constructor it wraps, because it is that constructor plus registration in one
call.
Building plugin features
Section titled “Building plugin features”A single plugin can activate many new things within Genkit. For example, the Vertex AI plugin activates several new models as well as an embedder. Here’s how to build some common plugin types.
Model plugins
Section titled “Model plugins”Genkit model plugins add one or more generative AI models to the Genkit registry. A model represents any generative model that is capable of receiving a prompt as input and generating text, media, or data as output.
Model definitions
Section titled “Model definitions”A model definition consists of three components:
- Metadata declaring the model’s capabilities.
- A configuration type with any specific parameters supported by the model.
- A generation function that accepts an
ai.ModelRequestand returns anai.ModelResponse, presumably using an AI model to generate the latter.
ai.NewModelAction binds all three. Its Config type parameter is inferred
from the generation function, so the signature you write is the contract:
// MyModelConfig defines the configuration options for your model. Its JSON// schema is inferred from this type and enforced on every request.type MyModelConfig struct { Temperature *float64 `json:"temperature,omitempty" jsonschema:"minimum=0,maximum=2"` MaxTokens int `json:"maxTokens,omitempty" jsonschema:"minimum=1"`}
func (p *MyPlugin) newModel(id string) *ai.ModelAction { opts := ai.ModelOptions{ Label: "My Model", // User-friendly label Supports: &ai.ModelSupports{ Multiturn: true, // Does the model support multi-turn chats? SystemRole: true, // Does the model support system messages? Media: false, // Can the model accept media input? Tools: false, // Does the model support function calling (tools)? }, Versions: []string{"my-model-001"}, // List supported versions/aliases } // An application entry corrects or extends what the plugin resolved. opts = opts.Overlay(p.Models[id])
return ai.NewModelAction(providerID+"/"+id, &opts, func(ctx context.Context, req *ai.ModelRequest, cfg *MyModelConfig, cb ai.ModelStreamCallback) (*ai.ModelResponse, error) { // cfg arrives deserialized and validated. It is nil when the caller // sent no config, because Config here is a pointer type. if cfg == nil { cfg = &MyModelConfig{} }
// Use your custom logic to convert Genkit's ai.ModelRequest into a // form usable by the model's native API. apiReq, err := apiRequestFromGenkitRequest(req, cfg) if err != nil { return nil, err }
// Send the request to the model API, using your own code or the // model API's client library. apiResp, err := p.client.generate(ctx, apiReq) if err != nil { return nil, wrapAPIError(err) }
// Convert the model's response to Genkit's ai.ModelResponse. return genkitResponseFromAPIResponse(req, apiResp) })}ai.NewModelAction returns an unregistered *ai.ModelAction that satisfies
both ai.Model and api.Action, so Init can return it in an []api.Action
without a type assertion. ai.ModelOptions.Overlay returns the receiver with
every field set in the override replacing it, which is how an application
corrects one capability without restating the label and the versions.
Defining your model’s config schema
Section titled “Defining your model’s config schema”To specify the generation options a model supports, define and export a
configuration type and use it as the Config type parameter. jsonschema
struct tags become the constraints, and jsonschema_description tags become the
field documentation the Developer UI renders in its config sidebar. Genkit has
an ai.GenerationCommonConfig type that contains options frequently supported
by generative AI model services, which you can embed if your provider matches
it.
The framework infers the config JSON schema from Config and validates every
request against it before your generation function runs, so the function never
needs a type check. Config sent as the exact type, as a pointer to it, or as a
map[string]any from a JSON caller such as the Developer UI is converted to the
typed value; anything else fails with INVALID_ARGUMENT. When Config is a
pointer type, a request that carries no config hands your function a nil
pointer, so nil-check it. When Config is a value type, you get the zero value.
Declaring model capabilities
Section titled “Declaring model capabilities”Every model definition must contain, as part of its metadata, an
ai.ModelSupports value that declares which features the model supports. Genkit
uses this information to determine certain behaviors, such as verifying whether
certain inputs are valid for the model. For example, if the model doesn’t
support multi-turn interactions, then it’s an error to pass it a message
history.
Note that these declarations refer to the capabilities of the model as provided by your plugin, and do not necessarily map one-to-one to the capabilities of the underlying model and model API. For example, even if the model API doesn’t provide a specific way to define system messages, your plugin might still declare support for the system role, and implement it as special logic that inserts system messages into the user prompt.
The two ways to be wrong are not symmetric. Declaring too narrowly is refused by Genkit before the request leaves the process, blocking a model that would have worked, while an over-wide claim reaches the provider and comes back with the real reason.
Transforming requests and responses
Section titled “Transforming requests and responses”The generation function carries out the primary work of a Genkit model plugin:
transforming the ai.ModelRequest from Genkit’s common format into a format
that is supported by your model’s API, and then transforming the response from
your model into the ai.ModelResponse format used by Genkit.
Sometimes, this may require massaging or manipulating data to work around model
limitations. For example, if your model does not natively support a system
message, you may need to transform a prompt’s system message into a user-model
message pair.
Classifying provider errors
Section titled “Classifying provider errors”Retry and fallback middleware branch on the status an error carries, so classify your SDK’s errors at the boundary rather than returning them raw.
// wrapAPIError classifies an SDK error so status-aware middleware can tell a// rate limit from a request the provider rejected.func wrapAPIError(err error) error { var apiErr *apiError if !errors.As(err, &apiErr) { return err } return status.Errorf(status.Base(status.FromHTTPCode(apiErr.StatusCode)), "%w", err)}status.FromHTTPCode maps an HTTP status code to a canonical status name, and
status.Base turns that name into the sentinel status.Errorf classifies with.
Recording the cause with %w keeps the original error reachable through
errors.Is and errors.As.
Leave anything that is not an API error unclassified. The retry middleware
retries an unclassified error unconditionally, which is the right answer for a
dial timeout, and the fallback middleware leaves it alone, because failing over
to a different billed model is worth requiring an explicit classification for. A
classified error is retried only when its status is on the middleware’s list, so
classifying a 401 as UNAUTHENTICATED is what stops it being reissued until the
attempts run out. The retry and fallback sample shows both from the application side.
Wrap with %w, never %v. Flattening the error destroys the classification,
which silently turns a non-retryable INVALID_ARGUMENT into an unclassified
error that is always retried.
Exports
Section titled “Exports”In addition to the resources that all plugins must export, a model plugin should also export the following:
-
A generation config type, as discussed earlier.
-
A
ModelReffunction, which creates a model reference paired with its config so the two travel together and the config is typed at the call site:
func ModelRef(id string, config *MyModelConfig) ai.ModelRef { return ai.NewModelRef(api.NewName(providerID, id), config)}Callers then write
ai.WithModel(myplugin.ModelRef("my-model", &myplugin.MyModelConfig{...})).
-
A
Models map[string]ai.ModelOptionsfield on the plugin struct, so an application can correct a capability your catalog got wrong, or describe a model released after your plugin was. Route every path that describes a model through one method that applies the overlay, so an entry is authoritative whetherInit,ListActions, orResolveActionreaches the model first.Prefer this to an exported
DefineModelfunction. Correction is data rather than API surface, and a model thatInithas already registered cannot be re-registered.
Retrievers, embedders, and evaluators
Section titled “Retrievers, embedders, and evaluators”The other primitives follow the same shape as models: ai.NewRetrieverAction,
ai.NewEmbedderAction, ai.NewEvaluatorAction, ai.NewBatchEvaluatorAction,
and ai.NewBackgroundModelAction, each with its own options struct and its own
typed Config type parameter, validated the same way. ai.NewBatchEvaluatorAction
shares ai.EvaluatorOptions with ai.NewEvaluatorAction; there is no separate
batch options type.
Applications reach the same constructors through the registering
genkit.Define*Action wrappers:
type MyRetrieverConfig struct { K int `json:"k,omitempty" jsonschema:"minimum=1"`}
type MyEmbedderConfig struct { Dimensions int `json:"dimensions,omitempty" jsonschema:"minimum=1"`}
genkit.DefineRetrieverAction(g, "myprovider/docs", &ai.RetrieverOptions{Label: "My Docs"}, func(ctx context.Context, req *ai.RetrieverRequest, cfg *MyRetrieverConfig) (*ai.RetrieverResponse, error) { k := 10 if cfg != nil && cfg.K > 0 { k = cfg.K } docs, err := search(ctx, req.Query, k) if err != nil { return nil, err } return &ai.RetrieverResponse{Documents: docs}, nil })
genkit.DefineEmbedderAction(g, "myprovider/embed-001", &ai.EmbedderOptions{Label: "My Embedder", Dimensions: 768}, func(ctx context.Context, req *ai.EmbedRequest, cfg *MyEmbedderConfig) (*ai.EmbedResponse, error) { vectors, err := embed(ctx, req.Input) if err != nil { return nil, err } return &ai.EmbedResponse{Embeddings: vectors}, nil })Registering JSON schemas
Section titled “Registering JSON schemas”Schema registration lives in package genkit only. Register a schema when a
.prompt file, ai.WithOutputSchemaName, or ai.WithInputSchemaName refers to
one by name. The call must run before the prompt is rendered.
// Register several Go types at once, each under its type's name.genkit.DefineSchemasFor(g, JokeRequest{}, Joke{})
// The single-type form.genkit.DefineSchemaFor[Recipe](g)
// A raw JSON schema needs an explicit name.genkit.DefineSchema(g, "Rating", map[string]any{ "type": "integer", "minimum": 1, "maximum": 5,})Each name may be registered once per Genkit instance. A second registration of
the same name panics, so call these helpers once at startup. DefineSchemasFor
and DefineSchemaFor take Go types, and panic on a map, a nil value, or an
unnamed type, pointing you at DefineSchema for a raw schema.
Package core keeps the schema utilities that do not register anything.
core.InferSchemaMap turns a Go type into a schema map, which is how a plugin
fills an options struct’s ConfigSchema:
opts := ai.ModelOptions{ConfigSchema: core.InferSchemaMap(MyModelConfig{})}Lower-level actions
Section titled “Lower-level actions”For anything that is not one of the AI primitives, package core has the
generic constructors: core.NewActionOf, core.NewStreamingActionOf,
core.NewBidiActionOf, and core.NewBackgroundActionOf. Each takes the action
type first, then the name, then an options struct, then the implementation.
action := core.NewActionOf(api.ActionTypeCustom, "myAction", &core.ActionOptions{ Description: "Processes a string", Metadata: map[string]any{"team": "search"}, // InputSchema, OutputSchema and StreamSchema left nil are inferred // from the type parameters. }, func(ctx context.Context, input string) (string, error) { return "processed: " + input, nil })Return the action from your plugin’s Init, as in the Init example above.
The framework registers whatever Init returns.
A nil options value is legal and infers every schema.
core.NewBackgroundActionOf takes a core.BackgroundActionOptions[In, Out],
whose Check field is required and whose Cancel field is optional: omitting
Cancel means the action does not support cancellation.
Building on the OpenAI-compatible core
Section titled “Building on the OpenAI-compatible core”If your provider speaks the OpenAI chat-completions API, build on
plugins/compat_oai rather than writing a client from scratch. Point the base
plugin at the endpoint and every model resolves by name, taking the OpenAI SDK’s
own request type as its config:
g := genkit.Init(ctx, genkit.WithPlugins(&compat_oai.OpenAICompatible{ Provider: "myprovider", APIKey: apiKey, BaseURL: "https://api.example.com/v1",}))
model := ai.NewModelRef("myprovider/some-model", &openai.ChatCompletionNewParams{ Temperature: openai.Float(0.7),})The custom provider sample is the worked example.
To give your provider a config of its own, and a home for the extensions the SDK
request type has no room for, declare a struct that embeds
compat_oai.RequestConfig, implement ApplyToChatCompletion, and build models
with compat_oai.NewChatModel[Config]:
type ChatConfig struct { compat_oai.RequestConfig
Temperature *float64 `json:"temperature,omitempty" jsonschema:"minimum=0,maximum=2"` EnableSearch *bool `json:"enableSearch,omitempty"`}
func (c ChatConfig) ApplyToChatCompletion(params *openai.ChatCompletionNewParams) { c.ApplyVersion(params) if c.Temperature != nil { params.Temperature = openai.Float(*c.Temperature) } if c.EnableSearch != nil { compat_oai.AddExtraFields(params, map[string]any{"enable_search": *c.EnableSearch}) }}
func (p *MyPlugin) newChatModel(id string, opts ai.ModelOptions) *ai.ModelAction { return compat_oai.NewChatModel[ChatConfig](&p.openAICompatible, id, opts)}Embedding RequestConfig gives every config three shared settings: a code-only
APIKey for a per-request credential, Version to pin an exact model version,
and Extra for undeclared body fields sent verbatim under the provider’s wire
names. compat_oai.ListChatActions and compat_oai.ResolveChatAction are the
matching DynamicPlugin methods, and compat_oai.ModelOptionsFor applies a
caller’s Models entry over what your catalog resolved.
On this path you do not classify SDK errors yourself: the shared request path
runs compat_oai.WrapAPIError, which wraps an error the OpenAI SDK returned for
an HTTP response in a status.Error carrying the status the server reported,
and passes everything else through untouched.
Telemetry plugins
Section titled “Telemetry plugins”The Genkit libraries are instrumented with OpenTelemetry to support collecting traces, metrics, and logs. Genkit users can export this telemetry data to monitoring and visualization tools by installing a plugin that configures the OpenTelemetry Go SDK to export to a particular OpenTelemetry-capable system.
Genkit includes a plugin that configures OpenTelemetry to export data to Google Cloud Monitoring and Cloud Logging. To support other monitoring systems, you can extend Genkit by writing a telemetry plugin. The telemetry sample exercises one end to end.
Exporters and loggers
Section titled “Exporters and loggers”The primary job of a telemetry plugin is to configure OpenTelemetry to export data to a particular service. To do so, you need the following:
- An implementation of OpenTelemetry’s
SpanExporterinterface that exports data to the service of your choice. - An implementation of OpenTelemetry’s
metric.Exporterinterface that exports data to the service of your choice. - Either a
slog.Loggeror an implementation of theslog.Handlerinterface, that exports logs to the service of your choice.
Depending on the service you’re interested in exporting to, this might be a relatively minor effort or a large one.
Because OpenTelemetry is an industry standard, many monitoring services already
have libraries that implement these interfaces. For example, the googlecloud
plugin for Genkit makes use of the
opentelemetry-operations-go
library, maintained by the Google Cloud team.
Similarly, many monitoring services provide libraries that implement the
standard slog interfaces.
On the other hand, if no such libraries are available for your service, implementing the necessary interfaces can be a substantial project.
Check the OpenTelemetry registry or the monitoring service’s docs to see if integrations are already available.
If you need to build these integrations yourself, take a look at the source of
the official OpenTelemetry exporters
and the page A Guide to Writing slog Handlers.
Building the plugin
Section titled “Building the plugin”Dependencies
Section titled “Dependencies”Every telemetry plugin needs to import the Genkit core library and several OpenTelemetry libraries:
// Import the Genkit tracing library."github.com/firebase/genkit/go/core/tracing"
// Import the OpenTelemetry libraries."go.opentelemetry.io/otel""go.opentelemetry.io/otel/sdk/metric"sdktrace "go.opentelemetry.io/otel/sdk/trace"If you are building a plugin around an existing OpenTelemetry or slog
integration, you will also need to import them.
Config
Section titled “Config”A telemetry plugin should, at a minimum, support the following configuration options:
type Config struct { // Export even in the dev environment. ForceExport bool
// The interval for exporting metric data. // The default is 60 seconds. MetricInterval time.Duration
// The minimum level at which logs will be written. // Defaults to [slog.LevelInfo]. LogLevel slog.Leveler}Most plugins will also include configuration settings for the service it’s exporting to (API key, project name, and so on).
Init()
Section titled “Init()”The Init() function of a telemetry plugin should do all of the following:
- Return early if Genkit is running in a development environment (such as when
running with with
genkit start) and theConfig.ForceExportoption isn’t set:
shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev"if !shouldExport { return nil}- Initialize your trace span exporter and register it with Genkit:
spanProcessor := sdktrace.NewBatchSpanProcessor(YourCustomSpanExporter{})tracing.TracerProvider().RegisterSpanProcessor(spanProcessor)- Initialize your metric exporter and register it with the OpenTelemetry library:
r := metric.NewPeriodicReader( YourCustomMetricExporter{}, metric.WithInterval(cfg.MetricInterval),)mp := metric.NewMeterProvider(metric.WithReader(r))otel.SetMeterProvider(mp)Use the user-configured collection interval (Config.MetricInterval) when
initializing the PeriodicReader.
- Register your
sloghandler as the default logger:
logger := slog.New(YourCustomHandler{ Options: &slog.HandlerOptions{Level: cfg.LogLevel},})slog.SetDefault(logger)You should configure your handler to honor the user-specified minimum log
level (Config.LogLevel).
PII redaction
Section titled “PII redaction”Because most generative AI flows begin with user input of some kind, it’s a likely possibility that some flow traces contain personally-identifiable information (PII). To protect your users’ information, you should redact PII from traces before you export them.
If you are building your own span exporter, you can build this functionality into it.
If you’re building your plugin around an existing OpenTelemetry integration, you
can wrap the provided span exporter with a custom exporter that carries out this
task. For example, the googlecloud plugin removes the genkit:input and
genkit:output attributes from every span before exporting them using a wrapper
similar to the following:
type redactingSpanExporter struct { trace.SpanExporter}
func (e *redactingSpanExporter) ExportSpans(ctx context.Context, spanData []trace.ReadOnlySpan) error { var redacted []trace.ReadOnlySpan for _, s := range spanData { redacted = append(redacted, redactedSpan{s}) } return e.SpanExporter.ExportSpans(ctx, redacted)}
func (e *redactingSpanExporter) Shutdown(ctx context.Context) error { return e.SpanExporter.Shutdown(ctx)}
type redactedSpan struct { trace.ReadOnlySpan}
func (s redactedSpan) Attributes() []attribute.KeyValue { // Omit input and output, which may contain PII. var ts []attribute.KeyValue for _, a := range s.ReadOnlySpan.Attributes() { if a.Key == "genkit:input" || a.Key == "genkit:output" { continue } ts = append(ts, a) } return ts}Troubleshooting
Section titled “Troubleshooting”If you’re having trouble getting data to show up where you expect, OpenTelemetry provides a useful diagnostic tool that helps locate the source of the problem.
Finding the constructor to call
Section titled “Finding the constructor to call”Every Define* helper that took an api.Registry as its first argument is
gone. A registry is unobtainable outside the framework, so those helpers had no
reachable callers; the replacement is to construct the action and return it from
Plugin.Init, which is what the framework registers. Every plugin-side
replacement below is a constructor, so return its result from Plugin.Init.
Applications keep reaching the same primitives through the genkit.Define*
functions, which construct and register in one call.
| Removed | Plugin-side replacement | App-side equivalent |
|---|---|---|
core.DefineAction | core.NewActionOf | None |
core.DefineStreamingAction | core.NewStreamingActionOf | None |
core.DefineBidiAction | core.NewBidiActionOf | None |
core.DefineBackgroundAction | core.NewBackgroundActionOf | None |
core.DefineFlow | core.NewFlow | genkit.DefineFlow |
core.DefineStreamingFlow | core.NewStreamingFlow | genkit.DefineStreamingFlow |
core.DefineSchema | None | genkit.DefineSchema |
core.DefineSchemaFor | None | genkit.DefineSchemaFor / genkit.DefineSchemasFor |
ai.DefineModel | ai.NewModelAction | genkit.DefineModelAction |
ai.DefineBackgroundModel | ai.NewBackgroundModelAction | genkit.DefineBackgroundModelAction |
ai.DefineRetriever | ai.NewRetrieverAction | genkit.DefineRetrieverAction |
ai.DefineEmbedder | ai.NewEmbedderAction | genkit.DefineEmbedderAction |
ai.DefineEvaluator | ai.NewEvaluatorAction | genkit.DefineEvaluatorAction |
ai.DefineBatchEvaluator | ai.NewBatchEvaluatorAction | genkit.DefineBatchEvaluatorAction |
ai.DefineTool | ai.NewTool | genkit.DefineTool |
ai.DefineToolWithInputSchema | ai.NewTool with ai.WithInputSchema | genkit.DefineTool with ai.WithInputSchema |
ai.DefineMultipartTool | ai.NewMultipartTool | genkit.DefineMultipartTool |
ai.DefineResource | ai.NewResource | genkit.DefineResource |
ai.DefineMiddleware | ai.NewMiddleware | genkit.DefineMiddleware |
ai.DefineFormat | ai.DefineFormats, which takes the name from each Formatter | genkit.DefineFormats |
ai/exp.DefineTool | ai/exp.NewTool | genkit/exp.DefineTool |
ai/exp.DefineInterruptibleTool | ai/exp.NewInterruptibleTool | genkit/exp.DefineInterruptibleTool |
ai.DefinePrompt, ai.DefineDataPrompt, ai.DefineFormats, and
ai.DefineGenerateAction still take a registry: there the registry is a
structural input rather than a registration target.
Package core’s own error envelope went with them. core.ReflectionError,
core.ToReflectionError, and core.SchemaValidationError were deleted with no
exported replacement, and core.NewSchemaValidationError gave way to
status.Errorf with a sentinel from core/status.
The flat core.NewAction, NewStreamingAction, NewBidiAction, and
NewBackgroundAction constructors still work and are deprecated in favor of
their Of counterparts. They take name before atype, so swapping the first
two arguments is the likeliest mistake when moving a call across. The untyped
ai.NewModel, NewRetriever, NewEmbedder, NewEvaluator,
NewBatchEvaluator, and NewBackgroundModel constructors, and the matching
genkit.Define* names, are likewise deprecated in favor of the typed-config
family: they resolve Config to any, which infers no schema and passes the
raw config through untouched.
Publishing a plugin
Section titled “Publishing a plugin”Genkit plugins can be published as normal Go packages. To increase
discoverability, your package should have genkit somewhere in its name so it
can be found with a simple search on
pkg.go.dev. Any of the following are
good choices:
github.com/yourorg/genkit-plugins/servicenamegithub.com/yourorg/your-repo/genkit/servicename