Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
97be756
addition of background model and integration of veo
Jul 21, 2025
b26cf57
Merge branch 'main' into sahdev/long-running-op
Jul 21, 2025
98da571
linting update
Jul 21, 2025
a7d85dd
Merge branch 'main' into sahdev/long-running-op
Jul 30, 2025
1618d66
resolving as per teh comments
Jul 30, 2025
c19f7a2
change in generate OPeration function to support LRO
Jul 30, 2025
3e22285
LRO support check in Generate operation
Jul 30, 2025
12ce243
fixes as per the comments
Jul 31, 2025
36be81d
Fix operation output from any to Out
Jul 31, 2025
42600c8
refactoring of code
Aug 1, 2025
9203524
removal of support cancel
Aug 1, 2025
9b65068
fixes as per the comment
Aug 3, 2025
7d6debd
change in generate operation function to call generate function under…
Aug 12, 2025
51ebade
Merge branch 'main' of https://github.com/firebase/genkit into sahdev…
Aug 12, 2025
c7296ab
removal of longrunning flag from GenerateActionOptions and useSupport…
Aug 12, 2025
0826d05
merge with main and refactor code as per the new structure
Aug 22, 2025
cce1d4f
merge with main and resolve conflicts
Aug 22, 2025
cb87e4e
feat: Refactor action definition and registration
Aug 25, 2025
4b532f1
merge with main
Sep 8, 2025
f1959d7
update in gen.go
Sep 8, 2025
12449ce
run go fmt
Sep 8, 2025
bdba0ef
addition of operations in model response
Sep 8, 2025
1407ab4
code merged with main
Sep 15, 2025
8f292e6
Merge branch 'main' into sahdev/long-running-op
sahdev77 Sep 15, 2025
d7db9bf
removal of supportLongOperation fucntional check and make a seperate …
Sep 16, 2025
b4d0e86
resolve merge conflict with main
Sep 16, 2025
5513be5
resolve merge conflict with main
Sep 16, 2025
a9138fd
addition of copyright
Sep 16, 2025
08e5fa6
Reverted the conflict file issue
Sep 19, 2025
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
1 change: 1 addition & 0 deletions go/ai/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ type ModelSupports struct {
SystemRole bool `json:"systemRole,omitempty"`
ToolChoice bool `json:"toolChoice,omitempty"`
Tools bool `json:"tools,omitempty"`
LongRunning bool `json:"longRunning,omitempty"`
}

type ConstrainedSupport string
Expand Down
188 changes: 188 additions & 0 deletions go/ai/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"slices"
"strings"
"time"

"github.com/firebase/genkit/go/core"
"github.com/firebase/genkit/go/core/logger"
Expand Down Expand Up @@ -92,6 +93,12 @@ type (
toolResponse *Part
interrupt *Part
}

StartModelFunc = core.StartOperationFunc[*ModelRequest, *ModelResponse] // Function to start a model background operation
CheckModelFunc = core.CheckOperationFunc[*ModelResponse] // Function to check model operation status
CancelModelFunc = core.CancelOperationFunc[*ModelResponse] // Function to cancel model operation

BackgroundAction = core.BackgroundAction[*ModelRequest, *ModelResponse] // Background action for model operations
)

// DefineGenerateAction defines a utility generate action.
Expand Down Expand Up @@ -133,6 +140,7 @@ func DefineModel(r *registry.Registry, provider, name string, info *ModelInfo, f
"tools": info.Supports.Tools,
"toolChoice": info.Supports.ToolChoice,
"constrained": info.Supports.Constrained,
"longRunning": info.Supports.LongRunning,
},
"versions": info.Versions,
"stage": info.Stage,
Expand Down Expand Up @@ -171,6 +179,12 @@ func LookupModel(r *registry.Registry, provider, name string) Model {
return (*model)(action)
}

// LookupBackgroundModel looks up a BackgroundAction registered by [DefineBackgroundModel].
// It returns nil if the background model was not found.
func LookupBackgroundModel(r *registry.Registry, provider, name string) BackgroundAction {
return core.LookupBackgroundAction[*ModelRequest, *ModelResponse](r, provider, name)
}

// LookupModelByName looks up a [Model] registered by [DefineModel].
// It will try to resolve the model dynamically if the model is not found.
// It returns an error if the model was not resolved.
Expand Down Expand Up @@ -1058,3 +1072,177 @@ func handleResumeOption(ctx context.Context, r *registry.Registry, genOpts *Gene
toolMessage: toolMessage,
}, nil
}

// DefineBackgroundModel defines a new model that runs in the background
func DefineBackgroundModel(
r *registry.Registry,
provider, name string,
options *core.BackgroundModelOptions,
start StartModelFunc,
check CheckModelFunc,
cancel CancelModelFunc,
) BackgroundAction {
label := options.Label
if label == "" {
label = name
}

// Prepare metadata
metadata := map[string]any{
"model": map[string]any{
"label": label,
"versions": options.Versions,
"supports": options.Supports,
},
}

if options.ConfigSchema != nil {
metadata["model"].(map[string]any)["customOptions"] = options.ConfigSchema
}
startFunc := func(ctx context.Context, request *ModelRequest) (*core.Operation, error) {
startTime := time.Now()

// Call the user's start function
operation, err := start(ctx, request)
if err != nil {
return nil, err
}

// Add latency metadata
if operation.Metadata == nil {
operation.Metadata = make(map[string]any)
}
operation.Metadata["latencyMs"] = float64(time.Since(startTime).Nanoseconds()) / 1e6

return operation, nil
}

bgAction := core.DefineBackgroundAction[*ModelRequest, *ModelResponse](r, provider, name, *options, metadata, startFunc,
func(ctx context.Context, operation *core.Operation) (*core.Operation, error) {
return check(ctx, operation)
},
func(ctx context.Context, operation *core.Operation) (*core.Operation, error) {
if cancel == nil {
return nil, core.NewError(core.UNIMPLEMENTED, "cancel not implemented")
}
return cancel(ctx, operation)
},
)

return bgAction
}

// SupportsLongRunning checks if a model supports long-running operations by model name.
func SupportsLongRunning(r *registry.Registry, modelName string) bool {
if modelName == "" {
return false
}

provider, name, found := strings.Cut(modelName, "/")
if !found {
name = provider
provider = ""
}

modelInstance := LookupModel(r, provider, name)
if modelInstance == nil {
return false
}

modelImpl, ok := modelInstance.(*model)
if !ok {
return false
}

action := (*core.ActionDef[*ModelRequest, *ModelResponse, *ModelResponseChunk])(modelImpl)
if action == nil {
return false
}

metadata := action.Desc().Metadata
if metadata == nil {
return false
}

modelMeta, ok := metadata["model"].(map[string]any)
if !ok {
return false
}

supportsMeta, ok := modelMeta["supports"].(map[string]any)
if !ok {
return false
}

longRunning, ok := supportsMeta["longRunning"].(bool)
if !ok {
return false
}

return longRunning
}

// GenerateOperation generates a model response as a long-running operation based on the provided options.
// It returns an error if the model does not support long-running operations.
func GenerateOperation(ctx context.Context, r *registry.Registry, opts ...GenerateOption) (*core.Operation, error) {
genOpts := &generateOptions{}
for _, opt := range opts {
if err := opt.applyGenerate(genOpts); err != nil {
return nil, core.NewError(core.INVALID_ARGUMENT, "ai.Generate: error applying options: %v", err)
}
}

if !SupportsLongRunning(r, genOpts.ModelName) {
return nil, core.NewError(core.UNIMPLEMENTED, "model %q does not support long-running operations", genOpts.ModelName)
}

var modelName string
if genOpts.Model != nil {
modelName = genOpts.Model.Name()
} else {
modelName = genOpts.ModelName
}

provider, name, _ := strings.Cut(modelName, "/")
bgAction := LookupBackgroundModel(r, provider, name)
if bgAction == nil {
return nil, core.NewError(core.NOT_FOUND, "background model %q not found", modelName)
}

var messages []*Message
if genOpts.SystemFn != nil {
system, err := genOpts.SystemFn(ctx, nil)
if err != nil {
return nil, err
}

messages = append(messages, NewSystemTextMessage(system))
}
if genOpts.MessagesFn != nil {
msgs, err := genOpts.MessagesFn(ctx, nil)
if err != nil {
return nil, err
}

messages = append(messages, msgs...)
}
if genOpts.PromptFn != nil {
prompt, err := genOpts.PromptFn(ctx, nil)
if err != nil {
return nil, err
}

messages = append(messages, NewUserTextMessage(prompt))
}

if modelRef, ok := genOpts.Model.(ModelRef); ok && genOpts.Config == nil {
genOpts.Config = modelRef.Config()
}

op, err := bgAction.Start(ctx, &ModelRequest{Messages: messages, Config: genOpts.Config})
if err != nil {
return nil, err
}

return op, nil
}
3 changes: 3 additions & 0 deletions go/core/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ const (
ActionTypeTool ActionType = "tool"
ActionTypeUtil ActionType = "util"
ActionTypeCustom ActionType = "custom"
ActionTypeBackgroundModel ActionType = "background-model"
ActionTypeCheckOperation ActionType = "check-operation"
ActionTypeCancelOperation ActionType = "cancel-operation"
)

// An ActionDef is a named, observable operation that underlies all Genkit primitives.
Expand Down
Loading
Loading