Status: Core modules complete ✅ | Experimentation-ready ✅ | Infrastructure utilities in progress 🚧
See ROADMAP.md for detailed progress
DSGo is a Go implementation of the DSPy framework for programming language models. Build production-ready LM applications with type-safe signatures, composable modules, and robust parsing.
Quick Links:
- ✅ Global Configuration: Centralized settings with environment variables + dynamic LM factory
- ✅ LM Factory Pattern: Dynamic provider/model switching via
dsgo.NewLM(ctx) - ✅ Signatures: Define structured inputs and outputs for LM calls
- ✅ Type Safety: Strong typing with validation for inputs and outputs
- ✅ Production-Grade Robustness:
- JSON Repair: Automatic fixing of malformed JSON (
{key: 'value'}→{"key": "value"}) - Partial Outputs: Validation diagnostics for training/optimization loops
- Class Normalization: Case-insensitive + alias matching (
"POSITIVE"→"positive") - Smart Extraction: Numeric values from text (
"High (95%)"→95)
- JSON Repair: Automatic fixing of malformed JSON (
- ✅ Robust Adapters: Multiple parsing strategies with automatic fallback
JSONAdapter: Structured JSON with repair + schema validationChatAdapter: Field marker format[[ ## field ## ]]with heuristicsTwoStepAdapter: Two-stage for reasoning models (o1/o3/gpt-5)FallbackAdapter: Automatic retry chain (Chat → JSON) with >95% parse success
- ✅ Modules: Composable building blocks for LM programs
Predict: Basic prediction moduleChainOfThought: Step-by-step reasoningReAct: Reasoning and Acting with tool supportRefine: Iterative refinement of predictionsBestOfN: Generate N solutions and select the bestProgramOfThought: Code generation and execution for reasoningProgram: Compose modules into pipelines
- ✅ LM Abstraction: Easy integration with different language models
- ✅ Tool Support: Define and use tools in ReAct agents
- ✅ Structured Outputs: JSON-based structured responses with validation
- ✅ Observability: Adapter metrics tracking (parse success, fallback usage, repair usage)
go get github.com/assagman/dsgopackage main
import (
"context"
"fmt"
"log"
"time"
"github.com/assagman/dsgo"
"github.com/assagman/dsgo/module"
"github.com/assagman/dsgo/providers/openai" // or providers/openrouter
)
func main() {
// Configure global settings
dsgo.Configure(
dsgo.WithProvider("openai"),
dsgo.WithModel("gpt-4"),
dsgo.WithTimeout(30*time.Second),
)
// Create signature
sig := dsgo.NewSignature("Analyze the sentiment of the given text").
AddInput("text", dsgo.FieldTypeString, "The text to analyze").
AddClassOutput("sentiment", []string{"positive", "negative", "neutral"}, "The sentiment").
AddOutput("confidence", dsgo.FieldTypeFloat, "Confidence score")
// Create language model from config (or use openai.NewOpenAI("gpt-4") directly)
ctx := context.Background()
lm, err := dsgo.NewLM(ctx)
if err != nil {
log.Fatal(err)
}
// Create Predict module
predict := module.NewPredict(sig, lm)
// Execute
ctx := context.Background()
inputs := map[string]interface{}{
"text": "I love this product!",
}
result, err := predict.Forward(ctx, inputs)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Sentiment: %v (Confidence: %v)\n",
result.GetString("sentiment"), result.GetFloat("confidence"))
}sig := dsgo.NewSignature("Solve the math word problem").
AddInput("problem", dsgo.FieldTypeString, "The problem").
AddOutput("answer", dsgo.FieldTypeFloat, "The answer").
AddOutput("explanation", dsgo.FieldTypeString, "Step-by-step solution")
lm := openai.NewOpenAI("gpt-4")
cot := module.NewChainOfThought(sig, lm)
result, err := cot.Forward(ctx, map[string]interface{}{
"problem": "If John has 5 apples and gives 2 away, how many does he have?",
})
// Access outputs: result.GetFloat("answer"), result.GetString("explanation")// Define a search tool
searchTool := dsgo.NewTool(
"search",
"Search for information",
func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
query := args["query"].(string)
return performSearch(query), nil
},
).AddParameter("query", "string", "Search query", true)
// Create ReAct module
sig := dsgo.NewSignature("Answer questions using available tools").
AddInput("question", dsgo.FieldTypeString, "The question").
AddOutput("answer", dsgo.FieldTypeString, "The answer")
lm := openai.NewOpenAI("gpt-4")
react := module.NewReAct(sig, lm, []dsgo.Tool{searchTool}).
WithMaxIterations(5).
WithVerbose(true)
result, err := react.Forward(ctx, map[string]interface{}{
"question": "What is DSPy?",
})
// Access result: result.GetString("answer")// Complex signature with diverse input/output types
sig := dsgo.NewSignature("Research and analyze a topic").
// Multiple input types
AddInput("topic", dsgo.FieldTypeString, "Research topic").
AddInput("depth_level", dsgo.FieldTypeInt, "Depth: 1-3").
AddInput("include_stats", dsgo.FieldTypeBool, "Include statistics").
// Multiple output types with constraints
AddOutput("summary", dsgo.FieldTypeString, "Executive summary").
AddOutput("key_findings", dsgo.FieldTypeString, "Main discoveries").
AddClassOutput("confidence", []string{"high", "medium", "low"}, "Confidence").
AddOutput("sources_count", dsgo.FieldTypeInt, "Number of sources").
AddOptionalOutput("statistics", dsgo.FieldTypeString, "Stats if requested")
// Use with ReAct and multiple tools
tools := []dsgo.Tool{searchTool, statsTool, factCheckTool}
react := module.NewReAct(sig, lm, tools).WithMaxIterations(7)
result, err := react.Forward(ctx, map[string]interface{}{
"topic": "AI in software development",
"depth_level": 2,
"include_stats": true,
})
// Access: result.GetString("summary"), result.GetInt("sources_count"), etc.DSGo provides centralized configuration management for consistent settings across your application:
import (
"time"
"github.com/assagman/dsgo"
)
// Configure via functional options
dsgo.Configure(
dsgo.WithProvider("openrouter"),
dsgo.WithModel("google/gemini-2.5-flash"),
dsgo.WithTimeout(30*time.Second),
dsgo.WithMaxRetries(3),
dsgo.WithTracing(true),
dsgo.WithAPIKey("openrouter", "your-api-key"),
)
// Access current settings
settings := dsgo.GetSettings()
fmt.Printf("Model: %s\n", settings.DefaultModel)Environment Variable Support:
DSGo automatically loads configuration from environment variables:
DSGO_PROVIDER- Default provider (e.g., "openai", "openrouter")DSGO_MODEL- Default model identifierDSGO_TIMEOUT- Timeout in secondsDSGO_MAX_RETRIES- Number of retries for failed callsDSGO_TRACING- Enable tracing ("true" or "false")DSGO_OPENAI_API_KEY- OpenAI API keyDSGO_OPENROUTER_API_KEY- OpenRouter API keyOPENAI_API_KEY,OPENROUTER_API_KEY- Fallback API keys
Configuration Priority:
- Environment variables are loaded first
- Functional options override environment variables
- Later calls to
Configure()override earlier calls
// Environment: DSGO_MODEL=gpt-4
dsgo.Configure(
dsgo.WithModel("google/gemini-2.5-flash"), // Overrides env
)Available Options:
WithProvider(string)- Set default providerWithModel(string)- Set default modelWithTimeout(time.Duration)- Set default timeoutWithLM(LM)- Set default LM instanceWithAPIKey(provider, key string)- Set provider API keyWithMaxRetries(int)- Set retry countWithTracing(bool)- Enable/disable tracing
Create LM instances dynamically from global configuration:
import (
"github.com/assagman/dsgo"
_ "github.com/assagman/dsgo/providers/openai" // Auto-registers "openai"
_ "github.com/assagman/dsgo/providers/openrouter" // Auto-registers "openrouter"
)
// Configure once
dsgo.Configure(
dsgo.WithProvider("openrouter"),
dsgo.WithModel("google/gemini-2.5-flash"),
)
// Create LM from configuration
ctx := context.Background()
lm, err := dsgo.NewLM(ctx)
if err != nil {
log.Fatal(err)
}
// Switch provider dynamically
dsgo.Configure(dsgo.WithProvider("openai"), dsgo.WithModel("gpt-4"))
lm2, _ := dsgo.NewLM(ctx)Benefits:
- Centralized Configuration: Set provider/model once, use everywhere
- Easy Switching: Change providers without code changes
- Environment-Based: Use env vars for deployment flexibility
- Auto-Registration: Providers register themselves via
init()
See examples/lm_factory for complete usage patterns.
Signatures define the structure of your LM program's inputs and outputs:
sig := dsgo.NewSignature("Description of the task").
AddInput("field_name", dsgo.FieldTypeString, "Field description").
AddOutput("result", dsgo.FieldTypeString, "Result description").
AddClassOutput("category", []string{"A", "B", "C"}, "Classification")Supported Field Types:
graph TD
subgraph "Primitive Types"
STR[String<br/>Text data]
INT[Int<br/>Integers]
FLT[Float<br/>Decimals]
BOOL[Bool<br/>true/false]
end
subgraph "Structured Types"
JSON[JSON<br/>Complex objects]
CLS[Class<br/>Enum/Categories]
end
subgraph "Rich Types"
IMG[Image<br/>Visual data]
DT[Datetime<br/>Timestamps]
end
SIG[Signature] --> STR & INT & FLT & BOOL
SIG --> JSON & CLS
SIG --> IMG & DT
classDef prim fill:#06d6a0,stroke:#073b4c,color:#000
classDef struct fill:#118ab2,stroke:#073b4c,color:#fff
classDef rich fill:#ef476f,stroke:#073b4c,color:#fff
classDef sig fill:#ffd166,stroke:#073b4c,color:#000
class STR,INT,FLT,BOOL prim
class JSON,CLS struct
class IMG,DT rich
class SIG sig
Field Types:
FieldTypeString- Text dataFieldTypeInt- Integer valuesFieldTypeFloat- Decimal numbersFieldTypeBool- Boolean (true/false)FieldTypeJSON- Complex structured dataFieldTypeClass- Enum/classification (constrained choices)FieldTypeImage- Image data (URLs or base64) [partial support]FieldTypeDatetime- Timestamps and dates
Modules are composable building blocks that implement different execution strategies:
graph LR
subgraph "Simple"
P[Predict<br/>Direct answer]
end
subgraph "Reasoning"
COT[ChainOfThought<br/>Step-by-step thinking]
POT[ProgramOfThought<br/>Code generation]
end
subgraph "Interactive"
R[ReAct<br/>Reasoning + Tools]
REF[Refine<br/>Iterative improvement]
end
subgraph "Sampling"
BON[BestOfN<br/>Generate & select best]
end
subgraph "Composition"
PRG[Program<br/>Module pipelines]
end
P -.upgrade.-> COT
COT -.add tools.-> R
P -.iterate.-> REF
P -.sample.-> BON
P & COT & R -.chain.-> PRG
classDef simple fill:#2a9d8f,stroke:#264653,color:#fff
classDef reason fill:#e76f51,stroke:#264653,color:#fff
classDef interact fill:#f4a261,stroke:#264653,color:#fff
classDef sample fill:#e9c46a,stroke:#264653,color:#fff
classDef compose fill:#264653,stroke:#264653,color:#fff
class P simple
class COT,POT reason
class R,REF interact
class BON sample
class PRG compose
Module Descriptions:
- Predict: Direct prediction based on signature
- ChainOfThought: Encourages step-by-step reasoning before answering
- ReAct: Combines reasoning with tool usage in iterative cycles
- Refine: Iteratively improve predictions with feedback
- BestOfN: Generate multiple candidates and select the best (supports early stopping with threshold)
- ProgramOfThought: Generate and execute code for reasoning tasks (with timeout enforcement)
- Program: Chain modules into pipelines for complex workflows
Implement the LM interface to add support for different providers:
type LM interface {
Generate(ctx context.Context, messages []Message, options *GenerateOptions) (*GenerateResult, error)
Name() string
SupportsJSON() bool
SupportsTools() bool
}Thread Safety: All modules automatically copy GenerateOptions before mutation to prevent race conditions and unexpected side effects when sharing options across goroutines.
// Safe to share options across modules
opts := dsgo.DefaultGenerateOptions()
opts.Temperature = 0.8
predict1 := module.NewPredict(sig1, lm).WithOptions(opts)
predict2 := module.NewPredict(sig2, lm).WithOptions(opts)
// Each module gets its own copy internallyIncluded Providers:
- OpenAI - GPT-3.5, GPT-4, GPT-4 Turbo
- OpenRouter - Access to 100+ models
Define tools for ReAct agents with automatic argument validation:
tool := dsgo.NewTool(
"tool_name",
"Description of what the tool does",
func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
// Tool implementation
return result, nil
},
).AddParameter("param", "string", "Parameter description", true) // Required parameter
.AddEnumParameter("mode", "Operation mode", []string{"fast", "accurate"}, false) // Optional enum
// Validation happens automatically before execution
result, err := tool.Execute(ctx, args) // Returns error if required params missing or enum invalidValidation Features:
- Required parameters are checked automatically
- Enum parameters validated against allowed values
- Clear error messages for validation failures
DSGo follows the DSPy philosophy of declarative, modular programming with language models:
graph TB
subgraph "Core Components"
S[Signature<br/>Define I/O Structure]
M[Module<br/>Execution Logic]
LM[LM Interface<br/>Model Abstraction]
end
subgraph "Module Types"
P[Predict]
COT[ChainOfThought]
R[ReAct]
REF[Refine]
BON[BestOfN]
POT[ProgramOfThought]
PRG[Program]
end
subgraph "Primitives"
T[Tools]
H[History]
PR[Prediction]
E[Examples]
end
subgraph "Providers"
OAI[OpenAI]
OR[OpenRouter]
end
S --> M
M --> LM
M --> P & COT & R & REF & BON & POT & PRG
R --> T
M --> H & PR & E
LM --> OAI & OR
classDef core fill:#1a1a2e,stroke:#16213e,color:#eee
classDef module fill:#0f3460,stroke:#16213e,color:#eee
classDef prim fill:#533483,stroke:#16213e,color:#eee
classDef provider fill:#e94560,stroke:#16213e,color:#eee
class S,M,LM core
class P,COT,R,REF,BON,POT,PRG module
class T,H,PR,E prim
class OAI,OR provider
- Declarative: Define what you want, not how to prompt
- Modular: Compose complex behaviors from simple modules
- Type-Safe: Strong typing with validation
- Tool-Enabled: Easy integration with external tools
sequenceDiagram
participant User
participant Module
participant Signature
participant LM
participant Tool
User->>Signature: Define inputs/outputs
User->>Module: Create (Predict/CoT/ReAct)
User->>Module: Forward(inputs)
Module->>Signature: ValidateInputs()
Module->>Signature: BuildPrompt()
Module->>LM: Generate(messages)
alt ReAct with Tools
LM-->>Module: Tool call request
Module->>Tool: Execute(args)
Tool-->>Module: Result
Module->>LM: Generate(with tool result)
end
LM-->>Module: Response
Module->>Signature: ValidateOutputs()
Module-->>User: Outputs
dsgo/
├── signature.go # Signature system (Field, Signature types)
├── lm.go # Language Model interface
├── module.go # Module interface
├── prediction.go # Prediction wrapper with metadata
├── history.go # Conversation history management
├── example.go # Few-shot learning support
├── tool.go # Tool/function definitions
├── adapter.go # Adapter interface + implementations
├── cache.go # LRU caching layer
├── *_test.go # Unit tests
├── internal/
│ └── jsonutil/ # JSON extraction/repair utilities
├── logging/ # Structured logging and tracing
│ └── README.md # Logging documentation
├── module/
│ ├── predict.go # Basic Predict module
│ ├── chain_of_thought.go # ChainOfThought module
│ ├── react.go # ReAct module with tool support
│ ├── refine.go # Refine module for iterative improvement
│ ├── best_of_n.go # BestOfN module for multiple sampling
│ ├── program_of_thought.go # ProgramOfThought module for code generation
│ └── program.go # Program structure for module composition
├── providers/
│ ├── openai/ # OpenAI LM provider
│ └── openrouter/ # OpenRouter LM provider
├── scripts/
│ └── test_examples_matrix/ # Unified example testing
├── examples/ # 20+ working examples
│ ├── shared/ # Shared provider utilities
│ ├── sentiment/ # Basic prediction & chain-of-thought
│ ├── chat_predict/ # Multi-turn conversation
│ ├── react_agent/ # ReAct agent with tools
│ ├── research_assistant/ # Complex signatures + multiple tools
│ ├── logging_tracing/ # Request ID propagation & observability
│ ├── fewshot_conversation/ # Few-shot learning
│ ├── composition/ # Module pipelines
│ ├── caching/ # LRU cache usage
│ ├── streaming/ # Real-time streaming
│ ├── observability/ # Metadata, tracking, streaming observability
│ └── ... # 10+ more examples
├── QUICKSTART.md # Get started in 30 seconds
├── AGENTS.md # Development guide
├── ROADMAP.md # Implementation roadmap
└── README.md # This file
Current Status: ROADMAP.md
Completed ✅:
- Core modules (Predict, ChainOfThought, ReAct, Refine, BestOfN, ProgramOfThought, Program)
- Robust adapters (JSON, Chat, TwoStep, Fallback)
- JSON repair, partial validation, class normalization
- Logging and caching infrastructure
- 20+ working examples
Recently Completed ✅:
- Streaming observability and metrics (Phase 4.2)
In Progress 🚧:
- Cache key improvements
- Advanced retry mechanisms
- Disk-backed cache persistence
Planned:
- Additional LM providers (Anthropic, Google, Groq, Cerebras)
- Evaluation framework
- Optimizer framework (future)
Generate multiple solutions and select the best, with optional early stopping when a threshold is met:
// Scorer function signature: func(inputs, prediction) (score, error)
scorer := func(inputs map[string]any, pred *dsgo.Prediction) (float64, error) {
confidence := pred.GetFloat("confidence")
return confidence, nil
}
bestOfN := module.NewBestOfN(predict, 5).
WithScorer(scorer).
WithThreshold(0.9). // Stop early if score >= 0.9
WithParallel(true). // Generate in parallel (requires stateless modules!)
WithReturnAll(true) // Include all scores in metadata
result, err := bestOfN.Forward(ctx, inputs)
// Returns early if any result scores >= 0.9
// Access metadata: result.Score, result.CompletionsScoring Functions:
DefaultScorer()- Prefers longer outputsConfidenceScorer(field)- Uses a confidence field from outputs- Custom function:
func(inputs map[string]any, prediction *dsgo.Prediction) (float64, error)
Generate and execute code with guaranteed timeout enforcement:
pot := module.NewProgramOfThought(sig, lm, "python").
WithAllowExecution(true).
WithExecutionTimeout(10) // 10 second timeout, enforced via context
result, err := pot.Forward(ctx, inputs)
// Code execution is cancelled after 10 seconds
// Timeout error returned in result["execution_error"]Features:
- Context-based timeout enforcement (prevents hanging)
- Supports Python, JavaScript, Go (limited)
- Captures both stdout and stderr
- Safe by default (execution disabled)
Use examples to guide LM behavior with random sampling support:
examples := dsgo.NewExampleSet("sentiment examples")
examples.AddPair(
map[string]any{"text": "Love it!"},
map[string]any{"sentiment": "positive"},
).AddPair(
map[string]any{"text": "Terrible experience"},
map[string]any{"sentiment": "negative"},
)
// Get random subset of examples
randomExamples := examples.GetRandom(3) // Returns 3 random examples
formatted, _ := examples.FormatExamples(signature)ExampleSet Methods:
Add(example)- Add a single exampleAddPair(inputs, outputs)- Quick add with mapsGet()- Get all examplesGetN(n)- Get first n examplesGetRandom(n)- Get n random examples (useful for diverse sampling)Len(),IsEmpty(),Clear(),Clone()
All modules automatically copy GenerateOptions to prevent mutation:
// Original options remain unchanged
opts := dsgo.DefaultGenerateOptions()
opts.Temperature = 0.8
predict := module.NewPredict(sig, lm).WithOptions(opts)
// Module internally calls opts.Copy() - original opts unchanged
// Manual copy
optsCopy := opts.Copy() // Deep copy including slices- QUICKSTART.md - Get started in 30 seconds
- AGENTS.md - Development and testing guide
- ROADMAP.md - Implementation progress and roadmap
- logging/README.md - Logging and request tracing
Contributions are welcome! This is an early-stage implementation.
Install golangci-lint v2.6.0 (required for linting):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.6.0Note: go install only installs v1.x; v2.x requires binary installation.
# Run all tests
make test
# Run tests with coverage
make test-cover
# Run tests with race detector
make test-race
# Run checks (fmt, vet, build)
make check
# Run checks and lint (requires golangci-lint v2.6.0)
make check-lintMIT License