trellis

package module
v0.7.7 Latest Latest
Warning

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

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

README

Trellis

Go Report Card Go Doc License Release

The Neuro-Symbolic Backbone for Agents & Automation.

Trellis e um motor deterministico de fluxos para orquestrar ferramentas, scripts e guardrails com previsibilidade. Use como framework de CLI/Markdown ou como biblioteca Go dentro do seu backend.

Hybrid Nature: Use como Framework (CLI + Markdown) para prototipagem rápida, ou como Biblioteca (Go) para controle total em seu backend. "Opinionated by default, flexible under the hood."

Onde brilha

  • CLIs e Ops: fluxos guiados, com validacao e auditabilidade.
  • Automacao de ferramentas: scripts e APIs encadeados como um grafo versionavel.
  • Agentes com governanca: guardrails reais, sem depender apenas de prompt.
  • Backends duraveis: execucoes longas com pause/resume e padroes SAGA.

O Conceito Neuro-Simbólico & Automação

O Trellis preenche a lacuna entre a Rigidez dos Processos e a Flexibilidade da Inteligência:

  • Para Agentes de IA: Substitua "If/Else" frágeis e Prompts gigantes por um grafo de estados auditável. O Trellis impede alucinações de fluxo.
  • Para Humanos: Funciona como um motor de Workflow as Code (similar a um n8n/Zapier, mas compilado e versionável), ideal para CLIs complexas e automação de Ops.
graph TD
    %% Nodes
    Brain["🧠 Cérebro (LLM) ou<br/>👤 Humano (CLI)"] -->|Intenção / Input| Trellis["🛡️ Espinha Dorsal<br/>(Trellis Engine)"]
    
    subgraph "Mundo Simbólico (Determinístico)"
        Trellis -->|Validação| Rules["📜 Regras de Negócio<br/>(State Machine)"]
        Rules -->|Ok / Block| Trellis
    end
    
    Trellis -->|Execução Segura| Tools["⚡ Ferramentas<br/>(API / DB / Scripts)"]
    Tools -->|Resultado| Trellis
    Trellis -->|Contexto Atualizado| Brain

    %% Styles
    style Brain fill:#f9f,stroke:#333,stroke-width:2px,color:black
    style Trellis fill:#9cf,stroke:#333,stroke-width:2px,color:black
    style Rules fill:#ff9,stroke:#333,stroke-width:2px,stroke-dasharray: 5 5,color:black
    style Tools fill:#9f9,stroke:#333,stroke-width:2px,color:black

O decisor (seja IA ou Humano) escolhe qual caminho tomar, mas o Trellis garante que ele existe e é válido.

Como modelar um fluxo

Voce define um Grafo de Estados com Nos (Passos) e Transicoes (Regras), e o Trellis gerencia a navegacao.

Voce pode definir esse grafo de duas formas:

1. Declarativo (Arquivos)

Ideal para prototipagem, visualização (Mermaid) e edição por LLMs. Suporta Markdown (Frontmatter), YAML ou JSON via Loam.

# start.yaml
type: question
content: Olá! Qual é o seu nome?
save_to: user_name  # Data Binding automático
to: greeting        # Transição incondicional
2. Programático (Go Structs)

Ideal para integração profunda em backends, performance crítica e type-safety total.

&domain.Node{
    ID: "start",
    Type: "question",
    Content: []byte("Olá! Qual é o seu nome?"),
    SaveTo: "user_name",
    Transitions: []domain.Transition{{ToNodeID: "greeting"}},
}

Nota: Ambas as formas geram a mesma estrutura em memória e podem co-existir (ex: carregar arquivos e injetar nós via código).

Funcionalidades Principais

Para produto e UX

  • Data Binding & Contexto: Capture inputs (save_to) e use variaveis ({{ .name }}) nativamente.
  • Namespaces (Sub-Grafos): Organize fluxos complexos em pastas e modulos (jump_to).
  • MCP Server: Integracao nativa com Model Context Protocol para conectar Agentes de IA.

Para engenharia

  • Strict Typing: Garante que seus fluxos sejam robustos e livres de erros de digitacao.
  • Embeddable & Agnostic: Use como CLI, Lib ou Service. O Core e desacoplado de IO e Persistencia.
  • Native SAGA Support: Orquestracao de transacoes distribuidas com undo e rollback automatico.

Quick Start

Instalação
Windows (Recomendado)

A forma mais fácil de instalar no Windows é via Scoop:

# 1. Adicione o bucket (apenas a primeira vez)
scoop bucket add aretw0 https://github.com/aretw0/scoop-bucket

# 2. Instale o Trellis
scoop install trellis
macOS / Linux

Instale via Homebrew:

brew install aretw0/tap/trellis
Via Go (Library Mode)

Para usar o Trellis como biblioteca dentro do seu backend (sem arquivos, puramente em memória):

go get github.com/aretw0/trellis
// Exemplo: Instanciando o Engine sem ler arquivos
loader, _ := memory.NewFromNodes(myNodes...)
eng, _ := trellis.New("", trellis.WithLoader(loader))
Rodando o Golden Path (Demo)
# Execucao do Engine (Demo)
trellis run ./examples/tour

Se voce estiver desenvolvendo dentro do repo, veja a secao "Modo de Desenvolvimento" abaixo.

Usage

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

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

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

Detalhes e variacoes em:

Introspecção

Visualize seu fluxo como um grafo Mermaid:

trellis graph ./my-flow
# Saída: graph TD ...
Para quem esta contribuindo

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

Executar direto do repo (Go):

go run ./cmd/trellis run ./examples/tour
go run ./cmd/trellis serve --dir ./examples/tour --port 8080
go run ./cmd/trellis mcp --dir ./examples/tour

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).

Documentacao

Mais em docs/.

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)

Licença

AGPL-3.0

Documentation

Overview

Package trellis is a deterministic state machine engine (DFA) designed for building robust conversational agents, CLIs, and automation workflows.

It implements a "Reentrant DFA with Controlled Side-Effects" architecture, separating the narrative graph (Logic) from the execution state (Context) and side-effects (Tools).

Concept

Trellis treats your application flow as a graph of nodes. The engine manages the state transitions, data binding, and persistence, while your application ("Host") manages the I/O and external tool execution. This Hexagonal Architecture allows Trellis to be embedded in any interface: CLI, HTTP Server, or AI Agent infrastructure.

Key Features

  • Deterministic Execution: Given the same state and input, the transition is always reproducible.
  • Hexagonal Architecture: Core logic is decoupled from adapters (Storage, UI, Tools).
  • State Persistence: Built-in support for long-running sessions ("Durable Execution").
  • Strict Contracts: Validates graph integrity and data types to prevent runtime surprises.

Usage

Initialize the engine using the "Start" entrypoint. You can use the default filesystem loader (Loam) or inject a custom one.

package main

import (
	"context"
	"log"

	"github.com/aretw0/trellis"
)

func main() {
	// Initialize Engine with default settings (reads from ./my-flow)
	eng, err := trellis.New("./my-flow")
	if err != nil {
		log.Fatal(err)
	}

	// Start a new session
	ctx := context.Background()
	state, err := eng.Start(ctx, "session-123", nil)
	if err != nil {
		log.Fatal(err)
	}

	// Main Loop: Render -> Input -> Navigate
	for {
		// 1. Render View (What to show/do?)
		actions, terminal, valErr := eng.Render(ctx, state)
		if valErr != nil {
			log.Printf("Error: %v", valErr)
			break
		}

		// Handle Actions (Print text, Call tools...)
		for _, act := range actions {
			log.Println("Action:", act)
		}

		if terminal {
			log.Println("End of flow.")
			break
		}

		// 2. Navigate (Next Step)
		// In a real app, this input comes from User or Tool Result
		state, err = eng.Navigate(ctx, state, "user input")
		if err != nil {
			log.Fatal(err)
		}
	}
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var Version string

Functions

This section is empty.

Types

type Engine

type Engine struct {
	Name string
	// 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 (Library)

ExampleNew_library demonstrates how to use Trellis purely as a Go library, injecting an in-memory graph without reading from the filesystem.

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 pure Go structs
	loader, err := memory.NewFromNodes(
		domain.Node{
			ID:      "start",
			Type:    "text",
			Content: []byte("Hello from Memory!"),
			Transitions: []domain.Transition{
				{ToNodeID: "finish"},
			},
		},
		domain.Node{
			ID:      "finish",
			Type:    "text",
			Content: []byte("Goodbye."),
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Initialize the Engine with the custom loader
	// No file path needed ("") because we are providing a loader.
	eng, err := trellis.New("", trellis.WithLoader(loader))
	if err != nil {
		log.Fatal(err)
	}

	// 3. Start a session
	ctx := context.Background()
	state, err := eng.Start(ctx, "session-mem", nil)
	if err != nil {
		log.Fatal(err)
	}

	// 4. Run the loop (simplified for example)
	for {
		// Render current state
		actions, terminal, _ := eng.Render(ctx, state)

		// Print content
		for _, act := range actions {
			if act.Type == domain.ActionRenderContent {
				fmt.Println(act.Payload)
			}
		}

		if terminal {
			break
		}

		// Move to next state
		state, err = eng.Navigate(ctx, state, nil)
		if err != nil {
			log.Fatal(err)
		}
	}

}
Output:
Hello from Memory!
Goodbye.
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.
dsl
schema
Package schema provides a type-safe validation system for structured data.
Package schema provides a type-safe validation system for structured data.

Jump to

Keyboard shortcuts

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