Skip to main content

Preserve Chat Configuration Updates

When working with the Gemini API's chat functionality, it's common to initialize a chat session with specific parameters such as temperature, max_output_tokens, system_instruction, and an initial history. A challenge arises when you later want to dynamically update only the temperature and max_output_tokens without losing the previously set system_instruction or the accumulated conversation history.

The core principle to understand from the provided API structure is that a Chat object's configuration (_config) and history (_comprehensive_history) are set during its initialization (__init__) and are not directly modifiable through public methods afterwards. Therefore, to achieve dynamic updates while preserving all existing settings, you must:

  1. Retrieve the current state of the existing Chat session (its configuration and history).
  2. Merge the new temperature and max_output_tokens values with the retrieved configuration.
  3. Recreate a new Chat instance using the combined, updated configuration and the preserved history.

Key Components​

  • google.genai.Client: The main client for interacting with the Gemini API.
  • google.genai.chats.Chat: Represents an ongoing chat session.
  • google.genai.types.GenerateContentConfig: A Pydantic model (or GenerateContentConfigDict for dictionary input) that encapsulates various model generation parameters, including temperature, max_output_tokens, and system_instruction.
  • google.genai.types.Content: Represents a piece of content in the conversation, used for both system_instruction and history.

Step-by-Step Guide for Preserving Configuration​

Assume you have an existing chat object that was created with an initial system_instruction and has accumulated history.

  1. Access Current Configuration and History:

    • The current GenerateContentConfig is stored in the _config attribute of the Chat object.
    • The current chat history is accessible via the get_history() method of the Chat object. It's recommended to retrieve the comprehensive history (default curated=False) to ensure all turns, including those with invalid outputs, are preserved for context.
    # From an existing chat_session object
    current_config = chat_session._config
    current_history = chat_session.get_history(curated=False)
  2. Prepare New Parameter Values:

    • Obtain the desired new temperature and max_output_tokens from the user or another source.
    • Implement logic to handle cases where the user might not want to change a specific parameter (e.g., allow empty input to keep the current value).
  3. Merge Configurations:

    • Convert the current_config (which is a Pydantic model) into a mutable dictionary using .model_dump().
    • Update this dictionary with the new temperature and max_output_tokens values. The existing system_instruction (and any other GenerateContentConfig fields) will be automatically carried over from current_config.
    • Reconstruct a new types.GenerateContentConfig object from this merged dictionary.
    # new_temperature and new_max_output_tokens are obtained from user input
    # (e.g., None if the user didn't specify a change)

    # Convert current config to dictionary for merging
    new_config_dict = current_config.model_dump() if current_config else {}

    if new_temperature is not None:
    new_config_dict['temperature'] = new_temperature
    if new_max_output_tokens is not None:
    new_config_dict['max_output_tokens'] = new_max_output_tokens

    # Create the final GenerateContentConfig object
    final_config = types.GenerateContentConfig(**new_config_dict)
  4. Recreate the Chat Session:

    • Call client.chats.create() again.
    • Pass the original model name (chat_session._model).
    • Pass the final_config that now contains all preserved and updated parameters.
    • Pass the current_history to ensure the conversational context is maintained.
    updated_chat_session = client.chats.create(
    model=chat_session._model,
    config=final_config,
    history=current_history
    )
  5. Replace the Old Chat Instance:

    • Crucially, you must replace your reference to the old chat_session object with the updated_chat_session object. All subsequent interactions will then use the new Chat instance with the desired persistent parameters.

Example Code​

The following example demonstrates an interactive chat where users can type update_params to change the temperature and max_output_tokens while keeping the system_instruction and history intact.

import google.genai as genai
from google.genai import types
import os

# Ensure your API key is set as an environment variable or passed directly
# os.environ["GOOGLE_API_KEY"] = "YOUR_API_KEY"

# --- Client Initialization ---
try:
client = genai.Client()
except Exception:
print("Google GenAI client could not be initialized.")
print("Please ensure GOOGLE_API_KEY environment variable is set or pass it directly.")
print("Using a mock client for demonstration purposes only if a real client isn't configured.")

# --- Mock Client for local testing without API key ---
class MockResponse:
def __init__(self, text):
self.text = text
def __str__(self):
return self.text

class MockChat:
def __init__(self, model: str, config: types.GenerateContentConfig, history: list[types.Content]):
self._model = model
self._config = config
self._comprehensive_history = history if history is not None else []
print(f"MockChat created: Model='{model}', Temp={config.temperature}, MaxTokens={config.max_output_tokens}, History Length={len(self._comprehensive_history)}")
if config.system_instruction:
print(f" System Instruction: '{config.system_instruction.parts[0].text}'")

def send_message(self, message: str):
# Append user message to mock history
self._comprehensive_history.append(types.Content(role='user', parts=[types.Part.from_text(message)]))

# Simulate model response
response_text = (
f"Mock response (T={self._config.temperature}, MaxTok={self._config.max_output_tokens})"
f" to: '{message}'. Current system instruction: '{self._config.system_instruction.parts[0].text}'"
)
model_content = types.Content(role='model', parts=[types.Part.from_text(response_text)])
self._comprehensive_history.append(model_content)

# Return a mock GenerateContentResponse
mock_gen_response = types.GenerateContentResponse(
candidates=[types.Candidate(content=model_content)]
)
return mock_gen_response

def get_history(self, curated: bool = False) -> list[types.Content]:
# For mock, always return comprehensive history
return self._comprehensive_history

class MockChats:
def create(self, model: str, config: types.GenerateContentConfig, history: list[types.Content] = None):
return MockChat(model=model, config=config, history=history)

class MockClient:
def __init__(self):
self.chats = MockChats()
client = MockClient()
# --- End Mock Client ---

def get_user_param_overrides():
"""Prompts the user for temperature and max_output_tokens, allowing for empty input."""
print("\n--- Enter New Model Parameters (Leave empty to keep current) ---")
temp_input = input("New Temperature (float, e.g., 0.7): ")
max_tokens_input = input("New Max Output Tokens (int, e.g., 200): ")

new_temperature = None
if temp_input:
try:
new_temperature = float(temp_input)
if not (0.0 <= new_temperature <= 1.0):
print("Note: Temperature usually ranges from 0.0 to 1.0 for typical use cases.")
except ValueError:
print("Invalid input for temperature. Keeping current value.")
new_temperature = None # Reset to None if invalid

new_max_output_tokens = None
if max_tokens_input:
try:
new_max_output_tokens = int(max_tokens_input)
if new_max_output_tokens <= 0:
print("Note: Max output tokens must be a positive integer.")
new_max_output_tokens = None # Reset to None if invalid
except ValueError:
print("Invalid input for max_output_tokens. Keeping current value.")
new_max_output_tokens = None # Reset to None if invalid

return new_temperature, new_max_output_tokens

def recreate_chat_with_updates(current_chat: genai.chats.Chat) -> genai.chats.Chat:
"""
Retrieves current chat state, merges with new parameters, and recreates the chat.
"""
print("\nAttempting to update chat parameters...")

# 1. Retrieve current configuration and history
# Accessing _config directly is necessary as there's no public getter
current_model_name = current_chat._model
current_gen_config: types.GenerateContentConfig = current_chat._config
current_history = current_chat.get_history(curated=False) # Get comprehensive history

# 2. Get new parameter values from user
new_temperature, new_max_output_tokens = get_user_param_overrides()

# 3. Merge configurations
# Start with a dictionary representation of the current config
updated_config_dict = current_gen_config.model_dump()

# Apply new parameters if provided by the user
if new_temperature is not None:
updated_config_dict['temperature'] = new_temperature
if new_max_output_tokens is not None:
updated_config_dict['max_output_tokens'] = new_max_output_tokens

# Create a new GenerateContentConfig object from the merged dictionary
final_gen_config = types.GenerateContentConfig(**updated_config_dict)

print("\nRecreating chat session with:")
print(f" Model: {current_model_name}")
print(f" Temperature: {final_gen_config.temperature}")
print(f" Max Output Tokens: {final_gen_config.max_output_tokens}")
if final_gen_config.system_instruction:
# Accessing parts[0].text is a simplification for typical text system instructions
print(f" System Instruction: '{final_gen_config.system_instruction.parts[0].text}'")
print(f" History length: {len(current_history)} turns")

# 4. Recreate the chat session
updated_chat = client.chats.create(
model=current_model_name,
config=final_gen_config,
history=current_history # Pass the preserved history
)
return updated_chat

def main_interactive_chat():
"""Initializes and runs an interactive chat session."""

# --- Initial Chat Setup ---
# Define an initial system instruction and history
initial_system_instruction = types.Content(parts=[types.Part.from_text("You are a knowledgeable and friendly AI assistant. Keep responses concise unless asked for details.")])
initial_history = [
types.Content(role='user', parts=[types.Part.from_text("Hi, what's your purpose?")]),
types.Content(role='model', parts=[types.Part.from_text("I'm here to assist you with information and tasks, designed by Google.")])
]

# Define initial generation config
initial_gen_config = types.GenerateContentConfig(
temperature=0.7,
max_output_tokens=150,
system_instruction=initial_system_instruction
)

print("--- Initializing Chat Session ---")
current_chat_session = client.chats.create(
model='gemini-pro', # Or your preferred model
config=initial_gen_config,
history=initial_history
)
print(f"Initial chat config: Temperature={current_chat_session._config.temperature}, MaxTokens={current_chat_session._config.max_output_tokens}")
print(f"Initial system instruction: {current_chat_session._config.system_instruction.parts[0].text}")
print(f"Initial history length: {len(current_chat_session.get_history())} turns")


print("\n--- Start Chatting ---")
print("Type 'update_params' to change temperature and max_output_tokens.")
print("Type 'quit' or 'exit' to end the chat.")

while True:
user_input = input("\nYou: ")
if user_input.lower() in ["quit", "exit"]:
print("Ending chat session. Goodbye!")
break

if user_input.lower() == "update_params":
# Call the function to recreate the chat with updated parameters
current_chat_session = recreate_chat_with_updates(current_chat_session)
print("\nChat session parameters updated. Continue typing messages.")
continue # Skip sending the "update_params" message to the model

try:
response = current_chat_session.send_message(user_input)
print(f"Model: {response.text}")
except Exception as e:
print(f"An error occurred during generation: {e}")
break

if __name__ == "__main__":
main_interactive_chat()