SDK Architecture
Understanding the pack-first design and component relationships.
Design Principles
Section titled “Design Principles”- Pack-First - Pack file is the single source of truth
- Minimal Boilerplate - 5 lines to hello world
- Type Safety - Leverage Go’s type system
- Thread Safe - All operations are concurrent-safe
- Extensible - Hooks and custom handlers
Architecture Overview
Section titled “Architecture Overview”Core Components
Section titled “Core Components”PackLoader
Section titled “PackLoader”Loads and validates pack files:
// Internal - used by sdk.Open()loader := packloader.New()pack, err := loader.Load("./app.pack.json")Responsibilities:
- Parse JSON pack files
- Validate schema
- Cache loaded packs
- Resolve prompt references
Conversation
Section titled “Conversation”The main interaction point:
type Conversation struct { pack *pack.Pack prompt *pack.Prompt state *ConversationState handlers map[string]ToolHandler eventBus *events.EventBus // ...}Responsibilities:
- Manage variables
- Register tool handlers
- Execute messages
- Maintain history
- Emit events
Hooks (EventBus)
Section titled “Hooks (EventBus)”Observability through events:
type Event struct { Type string Timestamp time.Time Data map[string]any}Event Flow:
conv.Send()emitsEventSend- Provider call executes
- Response emits
EventResponse - Tool calls emit
EventToolCall - Errors emit
EventError
Request Flow
Section titled “Request Flow”Tool Execution
Section titled “Tool Execution”State Management
Section titled “State Management”Variable Storage
Section titled “Variable Storage”type ConversationState struct { mu sync.RWMutex variables map[string]any messages []types.Message}Thread-safe access:
SetVar()acquires write lockGetVar()acquires read lock- Concurrent access safe
Message History
Section titled “Message History”// Append messagestate.AddMessage(types.Message{ Role: "user", Content: "Hello",})
// Get all messagesmessages := state.GetMessages()Thread Safety
Section titled “Thread Safety”All Conversation methods are thread-safe:
// Safe concurrent accessvar wg sync.WaitGroup
for i := 0; i < 10; i++ { wg.Add(1) go func(n int) { defer wg.Done() conv.SetVar(fmt.Sprintf("key_%d", n), n) }(i)}
wg.Wait()