Skip to content

Commit d1c27c0

Browse files
authored
fix(go): fixed incomplete dynamic action resolution (#3152)
1 parent 40d5394 commit d1c27c0

File tree

9 files changed

+130
-155
lines changed

9 files changed

+130
-155
lines changed

go/ai/embedder.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ func DefineEmbedder(
4646
}
4747

4848
// LookupEmbedder looks up an [Embedder] registered by [DefineEmbedder].
49-
// It returns nil if the embedder was not defined.
49+
// It will try to resolve the embedder dynamically if the embedder is not found.
50+
// It returns nil if the embedder was not resolved.
5051
func LookupEmbedder(r *registry.Registry, provider, name string) Embedder {
51-
action := core.LookupActionFor[*EmbedRequest, *EmbedResponse, struct{}](r, core.ActionTypeEmbedder, provider, name)
52+
action := core.ResolveActionFor[*EmbedRequest, *EmbedResponse, struct{}](r, core.ActionTypeEmbedder, provider, name)
5253
if action == nil {
5354
return nil
5455
}

go/ai/generate.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,17 +161,19 @@ func DefineModel(r *registry.Registry, provider, name string, info *ModelInfo, f
161161
}
162162

163163
// LookupModel looks up a [Model] registered by [DefineModel].
164-
// It returns nil if the model was not defined.
164+
// It will try to resolve the model dynamically if the model is not found.
165+
// It returns nil if the model was not resolved.
165166
func LookupModel(r *registry.Registry, provider, name string) Model {
166-
action := core.LookupActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](r, core.ActionTypeModel, provider, name)
167+
action := core.ResolveActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](r, core.ActionTypeModel, provider, name)
167168
if action == nil {
168169
return nil
169170
}
170171
return (*model)(action)
171172
}
172173

173174
// LookupModelByName looks up a [Model] registered by [DefineModel].
174-
// It returns an error if the model was not defined.
175+
// It will try to resolve the model dynamically if the model is not found.
176+
// It returns an error if the model was not resolved.
175177
func LookupModelByName(r *registry.Registry, modelName string) (Model, error) {
176178
if modelName == "" {
177179
return nil, core.NewError(core.INVALID_ARGUMENT, "ai.LookupModelByName: model not specified")

go/core/action.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,23 @@ func (a *ActionDef[In, Out, Stream]) Desc() ActionDesc {
311311
return *a.desc
312312
}
313313

314+
// ResolveActionFor returns the action for the given key in the global registry,
315+
// or nil if there is none.
316+
// It panics if the action is of the wrong type.
317+
func ResolveActionFor[In, Out, Stream any](r *registry.Registry, typ ActionType, provider, name string) *ActionDef[In, Out, Stream] {
318+
var key string
319+
if provider != "" {
320+
key = fmt.Sprintf("/%s/%s/%s", typ, provider, name)
321+
} else {
322+
key = fmt.Sprintf("/%s/%s", typ, name)
323+
}
324+
a := r.ResolveAction(key)
325+
if a == nil {
326+
return nil
327+
}
328+
return a.(*ActionDef[In, Out, Stream])
329+
}
330+
314331
// LookupActionFor returns the action for the given key in the global registry,
315332
// or nil if there is none.
316333
// It panics if the action is of the wrong type.

go/genkit/genkit.go

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,13 @@ func Init(ctx context.Context, opts ...GenkitOption) (*Genkit, error) {
179179
return nil, err
180180
}
181181

182-
// Register default formats
183-
ai.ConfigureFormats(r)
184-
185182
gOpts := &genkitOptions{}
186183
for _, opt := range opts {
187184
if err := opt.apply(gOpts); err != nil {
188185
return nil, fmt.Errorf("genkit.Init: error applying options: %w", err)
189186
}
190187
}
191188

192-
ai.DefineGenerateAction(ctx, r)
193-
194-
r.RegisterValue("genkit/defaultModel", gOpts.DefaultModel)
195-
r.RegisterValue("genkit/promptDir", gOpts.PromptDir)
196-
197189
g := &Genkit{reg: r}
198190

199191
for _, plugin := range gOpts.Plugins {
@@ -203,8 +195,23 @@ func Init(ctx context.Context, opts ...GenkitOption) (*Genkit, error) {
203195
r.RegisterPlugin(plugin.Name(), plugin)
204196
}
205197

198+
r.ActionResolver = func(actionType, provider, name string) error {
199+
plugins := r.ListPlugins()
200+
for _, plugin := range plugins {
201+
if dp, ok := plugin.(DynamicPlugin); ok && dp.Name() == provider {
202+
return dp.ResolveAction(g, core.ActionType(actionType), name)
203+
}
204+
}
205+
return nil
206+
}
207+
208+
ai.ConfigureFormats(r)
209+
ai.DefineGenerateAction(ctx, r)
206210
ai.LoadPromptDir(r, gOpts.PromptDir, "")
207211

212+
r.RegisterValue("genkit/defaultModel", gOpts.DefaultModel)
213+
r.RegisterValue("genkit/promptDir", gOpts.PromptDir)
214+
208215
if registry.CurrentEnvironment() == registry.EnvironmentDev {
209216
errCh := make(chan error, 1)
210217
serverStartCh := make(chan struct{})
@@ -446,31 +453,9 @@ func DefineModel(g *Genkit, provider, name string, info *ai.ModelInfo, fn ai.Mod
446453
// LookupModel retrieves a registered [ai.Model] by its provider and name.
447454
// It returns the model instance if found, or `nil` if no model with the
448455
// given identifier is registered (e.g., via [DefineModel] or a plugin).
456+
// It will try to resolve the model dynamically by matching the provider name;
457+
// this does not necessarily mean the model is valid.
449458
func LookupModel(g *Genkit, provider, name string) ai.Model {
450-
m := ai.LookupModel(g.reg, provider, name)
451-
if m != nil {
452-
return m
453-
}
454-
455-
plugins := g.reg.ListPlugins()
456-
if plugins == nil {
457-
return nil
458-
}
459-
460-
for _, plugin := range plugins {
461-
p, ok := plugin.(DynamicPlugin)
462-
if !ok {
463-
continue
464-
}
465-
if p.Name() != provider {
466-
continue
467-
}
468-
err := p.ResolveAction(g, core.ActionTypeModel, name)
469-
if err != nil {
470-
return nil
471-
}
472-
}
473-
474459
return ai.LookupModel(g.reg, provider, name)
475460
}
476461

@@ -771,6 +756,7 @@ func DefineEmbedder(g *Genkit, provider, name string, embed func(context.Context
771756
// LookupEmbedder retrieves a registered [ai.Embedder] by its provider and name.
772757
// It returns the embedder instance if found, or `nil` if no embedder with the
773758
// given identifier is registered (e.g., via [DefineEmbedder] or a plugin).
759+
// It will try to resolve the embedder dynamically if the embedder is not found.
774760
func LookupEmbedder(g *Genkit, provider, name string) ai.Embedder {
775761
return ai.LookupEmbedder(g.reg, provider, name)
776762
}

go/genkit/reflection.go

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -255,85 +255,6 @@ func wrapReflectionHandler(h func(w http.ResponseWriter, r *http.Request) error)
255255
}
256256
}
257257

258-
// resolveAction tries to resolve any type of action and the dependant actions of it
259-
func resolveAction(g *Genkit, key string, input json.RawMessage) (core.Action, error) {
260-
var atype, provider, name string
261-
262-
trimmedKey := strings.TrimPrefix(key, "/")
263-
found := strings.HasPrefix(trimmedKey, string(core.ActionTypeUtil))
264-
265-
// special case when the action "util/generate" gets called with a model that has not been defined
266-
if found && strings.Contains(key, "generate") {
267-
var inputMap map[string]any
268-
err := json.Unmarshal(input, &inputMap)
269-
if err != nil {
270-
return nil, core.NewError(core.INTERNAL, err.Error())
271-
}
272-
modelName, ok := inputMap["model"].(string)
273-
if !ok {
274-
if k, ok := inputMap["key"].(string); ok {
275-
modelName, found = strings.CutPrefix(k, "model/")
276-
if !found {
277-
return nil, core.NewError(core.INVALID_ARGUMENT, "unable to get model name for action %q", inputMap["key"])
278-
}
279-
}
280-
}
281-
provider, name, found = strings.Cut(modelName, "/")
282-
if !found {
283-
return nil, core.NewError(core.INVALID_ARGUMENT, "unable to get provider from %q", modelName)
284-
}
285-
plugin := g.reg.LookupPlugin(provider)
286-
if plugin == nil {
287-
return nil, core.NewError(core.NOT_FOUND, "plugin for provider %q not found", provider)
288-
}
289-
dp, ok := plugin.(DynamicPlugin)
290-
if ok {
291-
if a := g.reg.LookupAction(fmt.Sprintf("/%s/%s/%s", core.ActionTypeModel, provider, name)); a != nil {
292-
return g.reg.LookupAction(key).(core.Action), nil
293-
}
294-
err = dp.ResolveAction(g, core.ActionTypeModel, name)
295-
if err != nil {
296-
return nil, core.NewError(core.INTERNAL, err.Error())
297-
}
298-
}
299-
300-
action := g.reg.LookupAction(key).(core.Action)
301-
return action, nil
302-
}
303-
304-
parts := strings.Split(trimmedKey, "/")
305-
if len(parts) == 3 {
306-
atype = parts[0]
307-
provider = parts[1]
308-
name = parts[2]
309-
310-
for _, plugin := range g.reg.ListPlugins() {
311-
dp, ok := plugin.(DynamicPlugin)
312-
if !ok {
313-
continue
314-
}
315-
if dp.Name() != provider {
316-
continue
317-
}
318-
if a := g.reg.LookupAction(fmt.Sprintf("/%s/%s/%s", atype, provider, name)); a != nil {
319-
return a.(core.Action), nil
320-
}
321-
err := dp.ResolveAction(g, core.ActionTypeModel, name)
322-
if err != nil {
323-
return nil, core.NewError(core.INTERNAL, err.Error())
324-
}
325-
action := g.reg.LookupAction(key).(core.Action)
326-
return action, nil
327-
}
328-
}
329-
330-
action := g.reg.LookupAction(key)
331-
if action == nil {
332-
return nil, core.NewError(core.NOT_FOUND, "action %q not found", key)
333-
}
334-
return action.(core.Action), nil
335-
}
336-
337258
// handleRunAction looks up an action by name in the registry, runs it with the
338259
// provided JSON input, and writes back the JSON-marshaled request.
339260
func handleRunAction(g *Genkit) func(w http.ResponseWriter, r *http.Request) error {
@@ -507,9 +428,9 @@ type telemetry struct {
507428
}
508429

509430
func runAction(ctx context.Context, g *Genkit, key string, input json.RawMessage, telemetryLabels json.RawMessage, cb streamingCallback[json.RawMessage], runtimeContext map[string]any) (*runActionResponse, error) {
510-
action, err := resolveAction(g, key, input)
511-
if err != nil {
512-
return nil, err
431+
action := g.reg.ResolveAction(key)
432+
if action == nil {
433+
return nil, core.NewError(core.NOT_FOUND, "action %q not found", key)
513434
}
514435
if runtimeContext != nil {
515436
ctx = core.WithActionContext(ctx, runtimeContext)
@@ -530,7 +451,7 @@ func runAction(ctx context.Context, g *Genkit, key string, input json.RawMessage
530451
}
531452
}
532453
traceID = trace.SpanContextFromContext(ctx).TraceID().String()
533-
return action.RunJSON(ctx, input, cb)
454+
return action.(core.Action).RunJSON(ctx, input, cb)
534455
})
535456
if err != nil {
536457
return nil, err

go/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ require (
2626
github.com/jba/slog v0.2.0
2727
github.com/lib/pq v1.10.9
2828
github.com/mark3labs/mcp-go v0.29.0
29-
github.com/pgvector/pgvector-go v0.2.0
29+
github.com/pgvector/pgvector-go v0.3.0
3030
github.com/stretchr/testify v1.10.0
3131
github.com/weaviate/weaviate v1.30.0
3232
github.com/weaviate/weaviate-go-client/v5 v5.1.0

go/go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6Q
2929
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
3030
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
3131
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
32-
entgo.io/ent v0.13.1 h1:uD8QwN1h6SNphdCCzmkMN3feSUzNnVvV/WIkHKMbzOE=
33-
entgo.io/ent v0.13.1/go.mod h1:qCEmo+biw3ccBn9OyL4ZK5dfpwg++l1Gxwac5B1206A=
32+
entgo.io/ent v0.14.3 h1:wokAV/kIlH9TeklJWGGS7AYJdVckr0DloWjIcO9iIIQ=
33+
entgo.io/ent v0.14.3/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM=
3434
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
3535
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3636
firebase.google.com/go/v4 v4.15.2 h1:KJtV4rAfO2CVCp40hBfVk+mqUqg7+jQKx7yOgFDnXBg=
@@ -50,8 +50,6 @@ github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID
5050
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
5151
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
5252
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
53-
github.com/ankane/disco-go v0.1.0 h1:nkz+y4O+UFKnEGH8FkJ8wcVwX5boZvaRzJN6EMK7NVw=
54-
github.com/ankane/disco-go v0.1.0/go.mod h1:nkR7DLW+KkXeRRAsWk6poMTpTOWp9/4iKYGDwg8dSS0=
5553
github.com/anthropics/anthropic-sdk-go v1.4.0 h1:fU1jKxYbQdQDiEXCxeW5XZRIOwKevn/PMg8Ay1nnUx0=
5654
github.com/anthropics/anthropic-sdk-go v1.4.0/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
5755
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
@@ -299,8 +297,8 @@ github.com/openai/openai-go v0.1.0-alpha.65/go.mod h1:3SdE6BffOX9HPEQv8IL/fi3LYZ
299297
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
300298
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
301299
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
302-
github.com/pgvector/pgvector-go v0.2.0 h1:NZdW4NxUxdSCzaev3LVHb9ORf+LdX+uZOQVqQ6s2Zyg=
303-
github.com/pgvector/pgvector-go v0.2.0/go.mod h1:OQpvU5QZGQOPI9quIXAyHaRZ5yGk/RGUDbs9C3DPUNE=
300+
github.com/pgvector/pgvector-go v0.3.0 h1:Ij+Yt78R//uYqs3Zk35evZFvr+G0blW0OUN+Q2D1RWc=
301+
github.com/pgvector/pgvector-go v0.3.0/go.mod h1:duFy+PXWfW7QQd5ibqutBO4GxLsUZ9RVXhFZGIBsWSA=
304302
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
305303
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
306304
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -373,6 +371,8 @@ github.com/weaviate/weaviate-go-client/v5 v5.1.0 h1:3wSf4fktKLvspPHwDYnn07u0sKfD
373371
github.com/weaviate/weaviate-go-client/v5 v5.1.0/go.mod h1:gg5qyiHk53+HMZW2ynkrgm+cMQDD2Ewyma84rBeChz4=
374372
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
375373
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
374+
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
375+
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
376376
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
377377
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
378378
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=

0 commit comments

Comments
 (0)