Skip to content

zoobz-io/ago

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ago

CI Status codecov Go Report Card CodeQL Go Reference License Go Version Release

Typed LLM tool execution framework for Go. The same role rocco plays for HTTP, ago plays for LLM tool dispatch.

ago ("I do" in Latin) is where an LLM's decisions become validated, scoped, auditable actions in the host system. It sits alongside cogito ("I think") and zyn (typed LLM interactions) in the zoobzio AI stack.

Features

  • Typed tool handlers with generic input/output via Tool[In, Out]
  • Automatic JSON Schema generation from Go types via sentinel
  • Input validation with JSON deserialization and Validatable interface
  • Middleware chain for cross-cutting concerns (auth, logging, rate limiting)
  • Typed errors distinguishing tool errors (LLM can retry) from dispatch errors (system failure)
  • Observability via capitan lifecycle signals
  • Panic recovery on every tool invocation

Install

go get github.com/zoobz-io/ago

Quick Start

// Define input and output types.
type SearchInput struct {
    Query string `json:"query" desc:"The search query"`
    Limit int    `json:"limit" desc:"Max results to return"`
}

type SearchOutput struct {
    Results []string `json:"results"`
    Total   int      `json:"total"`
}

// Create a typed tool.
search := ago.NewTool[SearchInput, SearchOutput]("search",
    func(ctx context.Context, inv *ago.Invocation) (SearchOutput, error) {
        input, _ := ago.TypedInput[SearchInput](inv)
        results := doSearch(ctx, input.Query, input.Limit)
        return SearchOutput{Results: results, Total: len(results)}, nil
    },
).WithDescription("Search for items by query")

// Register and invoke.
registry := ago.NewRegistry()
registry.Register(search)

result, err := registry.Invoke(ctx, "search", []byte(`{"query":"go","limit":10}`), identity)

Schema Generation

Generate tool schemas for LLM APIs directly from registered tools:

schemas := ago.GenerateSchemas(registry)
// schemas is []ToolSchema with name, description, and JSON Schema for each tool

Middleware

// Global middleware applies to all tools.
registry.WithMiddleware(func(next ago.HandlerFunc) ago.HandlerFunc {
    return func(ctx context.Context, inv *ago.Invocation) (*ago.Result, error) {
        log.Printf("tool=%s id=%s", inv.ToolName, inv.ID)
        return next(ctx, inv)
    }
})

// Per-tool middleware.
tool.WithMiddleware(rateLimitMiddleware, auditMiddleware)

Error Handling

Tool errors are returned to the LLM as structured results. Dispatch errors are system failures.

// Define a tool error the LLM can act on.
var ErrNotFound = ago.NewError[NotFoundDetails]("NOT_FOUND", "resource not found")

// Return it from a handler — it becomes a tool_result, not a crash.
func handler(ctx context.Context, inv *ago.Invocation) (Output, error) {
    return Output{}, ErrNotFound.WithDetails(NotFoundDetails{Resource: "user"})
}

Observability

All lifecycle events are emitted as capitan signals:

Signal When
ToolRegistered Tool added to registry
ToolExecutionStarted Invocation dispatched
ToolExecutionCompleted Invocation succeeded
ToolExecutionFailed Invocation failed

Dependencies

Package Role
capitan Lifecycle signal emission
sentinel Type metadata for schema generation

License

MIT