tool

package
v0.53.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

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.

Failure owns a complete unsuccessful invocation outcome. CallError instead carries observed evidence through an ordinary error without declaring an outcome or retry policy; its evidence is not a model-visible ToolResult.

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

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrDuplicateTool   = errors.New("tool: duplicate tool")
	ErrInvalidRegistry = errors.New("tool: invalid registry")
)
View Source
var (
	ErrInvalidTool       = errors.New("tool: invalid tool")
	ErrInvalidInvocation = errors.New("tool: invalid invocation")
)
View Source
var ErrInvalidCallError = errors.New("tool: invalid call error")
View Source
var ErrInvalidFailure = errors.New("tool: invalid failure")
View Source
var ErrInvalidWrappingChain = errors.New("tool: invalid wrapping chain")

Functions

func Capability

func Capability[T any](value Tool) (capability T, found bool, err error)

Capability finds T on value or through its wrapping chain. The outermost implementation wins. Malformed chains are returned as errors.

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)

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 Bind

func Bind(executable Tool) (Binding, error)

Bind freezes and validates executable. Definition is read exactly once.

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.

func (Binding) Contract added in v0.19.0

func (b Binding) Contract() Contract

Contract returns the exact frozen contract used to prepare this Binding's invocations. Retaining it does not retain the executable Tool.

type CallError added in v0.38.0

type CallError struct {
	// contains filtered or unexported fields
}

CallError preserves evidence that would otherwise be lost when a Tool returns an error. Evidence is available for diagnostics and evaluation; it is not a completed result and must not be submitted to a model as a ToolResult.

CallError assigns no outcome or control meaning. Its transparent cause retains the original error contract; only Failure establishes a definite unsuccessful outcome. Runtimes must not infer retry safety from the presence of evidence.

func NewCallError added in v0.38.0

func NewCallError(config CallErrorConfig) (*CallError, error)

func (*CallError) Error added in v0.38.0

func (c *CallError) Error() string

func (*CallError) Evidence added in v0.38.0

func (c *CallError) Evidence() chat.ToolOutput

Evidence returns an independent snapshot of observations made before the error. Missing observations do not prove that effects did not occur.

func (*CallError) Unwrap added in v0.38.0

func (c *CallError) Unwrap() error

func (*CallError) Validate added in v0.38.0

func (c *CallError) Validate() error

type CallErrorConfig added in v0.38.0

type CallErrorConfig struct {
	Cause    error
	Evidence chat.ToolOutput
}

CallErrorConfig attaches observed execution evidence to an error. Evidence may acknowledge effects without establishing the invocation's final outcome.

type Contract added in v0.19.0

type Contract struct {
	// contains filtered or unexported fields
}

Contract is the immutable trust boundary from an untrusted chat.ToolCall to an Invocation. It retains the frozen definition, compiled schema, and input validator without retaining an executable Tool. It is safe for concurrent use independently of the Tool.

func (Contract) Definition added in v0.19.0

func (c Contract) Definition() chat.ToolDefinition

Definition projects an independent snapshot of the frozen definition from its single schema owner. InputSchema is the compiled Schema's canonical JSON, so the exposed definition and the enforced contract never disagree on bytes.

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, including CallError with execution evidence, 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

func (f *Failure) Cause() error

Cause does not participate in errors.Is or errors.As. Transparent error wrapping surrounds Failure; diagnostic causes are behind the outcome boundary.

func (*Failure) Error added in v0.17.0

func (f *Failure) Error() string

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.

func (*Failure) Validate added in v0.23.0

func (f *Failure) Validate() error

Validate rejects zero and typed-nil values crossing a Tool boundary.

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 is immutable and as safe for concurrent calls as the wrapped function.

A Func validates arguments by strictly decoding them into In, not against a retained compiled schema, so it owns only the canonical input-schema JSON it projects into its Definition. Bind compiles that JSON when it needs schema-level admission for the Tool contract.

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 checks cancellation before entering the application function. Once dispatched, that function owns the outcome, including acknowledged effects; cancellation never replaces its completed result. Successful output must satisfy the canonical ToolOutput contract.

func (Func[In, Out]) Definition

func (f Func[In, Out]) Definition() chat.ToolDefinition

func (Func[In, Out]) InputValidator added in v0.19.0

func (Func[In, Out]) InputValidator() func([]byte) error

InputValidator retains only the input type's decoding behavior. It captures neither this Func nor its application function.

type FuncConfig

type FuncConfig struct {
	Name        string
	Description string
}

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 tool definition so later changes cannot widen an approved contract. It validates the protocol shape; Bind compiles the schema and Contract.Prepare validates invocations.

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

func (Guard) Unwrap

func (g Guard) Unwrap() Tool

type GuardConfig

type GuardConfig struct {
	Tool       Tool
	Authorizer Authorizer
}

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 InputValidationPanicError added in v0.32.0

type InputValidationPanicError struct{ Value any }

InputValidationPanicError identifies a contained panic while declaring or running input validation. Prepare still rejects the invocation with ErrInvalidInvocation; this diagnostic distinguishes a validator defect from invalid caller input. Value retains the recovered value; an error value remains available through Unwrap.

func (*InputValidationPanicError) Error added in v0.32.0

func (i *InputValidationPanicError) Error() string

func (*InputValidationPanicError) Unwrap added in v0.32.0

func (i *InputValidationPanicError) Unwrap() error

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 concurrency-safe with a usable zero value. Registration is atomic for the full batch and snapshots definitions; model-visible views are detached and ordered by name.

func NewRegistry

func NewRegistry(initial ...Tool) (*Registry, error)

func (*Registry) Definitions

func (r *Registry) Definitions() []chat.ToolDefinition

func (*Registry) Register

func (r *Registry) Register(values ...Tool) error

func (*Registry) Resolve

func (r *Registry) Resolve(name string) (Binding, bool)

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. Use
	// [CallError] for non-final execution evidence without asserting an outcome.
	Call(ctx context.Context, invocation Invocation) (chat.ToolOutput, error)
}

Tool executes invocations admitted by its exact frozen Contract. Errors carry no retry, pause, or abort policy; the driving runtime owns those decisions.

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 exposes the decorator chain used to resolve optional capabilities.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL