Documentation
¶
Overview ¶
Package tool defines the provider-neutral executable Tool protocol, its immutable Contract and execution Binding, authorization boundary, typed function adapter, and instance-scoped registry.
Example ¶
package main
import (
"context"
"fmt"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/tool"
)
func main() {
type input struct {
A int `json:"a"`
B int `json:"b"`
}
add, err := tool.NewFunc(tool.FuncConfig{
Name: "add",
Description: "add two integers",
}, func(_ context.Context, value input) (int, error) {
return value.A + value.B, nil
})
if err != nil {
panic(err)
}
registry, err := tool.NewRegistry(add)
if err != nil {
panic(err)
}
fmt.Println(registry.Definitions()[0].Name)
binding, ok := registry.Resolve("add")
if !ok {
panic("missing add")
}
invocation, err := binding.Contract().Prepare(chat.ToolCall{ID: "call-1", Name: "add", Arguments: `{"a":2,"b":3}`})
if err != nil {
panic(err)
}
result, err := binding.Call(context.Background(), invocation)
if err != nil {
panic(err)
}
text, _ := result.Text()
fmt.Println(text)
}
Output: add 5
Index ¶
- Variables
- func Capability[T any](value Tool) (capability T, found bool, err error)
- type Authorization
- type AuthorizationError
- type Authorizer
- type AuthorizerFunc
- type Binding
- type Contract
- type Failure
- type FailureConfig
- type FailureKind
- type Func
- type FuncConfig
- type Guard
- type GuardConfig
- type InputValidatingTool
- type Invocation
- type Registry
- type Tool
- type WrappingTool
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrDuplicateTool = errors.New("tool: duplicate tool") ErrInvalidRegistry = errors.New("tool: invalid registry") )
var ( ErrInvalidTool = errors.New("tool: invalid tool") ErrInvalidInvocation = errors.New("tool: invalid invocation") )
var ErrInvalidFailure = errors.New("tool: invalid failure")
var ErrInvalidWrappingChain = errors.New("tool: invalid wrapping chain")
ErrInvalidWrappingChain reports a decorator chain too deep to traverse safely.
Functions ¶
Types ¶
type Authorization ¶
type Authorization struct {
// contains filtered or unexported fields
}
Authorization carries only the frozen model-visible contract and validated arguments, so policy code cannot bypass Contract validation or execute the invocation.
func (Authorization) Arguments ¶
func (a Authorization) Arguments() []byte
Arguments is detached for the same reason as Definition.
func (Authorization) Definition ¶
func (a Authorization) Definition() chat.ToolDefinition
Definition is detached because policy implementations may retain or annotate what they inspect without changing the executable contract.
type AuthorizationError ¶ added in v0.23.0
type AuthorizationError struct {
// contains filtered or unexported fields
}
AuthorizationError means the policy could not establish a decision for the current invocation. The Tool was not executed. Its internal cause is not the Tool's outcome, public output, input request, or execution cancellation. Guard separately preserves cancellation of the execution context.
func (*AuthorizationError) Cause ¶ added in v0.23.0
func (a *AuthorizationError) Cause() error
Cause retains the policy diagnostic without exposing it through Error, errors.Is, or errors.As. Callers must explicitly choose to inspect it.
func (*AuthorizationError) Error ¶ added in v0.23.0
func (a *AuthorizationError) Error() string
type Authorizer ¶
type Authorizer interface {
Authorize(ctx context.Context, authorization Authorization) (bool, error)
}
Authorizer is deliberately smaller than an application permission system: identity, consent, tenancy, and policy storage remain caller-owned context. A nil error establishes a decision: true permits execution, false refuses it. Any error means the decision could not be completed; its boolean is ignored. The Tool never executes after refusal or an authorization error. Guard seals policy errors in AuthorizationError; only cancellation of the execution context itself remains an errors.Is-visible control signal.
type AuthorizerFunc ¶
type AuthorizerFunc func(context.Context, Authorization) (bool, error)
AuthorizerFunc adapts a plain function to Authorizer, so a one-off policy does not require a named type.
func (AuthorizerFunc) Authorize ¶
func (a AuthorizerFunc) Authorize(ctx context.Context, authorization Authorization) (bool, error)
type Binding ¶
type Binding struct {
// contains filtered or unexported fields
}
Binding associates one immutable Contract with the Tool that executes it. Calls may run concurrently when the underlying Tool supports concurrent use.
func (Binding) Call ¶
func (b Binding) Call(ctx context.Context, invocation Invocation) (chat.ToolOutput, error)
Call executes an Invocation prepared by this Binding's exact Contract. It rejects another binding's contract even when the public Tool name matches.
type Contract ¶ added in v0.19.0
type Contract struct {
// contains filtered or unexported fields
}
Contract is the immutable trust boundary between an untrusted chat.ToolCall and a validated Invocation. It holds the frozen definition and compiled input schema and independent input validator without retaining an executable Tool. A Contract obtained from Binding.Contract is safe for concurrent use independently of the Tool.
func (Contract) Definition ¶ added in v0.19.0
func (c Contract) Definition() chat.ToolDefinition
Definition returns an independent snapshot of the frozen definition.
func (Contract) Prepare ¶ added in v0.19.0
func (c Contract) Prepare(call chat.ToolCall) (Invocation, error)
Prepare validates identity, RFC 7493 JSON syntax, the frozen input schema, and its independent input validator. It does not invoke the executable Tool or authorization policy. Blank arguments are normalized to the empty object.
type Failure ¶ added in v0.17.0
type Failure struct {
// contains filtered or unexported fields
}
Failure owns one complete unsuccessful outcome of the current Tool invocation. Output is explicitly public to the model. Cause is diagnostic only: it cannot change Kind, substitute another invocation's output, or issue a control signal. Ordinary errors do not establish a definite outcome.
func NewFailure ¶ added in v0.17.0
func NewFailure(config FailureConfig) (*Failure, error)
NewFailure snapshots public output. Failed outcomes require a cause; rejection may be a policy decision without an error. Wrapping the returned Failure with %w preserves its outcome; inspect Cause explicitly for internal diagnostics.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/tool"
)
func main() {
failure, err := tool.NewFailure(tool.FailureConfig{
Kind: tool.FailureKindRejected, Cause: errors.New("internal authorization diagnostic"),
Output: chat.NewTextToolOutput("this operation is not permitted"),
})
if err != nil {
panic(err)
}
wrapped := fmt.Errorf("tool call: %w", failure)
result, found := errors.AsType[*tool.Failure](wrapped)
if !found {
panic("missing definite outcome")
}
text, _ := result.Output().Text()
fmt.Println(result.Kind(), text)
fmt.Println(errors.Is(wrapped, result.Cause()))
}
Output: rejected this operation is not permitted false
func (*Failure) Cause ¶ added in v0.23.0
Cause does not participate in errors.Is or errors.As. Transparent error wrapping surrounds Failure; diagnostic causes are behind the outcome boundary.
func (*Failure) Kind ¶ added in v0.23.0
func (f *Failure) Kind() FailureKind
func (*Failure) Output ¶ added in v0.17.0
func (f *Failure) Output() chat.ToolOutput
Output returns an independent copy of the complete failure output.
type FailureConfig ¶ added in v0.23.0
type FailureConfig struct {
Kind FailureKind
Output chat.ToolOutput
Cause error
}
FailureConfig describes one unsuccessful invocation. NewFailure snapshots Output and retains Cause only for explicit diagnostic inspection.
type FailureKind ¶ added in v0.23.0
type FailureKind string
FailureKind describes the current invocation, independently of its diagnostic cause. Neither kind assigns retry or runtime control policy.
const ( // FailureKindFailed is a definite unsuccessful outcome. It does not assert // that execution began and may describe acknowledged partial effects. FailureKindFailed FailureKind = "failed" // FailureKindRejected means the invocation was refused permission to execute. FailureKindRejected FailureKind = "rejected" )
type Func ¶
type Func[In, Out any] struct { // contains filtered or unexported fields }
Func adapts a typed Go function to Tool. It owns the derived input contract, strict argument decoding, invocation, and result encoding.
Func is immutable after construction and is safe for concurrent calls when the wrapped function is safe for concurrent calls.
func NewFunc ¶
func NewFunc[In, Out any](config FuncConfig, function func(context.Context, In) (Out, error)) (Func[In, Out], error)
NewFunc derives the model-visible JSON Schema from the Go input type. Its independent input validator uses the same strict decoder as Call, so Bind admits arguments before any function executes, even when JSON Schema cannot express all decoder constraints. Custom JSON and text decoders in In must be deterministic, bounded, side-effect-free, and safe for concurrent use.
func (Func[In, Out]) Call ¶
func (f Func[In, Out]) Call(ctx context.Context, invocation Invocation) (chat.ToolOutput, error)
Call strictly decodes a promoted invocation, invokes the wrapped function, and returns a provider-neutral result.
func (Func[In, Out]) Definition ¶
func (f Func[In, Out]) Definition() chat.ToolDefinition
func (Func[In, Out]) InputValidator ¶ added in v0.19.0
InputValidator retains only the input type's decoding behavior. It captures neither this Func nor its application function.
type FuncConfig ¶
FuncConfig describes a typed function tool. NewFunc derives the model-visible schema and the independent input validator from In.
type Guard ¶
type Guard struct {
// contains filtered or unexported fields
}
Guard keeps authorization at the universal Tool.Call boundary, which makes the same policy work for direct calls, registries, and managed runtimes. Refusal produces a generic FailureKindRejected result. A Tool that owns more specific public feedback uses NewFailure at its own invocation boundary.
func NewGuard ¶
func NewGuard(config GuardConfig) (Guard, error)
NewGuard snapshots the wrapped tool's definition at construction, so the contract policy evaluates is the contract the model was shown. Resolving it per call would let a mutable inner tool widen its own arguments after approval. Bind compiles the frozen schema and Contract.Prepare validates invocations; construction validates only the definition's protocol shape.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/Tangerg/scope/core/chat"
"github.com/Tangerg/scope/core/tool"
)
func main() {
executable, err := tool.NewFunc(tool.FuncConfig{Name: "inspect"}, func(context.Context, struct{}) (string, error) {
return "inspected", nil
})
if err != nil {
panic(err)
}
guard, err := tool.NewGuard(tool.GuardConfig{
Tool: executable,
Authorizer: tool.AuthorizerFunc(func(context.Context, tool.Authorization) (bool, error) {
return false, nil
}),
})
if err != nil {
panic(err)
}
binding, err := tool.Bind(guard)
if err != nil {
panic(err)
}
invocation, err := binding.Contract().Prepare(chat.ToolCall{ID: "call", Name: "inspect", Arguments: `{}`})
if err != nil {
panic(err)
}
_, err = binding.Call(context.Background(), invocation)
failure, found := errors.AsType[*tool.Failure](err)
if !found {
panic(err)
}
fmt.Println(failure.Kind())
}
Output: rejected
func (Guard) Call ¶
func (g Guard) Call(ctx context.Context, invocation Invocation) (chat.ToolOutput, error)
func (Guard) Definition ¶
func (g Guard) Definition() chat.ToolDefinition
type GuardConfig ¶
type GuardConfig struct {
Tool Tool
Authorizer Authorizer
}
GuardConfig names both collaborators explicitly so neither can be defaulted. A guard silently constructed without an authorizer would report as protected while permitting everything.
type InputValidatingTool ¶ added in v0.19.0
type InputValidatingTool interface {
// InputValidator returns an independent, immutable validator. Neither the
// returned function nor anything it captures may retain an executable Tool
// or execution backend. It must be deterministic, bounded, side-effect-free,
// safe for concurrent use, and must not mutate or retain its argument.
// Nil means the schema is the complete input admission contract.
InputValidator() func([]byte) error
}
InputValidatingTool supplies the input admission rules that JSON Schema cannot express, such as Go numeric syntax or a custom JSON decoder's domain constraints. Bind freezes the declaration through the wrapping chain.
type Invocation ¶
type Invocation struct {
// contains filtered or unexported fields
}
Invocation is a complete JSON object admitted by one exact frozen Tool contract. Its fields are intentionally private: only Contract.Prepare can promote an untrusted model proposal into an executable invocation.
func (Invocation) Arguments ¶
func (i Invocation) Arguments() []byte
Arguments returns an owned copy of the validated JSON object.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is an instance-scoped, concurrency-safe collection of executable tools. Its zero value is ready to use. Each runtime or process owns its registry explicitly; there is no package-global counterpart. Registration is atomic for the full batch, definitions are snapshotted at registration time, and model-visible views are returned as defensive copies in stable name order.
func NewRegistry ¶
NewRegistry is a convenience over the usable zero value for the common case of a fixed tool set. Registration is all-or-nothing, so a duplicate name in the initial batch leaves no partially populated registry behind.
func (*Registry) Definitions ¶
func (r *Registry) Definitions() []chat.ToolDefinition
type Tool ¶
type Tool interface {
// Definition returns a detached, valid schema snapshot. Callers may expose or
// mutate the returned value without changing subsequent calls or execution.
Definition() chat.ToolDefinition
// Call executes one schema-validated invocation. Implementations still own
// capability-specific semantic validation. Ordinary failure is returned as
// error without assigning retry or control-flow meaning. Implementations must
// honor ctx and must not retain the invocation or its arguments.
// On error, the returned output is not consumed; use [Failure] to preserve
// complete failure content, including acknowledged partial effects.
Call(ctx context.Context, invocation Invocation) (chat.ToolOutput, error)
}
Tool is the minimal executable capability used by model-driven runtimes. Definition returns an independent snapshot safe to expose to a model. Call receives only an Invocation promoted by its exact frozen Contract.
Tool assigns no control-flow meaning to errors. Retry, pause, abort, and ordinary error feedback belong to the runtime driving the tool.
type WrappingTool ¶
type WrappingTool interface {
// Unwrap returns the next inner tool in a finite decorator chain. It must
// return the same tool for the wrapper's lifetime and must not perform I/O;
// cycles and excessive depth make the chain invalid.
Unwrap() Tool
}
WrappingTool is implemented by a decorator that stands in for another tool. Optional capabilities are resolved through this chain, so a decorator states once that it wraps a tool instead of re-implementing every optional interface the inner tool may acquire.