Usage | Components | Architecture | Examples | Frontend
Goleo turns Go functions, streaming handlers, HTTP endpoints, OpenAI-compatible APIs, Ollama models, local processes, and full-duplex voice handlers into embedded web apps.
Define inputs and outputs in Go, launch one binary, and get a usable local UI with prediction, streaming, upload, asset playback, and voice session endpoints. The built frontend is embedded into the Go binary, so there is no separate frontend deployment step.
Build richer local tools than a single textbox demo. The showcase form combines typed inputs, file uploads, and multiple outputs in one surface.
Use Chat with a streaming handler to ship copilot-style demos and assistant
workflows without building the chat shell yourself.
Wrap external backends behind the same UI contract. The same surface can front a native Go handler, an HTTP API, an OpenAI-compatible endpoint, Ollama, or a local process.
The embedded UI supports typed form controls and structured outputs out of the box.
Built-in constructors include Textbox, Number, Slider, Checkbox,
Dropdown, Button, Markdown, JSON, Image, Audio, File, State,
Row, Column, Group, and Chatbot.
You can also use CustomComponent when you need to introduce a schema type
before a first-class constructor exists.
Use Interface with Audio(...) when you want a normal request/response flow
with upload or microphone capture. The handler receives a goleo.AudioInput
with metadata plus a temporary Path. If the handler returns a
goleo.AudioOutput, Goleo stores it and serves it back through
/api/assets/{id} for browser playback.
app.Interface(
goleo.Handler(func(clip goleo.AudioInput) (string, goleo.AudioOutput, error) {
return "received " + clip.Name, goleo.AudioOutput{
Name: "reply.wav",
ContentType: clip.ContentType,
Path: clip.Path,
}, nil
}),
goleo.Inputs(goleo.Audio("Prompt audio")),
goleo.Outputs(goleo.Textbox("Summary"), goleo.Audio("Reply audio")),
)To keep richer demo behavior visible:
- tune throughput with
app.ConfigureQueue(maxConcurrency, maxQueue); - cancel stream runs with
POST /api/canceland returnedrequest_id; - carry session values in
goleo.State(...).
Use Voice when you need a live session over WebSocket with microphone chunks,
interrupts, and mixed text/audio output events. The browser connects to
/api/voice/{id}/ws; your handler works with session.Receive(),
session.Send(...), and session.SendAudio(...).
app.Voice(goleo.VoiceHandler(func(session *goleo.VoiceSession) error {
for {
event, err := session.Receive()
if err != nil {
return err
}
switch event.Type {
case "session.start":
if err := session.Send(goleo.VoiceEvent{Type: "session.ready"}); err != nil {
return err
}
case "input.stop":
if err := session.Send(goleo.VoiceEvent{Type: "output.text", Text: "turn complete"}); err != nil {
return err
}
case "session.close":
return session.Send(goleo.VoiceEvent{Type: "session.closed"})
}
}
}))Choose the surface based on the interaction model:
Interface + Audio: turn-based upload or one-shot microphone capture.Voice: long-lived duplex session with chunked mic input and interrupt.
Prerequisite: Goleo requires Go 1.24 or higher.
go get github.com/sneiko/goleoFor local development of this repository:
make checkTip
make run-* targets bind to :7871 by default. Override the port when it is
already in use, for example GOLEO_ADDR=:7872 make run-chat.
Create a Goleo app around any Go function:
package main
import (
"context"
"log/slog"
"os"
"os/signal"
"syscall"
"github.com/sneiko/goleo"
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
app := goleo.New(goleo.WithLogger(logger))
app.Interface(
goleo.Handler(func(input string) (string, error) {
return "Hello " + input, nil
}),
goleo.Inputs(goleo.Textbox("Prompt")),
goleo.Outputs(goleo.Textbox("Result")),
)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := app.LaunchContext(ctx, goleo.LaunchOptions{Addr: ":7860"}); err != nil {
logger.Error("goleo server stopped", "error", err)
os.Exit(1)
}
}Run the bundled minimal example:
make run-simpleOpen http://localhost:7871 after the server starts.
If you run examples directly with go run, they use :7860 unless
GOLEO_ADDR is set.
Use the richer examples when you want a more realistic surface than the minimal hello-world demo.
| Command | Example | What it shows |
|---|---|---|
make run-audio |
examples/audio |
Turn-based audio upload or mic capture with text, JSON, and playback outputs |
make run-showcase-form |
examples/showcase-form |
Typed form inputs, file upload, text output, and structured JSON |
make run-showcase-chat |
examples/showcase-chat |
Streaming chat transcript with a copilot-style response |
make run-showcase-adapters |
examples/showcase-adapters |
Adapter-oriented prompt flow with backend metadata |
make run-voice |
examples/voice |
Full-duplex voice session with mic chunks, interrupt, and reply audio |
make run-blocks |
examples/blocks |
Blocks interface with click/change/load events, state, and runtime updates |
The repository keeps the focused integration demos as separate entry points.
| Command | Example | Notes |
|---|---|---|
make run-simple |
examples/simple |
Minimal function-backed form |
make run-chat |
examples/chat |
Basic streaming chat surface |
make run-audio |
examples/audio |
First-class Audio component with app-served playback assets |
make run-blocks |
examples/blocks |
Event-driven Blocks interface with state, load events, and update envelopes |
make run-voice |
examples/voice |
WebSocket voice runtime with VoiceHandler |
make run-http |
examples/http-wrapper |
Wrap an HTTP endpoint |
OLLAMA_MODEL=llama3.2 make run-ollama |
examples/ollama |
Stream from Ollama |
OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_MODEL=llama3.2 make run-openai-stream |
examples/openai-stream |
Stream from any OpenAI-compatible API |
Use Chat with a streaming handler for chat-style demos:
app.Chat(goleo.StreamHandler(func(input string, emit goleo.EmitFunc) error {
emit("You said: " + input)
return nil
}))Run the bundled chat demo:
make run-chatUse Voice for true duplex audio sessions where the browser streams microphone
chunks to your handler and the handler emits back text, state, and audio
events.
Browser -> server events in v1:
session.startinput.audioinput.stopoutput.interruptsession.close
Server -> browser events in v1:
session.readyoutput.textoutput.audiooutput.stateerrorsession.closed
Run the bundled duplex demo:
make run-voiceGoleo adapters return regular handler bindings, so you can swap native Go functions for external systems without changing the UI definition.
Wrap an HTTP endpoint:
app.Interface(
goleo.HTTPAdapter(goleo.HTTPAdapterOptions{URL: "http://localhost:9000/predict"}),
goleo.Inputs(goleo.Textbox("Prompt")),
goleo.Outputs(goleo.Textbox("Result")),
)Wrap an OpenAI-compatible streaming API:
app.Chat(goleo.OpenAICompatibleStreamAdapter(goleo.OpenAICompatibleOptions{
BaseURL: "http://localhost:11434/v1",
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: "llama3.2",
}))Configure HTTP server timeouts when needed:
app.Launch(goleo.LaunchOptions{
Addr: ":7860",
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 0, // keep unset for long-lived streaming responses
IdleTimeout: 60 * time.Second,
ShutdownTimeout: 5 * time.Second,
})For graceful shutdown, cancel the context passed to LaunchContext:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
err := app.LaunchContext(ctx, goleo.LaunchOptions{Addr: ":7860"})For advanced lifecycle control, build the server yourself:
srv := app.Server(goleo.LaunchOptions{Addr: ":7860"})
err := srv.ListenAndServe()Goleo uses log/slog for structured logs. Logging is opt-in: if you do not
pass a logger, Goleo stays quiet.
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
app := goleo.New(goleo.WithLogger(logger))The built-in server logs request completion events with method, path, status,
duration, and request_id. If a request includes X-Request-ID, Goleo keeps
it; otherwise it generates one and returns it in the response header.
API errors use a structured JSON shape:
{
"error": {
"code": "bad_request",
"message": "interface_id is required"
}
}Goleo stays Go-first: built frontend assets are committed under server/assets,
so users can run examples without installing Node.js.
The embedded UI itself lives in frontend as a React/Vite/shadcn app:
make frontend-install
make frontend-dev
make frontend-test
make frontend-buildmake frontend-build writes the static assets consumed by Go's go:embed.
Maintainers can regenerate the README screenshots with:
make readme-assetsThis is an MVP implementation focused on Gradio-style local AI demos:
Interfacefor function-backed formsChatfor streaming chat demosVoicefor WebSocket duplex sessionsAudiofor upload and microphone-driven media inputs/outputs- Embedded frontend assets served by the Go binary
- JSON prediction endpoint, SSE streaming endpoint, upload endpoint, asset endpoint, and voice WebSocket endpoint
- Native Go, HTTP, OpenAI-compatible, Ollama, streaming, and process adapters
- Optional structured logging with request IDs
Production platform features such as auth, persistent storage, hosting, queues, and multi-user state are intentionally out of scope for v1.
The root goleo package is a facade over focused packages:
component: component schema and constructorscore: app model and schema generationmedia: handler-facing audio types and browser-safe asset descriptorsruntime: handler binding and streaming abstractionserver: HTTP routes, uploads, assets, SSE, voice sessions, embedded frontendadapter: HTTP, OpenAI-compatible, Ollama, and process adapters
See docs/architecture.md for extension points.




