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.
Invocation carries explicitly supplied continuation input. Result completes a call or requests external input; suspension never travels through context or an error. The driving runtime owns call identity, checkpoint commitment, input waits, and routing each answer back to the requesting call. Func adapts ordinary typed functions that always complete; resumable Tools implement Tool directly.
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/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(tool.Request{Name: "add", Arguments: `{"a":2,"b":3}`}, nil)
if err != nil {
panic(err)
}
result, err := binding.Call(context.Background(), invocation)
if err != nil {
panic(err)
}
text, _ := result.Output.Text()
fmt.Println(text)
}
Output: add 5
Index ¶
- Constants
- Variables
- func Capability[T any](value Tool) (capability T, found bool, err error)
- type Authorization
- type AuthorizationError
- type Authorizer
- type AuthorizerFunc
- type Binding
- type CallError
- type CallErrorConfig
- type Continuation
- type Contract
- type Definition
- type Failure
- type FailureConfig
- type FailureKind
- type Func
- type FuncConfig
- type Guard
- type GuardConfig
- type InputRequest
- func (i InputRequest) Continuation(response json.RawMessage) (Continuation, error)
- func (InputRequest) JSONSchemaModel() any
- func (i InputRequest) MarshalJSON() ([]byte, error)
- func (i InputRequest) Prompt() json.RawMessage
- func (i InputRequest) ResponseSchema() jsonschema.Schema
- func (i InputRequest) State() json.RawMessage
- func (i *InputRequest) UnmarshalJSON(data []byte) error
- func (i InputRequest) Valid() bool
- type InputRequestConfig
- type InputValidatingTool
- type InputValidationPanicError
- type Invocation
- type Output
- type Registry
- type Request
- type Result
- type Tool
- type WrappingTool
Examples ¶
Constants ¶
const MaxInputBytes = 1 << 20
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 ErrInvalidCallError = errors.New("tool: invalid call error")
var ErrInvalidDefinition = errors.New("tool: invalid definition")
var ErrInvalidFailure = errors.New("tool: invalid failure")
var ErrInvalidInputRequest = errors.New("tool: invalid input request")
var ErrInvalidOutput = errors.New("tool: invalid output")
var ErrInvalidRequest = errors.New("tool: invalid request")
var ErrInvalidResult = errors.New("tool: invalid result")
var ErrInvalidWrappingChain = errors.New("tool: invalid wrapping chain")
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() Definition
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.
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)
type CallErrorConfig ¶ added in v0.38.0
CallErrorConfig attaches observed execution evidence to an error. Evidence may acknowledge effects without establishing the invocation's final outcome.
type Continuation ¶ added in v0.54.0
type Continuation struct {
// contains filtered or unexported fields
}
Continuation is the immutable checkpoint and validated answer for one logical call. Passing a context does not pass this business input.
func (Continuation) Response ¶ added in v0.54.0
func (c Continuation) Response() json.RawMessage
func (Continuation) State ¶ added in v0.54.0
func (c Continuation) State() json.RawMessage
type Contract ¶ added in v0.19.0
type Contract struct {
// contains filtered or unexported fields
}
Contract is the immutable trust boundary from an untrusted Request 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() Definition
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) Name ¶ added in v0.59.0
Name identifies the frozen execution target without copying its schema.
func (Contract) Prepare ¶ added in v0.19.0
func (c Contract) Prepare(call Request, continuation *Continuation) (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. A nil continuation starts a new logical call. A driving runtime may pass an answer only for the same call that produced its committed InputRequest.
type Definition ¶ added in v0.59.0
type Definition struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
InputSchema json.RawMessage `json:"input_schema"`
}
Definition describes a tool independently of any model naming or transport convention.
func (Definition) Clone ¶ added in v0.59.0
func (d Definition) Clone() Definition
func (Definition) JSONSchemaModel ¶ added in v0.59.0
func (Definition) JSONSchemaModel() any
func (Definition) MarshalJSON ¶ added in v0.59.0
func (d Definition) MarshalJSON() ([]byte, error)
func (*Definition) UnmarshalJSON ¶ added in v0.59.0
func (d *Definition) UnmarshalJSON(data []byte) error
func (Definition) Validate ¶ added in v0.59.0
func (d Definition) Validate() error
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 ClassifyFailure ¶ added in v0.58.0
ClassifyFailure finds the complete outcome of the current invocation. Transparent wrapping preserves one Failure, but joined independent errors require their enclosing owner to establish an outcome. Diagnostic traversal with errors.As cannot distinguish those invocation boundaries. Nil, invalid, and ambiguous failures establish no 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/tool"
)
func main() {
failure, err := tool.NewFailure(tool.FailureConfig{
Kind: tool.FailureKindRejected, Cause: errors.New("internal authorization diagnostic"),
Output: tool.NewTextOutput("this operation is not permitted"),
})
if err != nil {
panic(err)
}
wrapped := fmt.Errorf("tool call: %w", failure)
result, found := tool.ClassifyFailure(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
type FailureConfig ¶ added in v0.23.0
type FailureConfig struct {
Kind FailureKind
Output Output
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 ¶
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() Definition
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 ¶
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"
"fmt"
"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(tool.Request{Name: "inspect", Arguments: `{}`}, nil)
if err != nil {
panic(err)
}
_, err = binding.Call(context.Background(), invocation)
failure, found := tool.ClassifyFailure(err)
if !found {
panic(err)
}
fmt.Println(failure.Kind())
}
Output: rejected
func (Guard) Definition ¶
func (g Guard) Definition() Definition
type GuardConfig ¶
type GuardConfig struct {
Tool Tool
Authorizer Authorizer
}
type InputRequest ¶ added in v0.54.0
type InputRequest struct {
// contains filtered or unexported fields
}
InputRequest freezes the prompt, answer contract, and Tool-owned checkpoint. The driving runtime binds it to one logical call and owns its wait identity.
func NewInputRequest ¶ added in v0.54.0
func NewInputRequest(config InputRequestConfig) (InputRequest, error)
NewInputRequest validates and freezes a re-entry checkpoint. A Tool must request input before external effects, or retain enough state for safe re-entry.
Example ¶
package main
import (
"fmt"
"github.com/Tangerg/scope/core/jsonschema"
"github.com/Tangerg/scope/core/tool"
)
func main() {
schema, err := jsonschema.Parse([]byte(`{"type":"boolean"}`))
if err != nil {
panic(err)
}
request, err := tool.NewInputRequest(tool.InputRequestConfig{
Prompt: []byte(`"Approve the write?"`), ResponseSchema: schema, ContinuationState: []byte(`{"record":"draft"}`),
})
if err != nil {
panic(err)
}
result := tool.Result{InputRequest: &request}
continuation, err := result.InputRequest.Continuation([]byte(`true`))
if err != nil {
panic(err)
}
// The runtime persists the checkpoint and routes this answer to the same
// call through binding.Contract().Prepare(call, &continuation).
fmt.Println(string(continuation.State()), string(continuation.Response()))
}
Output: {"record":"draft"} true
func (InputRequest) Continuation ¶ added in v0.54.0
func (i InputRequest) Continuation(response json.RawMessage) (Continuation, error)
Continuation validates an answer against this exact request and freezes the state and answer to pass explicitly to Contract.Prepare for the same call.
func (InputRequest) JSONSchemaModel ¶ added in v0.57.0
func (InputRequest) JSONSchemaModel() any
func (InputRequest) MarshalJSON ¶ added in v0.54.0
func (i InputRequest) MarshalJSON() ([]byte, error)
func (InputRequest) Prompt ¶ added in v0.54.0
func (i InputRequest) Prompt() json.RawMessage
func (InputRequest) ResponseSchema ¶ added in v0.54.0
func (i InputRequest) ResponseSchema() jsonschema.Schema
func (InputRequest) State ¶ added in v0.54.0
func (i InputRequest) State() json.RawMessage
func (*InputRequest) UnmarshalJSON ¶ added in v0.54.0
func (i *InputRequest) UnmarshalJSON(data []byte) error
func (InputRequest) Valid ¶ added in v0.54.0
func (i InputRequest) Valid() bool
type InputRequestConfig ¶ added in v0.54.0
type InputRequestConfig struct {
Prompt json.RawMessage
ResponseSchema jsonschema.Schema
ContinuationState json.RawMessage
}
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.
func (Invocation) Continuation ¶ added in v0.54.0
func (i Invocation) Continuation() (Continuation, bool)
Continuation returns input supplied explicitly for this invocation. Prepare with nil always starts a new call, regardless of values inherited in ctx.
type Output ¶ added in v0.59.0
type Output struct {
Content []corecontent.Block `json:"content,omitempty"`
Details json.RawMessage `json:"details,omitzero"`
}
Output is the provider-neutral value produced by a Tool. Content is the model-visible ordered text/media representation. Details is optional JSON for structured consumers; providers use its encoded JSON as the model-visible fallback only when Content is empty.
func NewJSONOutput ¶ added in v0.59.0
func NewJSONOutput(value json.RawMessage) (Output, error)
NewJSONOutput returns a structured output whose exact JSON encoding is preserved. The value must be one complete RFC 7493 JSON document.
func NewTextOutput ¶ added in v0.59.0
NewTextOutput returns a text output. Empty text is represented by the valid zero Output rather than an invalid empty text Part.
func (Output) JSONSchemaModel ¶ added in v0.59.0
func (Output) MarshalJSON ¶ added in v0.59.0
func (Output) Text ¶ added in v0.59.0
Text returns the lossless text projection used by providers whose tool result protocol accepts only strings. It reports false when Content contains media so adapters cannot silently discard it. Details is encoded only when Content is empty.
func (*Output) UnmarshalJSON ¶ added in v0.59.0
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 (*Registry) Definitions ¶
func (r *Registry) Definitions() []Definition
type Request ¶ added in v0.59.0
Request is an untrusted proposal. A provider correlation ID belongs to its transport; execution requires admission by the exact frozen Contract.
type Result ¶ added in v0.54.0
type Result struct {
Output Output
InputRequest *InputRequest
}
Result is either a completed output or a request for external input. An input request carries no completed output. Errors describe execution failure, never suspension; the runtime alone owns committing and resuming a logical call. The zero result completes with an empty output.
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() Definition
// 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.
// A successful Result completes or requests input. On error, the returned
// result 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) (Result, 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.