Variable Providers API Reference
Complete reference for dynamic variable resolution.
Provider Interface
Section titled “Provider Interface”type Provider interface { Name() string Provide(ctx context.Context) (map[string]string, error)}Methods
Section titled “Methods”func (p Provider) Name() stringReturns the provider identifier for logging and debugging.
Provide
Section titled “Provide”func (p Provider) Provide(ctx context.Context) (map[string]string, error)Resolves variables dynamically. Called before each template render.
Returns:
map[string]string: Variables to inject into template contexterror: Error if resolution fails
SDK Option
Section titled “SDK Option”WithVariableProvider
Section titled “WithVariableProvider”func WithVariableProvider(provider variables.Provider) OptionConfigures a variable provider for dynamic variable resolution.
Example:
conv, _ := sdk.Open("./pack.json", "chat", sdk.WithVariableProvider(variables.NewTimeProvider()),)Multiple providers can be configured:
conv, _ := sdk.Open("./pack.json", "chat", sdk.WithVariableProvider(variables.NewTimeProvider()), sdk.WithVariableProvider(&myRAGProvider{}),)Built-in Providers
Section titled “Built-in Providers”TimeProvider
Section titled “TimeProvider”Injects current time and date information.
func NewTimeProvider() ProviderVariables Provided:
| Variable | Format | Example |
|---|---|---|
current_time | 15:04:05 | ”14:30:45” |
current_date | 2006-01-02 | ”2025-01-15” |
current_datetime | 2006-01-02 15:04:05 | ”2025-01-15 14:30:45” |
day_of_week | Monday | ”Wednesday” |
timezone | Zone name | ”America/New_York” |
Example:
provider := variables.NewTimeProvider()
conv, _ := sdk.Open("./pack.json", "chat", sdk.WithVariableProvider(provider),)
// In pack template:// "The current time is {{current_time}} on {{day_of_week}}."StateProvider
Section titled “StateProvider”Extracts variables from conversation state metadata.
func NewStateProvider(store statestore.Store, conversationID string) ProviderParameters:
store: State store containing conversation metadataconversationID: ID of the conversation to read from
Variables Provided:
Any key-value pairs stored in the conversation’s metadata.
Example:
store := statestore.NewMemoryStore()convID := "conv-123"
// Store some metadatastate, _ := store.Load(ctx, convID)state.Metadata["user_tier"] = "premium"state.Metadata["language"] = "en"store.Save(ctx, convID, state)
// Use StateProviderprovider := variables.NewStateProvider(store, convID)
conv, _ := sdk.Open("./pack.json", "chat", sdk.WithStateStore(store), sdk.WithConversationID(convID), sdk.WithVariableProvider(provider),)
// In pack template:// "User tier: {{user_tier}}, Language: {{language}}"ChainProvider
Section titled “ChainProvider”Combines multiple providers in sequence.
func NewChainProvider(providers ...Provider) ProviderResolution Order:
- Providers are called in order
- Later providers override earlier ones for the same key
- Errors from any provider stop the chain
Example:
chain := variables.NewChainProvider( variables.NewTimeProvider(), // Base time variables variables.NewStateProvider(store, convID), // User state &myRAGProvider{}, // RAG context)
conv, _ := sdk.Open("./pack.json", "chat", sdk.WithVariableProvider(chain),)Creating Custom Providers
Section titled “Creating Custom Providers”Basic Provider
Section titled “Basic Provider”type MyProvider struct { data map[string]string}
func (p *MyProvider) Name() string { return "my_provider"}
func (p *MyProvider) Provide(ctx context.Context) (map[string]string, error) { return p.data, nil}Database Provider
Section titled “Database Provider”type DatabaseProvider struct { db *sql.DB userID string}
func NewDatabaseProvider(db *sql.DB, userID string) *DatabaseProvider { return &DatabaseProvider{db: db, userID: userID}}
func (p *DatabaseProvider) Name() string { return "database"}
func (p *DatabaseProvider) Provide(ctx context.Context) (map[string]string, error) { var name, email string err := p.db.QueryRowContext(ctx, "SELECT name, email FROM users WHERE id = $1", p.userID, ).Scan(&name, &email) if err != nil { return nil, fmt.Errorf("failed to load user: %w", err) }
return map[string]string{ "user_name": name, "user_email": email, }, nil}RAG Provider
Section titled “RAG Provider”type RAGProvider struct { vectorDB VectorDatabase topK int}
func NewRAGProvider(db VectorDatabase, topK int) *RAGProvider { return &RAGProvider{vectorDB: db, topK: topK}}
func (p *RAGProvider) Name() string { return "rag"}
func (p *RAGProvider) Provide(ctx context.Context) (map[string]string, error) { // Get query from context (set by pipeline) query, ok := ctx.Value("current_query").(string) if !ok || query == "" { return nil, nil }
results, err := p.vectorDB.Search(ctx, query, p.topK) if err != nil { return nil, err }
var context strings.Builder for i, r := range results { context.WriteString(fmt.Sprintf("[%d] %s\n", i+1, r.Text)) }
return map[string]string{ "rag_context": context.String(), "num_sources": strconv.Itoa(len(results)), }, nil}API Provider
Section titled “API Provider”type WeatherProvider struct { apiKey string location string}
func (p *WeatherProvider) Name() string { return "weather"}
func (p *WeatherProvider) Provide(ctx context.Context) (map[string]string, error) { resp, err := http.Get(fmt.Sprintf( "https://api.weather.com/v1/current?location=%s&key=%s", p.location, p.apiKey, )) if err != nil { return nil, err } defer resp.Body.Close()
var weather struct { Temp float64 `json:"temperature"` Condition string `json:"condition"` } json.NewDecoder(resp.Body).Decode(&weather)
return map[string]string{ "weather_temp": fmt.Sprintf("%.1f°C", weather.Temp), "weather_condition": weather.Condition, }, nil}Error Handling
Section titled “Error Handling”Graceful Degradation
Section titled “Graceful Degradation”func (p *OptionalProvider) Provide(ctx context.Context) (map[string]string, error) { result, err := p.fetchData(ctx) if err != nil { // Log but don't fail log.Printf("Provider %s failed: %v", p.Name(), err) return map[string]string{ "optional_data": "Not available", }, nil } return result, nil}Timeout Handling
Section titled “Timeout Handling”func (p *SlowProvider) Provide(ctx context.Context) (map[string]string, error) { ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel()
result, err := p.slowOperation(ctx) if errors.Is(err, context.DeadlineExceeded) { return map[string]string{ "slow_data": "Loading...", }, nil } if err != nil { return nil, err } return result, nil}Resolution Order
Section titled “Resolution Order”When multiple providers are configured:
- Static variables from
SetVar()/SetVars()are set first - Variable providers are called in configuration order
- Each provider can override variables from previous providers
- Final variables are used for template rendering
conv.SetVar("greeting", "Hello") // Static: greeting = "Hello"
// Provider 1: greeting = "Hi", name = "User"// Provider 2: name = "Alice"
// Final: greeting = "Hi", name = "Alice"Best Practices
Section titled “Best Practices”- Keep Providers Fast: Called on every
Send(), avoid slow operations - Cache Expensive Lookups: Use TTL-based caching for external calls
- Handle Errors Gracefully: Return defaults instead of failing
- Use Timeouts: Prevent slow providers from blocking
- Log Failures: Track provider issues for debugging
- Test Independently: Unit test providers separately
See Also
Section titled “See Also”- Variable Providers Tutorial - Getting started
- State Management - Conversation state
- Template Variables - Template syntax