agui

package
v0.2.10 Latest Latest
Warning

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

Go to latest
Published: Dec 21, 2025 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package agui provides utilities for integrating gains with the AG-UI protocol.

AG-UI (Agent-User Interface) is an open, lightweight, event-based protocol that standardizes how AI agents connect to user-facing applications. This package provides mapping utilities to convert gains events to AG-UI events, enabling easy integration with AG-UI-compatible frontends.

Overview

This package provides:

The package does NOT provide HTTP handlers or transport implementations. Users are responsible for implementing their own server using the AG-UI SDK's SSE writer or their preferred transport mechanism.

Usage

Create a Mapper for each run and use it to convert gains events:

// Create mapper for this run
mapper := agui.NewMapper(threadID, runID)

// Emit run started
writeEvent(mapper.RunStarted())

// Run agent and map events
for event := range myAgent.RunStream(ctx, messages) {
    aguiEvent := mapper.MapEvent(event)
    if aguiEvent != nil {
        writeEvent(aguiEvent)
    }
}

// Emit run finished
writeEvent(mapper.RunFinished())

Event Mapping

The Mapper tracks state to properly emit AG-UI's Start-Content-End sequences:

  • event.MessageDelta → TEXT_MESSAGE_START (on first delta), TEXT_MESSAGE_CONTENT
  • event.StepEnd → TEXT_MESSAGE_END (if message active), STEP_FINISHED
  • event.ToolCallStart → TOOL_CALL_START, TOOL_CALL_ARGS
  • event.ToolCallResult → TOOL_CALL_END, TOOL_CALL_RESULT

Message Conversion

Use ToGainsMessages to convert AG-UI messages to gains messages for input:

messages := agui.ToGainsMessages(aguiMessages)
result := agent.Run(ctx, messages)

Use FromGainsMessages to convert gains messages to AG-UI format for snapshots:

snapshot := events.NewMessagesSnapshotEvent(agui.FromGainsMessages(history))

Shared State

AG-UI supports bidirectional state synchronization between agents and frontends. The frontend can send state with each run via RunAgentInput.State, and agents can emit state updates via STATE_SNAPSHOT and STATE_DELTA events.

Reading frontend state:

type MyState struct {
    Progress int      `json:"progress"`
    Items    []string `json:"items"`
}

prepared, _ := input.Prepare()
state, err := agui.DecodeState[MyState](prepared)
// or: state := agui.MustDecodeState[MyState](prepared)

Emitting state to frontend:

// Full state snapshot
writeEvent(mapper.StateSnapshot(map[string]any{
    "progress": 50,
    "items": []string{"a", "b"},
}))

// Incremental delta (JSON Patch RFC 6902)
writeEvent(mapper.StateDelta(
    event.Replace("/progress", 75),
    event.Add("/items/-", "c"),
))

Or via gains events for integration with RunStream:

events <- event.NewStateSnapshot(state)
events <- event.NewStateDelta(
    event.Replace("/progress", 100),
)

Thread Safety

The Mapper is NOT safe for concurrent use. Each goroutine should have its own Mapper instance. Message conversion functions are stateless and safe for concurrent use.

Index

Constants

View Source
const (
	// CustomEventRouteSelected is emitted when a route is chosen in a Router step.
	// Value contains: stepName (string), routeName (string)
	CustomEventRouteSelected = "gains.route_selected"

	// CustomEventLoopIteration is emitted at the start of each loop iteration.
	// Value contains: stepName (string), iteration (int)
	CustomEventLoopIteration = "gains.loop_iteration"
)

Custom event names for gains-specific workflow events. These are emitted as AG-UI CUSTOM events with a name and value.

View Source
const (
	RoleUser      = "user"
	RoleAssistant = "assistant"
	RoleSystem    = "system"
	RoleTool      = "tool"
)

Role constants matching AG-UI protocol.

Variables

View Source
var ErrNoMessages = errors.New("no messages provided")

ErrNoMessages is returned when the input contains no messages.

View Source
var ErrNoWorkflowName = errors.New("no workflow name provided")

ErrNoWorkflowName is returned when the workflow name is empty.

Functions

func DecodeState

func DecodeState[T any](input *PreparedInput) (T, error)

DecodeState decodes the raw state into a typed struct. Returns the zero value of T if State is nil.

func DecodeWorkflowState added in v0.2.9

func DecodeWorkflowState[T any](input *PreparedWorkflowInput) (T, error)

DecodeWorkflowState decodes the raw state into a typed struct. Returns the zero value of T if State is nil.

func FromGainsMessage

func FromGainsMessage(msg ai.Message, index int) events.Message

FromGainsMessage converts a single gains message to an AG-UI message. The index is used to generate a message ID if needed.

func FromGainsMessages

func FromGainsMessages(msgs []ai.Message) []events.Message

FromGainsMessages converts gains messages to AG-UI messages.

func HandleApproval added in v0.2.9

func HandleApproval(broker *agent.ApprovalBroker, input *ApprovalInput) error

HandleApproval processes an approval input and sends it to the broker. This is a convenience function for AG-UI server handlers.

func HandleApprovalJSON added in v0.2.9

func HandleApprovalJSON(broker *agent.ApprovalBroker, data []byte) error

HandleApprovalJSON processes a JSON-encoded approval input.

func HandleUserInput added in v0.2.9

func HandleUserInput(broker *agent.UserInputBroker, input *UserInputInput) error

HandleUserInput processes a user input and sends it to the broker. This is a convenience function for AG-UI server handlers.

func HandleUserInputJSON added in v0.2.9

func HandleUserInputJSON(broker *agent.UserInputBroker, data []byte) error

HandleUserInputJSON processes a JSON-encoded user input response.

func InitializeState added in v0.2.9

func InitializeState[T any](input *PreparedInput) (*T, error)

InitializeState creates a new state struct initialized from frontend state. This is the recommended way to create workflow state from AG-UI input:

input, err := runAgentInput.Prepare()
state, err := agui.InitializeState[MyState](input)
result, err := workflow.Run(ctx, state, opts...)

func MergeState added in v0.2.9

func MergeState[T any](state *T, input *PreparedInput) error

MergeState merges frontend state into an existing state struct. Fields from the frontend state overwrite corresponding fields in state. This is useful when you have a pre-populated state with defaults:

state := &MyState{DefaultField: "value"}
agui.MergeState(state, input) // Overwrites with frontend values

func MustDecodeState

func MustDecodeState[T any](input *PreparedInput) T

MustDecodeState is like DecodeState but panics on error.

func MustDecodeWorkflowState added in v0.2.9

func MustDecodeWorkflowState[T any](input *PreparedWorkflowInput) T

MustDecodeWorkflowState is like DecodeWorkflowState but panics on error.

func MustInitializeState added in v0.2.9

func MustInitializeState[T any](input *PreparedInput) *T

MustInitializeState is like InitializeState but panics on error.

func MustMergeState added in v0.2.9

func MustMergeState[T any](state *T, input *PreparedInput)

MustMergeState is like MergeState but panics on error.

func ToGainsMessage

func ToGainsMessage(msg events.Message) ai.Message

ToGainsMessage converts a single AG-UI message to a gains message.

func ToGainsMessages

func ToGainsMessages(msgs []events.Message) []ai.Message

ToGainsMessages converts AG-UI messages to gains messages.

func ToGainsTools

func ToGainsTools(tools []Tool) []ai.Tool

ToGainsTools converts a slice of AG-UI tools to gains tools.

func ToolNames

func ToolNames(tools []Tool) []string

ToolNames extracts the names from a slice of tools.

Types

type ApprovalInput added in v0.2.9

type ApprovalInput struct {
	ToolCallID string `json:"toolCallId"`
	Approved   bool   `json:"approved"`
	Reason     string `json:"reason,omitempty"`
}

ApprovalInput represents an approval decision from the AG-UI frontend. This corresponds to a user action on a tool_approval activity.

func ParseApprovalInput added in v0.2.9

func ParseApprovalInput(data []byte) (*ApprovalInput, error)

ParseApprovalInput parses an approval decision from JSON.

func (*ApprovalInput) ToDecision added in v0.2.9

func (a *ApprovalInput) ToDecision() agent.ApprovalDecision

ToDecision converts an ApprovalInput to an agent.ApprovalDecision.

type Mapper

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

Mapper converts gains events to AG-UI events. With the unified event system, this is now a true 1:1 mapping - each gains event maps to exactly one AG-UI event.

The mapper tracks run depth to handle nested RunStart/RunEnd events (e.g., from workflows containing agents or sub-agents). Only the outermost run lifecycle events are mapped to AG-UI RUN_STARTED/RUN_FINISHED.

Create a new Mapper for each run using NewMapper. The Mapper is not safe for concurrent use - each goroutine should have its own Mapper.

func NewMapper

func NewMapper(threadID, runID string, opts ...MapperOption) *Mapper

NewMapper creates a new Mapper for a single run. The threadID and runID are used in lifecycle events (RUN_STARTED, RUN_FINISHED). Use WithInitialState to emit an initial STATE_SNAPSHOT after RUN_STARTED.

func (*Mapper) ActivityDelta added in v0.2.9

func (m *Mapper) ActivityDelta(activityID string, activityType event.ActivityType, patches []event.JSONPatch) events.Event

ActivityDelta returns an ACTIVITY_DELTA event.

func (*Mapper) ActivitySnapshot added in v0.2.9

func (m *Mapper) ActivitySnapshot(activityID string, activityType event.ActivityType, content any) events.Event

ActivitySnapshot returns an ACTIVITY_SNAPSHOT event.

func (*Mapper) MapEvent

func (m *Mapper) MapEvent(e event.Event) events.Event

MapEvent converts a unified gains event to an AG-UI event. This is a true 1:1 mapping - each gains event maps to exactly one AG-UI event. Returns nil for events that have no AG-UI equivalent.

For nested runs (e.g., workflows containing agents), only the outermost RunStart/RunEnd events are mapped to AG-UI lifecycle events.

func (*Mapper) MapStream

func (m *Mapper) MapStream(input <-chan event.Event) <-chan events.Event

MapStream wraps a gains event channel and yields AG-UI events. Events that have no AG-UI equivalent (returning nil from MapEvent) are filtered out. The returned channel closes when the input channel closes.

If the mapper was created with WithInitialState, a STATE_SNAPSHOT event is automatically emitted after the first RUN_STARTED event.

func (*Mapper) MessagesSnapshot added in v0.2.9

func (m *Mapper) MessagesSnapshot(messages []ai.Message) events.Event

MessagesSnapshot returns a MESSAGES_SNAPSHOT event with the given messages.

func (*Mapper) RunDepth added in v0.2.9

func (m *Mapper) RunDepth() int

RunDepth returns the current nesting depth of runs. Returns 0 when no runs are active, 1 during a top-level run, etc.

func (*Mapper) RunError

func (m *Mapper) RunError(err error) events.Event

RunError returns a RUN_ERROR event.

func (*Mapper) RunFinished

func (m *Mapper) RunFinished() events.Event

RunFinished returns a RUN_FINISHED event.

func (*Mapper) RunID

func (m *Mapper) RunID() string

RunID returns the run ID for this mapper.

func (*Mapper) RunStarted

func (m *Mapper) RunStarted() events.Event

RunStarted returns a RUN_STARTED event.

func (*Mapper) StateDelta

func (m *Mapper) StateDelta(patches ...event.JSONPatch) events.Event

StateDelta returns a STATE_DELTA event with the given JSON Patch operations.

func (*Mapper) StateSnapshot

func (m *Mapper) StateSnapshot(state any) events.Event

StateSnapshot returns a STATE_SNAPSHOT event with the given state.

func (*Mapper) ThreadID

func (m *Mapper) ThreadID() string

ThreadID returns the thread ID for this mapper.

type MapperOption added in v0.2.9

type MapperOption func(*Mapper)

MapperOption configures a Mapper.

func WithInitialState added in v0.2.9

func WithInitialState(state any) MapperOption

WithInitialState configures the mapper to emit a STATE_SNAPSHOT event with the given state immediately after RUN_STARTED. This ensures the frontend has the initial state for shared state synchronization.

type PreparedInput

type PreparedInput struct {
	ThreadID  string
	RunID     string
	Messages  []ai.Message
	Tools     []Tool   // Parsed frontend tools
	ToolNames []string // Tool names for cleanup tracking
	State     any      // Raw state from frontend
}

PreparedInput contains validated and converted input ready for agent execution.

func (*PreparedInput) GainsTools

func (p *PreparedInput) GainsTools() []ai.Tool

GainsTools converts the parsed frontend tools to gains tools. Returns nil if no tools were parsed.

type PreparedWorkflowInput added in v0.2.9

type PreparedWorkflowInput struct {
	ThreadID     string
	RunID        string
	WorkflowName string
	State        any // Raw state for workflow initialization
}

PreparedWorkflowInput contains validated workflow input ready for execution.

type RunAgentInput

type RunAgentInput struct {
	ThreadID       string           `json:"thread_id"`
	RunID          string           `json:"run_id"`
	Messages       []events.Message `json:"messages"`
	Tools          []any            `json:"tools,omitempty"`           // Frontend-provided tools
	Context        []any            `json:"context,omitempty"`         // Context items
	State          any              `json:"state,omitempty"`           // State
	ForwardedProps any              `json:"forwarded_props,omitempty"` // Forwarded props
}

RunAgentInput represents the AG-UI protocol request for running an agent. This mirrors the AG-UI protocol specification and is transport-agnostic.

func (*RunAgentInput) Prepare

func (r *RunAgentInput) Prepare() (*PreparedInput, error)

Prepare validates the input and converts it to gains types. Returns ErrNoMessages if Messages is empty. Returns an error if tool parsing fails.

type RunWorkflowInput added in v0.2.9

type RunWorkflowInput struct {
	ThreadID       string `json:"thread_id"`
	RunID          string `json:"run_id"`
	WorkflowName   string `json:"workflow_name"`   // Name of workflow to execute
	State          any    `json:"state,omitempty"` // Initial workflow state
	ForwardedProps any    `json:"forwarded_props,omitempty"`
}

RunWorkflowInput represents the AG-UI protocol request for running a workflow. This is designed for dispatching workflows by name with typed state.

func (*RunWorkflowInput) Prepare added in v0.2.9

func (r *RunWorkflowInput) Prepare() (*PreparedWorkflowInput, error)

Prepare validates the workflow input. Returns ErrNoWorkflowName if WorkflowName is empty.

type Tool

type Tool struct {
	Name        string          `json:"name"`
	Description string          `json:"description"`
	Parameters  json.RawMessage `json:"parameters,omitempty"`
}

Tool represents a tool definition from the AG-UI protocol. Frontend applications send these to define capabilities available to agents.

func ParseTools

func ParseTools(raw []any) ([]Tool, error)

ParseTools parses a slice of any (from JSON unmarshaling) into Tool structs. This handles the Tools field from RunAgentInput which is []any.

func (Tool) ToGainsTool

func (t Tool) ToGainsTool() ai.Tool

ToGainsTool converts an AG-UI tool to a gains Tool.

type UserInputInput added in v0.2.9

type UserInputInput struct {
	RequestID string `json:"requestId"`
	Value     string `json:"value,omitempty"`
	Confirmed bool   `json:"confirmed,omitempty"`
	Cancelled bool   `json:"cancelled,omitempty"`
}

UserInputInput represents a user input response from the AG-UI frontend. This corresponds to a user action on a user_input activity.

func ParseUserInputInput added in v0.2.9

func ParseUserInputInput(data []byte) (*UserInputInput, error)

ParseUserInputInput parses a user input response from JSON.

func (*UserInputInput) ToResponse added in v0.2.9

func (u *UserInputInput) ToResponse() agent.UserInputResponse

ToResponse converts a UserInputInput to an agent.UserInputResponse.

Jump to

Keyboard shortcuts

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