Skip to content

Repository files navigation

structllm

Typed, validated LLM output for Go. The Go equivalent of Python's instructor / pydantic-ai — define a struct, get back a typed value.

Go Reference Go Report Card test Go Version License: MIT

Why structllm?

structllm is a Go library for structured output from LLMs. You write a Go struct; structllm turns it into a JSON Schema, sends it to the model through the provider's native structured-output mechanism, repairs and validates the response, retries on validation failure, and gives you back a typed T.

It works with the three most common Go LLM destinations out of the box:

  • OpenAI — via response_format (Structured Outputs / JSON Schema)
  • Anthropic Claude — via tool use
  • Ollama — via the format parameter

Built on JSON Schema, native function calling / tool use, and Go generics — no DSL, no codegen, no runtime dependency on Python.

Install

go get github.com/mohsenm4/structllm

Requires Go 1.25 or later.

Quick start

type Article struct {
    Title     string   `json:"title"`
    Tags      []string `json:"tags"`
    Sentiment string   `json:"sentiment" enum:"positive,negative,neutral"`
}

client := anthropic.New(os.Getenv("ANTHROPIC_API_KEY"))

article, err := structllm.Generate[Article](ctx, client, structllm.Request{
    Model:  "claude-sonnet-4-6",
    Prompt: "Analyze: " + text,
})
// article.Title, article.Tags, article.Sentiment — typed, validated, ready

Under the hood:

  1. A JSON Schema is generated from the struct via reflection + struct tags.
  2. The schema is sent to the provider using its native structured-output mode (OpenAI response_format, Anthropic tool use, Ollama format).
  3. The response is parsed, repaired (markdown fences, trailing commas, truncation), and validated against the schema.
  4. On validation failure the error is sent back to the model with a retry — up to Request.MaxAttempts times (default 3).

Providers

import (
    "github.com/mohsenm4/structllm/provider/anthropic"
    "github.com/mohsenm4/structllm/provider/openai"
    "github.com/mohsenm4/structllm/provider/ollama"
)

anthropicClient := anthropic.New(os.Getenv("ANTHROPIC_API_KEY"))
openaiClient    := openai.New(os.Getenv("OPENAI_API_KEY"))
ollamaClient, _ := ollama.FromEnvironment() // reads OLLAMA_HOST

All three satisfy structllm.Provider — swap them freely. Any custom backend that implements the one-method Provider interface works too.

Struct tag reference

Tag Effect
json:"name" field name in the schema (standard)
json:",omitempty" or *T field is optional (not in required)
description:"..." adds a description to help the model
enum:"a,b,c" restrict the value to a fixed set

Maybe[T] — values the model may not be able to fill

For fields where the source text may not contain the answer:

type Quote struct {
    Text   string                  `json:"text"`
    Author structllm.Maybe[string] `json:"author"`
}

quote, _ := structllm.Generate[Quote](ctx, client, req)
if name, ok := quote.Author.Get(); ok {
    fmt.Println("found author:", name)
} else {
    fmt.Println("no author:", quote.Author.Reason)
}

This avoids the common failure mode where the model invents an answer because the schema demanded one.

GenerateWithReasoning — chain-of-thought

Ask the model to think step-by-step before committing to an answer:

result, err := structllm.GenerateWithReasoning[Answer](ctx, client, req)
fmt.Println(result.Reasoning)  // the model's thinking
fmt.Println(result.Answer)     // typed answer

The reasoning field is declared first in the schema so the model writes its thinking before the final answer.

Retry with model feedback

If the response fails schema validation, structllm re-prompts the model with its previous attempt and the validation error. Capped by Request.MaxAttempts (default 3; set to 1 to disable).

Standalone packages

The pieces are also exported for direct use:

Package Use
schema Generate[T]() — JSON Schema from a Go type
repair JSON([]byte) — clean up markdown fences, trailing commas, truncated output
validate JSON(schema, data) — validate JSON against a JSON Schema

Examples

Runnable programs are in examples/:

Each example needs ANTHROPIC_API_KEY set in the environment:

go run ./examples/basic

Comparison

structllm raw provider SDK LangChain-style framework
Typed output ✅ Generic T map[string]any / strings ⚠️ Often any
Schema from struct tags ❌ Hand-write JSON Schema ⚠️ Varies
Validation + repair ✅ Built-in ❌ Roll your own ⚠️ Varies
Retry with feedback ⚠️ Varies
Multi-provider ✅ OpenAI / Claude / Ollama ❌ One per SDK
Dependencies Minimal Minimal Heavy

Status

v0.1.0 — usable. Not yet battle-tested in production; please open issues.

Roadmap

  • Streaming partial output (v0.2)
  • More providers (Gemini, Mistral, Groq)
  • Function/tool calling beyond structured output

Contributing

See CONTRIBUTING.md. Bug reports, feature requests, and PRs welcome.

Inspiration

License

MIT — see LICENSE.


Keywords: golang llm structured output, go openai structured output, go anthropic claude tool use, go ollama json format, go json schema from struct, go instructor alternative, go pydantic alternative, typed llm response golang, go function calling library.

About

Typed, validated LLM output for Go. JSON Schema-based structured generation for OpenAI, Anthropic Claude, and Ollama.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages