Help us shape the future of Go! Take the 2026 Go Developer Survey

invoker

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package invoker defines packtrail's agnostic node-invocation contract. The engine never speaks a wire protocol directly: every task/branch node is executed through an Invoker. This is the single seam that makes packtrail reusable beyond any one ecosystem — a project plugs in its own Invoker (an agent caller, an HTTP client, a NATS request/reply worker) and inherits all of packtrail's durability, retries, fan-in policies, choice routing, signals and timers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

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

Cache is an Invoker decorator that makes invocations idempotent under packtrail's at-least-once delivery. Every step is driven by a durable work message; if an engine crashes after invoking a node but before persisting the advance/ack, the work item is redelivered and the node would otherwise run twice — double side effects (a re-billed LLM call, a duplicate write, a second e-mail).

Cache keys a stored Result by (execution, node, generation, attempt). A redelivery of the same attempt is served from the cache and never reaches the delegate, while a genuine retry (a new attempt number), Resume, or legal cycle revisit gets a fresh key and does re-run. Transport errors are never cached, so a failed call is always retried.

Cache solves the engine-side double-dispatch window. It cannot make a non-deterministic task deterministic; an Invoker with external side effects it cannot see should still carry its own idempotency key where it can.

func NewCache

func NewCache(kv jetstream.KeyValue, delegate Invoker) *Cache

NewCache wraps delegate so its results are deduplicated through kv.

func NewCacheKeyed added in v0.1.0

func NewCacheKeyed(kv jetstream.KeyValue, delegate Invoker, prefix string) *Cache

NewCacheKeyed wraps delegate like NewCache but namespaces every key with prefix. Two Cache layers sharing one bucket must not collide on the same (execution, node, generation, attempt): packtrail's engine-side dispatch cache stores StatusPending for an async node under that tuple, while the async worker's execution cache stores the real result of the same tuple — a shared key would either freeze the node at Pending or clobber the dispatch dedup.

func (*Cache) Invoke

func (c *Cache) Invoke(ctx context.Context, req Request) (Result, error)

Invoke returns a cached Result for this (execution, node, attempt) if present; otherwise it calls the delegate and caches a non-error result before returning it.

type Func

type Func func(ctx context.Context, req Request) (Result, error)

Func adapts a plain function to the Invoker interface.

func (Func) Invoke

func (f Func) Invoke(ctx context.Context, req Request) (Result, error)

Invoke implements Invoker.

type Invoker

type Invoker interface {
	Invoke(ctx context.Context, req Request) (Result, error)
}

Invoker executes a single node invocation. Implementations must be safe for concurrent use: the engine invokes many nodes in parallel.

type Registry

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

Registry dispatches an invocation to a registered Invoker by kind (Request.Invoker). It is itself an Invoker, so the engine holds exactly one Invoker regardless of how many kinds are configured.

The kind map is not synchronized: all Register calls must complete during setup (before Server.Run starts concurrent Invoke dispatch). Registering a kind while invocations are in flight is a data race — the Registry is build-once, read-only-thereafter.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty Registry.

func (*Registry) Has

func (r *Registry) Has(kind string) bool

Has reports whether kind is registered.

func (*Registry) Invoke

func (r *Registry) Invoke(ctx context.Context, req Request) (Result, error)

Invoke routes req to the Invoker registered under req.Invoker.

func (*Registry) Register

func (r *Registry) Register(kind string, inv Invoker) error

Register binds kind to inv. It rejects nil invokers and duplicate kinds so a registration mistake cannot silently replace the invoker that will execute later work. Use Replace when replacement is intentional.

func (*Registry) Replace added in v0.1.0

func (r *Registry) Replace(kind string, inv Invoker) error

Replace binds kind to inv, replacing any previous binding for kind.

type Request

type Request struct {
	Invoker     string          `json:"invoker"`      // invoker kind selected for this node
	Target      string          `json:"target"`       // invoker-specific target (resolved)
	ExecutionID string          `json:"execution_id"` // owning execution
	NodeID      string          `json:"node_id"`      // node being executed
	Payload     json.RawMessage `json:"payload"`      // shared execution context
	Generation  uint64          `json:"generation"`   // execution-scoped visit generation for this node
	Attempt     int             `json:"attempt"`      // 0-based attempt number
	Deadline    time.Time       `json:"deadline"`     // hard deadline for this attempt
}

Request is everything an Invoker needs to execute one node invocation. It is transport-agnostic: Target is interpreted by the chosen Invoker (a subject, an agent name, a URL, …) and any {execution_id} placeholder is already resolved.

type Result

type Result struct {
	Status  Status          `json:"status"`
	Payload json.RawMessage `json:"payload,omitempty"`
	Error   string          `json:"error,omitempty"`
}

Result is what an Invoker returns. A non-nil error from Invoke is treated as a transient transport failure (equivalent to StatusRetry) and is never cached.

type Status

type Status string

Status is the outcome an Invoker reports for a single invocation. The string values intentionally match the built-in nats-task wire contract so transports can map onto them directly.

const (
	// StatusOK means the node succeeded; Result.Payload is the new shared context.
	StatusOK Status = "ok"
	// StatusError means the node failed permanently; the engine does not retry.
	StatusError Status = "error"
	// StatusRetry means the node asks to be retried per its node retry policy.
	StatusRetry Status = "retry"
	// StatusPending means the node was dispatched asynchronously and will be
	// settled later via Engine.CompleteActivityWithGeneration. The engine parks
	// the execution (waiting) and frees its work slot; Result.Payload is ignored.
	// Use this for long-running activities (e.g. an agent call) so the engine does
	// not block.
	StatusPending Status = "pending"
)

Directories

Path Synopsis
Package asyncqueue is packtrail's durable asynchronous Invoker: it makes any ordinary synchronous Invoker run off the engine's critical path with at-least-once delivery.
Package asyncqueue is packtrail's durable asynchronous Invoker: it makes any ordinary synchronous Invoker run off the engine's critical path with at-least-once delivery.
Package natstask is packtrail's built-in Invoker: a NATS request/reply caller that speaks the pkg/protocol envelope.
Package natstask is packtrail's built-in Invoker: a NATS request/reply caller that speaks the pkg/protocol envelope.

Jump to

Keyboard shortcuts

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