https://github.com/jxnl/instructor-go
structured outputs for llms
https://github.com/jxnl/instructor-go
hacktoberfest
Last synced: 3 months ago
JSON representation
structured outputs for llms
- Host: GitHub
- URL: https://github.com/jxnl/instructor-go
- Owner: jxnl
- License: mit
- Created: 2024-05-07T13:28:29.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2026-04-16T21:28:52.000Z (3 months ago)
- Last Synced: 2026-04-16T23:26:14.859Z (3 months ago)
- Topics: hacktoberfest
- Language: Go
- Homepage: https://go.useinstructor.com/
- Size: 28.5 MB
- Stars: 201
- Watchers: 4
- Forks: 20
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# instructor-go: Structured LLM Outputs
Get reliable structured data from any LLM. Built on `jsonschema` for validation, type safety, and compile-time guarantees.
```go
import (
"github.com/jxnl/instructor-go/pkg/instructor"
"github.com/sashabaranov/go-openai"
)
// Define what you want
type User struct {
Name string `json:"name" jsonschema:"description=The person's name"`
Age int `json:"age" jsonschema:"description=The person's age"`
}
// Extract it from natural language
client := instructor.FromOpenAI(openai.NewClient(apiKey))
var user User
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4o,
Messages: []openai.ChatCompletionMessage{
{Role: openai.ChatMessageRoleUser, Content: "John is 25 years old"},
},
},
&user,
)
fmt.Println(user) // User{Name: "John", Age: 25}
```
**That's it.** No JSON parsing, no error handling, no retries. Just define a struct and get validated data.
[](https://pkg.go.dev/github.com/jxnl/instructor-go/pkg/instructor)
[](https://github.com/jxnl/instructor-go)
[](https://discord.gg/UD9GPjbs8c)
***
## Why Instructor Go?
Getting structured data from LLMs is hard. You need to:
1. Write complex JSON schemas
2. Handle validation errors
3. Retry failed extractions
4. Parse unstructured responses
5. Deal with different provider APIs
**Instructor Go handles all of this with one simple interface:**
Without Instructor
With Instructor
```go
resp, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4,
Messages: []openai.ChatCompletionMessage{
{Role: "user", Content: "..."},
},
Tools: []openai.Tool{
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "extract_user",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
}
}`),
},
},
},
},
)
// Parse response
toolCall := resp.Choices[0].Message.ToolCalls[0]
var userData map[string]interface{}
json.Unmarshal([]byte(toolCall.Function.Arguments), &userData)
// Validate manually
if _, ok := userData["name"]; !ok {
// Handle error...
}
```
```go
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
)
var user User
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4,
Messages: []openai.ChatCompletionMessage{
{Role: "user", Content: "..."},
},
},
&user,
)
// That's it! user is validated and typed
```
## Install in seconds
```bash
go get github.com/jxnl/instructor-go/pkg/instructor
```
## Works with every major provider
Use the same code with any LLM provider:
```go
import (
"github.com/jxnl/instructor-go/pkg/instructor"
instructor_openai "github.com/jxnl/instructor-go/pkg/instructor/providers/openai"
instructor_anthropic "github.com/jxnl/instructor-go/pkg/instructor/providers/anthropic"
instructor_google "github.com/jxnl/instructor-go/pkg/instructor/providers/google"
instructor_cohere "github.com/jxnl/instructor-go/pkg/instructor/providers/cohere"
)
// OpenAI
client := instructor.FromOpenAI(openai.NewClient(apiKey))
// Anthropic
client := instructor.FromAnthropic(anthropic.NewClient(apiKey))
// Google Gemini
client := instructor.FromGoogle(google.NewClient(apiKey))
// Cohere
client := instructor.FromCohere(cohere.NewClient(apiKey))
// All use the same API pattern!
var result YourStruct
_, err := client.CreateChatCompletion(ctx, request, &result)
```
## Production-ready features
### Automatic retries with validation
Failed validations are automatically retried with descriptive error messages:
```go
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
instructor.WithMaxRetries(3),
instructor.WithMode(instructor.ModeToolCall),
)
type User struct {
Name string `json:"name" jsonschema:"description=The person's name,minLength=1"`
Age int `json:"age" jsonschema:"description=The person's age,minimum=0,maximum=150"`
}
// Instructor automatically retries when validation fails
var user User
_, err := client.CreateChatCompletion(ctx, request, &user)
```
### Debug mode for troubleshooting
Hit "max retry attempts" errors? Enable debug logging to see exactly what's happening:
```go
client := instructor.FromOpenAI(
openai.NewClient(apiKey),
instructor.WithLogging("debug"), // 🔍 See all retry attempts
)
```
**Output shows:**
* Each retry attempt with full context
* Response previews on failures
* Token usage across retries
* Exact JSON/validation errors
**Common formats:**
```go
instructor.WithLogging("debug") // Development - see everything
instructor.WithLogging("json") // Production - structured logs
instructor.WithLogging("json:error") // Production - only errors
```
### Streaming support
Stream partial objects as they're generated:
```go
import "github.com/jxnl/instructor-go/pkg/instructor"
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
schema, _ := instructor.GetSchema(&User{})
client := instructor.FromOpenAI(openai.NewClient(apiKey))
stream, err := client.CreateChatCompletionStream(ctx, request, schema)
defer stream.Close()
for stream.Next() {
var partial User
if err := stream.Scan(&partial); err == nil {
fmt.Printf("Partial: %+v\n", partial)
// User{Name: "", Age: 0}
// User{Name: "John", Age: 0}
// User{Name: "John", Age: 25}
}
}
```
### Nested and complex structures
Extract complex, nested data structures with full type safety:
```go
type Address struct {
Street string `json:"street" jsonschema:"description=Street address"`
City string `json:"city" jsonschema:"description=City name"`
Country string `json:"country" jsonschema:"description=Country name"`
}
type User struct {
Name string `json:"name" jsonschema:"description=User's full name"`
Age int `json:"age" jsonschema:"description=User's age"`
Addresses []Address `json:"addresses" jsonschema:"description=List of addresses"`
}
// Instructor handles nested objects automatically
var user User
_, err := client.CreateChatCompletion(ctx, request, &user)
```
### Union types for agent patterns
Build flexible AI agents that can choose between multiple tools or actions:
```go
type SearchTool struct {
Type string `json:"type" jsonschema:"const=search"`
Query string `json:"query" jsonschema:"description=Search query"`
}
type FinishTool struct {
Type string `json:"type" jsonschema:"const=finish"`
Answer string `json:"answer" jsonschema:"description=Final answer"`
}
// Agent loop
for turn := 0; turn < maxTurns; turn++ {
action, _, err := client.CreateChatCompletionUnion(
ctx,
request,
instructor.UnionOptions{
Discriminator: "type",
Variants: []any{SearchTool{}, FinishTool{}},
},
)
switch tool := action.(type) {
case SearchTool:
result := executeSearch(tool.Query)
conversation.AddUserMessage(result)
case FinishTool:
return tool.Answer, nil
}
}
```
### Multi-provider conversation history
Unified conversation API that works across all providers:
```go
import "github.com/jxnl/instructor-go/pkg/instructor/core"
// Create a conversation with a system prompt
conversation := core.NewConversation("You are a helpful assistant")
// Add messages
conversation.AddUserMessage("What's the weather in SF?")
// Vision support
conversation.AddUserMessageWithImageURLs(
"What's in this image?",
"https://example.com/image.jpg",
)
// Convert to provider-specific format
messages := instructor_openai.ConversationToMessages(conversation)
// OR
system, messages := instructor_anthropic.ConversationToMessages(conversation)
// OR
contents := instructor_google.ConversationToContents(conversation)
```
### Token usage tracking
Automatic token counting across retries:
```go
resp, err := client.CreateChatCompletion(ctx, request, &user)
fmt.Printf("Input tokens: %d\n", resp.Usage.PromptTokens)
fmt.Printf("Output tokens: %d\n", resp.Usage.CompletionTokens)
fmt.Printf("Total tokens: %d\n", resp.Usage.TotalTokens)
// Usage is summed across all retry attempts
```
## Used in production
Trusted by developers building production AI applications in Go:
* **Type-safe** by design - catch errors at compile time
* **Zero reflection overhead** - uses code generation where possible
* **Battle-tested** across multiple LLM providers
* **Enterprise-ready** with comprehensive error handling
## Get started
### Basic extraction
Extract structured data from any text:
```go
import (
"context"
"fmt"
"os"
"github.com/jxnl/instructor-go/pkg/instructor"
"github.com/sashabaranov/go-openai"
)
type Product struct {
Name string `json:"name" jsonschema:"description=Product name"`
Price float64 `json:"price" jsonschema:"description=Price in USD"`
InStock bool `json:"in_stock" jsonschema:"description=Availability status"`
}
func main() {
ctx := context.Background()
client := instructor.FromOpenAI(openai.NewClient(os.Getenv("OPENAI_API_KEY")))
var product Product
_, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT4o,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "iPhone 15 Pro, $999, available now",
},
},
},
&product,
)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", product)
// Product{Name: "iPhone 15 Pro", Price: 999.0, InStock: true}
}
```
### Multiple languages
Instructor's simple API is available in many languages:
* **[Go](https://go.useinstructor.com)** - You are here
* [Python](https://python.useinstructor.com) - The original
* [TypeScript](https://js.useinstructor.com) - Full TypeScript support
* [Ruby](https://ruby.useinstructor.com) - Ruby implementation
* [Elixir](https://hex.pm/packages/instructor) - Elixir implementation
* [Rust](https://rust.useinstructor.com) - Rust implementation
### Learn more
* [Documentation](https://pkg.go.dev/github.com/jxnl/instructor-go/pkg/instructor) - Full API reference
* [Examples](examples/README.md) - Copy-paste recipes for common patterns
* [Discord](https://discord.gg/UD9GPjbs8c) - Get help from the community
## Why use Instructor Go over alternatives?
**vs Raw JSON mode**: Instructor provides automatic validation, retries, streaming, and nested object support. No manual schema writing or parsing.
**vs LangChain Go (or other frameworks)**: Instructor is focused on one thing - structured extraction. It's lighter, faster, and easier to debug with full type safety.
**vs Custom solutions**: Battle-tested across multiple providers and edge cases. Handles retries, validation, and provider differences automatically.
**vs Python/JS versions**: Native Go performance and type safety. No runtime overhead, compile-time guarantees, and seamless integration with Go codebases.
## Examples
See [`examples/`](examples/) for complete working examples:
* [`examples/user/`](examples/user/) - Basic extraction
* [`examples/agent/`](examples/agent/) - Union types and agent loops
* [`examples/anthropic_agent/`](examples/anthropic_agent/) - Multi-provider agents
* [`examples/streaming/`](examples/streaming/) - Streaming responses
* More examples in the [`examples/`](examples/) directory
## Contributing
We welcome contributions! Check out our [good first issues](https://github.com/jxnl/instructor-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to get started.
## License
MIT License - see [LICENSE](LICENSE) for details.
***
Built by the Instructor community. Part of the Instructor ecosystem.