Add Temperature Setting to Ollama Integration Configuration

I believe that a temperature setting for the Ollama integration setting would allow for more control over LLM hallucinations. The present Ollama integration does not control the LLM model temperature value setting.

I agree - the ChatGPT integration has the ability to modify the top_p and temperature - I don’t understand why the Ollama integration doesn’t have this. It might also be useful to have the ability to set the seed too in order to get more consistent responses. I’m using various VLMs and LLMs to recognise which refuse bins are out and the inconsistency between each run means I have to run multiple times to get consistent results. Also the AI task settings need the ability to change the system prompt. Telling a VLM or LLM that it’s part of home assistant causes them to imagine all kinds of scenarios when essentially you just want it to concentrate on the task at hand.

As I think this isn’t going to be fixed any time soon I’ve made a python script that’ll patch the relevant Ollama integration files so you can control the Temp, Top_p and Seed for any Ollama instance. It’s got some default settings that you can change before implementing and make sure you backup the files in /usr/src/homeassistant/homeassistant/components/ollama before you run it. NB. needs to be run from within the homeassistant docker instance - if you don’t know how to do that then find that out first.

Anyhoo I can now set Temp, Top_p and Seed in the UI for any ollama agent. Let me know if you need the code or any help getting it working.

Hey Spot, I’d like to get your script that exposes those. Needing to adjust these, and you were the first thing I’ve seen that looks like it has the settings i’m looking for!
Could I get the code you implemented for this to try out?

Sure - you’ll need the Advanced SSH & Web Terminal installed. Copy the python script to /config/patch_ollama.py and in the terminal run ‘docker exec -it homeassistant /bin/bash’ to get to the shell of the home assistant docker image.

Once in the shell and in the /config folder run:

python ./patch_ollama.py

The script will create a backup of the files affected but I’d suggest doing a full backup beforehand just in case. NB. this is unofficial code, comes with no warranty and needs to be run after every Home Assistant upgrade. That said, it appears to be working for me.

You can modify the script to change the default values to your liking - the settings here are generic.

#!/usr/bin/env python3
"""
Ollama Integration Patcher for Home Assistant
Adds temperature, top_p, and seed configuration options
Place this in your config folder and run after HA upgrades
"""

import os
import re
import shutil
from pathlib import Path

OLLAMA_PATH = Path("/usr/src/homeassistant/homeassistant/components/ollama")
BACKUP_SUFFIX = ".pre-patch-backup"

# Constants to add
NEW_CONSTANTS = '''
# Added by patch script
CONF_TEMPERATURE = "temperature"
DEFAULT_TEMPERATURE = 0.8
MIN_TEMPERATURE = 0.0
MAX_TEMPERATURE = 2.0

CONF_TOP_P = "top_p"
DEFAULT_TOP_P = 0.9
MIN_TOP_P = 0.0
MAX_TOP_P = 1.0

CONF_SEED = "seed"
DEFAULT_SEED = 0
MIN_SEED = 0
MAX_SEED = 2147483647
'''

def print_header():
    """Print script header"""
    print("=" * 50)
    print("Ollama Integration Patcher for Home Assistant")
    print("=" * 50)

def check_ollama_exists():
    """Check if ollama integration directory exists"""
    if not OLLAMA_PATH.exists():
        print(f"❌ ERROR: Ollama integration not found at {OLLAMA_PATH}")
        return False
    print(f"✓ Found Ollama integration at {OLLAMA_PATH}")
    return True

def check_if_patched():
    """Check if the integration is already patched"""
    const_file = OLLAMA_PATH / "const.py"
    if const_file.exists():
        content = const_file.read_text()
        if "CONF_TEMPERATURE" in content:
            print("✓ Integration is already patched")
            return True
    print("⚠ Integration needs patching")
    return False

def backup_file(filepath):
    """Create a backup of a file"""
    backup_path = Path(str(filepath) + BACKUP_SUFFIX)
    shutil.copy2(filepath, backup_path)
    print(f"  ✓ Backed up: {filepath.name}")

def patch_const_py():
    """Add new constants to const.py"""
    print("\n📝 Patching const.py...")
    const_file = OLLAMA_PATH / "const.py"
    
    backup_file(const_file)
    
    content = const_file.read_text()
    content += NEW_CONSTANTS
    const_file.write_text(content)
    print("  ✓ Added temperature, top_p, and seed constants")

def patch_init_py():
    """Add imports to __init__.py"""
    print("\n📝 Patching __init__.py...")
    init_file = OLLAMA_PATH / "__init__.py"
    
    backup_file(init_file)
    
    content = init_file.read_text()
    
    # Add to imports (after CONF_NUM_CTX,)
    content = re.sub(
        r'(CONF_NUM_CTX,)',
        r'\1\n    CONF_TEMPERATURE,\n    CONF_TOP_P,\n    CONF_SEED,',
        content,
        count=1
    )
    
    # Add to __all__ exports (after "CONF_NUM_CTX",)
    content = re.sub(
        r'("CONF_NUM_CTX",)',
        r'\1\n    "CONF_TEMPERATURE",\n    "CONF_TOP_P",\n    "CONF_SEED",',
        content,
        count=1
    )
    
    init_file.write_text(content)
    print("  ✓ Added imports and exports")

def patch_config_flow_py():
    """Add configuration fields to config_flow.py"""
    print("\n📝 Patching config_flow.py...")
    config_file = OLLAMA_PATH / "config_flow.py"
    
    backup_file(config_file)
    
    content = config_file.read_text()
    
    # Add imports in the const import block
    # Find the from .const import block and add our new imports
    content = re.sub(
        r'(from \.const import \([^)]*CONF_NUM_CTX,)',
        r'\1\n    CONF_TEMPERATURE,\n    CONF_TOP_P,\n    CONF_SEED,',
        content,
        count=1
    )
    
    content = re.sub(
        r'(from \.const import \([^)]*DEFAULT_NUM_CTX,)',
        r'\1\n    DEFAULT_TEMPERATURE,\n    DEFAULT_TOP_P,\n    DEFAULT_SEED,',
        content,
        count=1
    )
    
    content = re.sub(
        r'(from \.const import \([^)]*MIN_NUM_CTX,)',
        r'\1\n    MIN_TEMPERATURE,\n    MIN_TOP_P,\n    MIN_SEED,',
        content,
        count=1
    )
    
    content = re.sub(
        r'(from \.const import \([^)]*MAX_NUM_CTX,)',
        r'\1\n    MAX_TEMPERATURE,\n    MAX_TOP_P,\n    MAX_SEED,',
        content,
        count=1
    )
    
    # Add form fields after num_ctx field
    form_fields = '''            vol.Optional(
                CONF_TEMPERATURE,
                default=options.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE)
            ): vol.All(vol.Coerce(float), vol.Range(min=MIN_TEMPERATURE, max=MAX_TEMPERATURE)),
            vol.Optional(
                CONF_TOP_P,
                default=options.get(CONF_TOP_P, DEFAULT_TOP_P)
            ): vol.All(vol.Coerce(float), vol.Range(min=MIN_TOP_P, max=MAX_TOP_P)),
            vol.Optional(
                CONF_SEED,
                default=options.get(CONF_SEED, DEFAULT_SEED)
            ): vol.All(vol.Coerce(int), vol.Range(min=MIN_SEED, max=MAX_SEED)),'''
    
    # Find the num_ctx field and add our fields after it
    pattern = r'(vol\.Optional\(\s*CONF_NUM_CTX,.*?\),)'
    content = re.sub(pattern, r'\1' + '\n' + form_fields, content, flags=re.DOTALL)
    
    config_file.write_text(content)
    print("  ✓ Added configuration form fields")

def patch_entity_py():
    """Update entity.py to include new options"""
    print("\n📝 Patching entity.py...")
    entity_file = OLLAMA_PATH / "entity.py"
    
    backup_file(entity_file)
    
    content = entity_file.read_text()
    
    # Add imports
    content = re.sub(
        r'(CONF_NUM_CTX,)',
        r'\1\n    CONF_TEMPERATURE,\n    CONF_TOP_P,\n    CONF_SEED,',
        content,
        count=1
    )
    
    content = re.sub(
        r'(DEFAULT_NUM_CTX,)',
        r'\1\n    DEFAULT_TEMPERATURE,\n    DEFAULT_TOP_P,\n    DEFAULT_SEED,',
        content,
        count=1
    )
    
    # Update options dictionary - this is the tricky one
    # Look for the options={CONF_NUM_CTX: ...} pattern and replace it
    old_options = r'options=\{CONF_NUM_CTX: settings\.get\(CONF_NUM_CTX, DEFAULT_NUM_CTX\)\}'
    new_options = '''options={
                        CONF_NUM_CTX: settings.get(CONF_NUM_CTX, DEFAULT_NUM_CTX),
                        CONF_TEMPERATURE: settings.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE),
                        CONF_TOP_P: settings.get(CONF_TOP_P, DEFAULT_TOP_P),
                        CONF_SEED: settings.get(CONF_SEED, DEFAULT_SEED),
                    }'''
    
    content = re.sub(old_options, new_options, content)
    
    entity_file.write_text(content)
    print("  ✓ Updated options dictionary")

def print_completion_message():
    """Print completion message with instructions"""
    print("\n" + "=" * 50)
    print("✅ Patch Complete!")
    print("=" * 50)
    print(f"Backups saved with suffix: {BACKUP_SUFFIX}")
    print("\n⚠️  IMPORTANT: You must restart Home Assistant")
    print("   Settings > System > Restart")
    print("   Or run: ha core restart")
    print("\n📋 After restart:")
    print("   Reconfigure the Ollama integration to see:")
    print("   • Temperature (0.0 - 2.0)")
    print("   • Top P (0.0 - 1.0)")
    print("   • Seed (0 - 2147483647)")
    print("=" * 50)

def main():
    """Main execution function"""
    try:
        print_header()
        
        # Pre-flight checks
        if not check_ollama_exists():
            return 1
        
        if check_if_patched():
            print("\n✓ Nothing to do - already patched!")
            return 0
        
        # Apply patches
        print("\n🔧 Starting patch process...")
        patch_const_py()
        patch_init_py()
        patch_config_flow_py()
        patch_entity_py()
        
        print_completion_message()
        return 0
        
    except Exception as e:
        print(f"\n❌ ERROR: {e}")
        print(f"\n⚠️  Restore from backups if needed:")
        print(f"   Files are saved with {BACKUP_SUFFIX} extension")
        return 1

if __name__ == "__main__":
    exit(main())
1 Like