メインコンテンツへスキップ
増分ストリーミングを使うと、クライアントは最終的な完全な agent.message イベントの前にアシスタント出力のチャンクを受信できます。Session の作成時に有効化します。
{
  "incremental_streaming_enabled": true
}
この設定は Session に保存されます。ストリームリクエストのクエリパラメータやヘッダーでは制御しません。

エンドポイント

パスは変わりません。
API増分の動作
GET /api/v1/cloud/sessions/{session_id}/events/streamライブ Session SSE ストリーム
GET /api/v1/cloud/sessions/{session_id}/threads/{thread_id}/streamthread に限定したライブ SSE ストリーム
GET /api/v1/cloud/sessions/{session_id}/events過去の Session イベントのリプレイ
GET /api/v1/cloud/sessions/{session_id}/threads/{thread_id}/eventsthread に限定した過去イベントのリプレイ
incremental_streaming_enabledfalse または省略されている場合、これらの API は元の動作を維持し、増分イベントを非表示にします。

イベントモデル

公開イベントは qodercli/Anthropic の raw stream イベントと整合します。CAW は message_startcontent_block_start などの raw stream イベントタイプを発行します。CAS は typeagent. プレフィックスを付け、idsession_idsession_thread_idturn_idmessage_idparent_tool_use_idprocessed_at などの CAS メタデータを追加して、それらを公開します。 トップレベルの増分イベントタイプ:
agent.message_start
agent.content_block_start
agent.content_block_delta
agent.content_block_stop
agent.message_delta
agent.message_stop
チャンクの種類は agent.content_block_delta.delta.type 内にネストされます。これらはトップレベルのイベントタイプではありません。 現在の実装では、agent.content_block_start.content_block.type は次のいずれかになります。
content_block.type意味
thinking初期状態で空の thinking を持つ thinking ブロック
text初期状態で空の text を持つ text ブロック
tool_useidname、初期状態で空の input を持つ tool-use ブロック
delta.type意味
text_deltaテキスト出力のチャンク
thinking_deltaモデル/プロバイダーが出力する場合の thinking チャンク
signature_delta利用可能な場合の thinking ブロックの署名チャンク
input_json_deltaツール入力 JSON のチャンク。製品レベルの tool_input_delta 概念はこのワイヤー形を partial_json とともに使用します
tool_output_delta将来のツール出力ストリーミング向けに予約されています。現在の実装ではこの delta を発行しません。ツール結果は引き続き完全な agent.tool_result イベントとして返されます
一般的なイベントの形:
{
  "type": "agent.message_start",
  "message_id": "asst_...",
  "message": {
    "id": "asst_...",
    "type": "message",
    "role": "assistant",
    "model": "qwen3-coder-plus",
    "content": [],
    "stop_reason": null,
    "stop_sequence": null,
    "usage": {
      "input_tokens": 0,
      "output_tokens": 0
    }
  }
}
{
  "type": "agent.content_block_delta",
  "message_id": "asst_...",
  "index": 0,
  "delta": {
    "type": "text_delta",
    "text": "Hello"
  }
}
{
  "type": "agent.message_delta",
  "message_id": "asst_...",
  "delta": {
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "container": null
  },
  "usage": {
    "input_tokens": 123,
    "output_tokens": 45
  }
}
完全な agent.message は増分シーケンスの後に引き続き返され、正規の最終結果のままです。同じ text ブロックでは、text_delta.text を順に連結すると、最終的な agent.message.content[index].text と一致するはずです。プロバイダーがストリームチャンクから最後のサフィックスを省略する場合、CAW は agent.content_block_stop の前に、残りのサフィックスを最終的な text_delta として発行します。

クイック検証

前提条件:
  • QODER_PAT に有効な PAT
  • AGENT_ID に既存の Agent ID
  • ENVIRONMENT_ID に既存の Environment ID
  • ローカルに jq がインストールされていること
API のベースを設定します。デフォルトでは本番のベースを使用するか、プレリリースのデプロイを検証する場合は Global Test のベースに置き換えてください。
export BASE_URL="https://api.qoder.com/api/v1/cloud"
# export BASE_URL="https://test-api.qoder.ai/api/v1/cloud"

export QODER_PAT="pat_..."
export AGENT_ID="agent_..."
export AGENT_VERSION="1"
export ENVIRONMENT_ID="env_..."
増分ストリーミングを有効にして Session を作成します。
SESSION_JSON=$(
  jq -n \
    --arg agent_id "$AGENT_ID" \
    --argjson agent_version "$AGENT_VERSION" \
    --arg environment_id "$ENVIRONMENT_ID" \
    '{
      agent: {id: $agent_id, type: "agent", version: $agent_version},
      environment_id: $environment_id,
      title: "incremental streaming verification",
      incremental_streaming_enabled: true
    }' |
  curl -s -X POST "$BASE_URL/sessions" \
    -H "Authorization: Bearer $QODER_PAT" \
    -H "Content-Type: application/json" \
    --data-binary @-
)

export SESSION_ID=$(echo "$SESSION_JSON" | jq -r '.id')
echo "$SESSION_JSON" | jq '{id, incremental_streaming_enabled, status}'
想定されるレスポンス:
{
  "id": "sess_...",
  "incremental_streaming_enabled": true,
  "status": "idle"
}
1 つのターミナルで Session の SSE ストリームを開きます。
curl -sN "$BASE_URL/sessions/$SESSION_ID/events/stream" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Accept: text/event-stream" |
while IFS= read -r line; do
  case "$line" in
    event:*) echo "$line" ;;
    data:*) echo "${line#data: }" | jq -cr '{type, message_id, index, block_type: .content_block.type, delta_type: .delta.type, text: .delta.text, thinking: .delta.thinking, partial_json: .delta.partial_json}' ;;
  esac
done
別のターミナルからユーザーメッセージを送信します。
curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/events" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "type": "user.message",
        "content": [
          {"type": "text", "text": "Reply with exactly one short sentence."}
        ]
      }
    ]
  }' | jq
ストリームには、最終的な完全イベントの前に増分シーケンスが表示されるはずです。
event: agent.message_start
event: agent.content_block_start
event: agent.content_block_delta
{"type":"agent.content_block_delta","delta_type":"text_delta","text":"..."}
event: agent.content_block_stop
event: agent.message_delta
event: agent.message_stop
event: agent.message
event: session.status_idle
過去イベントのリプレイを検証します。
curl -s "$BASE_URL/sessions/$SESSION_ID/events?limit=200" \
  -H "Authorization: Bearer $QODER_PAT" |
jq -r '.data[].type' |
grep -E 'agent\.message_start|agent\.content_block_delta|agent\.message_stop'
thread に限定したリプレイとストリームを検証します。
export THREAD_ID=$(
  curl -s "$BASE_URL/sessions/$SESSION_ID/threads?limit=20" \
    -H "Authorization: Bearer $QODER_PAT" |
  jq -r '.data[0].id'
)

curl -s "$BASE_URL/sessions/$SESSION_ID/threads/$THREAD_ID/events?limit=200" \
  -H "Authorization: Bearer $QODER_PAT" |
jq -r '.data[].type' |
grep -E 'agent\.message_start|agent\.content_block_delta|agent\.message_stop'
thread に限定したライブストリームを観察するには、thread stream エンドポイントに対して同じパーサーループを使用します。
curl -sN "$BASE_URL/sessions/$SESSION_ID/threads/$THREAD_ID/stream" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Accept: text/event-stream" |
while IFS= read -r line; do
  case "$line" in
    event:*) echo "$line" ;;
    data:*) echo "${line#data: }" | jq -cr '{type, message_id, index, block_type: .content_block.type, delta_type: .delta.type, text: .delta.text, thinking: .delta.thinking, partial_json: .delta.partial_json}' ;;
  esac
done

無効化した場合の対照確認

incremental_streaming_enabled を付けずに、または false に設定して、別の Session を作成します。レスポンスには次が含まれるはずです。
{
  "incremental_streaming_enabled": false
}
同じ user.message を送信した後、ストリームと履歴には agent.messagesession.status_idle などの完全イベントが含まれますが、上記の増分イベントタイプは含まれません。

パーサーのチェックリスト

  • SSE の event: と JSON の data.type を公開イベントタイプとして扱う。
  • delta.typetext_delta のイベントについて、agent.content_block_delta.delta.text を連結してテキストを再構築する。
  • delta.typethinking_delta のイベントについて、agent.content_block_delta.delta.thinking を連結して thinking を再構築する。完全な互換用 agent.thinking イベントが後で届くこともある。
  • ツール入力の増分については、delta.type == "input_json_delta" を確認して delta.partial_json を連結する。トップレベルの tool_input_delta イベントは期待しないこと。
  • tool_output_delta はまだ発行されない。ツール実行結果は完全な agent.tool_result イベントとして返される。
  • 複数の content block を区別するために index を追跡する。
  • agent 生成イベントでは processed_at を任意として扱う。
  • 同じ接続で複数ターンをサポートするクライアントの場合、session.status_idle の後もリッスンを続ける。
  • ネットワーク切断後は Last-Event-ID を付けて再接続する。