diff --git a/callbacks/aspect_inject.go b/callbacks/aspect_inject.go index bae111c..a5b3a07 100644 --- a/callbacks/aspect_inject.go +++ b/callbacks/aspect_inject.go @@ -93,9 +93,9 @@ func OnError(ctx context.Context, err error) context.Context { return ctx } -// InitCallbacksWithExistingHandlers initializes a new context with the provided RunInfo, while using the same handlers already exist. -func InitCallbacksWithExistingHandlers(ctx context.Context, info *RunInfo) context.Context { - return callbacks.InitCallbacksWithExistingHandlers(ctx, info) +// ReuseHandler initializes a new context with the provided RunInfo, while using the same handlers already exist. +func ReuseHandler(ctx context.Context, info *RunInfo) context.Context { + return callbacks.ReuseHandler(ctx, info) } // InitCallbacks initializes a new context with the provided RunInfo and handlers. diff --git a/compose/graph.go b/compose/graph.go index 6d3dbdc..728ed7d 100644 --- a/compose/graph.go +++ b/compose/graph.go @@ -992,3 +992,11 @@ func validateDAG(chanSubscribeTo map[string]*chanCall, invertedEdges map[string] } return nil } + +func NewNodePath(path ...string) *NodePath { + return &NodePath{path: path} +} + +type NodePath struct { + path []string +} diff --git a/compose/graph_call_options.go b/compose/graph_call_options.go index fd244b9..8783b3a 100644 --- a/compose/graph_call_options.go +++ b/compose/graph_call_options.go @@ -34,23 +34,54 @@ type Option struct { options []any handler []callbacks.Handler - keys []string + paths []*NodePath maxRunSteps int } -// DesignateNode set the key of the node which will be used to. -// eg. +func (o Option) deepCopy() Option { + nOptions := make([]any, len(o.options)) + copy(nOptions, o.options) + nHandler := make([]callbacks.Handler, len(o.handler)) + copy(nHandler, o.handler) + nPaths := make([]*NodePath, len(o.paths)) + for i, path := range o.paths { + nPath := *path + nPaths[i] = &nPath + } + return Option{ + options: nOptions, + handler: nHandler, + paths: nPaths, + maxRunSteps: o.maxRunSteps, + } +} + +// DesignateNode set the key of the node which will the option be applied to. +// notice: only effective at the top graph. +// e.g. // // embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small")) // runnable.Invoke(ctx, "input", embeddingOption.DesignateNode("my_embedding_node")) func (o Option) DesignateNode(key ...string) Option { - o.keys = append(o.keys, key...) + nKeys := make([]*NodePath, len(key)) + for i, k := range key { + nKeys[i] = NewNodePath(k) + } + return o.DesignateNodeWithPath(nKeys...) +} + +// DesignateNodeWithPath sets the path of the node(s) to which the option will be applied to. +// You can make the option take effect in the subgraph by specifying the key of the subgraph. +// e.g. +// DesignateNodeWithPath({"sub graph node key", "node key within sub graph"}) +func (o Option) DesignateNodeWithPath(path ...*NodePath) Option { + o.paths = append(o.paths, path...) return o } // WithEmbeddingOption is a functional option type for embedding component. -// eg. +// e.g. // // embeddingOption := compose.WithEmbeddingOption(embedding.WithModel("text-embedding-3-small")) // runnable.Invoke(ctx, "input", embeddingOption) @@ -59,7 +90,7 @@ func WithEmbeddingOption(opts ...embedding.Option) Option { } // WithRetrieverOption is a functional option type for retriever component. -// eg. +// e.g. // // retrieverOption := compose.WithRetrieverOption(retriever.WithIndex("my_index")) // runnable.Invoke(ctx, "input", retrieverOption) @@ -73,7 +104,7 @@ func WithLoaderSplitterOption(opts ...document.LoaderSplitterOption) Option { } // WithLoaderOption is a functional option type for loader component. -// eg. +// e.g. // // loaderOption := compose.WithLoaderOption(document.WithCollection("my_collection")) // runnable.Invoke(ctx, "input", loaderOption) @@ -87,7 +118,7 @@ func WithDocumentTransformerOption(opts ...document.TransformerOption) Option { } // WithIndexerOption is a functional option type for indexer component. -// eg. +// e.g. // // indexerOption := compose.WithIndexerOption(indexer.WithSubIndexes([]string{"my_sub_index"})) // runnable.Invoke(ctx, "input", indexerOption) @@ -96,7 +127,7 @@ func WithIndexerOption(opts ...indexer.Option) Option { } // WithChatModelOption is a functional option type for chat model component. -// eg. +// e.g. // // chatModelOption := compose.WithChatModelOption(model.WithTemperature(0.7)) // runnable.Invoke(ctx, "input", chatModelOption) @@ -118,12 +149,12 @@ func WithToolsNodeOption(opts ...ToolsNodeOption) Option { func WithLambdaOption(opts ...any) Option { return Option{ options: opts, - keys: make([]string, 0), + paths: make([]*NodePath, 0), } } // WithCallbacks set callback handlers for all components in a single call. -// eg. +// e.g. // // runnable.Invoke(ctx, "input", compose.WithCallbacks(&myCallbacks{})) func WithCallbacks(cbs ...callbacks.Handler) Option { @@ -154,7 +185,7 @@ func withComponentOption[TOption any](opts ...TOption) Option { } return Option{ options: o, - keys: make([]string, 0), + paths: make([]*NodePath, 0), } } diff --git a/compose/graph_call_options_test.go b/compose/graph_call_options_test.go index cb8a67e..708860b 100644 --- a/compose/graph_call_options_test.go +++ b/compose/graph_call_options_test.go @@ -300,3 +300,197 @@ func TestCallOptionsOneByOne(t *testing.T) { assert.Equal(t, int64(123), opt.uid) }) } + +func TestCallOptionInSubGraph(t *testing.T) { + ctx := context.Background() + + type child1Option string + type child2Option string + type parentOption string + type grandparentOption string + + child1 := NewGraph[string, string]() + err := child1.AddLambdaNode("1", InvokableLambdaWithOption(func(ctx context.Context, input string, opts ...child1Option) (output string, err error) { + if len(opts) != 1 || opts[0] != "child1-1" { + t.Fatal("child1-1 option error") + } + return input + " child1-1", nil + }), WithNodeName("child1-1")) + assert.NoError(t, err) + err = child1.AddEdge(START, "1") + assert.NoError(t, err) + err = child1.AddEdge("1", END) + assert.NoError(t, err) + + child2 := NewGraph[string, string]() + err = child2.AddLambdaNode("1", InvokableLambdaWithOption(func(ctx context.Context, input string, opts ...child2Option) (output string, err error) { + if len(opts) != 1 || opts[0] != "child2-1" { + t.Fatal("child2-1 option error") + } + return input + " child2-1", nil + }), WithNodeName("child2-1")) + assert.NoError(t, err) + err = child2.AddEdge(START, "1") + assert.NoError(t, err) + err = child2.AddEdge("1", END) + assert.NoError(t, err) + + parent := NewGraph[string, string]() + err = parent.AddLambdaNode("1", InvokableLambdaWithOption(func(ctx context.Context, input string, opts ...parentOption) (output string, err error) { + if len(opts) != 1 || opts[0] != "parent-1" { + t.Fatal("parent-1 option error") + } + return input + " parent-1", nil + }), WithNodeName("parent-1")) + assert.NoError(t, err) + err = parent.AddGraphNode("2", child1, WithNodeName("child1")) + assert.NoError(t, err) + err = parent.AddGraphNode("3", child2, WithNodeName("child2")) + assert.NoError(t, err) + err = parent.AddEdge(START, "1") + assert.NoError(t, err) + err = parent.AddEdge("1", "2") + assert.NoError(t, err) + err = parent.AddEdge("2", "3") + assert.NoError(t, err) + err = parent.AddEdge("3", END) + assert.NoError(t, err) + + grandParent := NewGraph[string, string]() + err = grandParent.AddLambdaNode("1", InvokableLambdaWithOption(func(ctx context.Context, input string, opts ...grandparentOption) (output string, err error) { + if len(opts) != 1 || opts[0] != "grandparent-1" { + t.Fatal("grandparent-1 option error") + } + return input + " grandparent-1", nil + }), WithNodeName("grandparent-1")) + assert.NoError(t, err) + err = grandParent.AddGraphNode("2", parent, WithNodeName("parent")) + assert.NoError(t, err) + err = grandParent.AddEdge(START, "1") + assert.NoError(t, err) + err = grandParent.AddEdge("1", "2") + assert.NoError(t, err) + err = grandParent.AddEdge("2", END) + assert.NoError(t, err) + + r, err := grandParent.Compile(ctx, WithGraphName("grandparent")) + assert.NoError(t, err) + + grandCommonTimes := 0 + grandCommonCB := callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + switch grandCommonTimes { + case 0: + if info.Name != "grandparent" || info.Component != ComponentOfGraph { + t.Fatal("grandparent common callback 0 error") + } + case 1: + if info.Name != "grandparent-1" { + t.Fatal("grandparent common callback 1 error") + } + case 2: + if info.Name != "parent" { + t.Fatal("grandparent common callback 2 error") + } + case 3: + if info.Name != "parent-1" { + t.Fatal("grandparent common callback 3 error") + } + case 4: + if info.Name != "child1" { + t.Fatal("grandparent common callback 4 error") + } + case 5: + if info.Name != "child1-1" { + t.Fatal("grandparent common callback 5 error") + } + case 6: + if info.Name != "child2" { + t.Fatal("grandparent common callback 6 error") + } + case 7: + if info.Name != "child2-1" { + t.Fatal("grandparent common callback 7 error") + } + default: + t.Fatal("grandparent common callback too many") + } + grandCommonTimes++ + return ctx + }).Build() + grand1CB := callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + if info.Name != "grandparent-1" { + t.Fatal("grandparent common callback 0 error") + } + return ctx + }).Build() + parentCommonCBTimes := 0 + parentCommonCB := callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + switch parentCommonCBTimes { + case 0: + if info.Name != "parent" { + t.Fatal("parent common callback 0 error") + } + case 1: + if info.Name != "parent-1" { + t.Fatal("parent common callback 1 error") + } + case 2: + if info.Name != "child1" { + t.Fatal("parent common callback 2 error") + } + case 3: + if info.Name != "child1-1" { + t.Fatal("parent common callback 3 error") + } + case 4: + if info.Name != "child2" { + t.Fatal("parent common callback 4 error") + } + case 5: + if info.Name != "child2-1" { + t.Fatal("parent common callback 5 error") + } + default: + t.Fatal("parent common callback too many") + } + parentCommonCBTimes++ + return ctx + }).Build() + child1CommonCBTimes := 0 + child1CommonCB := callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + switch child1CommonCBTimes { + case 0: + if info.Name != "child1" { + t.Fatal("child1 common callback 0 error") + } + case 1: + if info.Name != "child1-1" { + t.Fatal("child1 common callback 1 error") + } + default: + t.Fatal("child1 common callback too many") + } + child1CommonCBTimes++ + return ctx + }).Build() + child2CB := callbacks.NewHandlerBuilder().OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context { + if info.Name != "child2-1" { + t.Fatal("child2-1 common callback 0 error") + } + return ctx + }).Build() + + result, err := r.Invoke(ctx, "input", + WithCallbacks(grandCommonCB), + WithCallbacks(parentCommonCB).DesignateNodeWithPath(NewNodePath("2")), + WithCallbacks(grand1CB).DesignateNode("1"), + WithCallbacks(child1CommonCB).DesignateNodeWithPath(NewNodePath("2", "2")), + WithCallbacks(child2CB).DesignateNodeWithPath(NewNodePath("2", "3", "1")), + WithLambdaOption(grandparentOption("grandparent-1")).DesignateNodeWithPath(NewNodePath("1")), + WithLambdaOption(parentOption("parent-1")).DesignateNodeWithPath(NewNodePath("2", "1")), + WithLambdaOption(child1Option("child1-1")).DesignateNodeWithPath(NewNodePath("2", "2", "1")), + WithLambdaOption(child2Option("child2-1")).DesignateNodeWithPath(NewNodePath("2", "3", "1")), + ) + assert.NoError(t, err) + assert.Equal(t, result, "input grandparent-1 parent-1 child1-1 child2-1") +} diff --git a/compose/graph_run.go b/compose/graph_run.go index ec7447c..db9cd99 100644 --- a/compose/graph_run.go +++ b/compose/graph_run.go @@ -24,7 +24,6 @@ import ( "runtime/debug" "sync" - "github.com/cloudwego/eino/callbacks" "github.com/cloudwego/eino/schema" "github.com/cloudwego/eino/utils/safe" ) @@ -496,33 +495,3 @@ func (r *runner) parserOrValidateTypeIfNeeded(cur, next string, isStream bool, v } return value, nil } - -func initNodeCallbacks(ctx context.Context, key string, info *nodeInfo, meta *executorMeta, opts ...Option) context.Context { - ri := &callbacks.RunInfo{} - if meta != nil { - ri.Component = meta.component - ri.Type = meta.componentImplType - } - - if info != nil { - ri.Name = info.name - } - - var cbs []callbacks.Handler - for i := range opts { - if len(opts[i].handler) != 0 { - if len(opts[i].keys) == 0 { - cbs = append(cbs, opts[i].handler...) - } else { - for _, k := range opts[i].keys { - if k == key { - cbs = append(cbs, opts[i].handler...) - break - } - } - } - } - } - - return callbacks.InitCallbacks(ctx, ri, cbs...) -} diff --git a/compose/tool_node.go b/compose/tool_node.go index 6f638d3..fb83975 100644 --- a/compose/tool_node.go +++ b/compose/tool_node.go @@ -167,7 +167,7 @@ func (tn *ToolsNode) genToolCallTasks(input *schema.Message) ([]toolCallTask, er } func runToolCallTaskByInvoke(ctx context.Context, task *toolCallTask, opts ...tool.Option) { - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{ + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{ Name: task.name, Type: task.meta.componentImplType, Component: task.meta.component, @@ -176,7 +176,7 @@ func runToolCallTaskByInvoke(ctx context.Context, task *toolCallTask, opts ...to } func runToolCallTaskByStream(ctx context.Context, task *toolCallTask, opts ...tool.Option) { - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{ + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{ Name: task.name, Type: task.meta.componentImplType, Component: task.meta.component, diff --git a/compose/utils.go b/compose/utils.go index 5a6547c..e2e14fc 100644 --- a/compose/utils.go +++ b/compose/utils.go @@ -214,12 +214,40 @@ func initGraphCallbacks(ctx context.Context, info *nodeInfo, meta *executorMeta, var cbs []callbacks.Handler for i := range opts { - if len(opts[i].handler) != 0 && len(opts[i].keys) == 0 { + if len(opts[i].handler) != 0 && len(opts[i].paths) == 0 { cbs = append(cbs, opts[i].handler...) } } - return callbacks.InitCallbacks(ctx, ri, cbs...) + return icb.AppendHandler(ctx, ri, cbs...) +} + +func initNodeCallbacks(ctx context.Context, key string, info *nodeInfo, meta *executorMeta, opts ...Option) context.Context { + ri := &callbacks.RunInfo{} + if meta != nil { + ri.Component = meta.component + ri.Type = meta.componentImplType + } + + if info != nil { + ri.Name = info.name + } + + var cbs []callbacks.Handler + for i := range opts { + if len(opts[i].handler) != 0 { + if len(opts[i].paths) != 0 { + for _, k := range opts[i].paths { + if len(k.path) == 1 && k.path[0] == key { + cbs = append(cbs, opts[i].handler...) + break + } + } + } + } + } + + return icb.AppendHandler(ctx, ri, cbs...) } func streamChunkConvertForCBOutput[O any](o O) (callbacks.CallbackOutput, error) { @@ -271,34 +299,63 @@ func checkAssignable(input, arg reflect.Type) assignableType { func extractOption(nodes map[string]*chanCall, opts ...Option) (map[string][]any, error) { optMap := map[string][]any{} for _, opt := range opts { - if len(opt.options) == 0 { - continue - } - if len(opt.keys) == 0 { - // common option, check type + if len(opt.paths) == 0 { + // common, discard callback, filter option by type + if len(opt.options) == 0 { + continue + } for name, c := range nodes { - if reflect.TypeOf(opt.options[0]) == c.action.optionType { // assume that types of options are the same + if c.action.optionType == nil { + // subgraph + optMap[name] = append(optMap[name], opt) + } else if reflect.TypeOf(opt.options[0]) == c.action.optionType { // assume that types of options are the same optMap[name] = append(optMap[name], opt.options...) } } } - for _, key := range opt.keys { - if _, ok := nodes[key]; !ok { - return nil, fmt.Errorf("option has designated an unknown node: %s", key) + for _, path := range opt.paths { + if len(path.path) == 0 { + return nil, fmt.Errorf("call option has designated an empty path") } - if nodes[key].action.optionType != reflect.TypeOf(opt.options[0]) { // assume that types of options are the same - return nil, fmt.Errorf("option type[%s] is different from which the designated node[%s] expects[%s]", - reflect.TypeOf(opt.options[0]).String(), key, nodes[key].action.optionType.String()) + + var curNode *chanCall + var ok bool + if curNode, ok = nodes[path.path[0]]; !ok { + return nil, fmt.Errorf("option has designated an unknown node: %s", path) + } + curNodeKey := path.path[0] + + if len(path.path) == 1 { + if len(opt.options) == 0 { + // sub graph common callbacks has been added to ctx in initNodeCallback and won't be passed to subgraph only pass options + // node callback also won't be passed + continue + } + if curNode.action.optionType == nil { + nOpt := opt.deepCopy() + nOpt.paths = []*NodePath{} + optMap[curNodeKey] = append(optMap[curNodeKey], nOpt) + } else { + // designate to component + if curNode.action.optionType != reflect.TypeOf(opt.options[0]) { // assume that types of options are the same + return nil, fmt.Errorf("option type[%s] is different from which the designated node[%s] expects[%s]", + reflect.TypeOf(opt.options[0]).String(), path, curNode.action.optionType.String()) + } + optMap[curNodeKey] = append(optMap[curNodeKey], opt.options...) + } + } else { + if curNode.action.optionType != nil { + // component + return nil, fmt.Errorf("cannot designate sub path of a component, path:%s", path) + } + // designate to sub graph's nodes + nOpt := opt.deepCopy() + nOpt.paths = []*NodePath{NewNodePath(path.path[1:]...)} + optMap[curNodeKey] = append(optMap[curNodeKey], nOpt) } - optMap[key] = append(optMap[key], opt.options...) - } - } - for k, v := range nodes { - if v.action.optionType == nil { - // sub graph - optMap[k] = toAnyList(opts) } } + return optMap, nil } diff --git a/flow/retriever/multiquery/multi_query.go b/flow/retriever/multiquery/multi_query.go index d4cff0d..565be2a 100644 --- a/flow/retriever/multiquery/multi_query.go +++ b/flow/retriever/multiquery/multi_query.go @@ -207,5 +207,5 @@ func ctxWithFusionRunInfo(ctx context.Context) context.Context { runInfo.Name = runInfo.Type + string(runInfo.Component) - return callbacks.InitCallbacksWithExistingHandlers(ctx, runInfo) + return callbacks.ReuseHandler(ctx, runInfo) } diff --git a/flow/retriever/router/router.go b/flow/retriever/router/router.go index f16ade2..7138236 100644 --- a/flow/retriever/router/router.go +++ b/flow/retriever/router/router.go @@ -178,7 +178,7 @@ func ctxWithRouterRunInfo(ctx context.Context) context.Context { runInfo.Name = runInfo.Type + string(runInfo.Component) - return callbacks.InitCallbacksWithExistingHandlers(ctx, runInfo) + return callbacks.ReuseHandler(ctx, runInfo) } func ctxWithFusionRunInfo(ctx context.Context) context.Context { @@ -189,5 +189,5 @@ func ctxWithFusionRunInfo(ctx context.Context) context.Context { runInfo.Name = runInfo.Type + string(runInfo.Component) - return callbacks.InitCallbacksWithExistingHandlers(ctx, runInfo) + return callbacks.ReuseHandler(ctx, runInfo) } diff --git a/flow/retriever/utils/utils.go b/flow/retriever/utils/utils.go index 64152d3..7ef971a 100644 --- a/flow/retriever/utils/utils.go +++ b/flow/retriever/utils/utils.go @@ -79,5 +79,5 @@ func ctxWithRetrieverRunInfo(ctx context.Context, r retriever.Retriever) context runInfo.Name = runInfo.Type + string(runInfo.Component) - return callbacks.InitCallbacksWithExistingHandlers(ctx, runInfo) + return callbacks.ReuseHandler(ctx, runInfo) } diff --git a/internal/callbacks/inject.go b/internal/callbacks/inject.go index ffafddf..783c4c2 100644 --- a/internal/callbacks/inject.go +++ b/internal/callbacks/inject.go @@ -34,7 +34,7 @@ func InitCallbacks(ctx context.Context, info *RunInfo, handlers ...Handler) cont return ctxWithManager(ctx, nil) } -func InitCallbacksWithExistingHandlers(ctx context.Context, info *RunInfo) context.Context { +func ReuseHandler(ctx context.Context, info *RunInfo) context.Context { cbm, ok := managerFromCtx(ctx) if !ok { return ctx @@ -43,6 +43,14 @@ func InitCallbacksWithExistingHandlers(ctx context.Context, info *RunInfo) conte return ctxWithManager(ctx, cbm.withRunInfo(info)) } +func AppendHandler(ctx context.Context, info *RunInfo, handlers ...Handler) context.Context { + cbm, ok := managerFromCtx(ctx) + if !ok { + return InitCallbacks(ctx, info, handlers...) + } + return InitCallbacks(ctx, info, append(cbm.handlers, handlers...)...) +} + type Handle[T any] func(context.Context, T, *RunInfo, []Handler) (context.Context, T) func On[T any](ctx context.Context, inOut T, handle Handle[T], timing CallbackTiming) (context.Context, T) { diff --git a/utils/callbacks/template_test.go b/utils/callbacks/template_test.go index e14126d..f3f5a86 100644 --- a/utils/callbacks/template_test.go +++ b/utils/callbacks/template_test.go @@ -176,19 +176,19 @@ func TestNewComponentTemplate(t *testing.T) { callbacks.OnStart[any](ctx, nil) assert.Equal(t, 22, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfPrompt}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfPrompt}) callbacks.OnStart[any](ctx, nil) assert.Equal(t, 23, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfIndexer}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfIndexer}) callbacks.OnEnd[any](ctx, nil) assert.Equal(t, 23, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfEmbedding}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfEmbedding}) callbacks.OnError(ctx, nil) assert.Equal(t, 24, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfLoader}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfLoader}) callbacks.OnStart[any](ctx, nil) assert.Equal(t, 24, cnt) @@ -239,11 +239,11 @@ func TestNewComponentTemplate(t *testing.T) { callbacks.OnEnd[any](ctx, nil) assert.Equal(t, 25, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfIndexer}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfIndexer}) callbacks.OnStart[any](ctx, nil) assert.Equal(t, 26, cnt) - ctx = callbacks.InitCallbacksWithExistingHandlers(ctx, &callbacks.RunInfo{Component: components.ComponentOfLoader}) + ctx = callbacks.ReuseHandler(ctx, &callbacks.RunInfo{Component: components.ComponentOfLoader}) callbacks.OnEnd[any](ctx, nil) assert.Equal(t, 27, cnt) })