Environment details
- Programming language: python
- OS: Arch linux 64-bit
- Language runtime version: 3.13.8
- Package version: 2.7.0
Hello! My code is:
import asyncio
from pathlib import Path
import sys
from google import genai
from google.genai import types
from utils import Utils
from websockets.exceptions import ConnectionClosedError
import sys
key_path = Path("./gemini-api-key.txt")
if not key_path.exists():
key_path.touch()
with open(key_path, "r") as f:
key = f.readline().strip()
if not key:
print(f"Paste your API key into {key_path.resolve()}")
sys.exit(1)
client = genai.Client(
api_key=key,
http_options={'api_version': 'v1beta'}
)
MODEL = "gemini-2.5-flash-native-audio-latest"
get_weather_tool = {
"name": "getWeather",
"description": "gets the weather (temperature in Celsius) for a requested city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
}
HANDLE_STORE = Path("./last_session_handle.txt")
tools_config = [types.Tool(function_declarations=[get_weather_tool])]
def load_saved_handle():
if HANDLE_STORE.exists():
return HANDLE_STORE.read_text().strip()
return None
def save_handle(token):
HANDLE_STORE.write_text(token)
saved_token = load_saved_handle()
CONFIG = types.LiveConnectConfig(
response_modalities=["AUDIO", "TEXT"],
output_audio_transcription={},
tools=tools_config,
session_resumption=types.SessionResumptionConfig(
handle=saved_token
),
)
def get_weather(city=""):
temp = 30
if city == "Kyiv":
temp = 25
elif city == "Paris":
temp = 34
return temp
async def run():
current_handle = None
while True:
try:
current_config = types.LiveConnectConfig(
response_modalities=["AUDIO"],
output_audio_transcription={},
tools=tools_config,
session_resumption=types.SessionResumptionConfig(
handle=current_handle
)
)
async with client.aio.live.connect(model=MODEL, config=current_config) as session:
print("Connected!")
while True:
text = await asyncio.to_thread(input, "\nYou: ")
if text.lower() == "q":
sys.exit(0)
await session.send_realtime_input(
text=text
)
print("Gemini: ", end="")
async for response in session.receive():
if response.tool_call:
print("The tool was called")
function_responses = []
for fc in response.tool_call.function_calls:
print(f"F to call: {fc.name} {fc.args}")
if fc.name == "getWeather":
result = get_weather(fc.args["city"])
function_response = types.FunctionResponse(
id=fc.id,
name=fc.name,
response={"result": result}
)
function_responses.append(
function_response)
await session.send_tool_response(function_responses=function_responses)
if response.go_away is not None:
# The connection will soon be terminated
print("The session will soon be terminated: ",
response.go_away.time_left)
if response.server_content:
if response.server_content.output_transcription:
print(
Utils.yellow(response.server_content.output_transcription.text), end="", flush=True)
if response.server_content.model_turn:
for part in response.server_content.model_turn.parts:
if part.text:
print(Utils.red(part.text),
end="", flush=True)
if response.session_resumption_update and response.session_resumption_update.resumable and response.session_resumption_update.new_handle:
current_handle = response.session_resumption_update.new_handle
print(Utils.pink(
f"Saving normal handle: {response.session_resumption_update.new_handle}"))
print()
try:
iterator = session.receive().__aiter__()
trailing_response = await asyncio.wait_for(iterator.__anext__(), timeout=1.0)
if trailing_response.session_resumption_update and \
trailing_response.session_resumption_update.resumable and \
trailing_response.session_resumption_update.new_handle:
current_handle = trailing_response.session_resumption_update.new_handle
print(Utils.pink(
"Saved final handle (from trailing response)"))
except (asyncio.TimeoutError, StopAsyncIteration):
pass
except Exception as e:
print(f"\n[SYSTEM] Any error occured: {repr(e)}")
print("[SYSTEM] Reconnecting in 2 seconds...")
await asyncio.sleep(2)
except ConnectionClosedError as e:
print(f"\n[SYSTEM] Connection lost: {repr(e)}")
print("[SYSTEM] Reconnecting in 2 seconds...")
await asyncio.sleep(2)
if __name__ == "__main__":
asyncio.run(run())
So every time the main async receive loop exists because of StopAsyncIteration exeption, the server sends the updated handle with the latest model answer in another async session with the updated handle.
Take a look at the output:
Connected!
You: Remember numbers 3, 4, 5
Gemini: **Confirming Number Sequence**
I've stored the number sequence: 3, 4, 5. I will make sure I do not forget. I can retrieve it on request.
Got it, I'll remember 3, 4, 5.
Saved final handle (from trailing response)
You: Now tell me the numbers
Gemini: **Recalling the Digits**
I've successfully retrieved the memorized sequence: 3, 4, and 5. I am now prepared to provide this information as requested.
The numbers I remember are 3, 4, and 5.
Saved final handle (from trailing response)
You:
but if I comment the final handle section out, this is the behaviour:
Connected!
You: Remember numbers 3, 4, 5
Gemini: **Confirming Number Sequence**
I've stored the number sequence: 3, 4, 5. I will make sure I do not forget. I can retrieve it on request.
Got it, I'll remember the numbers 3, 4, and 5.
(No handle arrived here)
You: Tell me the numbers
Gemini: Saving normal handle: Cig5MTdjb2d1Y213ZnRoc25hZDQ5aWgzdnFybjJ4dzA2aXYyYThjNGFv (handle that arrived late after model's responce)
**Recalling Numbers Requested**
I recall the user's specific request in the prior turn: "Remember numbers 3, 4, 5." Accordingly, my primary task is to state those numbers, as requested.
The numbers you asked me to remember are 3, 4, and 5.
You:
Can you please fix this issue?
Environment details
Hello! My code is:
So every time the main async receive loop exists because of StopAsyncIteration exeption, the server sends the updated handle with the latest model answer in another async session with the updated handle.
Take a look at the output:
but if I comment the final handle section out, this is the behaviour:
Can you please fix this issue?