Observability
Understanding the event system in SDK v2.
Overview
SDK v2 uses an event-based observability system through the hooks package. Events are emitted at key points during execution, allowing you to monitor, debug, and audit your applications.
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
)
Event Flow
conv.Send(ctx, "Hello")
│
▼
EventSend ─────────► Subscriber
│
▼
Provider Call
│
▼
EventResponse ─────► Subscriber
│
│ (if tool call)
├────────────────┐
│ ▼
│ EventToolCall ──► Subscriber
│ │
│ Handler executes
│ │
│ EventToolResult ─► Subscriber
│ │
└────────────────┘
│
▼
Return Response
Subscribing to Events
import "github.com/AltairaLabs/PromptKit/sdk/hooks"
conv.Subscribe(hooks.EventSend, func(e hooks.Event) {
log.Printf("Sent: %v", e.Data["message"])
})
Event Structure
type Event struct {
Type string // Event type (EventSend, etc.)
Timestamp time.Time // When the event occurred
Data map[string]any // Event-specific data
}
Event Data
EventSend
e.Data["message"] // The message sent
EventResponse
e.Data["text"] // Response text
e.Data["tokens"] // Token count (if available)
EventToolCall
e.Data["tool"] // Tool name
e.Data["args"] // Tool arguments
EventToolResult
e.Data["tool"] // Tool name
e.Data["result"] // Tool result
EventError
e.Data["error"] // The error
Use Cases
Logging
func attachLogger(conv *sdk.Conversation) {
events := []string{
hooks.EventSend,
hooks.EventResponse,
hooks.EventToolCall,
hooks.EventError,
}
for _, event := range events {
name := event
conv.Subscribe(name, func(e hooks.Event) {
log.Printf("[%s] %s: %v",
e.Timestamp.Format("15:04:05"),
name,
e.Data,
)
})
}
}
Metrics
type Metrics struct {
Messages int64
ToolCalls int64
Errors int64
mu sync.Mutex
}
func (m *Metrics) Attach(conv *sdk.Conversation) {
conv.Subscribe(hooks.EventSend, func(e hooks.Event) {
m.mu.Lock()
m.Messages++
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()
})
}
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\n")
})
conv.Subscribe(hooks.EventToolCall, func(e hooks.Event) {
fmt.Printf("🔧 TOOL: %s(%v)\n", e.Data["tool"], e.Data["args"])
})
conv.Subscribe(hooks.EventError, func(e hooks.Event) {
fmt.Printf("❌ ERROR: %v\n", e.Data["error"])
})
}
Thread Safety
Event handlers are called synchronously on the goroutine that triggered the event. Use appropriate synchronization if handlers access shared state.
See Also
Was this page helpful?