Documentation
¶
Overview ¶
Package tool adapts explicitly capable external tools to durable background tasks.
Index ¶
- Constants
- Variables
- func NewManagedTool(ctx context.Context, config *ManagedToolConfig) (componenttool.BaseTool, error)
- func RegisterExecutors(executors *backgroundtask.ExecutorRegistry, registry *Registry) error
- func Submit(ctx context.Context, manager *backgroundtask.Manager, registry *Registry, ...) (*backgroundtask.Task, error)
- type AdoptRequest
- type AdoptResult
- type BackgroundTool
- type ForegroundHandoffTool
- type InputPreparer
- type InputRequest
- type ManagedToolConfig
- type ManagedToolResponseEvent
- type ManagedToolResponseEventType
- type MaterializeOutputRequest
- type Outcome
- type OutputMaterializer
- type ProgressReader
- type RecoverRequest
- type RecoverableBackgroundTool
- type Registration
- type Registry
- type ReserveOutputRequest
- type ResumableBackgroundTool
- type ResumeRequest
- type Run
- type StartRequest
- type StartResult
- type SubmitRequest
- type Update
- type UpdateSource
Examples ¶
Constants ¶
const ( // ExecutorKey identifies non-recoverable managed tools. ExecutorKey = "eino.dev/background-tool" // RecoverableExecutorKey identifies managed tools that support Worker handoff. RecoverableExecutorKey = "eino.dev/recoverable-background-tool" )
Variables ¶
var ( // ErrResumeInputRejected reports that resume data is invalid for the // current InputRequest. ResumableBackgroundTool.Resume must return this // error before applying side effects and with a nil Run. The framework // leaves the task waiting on the same request. ErrResumeInputRejected = errors.New( "backgroundtask/tool: resume input rejected", ) )
Functions ¶
func NewManagedTool ¶
func NewManagedTool( ctx context.Context, config *ManagedToolConfig, ) (componenttool.BaseTool, error)
NewManagedTool creates a wrapper implementing EnhancedInvokableTool and EnhancedStreamableTool. Every result includes a text control envelope; completed foreground results may append rich parts through Registration.RenderResult. Detaching closes only the caller projection; durable persistence continues. A foreground timeout without a successful handoff returns a *backgroundtask.ForegroundTimeoutError.
Example ¶
/*
* Copyright 2026 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 main
import (
"context"
"encoding/json"
"fmt"
"github.com/cloudwego/eino/adk/backgroundtask"
backgroundtool "github.com/cloudwego/eino/adk/backgroundtask/tool"
componenttool "github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/schema"
)
type exampleTool struct{}
func (exampleTool) ValidateArguments(arguments string) error {
var input map[string]any
return json.Unmarshal([]byte(arguments), &input)
}
func (exampleTool) Start(
context.Context,
*backgroundtool.StartRequest,
) (*backgroundtool.StartResult, error) {
return &backgroundtool.StartResult{Run: exampleRun{}}, nil
}
type exampleRun struct{}
func (exampleRun) Wait(context.Context) (*backgroundtool.Outcome, error) {
return &backgroundtool.Outcome{
Status: backgroundtask.StatusCompleted, Data: []byte("video ready"),
}, nil
}
func (exampleRun) Stop(context.Context) error { return nil }
func main() {
executors := backgroundtask.NewExecutorRegistry()
manager, err := backgroundtask.New(context.Background(), &backgroundtask.Config{
Executors: executors,
SendTaskCreatedEvent: func(context.Context, *backgroundtask.Task) error {
return nil
},
IDGen: func(context.Context, *backgroundtask.AllocateTaskIDRequest) (string, error) {
return "task_video", nil
},
})
if err != nil {
panic(err)
}
registry := backgroundtool.NewRegistry()
_ = registry.Register(&backgroundtool.Registration{
Info: &schema.ToolInfo{
Name: "generate_video", Desc: "Generate a product video",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"prompt": {Type: schema.String, Required: true},
}),
},
Tool: exampleTool{},
})
wrapped, _ := backgroundtool.NewManagedTool(context.Background(), &backgroundtool.ManagedToolConfig{
Manager: manager, Executors: executors, Registry: registry, ToolName: "generate_video",
SessionID: func(context.Context) (string, error) { return "session", nil },
})
result, _ := wrapped.(componenttool.EnhancedInvokableTool).InvokableRun(
context.Background(), &schema.ToolArgument{Text: `{"prompt":"launch"}`},
)
var event backgroundtool.ManagedToolResponseEvent
_ = json.Unmarshal([]byte(result.Parts[0].Text), &event)
fmt.Println(event.TaskID, event.Status, event.Output)
}
Output: completed video ready
func RegisterExecutors ¶
func RegisterExecutors(executors *backgroundtask.ExecutorRegistry, registry *Registry) error
RegisterExecutors installs the plain and recoverable managed-tool executors.
func Submit ¶
func Submit( ctx context.Context, manager *backgroundtask.Manager, registry *Registry, req *SubmitRequest, ) (*backgroundtask.Task, error)
Submit validates and persists one registered managed tool without invoking InputPreparer or exposing the private executor payload. Managed-tool executors for registry must already be registered with manager. If the returned error wraps backgroundtask.ErrTaskCreatedEventUndelivered and task is non-nil, durable ownership has transferred and callers must not retry Submit.
Types ¶
type AdoptRequest ¶
AdoptRequest transfers an Attempt 0 foreground operation into background ownership. Run must be the live handle returned by the matching Start call.
type AdoptResult ¶
AdoptResult contains the background-owned run handle and checkpoint to use for the first durable attempt after handoff.
type BackgroundTool ¶
type BackgroundTool interface {
ValidateArguments(arguments string) error
Start(context.Context, *StartRequest) (*StartResult, error)
}
BackgroundTool starts one logical external operation. ValidateArguments must be repeatable and side-effect free. Start receives the Eino task ID before any external side effect occurs. Attempt 0 is a parent-owned foreground invocation with no persisted task or store-authorized attempt; background attempts start at 1 and must be preceded by Manager.Submit. InputPreparer supports ordinary Runner interruption before durable task creation. ResumableBackgroundTool supports durable input requests after creation. An error from Start is a durable execution failure for background attempts and a model-visible foreground failure for Attempt 0. For recoverable tools, Eino commits StartResult.Checkpoint and the external-start boundary before calling Run.Wait only for background attempts.
type ForegroundHandoffTool ¶
type ForegroundHandoffTool interface {
BackgroundTool
Adopt(context.Context, *AdoptRequest) (*AdoptResult, error)
}
ForegroundHandoffTool can adopt a running Attempt 0 foreground operation into background ownership without repeating its side effects.
type InputPreparer ¶
type InputPreparer interface {
PrepareInput(
ctx context.Context,
arguments string,
) (preparedArguments string, err error)
}
InputPreparer optionally completes or rewrites tool arguments before durable task creation. The managed-tool wrapper calls PrepareInput synchronously in the parent Runner tool invocation, before task ID allocation, output reservation, or persistence. Implementations may use components/tool StatefulInterrupt, GetInterruptState, and GetResumeContext normally.
PrepareInput may be re-entered by Runner and must not start the external operation or perform non-idempotent side effects. Its non-empty result must be the final JSON arguments: the framework validates, persists, and supplies them to policy callbacks and Start or Recover.
type InputRequest ¶
type InputRequest struct {
ID string `json:"id"`
Data json.RawMessage `json:"data,omitempty"`
}
InputRequest describes the current durable question from a managed tool. ID must be stable for this question across recovery. Data must contain one valid JSON value and is embedded unchanged in model-facing responses.
func ReadInputRequest ¶
func ReadInputRequest(task *backgroundtask.Task) (*InputRequest, error)
ReadInputRequest returns the application-facing request for a managed tool currently in StatusWaitingInput. The returned value owns its Data bytes.
type ManagedToolConfig ¶
type ManagedToolConfig struct {
// Manager and Executors are required; Executors must be the registry used by
// Manager's workers.
Manager *backgroundtask.Manager
Executors *backgroundtask.ExecutorRegistry
// Registry and ToolName select a required registered implementation.
Registry *Registry
ToolName string
// ForegroundTimeoutMs overrides the default foreground observation timeout.
// Nil uses the framework default; non-positive disables the timer.
ForegroundTimeoutMs *int
// ShouldAutoBackground is evaluated after foreground timeout. Nil means
// timeout the operation instead of detaching. It may be called concurrently.
// A non-positive ForegroundTimeoutMs therefore also disables this policy:
// with no timer there is no expiry to evaluate it on.
ShouldAutoBackground func(context.Context, *backgroundtask.ForegroundCandidate) bool
// RunInBackground requests explicit detachment from JSON arguments. Nil
// never requests it and takes precedence over foreground timeout.
RunInBackground func(context.Context, string) bool
// InvocationTimeoutMs returns an optional operation timeout in milliseconds.
// Nil or a nil result means no operation timeout.
InvocationTimeoutMs func(context.Context, string) *int
// SessionID resolves the optional session notification target. An empty
// result disables session-routed lifecycle notifications. Nil uses the
// current Runner session when one exists and otherwise disables notification.
SessionID func(context.Context) (string, error)
}
ManagedToolConfig configures the framework-owned model-facing wrapper.
type ManagedToolResponseEvent ¶
type ManagedToolResponseEvent struct {
Type ManagedToolResponseEventType `json:"type"`
TaskID string `json:"task_id,omitempty"`
Status backgroundtask.Status `json:"status,omitempty"`
Description string `json:"description,omitempty"`
Output any `json:"output,omitempty"`
Error string `json:"error,omitempty"`
InputRequest *InputRequest `json:"input_request,omitempty"`
Update *Update `json:"update,omitempty"`
}
ManagedToolResponseEvent is the framework-owned model-facing text control envelope. The enhanced managed-tool wrapper encodes it as the first text part of every ToolResult; streaming uses one newline-terminated record per chunk. Type determines the legal variant: update events set only Update, while launch-result events set task identity, status, description, and either a waiting InputRequest or terminal Output or Error.
type ManagedToolResponseEventType ¶
type ManagedToolResponseEventType string
ManagedToolResponseEventType identifies one model-facing managed-tool response variant.
const ( // ManagedToolResponseEventUpdate carries one live progress Update. All other // ManagedToolResponseEvent fields are empty. ManagedToolResponseEventUpdate ManagedToolResponseEventType = "update" // ManagedToolResponseEventLaunchResult carries the task launch or foreground result. // Update is nil; the remaining fields describe the task and its current or // terminal outcome. ManagedToolResponseEventLaunchResult ManagedToolResponseEventType = "launch_result" // ManagedToolResponseEventForegroundResult carries a synchronous foreground // result. No TaskID is set because no background task was persisted. ManagedToolResponseEventForegroundResult ManagedToolResponseEventType = "foreground_result" )
type MaterializeOutputRequest ¶
MaterializeOutputRequest describes one caller-identified progress event.
type Outcome ¶
type Outcome struct {
Status backgroundtask.Status
Data []byte
Error string
InputRequest *InputRequest
// Checkpoint replaces the latest opaque tool checkpoint when Status is
// StatusWaitingInput and this field is non-empty. Empty retains the latest
// checkpoint. Other statuses must leave it empty.
Checkpoint []byte
}
Outcome is the authoritative logical-operation result. Completed outcomes may contain Data and no Error. Failed outcomes require Error and no Data. Canceled outcomes may contain Error and no Data. Waiting-input outcomes set InputRequest and may set Checkpoint; a non-empty Checkpoint replaces the latest tool checkpoint while an empty value retains it. Other statuses must leave InputRequest and Checkpoint empty. Waiting input is supported only by ResumableBackgroundTool.
type OutputMaterializer ¶
type OutputMaterializer interface {
// ReserveOutput must return the same path when repeated with one TaskID.
// A reservation may remain unused when later task submission fails.
ReserveOutput(context.Context, *ReserveOutputRequest) (string, error)
AppendOutput(context.Context, *MaterializeOutputRequest) error
}
OutputMaterializer optionally projects caller-identified task events to a deterministic file or object. AppendOutput must durably deduplicate by (TaskID, EventID) for the task's recovery and retention lifetime. Distinct events must be applied in call order; EventID is opaque and must not be sorted. Recoverable update sources must therefore replay in stable order.
type ProgressReader ¶
type ProgressReader struct {
// contains filtered or unexported fields
}
ProgressReader formats a bounded recent view of managed-tool task events.
func NewProgressReader ¶
func NewProgressReader( manager *backgroundtask.Manager, limit int, ) (*ProgressReader, error)
NewProgressReader creates a managed-tool progress reader. Limit is the number of newest events rendered; non-positive values use 20 and values above the store maximum are capped by ListTaskEvents.
func (*ProgressReader) ReadProgress ¶
func (r *ProgressReader) ReadProgress( ctx context.Context, task *backgroundtask.Task, ) (string, error)
ReadProgress implements middleware executor-specific progress projection.
type RecoverRequest ¶
RecoverRequest describes reconstruction of an existing logical operation. Checkpoint is an independently owned copy of the latest opaque tool checkpoint.
type RecoverableBackgroundTool ¶
type RecoverableBackgroundTool interface {
BackgroundTool
Recover(context.Context, *RecoverRequest) (Run, error)
}
RecoverableBackgroundTool reconstructs the same logical operation after a Worker loss or graceful yield. Implementations must make Start idempotent by TaskID because Worker loss may occur after the external start but before Eino persists StartResult.Checkpoint and the started marker.
type Registration ¶
type Registration struct {
// Info and Tool are required and snapshotted by Register.
Info *schema.ToolInfo
Tool BackgroundTool
// Description formats persisted arguments for task presentation. Nil uses
// the tool name. It may be called concurrently, must not panic, and must
// return the same value when repeated with the same arguments.
Description func(arguments string) string
// RenderResult returns rich content for a successfully completed foreground
// result. The framework prepends its text control envelope to the returned
// parts. Nil embeds raw result bytes in that envelope. It may be called
// concurrently; errors are returned to the invoking model call without
// changing terminal task state.
RenderResult func(context.Context, *backgroundtask.Task) (*schema.ToolResult, error)
// Materializer optionally derives an EventID-idempotent output file.
Materializer OutputMaterializer
}
Registration binds a stable model-facing tool name to an implementation. Equivalent registrations must be installed on every Worker eligible to claim tasks for the selected executor key.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry stores plain and recoverable registrations independently so a name may migrate between capability classes while old persisted tasks remain valid.
func (*Registry) Register ¶
func (r *Registry) Register(registration *Registration) error
Register adds a registration to the capability class implemented by Tool.
type ReserveOutputRequest ¶
type ReserveOutputRequest struct {
TaskID string
}
ReserveOutputRequest identifies the task whose derived output path is reserved.
type ResumableBackgroundTool ¶
type ResumableBackgroundTool interface {
RecoverableBackgroundTool
// Resume validates and applies input atomically from the framework's
// perspective. Invalid input returns ErrResumeInputRejected before any side
// effect; other errors are durable execution failures.
Resume(context.Context, *ResumeRequest) (Run, error)
}
ResumableBackgroundTool supports durable input requests after the operation has started. Resume must apply the same RequestID and Data idempotently: Worker loss may cause the framework to repeat the call on a later attempt.
Implementations keep operation state in their external durable backend. The framework persists opaque tool checkpoints and the current InputRequest but never interprets tool checkpoint bytes.
type ResumeRequest ¶
type ResumeRequest struct {
TaskID string
Arguments string
Attempt int64
RequestID string
Data []byte
Checkpoint []byte
}
ResumeRequest applies durable external input to a waiting logical operation. RequestID identifies the exact InputRequest being answered. Data is opaque to the framework and may be empty. Checkpoint is an independently owned copy of the checkpoint persisted at that waiting boundary. A later attempt may replay the same request.
type Run ¶
Run is an attempt-local handle for one logical external operation. Canceling Wait stops observation only. Stop requests logical cancellation and must be safe under repeated or concurrent calls.
type StartRequest ¶
StartRequest describes an initial external-operation start.
type StartResult ¶
StartResult contains the attempt-local Run and the initial opaque tool checkpoint. For Attempt >= 1, Eino persists an independently owned copy before calling Run.Wait. For Attempt 0, the checkpoint is held in memory until foreground handoff or stored in the parent Runner checkpoint for foreground waiting-input. Checkpoint may be empty when TaskID alone is sufficient to recover and must be empty for a non-recoverable BackgroundTool.
type SubmitRequest ¶
type SubmitRequest struct {
TaskID string
ToolName string
Arguments string
Description string
SessionID string
DisableLifecycleNotifications bool
}
SubmitRequest describes a registered managed tool submitted directly as a durable task. Empty TaskID asks Manager to allocate one. SessionID optionally identifies the session notification target. It may be empty only when DisableLifecycleNotifications is true.
type Update ¶
type Update struct {
EventID string `json:"event_id,omitempty"`
Kind string `json:"kind,omitempty"`
Data []byte `json:"data,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
Update is a bounded serializable progress event. Data is limited to 256 KiB, Kind to 128 bytes, and Metadata to 32 entries with keys and values at most 1024 bytes each. Recoverable implementations must assign a non-empty, lifetime-stable EventID and replay updates in logical order. For plain tools, the framework may generate an ID without mutating this value. EventID is not an ordering key or pagination cursor.
type UpdateSource ¶
type UpdateSource interface {
Updates() *schema.StreamReader[*Update]
}
UpdateSource optionally exposes replayable incremental updates. The framework calls Updates once per Run, owns and closes the returned reader, and expects it to close shortly after Wait reaches a terminal outcome. Recovery starts at the beginning of the replayable history; EventID deduplication removes repeats.