Melakukan streaming pesan menggunakan Google Chat API

Panduan ini menjelaskan cara melakukan streaming pesan secara bertahap di Google Chat menggunakan resource StreamMessage di Chat API. Streaming memungkinkan aplikasi Google Chat Anda menampilkan indikator progres live, langkah-langkah penalaran atau eksekusi alat perantara, dan respons teks inkremental sebelum menyelesaikan pesan.

Streaming dirancang untuk aplikasi Chat yang didukung oleh AI generatif atau alur kerja backend yang berjalan lama, yang memerlukan waktu beberapa detik atau menit untuk menghasilkan respons lengkap.

Cara kerja streaming pesan

Pesan streaming akan melalui status streaming aktif sebelum menjadi resource Message standar di ruang:

  1. Memulai rangkaian pesan (spaces.streamMessages.create): Aplikasi Chat Anda membuat StreamMessage di ruang berangkai, yang merujuk ke pesan interaksi pengguna yang dibalas. API akan menampilkan indikator progres langsung di rangkaian pesan dan menampilkan nama resource StreamMessage dan nextChunkToken awal.
  2. Memperbarui streaming (appendStreamMessageContent atau patch): Saat aplikasi Chat Anda menghasilkan output, aplikasi tersebut akan mengirimkan update berurutan menggunakan nextChunkToken terbaru:
    • Panggil spaces.streamMessages.appendStreamMessageContent untuk menggabungkan bagian teks baru, menambahkan langkah-langkah perantara, menambahkan kutipan inline, atau memperbarui progressText.
    • Panggil spaces.streamMessages.patch untuk mengganti potongan konten yang di-streaming atau mengupdate progressText di tempat.
  3. Finalize or discard the stream (commit or delete):
    • Panggil spaces.streamMessages.commit untuk menyelesaikan streaming dan mengonversinya menjadi resource Message permanen. Anda juga dapat melampirkan elemen khusus commit seperti kartu, widget aksesori, dan sumber footer saat melakukan commit.
    • Panggil spaces.streamMessages.delete untuk menghapus streaming aktif tanpa menyimpan pesan di ruang.

Saat streaming aktif, hanya pengirim pesan yang direferensikan (referencedMessage) yang sedang di-streaming yang dapat mengklik Stop untuk membatalkan pembuatan. Chat mengirimkan peristiwa perintah sistem pembatalan ke aplikasi Chat Anda sehingga Anda dapat menghentikan pekerjaan backend dan melakukan commit output parsial atau menghapus streaming.

Prasyarat

Untuk melakukan streaming pesan dengan Chat API, selesaikan prasyarat berikut:

  • Akun Google Workspace Business atau Enterprise dengan akses ke Google Chat.
  • Siapkan lingkungan Anda:
  • Tambahkan aplikasi Chat Anda ke ruang Chat berangkai (atau pesan langsung personal yang sudah berisi setidaknya satu pesan), dan dapatkan nama resource pesan yang dapat diakses untuk dibalas (spaces/SPACE_ID/messages/MESSAGE_ID).
  • Untuk contoh Python, instal Library Klien Google API untuk Python dan google-auth, lalu lakukan inisialisasi klien layanan Chat API:

    pip install google-api-python-client google-auth
    from google.oauth2 import service_account
    from googleapiclient.discovery import build
    
    SCOPES = ["https://www.googleapis.com/auth/chat.bot"]
    creds = service_account.Credentials.from_service_account_file(
        "credentials.json", scopes=SCOPES
    )
    chat = build("chat", "v1", credentials=creds)
    

Memulai streaming

Untuk mulai menstreaming balasan, panggil spaces.streamMessages.create dan teruskan StreamMessage dalam isi permintaan:

  • referencedMessage (Wajib): Nama resource pesan yang memicu respons, yang diformat sebagai spaces/{space}/messages/{message}. Pesan yang direferensikan harus berada di ruang yang sama dan dapat diakses oleh aplikasi Chat Anda. Chat akan memposting balasan yang di-streaming di rangkaian pesan yang sama dengan referencedMessage.
  • progressText (Wajib): Label status teks biasa yang tidak kosong (hingga 250 karakter) yang ditampilkan di samping indikator progres, seperti "Thinking...".
  • markupSyntax (Opsional): Sintaksis pemformatan teks untuk pesan, seperti MARKUP_SYNTAX_MARKDOWN atau MARKUP_SYNTAX_CHAT (secara default menggunakan sintaksis yang dikonfigurasi aplikasi Chat jika MARKUP_SYNTAX_UNSPECIFIED). Tetapkan MARKUP_SYNTAX_MARKDOWN jika Anda berencana menyertakan kutipan inline.
  • streamContentChunks (Opsional): Chunk text, step, atau citation awal yang akan langsung ditampilkan.

Simpan name (spaces/{space}/streamMessages/{stream_message}) dan nextChunkToken dari respons untuk panggilan berikutnya.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
  "progressText": "Thinking...",
  "markupSyntax": "MARKUP_SYNTAX_MARKDOWN"
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .create(
        parent="spaces/SPACE_ID",
        body={
            "referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
            "progressText": "Thinking...",
            "markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
        },
    )
    .execute()
)

stream_name = stream["name"]
chunk_token = stream["nextChunkToken"]

Metode ini menampilkan resource StreamMessage:

{
  "name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
  "progressText": "Thinking...",
  "markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
  "nextChunkToken": "INITIAL_CHUNK_TOKEN"
}

Menambahkan potongan konten

Saat aplikasi Chat Anda menghasilkan langkah-langkah perantara atau token teks, panggil spaces.streamMessages.appendStreamMessageContent:

  • Teruskan nextChunkToken terbaru di chunkToken. Setiap panggilan yang berhasil akan menampilkan nextChunkToken baru yang harus Anda teruskan pada permintaan update atau commit berikutnya.
  • text potongan digabungkan langsung ke teks yang di-streaming sebelumnya. Sertakan spasi atau baris baru jika diperlukan di antara potongan.
  • step potongan ditambahkan ke daftar langkah perantara yang ditampilkan pada pesan.
  • progressText bersifat opsional. Jika Anda menghapus progressText atau menyetelnya ke "", Chat akan terus menampilkan progressText yang ditetapkan sebelumnya.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:appendStreamMessageContent HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "chunkToken": "INITIAL_CHUNK_TOKEN",
  "progressText": "Searching documentation...",
  "streamContentChunks": [
    {
      "step": {
        "title": "Search knowledge base",
        "description": "Found 3 matching articles."
      }
    },
    {
      "text": "See <chat-citation data-id=\"c1\">guide</chat-citation>:\n"
    },
    {
      "citation": {
        "id": "c1",
        "citedSources": [
          {
            "title": "System Architecture Guide",
            "uri": "https://example.com/docs/arch"
          }
        ]
      }
    }
  ]
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .appendStreamMessageContent(
        name=stream_name,
        body={
            "chunkToken": chunk_token,
            "progressText": "Searching documentation...",
            "streamContentChunks": [
                {
                    "step": {
                        "title": "Search knowledge base",
                        "description": "Found 3 matching articles.",
                    }
                },
                {
                    "text": (
                        'See <chat-citation data-id="c1">'
                        "guide</chat-citation>:\n"
                    )
                },
                {
                    "citation": {
                        "id": "c1",
                        "citedSources": [
                            {
                                "title": "System Architecture Guide",
                                "uri": "https://example.com/docs/arch",
                            }
                        ],
                    }
                },
            ],
        },
    )
    .execute()
)

chunk_token = stream["nextChunkToken"]

Mengganti konten yang di-streaming atau teks progres

Jika Anda perlu menulis ulang konten yang di-streaming sebelumnya—misalnya, setelah menjalankan filter pascapemrosesan atau keamanan—atau hanya memperbarui progressText tanpa menambahkan potongan, panggil spaces.streamMessages.patch:

  • Tetapkan updateMask ke daftar kolom yang dipisahkan koma untuk diupdate: progressText, streamContentChunks, atau progressText,streamContentChunks.
  • Sertakan nextChunkToken terbaru di dalam isi permintaan StreamMessage, dan catat nextChunkToken baru yang ditampilkan dalam respons.
  • Jika streamContentChunks berada dalam updateMask, potongan yang disediakan akan menggantikan konten yang di-streaming sebelumnya, bukan menambahkannya.
  • Jika progressText berada dalam updateMask, progressText harus berupa teks biasa yang tidak kosong hingga 250 karakter.

HTTP

PATCH https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID?updateMask=progressText,streamContentChunks HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "nextChunkToken": "LATEST_CHUNK_TOKEN",
  "progressText": "Formatting response...",
  "streamContentChunks": [
    {
      "text": "Updated response text."
    }
  ]
}

Python

stream = (
    chat.spaces()
    .streamMessages()
    .patch(
        name=stream_name,
        updateMask="progressText,streamContentChunks",
        body={
            "nextChunkToken": chunk_token,
            "progressText": "Formatting response...",
            "streamContentChunks": [
                {"text": "Updated response text."}
            ],
        },
    )
    .execute()
)

chunk_token = stream["nextChunkToken"]

Memfinalisasi streaming

Setelah pembuatan selesai, panggil spaces.streamMessages.commit dengan chunkToken terbaru untuk menyelesaikan streaming menjadi resource Message permanen:

  • Anda dapat menyertakan streamContentChunks akhir dalam permintaan commit, termasuk jenis potongan khusus commit (card, accessoryWidget, dan citedSource).
  • Jika Anda telah melakukan streaming konten yang tidak kosong dalam panggilan sebelumnya, Anda dapat menghilangkan streamContentChunks saat melakukan commit. Pesan final yang di-commit tidak boleh kosong.

HTTP

POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:commit HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "chunkToken": "LATEST_CHUNK_TOKEN",
  "streamContentChunks": [
    {
      "text": "All checks passed."
    },
    {
      "citedSource": {
        "title": "Deployment Runbook",
        "uri": "https://example.com/docs/runbook"
      }
    },
    {
      "card": {
        "cardId": "details-card",
        "card": {
          "header": {
            "title": "Build Status"
          },
          "sections": [
            {
              "widgets": [
                {
                  "textParagraph": {
                    "text": "All 42 checks succeeded."
                  }
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

Python

status_card = {
    "cardId": "details-card",
    "card": {
        "header": {"title": "Build Status"},
        "sections": [
            {
                "widgets": [
                    {"textParagraph": {"text": "All 42 checks succeeded."}}
                ]
            }
        ],
    },
}

commit_response = (
    chat.spaces()
    .streamMessages()
    .commit(
        name=stream_name,
        body={
            "chunkToken": chunk_token,
            "streamContentChunks": [
                {"text": "All checks passed."},
                {
                    "citedSource": {
                        "title": "Deployment Runbook",
                        "uri": "https://example.com/docs/runbook",
                    }
                },
                {"card": status_card},
            ],
        },
    )
    .execute()
)

final_message = commit_response["message"]
print(f"Committed message: {final_message['name']}")

Menghapus streaming aktif

Jika terjadi error yang tidak dapat dipulihkan atau pengguna membatalkan pembuatan dan Anda tidak ingin meninggalkan balasan parsial dalam rangkaian pesan, panggil spaces.streamMessages.delete:

HTTP

DELETE https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID HTTP/1.1
Authorization: Bearer ACCESS_TOKEN

Python

chat.spaces().streamMessages().delete(name=stream_name).execute()

Setelah aliran di-commit ke Message, panggil spaces.messages.delete jika Anda perlu menghapus pesan yang telah diselesaikan.

Jenis potongan konten yang didukung

Setiap entri di streamContentChunks menentukan satu kolom dalam gabungan StreamContentChunk. Beberapa jenis potongan dapat dikirim di seluruh streaming, sementara yang lain hanya diterima di spaces.streamMessages.commit:

Kolom chunk Metode yang didukung Kolom Message akhir Catatan
text create, appendStreamMessageContent, patch, commit text Teks inkremental. Digabungkan pada appendStreamMessageContent; diganti pada patch. Teks akhir yang di-commit tidak boleh melebihi 32.000 byte.
step create, appendStreamMessageContent, patch, commit elements.steps.steps[] Langkah pemrosesan sementara dengan title teks biasa (wajib) dan description opsional.
citation create, appendStreamMessageContent, patch, commit elements.citations[] Citation inline dengan id alfanumerik (di bawah 63 karakter, garis bawah diizinkan) dan citedSources. Memerlukan tag markupSyntax: "MARKUP_SYNTAX_MARKDOWN" dan tag <chat-citation data-id="ID">...</chat-citation> yang cocok di text. Mengirim citation dengan id yang sudah ada akan menimpa kutipan sebelumnya.
card commit saja cardsV2[] Kartu CardWithId interaktif atau informasional.
accessoryWidget commit saja accessoryWidgets[] Tombol AccessoryWidget ditampilkan di bawah pesan, seperti tombol masukan.
citedSource commit saja elements.citedSources[] Link footer CitedSource ditampilkan di bawah pesan tanpa tag kutipan inline.

Menangani pembatalan streaming yang dimulai pengguna

Saat streaming aktif, hanya pengirim pesan yang direferensikan (referencedMessage) yang sedang di-streaming yang dapat mengklik Berhenti pada pesan streaming. Saat pengguna menghentikan streaming, Chat akan mengirimkan peristiwa interaksi perintah sistem yang berisi cancelStream.streamMessage (nama resource spaces/{space}/streamMessages/{stream_message} dari streaming yang dihentikan) ke endpoint aplikasi Chat Anda.

Struktur payload JSON yang masuk bergantung pada apakah Anda membangun aplikasi Chat sebagai add-on Google Workspace atau menggunakan peristiwa interaksi Chat API:

Add-on Google Workspace

{
  "commonEventObject": {
    "hostApp": "CHAT"
  },
  "chat": {
    "user": {
      "name": "users/USER_ID"
    },
    "space": {
      "name": "spaces/SPACE_ID"
    },
    "systemCommandPayload": {
      "systemCommandMetadata": {
        "cancelStream": {
          "streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
        }
      }
    }
  }
}

Google Chat API

{
  "type": "SYSTEM_COMMAND",
  "space": {
    "name": "spaces/SPACE_ID"
  },
  "user": {
    "name": "users/USER_ID"
  },
  "systemCommandMetadata": {
    "cancelStream": {
      "streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
    }
  }
}

Saat aplikasi Chat Anda menerima peristiwa cancelStream:

  1. Beri sinyal kepada worker latar belakang Anda untuk berhenti membuat token lebih lanjut atau menjalankan alat downstream untuk streamMessage.
  2. Selesaikan streaming aktif menggunakan salah satu pendekatan berikut:
    • Keep partial output: Panggil spaces.streamMessages.commit dengan chunkToken terbaru (opsional menambahkan catatan seperti "\n\n_Generation stopped._").
    • Menghapus balasan: Panggil spaces.streamMessages.delete agar tidak ada yang disimpan ke rangkaian pesan.
  3. Tampilkan respons JSON 200 OK HTTP kosong ({}) dari pengendali peristiwa interaksi Anda.

Batasan

Streaming pesan memiliki batasan berikut:

  • Ketersediaan platform: Streaming pesan hanya terlihat di Chat di web. Dukungan untuk Android dan iOS akan segera hadir.
  • Waktu tunggu streaming: StreamMessage yang aktif akan habis waktu tunggunya 10 menit setelah pembuatan. Aplikasi Chat Anda harus memanggil spaces.streamMessages.commit atau spaces.streamMessages.delete dalam waktu 10 menit setelah memulai streaming.