<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: akartit</title>
    <description>The latest articles on DEV Community by akartit (@akartit).</description>
    <link>https://dev.to/akartit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3342534%2Fdb273cde-cfd4-435e-a12f-565e618fc11b.jpg</url>
      <title>DEV Community: akartit</title>
      <link>https://dev.to/akartit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akartit"/>
    <language>en</language>
    <item>
      <title>I Ran Qwen3.8-27B on a 24GB MacBook - Here Is the VRAM Number Apple Does Not Print</title>
      <dc:creator>akartit</dc:creator>
      <pubDate>Thu, 27 Aug 2026 21:39:47 +0000</pubDate>
      <link>https://dev.to/akartit/i-ran-qwen38-27b-on-a-24gb-macbook-here-is-the-vram-number-apple-does-not-print-el</link>
      <guid>https://dev.to/akartit/i-ran-qwen38-27b-on-a-24gb-macbook-here-is-the-vram-number-apple-does-not-print-el</guid>
      <description>&lt;p&gt;I tried to run a 27B parameter model on a 24GB MacBook.&lt;/p&gt;

&lt;p&gt;It kernel-panicked my Mac. Three times. Hard reboot, lost work, the whole thing. And the fix every forum gives you made it &lt;strong&gt;worse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the number nobody prints, and how to find yours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=OteEizHcGmc" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5dgr489xylyyewoe2b3.jpg" alt="I ran Qwen3.8-27B on a 24GB MacBook. It kernel-panicked 3 times"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Click to watch: 12 minutes, the three panics, picking the quant, and the model driving a TV over MCP.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Mac has 24GB. Your GPU gets 17.76 GiB.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flyt8y9hbjeb2edvg9gzk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flyt8y9hbjeb2edvg9gzk.webp" alt="A 24GB bar above a 17.76 GiB bar at 74 percent of it, with the remaining 6.24 GiB marked as memory the GPU never sees"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is about &lt;strong&gt;74%&lt;/strong&gt; of what is printed on the box. Your weights, your KV cache and your compute buffers all share it.&lt;/p&gt;

&lt;p&gt;Every guide comparing a GGUF's file size against your total RAM is doing arithmetic against a number the GPU never sees.&lt;/p&gt;

&lt;p&gt;Find yours right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ioreg &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;recommendedMaxWorkingSetSize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Divide by 2^30.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your Mac&lt;/th&gt;
&lt;th&gt;Expected GPU working set&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;~11.8 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;24 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;17.76 GiB&lt;/strong&gt; (measured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;36 GB&lt;/td&gt;
&lt;td&gt;~26.6 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48 GB&lt;/td&gt;
&lt;td&gt;~35.5 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;64 GB&lt;/td&gt;
&lt;td&gt;~47.4 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only the 24GB row is measured on my machine. Run the command and trust your own number.&lt;/p&gt;

&lt;p&gt;Worth being precise about what this is, because the popular version is wrong. Apple does not call it a cap. Their &lt;a href="https://developer.apple.com/documentation/metal/mtldevice/recommendedmaxworkingsetsize" rel="noopener noreferrer"&gt;own definition&lt;/a&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An approximation of how much memory, in bytes, this GPU device can allocate without affecting its runtime performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a threshold Apple recommends you stay under, not a wall that stops you. Nothing refuses your allocation at 17.76 GiB. You are free to walk straight past it, into territory Apple told you to avoid, which on this machine contains a driver bug.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why it panics instead of erroring
&lt;/h2&gt;

&lt;p&gt;Q4_K_M, the quant everyone recommends, is 15.3GB of weights and peaks at 19.6GB once the projector and runtime buffers load. That is &lt;strong&gt;110%&lt;/strong&gt; of the budget.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;panic(cpu 9): "pending memory object unexpectedly found in non pending hash"
IOGPUGroupMemory.cpp:528
process: WindowServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The important line is elsewhere in the log:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compressor Info: 4% of compressed pages limit (OK)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Four percent. &lt;strong&gt;The machine did not run out of memory.&lt;/strong&gt; It hit a bug in Apple's GPU driver.&lt;/p&gt;

&lt;p&gt;Not just me, and not just llama.cpp. The same failure is filed repeatedly against MLX: a &lt;a href="https://github.com/ml-explore/mlx/issues/3186" rel="noopener noreferrer"&gt;kernel panic on an M4 Max during a large prefill&lt;/a&gt;, and &lt;a href="https://github.com/ml-explore/mlx-lm/issues/883" rel="noopener noreferrer"&gt;unbounded KV cache growth taking down a Mac Studio&lt;/a&gt;. That thread names the mechanism: the memory is wired, so macOS's out-of-memory killer cannot reclaim it and never even sees pressure.&lt;/p&gt;

&lt;p&gt;A normal process that asks for too much gets killed. A GPU allocation that asks for too much takes the kernel with it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why raising the wired limit backfires
&lt;/h2&gt;

&lt;p&gt;Every forum says &lt;code&gt;sudo sysctl iogpu.wired_limit_mb=20480&lt;/code&gt;. It is backwards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1oruv34pupqkqp72jjj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1oruv34pupqkqp72jjj.webp" alt="Pageable memory blocks being reclaimed into swap, versus wired memory blocks padlocked with nowhere to go"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Normal memory is a car in a car park. If the system needs the space, it tows the car.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wired memory is bolted to the ground.&lt;/strong&gt; The kernel is not allowed to move it, compress it, or swap it out. That is what the word means.&lt;/p&gt;

&lt;p&gt;Raising the limit does not make the car park bigger. It bolts down more cars. You get &lt;em&gt;less&lt;/em&gt; room to shuffle, and you remove macOS's ability to dig itself out.&lt;/p&gt;

&lt;p&gt;I raised it. It panicked again.&lt;/p&gt;
&lt;h2&gt;
  
  
  "But I have 64GB and it is still slow"
&lt;/h2&gt;

&lt;p&gt;This is the most common reply I get, so it gets its own answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnx78sw0i2z42pfmw3ves.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnx78sw0i2z42pfmw3ves.webp" alt="Three identical 64GB Macs beside bandwidth bars of 273, 400 and 800 GB per second"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RAM decides what fits. Bandwidth decides how fast.&lt;/strong&gt; Separate limits, constantly conflated.&lt;/p&gt;

&lt;p&gt;Your GPU re-reads the whole active weight set for every token it writes, so speed is roughly bandwidth divided by model size. Your RAM figure tells you nothing about your bandwidth:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chip&lt;/th&gt;
&lt;th&gt;Bandwidth&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;M4 Pro&lt;/td&gt;
&lt;td&gt;273 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M2 / M3 Max&lt;/td&gt;
&lt;td&gt;400 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra&lt;/td&gt;
&lt;td&gt;800 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three times the spread at the same 64GB. On the Pro tier you are capped and no setting fixes it. You are likely comparing yourself to people with double your bandwidth.&lt;/p&gt;
&lt;h2&gt;
  
  
  What actually works
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llama-server &lt;span class="nt"&gt;-m&lt;/span&gt; Qwen3.8-27B-UD-Q3_K_XL.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mmproj&lt;/span&gt; mmproj-F16.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-ngl&lt;/span&gt; 99 &lt;span class="nt"&gt;-c&lt;/span&gt; 16384 &lt;span class="nt"&gt;-fa&lt;/span&gt; on &lt;span class="nt"&gt;-ctk&lt;/span&gt; q4_0 &lt;span class="nt"&gt;-ctv&lt;/span&gt; q4_0 &lt;span class="nt"&gt;--jinja&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;-ctk q4_0 -ctv q4_0&lt;/code&gt; quantises the KV cache itself to 4 bits, roughly 4x smaller. On this machine it is the difference between 16k context fitting and not. &lt;strong&gt;Quantise your cache before you drop a tier on your weights.&lt;/strong&gt; You lose far less.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decode, clean machine&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12.60 tok/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same config, 4.15GB of swap present&lt;/td&gt;
&lt;td&gt;11.26 tok/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefix cache&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;14.2x&lt;/strong&gt; (47.9s to 3.4s on 4,826 tokens)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Objective tasks&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;21/21&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool calling&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wired memory&lt;/td&gt;
&lt;td&gt;16.37 GiB, &lt;strong&gt;92%&lt;/strong&gt; of budget&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That second row is the point. Same everything, 11% slower purely because the machine was carrying swap from a long session. Eleven percent of your throughput can be sitting in a browser tab you forgot about.&lt;/p&gt;

&lt;p&gt;And one thing that nearly caught me out: my fit tool &lt;strong&gt;estimated&lt;/strong&gt; 90% of budget. Measured, it wires &lt;strong&gt;92%&lt;/strong&gt;, hitting &lt;strong&gt;94%&lt;/strong&gt; the moment you show it an image. Treat any fit estimate as a floor, not a ceiling.&lt;/p&gt;
&lt;h2&gt;
  
  
  Will it fit on your machine
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;localfit
localfit qwen3.8-27b &lt;span class="nt"&gt;--gpu-gb&lt;/span&gt; 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;--gpu-gb&lt;/code&gt; scores every quant against your budget instead of mine. Feed it your own &lt;code&gt;ioreg&lt;/code&gt; number.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnassKartit" rel="noopener noreferrer"&gt;
        AnassKartit
      &lt;/a&gt; / &lt;a href="https://github.com/AnassKartit/localfit" rel="noopener noreferrer"&gt;
        localfit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Will it fit? GPU toolkit for AI models — MLX + GGUF + Cloud in one menu.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;localfit&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;██╗      ██████╗  ██████╗ █████╗ ██╗     ███████╗██╗████████╗
██║     ██╔═══██╗██╔════╝██╔══██╗██║     ██╔════╝██║╚══██╔══╝
██║     ██║   ██║██║     ███████║██║     █████╗  ██║   ██║
██║     ██║   ██║██║     ██╔══██║██║     ██╔══╝  ██║   ██║
███████╗╚██████╔╝╚██████╗██║  ██║███████╗██║     ██║   ██║
╚══════╝ ╚═════╝  ╚═════╝╚═╝  ╚═╝╚══════╝╚═╝     ╚═╝   ╚═╝
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Will it fit?&lt;/strong&gt; Say what model you want — localfit figures out the rest.&lt;/p&gt;
&lt;p&gt;Text and image generation. Fits locally? Run it. Doesn't fit? Free Kaggle GPU. Still too big? RunPod cloud. One command does everything: downloads models, starts servers, configures tools, launches your UI.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pip install localfit&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;One Command — Everything Works&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Chat + image gen in Open WebUI (gemma4 LLM + Flux Klein 4B images)&lt;/span&gt;
localfit launch openwebui --model gemma4:e4b --img klein-4b
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Code with image gen in localcoder&lt;/span&gt;
localfit launch localcoder --model gemma4:e4b --img klein-4b

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Claude Code with image MCP tools&lt;/span&gt;
localfit launch claude --model gemma4:e4b --img klein-4b

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Model doesn't fit locally? Run on free Kaggle GPU&lt;/span&gt;
localfit launch openwebui&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnassKartit/localfit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  The full version
&lt;/h2&gt;

&lt;p&gt;The complete write-up has the agent tool-calling results, the MCP setup where I handed the model control of a television, and the trick that gets llama.cpp decoding video when the server insists it cannot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.kartit.net/blog/qwen38-27b-24gb-macbook" rel="noopener noreferrer"&gt;Read it on kartit.net&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have hit this, I want your &lt;code&gt;ioreg&lt;/code&gt; number and your chip. Drop them in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>opensource</category>
      <category>macos</category>
    </item>
    <item>
      <title>I Tested Every Gemma 4 Model Locally on My MacBook - What Actually Works</title>
      <dc:creator>akartit</dc:creator>
      <pubDate>Sat, 04 Apr 2026 10:06:32 +0000</pubDate>
      <link>https://dev.to/akartit/i-tested-every-gemma-4-model-locally-on-my-macbook-what-actually-works-3g2o</link>
      <guid>https://dev.to/akartit/i-tested-every-gemma-4-model-locally-on-my-macbook-what-actually-works-3g2o</guid>
      <description>&lt;p&gt;&lt;em&gt;Audio ASR in 3 languages, image understanding, full-stack app generation, coding, and agentic behavior -- all running on a MacBook M4 Pro with 24GB RAM.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interactive version with playable audio, live charts, and the working React app:&lt;/strong&gt; &lt;a href="https://gemma4-benchmark.pages.dev" rel="noopener noreferrer"&gt;gemma4-benchmark.pages.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1UBpg6efjBs" width="100%" height="390"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;Google just released Gemma 4 -- their new family of open-source multimodal models. Four sizes, Apache-2.0 licensed, supports text + image + audio.&lt;/p&gt;

&lt;p&gt;I spent a day testing every variant. Real audio files. Real images. Code that has to compile and run. Here is my honest report.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gemma 4 Family
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E2B&lt;/strong&gt; -- Dense 2.3B, Text/Image/Audio, 4 GB at 4-bit. Phones and edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E4B&lt;/strong&gt; -- Dense 4.5B, Text/Image/Audio, 5.5 GB at 4-bit. Laptops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;26B-A4B&lt;/strong&gt; -- MoE 4B active/26B total, Text/Image, 16-18 GB at 4-bit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;31B&lt;/strong&gt; -- Dense 31B, Text/Image, 17-20 GB at 4-bit. Maximum quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Speed Benchmarks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4aqwtq5rh2rn0euwhsdh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4aqwtq5rh2rn0euwhsdh.png" alt="Speed benchmark chart" width="799" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama:&lt;/strong&gt; E2B &lt;strong&gt;95 tok/s&lt;/strong&gt; | E4B &lt;strong&gt;57 tok/s&lt;/strong&gt; | 26B ~2 tok/s (swap) | 31B won't fit&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unsloth MLX:&lt;/strong&gt; E2B 81 tok/s (3.6 GB) | E4B 49 tok/s (5.6 GB)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama is 15-20% faster. Unsloth MLX uses 40% less memory.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Audio ASR: 3 Languages
&lt;/h2&gt;

&lt;p&gt;Tested via Ollama OpenAI-compatible endpoint. Only E2B and E4B support audio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lma66xvmiyldchg169w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lma66xvmiyldchg169w.png" alt="Audio ASR quality comparison" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Listen to all test audio samples:&lt;/strong&gt; &lt;a href="https://gemma4-benchmark.pages.dev/audio_player.html" rel="noopener noreferrer"&gt;Audio Player&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  English ASR
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;E4B (1.0s):&lt;/strong&gt; Perfect transcription. Every word correct with punctuation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2B (2.8s):&lt;/strong&gt; Garbled -- missing words, no punctuation.&lt;/p&gt;

&lt;h3&gt;
  
  
  French ASR
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;E4B (1.6s):&lt;/strong&gt; Perfect transcription with all French accents correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2B (4.1s):&lt;/strong&gt; Fragmented, missing most of the sentence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arabic ASR
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;E4B (6.0s):&lt;/strong&gt; Perfect Arabic transcription -- every word correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2B (6.0s):&lt;/strong&gt; Garbled -- wrong words, disordered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speech Translation (E4B)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;French to English:&lt;/strong&gt; "All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience..."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arabic to English:&lt;/strong&gt; "Hello, I am an artificial intelligence model. Today we will test speech recognition in the Arabic language..."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E4B is dramatically better than E2B for audio across all 3 languages.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Image Understanding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Test 1: Thai Temple -- Landmark Identification
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fai419sdnjabqsvybuuua.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fai419sdnjabqsvybuuua.jpg" alt="Thai Temple in Bangkok" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E4B (54 tok/s):&lt;/strong&gt; Thailand, Bangkok, &lt;strong&gt;Wat Phra Kaew&lt;/strong&gt; (Temple of the Emerald Buddha) within the Grand Palace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2B (88 tok/s):&lt;/strong&gt; Thailand, Bangkok, Grand Palace (less specific).&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 2: AI-Generated Tokyo + Japanese OCR
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2ha05gz4bfen8spy062.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2ha05gz4bfen8spy062.jpg" alt="AI-generated Tokyo street at night" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-generated with nano-banana / Gemini&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both models correctly read Japanese kanji: &lt;strong&gt;新宿ラーメン通り&lt;/strong&gt; (Shinjuku Ramen Street)&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 3: Venice Seagull
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu1wze6ah7j1w7axabck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu1wze6ah7j1w7axabck.png" alt="Seagull in Venice" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E4B:&lt;/strong&gt; "A magnificent seagull perches watchfully atop a sculpted pedestal. The backdrop is a rich study in contrasting architectural styles..."&lt;/p&gt;




&lt;h2&gt;
  
  
  Full-Stack App Generation
&lt;/h2&gt;

&lt;p&gt;E4B generated a 155-line working React + Tailwind Task Manager:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yx0ooils57xg8d1rj2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yx0ooils57xg8d1rj2b.png" alt="E4B Task Manager running in browser" width="799" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://gemma4-benchmark.pages.dev/task_manager.html" rel="noopener noreferrer"&gt;gemma4-benchmark.pages.dev/task_manager.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;E2B failed&lt;/strong&gt; -- code fragments instead of single file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coding: Compile and Run
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;E2B&lt;/th&gt;
&lt;th&gt;E4B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fibonacci&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sieve of Eratosthenes&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON processor&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP request&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React single file&lt;/td&gt;
&lt;td&gt;FAIL&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Agentic Multi-Step Reasoning
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ayn5eme064mpvasv1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ayn5eme064mpvasv1w.png" alt="Agentic radar chart" width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6-step blog platform design. Both completed 6/6 steps. E4B output was 57% longer with more detail.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why 26B Fails on 24GB
&lt;/h2&gt;

&lt;p&gt;Community reports from &lt;a href="https://reddit.com/r/LocalLLaMA" rel="noopener noreferrer"&gt;r/LocalLLaMA&lt;/a&gt; suggest Gemma 4 has a KV cache memory issue (not verified on our hardware):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;31B at 262K context: ~22GB just for KV cache (on top of model)&lt;/li&gt;
&lt;li&gt;Google did not adopt KV-reducing techniques from Qwen 3.5&lt;/li&gt;
&lt;li&gt;Workaround: &lt;code&gt;--ctx-size 8192 --cache-type-k q4_0 --parallel 1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Official Benchmarks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0uaf51v2nn8frsux78oi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0uaf51v2nn8frsux78oi.png" alt="Official Google Benchmarks" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26fnu51wwcrea2tg3dhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26fnu51wwcrea2tg3dhq.png" alt="Final verdict scorecard" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  E4B -- The Sweet Spot -- 8.5/10
&lt;/h3&gt;

&lt;p&gt;Perfect ASR in 3 languages. Working React app. Japanese OCR. 57 tok/s. 5.6 GB.&lt;/p&gt;

&lt;h3&gt;
  
  
  E2B -- Speed Demon -- 7/10
&lt;/h3&gt;

&lt;p&gt;95 tok/s. 3.6 GB. Python works. Audio garbled. Failed complex HTML gen.&lt;/p&gt;

&lt;h3&gt;
  
  
  26B-A4B -- Heartbreaker -- 2/10 on 24GB
&lt;/h3&gt;

&lt;p&gt;Amazing benchmarks (88.3% AIME). ~2 tok/s on 24GB. Needs 32GB+.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ollama
ollama pull gemma4:e4b
ollama run gemma4:e4b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For 24GB MacBook: &lt;code&gt;ollama run gemma4:e4b&lt;/code&gt; is the answer.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tested April 3, 2026. MacBook Pro M4 Pro, 24GB, macOS Sequoia.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive version:&lt;/strong&gt; &lt;a href="https://gemma4-benchmark.pages.dev" rel="noopener noreferrer"&gt;gemma4-benchmark.pages.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; &lt;a href="https://ai.google.dev/gemma/docs/core/model_card_4" rel="noopener noreferrer"&gt;Google Model Card&lt;/a&gt; | &lt;a href="https://huggingface.co/blog/gemma4" rel="noopener noreferrer"&gt;HuggingFace Blog&lt;/a&gt; | &lt;a href="https://ollama.com/library/gemma4" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; | &lt;a href="https://unsloth.ai/docs/models/gemma-4" rel="noopener noreferrer"&gt;Unsloth Guide&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Add Gemma 4 Models to OpenClaw (Fix Missing Model Error)</title>
      <dc:creator>akartit</dc:creator>
      <pubDate>Fri, 03 Apr 2026 19:37:42 +0000</pubDate>
      <link>https://dev.to/akartit/how-to-add-gemma-4-models-to-openclaw-fix-missing-model-error-1b3l</link>
      <guid>https://dev.to/akartit/how-to-add-gemma-4-models-to-openclaw-fix-missing-model-error-1b3l</guid>
      <description>&lt;p&gt;Google's Gemma 4 family just dropped — and it's a big deal. Open weights, Apache 2.0 license, multimodal, reasoning-capable, and free to use via the Gemini API. But if you're running OpenClaw as your AI assistant gateway, you'll hit a wall: &lt;strong&gt;OpenClaw doesn't have Gemma 4 in its built-in model catalog yet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how I fixed it in under 5 minutes — for both the &lt;strong&gt;31B Dense&lt;/strong&gt; and the &lt;strong&gt;26B MoE (A4B)&lt;/strong&gt; variants.&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/P4pUmL3HBRs" width="100%" height="390"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;After setting &lt;code&gt;google/gemma-4-31b-it&lt;/code&gt; as my default model in OpenClaw, running &lt;code&gt;openclaw models list&lt;/code&gt; showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model                          Input   Ctx   Auth  Tags
google/gemma-4-31b-it          -       -     -     default,configured,missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;missing&lt;/code&gt; tag means OpenClaw has the model name in config but no metadata — no input types, no context window size, no API protocol. It doesn't know &lt;em&gt;how&lt;/em&gt; to talk to it.&lt;/p&gt;

&lt;p&gt;Meanwhile, the raw API works fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gemma-4-31b-it"&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://generativelanguage.googleapis.com/v1beta/models/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:streamGenerateContent?key=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "contents": [{
      "role": "user",
      "parts": [{"text": "Hello, what model are you?"}]
    }],
    "generationConfig": {
      "thinkingConfig": { "thinkingLevel": "HIGH" }
    },
    "tools": [{ "googleSearch": {} }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works perfectly. The problem is purely on OpenClaw's side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Register Custom Model Metadata
&lt;/h2&gt;

&lt;p&gt;OpenClaw's config schema supports a top-level &lt;code&gt;models&lt;/code&gt; block where you can inject model definitions that the built-in catalog doesn't have. You need to tell OpenClaw the API protocol, capabilities, and context window for each model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Edit your OpenClaw config
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt; and add a &lt;code&gt;models&lt;/code&gt; block at the top level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"merge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"providers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"google"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://generativelanguage.googleapis.com/v1beta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gemma-4-31b-it"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gemma 4 31B IT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"google-generative-ai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"reasoning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"contextWindow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;262144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maxTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;131072&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gemma-4-26b-a4b-it"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gemma 4 26B A4B IT (MoE)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"google-generative-ai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"reasoning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"contextWindow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;262144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maxTokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;262144&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What each field does
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"merge"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds to the existing catalog instead of replacing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;baseUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Google's v1beta endpoint&lt;/td&gt;
&lt;td&gt;Required by schema, even for the built-in Google provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"google-generative-ai"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tells OpenClaw to use Google's native API protocol (not OpenAI-compatible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reasoning&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both models support configurable thinking modes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;input&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["text", "image"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multimodal: text + image (variable aspect ratio &amp;amp; resolution)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contextWindow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;262144&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;262K context window (256K usable + overhead)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxTokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;131072&lt;/code&gt; / &lt;code&gt;262144&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Max output tokens (31B: 131K, 26B MoE: 262K)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 2: Set up Google API auth
&lt;/h3&gt;

&lt;p&gt;Make sure you have a Google auth profile in OpenClaw. If you don't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw models auth login
&lt;span class="c"&gt;# Select "google" provider, "api_key" mode&lt;/span&gt;
&lt;span class="c"&gt;# Paste your Gemini API key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or set it via environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-key-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Validate, restart, verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Validate config&lt;/span&gt;
openclaw config validate

&lt;span class="c"&gt;# Restart the gateway&lt;/span&gt;
openclaw gateway restart

&lt;span class="c"&gt;# Check models&lt;/span&gt;
openclaw models list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model                          Input      Ctx    Auth  Tags
google/gemma-4-31b-it          text+image 256k   yes   default,configured
google/gemma-4-26b-a4b-it      text+image 256k   yes   configured
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more &lt;code&gt;missing&lt;/code&gt; tag!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Set your default and test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set default model&lt;/span&gt;
openclaw models &lt;span class="nb"&gt;set &lt;/span&gt;google/gemma-4-31b-it

&lt;span class="c"&gt;# Test it&lt;/span&gt;
openclaw agent &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Hello! What model are you?"&lt;/span&gt; &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--session-id&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I am Gemma 4, a large language model developed by Google DeepMind."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Auto-Patch Script: One Command to Fix Everything
&lt;/h2&gt;

&lt;p&gt;Don't want to edit JSON by hand? Save this script and run it — it patches your OpenClaw config automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# openclaw-gemma4-patch.sh&lt;/span&gt;
&lt;span class="c"&gt;# Auto-patches OpenClaw config to add Gemma 4 models (31B Dense + 26B MoE)&lt;/span&gt;
&lt;span class="c"&gt;# Usage: chmod +x openclaw-gemma4-patch.sh &amp;amp;&amp;amp; ./openclaw-gemma4-patch.sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OPENCLAW_CONFIG_PATH&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.openclaw/openclaw.json&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONFIG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.bak.&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- Preflight checks ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; openclaw &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: openclaw not found in PATH"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3 &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: python3 required for JSON patching"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: OpenClaw config not found at &lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Run 'openclaw configure' first or set OPENCLAW_CONFIG_PATH"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Backing up config to &lt;/span&gt;&lt;span class="nv"&gt;$BACKUP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- Patch the config ---&lt;/span&gt;
python3 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PYEOF&lt;/span&gt;&lt;span class="sh"&gt;'
import json, sys, os

config_path = os.environ.get("OPENCLAW_CONFIG_PATH",
    os.path.expanduser("~/.openclaw/openclaw.json"))

with open(config_path, "r") as f:
    config = json.load(f)

gemma4_models = [
    {
        "id": "gemma-4-31b-it",
        "name": "Gemma 4 31B IT",
        "api": "google-generative-ai",
        "reasoning": True,
        "input": ["text", "image"],
        "contextWindow": 262144,
        "maxTokens": 131072
    },
    {
        "id": "gemma-4-26b-a4b-it",
        "name": "Gemma 4 26B A4B IT (MoE)",
        "api": "google-generative-ai",
        "reasoning": True,
        "input": ["text", "image"],
        "contextWindow": 262144,
        "maxTokens": 262144
    }
]

if "models" not in config:
    config["models"] = {"mode": "merge", "providers": {}}
if "providers" not in config["models"]:
    config["models"]["providers"] = {}
if "google" not in config["models"]["providers"]:
    config["models"]["providers"]["google"] = {
        "baseUrl": "https://generativelanguage.googleapis.com/v1beta",
        "models": []
    }

google = config["models"]["providers"]["google"]
if "baseUrl" not in google:
    google["baseUrl"] = "https://generativelanguage.googleapis.com/v1beta"
if "models" not in google:
    google["models"] = []

existing_ids = {m["id"] for m in google["models"]}
added = []
for model in gemma4_models:
    if model["id"] not in existing_ids:
        google["models"].append(model)
        added.append(model["id"])

if "agents" in config and "defaults" in config["agents"]:
    defaults = config["agents"]["defaults"]
    if "models" not in defaults:
        defaults["models"] = {}
    for model in gemma4_models:
        key = f"google/{model['id']}"
        if key not in defaults["models"]:
            defaults["models"][key] = {}

ordered = {"models": config.pop("models")}
ordered.update(config)

with open(config_path, "w") as f:
    json.dump(ordered, f, indent=2)
    f.write("&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;")

if added:
    print(f"Added models: {', '.join(added)}")
else:
    print("Models already present, no changes needed")
&lt;/span&gt;&lt;span class="no"&gt;PYEOF

&lt;/span&gt;&lt;span class="c"&gt;# --- Validate ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Validating config..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; openclaw config validate&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Validation failed! Restoring backup..."&lt;/span&gt;
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restored. Please check your config manually."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- Restart gateway ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restarting gateway..."&lt;/span&gt;
openclaw gateway restart

&lt;span class="c"&gt;# --- Verify ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verifying models..."&lt;/span&gt;
openclaw models list

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Done! Gemma 4 models are ready."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Set your default with: openclaw models set google/gemma-4-31b-it"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test with: openclaw agent -m 'Hello!' --local --session-id test-gemma"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x openclaw-gemma4-patch.sh
./openclaw-gemma4-patch.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script is &lt;strong&gt;idempotent&lt;/strong&gt; — running it twice won't duplicate models. It backs up your config before patching and auto-rolls back if validation fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Both Models
&lt;/h2&gt;

&lt;p&gt;Once patched, verify both variants respond:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test the 31B Dense model&lt;/span&gt;
openclaw models &lt;span class="nb"&gt;set &lt;/span&gt;google/gemma-4-31b-it
openclaw agent &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"What model are you? One sentence."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--session-id&lt;/span&gt; test-31b &lt;span class="nt"&gt;--json&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import json,sys
r = json.load(sys.stdin)
print(f'Model: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;meta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;agentMeta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}')
print(f'Response: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;payloads&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][0][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}')
print(f'Time: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;meta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;durationMs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}ms')
"&lt;/span&gt;

&lt;span class="c"&gt;# Test the 26B MoE model&lt;/span&gt;
openclaw models &lt;span class="nb"&gt;set &lt;/span&gt;google/gemma-4-26b-a4b-it
openclaw agent &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"What model are you? One sentence."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--session-id&lt;/span&gt; test-26b &lt;span class="nt"&gt;--json&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import json,sys
r = json.load(sys.stdin)
print(f'Model: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;meta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;agentMeta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}')
print(f'Response: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;payloads&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][0][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}')
print(f'Time: {r[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;meta&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;][&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;durationMs&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}ms')
"&lt;/span&gt;

&lt;span class="c"&gt;# Switch back to your preferred default&lt;/span&gt;
openclaw models &lt;span class="nb"&gt;set &lt;/span&gt;google/gemma-4-31b-it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test via Raw curl (No OpenClaw)
&lt;/h2&gt;

&lt;p&gt;Verify your API key works before patching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# test-gemma4-api.sh — Quick API smoke test for both Gemma 4 models&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GEMINI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: Set GEMINI_API_KEY first"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

for &lt;/span&gt;MODEL &lt;span class="k"&gt;in &lt;/span&gt;gemma-4-31b-it gemma-4-26b-a4b-it&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Testing &lt;/span&gt;&lt;span class="nv"&gt;$MODEL&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
  curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"https://generativelanguage.googleapis.com/v1beta/models/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:generateContent?key=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
      "contents": [{
        "role": "user",
        "parts": [{"text": "What model are you? Reply in one sentence."}]
      }],
      "generationConfig": {
        "thinkingConfig": { "thinkingLevel": "HIGH" }
      }
    }'&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import json,sys
r = json.load(sys.stdin)
for part in r.get('candidates',[{}])[0].get('content',{}).get('parts',[]):
    if 'text' in part:
        print(part['text'])
        break
"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Diagnosed This
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;openclaw models list&lt;/code&gt;&lt;/strong&gt; — showed model as &lt;code&gt;missing&lt;/code&gt; (no metadata)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;openclaw config schema&lt;/code&gt;&lt;/strong&gt; — extracted the full JSON schema to find the exact format for custom model definitions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Found the supported &lt;code&gt;api&lt;/code&gt; protocols&lt;/strong&gt;: &lt;code&gt;google-generative-ai&lt;/code&gt;, &lt;code&gt;openai-completions&lt;/code&gt;, &lt;code&gt;anthropic-messages&lt;/code&gt;, &lt;code&gt;ollama&lt;/code&gt;, and others&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added the config block&lt;/strong&gt;, validated with &lt;code&gt;openclaw config validate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restarted gateway&lt;/strong&gt; and confirmed with a test message&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  About Gemma 4
&lt;/h2&gt;

&lt;p&gt;Gemma 4 is Google's latest open model family with three architectures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Active Params&lt;/th&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Memory (BF16)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 E2B&lt;/td&gt;
&lt;td&gt;Efficient&lt;/td&gt;
&lt;td&gt;2B effective&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;td&gt;9.6 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 E4B&lt;/td&gt;
&lt;td&gt;Efficient&lt;/td&gt;
&lt;td&gt;4B effective&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;td&gt;15 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemma 4 31B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dense&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;256K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;58.3 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemma 4 26B A4B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MoE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;256K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Key capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning&lt;/strong&gt; with configurable thinking modes (OFF/LOW/MEDIUM/HIGH)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal&lt;/strong&gt;: text, image (all models), video &amp;amp; audio (E2B/E4B)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function calling&lt;/strong&gt; for agentic workflows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native system prompt support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache 2.0 license&lt;/strong&gt; — fully open for commercial use&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Free Tier Rate Limits (Gemini API)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;RPM (Requests/min)&lt;/th&gt;
&lt;th&gt;TPM (Tokens/min)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 26B&lt;/td&gt;
&lt;td&gt;3 / 15&lt;/td&gt;
&lt;td&gt;77 / Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 31B&lt;/td&gt;
&lt;td&gt;3 / 15&lt;/td&gt;
&lt;td&gt;15.04K / Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 31B gets significantly more token throughput on free tier. Both work for experimentation and personal assistant use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  The General Pattern: Adding Any Unsupported Model
&lt;/h2&gt;

&lt;p&gt;This same approach works for &lt;strong&gt;any model&lt;/strong&gt; from &lt;strong&gt;any provider&lt;/strong&gt; that OpenClaw doesn't ship in its catalog:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify the API key works&lt;/strong&gt; outside OpenClaw (use curl)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the right &lt;code&gt;api&lt;/code&gt; protocol&lt;/strong&gt; from &lt;code&gt;openclaw config schema&lt;/code&gt; — options include:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;google-generative-ai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openai-completions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openai-responses&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;anthropic-messages&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ollama&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bedrock-converse-stream&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github-copilot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;azure-openai-responses&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add the &lt;code&gt;models.providers.&amp;lt;name&amp;gt;&lt;/code&gt; block&lt;/strong&gt; with &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;contextWindow&lt;/code&gt;, &lt;code&gt;maxTokens&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate and restart&lt;/strong&gt;: &lt;code&gt;openclaw config validate &amp;amp;&amp;amp; openclaw gateway restart&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. Sixty seconds from "unknown model" to a working agent.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.kaggle.com/models?query=gemma-4&amp;amp;publisher=google" rel="noopener noreferrer"&gt;Gemma 4 on Kaggle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/collections/google/gemma-4" rel="noopener noreferrer"&gt;Gemma 4 on Hugging Face&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.google.dev/gemma/docs/core/model_card_4" rel="noopener noreferrer"&gt;Gemma 4 Model Card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openclaw.ai/cli/models" rel="noopener noreferrer"&gt;OpenClaw Models CLI Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.google.dev/gemma/docs/get_started" rel="noopener noreferrer"&gt;Get started with Gemma&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemma</category>
      <category>ai</category>
      <category>google</category>
    </item>
  </channel>
</rss>
