Skip to content

Commit 7399074

Browse files
web-flowclaude
andcommitted
feat: migrate Voice PE conversation model to gemma4:e2b
Benchmarked gemma4:e2b vs qwen3.5:4b across 4 Voice PE scenarios: - gemma4:e2b: 20.1 tok/s avg, 1.21s latency (PASS) - qwen3.5:4b: 5.4 tok/s avg, 6.09s latency (FAIL) gemma4:e2b is 269% faster and passes the >10 tok/s Voice PE threshold. Updated model-update Job, README model table, and HA conversation agent. Added migration runbook with benchmark data and rollback plan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7f12596 commit 7399074

4 files changed

Lines changed: 118 additions & 10 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Ollama Gemma 4 Migration Runbook
2+
3+
**Date**: 2026-04-05
4+
**Author**: Claude Code
5+
**Status**: Complete
6+
7+
## Summary
8+
9+
Migrate Ollama models from current stack to Gemma 4 (e2b) after benchmark testing showed decisive performance improvements.
10+
11+
## Context
12+
13+
### Current Model Stack (pre-migration)
14+
| Use Case | Model | Speed | Notes |
15+
|----------|-------|-------|-------|
16+
| Voice PE conversation | qwen3.5:4b | 5.4 tok/s | BELOW 10 tok/s threshold |
17+
| Camera vision analysis | gemma3:4b | ~15 tok/s | LLM Vision blueprint |
18+
| Package delivery detection | llava:7b | ~8 tok/s | llmvision.image_analyzer |
19+
20+
### Hardware
21+
- **GPU**: NVIDIA RTX 3070 (8GB VRAM)
22+
- **Node**: k3s-vm-pumped-piglet-gpu
23+
- **Constraints**: OLLAMA_MAX_LOADED_MODELS=1, OLLAMA_GPU_OVERHEAD=1GB
24+
25+
### Benchmark Results (2026-04-05)
26+
27+
**qwen3.5:4b vs gemma4:e2b** across 4 Voice PE scenarios, 3 iterations each:
28+
29+
| Test | qwen3.5:4b | gemma4:e2b |
30+
|------|-----------|-----------|
31+
| Quick Q&A | 8.84s / 5.1 t/s | 0.99s / 21.7 t/s |
32+
| Time parsing | 3.58s / 6.0 t/s | 0.92s / 20.8 t/s |
33+
| Entity control | 5.89s / 5.3 t/s | 1.31s / 18.3 t/s |
34+
| Multi-step reasoning | 6.06s / 5.4 t/s | 1.60s / 19.6 t/s |
35+
| **Overall** | **6.09s / 5.4 t/s** | **1.21s / 20.1 t/s** |
36+
37+
**Result**: gemma4:e2b is 269% faster, 80% less latency. Passes Voice PE threshold (>10 tok/s). Response quality is concise and natural (better for voice).
38+
39+
### Gemma 4 Model Selection
40+
41+
| Variant | Size | VRAM Fit (8GB) | Notes |
42+
|---------|------|----------------|-------|
43+
| gemma4:e2b | 7.2GB (q4_K_M) | YES | Best fit for RTX 3070 |
44+
| gemma4:e4b | 9.6GB (q4_K_M) | NO | Exceeds VRAM with 1GB overhead |
45+
| gemma4:26b | 18GB | NO | Way too large |
46+
47+
## Migration Steps
48+
49+
### Step 1: Upgrade Ollama (DONE)
50+
- **File**: `gitops/clusters/homelab/apps/ollama/deployment.yaml`
51+
- **Change**: `ollama/ollama:0.17.7` -> `ollama/ollama:0.20.2`
52+
- **Why**: Gemma 4 requires Ollama 0.20.0+
53+
54+
### Step 2: Update model-update Job
55+
- **File**: `gitops/clusters/homelab/apps/ollama/job-model-update.yaml`
56+
- **Change**: NEW_MODEL=`gemma4:e2b`, OLD_MODEL=`qwen3.5:4b`
57+
- **Why**: Flux-managed model lifecycle
58+
59+
### Step 3: Update HA conversation agent
60+
- **Script**: `scripts/ollama/set-ha-model.sh gemma4:e2b`
61+
- **What it does**: Edits HA storage JSON inside HAOS container, reloads Ollama integration
62+
- **Rollback**: Timestamped backup created automatically
63+
64+
### Step 4: Update vision automations (NOT changing)
65+
- **LLM Vision** (automations.yaml lines 17, 37): `gemma3:4b` -> keep as-is
66+
- **Package Detection** (lines 189, 319): `llava:7b` -> keep as-is
67+
- **Why**: These use the `llmvision.image_analyzer` action with separate provider config.
68+
gemma4:e2b is multimodal but the LLM Vision integration manages its own model loading.
69+
Since OLLAMA_MAX_LOADED_MODELS=1, loading gemma4:e2b for vision would evict
70+
the conversation model. The vision models are loaded on-demand when events trigger.
71+
Changing these requires testing the LLM Vision integration separately.
72+
73+
### Step 5: Update model selection reference
74+
- **File**: `scripts/ollama/README.md`
75+
- **Change**: Add gemma4:e2b to model table, mark as recommended
76+
77+
### Step 6: Clean up old models
78+
- Old models (qwen3.5:4b, qwen3:4b) removed via model-update Job
79+
- gemma4:e2b pulled via same Job
80+
81+
## Rollback Plan
82+
83+
1. **Revert deployment image**: `ollama/ollama:0.20.2` -> `ollama/ollama:0.17.7` (only if 0.20.2 unstable)
84+
2. **Revert conversation model**: `scripts/ollama/set-ha-model.sh qwen3.5:4b`
85+
3. **Revert model-update Job**: Change NEW_MODEL back to `qwen3.5:4b`
86+
87+
## Verification
88+
89+
After migration:
90+
```bash
91+
# Check Ollama version and models
92+
scripts/ollama/check-ollama.sh
93+
94+
# Test inference speed
95+
scripts/ollama/test-inference.sh gemma4:e2b "what time is it?"
96+
97+
# Test voice PE end-to-end
98+
# Say "Hey Nabu, what's the weather like?" and confirm response <3s
99+
```
100+
101+
## Future Work
102+
103+
- Test gemma4:e2b with LLM Vision integration for camera analysis (replace gemma3:4b)
104+
- Test gemma4:e2b with package detection (replace llava:7b)
105+
- Consider gemma4:e4b if a GPU upgrade happens

gitops/clusters/homelab/apps/ollama/job-model-update.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
apiVersion: batch/v1
22
kind: Job
33
metadata:
4-
name: ollama-model-update-qwen35
4+
name: ollama-model-update-gemma4
55
namespace: ollama
66
annotations:
77
description: "Pull new Ollama model and remove old one via HTTP API"
@@ -22,8 +22,8 @@ spec:
2222
image: ghcr.io/homeiac/ollama-scripts:5cde4ad # {"$imagepolicy": "flux-system:ollama-scripts"}
2323
env:
2424
- name: NEW_MODEL
25-
value: "qwen3.5:4b"
25+
value: "gemma4:e2b"
2626
- name: OLD_MODEL
27-
value: "qwen2.5:3b"
27+
value: "qwen3.5:4b"
2828
- name: OLLAMA_URL
2929
value: "http://ollama-gpu.ollama.svc:80"

scripts/ollama/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ scripts/ollama/set-ha-model.sh qwen2.5:3b
2727

2828
| Model | GPU Split | Speed | Voice Use |
2929
|-------|-----------|-------|-----------|
30-
| qwen2.5:3b | 86% GPU | Fast | Recommended |
31-
| gemma3:4b | ~70% GPU | Medium | Good quality |
30+
| gemma4:e2b | ~85% GPU | 20 tok/s | **Recommended** (2026-04-05) |
31+
| qwen3.5:4b | ~70% GPU | 5.4 tok/s | Previous default, below threshold |
32+
| gemma3:4b | ~70% GPU | ~15 tok/s | Vision (LLM Vision blueprint) |
33+
| qwen2.5:3b | 86% GPU | Fast | Legacy |
3234
| qwen2.5:7b | 46% GPU | ~1 tok/s | Too slow |
3335

3436
## Related

scripts/ollama/compare-models.sh

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ print(json.dumps({
8282
'prompt': $(python3 -c "import json; print(json.dumps('''${prompt}'''))"),
8383
'system': $(python3 -c "import json; print(json.dumps('''${system}'''))"),
8484
'stream': False,
85+
'think': False,
8586
'options': {'num_predict': 256}
8687
}))
8788
" > "$TMPDIR_WORK/payload.json"
@@ -94,7 +95,7 @@ print(json.dumps({
9495

9596
parse_result() {
9697
local file="$1"
97-
python3 << 'PYEOF' "$file"
98+
python3 -c "
9899
import json, sys
99100
try:
100101
with open(sys.argv[1]) as f:
@@ -108,13 +109,13 @@ try:
108109
ttft = load_s + prompt_s
109110
print(f'{total_s:.2f}|{tps:.1f}|{tokens}|{ttft:.2f}|{eval_s:.2f}')
110111
except Exception as e:
111-
print(f'0|0|0|0|0')
112-
PYEOF
112+
print('0|0|0|0|0')
113+
" "$file"
113114
}
114115

115116
extract_response() {
116117
local file="$1"
117-
python3 << 'PYEOF' "$file"
118+
python3 -c "
118119
import json, sys
119120
try:
120121
with open(sys.argv[1]) as f:
@@ -125,7 +126,7 @@ try:
125126
print(r)
126127
except:
127128
print('ERROR: Could not parse response')
128-
PYEOF
129+
" "$file"
129130
}
130131

131132
ensure_model() {

0 commit comments

Comments
 (0)