Trong Live API, phiên là một kết nối liên tục, trong đó dữ liệu đầu vào và đầu ra được truyền trực tuyến liên tục qua cùng một kết nối (đọc thêm về cách hoạt động).
Thiết kế phiên độc đáo này cho phép độ trễ thấp và hỗ trợ các tính năng độc đáo, nhưng cũng có thể gây ra những thách thức, chẳng hạn như giới hạn thời gian phiên và chấm dứt sớm.
Hướng dẫn này đề cập đến các chiến lược để khắc phục những thách thức về việc quản lý phiên có thể phát sinh khi sử dụng Live API.
Thời gian tồn tại của phiên
Nếu không nén, các phiên chỉ có âm thanh sẽ bị giới hạn ở 15 phút và các phiên có cả âm thanh và video sẽ bị giới hạn ở 2 phút. Nếu vượt quá các giới hạn này, phiên sẽ kết thúc (và do đó, kết nối cũng kết thúc), nhưng bạn có thể sử dụng tính năng nén cửa sổ ngữ cảnh để kéo dài phiên đến một khoảng thời gian không giới hạn.
Thời gian tồn tại của một kết nối cũng bị giới hạn, khoảng 10 phút. Khi kết nối chấm dứt, phiên cũng sẽ chấm dứt. Trong trường hợp này, bạn có thể định cấu hình một phiên duy nhất để duy trì hoạt động trên nhiều kết nối bằng cách sử dụng tính năng tiếp tục phiên.
Bạn cũng sẽ nhận được thông báo GoAway trước khi kết thúc kết nối, cho phép bạn thực hiện các hành động khác.
Nén cửa sổ ngữ cảnh
Để bật các phiên dài hơn và tránh tình trạng kết nối bị chấm dứt đột ngột, bạn có thể bật tính năng nén cửa sổ ngữ cảnh bằng cách đặt trường contextWindowCompression trong cấu hình phiên.
Để ngăn việc kết thúc phiên khi máy chủ định kỳ đặt lại kết nối WebSocket, hãy định cấu hình trường sessionResumption trong cấu hình thiết lập.
Việc truyền cấu hình này khiến máy chủ gửi thông báo SessionResumptionUpdate. Bạn có thể dùng thông báo này để tiếp tục phiên bằng cách truyền mã thông báo tiếp tục gần đây nhất dưới dạng SessionResumptionConfig.handle của kết nối tiếp theo.
Mã thông báo tiếp tục có hiệu lực trong 2 giờ sau khi phiên gần nhất kết thúc.
Python
importasynciofromgoogleimportgenaifromgoogle.genaiimporttypesclient=genai.Client()model="gemini-live-2.5-flash-preview"asyncdefmain():print(f"Connecting to the service with handle {previous_session_handle}...")asyncwithclient.aio.live.connect(model=model,config=types.LiveConnectConfig(response_modalities=["AUDIO"],session_resumption=types.SessionResumptionConfig(# The handle of the session to resume is passed here,# or else None to start a new session.handle=previous_session_handle),),)assession:whileTrue:awaitsession.send_client_content(turns=types.Content(role="user",parts=[types.Part(text="Hello world!")]))asyncformessageinsession.receive():# Periodically, the server will send update messages that may# contain a handle for the current state of the session.ifmessage.session_resumption_update:update=message.session_resumption_updateifupdate.resumableandupdate.new_handle:# The handle should be retained and linked to the session.returnupdate.new_handle# For the purposes of this example, placeholder input is continually fed# to the model. In non-sample code, the model inputs would come from# the user.ifmessage.server_contentandmessage.server_content.turn_complete:breakif__name__=="__main__":asyncio.run(main())
JavaScript
import{GoogleGenAI,Modality}from'@google/genai';constai=newGoogleGenAI({});constmodel='gemini-live-2.5-flash-preview';asyncfunctionlive(){constresponseQueue=[];asyncfunctionwaitMessage(){letdone=false;letmessage=undefined;while(!done){message=responseQueue.shift();if(message){done=true;}else{awaitnewPromise((resolve)=>setTimeout(resolve,100));}}returnmessage;}asyncfunctionhandleTurn(){constturns=[];letdone=false;while(!done){constmessage=awaitwaitMessage();turns.push(message);if(message.serverContent && message.serverContent.turnComplete){done=true;}}returnturns;}console.debug('Connecting to the service with handle %s...',previousSessionHandle)constsession=awaitai.live.connect({model:model,callbacks:{onopen:function(){console.debug('Opened');},onmessage:function(message){responseQueue.push(message);},onerror:function(e){console.debug('Error:',e.message);},onclose:function(e){console.debug('Close:',e.reason);},},config:{responseModalities:[Modality.TEXT],sessionResumption:{handle:previousSessionHandle}// The handle of the session to resume is passed here, or else null to start a new session.}});constinputTurns='Hello how are you?';session.sendClientContent({turns:inputTurns});constturns=awaithandleTurn();for(constturnofturns){if(turn.sessionResumptionUpdate){if(turn.sessionResumptionUpdate.resumable && turn.sessionResumptionUpdate.newHandle){letnewHandle=turn.sessionResumptionUpdate.newHandle// ...Store newHandle and start new session with this handle here}}}session.close();}asyncfunctionmain(){awaitlive().catch((e)=>console.error('got error',e));}main();
Nhận được thông báo trước khi phiên kết nối bị ngắt
Máy chủ gửi thông báo GoAway cho biết rằng kết nối hiện tại sẽ sớm bị chấm dứt. Thông báo này bao gồm timeLeft, cho biết thời gian còn lại và cho phép bạn thực hiện các hành động khác trước khi kết nối bị chấm dứt dưới dạng ABORTED.
Python
asyncforresponseinsession.receive():ifresponse.go_awayisnotNone:# The connection will soon be terminatedprint(response.go_away.time_left)
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-08-22 UTC."],[],[],null,["# Session management with Live API\n\nIn the Live API, a session refers to a persistent\nconnection where input and output are streamed continuously over the same\nconnection (read more about [how it works](/gemini-api/docs/live)).\nThis unique session design enables low latency and supports unique features, but\ncan also introduce challenges, like session time limits, and early termination.\nThis guide covers strategies for overcoming the session management challenges\nthat can arise when using the Live API.\n\nSession lifetime\n----------------\n\nWithout compression, audio-only sessions are limited to 15 minutes,\nand audio-video sessions are limited to 2 minutes. Exceeding these limits\nwill terminate the session (and therefore, the connection), but you can use\n[context window compression](#context-window-compression) to extend sessions to\nan unlimited amount of time.\n\nThe lifetime of a connection is limited as well, to around 10 minutes. When the\nconnection terminates, the session terminates as well. In this case, you can\nconfigure a single session to stay active over multiple connections using\n[session resumption](#session-resumption).\nYou'll also receive a [GoAway message](#goaway-message) before the\nconnection ends, allowing you to take further actions.\n\nContext window compression\n--------------------------\n\nTo enable longer sessions, and avoid abrupt connection termination, you can\nenable context window compression by setting the [contextWindowCompression](/api/live#BidiGenerateContentSetup.FIELDS.ContextWindowCompressionConfig.BidiGenerateContentSetup.context_window_compression)\nfield as part of the session configuration.\n\nIn the [ContextWindowCompressionConfig](/api/live#contextwindowcompressionconfig), you can configure a\n[sliding-window mechanism](/api/live#ContextWindowCompressionConfig.FIELDS.ContextWindowCompressionConfig.SlidingWindow.ContextWindowCompressionConfig.sliding_window)\nand the [number of tokens](/api/live#ContextWindowCompressionConfig.FIELDS.int64.ContextWindowCompressionConfig.trigger_tokens)\nthat triggers compression. \n\n### Python\n\n from google.genai import types\n\n config = types.LiveConnectConfig(\n response_modalities=[\"AUDIO\"],\n context_window_compression=(\n # Configures compression with default parameters.\n types.ContextWindowCompressionConfig(\n sliding_window=types.SlidingWindow(),\n )\n ),\n )\n\n### JavaScript\n\n const config = {\n responseModalities: [Modality.AUDIO],\n contextWindowCompression: { slidingWindow: {} }\n };\n\nSession resumption\n------------------\n\nTo prevent session termination when the server periodically resets the WebSocket\nconnection, configure the [sessionResumption](/api/live#BidiGenerateContentSetup.FIELDS.SessionResumptionConfig.BidiGenerateContentSetup.session_resumption)\nfield within the [setup configuration](/api/live#BidiGenerateContentSetup).\n\nPassing this configuration causes the\nserver to send [SessionResumptionUpdate](/api/live#SessionResumptionUpdate)\nmessages, which can be used to resume the session by passing the last resumption\ntoken as the [`SessionResumptionConfig.handle`](/api/live#SessionResumptionConfig.FIELDS.string.SessionResumptionConfig.handle)\nof the subsequent connection.\n\nResumption tokens are valid for 2 hr after the last sessions termination. \n\n### Python\n\n import asyncio\n from google import genai\n from google.genai import types\n\n client = genai.Client()\n model = \"gemini-live-2.5-flash-preview\"\n\n async def main():\n print(f\"Connecting to the service with handle {previous_session_handle}...\")\n async with client.aio.live.connect(\n model=model,\n config=types.LiveConnectConfig(\n response_modalities=[\"AUDIO\"],\n session_resumption=types.SessionResumptionConfig(\n # The handle of the session to resume is passed here,\n # or else None to start a new session.\n handle=previous_session_handle\n ),\n ),\n ) as session:\n while True:\n await session.send_client_content(\n turns=types.Content(\n role=\"user\", parts=[types.Part(text=\"Hello world!\")]\n )\n )\n async for message in session.receive():\n # Periodically, the server will send update messages that may\n # contain a handle for the current state of the session.\n if message.session_resumption_update:\n update = message.session_resumption_update\n if update.resumable and update.new_handle:\n # The handle should be retained and linked to the session.\n return update.new_handle\n\n # For the purposes of this example, placeholder input is continually fed\n # to the model. In non-sample code, the model inputs would come from\n # the user.\n if message.server_content and message.server_content.turn_complete:\n break\n\n if __name__ == \"__main__\":\n asyncio.run(main())\n\n### JavaScript\n\n import { GoogleGenAI, Modality } from '@google/genai';\n\n const ai = new GoogleGenAI({});\n const model = 'gemini-live-2.5-flash-preview';\n\n async function live() {\n const responseQueue = [];\n\n async function waitMessage() {\n let done = false;\n let message = undefined;\n while (!done) {\n message = responseQueue.shift();\n if (message) {\n done = true;\n } else {\n await new Promise((resolve) =\u003e setTimeout(resolve, 100));\n }\n }\n return message;\n }\n\n async function handleTurn() {\n const turns = [];\n let done = false;\n while (!done) {\n const message = await waitMessage();\n turns.push(message);\n if (message.serverContent && message.serverContent.turnComplete) {\n done = true;\n }\n }\n return turns;\n }\n\n console.debug('Connecting to the service with handle %s...', previousSessionHandle)\n const session = await ai.live.connect({\n model: model,\n callbacks: {\n onopen: function () {\n console.debug('Opened');\n },\n onmessage: function (message) {\n responseQueue.push(message);\n },\n onerror: function (e) {\n console.debug('Error:', e.message);\n },\n onclose: function (e) {\n console.debug('Close:', e.reason);\n },\n },\n config: {\n responseModalities: [Modality.TEXT],\n sessionResumption: { handle: previousSessionHandle }\n // The handle of the session to resume is passed here, or else null to start a new session.\n }\n });\n\n const inputTurns = 'Hello how are you?';\n session.sendClientContent({ turns: inputTurns });\n\n const turns = await handleTurn();\n for (const turn of turns) {\n if (turn.sessionResumptionUpdate) {\n if (turn.sessionResumptionUpdate.resumable && turn.sessionResumptionUpdate.newHandle) {\n let newHandle = turn.sessionResumptionUpdate.newHandle\n // ...Store newHandle and start new session with this handle here\n }\n }\n }\n\n session.close();\n }\n\n async function main() {\n await live().catch((e) =\u003e console.error('got error', e));\n }\n\n main();\n\nReceiving a message before the session disconnects\n--------------------------------------------------\n\nThe server sends a [GoAway](/api/live#GoAway) message that signals that the current\nconnection will soon be terminated. This message includes the [timeLeft](/api/live#GoAway.FIELDS.google.protobuf.Duration.GoAway.time_left),\nindicating the remaining time and lets you take further action before the\nconnection will be terminated as ABORTED. \n\n### Python\n\n async for response in session.receive():\n if response.go_away is not None:\n # The connection will soon be terminated\n print(response.go_away.time_left)\n\n### JavaScript\n\n const turns = await handleTurn();\n\n for (const turn of turns) {\n if (turn.goAway) {\n console.debug('Time left: %s\\n', turn.goAway.timeLeft);\n }\n }\n\nReceiving a message when the generation is complete\n---------------------------------------------------\n\nThe server sends a [generationComplete](/api/live#BidiGenerateContentServerContent.FIELDS.bool.BidiGenerateContentServerContent.generation_complete)\nmessage that signals that the model finished generating the response. \n\n### Python\n\n async for response in session.receive():\n if response.server_content.generation_complete is True:\n # The generation is complete\n\n### JavaScript\n\n const turns = await handleTurn();\n\n for (const turn of turns) {\n if (turn.serverContent && turn.serverContent.generationComplete) {\n // The generation is complete\n }\n }\n\nWhat's next\n-----------\n\nExplore more ways to work with the Live API in the full\n[Capabilities](/gemini-api/docs/live) guide,\nthe [Tool use](/gemini-api/docs/live-tools) page, or the\n[Live API cookbook](https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Get_started_LiveAPI.ipynb)."]]