Skip to content

Observability

Understanding the event system in SDK.

SDK 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.

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
)
conv.Send(ctx, "Hello")
EventSend ─────────► Subscriber
Provider Call
EventResponse ─────► Subscriber
│ (if tool call)
├────────────────┐
│ ▼
│ EventToolCall ──► Subscriber
│ │
│ Handler executes
│ │
│ EventToolResult ─► Subscriber
│ │
└────────────────┘
Return Response
import "github.com/AltairaLabs/PromptKit/sdk/hooks"
conv.Subscribe(hooks.EventSend, func(e hooks.Event) {
log.Printf("Sent: %v", e.Data["message"])
})
type Event struct {
Type string // Event type (EventSend, etc.)
Timestamp time.Time // When the event occurred
Data map[string]any // Event-specific data
}
e.Data["message"] // The message sent
e.Data["text"] // Response text
e.Data["tokens"] // Token count (if available)
e.Data["tool"] // Tool name
e.Data["args"] // Tool arguments
e.Data["tool"] // Tool name
e.Data["result"] // Tool result
e.Data["error"] // The error
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,
)
})
}
}
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()
})
}
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"])
})
}

Event handlers are called synchronously on the goroutine that triggered the event. Use appropriate synchronization if handlers access shared state.