Files
2026-07-13 12:49:10 +08:00

146 lines
6.9 KiB
Go

/*
* Copyright 2024 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package callbacks
import (
"github.com/cloudwego/eino/internal/callbacks"
)
// RunInfo describes the entity that triggered a callback. Always nil-check
// before dereferencing — a component that calls OnStart without first calling
// EnsureRunInfo or InitCallbacks will leave RunInfo absent in the context.
//
// Fields:
// - Name: business-meaningful name specified by the user. For nodes in a
// graph this is the node name (compose.WithNodeName). For standalone
// components it must be set explicitly via [InitCallbacks] or
// [ReuseHandlers]; it is empty string if not set.
// - Type: implementation identity, e.g. "OpenAI". Set by the component via
// [components.Typer]; falls back to reflection (struct/func name) if the
// interface is not implemented. Empty for Graph itself.
// - Component: category constant, e.g. components.ComponentOfChatModel.
// Fixed value "Lambda" for lambdas, "Graph"/"Chain"/"Workflow" for graphs.
// Use this to branch on component kind without caring about implementation.
//
// Handlers should filter using RunInfo rather than assuming a fixed execution
// order — there is no guaranteed ordering between different Handlers.
type RunInfo = callbacks.RunInfo
// CallbackInput is the value passed to OnStart and OnStartWithStreamInput
// handlers. The concrete type is defined by the component — for example,
// ChatModel callbacks carry *model.CallbackInput. Use the component package's
// ConvCallbackInput helper (e.g. model.ConvCallbackInput) to cast safely; it
// returns nil if the type does not match, so you can ignore irrelevant
// component types:
//
// modelInput := model.ConvCallbackInput(in)
// if modelInput == nil {
// return ctx // not a model invocation, skip
// }
// log.Printf("prompt: %v", modelInput.Messages)
type CallbackInput = callbacks.CallbackInput
// CallbackOutput is the value passed to OnEnd and OnEndWithStreamOutput
// handlers. Like CallbackInput, the concrete type is component-defined.
// Use the component package's ConvCallbackOutput helper to cast safely.
type CallbackOutput = callbacks.CallbackOutput
// Handler is the unified callback handler interface. Implement all five
// methods (OnStart, OnEnd, OnError, OnStartWithStreamInput,
// OnEndWithStreamOutput) or use [NewHandlerBuilder] to set only the timings
// you care about.
//
// Each method receives the context returned by the previous timing of the
// SAME handler, which lets a single handler pass state between its OnStart
// and OnEnd calls via context.WithValue. There is NO guaranteed execution
// order between DIFFERENT handlers, and the context chain does not flow
// from one handler to the next — do not rely on handler ordering.
//
// Implement [TimingChecker] (the Needed method) on your handler so the
// framework can skip timings you have not registered; this avoids unnecessary
// stream copies and goroutine allocations on every component invocation.
//
// Stream handlers (OnStartWithStreamInput, OnEndWithStreamOutput) receive a
// [*schema.StreamReader] that has already been copied; they MUST close their
// copy after reading. If any handler's copy is not closed, the original stream
// cannot be freed, causing a goroutine/memory leak for the entire pipeline.
//
// Important: do NOT mutate the Input or Output values. All downstream nodes
// and handlers share the same pointer (direct assignment, not a deep copy).
// Mutations cause data races in concurrent graph execution.
type Handler = callbacks.Handler
// InitCallbackHandlers sets the global callback handlers.
// It should be called BEFORE any callback handler by user.
// It's useful when you want to inject some basic callbacks to all nodes.
// Deprecated: Use AppendGlobalHandlers instead.
func InitCallbackHandlers(handlers []Handler) {
callbacks.GlobalHandlers = handlers
}
// AppendGlobalHandlers appends handlers to the process-wide list of callback
// handlers. Global handlers run before per-invocation handlers provided via
// compose.WithCallbacks, giving them higher priority for instrumentation that
// must observe every component invocation (e.g. distributed tracing, metrics).
//
// This function is NOT thread-safe. Call it once during program initialization
// (e.g. in main or TestMain), before any graph executions begin.
// Calling it concurrently with ongoing graph executions leads to data races.
func AppendGlobalHandlers(handlers ...Handler) {
callbacks.GlobalHandlers = append(callbacks.GlobalHandlers, handlers...)
}
// CallbackTiming enumerates the lifecycle moments at which a callback handler
// is invoked. Implement [TimingChecker] on your handler and return false for
// timings you do not handle, so the framework skips the overhead of stream
// copying and goroutine spawning for those timings.
type CallbackTiming = callbacks.CallbackTiming
// Callback timing constants.
const (
// TimingOnStart fires just before the component begins processing.
// Receives a fully-formed input value (non-streaming).
TimingOnStart CallbackTiming = iota
// TimingOnEnd fires after the component returns a result successfully.
// Receives the output value. Only fires on success — not on error.
TimingOnEnd
// TimingOnError fires when the component returns a non-nil error.
// Stream errors (mid-stream panics) are NOT reported here; they surface
// as errors inside the stream reader.
TimingOnError
// TimingOnStartWithStreamInput fires when the component receives a
// streaming input (Collect / Transform paradigms). The handler receives a
// copy of the input stream and must close it after reading.
TimingOnStartWithStreamInput
// TimingOnEndWithStreamOutput fires after the component returns a
// streaming output (Stream / Transform paradigms). The handler receives a
// copy of the output stream and must close it after reading. This is
// typically where you implement streaming metrics or logging.
TimingOnEndWithStreamOutput
)
// TimingChecker is an optional interface for [Handler] implementations.
// When a handler implements Needed, the framework calls it before each
// component invocation to decide whether to set up callback infrastructure
// (stream copying, goroutine allocation) for that timing. Returning false
// avoids unnecessary overhead.
//
// Handlers built with [NewHandlerBuilder] or
// utils/callbacks.NewHandlerHelper automatically implement TimingChecker
// based on which callback functions were set.
type TimingChecker = callbacks.TimingChecker