trellis

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jan 24, 2026 License: AGPL-3.0 Imports: 11 Imported by: 2

README

Trellis

Go Report Card Go Doc License Release

"Simplifique o Caos. Construa fluxos determinísticos." - Filosofia Trellis

Trellis é um Motor de Máquina de Estados Determinístico (Deterministic State Machine Engine) projetado para a construção de CLIs, ChatOps resilientes e Guardrails para Agentes de IA (Neuro-Symbolic).

Ele atua como a espinha dorsal lógica do seu sistema: enquanto sua interface (ou LLM) gerencia a conversa, o Trellis impõe estritamente as regras de negócio, o contexto e as transições permitidas.

Visão: O Trellis almeja ser o "Temporal Visual" — uma plataforma de Durable Execution que permite fluxos de longa duração (SAGA) e recuperação de falhas.

Como funciona?

O Trellis define fluxos através de arquivos Markdown (Loam). Texto, Lógica e Dados vivem juntos:

# start.yaml
type: question
save_to: user_name  # Data Binding automático
to: greeting # Transição explícita
content: Olá! Qual é o seu nome?
# greeting.yaml
options: # Transições explícitas
  - text: "Ver Menu"
    to: "menu"
  - text: "Sair"
    to: "exit"
content: Prazer, {{ .user_name }}! O que deseja fazer?

Funcionalidades Principais

  • Data Binding & Contexto: Capture inputs (save_to) e use variáveis ({{ .name }}) nativamente.
  • Namespaces (Sub-Grafos): Organize fluxos complexos em pastas e módulos (jump_to), escalando sua arquitetura.
  • MCP Server: Integração nativa com Model Context Protocol para conectar Agentes de IA (Claude, Cursor, etc.).
  • Strict Typing: Garante que seus fluxos sejam robustos e livres de erros de digitação (Zero "undefined" errors).
  • Embeddable & Agnostic: Use como CLI, Lib ou Service. O Core é desacoplado de IO e Persistência (Hexagonal).
  • Error Handling: Mecanismo nativo de recuperação (on_error) para ferramentas que falham.
  • Native SAGA Support: Orquestração de transações distribuídas com undo e rollback automático.
  • Hot Reload: Desenvolva com feedback instantâneo (SSE) ao salvar seus arquivos.

Quick Start

Instalação

Como o Trellis é tanto uma Library quanto um CLI, recomendamos clonar para ter acesso aos exemplos e ferramentas:

git clone https://github.com/aretw0/trellis
cd trellis
go mod tidy # Sincroniza dependências
Rodando o Golden Path (Demo)
# Execução do Engine (Demo)
go run ./cmd/trellis ./examples/tour

Usage

Rodando um Fluxo (CLI)
# Modo Interativo (Terminal)
go run ./cmd/trellis run ./examples/tour

# Modo HTTP Server (Stateless API)
go run ./cmd/trellis serve --dir ./examples/tour --port 8080
# Swagger UI disponível em: http://localhost:8080/swagger

# Modo MCP Server (Para Agentes de IA)
go run ./cmd/trellis mcp --dir ./examples/tour

# Modo Debug (Observability)
go run ./cmd/trellis run --debug ./examples/observability

# Exemplo Global Signals (Interrupts)
go run ./cmd/trellis run ./examples/interrupt-demo

# Exemplo Tool Safety & Error Handling
go run ./cmd/trellis run ./examples/tools-demo

# Exemplo Log Estruturado (Production Recipe)
go run ./examples/structured-logging
Introspecção

Visualize seu fluxo como um grafo Mermaid:

trellis graph ./my-flow
# Saída: graph TD ...
Modo de Desenvolvimento

Usando Makefile (Recomendado):

make gen    # Gera código Go a partir da spec OpenAPI
make serve  # Roda servidor com exemplo 'tour'
make test   # Roda testes

Hot Reload Manual: Itere mais rápido observando mudanças de arquivo:

trellis run --watch --dir ./my-flow

O engine monitorará seus arquivos .md, .json, .yaml. Ao salvar, a sessão recarrega automaticamente (preservando o loop de execução).

Documentação

Estrutura

trellis/
├── cmd/           # Entrypoints (trellis CLI)
├── docs/          # Documentação do Projeto
├── examples/      # Demos e Receitas (Tours, Patterns)
├── internal/      # Implementação Privada (Runtime, TUI)
├── pkg/           # Contratos Públicos (Facade, Domain, Ports, Adapters)
└── tests/         # Testes de Integração (Certification Suite)

Library vs Framework

O Trellis foi desenhado para ser usado de duas formas:

  1. Como Framework (CLI): Use o executável trellis para rodar pastas de Markdown (loam). Ótimo para scripts rapidos e prototipagem.
  2. Como Biblioteca (Go): Importe github.com/aretw0/trellis e instancie o Engine dentro do seu binário. Você pode injetar grafos em memória, usar banco de dados ou qualquer outra fonte, sem depender de arquivos ou do Loam.

"Opinionated by default (Loam), flexible under the hood (Memory/Custom)."

Documentation

Overview

Package trellis is a state machine engine designed for building text-based adventure games, interactive stories, and complex conversational flows.

It provides a flexible runtime that separates the narrative graph definition from the execution state, enabling rich, logic-driven navigation.

Key Features

  • Graph-based State Machine: Define complex flows with nodes and transitions.
  • Pluggable Loaders: Load graphs from the filesystem (Markdown/Frontmatter) or in-memory structures.
  • Conditional Logic: Dynamic transitions based on custom evaluators.
  • State Management: Serializable state for long-running sessions.

Basic Usage

Initialize the engine seamlessly using the filesystem loader (powered by Loam):

eng, err := trellis.New("./story-data")
if err != nil {
    log.Fatal(err)
}

state := eng.Start()

// Render the initial view
actions, _, err := eng.Render(context.Background(), state)

// Navigate based on input
nextState, err := eng.Navigate(context.Background(), state, "open door")

For advanced usage, including custom loaders or conditional logic, refer to the examples and sub-packages.

Index

Examples

Constants

This section is empty.

Variables

View Source
var Version string

Functions

This section is empty.

Types

type Engine

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

Engine is the high-level entry point for the Trellis library. It wraps the internal runtime and provides a simplified API for consumers.

func New

func New(repoPath string, opts ...Option) (*Engine, error)

New initializes a new Trellis Engine. By default, it uses a Loam repository at the given path. If WithLoader option is provided, repoPath can be empty and Loam is skipped.

Example (Memory)

ExampleNew_memory demonstrates how to use the Engine with an in-memory graph definition. This is useful for testing, embedded scenarios, or when you don't want to rely on the file system.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aretw0/trellis"
	"github.com/aretw0/trellis/pkg/adapters/memory"
	"github.com/aretw0/trellis/pkg/domain"
)

func main() {
	// 1. Define your graph using helper NewFromNodes for clean, type-safe construction.
	loader, err := memory.NewFromNodes(
		domain.Node{
			ID:      "start",
			Type:    "question",
			Content: []byte("Hello! Do you want to proceed? [yes] [no]"),
			Transitions: []domain.Transition{
				{ToNodeID: "yes", Condition: "input == 'yes'"},
				{ToNodeID: "no"},
			},
		},
		domain.Node{
			ID:      "yes",
			Type:    "text",
			Content: []byte("Great! You moved forward."),
		},
		domain.Node{
			ID:      "no",
			Type:    "text",
			Content: []byte("Okay, bye."),
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Initialize Trellis with the custom loader
	// Note: We leave path empty ("") because we are providing a loader.
	engine, err := trellis.New("", trellis.WithLoader(loader))
	if err != nil {
		log.Fatal(err)
	}

	// 4. Start the flow
	ctx := context.Background()
	state, err := engine.Start(ctx, "test", nil)
	if err != nil {
		panic(err)
	}

	// 5. Navigate (Input: "yes")
	// "start" -> (input: yes) -> "yes"
	// Note: Example previously captured actions from Step.
	// Render to show we can get actions, then Navigate.
	actions, _, err := engine.Render(ctx, state)
	if err != nil {
		log.Fatal(err)
	}
	// Verify actions from start node (optional in example, but good for completeness)

	nextState, err := engine.Navigate(ctx, state, "yes")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Current Node: %s\n", nextState.CurrentNodeID)
	for _, action := range actions {
		fmt.Printf("Action: %s\n", action.Type)
	}
}
Output:
Current Node: yes
Action: RENDER_CONTENT
Action: REQUEST_INPUT

func (*Engine) Inspect

func (e *Engine) Inspect() ([]domain.Node, error)

Inspect returns the full graph definition for visualization or introspection tools.

func (*Engine) Loader added in v0.3.3

func (e *Engine) Loader() ports.GraphLoader

Loader returns the underlying GraphLoader used by the engine.

func (*Engine) Navigate added in v0.3.2

func (e *Engine) Navigate(ctx context.Context, state *domain.State, input any) (*domain.State, error)

Navigate determines the next state based on input.

func (*Engine) Render added in v0.3.2

func (e *Engine) Render(ctx context.Context, state *domain.State) ([]domain.ActionRequest, bool, error)

Render generates the actions (view) for the current state without transitioning. Returns actions, isTerminal (true if no transitions), and error.

func (*Engine) Signal added in v0.5.2

func (e *Engine) Signal(ctx context.Context, state *domain.State, signalName string) (*domain.State, error)

Signal triggers a state transition based on a global signal (e.g. interrupt).

func (*Engine) Start

func (e *Engine) Start(ctx context.Context, sessionID string, initialContext map[string]any) (*domain.State, error)

Start creates the initial state for the flow and triggers lifecycle hooks.

func (*Engine) Watch added in v0.3.2

func (e *Engine) Watch(ctx context.Context) (<-chan string, error)

Watch returns a channel that signals when the underlying graph changes. Returns error if the loader does not support watching.

type Option

type Option func(*Engine)

Option defines a functional option for configuring the Engine.

func WithConditionEvaluator

func WithConditionEvaluator(eval runtime.ConditionEvaluator) Option

WithConditionEvaluator sets a custom logic evaluator for the engine.

func WithDefaultErrorNode added in v0.7.0

func WithDefaultErrorNode(nodeID string) Option

WithDefaultErrorNode sets a global fallback node for tool errors.

func WithInterpolator added in v0.5.0

func WithInterpolator(interp runtime.Interpolator) Option

WithInterpolator sets a custom interpolator for the engine.

func WithLifecycleHooks added in v0.5.1

func WithLifecycleHooks(hooks domain.LifecycleHooks) Option

WithLifecycleHooks registers observability hooks.

func WithLoader added in v0.3.1

func WithLoader(l ports.GraphLoader) Option

WithLoader injects a custom GraphLoader, bypassing the default Loam initialization.

func WithLogger added in v0.5.3

func WithLogger(logger *slog.Logger) Option

WithLogger sets a custom structured logger for the engine.

Directories

Path Synopsis
cmd
trellis command
examples
hello-world command
low-level-api command
manual-security command
internal
cli
pkg
adapters/http
Package http provides primitives to interact with the openapi HTTP API.
Package http provides primitives to interact with the openapi HTTP API.

Jump to

Keyboard shortcuts

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