gotato

module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0

README ΒΆ

Gotato

🎯 Our mission: Agent-as-a-Service. Ultimately, any service can easily become an agent. The agent becomes the cloud service itself.

CI Go Reference License Go Status Core

πŸ€” Why Gotato?

  • ☁️ Why agents as a cloud service? A machine per conversation does not scale: a million is about a petabyte, and at a few kilobytes idle it is a few gigabytes.
  • 🐹 Why Go? Your services already are. No second language, no second deployment: the agent is a package, not a system beside yours.
  • πŸ•ΈοΈ Why not a graph? Models plan on their own now. One loop does what the nodes did, with less to run and debug.
  • πŸ“¦ Why not an existing SDK? Each brings a subprocess per session, a conversation held elsewhere, or a whole toolkit. None is the smallest thing that runs on many pods.
  • πŸͺΆ Why minimalistic? The model keeps taking over the harness, so the harness stays thin: a standard-library core, with your store, tools and telemetry plugged in.

πŸ’‘ The idea

An Agent is a definition: model, instruction, tools, limits. A Session is one conversation running it: a small loop, plus a runtime that saves every step to your store.

Where the Agent lives depends on how you run it. With the Go API alone, the Agent is the options you pass to gotato.NewSession (a model, an instruction, tools). With a service.Runner, it is a service.Agent value you register once, and the Runner starts a Session from it for each conversation.

A Session saves itself by writing its Record, an append-only log of everything that happened, to a Store. A Session built with gotato.NewSession alone keeps that Record in memory and is gone when the process exits; a Runner over a Store keeps it there.

Between messages a Session is only a record in your store. Any pod can load it and carry on, and one writer per Session keeps two pods apart. No process, no shell, no sandbox, and a few kilobytes while idle.

A pod is one running copy of your service; the glossary calls it an Instance. A request can reach any pod. Only one of them runs a given Session at a time, and the Session itself lives in your store:

                request
                   β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚      load balancer      β”‚   the one you already run
      β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜
          β”‚           β”‚       β”‚
      β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”   β”Œβ”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β–Όβ”€β”€β”€β”€β”€β”
      β”‚ pod A β”‚   β”‚ pod B β”‚ β”‚ pod C β”‚   each pod: a Runner hosting Sessions
      β””β”€β”€β”€β”¬β”€β”€β”€β”˜   β””β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”¬β”€β”€β”€β”€β”€β”˜
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”˜
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚         your store          β”‚   Session S = its record, a log that only grows
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  pod A runs S ──► pod A goes away ──► the next message reaches pod B, which loads S and goes on

PROPOSAL.md tells the longer story.

✨ What you get

  • πŸ”Œ Fits your service. A library, not a stack, with a standard-library core.
  • 🌐 Runs on many pods. Every step is saved, and one pod runs a Session at a time.
  • 🧩 Stays simple. One loop, no workflow language, and you see what the model sees.
  • πŸ”’ Stays stable. Breaks are versioned and come with before-and-after notes, from the first release on.

πŸ“ A program with the agent, runner, store and HTTP server pulls 3 modules and builds an 8.7 MB binary (measured).

🚫 What it is not

Not a coding agent, a workflow language, a platform or infrastructure. Gotato runs purpose-built agents inside your service, and your service owns the rest (the full list).

🧰 Capabilities

Capability What you get
Agent loop Prompt, Continue and Abort; steer, follow up and interrupt a Run in flight βœ…
Context management built every Turn, your own builder, inspection; automatic compaction into Segments the model can read back βœ…
Tools Go functions with an Effect; tool search; large results spilled and read back in pages βœ…
MCP MCP servers as tool sets, over streamable HTTP or stdio βœ…
Extensions six stages around the loop, from TurnPreparer to TurnStopper βœ…
Models OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, or your own βœ…
Storage memory, file, or your own database behind eight calls, with a conformance suite βœ…
Many instances one writer per Session, takeover, drain, not_writer forwarding βœ…
Events every step as a Go type, streamed, resumable on any instance βœ…
Endpoints Go API and HTTP βœ…
Testing fake models, a fake gateway for each wire API, a reference workload βœ…
Telemetry OpenTelemetry mapping of Events later

βœ… built, 🚧 built with a gap named, and later. FEATURES.md lists every feature and its status.

⚑ How Simple is Gotato?

A real model, one tool, one question:

import (
    "github.com/jinhuang712/gotato/gateway"
    "github.com/jinhuang712/gotato/gotato"
)

// 1. A model. A provider is configuration: a wire API, a model and a key.
model, _ := gateway.New(gateway.Config{
    API:    gateway.APIAnthropicMessages, // or OpenAI Chat Completions, or Responses
    Model:  "claude-sonnet-5-5",
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
})

// 2. A tool is a Go function. Its input type becomes the schema the model sees.
weather, _ := gotato.NewFuncTool("weather", "Returns the weather of a city.", func(_ context.Context, in struct {
    City string `json:"city" description:"the city to look up"`
}) (string, error) {
    return "sunny in " + in.City, nil
})

// 3. A Session is one conversation: the model, an instruction and the tools.
s, _ := gotato.NewSession(ctx,
    gotato.WithModel(model),
    gotato.WithInstruction("You are a helpful assistant."),
    gotato.WithTools(weather),
)
defer s.Close(ctx)

// 4. Ask. Prompt runs the loop (model, tools, model, ...) and returns the final reply.
result, _ := s.Prompt(ctx, gotato.UserMessage("Do I need an umbrella in Paris?"))
fmt.Println(gotato.TextOf(*result.FinalMessage))

Prompt returns when the Run settles, and ctx carries its cancellation and deadline. The model asks for the tool, Gotato runs it and goes on until the model answers. Errors are left out here. This code is run as a test, against a fake gateway that speaks the same wire API: Example_gatewayAndTool.

Tools from an MCP server

A server you already run as MCP becomes a ToolSet. Its tools are named <server>.<tool>, and the adapter connects at the first Run. WithActiveToolSet shows the model every tool of the server; WithToolSet and WithFoldedToolSet leave them to search_tools when a server has many:

import "github.com/jinhuang712/gotato/adapter/mcp"

crm, _ := mcp.NewToolSet("crm", mcp.StreamableHTTP("https://crm.internal/mcp"))
defer crm.Close() // the service closes it, never a Session

s, _ := gotato.NewSession(ctx, gotato.WithModel(model), gotato.WithActiveToolSet(crm))

The MCP adapter lists the transports (streamable HTTP and stdio), how a tool's annotations become its Effect, and how to authenticate.

πŸ§ͺ Example

A chat bot on several pods. A Lark bot built on Gotato: one Session per thread, any pod can answer, and a killed or replaced pod does not end a conversation. It runs on your machine with a fake chat platform and no secrets, and on a local Kubernetes cluster through a rolling deploy, a killed writer and a scale-down. See examples/larkbot and its guide.

πŸ“– Guides

Guide You learn
Install how to add Gotato and the MCP adapter to your go.mod
Which packages do I import? what each package is for, and what leaving one out costs
Getting started a Session, a tool and the Events of a Run
Embed Gotato in a service the Runner, many pods and shutting down
HTTP in five minutes a service you can curl, with streaming and health checks
A minimal main.go a complete program, and the order to shut it down in
Write a Tool or a Model a function Tool, your own Tool type, a ToolSet, a Model wrapper
Write a Store the Store contract and the suite that checks your adapter
Test with fakes scripted models and tools, no network
Extension points which seam to use to change a model, a tool, a store or what the model sees
Example: a chat bot several instances behind a chat platform

Each feature, with its status, is in FEATURES.md.

πŸš€ Install

Gotato needs Go 1.24. The runtime is one module; the MCP adapter is a module of its own, so a service that does not use MCP carries none of it:

go get github.com/jinhuang712/gotato@v0.2.0
go get github.com/jinhuang712/gotato/adapter/mcp@v0.2.0   # only for MCP servers

From 0.2.0 on, a break is written in CHANGELOG.md. Install has the details, and the 0.2.0 notes say what was never checked against a real provider.

πŸ“š Documents

Document Role
PROPOSAL.md why Gotato exists, and what it refuses to become
PHILOSOPHY.md the beliefs behind it
DESIGNS.md design principles, and the decisions that follow from them
GLOSSARY.md one name per thing, and its name in today's code
FEATURES.md what is built, with status markers, and what is not planned
RUNBOOK.md what to do when a service built on Gotato misbehaves
plan/GOALS.md goals that pass or fail, and benchmarks tracked against a reference
plan/ROADMAP.md what gets built next, and how each step is known to be done
plan/TODO.md open defects and decisions
AGENTS.md how to work in this repository, and the Git policy
CHANGELOG.md what changed, newest first

πŸ“„ License

Gotato is licensed under the Apache License 2.0.

Directories ΒΆ

Path Synopsis
adapter
bash module
mcp module
Package gateway provides provider streaming Model adapters for Gotato: the OpenAI-compatible Chat Completions and Responses APIs and the Anthropic Messages API.
Package gateway provides provider streaming Model adapters for Gotato: the OpenAI-compatible Chat Completions and Responses APIs and the Anthropic Messages API.
Package gotato is the Gotato Core: the live Session, its Loop and the contracts it runs against.
Package gotato is the Gotato Core: the live Session, its Loop and the contracts it runs against.
internal
cmd/gotato command
Command gotato is the test tool of this repository: a thin client over the runtime packages.
Command gotato is the test tool of this repository: a thin client over the runtime packages.
cmd/gotato-workload command
Command gotato-workload runs the reference workload that GOALS.md measures and prints its report as JSON on stdout.
Command gotato-workload runs the reference workload that GOALS.md measures and prints its report as JSON on stdout.
fakegateway
Package fakegateway is a test-only server that speaks the three wire APIs a Gotato gateway adapts to, and plays each provider's quirks on demand.
Package fakegateway is a test-only server that speaks the three wire APIs a Gotato gateway adapts to, and plays each provider's quirks on demand.
fakegateway/cmd/fakegateway command
Command fakegateway serves internal/fakegateway on a port, for a test that runs outside the Go process, such as the local cluster.
Command fakegateway serves internal/fakegateway on a port, for a test that runs outside the Go process, such as the local cluster.
reftasks
Package reftasks holds the tasks of the reference workload that plan/GOALS.md measures: each task has a history, prompts, Tools, and a fake Model that plays it the same way every time, with a check of its answers.
Package reftasks holds the tasks of the reference workload that plan/GOALS.md measures: each task has a history, prompts, Tools, and a fake Model that plays it the same way every time, with a check of its answers.
text
Package text holds the string helpers that the core and its sibling packages share: paging, spill refs and the escape of Gotato's own tags.
Package text holds the string helpers that the core and its sibling packages share: paging, spill refs and the escape of Gotato's own tags.
workload
Package workload drives the reference workload that GOALS.md measures through a service.Runner, and reports what it saw: how each task went, every failed Run, tool call and check with its cause, token usage and cost, the time Gotato adds per Turn next to the model's own time, the time to load a Session, and whether each Turn's Context matches an inspection of the Transcript it was built from (G-G03).
Package workload drives the reference workload that GOALS.md measures through a service.Runner, and reports what it saw: how each task went, every failed Run, tool call and check with its cause, token usage and cost, the time Gotato adds per Turn next to the model's own time, the time to load a Session, and whether each Turn's Context matches an inspection of the Transcript it was built from (G-G03).
Package modelctx builds what the Model sees now.
Package modelctx builds what the Model sees now.
Package service turns the runtime into a service: named Agents, a Store of Session Records, and a live Session built for each Run.
Package service turns the runtime into a service: named Agents, a Store of Session Records, and a live Session built for each Run.
httpapi
Package httpapi exposes a service.Runner over HTTP.
Package httpapi exposes a service.Runner over HTTP.
Package session holds a Session's Record, what the Store keeps of it, and the Stores that keep it.
Package session holds a Session's Record, what the Store keeps of it, and the Stores that keep it.
storetest
Package storetest checks that a session.Store keeps the contract Gotato relies on: a Record is an append-only log, every write is all or nothing, a position is written once, a stale writer is refused, a load starts at the last Checkpoint (a session_created or compaction_ended Entry, or the "checkpoint" of the earlier format) and hands back the segment_closed Entries before it, Liveness rows expire by the store's clock, and a row its Instance ended is removed.
Package storetest checks that a session.Store keeps the contract Gotato relies on: a Record is an append-only log, every write is all or nothing, a position is written once, a stale writer is refused, a load starts at the last Checkpoint (a session_created or compaction_ended Entry, or the "checkpoint" of the earlier format) and hands back the segment_closed Entries before it, Liveness rows expire by the store's clock, and a row its Instance ended is removed.
Package testkit provides deterministic doubles for testing Gotato runtimes without a network provider: scripted and replayed Models, a recording FakeTool, an EventRecorder over a Subscription, Session fixtures, the echo/demo Models used by the CLI and the reference service.
Package testkit provides deterministic doubles for testing Gotato runtimes without a network provider: scripted and replayed Models, a recording FakeTool, an EventRecorder over a Subscription, Session fixtures, the echo/demo Models used by the CLI and the reference service.

Jump to

Keyboard shortcuts

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