Tutorial 6: Observability
Learn how to monitor and observe your SDK applications with the hooks package.
What You’ll Learn
Section titled “What You’ll Learn”- Subscribe to SDK events
- Monitor tool calls and responses
- Track conversation flow
- Debug issues
Why Observability?
Section titled “Why Observability?”Observability helps you:
- Debug - Understand what’s happening in your application
- Monitor - Track performance and usage
- Audit - Log all actions for compliance
- Optimize - Find bottlenecks and improve
Prerequisites
Section titled “Prerequisites”Complete Tutorial 1 and understand basic SDK usage.
Basic Event Subscription
Section titled “Basic Event Subscription”Use Subscribe() to listen for events:
package main
import ( "context" "fmt" "log"
"github.com/AltairaLabs/PromptKit/sdk" "github.com/AltairaLabs/PromptKit/sdk/hooks")
func main() { conv, err := sdk.Open("./hello.pack.json", "chat") if err != nil { log.Fatal(err) } defer conv.Close()
// Subscribe to send events conv.Subscribe(hooks.EventSend, func(e hooks.Event) { fmt.Printf("[SEND] Message: %v\n", e.Data["message"]) })
// Subscribe to response events conv.Subscribe(hooks.EventResponse, func(e hooks.Event) { fmt.Printf("[RESPONSE] Got reply\n") })
ctx := context.Background() resp, _ := conv.Send(ctx, "Hello!") fmt.Println(resp.Text())}Event Types
Section titled “Event Types”The hooks package provides these event types:
const ( EventSend = "send" // Message sent EventResponse = "response" // Response received EventToolCall = "tool_call" // Tool invoked EventToolResult = "tool_result" // Tool returned EventError = "error" // Error occurred EventStream = "stream" // Stream chunk received)Monitoring Tool Calls
Section titled “Monitoring Tool Calls”Track tool execution:
conv.Subscribe(hooks.EventToolCall, func(e hooks.Event) { toolName := e.Data["tool"].(string) args := e.Data["args"] fmt.Printf("[TOOL] Calling %s with %v\n", toolName, args)})
conv.Subscribe(hooks.EventToolResult, func(e hooks.Event) { toolName := e.Data["tool"].(string) result := e.Data["result"] fmt.Printf("[TOOL] %s returned: %v\n", toolName, result)})Logging All Events
Section titled “Logging All Events”Create a comprehensive logger:
func logAllEvents(conv *sdk.Conversation) { events := []string{ hooks.EventSend, hooks.EventResponse, hooks.EventToolCall, hooks.EventToolResult, hooks.EventError, hooks.EventStream, }
for _, event := range events { eventName := event // Capture for closure conv.Subscribe(eventName, func(e hooks.Event) { log.Printf("[%s] %s: %v", e.Timestamp.Format("15:04:05"), eventName, e.Data, ) }) }}
// Usageconv, _ := sdk.Open("./pack.json", "chat")logAllEvents(conv)Error Monitoring
Section titled “Error Monitoring”Track and alert on errors:
conv.Subscribe(hooks.EventError, func(e hooks.Event) { err := e.Data["error"]
// Log the error log.Printf("[ERROR] %v", err)
// Alert if critical if isCritical(err) { alertTeam(err) }})Stream Monitoring
Section titled “Stream Monitoring”Track streaming progress:
var charCount int
conv.Subscribe(hooks.EventStream, func(e hooks.Event) { chunk := e.Data["chunk"].(string) charCount += len(chunk)
// Log progress every 100 characters if charCount % 100 == 0 { log.Printf("[STREAM] %d characters received", charCount) }})Metrics Collection
Section titled “Metrics Collection”Collect metrics for monitoring systems:
type Metrics struct { MessageCount int64 ToolCalls int64 Errors int64 TotalTokens int64 mu sync.Mutex}
func (m *Metrics) Attach(conv *sdk.Conversation) { conv.Subscribe(hooks.EventSend, func(e hooks.Event) { m.mu.Lock() m.MessageCount++ m.mu.Unlock() })
conv.Subscribe(hooks.EventToolCall, func(e hooks.Event) { m.mu.Lock() m.ToolCalls++ m.mu.Unlock() })
conv.Subscribe(hooks.EventError, func(e hooks.Event) { m.mu.Lock() m.Errors++ m.mu.Unlock() })}Debug Mode
Section titled “Debug Mode”Enable verbose debugging:
func enableDebug(conv *sdk.Conversation) { conv.Subscribe(hooks.EventSend, func(e hooks.Event) { fmt.Printf("📤 SEND: %v\n", e.Data) })
conv.Subscribe(hooks.EventResponse, func(e hooks.Event) { fmt.Printf("📥 RESPONSE: %v\n", e.Data) })
conv.Subscribe(hooks.EventToolCall, func(e hooks.Event) { fmt.Printf("🔧 TOOL CALL: %s(%v)\n", e.Data["tool"], e.Data["args"]) })
conv.Subscribe(hooks.EventToolResult, func(e hooks.Event) { fmt.Printf("✅ TOOL RESULT: %v\n", e.Data["result"]) })
conv.Subscribe(hooks.EventError, func(e hooks.Event) { fmt.Printf("❌ ERROR: %v\n", e.Data["error"]) })}What You’ve Learned
Section titled “What You’ve Learned”✅ Subscribe to events with Subscribe()
✅ Monitor tool calls and responses
✅ Track errors and stream progress
✅ Build metrics collection
✅ Enable debug logging
Next Steps
Section titled “Next Steps”- How-To: Monitor Events - Advanced monitoring
- Explanation: Observability - Architecture deep-dive
Complete Example
Section titled “Complete Example”See observability patterns in sdk/examples/.