State Management
Understanding conversation state and persistence in PromptKit.
What is State Management?
Section titled “What is State Management?”State management maintains conversation history across multiple turns. It allows LLMs to remember previous interactions.
Why Manage State?
Section titled “Why Manage State?”Context: LLMs need history to understand conversations
Continuity: Users expect the AI to remember
Multi-turn: Enable back-and-forth dialogue
Personalization: Remember user preferences
The Problem
Section titled “The Problem”LLMs are stateless:
// First messageresponse1 := llm.Complete("What's the capital of France?")// "Paris"
// Second message - no memory!response2 := llm.Complete("What about Germany?")// "What do you mean 'what about Germany'?"The Solution
Section titled “The Solution”Pass conversation history:
messages := []Message{ {Role: "user", Content: "What's the capital of France?"}, {Role: "assistant", Content: "Paris"}, {Role: "user", Content: "What about Germany?"},}response := llm.Complete(messages)// "The capital of Germany is Berlin"State in PromptKit
Section titled “State in PromptKit”Session-Based Architecture
Section titled “Session-Based Architecture”Each conversation has a session ID:
sessionID := "user-123"result, _ := pipeline.ExecuteWithSession(ctx, sessionID, "user", "Hello")Sessions enable:
- Multi-user support: Separate conversations
- History isolation: Users don’t see each other’s messages
- Concurrent access: Multiple requests per session
StateMiddleware
Section titled “StateMiddleware”Manages state automatically:
store := statestore.NewRedisStateStore(redisClient)
stateMiddleware := middleware.StateMiddleware(store, &middleware.StateMiddlewareConfig{ MaxMessages: 10, TTL: 24 * time.Hour,})
pipe := pipeline.NewPipeline( stateMiddleware, // Loads history before, saves after middleware.ProviderMiddleware(provider, nil, nil, nil),)Before execution: Loads history
After execution: Saves new messages
State Stores
Section titled “State Stores”In-Memory Store
Section titled “In-Memory Store”Fast, but not persistent:
store := statestore.NewInMemoryStateStore()Pros: Very fast (~1-10µs)
Cons: Lost on restart, single-instance only
Use for: Development, testing, demos
Redis Store
Section titled “Redis Store”Persistent and scalable:
redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379",})store := statestore.NewRedisStateStore(redisClient)Pros: Persistent, multi-instance, TTL support
Cons: Slower (~1-5ms), requires Redis
Use for: Production, distributed systems
Configuration Options
Section titled “Configuration Options”Message Limits
Section titled “Message Limits”Control history size:
config := &middleware.StateMiddlewareConfig{ MaxMessages: 20, // Keep last 20 messages}Benefits:
- Lower costs (fewer tokens)
- Faster loading
- More relevant context
Time-To-Live (TTL)
Section titled “Time-To-Live (TTL)”Auto-delete old sessions:
config := &middleware.StateMiddlewareConfig{ TTL: 24 * time.Hour, // Delete after 24h}Benefits:
- Automatic cleanup
- Privacy compliance
- Cost reduction
Session Patterns
Section titled “Session Patterns”User Sessions
Section titled “User Sessions”One session per user:
sessionID := fmt.Sprintf("user-%s", userID)Use case: Single ongoing conversation per user
Conversation Sessions
Section titled “Conversation Sessions”Multiple conversations per user:
sessionID := fmt.Sprintf("user-%s-conv-%s", userID, conversationID)Use case: User can start multiple conversations
Temporary Sessions
Section titled “Temporary Sessions”Anonymous sessions:
sessionID := uuid.New().String()Use case: Guest users, no account required
Best Practices
Section titled “Best Practices”✅ Use Redis in production
// Productionstore := statestore.NewRedisStateStore(redisClient)
// Developmentstore := statestore.NewInMemoryStateStore()✅ Set appropriate limits
// Balance context vs costMaxMessages: 10-20 // Good for most cases✅ Set TTL for privacy
TTL: 24 * time.Hour // Delete old conversations✅ Handle errors gracefully
messages, err := store.Load(sessionID)if err != nil { // Continue with empty history messages = []Message{}}Don’ts
Section titled “Don’ts”❌ Don’t store infinite history - Cost and performance
❌ Don’t use in-memory in production - Not persistent
❌ Don’t forget to clean up - Privacy and storage
❌ Don’t ignore errors - Handle store failures
Multi-Instance Scaling
Section titled “Multi-Instance Scaling”With Redis
Section titled “With Redis”User A → [Instance 1] ↘ [Redis Store]User B → [Instance 2] ↗Benefits:
- High availability
- Horizontal scaling
- Shared state
Session Affinity
Section titled “Session Affinity”Route user to same instance:
User (session-123) → Instance 1 (every time)User (session-456) → Instance 2 (every time)Benefits:
- Local caching
- Reduced Redis load
- Lower latency
Performance Optimization
Section titled “Performance Optimization”Limit History Size
Section titled “Limit History Size”// Fast: Load 10 messagesMaxMessages: 10
// Slow: Load all messagesMaxMessages: -1 // UnlimitedLazy Loading
Section titled “Lazy Loading”Load on demand:
// Only load when neededif requiresHistory { messages, _ := store.Load(sessionID)}Compression
Section titled “Compression”For large histories:
compressed := gzip.Compress(messages)store.Save(sessionID, compressed)Monitoring State
Section titled “Monitoring State”Track Metrics
Section titled “Track Metrics”type StateMetrics struct { ActiveSessions int AvgHistorySize int LoadLatency time.Duration StorageUsed int64}Set Alerts
Section titled “Set Alerts”if metrics.ActiveSessions > 10000 { alert.Send("High active session count")}
if metrics.LoadLatency > 100*time.Millisecond { alert.Send("Slow state loading")}Testing State Management
Section titled “Testing State Management”Unit Tests
Section titled “Unit Tests”func TestStateStore(t *testing.T) { store := statestore.NewInMemoryStateStore()
// Save messages messages := []types.Message{ {Role: "user", Content: "Hello"}, } err := store.Save("session-1", messages) assert.NoError(t, err)
// Load messages loaded, err := store.Load("session-1") assert.NoError(t, err) assert.Equal(t, messages, loaded)}Integration Tests
Section titled “Integration Tests”func TestConversationFlow(t *testing.T) { pipe := createPipelineWithState() sessionID := "test-session"
// First message result1, _ := pipe.ExecuteWithSession(ctx, sessionID, "user", "My name is Alice")
// Second message - should remember result2, _ := pipe.ExecuteWithSession(ctx, sessionID, "user", "What's my name?")
assert.Contains(t, result2.Response.Content, "Alice")}Summary
Section titled “Summary”State management provides:
✅ Context - LLMs remember conversations
✅ Continuity - Multi-turn dialogue
✅ Scalability - Redis for distributed systems
✅ Performance - Configurable limits
✅ Privacy - TTL-based cleanup
Related Documentation
Section titled “Related Documentation”- State Management Explanation - Architecture details
- Manage State How-To - Implementation guide
- Multi-turn Tutorial - Step-by-step guide
- StateStore Reference - API documentation