<?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: Chris Kesler</title>
    <description>The latest articles on DEV Community by Chris Kesler (@chris_kesler_8a60b6e38dd8).</description>
    <link>https://dev.to/chris_kesler_8a60b6e38dd8</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3813304%2F7742d1c3-7a4b-4616-8282-c6be42e3e596.jpg</url>
      <title>DEV Community: Chris Kesler</title>
      <link>https://dev.to/chris_kesler_8a60b6e38dd8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chris_kesler_8a60b6e38dd8"/>
    <language>en</language>
    <item>
      <title>OpenClaw Model Manager: A GUI for the Power Users Who Hate Waiting</title>
      <dc:creator>Chris Kesler</dc:creator>
      <pubDate>Sun, 08 Mar 2026 21:13:54 +0000</pubDate>
      <link>https://dev.to/chris_kesler_8a60b6e38dd8/openclaw-model-manager-a-gui-for-the-power-users-who-hate-waiting-2c55</link>
      <guid>https://dev.to/chris_kesler_8a60b6e38dd8/openclaw-model-manager-a-gui-for-the-power-users-who-hate-waiting-2c55</guid>
      <description>&lt;p&gt;&lt;em&gt;How I built a standalone web dashboard to tame OpenClaw's CLI—and what it taught me about AI infrastructure in the real world.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;OpenClaw is genuinely powerful. It runs a local AI gateway that routes your conversations through any combination of models—Anthropic, OpenRouter, Google, local Ollama models—with fallback chains, auth profiles, aliases, and session management baked right in. Once it's configured, it mostly just works.&lt;/p&gt;

&lt;p&gt;But "mostly just works" hides a lot of friction.&lt;/p&gt;

&lt;p&gt;Want to know if your gateway is running? &lt;code&gt;openclaw gateway status&lt;/code&gt;. Want to change your primary model? Edit a JSON config file, then restart the gateway. Want to see which provider is in a rate-limit cooldown? Good luck—dig through &lt;code&gt;auth-profiles.json&lt;/code&gt; manually. Want to check if your dual RTX 3060s can actually run that 34B parameter model? Open a calculator.&lt;/p&gt;

&lt;p&gt;The tools are all there. They're just scattered, CLI-only, and invisible when you need them most.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;strong&gt;OpenClaw Model Manager&lt;/strong&gt;.&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%2F87sxohq1w3nt4pjsfa6l.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%2F87sxohq1w3nt4pjsfa6l.png" alt=" " width="800" height="650"&gt;&lt;/a&gt;&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%2Fq6ggx6une5gx3g6pmh1u.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%2Fq6ggx6une5gx3g6pmh1u.png" alt=" " width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&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%2Ffr1rwvpj3anabtsto03l.png" alt=" " width="800" height="428"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What It Is
&lt;/h2&gt;

&lt;p&gt;OpenClaw Model Manager is a standalone web dashboard that wraps OpenClaw's existing CLI and config files with a clean, dark-themed UI. &lt;/p&gt;

&lt;p&gt;It runs as its own Express server on port &lt;code&gt;18800&lt;/code&gt;—completely independent of the OpenClaw gateway itself. That's intentional: &lt;strong&gt;the manager doesn't go down when the gateway does&lt;/strong&gt;, and it's exactly how you bring the gateway back up when it crashes. It's not a replacement for OpenClaw; it's a control panel for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No build step. No framework. No dependencies beyond Express and &lt;code&gt;ws&lt;/code&gt;.&lt;/strong&gt; Just static HTML, CSS, and JavaScript backed by a thin Node server that shells out to the same &lt;code&gt;openclaw&lt;/code&gt; CLI you'd use in your terminal. Bind it to &lt;code&gt;0.0.0.0:18800&lt;/code&gt; and it's accessible over Tailscale or your local network from any device—your phone, a laptop on the couch, or a remote machine across the country.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Features (And Why They Matter)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚨 Provider Failover Panel
&lt;/h3&gt;

&lt;p&gt;This one was born from real pain. &lt;/p&gt;

&lt;p&gt;During development, switching primary models repeatedly triggered Anthropic's rate limiter. The gateway put Anthropic on a 5-minute cooldown—but the first fallback in my chain was &lt;em&gt;also&lt;/em&gt; an Anthropic model. So both were blocked. The error messages were confusing, the fix required editing a JSON file, and there was no visibility into what was happening.&lt;/p&gt;

&lt;p&gt;The Failover Panel solves this. It lives at the top of the Health tab so you see it immediately when something's wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red borders&lt;/strong&gt; appear the moment any provider enters cooldown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Countdown timers&lt;/strong&gt; show exactly how long until each provider recovers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Switch To ⚡"&lt;/strong&gt; button hot-swaps your primary model to any ready provider &lt;em&gt;instantly&lt;/em&gt;—no gateway restart, no JSON editing, no waiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Clear Cooldown"&lt;/strong&gt; resets a provider's error state immediately if you know the rate limit has lifted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; When you're in the middle of a workflow and your primary model goes down, you need one click to fix it—not a terminal and a config file.&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%2Fuvgavrw2kwm96dm75q21.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%2Fuvgavrw2kwm96dm75q21.png" alt=" " width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Live System Health
&lt;/h3&gt;

&lt;p&gt;The Health tab shows a plain-English summary of what's happening on your machine right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GPU VRAM bars:&lt;/strong&gt; Utilization percentage and temperature for each card, refreshed every 3 seconds via &lt;code&gt;nvidia-smi&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM usage:&lt;/strong&gt; Total and available memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model offload detection:&lt;/strong&gt; Plainly shows whether your current model is running entirely on GPU, split across GPU and CPU, or running in CPU-only mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🦙 Local Model Compatibility &amp;amp; The "12GB Trap"
&lt;/h3&gt;

&lt;p&gt;The Local Models tab connects to your Ollama instance and lists every model you have installed, alongside an honest assessment of whether your hardware can run it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Fits on GPU:&lt;/strong&gt; Full VRAM available; runs fast.&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Partial GPU:&lt;/strong&gt; Model is larger than available VRAM; will split to system RAM (slower).&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;CPU only:&lt;/strong&gt; Too large for GPU; will be incredibly slow.&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Won't fit in RAM:&lt;/strong&gt; Model exceeds your total system memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The Math:&lt;/em&gt; VRAM requirements are estimated as &lt;code&gt;Model Size × 1.2&lt;/code&gt; (accounting for a 20% overhead for the KV cache at short contexts). For a 24GB AI Lab (like my 2× RTX 3060 setup), this gives you a realistic picture of which 7B, 13B, and 34B models are actually usable day-to-day before you even try to load them.&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%2Ffwtwa42gzq51nj4ljc07.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%2Ffwtwa42gzq51nj4ljc07.png" alt=" " width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔗 Drag-and-Drop Fallback Chains
&lt;/h3&gt;

&lt;p&gt;Your fallback chain is the safety net that keeps conversations going when your primary model fails. OpenClaw tries each model in order until one works. &lt;/p&gt;

&lt;p&gt;The Fallbacks tab lets you manage it visually. Drag tiles to reorder them, see your balance of cloud vs. local models at a glance, and hit save. It writes directly to your &lt;code&gt;openclaw.json&lt;/code&gt; config.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; The right fallback order is the difference between a 500ms retry to OpenRouter and a 25-minute wait for Anthropic to recover.&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%2F3fe5zdihawuvh5z2mbq7.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%2F3fe5zdihawuvh5z2mbq7.png" alt=" " width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Remote Connection Manager
&lt;/h3&gt;

&lt;p&gt;If you run OpenClaw on more than one machine, the Connection Manager lets you add any instance—local or remote—and switch between them with a single dropdown. Switch connections over Tailscale, and every tab immediately updates to show data for that specific remote instance.&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%2Fxt2ds352uouall1jlnus.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%2Fxt2ds352uouall1jlnus.png" alt=" " width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture is built for resilience. The server stays alive independent of the gateway. Local operations shell out to the &lt;code&gt;openclaw&lt;/code&gt; CLI, while remote operations hit the remote Model Manager's HTTP API. No custom protocol, no agents, no magic—just HTTP all the way down.&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%2Fzd53la2ubbd2grp5pku7.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%2Fzd53la2ubbd2grp5pku7.png" alt=" " width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you're running multiple OpenClaw instances, dealing with provider rate limits, or trying to figure out which Ollama models will actually run on your GPUs, this dashboard is for you. &lt;/p&gt;

&lt;p&gt;It's free, it's open source, and it took one morning to build from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="o"&gt;[&lt;/span&gt;https://github.com/chriskesler35/openclaw-model-manager]&lt;span class="o"&gt;(&lt;/span&gt;https://github.com/chriskesler35/openclaw-model-manager&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;openclaw-model-manager
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
Open http://localhost:18800 &lt;span class="k"&gt;in &lt;/span&gt;your browser.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Built with Express, WebSockets, and stubbornness. Dark mode only.)&lt;/p&gt;

&lt;p&gt;Check out the full repository here: github.com/chriskesler35/openclaw-model-manager&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The 24GB AI Lab: A Survival Guide to Full-Stack Local AI on Consumer Hardware</title>
      <dc:creator>Chris Kesler</dc:creator>
      <pubDate>Sun, 08 Mar 2026 19:08:42 +0000</pubDate>
      <link>https://dev.to/chris_kesler_8a60b6e38dd8/the-24gb-ai-lab-a-survival-guide-to-full-stack-local-ai-on-consumer-hardware-931</link>
      <guid>https://dev.to/chris_kesler_8a60b6e38dd8/the-24gb-ai-lab-a-survival-guide-to-full-stack-local-ai-on-consumer-hardware-931</guid>
      <description>&lt;p&gt;We’ve all been there: You see a viral post about a new AI model, you try to run a fine-tune locally, and your terminal rewards you with a wall of red text and a &lt;code&gt;CUDA Out of Memory&lt;/code&gt; error. &lt;/p&gt;

&lt;p&gt;If you’re running a mid-range, multi-GPU setup—specifically a dual-GPU rig like the NVIDIA RTX 3060 (12GB each)—you aren't just a hobbyist; you’re an orchestrator. You have 24GB of total VRAM, but because it’s physically split across two cards, the default settings of almost every AI tool will crash your system.&lt;/p&gt;

&lt;p&gt;After months of trial and error in a Dockerized Windows environment, I’ve developed a "Zero-Crash Pipeline." This is the exact blueprint for taking a model from a raw fine-tune in Unsloth to an agentic reality using Ollama, OpenClaw, and ComfyUI.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Foundation: Docker &amp;amp; The "Windows Handshake"
&lt;/h3&gt;

&lt;p&gt;Running your ML environment in Docker (using the Unsloth image) keeps your Windows host clean, but Docker needs strict instructions on how to handle memory across two GPUs. &lt;/p&gt;

&lt;p&gt;Before you even load a model, you must inject these two settings into your Python script. These are the guardrails that prevent 3:00 AM crashes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Memory Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PYTORCH_CUDA_ALLOC_CONF&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expandable_segments:True&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, PyTorch hogs rigid blocks of VRAM. This setting treats your VRAM as a dynamic pool, allowing it to grow and shrink as needed. This simple change eliminates the common VRAM fragmentation error that frequently crashes a 12GB card halfway through a training run.&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%2F5ih7oklq2dwaquy30uq5.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%2F5ih7oklq2dwaquy30uq5.png" alt="The VRAM Allocation Comparison" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Multi-GPU Bug Fix: If you use two GPUs, the system tries to do math across both cards simultaneously. To prevent the training script from throwing a cryptic 'int object has no attribute mean' error, you must explicitly tell TrainingArguments to stop token averaging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;average_tokens_across_devices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. The Training Phase: "The Rule of 1024"
&lt;/h3&gt;

&lt;p&gt;You might want a model that can read a whole novel at once, but consumer hardware requires strict budget discipline.&lt;/p&gt;

&lt;p&gt;Context Limit: Set max_seq_length = 1024. This is the stability sweet spot for 24GB of combined VRAM. It provides significant "headroom" for the OS and Docker overhead during peaks.&lt;/p&gt;

&lt;p&gt;Batch Discipline: Keep per_device_train_batch_size = 1.&lt;/p&gt;

&lt;p&gt;The Secret Sauce: Set gradient_accumulation_steps = 8. Instead of trying to process 8 items at once (which instantly spikes VRAM), the model processes 1 item, 8 times, and then updates itself. It’s the same mathematical result with a fraction of the memory pressure.&lt;/p&gt;

&lt;p&gt;The Missing Link: Always import and use DataCollatorForLanguageModeling. Many tutorials skip this, but without it, your dual-GPU setup will throw dimension mismatch errors when trying to batch text dynamically.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The "Merge" (The Most Dangerous Step)
&lt;/h3&gt;

&lt;p&gt;You’ve finished training your LoRA. Now you need to bake those new learnings back into the main base model.&lt;/p&gt;

&lt;p&gt;The Step Everyone Skips: If you run a standard merge, PyTorch will try to load the base model and the LoRA into your VRAM simultaneously. On 12GB cards, this is an instant system freeze. You must force the computer to use your System RAM (CPU offloading) for the heavy lifting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The VRAM Insurance Policy
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_merged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;save_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merged_4bit_forced&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Best format for Ollama
&lt;/span&gt;    &lt;span class="n"&gt;maximum_memory_usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# Forces CPU offloading
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Context: This limits the merge process to 40% of your VRAM, pushing the rest of the workload to your system memory. It takes a few minutes longer, but it works 100% of the time.&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%2Fngm683va1da5jzj0nilx.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%2Fngm683va1da5jzj0nilx.png" alt="The CPU Offloading Strategy" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "Sanitization" Script (The Final Polish)
&lt;/h3&gt;

&lt;p&gt;You have your output file (often a .gguf or .safetensors), but when you try to load it into Ollama, it rejects it with an unexpected EOF or invalid format error.&lt;/p&gt;

&lt;p&gt;Why? Because the PyTorch export process often leaves behind non-standard metadata (U8/U9 headers)—essentially digital junk mail that confuses the local inference engine.&lt;/p&gt;

&lt;p&gt;The Fix: A quick Python "Washing Script." Run this utility over your output directory to strip the headers before creating your Ollama Modelfile.&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%2F8smem96728ri6bd74eay.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%2F8smem96728ri6bd74eay.png" alt="The Metadata " width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;safetensors.torch&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_file&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sanitize_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.safetensors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tensors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Save it back with an explicitly empty metadata dictionary
&lt;/span&gt;            &lt;span class="n"&gt;save_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tensors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sanitized: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Point these to your Docker volume mounts
&lt;/span&gt;&lt;span class="nf"&gt;sanitize_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/workspace/work/model_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/workspace/work/sanitized_model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fah0uow8sm6j5ysvdt4wg.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%2Fah0uow8sm6j5ysvdt4wg.png" alt="The Agentic Loop" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Deployment: From Model to Agent
&lt;/h3&gt;

&lt;p&gt;With your "washed" model successfully running in Ollama, the loop is closed. Because the model is optimized for your hardware's strict 1024 context window, the latency is near-zero.&lt;/p&gt;

&lt;p&gt;You can now point OpenClaw's local API setting directly to your Ollama localhost. OpenClaw handles the logic and tool-calling, and when a visual task is required, it triggers your local ComfyUI instance.&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%2Fghs7k1stqtl8azaf05je.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%2Fghs7k1stqtl8azaf05je.png" alt="The Unified Pipeline" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Appendix: The Dual-GPU Troubleshooting Matrix
&lt;/h3&gt;

&lt;p&gt;If you are running a multi-GPU Docker setup, you will likely encounter these three "Gatekeeper" errors. Use these verified configurations to bypass them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error Message / Symptom&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;The "Hardware-Aware" Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;CUDA Out of Memory (OOM)&lt;/code&gt;&lt;/strong&gt; during long training runs.&lt;/td&gt;
&lt;td&gt;VRAM fragmentation within the Docker container.&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"&lt;/code&gt; before initializing the model.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;AttributeError: 'int' object has no attribute 'mean'&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-GPU synchronization conflict in Unsloth/HuggingFace.&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;average_tokens_across_devices=False&lt;/code&gt; in your &lt;code&gt;TrainingArguments&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Ollama create: unexpected EOF&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;Tensor not found&lt;/code&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Unsanitized U8/U9 metadata headers in the Safetensors file.&lt;/td&gt;
&lt;td&gt;Run the "Header Stripper" Python script to load and re-save the weights with an empty metadata dictionary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;System Freeze&lt;/strong&gt; during the &lt;code&gt;save_pretrained_merged&lt;/code&gt; step.&lt;/td&gt;
&lt;td&gt;Attempting to load the base model and LoRA into VRAM simultaneously.&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;maximum_memory_usage=0.4&lt;/code&gt; and &lt;code&gt;save_method="merged_4bit_forced"&lt;/code&gt; to force CPU offloading.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Building local AI on a multi-GPU rig isn't about having the fastest hardware; it's about being the best mechanic. By controlling your memory allocation, capping your context, and "washing" your metadata, you can turn consumer graphics cards into a highly capable, private, agentic laboratory.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
