# Callback Events

The Go-AI SDK fires structured lifecycle events during text generation and agent execution. Each event is a typed struct delivered via callback fields on `GenerateTextOptions`, `StreamTextOptions`, and `AgentConfig`.

`GenerateTextOptions`, `StreamTextOptions`, `AgentGenerateOptions`, and `AgentConfig` use the TypeScript-aligned tool callback names `OnToolExecutionStart` and `OnToolExecutionEnd`. The event structs keep their Go names, `OnToolCallStartEvent` and `OnToolCallFinishEvent`.

All callbacks are **optional** and **panic-safe** — a panicking callback never aborts generation.

## Event Types

### OnStartEvent

Fired once at the beginning of a `GenerateText` or `StreamText` call, before any LLM request is made. Each struct below shows selected fields; see `go doc ./pkg/ai <Type>` for the complete list.

```go
type OnStartEvent struct {
    CallID        string // correlates this event with other events for the same call
    ModelProvider string
    ModelID       string
    Prompt        string
    System        string // Deprecated: use Instructions
    Messages      []types.Message
    Tools         []types.Tool
    // ... generation parameters, ProviderOptions, RuntimeContext, etc.
}
```

| Field | Description |
|-------|-------------|
| CallID | Unique ID for this generation call; correlates `OnStart`/`OnStepStart`/`OnStepFinish`/`OnFinish` events |
| ModelProvider | Provider name (e.g., `"openai"`, `"anthropic"`) |
| ModelID | Model identifier (e.g., `"gpt-4o"`) |
| Prompt | The user prompt string |
| System | The system prompt string (deprecated alias for `Instructions`) |
| Messages | Initial conversation messages |
| Tools | Tools available for the call |

---

### OnStepStartEvent

Fired at the beginning of each generation step (each LLM call in a multi-step tool loop).

```go
type OnStepStartEvent struct {
    CallID     string
    StepNumber int // 0-indexed
    Messages   []types.Message
    Tools      []types.Tool
    // ... ToolChoice, ActiveTools, Steps (prior step results), etc.
}
```

| Field | Description |
|-------|-------------|
| CallID | Correlates this event with the other events for this call |
| StepNumber | 0-indexed step counter |
| Messages | Messages sent to the LLM for this step |
| Tools | Tools available for this step |

---

### OnToolCallStartEvent

Fired before each individual tool is executed. Use this event with `OnToolExecutionStart`.

```go
type OnToolCallStartEvent struct {
    CallID     string
    ToolCallID string
    ToolName   string
    ToolCall   types.ToolCall
    Messages   []types.Message
    // Args and StepNumber are deprecated; use ToolCall.Arguments and
    // correlate with step events via CallID instead.
}
```

| Field | Description |
|-------|-------------|
| CallID | Correlates this event with the generation call that triggered it |
| ToolCallID | Unique identifier for this tool call |
| ToolName | Name of the tool being called |
| ToolCall | The full tool call, including `Arguments` |

---

### OnToolCallFinishEvent

Fired after each tool completes (success or failure). Use this event with `OnToolExecutionEnd`.

```go
type OnToolCallFinishEvent struct {
    CallID          string
    ToolCallID      string
    ToolName        string
    ToolCall        types.ToolCall
    ToolOutput      types.ToolResult // exactly one of ToolOutput.Result / ToolOutput.Error is meaningful
    ToolExecutionMs int64
    Messages        []types.Message
    // Result, Error, Args, StepNumber, DurationMs are deprecated aliases
    // for ToolOutput.Result, ToolOutput.Error, ToolCall.Arguments,
    // CallID-based correlation, and ToolExecutionMs respectively.
}
```

| Field | Description |
|-------|-------------|
| ToolOutput | Tool result; `ToolOutput.Result` is non-nil on success, `ToolOutput.Error` on failure |
| ToolExecutionMs | Tool execution time in milliseconds |
| Result (deprecated) | Tool output on success (`nil` on failure) |
| Error (deprecated) | Execution error on failure (`nil` on success) |

---

### OnStepFinishEvent

Fired after each generation step completes, including after all tools for that step have been executed.

```go
type OnStepFinishEvent struct {
    CallID       string
    StepNumber   int // 0-indexed
    Text         string
    ToolCalls    []types.ToolCall
    ToolResults  []types.ToolResult
    FinishReason types.FinishReason
    Usage        types.Usage
    Warnings     []types.Warning
    // ... Reasoning, Sources, Files, ProviderMetadata, Request, Response, etc.
}
```

| Field | Description |
|-------|-------------|
| StepNumber | 0-indexed step counter |
| Text | LLM text output for this step |
| ToolCalls | Tool calls made during this step |
| ToolResults | Results from tool executions in this step |
| FinishReason | Why the LLM stopped (e.g., `ToolCalls`, `Stop`) |
| Usage | Token usage for this step |

---

### OnFinishEvent

Fired once when generation completes (after all steps).

```go
type OnFinishEvent struct {
    CallID       string
    Text         string
    ToolCalls    []types.ToolCall
    ToolResults  []types.ToolResult
    FinishReason types.FinishReason
    Steps        []types.StepResult
    Usage        types.Usage      // usage of the final step
    TotalUsage   types.Usage      // sum across all steps
    Output       interface{}      // parsed structured output, if configured
    // ... Reasoning, Sources, Files, Warnings, ProviderMetadata, etc.
}
```

| Field | Description |
|-------|-------------|
| Text | Final generated text |
| ToolResults | Tool results from the final step |
| FinishReason | Final finish reason |
| Steps | All steps taken during generation |
| TotalUsage | Aggregated token usage across all steps |

---

## Notify Utility

The SDK exposes the `Notify` function used internally for safe event dispatch:

```go
// Listener is a function that receives a typed event.
type Listener[E any] func(ctx context.Context, event E)

// Notify safely dispatches event to each listener.
// Panics in any listener are recovered and do not propagate.
func Notify[E any](ctx context.Context, event E, listeners ...Listener[E])
```

`Notify` is useful for fan-out scenarios where you want to dispatch the same event to multiple independent listeners.

---

## Usage

### With GenerateText

```go
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: "What is 2+2?",
    Tools:  []types.Tool{calculatorTool},
    StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
    OnStart: func(ctx context.Context, e ai.OnStartEvent) {
        log.Printf("[start] model=%s/%s prompt=%q", e.ModelProvider, e.ModelID, e.Prompt)
    },
    OnStepStart: func(ctx context.Context, e ai.OnStepStartEvent) {
        log.Printf("[step %d start] messages=%d", e.StepNumber, len(e.Messages))
    },
    OnToolExecutionStart: func(ctx context.Context, e ai.OnToolCallStartEvent) {
        log.Printf("[tool start] %s args=%v", e.ToolName, e.Args)
    },
    OnToolExecutionEnd: func(ctx context.Context, e ai.OnToolCallFinishEvent) {
        if e.Error != nil {
            log.Printf("[tool error] %s: %v", e.ToolName, e.Error)
        } else {
            log.Printf("[tool finish] %s result=%v", e.ToolName, e.Result)
        }
    },
    OnStepFinishEvent: func(ctx context.Context, e ai.OnStepFinishEvent) {
        log.Printf("[step %d finish] finish_reason=%s tokens=%d",
            e.StepNumber, e.FinishReason, safeTokens(e.Usage.TotalTokens))
    },
    OnFinishEvent: func(ctx context.Context, e ai.OnFinishEvent) {
        log.Printf("[finish] steps=%d total_tokens=%d text=%q",
            len(e.Steps), safeTokens(e.TotalUsage.TotalTokens), e.Text)
    },
})
```

### With AgentConfig (ToolLoopAgent)

Callbacks on `AgentConfig` fire for every call to `Execute`. They are merged with any per-call callbacks:

```go
agent := agent.NewToolLoopAgent(agent.AgentConfig{
    Model:    model,
    Tools:    tools,
    StopWhen: []ai.StopCondition{ai.IsStepCount(10)},
    OnStart: func(ctx context.Context, e ai.OnStartEvent) {
        log.Printf("Agent starting: model=%s", e.ModelID)
    },
    OnToolExecutionStart: func(ctx context.Context, e ai.OnToolCallStartEvent) {
        log.Printf("Tool call: %s", e.ToolName)
    },
    OnToolExecutionEnd: func(ctx context.Context, e ai.OnToolCallFinishEvent) {
        if e.Error != nil {
            log.Printf("Tool failed: %s: %v", e.ToolName, e.Error)
        }
    },
    OnFinishEvent: func(ctx context.Context, e ai.OnFinishEvent) {
        log.Printf("Agent done: %d steps", len(e.Steps))
    },
})
```

---

## Event Ordering

For a single `GenerateText` call with one tool call, the event order is:

```
OnStart
└── Step 1
    ├── OnStepStart
    ├── OnToolExecutionStart (for each tool execution)
    ├── OnToolExecutionEnd (for each tool execution)
    └── OnStepFinish
└── Step 2
    ├── OnStepStart
    └── OnStepFinish
OnFinish
```

---

## Callback Merging in Agents

When using `ToolLoopAgent`, callbacks set in `AgentConfig` are merged with any per-call callbacks. Both fire in order: **settings-level first, then call-level**.

This is useful for attaching persistent observability (logging, metrics) at the agent level while allowing per-request overrides.

---

## See Also

- [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) — Full reference for `GenerateText`
- [StreamText](https://goaisdk.com/docs/reference/ai/stream-text.md) — Full reference for `StreamText`
- [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md) — Full reference for `ToolLoopAgent`
