Skip to content

feat(go/core): add options-struct New*ActionOf constructors and delete registry-taking Define* helpers - #5862

Open
apascal07 wants to merge 10 commits into
mainfrom
ap/go-action-constructors
Open

feat(go/core): add options-struct New*ActionOf constructors and delete registry-taking Define* helpers#5862
apascal07 wants to merge 10 commits into
mainfrom
ap/go-action-constructors

Conversation

@apascal07

@apascal07 apascal07 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Reworks the core action constructor surface as a non-breaking addition for the next minor release: options-struct constructors under new names (New*ActionOf), deprecated old constructors delegating to them, and outright deletion of the registry-taking Define* helpers, which have no reachable external callers. This is the base PR of a train: the typed-config primitive constructors (ai.NewTyped*) build on it next, followed by plugin migrations.

Options-struct action constructors

The core constructors gain variants that lead with the action type (matching ResolveActionFor, which already leads with it) and take one options struct covering all schema slots. The names read as "new action of <type>".

Before: metadata and the input schema are positional, and output and stream schemas have no override at all.

core.NewAction[In, Out](name, atype, metadata map[string]any, inputSchema map[string]any, fn)
core.NewStreamingAction[In, Out, Stream](name, atype, metadata, inputSchema, fn)
core.NewBackgroundAction[In, Out](name, atype, metadata, startFn, checkFn, cancelFn)
core.NewBidiAction[In, Out, Stream, Init](name, atype, opts, fn)

After: all schema slots share one inference and override path.

type core.ActionOptions struct {
    Description  string
    Metadata     map[string]any
    InputSchema  map[string]any // inferred from In if nil
    OutputSchema map[string]any // inferred from Out if nil
    StreamSchema map[string]any // inferred from Stream if nil
}

core.NewActionOf[In, Out](atype, name, opts, fn)
core.NewStreamingActionOf[In, Out, Stream](atype, name, opts, fn)
core.NewBackgroundActionOf[In, Out](atype, name, opts, startFn, checkFn, cancelFn)
core.NewBidiActionOf[In, Out, Stream, Init](atype, name, opts, fn)

core.BackgroundActionDef is renamed to BackgroundAction so the concrete action type names are consistent (Action, BackgroundAction, BidiAction, Flow); the Def suffix carried no distinction since the action interface already lives at api.Action. The old name remains as a deprecated alias, mirroring the existing ActionDef alias.

Options structs hold descriptor data only (documented on ActionOptions); implementation functions are positional, with optional ones nil-able, so the background cancel function stays a trailing argument. NewBackgroundActionOf takes its own data-only BackgroundActionOptions (ActionOptions minus the stream slot, since the component actions are non-streaming) and no longer leaks the start action's schema slots into the check/cancel sub-actions; they keep the shared description and metadata but infer their own Operation schemas. The bidi constructors already took an options struct, so their Of variant only changes the argument order; BidiActionOptions gains Description to match ActionOptions.

All framework-internal call sites (flows, tools, prompts, resources, generate, agent snapshot/abort actions) now use the new constructors.

New shared helpers back this and the follow-up typed-config work, reusable by in-repo plugins: base.ConvertToExact (exact-type conversion with base.ErrTypeMismatch) and base.SchemaMapFor[T] (schema inference that returns nil for interface types). core.InferSchemaMap remains the exported value-based helper.

Registry-taking Define helpers deleted

A registry is unobtainable outside the framework: plugins return actions from Init for the framework to register, and applications go through the genkit package. So the registry-taking Define* helpers in core had no reachable external callers and are deleted outright rather than deprecated; the genkit wrappers remain the user touchpoints and now compose New* + Register directly.

Deleted: DefineAction, DefineStreamingAction, DefineBackgroundAction, DefineBidiAction, DefineFlow, DefineStreamingFlow, DefineSchema, DefineSchemaFor.

To make New* + Register fully equivalent to the old Define*, the "dynamic" metadata marker ("created at runtime, outside any registry") is now registration-derived: Register removes it instead of leaving stale construction-time state (pinned by test; nothing in the Go codebase reads the flag).

Deprecations

Deprecated, delegating to the new functions with identical behavior: core.NewAction, core.NewStreamingAction, core.NewBackgroundAction, core.NewBidiAction (each pointing at its New*ActionOf counterpart).

When the deprecated functions are removed in the next major, the new ones can collapse back to the plain names with the same signatures, so migrating now is the last rename.

Scope

  • No Define*Of surface. Registration composes through the existing Register methods, so a parallel Define* family would only be New* + Register under a second name. The genkit wrappers stay the registering touchpoint.
  • The cancel function stays out of BackgroundActionOptions. Holding a CancelOpFunc[Out] would force the options struct to become generic over Out for one optional field, so it stays a nil-able trailing argument and the struct stays data-only.
  • The ai and genkit layers are untouched here. Their constructors move in feat(go/ai): add typed-config NewTyped* constructors #5849, which builds on this; plugin migrations follow that.

Verification

Go 1.26.3, full go build ./... and go test ./... in go/: 40 packages with tests pass, no failures.

New coverage in core/action_options_test.go: each schema slot is inferred when the option is nil and honored when set, Description wins over Metadata["description"] and falls back to it, a streaming action infers its stream schema, the deprecated constructors delegate to their Of counterparts, and Register clears the "dynamic" marker. core/define_test.go holds test-local New* + Register helpers standing in for the deleted registry-taking Define*, so the existing suites keep exercising the same construction paths under the new constructors.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the action definition and registration API in Genkit by deprecating flat-argument constructors (e.g., NewAction, NewBackgroundAction) in favor of options-based constructors (e.g., NewActionOf, NewBackgroundActionOf) that accept a new ActionOptions struct. It also removes global Define* helpers in favor of explicit creation and registration (e.g., calling .Register(r) or r.RegisterSchema). Feedback on the changes suggests simplifying the description assignment in go/ai/exp/agent.go by setting it directly on the BidiActionOptions struct instead of using a conditional block.

Comment thread go/ai/exp/agent.go
apascal07 added 10 commits July 31, 2026 15:25
ConvertToExact converts a dynamically typed value to T without coercing
between mismatched Go types: exactly T, *T, or map[string]any (JSON wire
form) are accepted, anything else errors wrapping ErrTypeMismatch.
SchemaMapFor infers a JSON schema from a type parameter, returning nil
for interface types. Both back the upcoming typed-config constructors
and are reusable by plugins.
…tructors

Adds core.ActionOptions covering description, metadata, and all three
schema slots (output and stream previously had no override at all), and
New[Streaming]ActionWithOptions, NewBackgroundActionWithOptions, and
NewBidiActionWithOptions constructors that lead with the action type,
matching ResolveActionFor.

Only New* constructors are added; registration composes via the
existing Register methods. The flat-argument New*/Define* constructors
are deprecated and delegate to the new ones, so descriptors are built
on one code path (pinned by TestDeprecatedConstructorsDelegate).

NewBackgroundActionWithOptions no longer leaks the start action's
schema slots into the check/cancel sub-actions; they keep the shared
description and metadata but infer their own Operation schemas.
BidiActionOptions gains Description.
…ructors

Moves flows, tools, prompts, resources, agent snapshot/abort actions,
and the bidi tests off the deprecated flat-argument core constructors
onto New*WithOptions + Register. No behavior change; plugins migrate in
follow-up PRs.
NewActionOf, NewStreamingActionOf, NewBackgroundActionOf, and
NewBidiActionOf read as "new action of <type>" with the leading action
type argument, and drop the WithOptions mouthful. These names shipped
earlier on this branch and were never released, so this is a rename,
not a break.
…dActionOptions

The cancel function is optional, so it belongs in the options struct
rather than as a trailing nil-able positional argument, mirroring how
BidiActionOptions carries the bidi-specific InitSchema slot. It is
generic in the operation's output type, so it gets its own
BackgroundActionOptions[Out] instead of a slot on the shared
ActionOptions; a nil options value still infers cleanly from the
start/check functions.
…ActionOptions data-only

Options structs in this package hold descriptor data only; an action's
implementation functions are positional constructor arguments, with
optional ones accepting nil. Cancel is a third of the background
action's implementation and its presence changes the registered surface
(whether a cancel.operation action exists), so it moves back to a
positional nil-able argument. BackgroundActionOptions stays, but as the
data-only per-shape options struct (ActionOptions minus the stream
slot), which also makes it non-generic again. The invariant is
documented on ActionOptions.
Matches background_action.go and the file's contents (BidiAction and
its constructors). Test file follows.
The "dynamic" metadata marker means created at runtime outside any
registry (ai.NewTool, ai.NewMultipartTool, and ai.NewResource stamp
it). Registering such an action makes the marker false by definition,
so Action.Register and BidiAction.Register now remove it instead of
leaving stale construction-time state. This also makes New* + Register
produce the same descriptor as the registry-taking Define helpers,
unblocking their removal.
A registry is unobtainable outside the framework: plugins return
actions from Init for the framework to register and applications go
through the genkit package, so the registry-taking Define helpers had
no reachable external callers. Instead of carrying them through a
deprecation cycle, delete them; the genkit wrappers remain the user
touchpoints and now compose New* + Register directly.

Deleted: core.Define[Streaming]Action, core.DefineBackgroundAction,
core.DefineBidiAction, core.Define[Streaming]Flow, core.DefineSchema,
and core.DefineSchemaFor.

Tests use package-local define helpers that compose New* + Register,
which also exercises the composition path users are pointed at.
Makes the concrete action type names consistent: Action, BackgroundAction,
BidiAction, Flow. The Def suffix carried no distinction since the action
interface already lives at api.Action. The old name remains as a deprecated
alias.
@apascal07
apascal07 force-pushed the ap/go-action-constructors branch from 6b639ec to a0c6bfd Compare July 31, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant