SPB Git

Repository Public

events.md 19.8 KB 516 lines markdown

1# Events and items23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Events report what happens as an agent works. Items are the saved messages and tool calls you can retrieve later. Use events to update your application in real time and items to display its saved history.6789101112Your application sends input events to submit messages, cancel turns, or return tool results. The agent sends events that report output and changes to the session. See [Run and continue sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions) for sending input.1314151617## Consume a stream1819Subscribe before sending work so your application receives the turn's early events. Pass your API client, the conversation's session ID, and an event handler:2021Stream session events2223```javascript24// Pass your saved session ID to this helper.25async function streamSession(client, sessionId, handleEvent) {26  const events = await client.beta.agents.sessions.events.stream(sessionId);27  try {28    for await (const event of events) {29      await handleEvent(event);30      switch (event.type) {31        case "agent.session.idle":32          continue;33        case "error":34          throw new Error(event.error.message);35        case "agent.session.failed":36        case "agent.session.environment.failed":37          throw new Error(`Agent lifecycle failure: ${event.type}`);38        case "agent.session.turn.failed":39          if (event.turn.subagent_id === null) {40            throw new Error(41              `${event.type}: ${event.turn.error?.message ?? ""}`42            );43          }44          break;45        case "agent.session.turn.cancelled":46          if (event.turn.subagent_id === null) {47            throw new Error("The agent turn was cancelled");48          }49          break;50        case "agent.session.turn.completed":51          if (event.turn.subagent_id === null) return;52          break;53      }54    }55    throw new Error(56      "Stream closed before a turn ended. Retrieve the saved state."57    );58  } finally {59    events.controller.abort();60  }61}62```6364```python65# Pass your saved session ID to this helper.66def stream_session(client: OpenAI, session_id: str, handle_event):67    with client.beta.agents.sessions.events.stream(session_id) as events:68        for event in events:69            handle_event(event)70            match event.type:71                case "agent.session.idle":72                    continue73                case "error":74                    raise RuntimeError(event.error.message)75                case "agent.session.failed" | "agent.session.environment.failed":76                    raise RuntimeError(f"Agent lifecycle failure: {event.type}")77                case "agent.session.turn.failed":78                    if event.turn.subagent_id is None:79                        detail = event.turn.error.message if event.turn.error else ""80                        raise RuntimeError(f"{event.type}: {detail}")81                case "agent.session.turn.cancelled":82                    if event.turn.subagent_id is None:83                        raise RuntimeError("The agent turn was cancelled")84                case "agent.session.turn.completed":85                    if event.turn.subagent_id is None:86                        return87    raise RuntimeError("Stream closed before a turn ended. Retrieve the saved state.")88```8990```go91// Pass your saved session ID to this helper.92func streamSession(ctx context.Context, client *openai.Client, sessionID string, handleEvent func(openai.AgentSessionEventUnion)) error {93	events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, sessionID)94	defer events.Close()95	for events.Next() {96		event := events.Current()97		handleEvent(event)98		switch event.Type {99		case "agent.session.idle":100			continue101		case "error":102			return fmt.Errorf("agent error: %s", event.RawJSON())103		case "agent.session.failed", "agent.session.environment.failed":104			return fmt.Errorf("agent lifecycle failure: %s", event.RawJSON())105		case "agent.session.turn.failed", "agent.session.turn.cancelled":106			if event.Turn.SubagentID == "" {107				return fmt.Errorf("agent turn did not complete: %s", event.RawJSON())108			}109		case "agent.session.turn.completed":110			if event.Turn.SubagentID == "" {111				return nil112			}113		}114	}115	if err := events.Err(); err != nil {116		return err117	}118	return fmt.Errorf("stream closed before a turn ended; retrieve the saved state")119}120```121122```java123// Pass your saved session ID to this helper.124public static void streamSession(125    OpenAIClient client, String sessionId, Consumer<AgentSessionEvent> handleEvent) {126  try (StreamResponse<AgentSessionEvent> events =127      client.beta().agents().sessions().events().streamStreaming(sessionId)) {128    var iterator = events.stream().iterator();129    while (iterator.hasNext()) {130      var event = iterator.next();131      handleEvent.accept(event);132      if (event.idle().isPresent()) {133        continue;134      }135      if (event.error().isPresent()) {136        throw new IllegalStateException("Agent error: " + event);137      }138      if (event.failed().isPresent() || event.environmentFailed().isPresent()) {139        throw new IllegalStateException("Agent lifecycle failure: " + event);140      }141      if (event.turnFailed().filter(e -> e.turn().subagentId().isEmpty()).isPresent()142          || event.turnCancelled().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {143        throw new IllegalStateException("Agent turn did not complete: " + event);144      }145      if (event.turnCompleted().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {146        return;147      }148    }149    throw new IllegalStateException(150        "Stream closed before a turn ended. Retrieve the saved state.");151  }152}153```154155```ruby156# Pass your saved session ID to this helper.157def stream_session(client, session_id, &handle_event)158  events = client.beta.agents.sessions.events.stream_streaming(session_id)159  begin160    events.each do |event|161      handle_event.call(event)162      case event.type.to_s163      when "agent.session.idle"164        next165      when "error"166        raise event.error.message167      when "agent.session.failed", "agent.session.environment.failed"168        raise "Agent lifecycle failure: #{event.type}"169      when "agent.session.turn.failed"170        raise "#{event.type}: #{event.turn.error&.message}" if event.turn.subagent_id.nil?171      when "agent.session.turn.cancelled"172        raise "The agent turn was cancelled" if event.turn.subagent_id.nil?173      when "agent.session.turn.completed"174        return nil if event.turn.subagent_id.nil?175      end176    end177    raise "Stream closed before a turn ended. Retrieve the saved state."178  ensure179    events.close180  end181end182```183184```bash185curl -N \186  "https://api.openai.com/v1/agents/sessions/$session_id/events?stream=true" \187  -H "OpenAI-Beta: agents=v1" \188  -H "Authorization: Bearer $OPENAI_API_KEY" \189  -H "Accept: text/event-stream"190```191192193The helper passes each event to your handler, then checks common event types. It continues on `agent.session.idle` and returns when the root turn completes. It raises an error if the root turn fails or is cancelled, the session or environment fails, or an `error` event arrives. Subagent turn events do not end the stream. Your handler decides how to display output; the caller handles errors from the helper. If the stream closes before a turn ends, the helper raises an error. See [Recover a disconnected stream](#how-to-recover-a-disconnected-stream).194195196197198<details>199<summary>Send a message after subscribing</summary>200201This version accepts a message and submits it after opening the stream:202203Send and stream a message204205```javascript206// Pass your saved session ID and message to this helper.207async function sendAndStream(client, sessionId, text, handleEvent) {208  const events = await client.beta.agents.sessions.events.stream(sessionId);209  try {210    await client.beta.agents.sessions.events.create(sessionId, {211      events: [212        {213          type: "agent.session.input.message",214          input: [{ role: "user", content: [{ type: "input_text", text }] }],215        },216      ],217    });218    for await (const event of events) {219      await handleEvent(event);220      switch (event.type) {221        case "agent.session.idle":222          continue;223        case "error":224          throw new Error(event.error.message);225        case "agent.session.failed":226        case "agent.session.environment.failed":227          throw new Error(`Agent lifecycle failure: ${event.type}`);228        case "agent.session.turn.failed":229          if (event.turn.subagent_id === null) {230            throw new Error(231              `${event.type}: ${event.turn.error?.message ?? ""}`232            );233          }234          break;235        case "agent.session.turn.cancelled":236          if (event.turn.subagent_id === null) {237            throw new Error("The agent turn was cancelled");238          }239          break;240        case "agent.session.turn.completed":241          if (event.turn.subagent_id === null) return;242          break;243      }244    }245    throw new Error(246      "Stream closed before a turn ended. Retrieve the saved state."247    );248  } finally {249    events.controller.abort();250  }251}252```253254```python255# Pass your saved session ID and message to this helper.256def send_and_stream(client: OpenAI, session_id: str, text, handle_event):257    with client.beta.agents.sessions.events.stream(session_id) as events:258        client.beta.agents.sessions.events.create(259            session_id,260            events=[261                {262                    "type": "agent.session.input.message",263                    "input": [264                        {265                            "role": "user",266                            "content": [{"type": "input_text", "text": text}],267                        }268                    ],269                }270            ],271        )272        for event in events:273            handle_event(event)274            match event.type:275                case "agent.session.idle":276                    continue277                case "error":278                    raise RuntimeError(event.error.message)279                case "agent.session.failed" | "agent.session.environment.failed":280                    raise RuntimeError(f"Agent lifecycle failure: {event.type}")281                case "agent.session.turn.failed":282                    if event.turn.subagent_id is None:283                        detail = event.turn.error.message if event.turn.error else ""284                        raise RuntimeError(f"{event.type}: {detail}")285                case "agent.session.turn.cancelled":286                    if event.turn.subagent_id is None:287                        raise RuntimeError("The agent turn was cancelled")288                case "agent.session.turn.completed":289                    if event.turn.subagent_id is None:290                        return291    raise RuntimeError("Stream closed before a turn ended. Retrieve the saved state.")292```293294```go295// Pass your saved session ID and message to this helper.296func sendAndStream(ctx context.Context, client *openai.Client, sessionID string, text string, handleEvent func(openai.AgentSessionEventUnion)) error {297	events := client.Beta.Agents.Sessions.Events.StreamStreaming(ctx, sessionID)298	defer events.Close()299	if err := events.Err(); err != nil {300		return err301	}302	err := client.Beta.Agents.Sessions.Events.New(ctx,303		sessionID,304		openai.BetaAgentSessionEventNewParams{305			Events: []openai.AgentSessionInputParamUnion{306				{307					OfParamAgentSessionInputMessage: &openai.AgentSessionInputParamAgentSessionInputMessage{308						Input: []openai.AgentSessionInputMessageParam{309							{310								Content: []openai.InputContentParamUnion{311									{312										OfParamInputText: &openai.InputContentParamInputText{313											Text: text,314										},315									},316								},317							},318						},319					},320				},321			},322		})323	if err != nil {324		return err325	}326	for events.Next() {327		event := events.Current()328		handleEvent(event)329		switch event.Type {330		case "agent.session.idle":331			continue332		case "error":333			return fmt.Errorf("agent error: %s", event.RawJSON())334		case "agent.session.failed", "agent.session.environment.failed":335			return fmt.Errorf("agent lifecycle failure: %s", event.RawJSON())336		case "agent.session.turn.failed", "agent.session.turn.cancelled":337			if event.Turn.SubagentID == "" {338				return fmt.Errorf("agent turn did not complete: %s", event.RawJSON())339			}340		case "agent.session.turn.completed":341			if event.Turn.SubagentID == "" {342				return nil343			}344		}345	}346	if err := events.Err(); err != nil {347		return err348	}349	return fmt.Errorf("stream closed before a turn ended; retrieve the saved state")350}351```352353```java354// Pass your saved session ID and message to this helper.355public static void sendAndStream(356    OpenAIClient client, String sessionId, String text, Consumer<AgentSessionEvent> handleEvent) {357  try (StreamResponse<AgentSessionEvent> events =358      client.beta().agents().sessions().events().streamStreaming(sessionId)) {359    client360        .beta()361        .agents()362        .sessions()363        .events()364        .create(365            EventCreateParams.builder()366                .sessionId(sessionId)367                .addEvent(368                    AgentSessionInputParam.AgentSessionInputMessage.builder()369                        .addInput(370                            AgentSessionInputMessageParam.builder()371                                .addInputTextContent(text)372                                .build())373                        .build())374                .build());375    var iterator = events.stream().iterator();376    while (iterator.hasNext()) {377      var event = iterator.next();378      handleEvent.accept(event);379      if (event.idle().isPresent()) {380        continue;381      }382      if (event.error().isPresent()) {383        throw new IllegalStateException("Agent error: " + event);384      }385      if (event.failed().isPresent() || event.environmentFailed().isPresent()) {386        throw new IllegalStateException("Agent lifecycle failure: " + event);387      }388      if (event.turnFailed().filter(e -> e.turn().subagentId().isEmpty()).isPresent()389          || event.turnCancelled().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {390        throw new IllegalStateException("Agent turn did not complete: " + event);391      }392      if (event.turnCompleted().filter(e -> e.turn().subagentId().isEmpty()).isPresent()) {393        return;394      }395    }396    throw new IllegalStateException(397        "Stream closed before a turn ended. Retrieve the saved state.");398  }399}400```401402```ruby403# Pass your saved session ID and message to this helper.404def send_and_stream(client, session_id, text, &handle_event)405  events = client.beta.agents.sessions.events.stream_streaming(session_id)406  begin407    client.beta.agents.sessions.events.create(408      session_id,409      events: [410        {411          type: "agent.session.input.message",412          input: [413            {414              role: "user",415              content: [416                {417                  type: "input_text",418                  text: text419                }420              ]421            }422          ]423        }424      ]425    )426    events.each do |event|427      handle_event.call(event)428      case event.type.to_s429      when "agent.session.idle"430        next431      when "error"432        raise event.error.message433      when "agent.session.failed", "agent.session.environment.failed"434        raise "Agent lifecycle failure: #{event.type}"435      when "agent.session.turn.failed"436        raise "#{event.type}: #{event.turn.error&.message}" if event.turn.subagent_id.nil?437      when "agent.session.turn.cancelled"438        raise "The agent turn was cancelled" if event.turn.subagent_id.nil?439      when "agent.session.turn.completed"440        return nil if event.turn.subagent_id.nil?441      end442    end443    raise "Stream closed before a turn ended. Retrieve the saved state."444  ensure445    events.close446  end447end448```449450451</details>452453454455456457458459460## Handle updates461462Use the event's `type` to decide what your application should do:463464- **Display text:** Append `agent.session.turn.output_text.delta` to the relevant content part. When `agent.session.turn.output_text.done` arrives, replace that part with its complete text. Deltas may be absent.465- **Track work:** Session, turn, and item events report progress. Check for `agent.session.turn.completed`, `agent.session.turn.failed`, or `agent.session.turn.cancelled` to determine the turn's outcome.466- **Provide required input:** On `agent.session.requires_action`, retrieve the session and inspect `required_actions`. Your code may need to return a function result or connect an environment.467468An idle session or a closed stream alone does not establish success. A completed turn also does not guarantee that every tool succeeded. Inspect the agent's output.469470Use `item_id`, `output_index`, and `content_index` to connect text updates to the same content part. For example, these abbreviated events update one part:471472```json473{474  "type": "agent.session.turn.output_text.delta",475  "item_id": "msg_789",476  "output_index": 0,477  "content_index": 0,478  "delta": "Acme competes"479}480```481482```json483{484  "type": "agent.session.turn.output_text.done",485  "item_id": "msg_789",486  "output_index": 0,487  "content_index": 0,488  "text": "Acme competes on price and distribution."489}490```491492Each event has its own `event_id`. The shared `item_id` identifies the saved item, which includes the message's content, status, and phase. See [Retrieve saved work](https://developers.openai.com/api/docs/guides/agents-api/sessions#retrieve-session-items).493494See the [streaming events reference](https://developers.openai.com/api/reference/resources/beta/subresources/agents/streaming-events) for all event types and fields. These stream events are distinct from [webhooks](https://developers.openai.com/api/docs/guides/agents-api/sessions/webhooks). For subagent activity and command attribution, see [Observe delegation](https://developers.openai.com/api/docs/guides/agents-api/multi-agent#observe-delegation).495496## Fetch items and turns497498Use the session ID from your application's conversation state to retrieve saved work:499500- **Session items:** [List items](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/items/methods/list) to retrieve the root agent's messages and tool calls across turns.501- **Turns:** [List turns](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/turns/methods/list) to browse the session's work. [Retrieve a turn](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/turns/methods/retrieve) by ID to inspect its status, timestamps, usage, and error.502- **Items from one turn:** For a root-agent turn, filter session items by `turn_id`. Each subagent has its own [item history](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/subagents/subresources/items/methods/list) and a [per-turn items endpoint](https://developers.openai.com/api/reference/resources/beta/subresources/agents/subresources/sessions/subresources/subagents/subresources/turns/subresources/items/methods/list).503504List endpoints return one page at a time. Use SDK pagination helpers or the `after` cursor to retrieve more results. A single page may not contain every item for a turn. Use `order: "asc"` to read items from oldest to newest.505506## How to recover a disconnected stream507508Streams do not replay missed events. To restore your application's view:5095101. Open a new stream and buffer incoming events.5112. Retrieve the session and its saved items while the stream stays connected.5123. Restore your local state from those items, keyed by item ID.5134. Apply buffered item updates using `item_id`. Discard updates for items that already reached their final state in the retrieved history.5145. Resume handling live events.515516An `output_text.done` event can replace a temporary text buffer with the complete text. Saved items let you recover completed work, but not every intermediate event you missed.
Files main
Menu

Theme