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()