<?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: Adrian Makridenko</title>
    <description>The latest articles on DEV Community by Adrian Makridenko (@mkrdnk).</description>
    <link>https://dev.to/mkrdnk</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%2F4102386%2F4e6f26cd-e357-4e67-b243-a388a103f194.jpg</url>
      <title>DEV Community: Adrian Makridenko</title>
      <link>https://dev.to/mkrdnk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mkrdnk"/>
    <language>en</language>
    <item>
      <title>Building an Internal LLM: vLLM, OpenWebUI, and a Few Hacks</title>
      <dc:creator>Adrian Makridenko</dc:creator>
      <pubDate>Wed, 02 Sep 2026 15:41:47 +0000</pubDate>
      <link>https://dev.to/mkrdnk/building-an-internal-llm-vllm-openwebui-and-a-few-hacks-28f6</link>
      <guid>https://dev.to/mkrdnk/building-an-internal-llm-vllm-openwebui-and-a-few-hacks-28f6</guid>
      <description>&lt;p&gt;It so happened that the company I currently work for wasn't just unfamiliar with AI agents before I joined — most developers had never even seen what all this AI hype was about. Classic enterprise environment.&lt;br&gt;
As a result, I ended up becoming both the initiator and the driving force behind introducing AI into the development process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So I decided to share my experience and show exactly what I did, so you can reproduce it yourself if needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since this was still a heavily regulated enterprise environment, the question of running an LLM locally, inside the company perimeter, came up almost immediately. Claude, Codex, and other cloud-based solutions were completely off-limits.&lt;/p&gt;


&lt;h2&gt;
  
  
  What I Had
&lt;/h2&gt;

&lt;p&gt;After several months of negotiations, I was finally given a machine with an NVIDIA H100, and I started figuring out how all of this worked. My previous experience was limited to running models on a home PC, so at first it seemed like everything would be more or less the same.&lt;br&gt;
It didn't take long to discover that enterprise infrastructure always has a few surprises waiting for you.&lt;br&gt;
My GPU only had 80 GB of VRAM. By modern model standards, that's not a lot, so model selection had to be done carefully.&lt;/p&gt;

&lt;p&gt;I considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek&lt;/li&gt;
&lt;li&gt;Qwen3&lt;/li&gt;
&lt;li&gt;Qwen3.6&lt;/li&gt;
&lt;li&gt;Qwen3-Coder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a few tests, I settled on &lt;code&gt;Qwen/Qwen3-Coder-30B-A3B-Instruct&lt;/code&gt; because &lt;code&gt;deepseek-ai/deepseek-coder-33b-base&lt;/code&gt; (the only reasonably usable DeepSeek model that fit into memory) turned out to be fairly weak, while &lt;code&gt;Qwen3.6&lt;/code&gt; wouldn't start at all because of driver issues.&lt;/p&gt;

&lt;p&gt;More on that in a moment.&lt;/p&gt;


&lt;h2&gt;
  
  
  First vLLM Launch
&lt;/h2&gt;

&lt;p&gt;Like any reasonable person, I started by deploying the model with vLLM and calling it directly through the API.&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;mkdir &lt;/span&gt;vllm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;vllm

pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
pip &lt;span class="nb"&gt;install &lt;/span&gt;uv

uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12 &lt;span class="nt"&gt;--seed&lt;/span&gt; &lt;span class="nt"&gt;--managed-python&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/vllm/.venv/bin/activate

uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"transformers&amp;lt;5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"vllm==0.10.2"&lt;/span&gt;

uv run vllm serve Qwen/Qwen3-Coder-30B-A3B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's where the adventure began.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Enterprise Infrastructure Meets LLMs
&lt;/h2&gt;

&lt;p&gt;Some of you may have noticed that the versions of &lt;code&gt;vllm&lt;/code&gt; and &lt;code&gt;transformers&lt;/code&gt; are far from the latest. That wasn't accidental.&lt;br&gt;
The GPU was provisioned through MIG, and along with it came a whole collection of limitations. The biggest issue was the drivers. I couldn't update them, and due to a combination of architectural constraints and bureaucracy, the administrators couldn't update them either.&lt;br&gt;
That's exactly why I never managed to get Qwen 3.6 running.&lt;/p&gt;

&lt;p&gt;But the fun didn't stop there.&lt;br&gt;
Every night at around 2:00 AM, the MIG instance would disappear from the system for a few seconds and then come back with a new UID. I had no interest in figuring out who was responsible, so I chose the most engineering-oriented solution possible:&lt;br&gt;
Every morning at 5:00 AM, the server simply reboots.&lt;br&gt;
As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a fresh UID appears;&lt;/li&gt;
&lt;li&gt;accumulated issues disappear;&lt;/li&gt;
&lt;li&gt;the system gets a preventive reboot before the workday starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, it's a hack, but it works.&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%2F8z2f4k66hbwpipchci85.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8z2f4k66hbwpipchci85.png" alt="flow-1" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why vLLM Alone Isn't Enough
&lt;/h2&gt;

&lt;p&gt;For several days I used the model on my own without any issues.&lt;br&gt;
It quickly became obvious, however, that this approach only works for a single person. If other employees started using the model, I would need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a web interface;&lt;/li&gt;
&lt;li&gt;user management;&lt;/li&gt;
&lt;li&gt;integration with corporate authentication;&lt;/li&gt;
&lt;li&gt;the ability to revoke access quickly;&lt;/li&gt;
&lt;li&gt;an open-source solution that could be modified internally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a bit of research, I settled on OpenWebUI. It had everything I needed and then some.&lt;/p&gt;
&lt;h2&gt;
  
  
  OpenWebUI as the Entry Point
&lt;/h2&gt;

&lt;p&gt;Initially, OpenWebUI lived on the same machine as vLLM.&lt;br&gt;
Very quickly, though, it became obvious that it was consuming resources I'd rather leave available for the model itself. So I deployed a separate virtual machine.&lt;/p&gt;

&lt;p&gt;The resulting architecture looked like this:&lt;br&gt;
The user interacts with OpenWebUI, and OpenWebUI sends requests to vLLM.&lt;/p&gt;

&lt;p&gt;This turned out to be convenient for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;access can be restricted to specific users or groups;&lt;/li&gt;
&lt;li&gt;usage statistics can be collected;&lt;/li&gt;
&lt;li&gt;response ratings can be gathered;&lt;/li&gt;
&lt;li&gt;system prompts can be managed centrally.&lt;/li&gt;
&lt;/ul&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%2F6ckrrkja0i9ugtk11xjb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ckrrkja0i9ugtk11xjb.png" alt="flow-2" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Squeezing the Most Out of 80 GB of Memory
&lt;/h2&gt;

&lt;p&gt;At this point I started experimenting with model settings.&lt;br&gt;
My first idea was to increase the context window.&lt;br&gt;
I tried this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run vllm serve Qwen/Qwen3-Coder-30B-A3B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-model-len&lt;/span&gt; 120000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It didn't work.&lt;/p&gt;

&lt;p&gt;After a series of experiments, I arrived at the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run vllm serve Qwen/Qwen3-Coder-30B-A3B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 8000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dtype&lt;/span&gt; bfloat16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-model-len&lt;/span&gt; 70000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gpu-memory-utilization&lt;/span&gt; 0.82 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-num-seqs&lt;/span&gt; 8 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-num-batched-tokens&lt;/span&gt; 8192 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-auto-tool-choice&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tool-call-parser&lt;/span&gt; qwen3_coder &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--served-model-name&lt;/span&gt; Qwen-Coder &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-prefix-caching&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-chunked-prefill&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kv-cache-dtype&lt;/span&gt; fp8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gave me roughly 70,000 tokens of context.&lt;br&gt;
Unfortunately, it didn't take long to discover that even that wasn't enough.&lt;/p&gt;


&lt;h2&gt;
  
  
  Hack #1: Rebuilding the Cache After Reboot
&lt;/h2&gt;

&lt;p&gt;The most obvious solution was to use &lt;strong&gt;prefix caching&lt;/strong&gt;. The problem was that every morning the server rebooted, and the entire cache disappeared along with it.&lt;/p&gt;

&lt;p&gt;So another hack was born.&lt;br&gt;
After startup, the system automatically sends a predefined set of popular requests to the model.&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;# -*- coding: utf-8 -*-
# /opt/openwebui/scripts/token_recache_service.py
&lt;/span&gt;
&lt;span class="n"&gt;RECACHE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_recache_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;role&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;system&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RECACHE_PROMPT&lt;/span&gt;&lt;span class="p"&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;role&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;user&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;content&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;warmup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, I'm simply forcing the model to recompute the tokens I want cached.&lt;br&gt;
The list of popular requests was assembled together with an AI agent and continues to grow over time.&lt;/p&gt;

&lt;p&gt;So far, this solution has been sufficient.&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%2Fgqtbjntztyb0cd28ayfd.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgqtbjntztyb0cd28ayfd.png" alt="flow-3" width="799" height="431"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Wrapping Everything Into a Service
&lt;/h2&gt;

&lt;p&gt;From there, it was mostly standard infrastructure work.&lt;/p&gt;

&lt;p&gt;I deployed &lt;strong&gt;PostgreSQL&lt;/strong&gt; instead of &lt;strong&gt;SQLite&lt;/strong&gt;, configured &lt;strong&gt;nginx&lt;/strong&gt;, issued certificates, and locked down access to the &lt;strong&gt;vLLM&lt;/strong&gt; server as much as possible.&lt;/p&gt;

&lt;p&gt;Only &lt;strong&gt;OpenWebUI&lt;/strong&gt; is allowed to communicate with the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:17&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;open-webui-postgres&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;open-webui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/open-webui/open-webui:main&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;open-webui&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately solved several problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, nobody can access the model directly anymore.&lt;/li&gt;
&lt;li&gt;Second, if I need to perform maintenance or testing, I can simply disable the model in OpenWebUI.&lt;/li&gt;
&lt;li&gt;Third, the system is no longer limited to developers using VSCode, Zed, or Opencode. Any employee can use it through a browser.&lt;/li&gt;
&lt;/ul&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%2Ft0p2chle8d4vyfqvqq0x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0p2chle8d4vyfqvqq0x.png" alt="schem-2" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Hack #2: The Model Wants Coffee Too
&lt;/h2&gt;

&lt;p&gt;After some time, I noticed an interesting pattern.&lt;br&gt;
If nobody used the model for a long period, the first few requests in the morning performed noticeably worse. Responses took longer, hallucinations became more frequent, and overall behavior felt strange.&lt;/p&gt;

&lt;p&gt;After several requests, everything returned to normal.&lt;br&gt;
At first I assumed the GPU was simply sitting idle and "cooling down."&lt;br&gt;
I did some searching, found that similar observations weren't unique to me, and wrote a simple script that generated a small amount of continuous GPU load.&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;DEVICE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matmul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synchronize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked. But it felt like I was wasting resources.&lt;br&gt;
Later I replaced it with a different approach.&lt;br&gt;
Now, every few minutes, the system sends a meaningless request to the model:&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;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;URL&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLM_API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;TIMEOUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;

&lt;span class="k"&gt;while&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&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;model&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;qwen&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;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;role&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;user&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;content&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;ping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, the model never stays idle for too long, and the first real user requests tend to perform much more consistently.&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%2Fvcsf6k48pudngxwt6zpi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvcsf6k48pudngxwt6zpi.png" alt="flow-4" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Architecture
&lt;/h2&gt;

&lt;p&gt;As a finishing touch, our DevOps engineer and I configured authentication through FreeIPA, issued an internal certificate, and added the service to the corporate DNS.&lt;/p&gt;

&lt;p&gt;The final architecture ended up looking like this:&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%2F5l41hlb84l6jqz5salse.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5l41hlb84l6jqz5salse.png" alt="final" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The system is now being actively used within the company and is gradually being enhanced with additional features. As a next step, I want to move the project knowledge into a separate RAG service and focus separately on the long-term storage and restoration of caches. And very soon I’ll have a machine running on an H200, where I’ll be testing more sophisticated models.&lt;/p&gt;

&lt;p&gt;But that’s a story for the next article.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Delta by Zed</title>
      <dc:creator>Adrian Makridenko</dc:creator>
      <pubDate>Mon, 31 Aug 2026 09:24:33 +0000</pubDate>
      <link>https://dev.to/mkrdnk/delta-by-zed-35bm</link>
      <guid>https://dev.to/mkrdnk/delta-by-zed-35bm</guid>
      <description>&lt;p&gt;I got into the &lt;a href="https://delta.dev" rel="noopener noreferrer"&gt;Delta&lt;/a&gt; whitelist and spent some time using it for work and not-so-work-related stuff. And I really liked it — this is exactly what I was missing. It’s not some ugly terminal thing written in JS and React, nor a janky stripped-down extension for Zed or VS Code. It’s a &lt;strong&gt;native&lt;/strong&gt;, fast app that does a lot right out of the box.&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%2Fn1cjcatujburgi5k2fha.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1cjcatujburgi5k2fha.png" alt="ui" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it have that Claude Code, Codex, and OpenCode don’t?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First of all, really damn good code review
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzi0fossks2qdif4qlycm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzi0fossks2qdif4qlycm.png" alt="review" width="577" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before this, I used &lt;a href="https://revdiff.com" rel="noopener noreferrer"&gt;revdiff&lt;/a&gt; by &lt;a href="https://github.com/umputun" rel="noopener noreferrer"&gt;Umputun&lt;/a&gt;, but objectively speaking, it’s a pretty hacky workaround. Opening some TUI program through tmux and terminal overlays, only for it to write something like &lt;code&gt;line_number: comment&lt;/code&gt; when you exit — eh, not exactly great.&lt;/p&gt;

&lt;p&gt;Here, you just walk through the code, leave comments, and then send them to the agent in one action. It’s very similar to how we do reviews in GitLab or GitHub. Really damn nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second, worktree-based workflow
&lt;/h3&gt;

&lt;p&gt;The folks at &lt;a href="https://atom-editor.cc/" rel="noopener noreferrer"&gt;Atom&lt;/a&gt; — the same people building Zed and Delta — have been pushing Git worktrees for a long time, going all the way back to the Atom days.&lt;/p&gt;

&lt;p&gt;I like this approach too. Especially in the age of agentic coding, I don’t really want to let an agent loose directly in my working tree. It’s much more convenient to create a worktree from the current state of the project and let the agent have fun there. After the review, the changes can be moved into the main branch.&lt;/p&gt;

&lt;p&gt;That’s exactly what &lt;strong&gt;Delta&lt;/strong&gt; does: each thread gets its own worktree, and all the work happens there. It’s both safer and more convenient, especially when multiple agents are working in parallel.&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%2Fhhrcueckr8agt0njlzb0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhhrcueckr8agt0njlzb0.png" alt="tree-pic" width="515" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Third, a rather niche use case, but...
&lt;/h3&gt;

&lt;p&gt;Collaborative work.&lt;/p&gt;

&lt;p&gt;I can share a session and work on a task together with a colleague in real time. I &lt;strong&gt;really&lt;/strong&gt; like this feature in Zed itself. At work, I occasionally use it to look at code together with colleagues, and it’s much more convenient than sharing your screen during a call and trying to show someone what you’re doing.&lt;/p&gt;

&lt;p&gt;And once coding started turning into chasing AI agents around, it became logical to share not the code itself, but the session: the plans, the reasoning, the same code, and now also the diffs produced by the agent.&lt;/p&gt;

&lt;p&gt;Not many people need this, but personally, I consider it one of the best features in both Zed and Delta.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fourth, NATIVENESS
&lt;/h3&gt;

&lt;p&gt;YES. GOD. YES.&lt;/p&gt;

&lt;p&gt;I am so damn tired of this entire zoo of Electron, JS, and TS. Why? What for?&lt;/p&gt;

&lt;p&gt;(Ironically, the people who created Zed also created Electron for Atom back in the day, and now they’re trying to do everything natively. Making up for their sins 100%.)&lt;/p&gt;

&lt;p&gt;Why, for example, would you write Bitwarden in TypeScript? Both the desktop app and the CLI. Why would you write Claude Code in TypeScript? And React on top of that?&lt;/p&gt;

&lt;p&gt;Electron has generally become a genuine plague of modern desktop software, so the fact that Delta is fast and native is a &lt;strong&gt;huge&lt;/strong&gt; plus.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is it actually missing?
&lt;/h3&gt;

&lt;h4&gt;
  
  
  First and foremost, custom providers.
&lt;/h4&gt;

&lt;p&gt;I really want to connect a work model using our own tokens (mostly so the security people don’t come after me) and work through it. Maybe this isn’t available yet simply because Delta is still in closed beta. But I didn’t see a single word about custom providers on the roadmap.&lt;/p&gt;

&lt;h4&gt;
  
  
  Then, of course, Git integration.
&lt;/h4&gt;

&lt;p&gt;Right now, they suggest moving changes over using &lt;code&gt;stash&lt;/code&gt; or checking out commits from the worktree in your terminal outside of Delta. I’m not really against that, but I’d like to have a slightly more convenient way of doing it. Fortunately, this is already on the roadmap, so they’ll probably improve it.&lt;/p&gt;

&lt;h4&gt;
  
  
  And finally, switching agents.
&lt;/h4&gt;

&lt;p&gt;I’d like to first build a plan with a read-only agent and then hand it over to another agent to actually do the work. That’s not possible yet, but we’ll see how the product evolves.&lt;/p&gt;




&lt;p&gt;At some point while working, I caught myself thinking that I don’t really perceive Delta as yet another tool for working with &lt;strong&gt;AI&lt;/strong&gt; — or even as an “agentic IDE.”&lt;/p&gt;

&lt;p&gt;With the arrival of full-fledged agents in our workflow, the development process itself has changed. Previously, a code editor was primarily where you wrote code yourself. Now, you open it mostly to read the code the agent wrote — and even that happens pretty rarely, because the diffs in Claude Code / Codex are often enough.&lt;/p&gt;

&lt;p&gt;In Delta, all of this is there. You can read the entire project, manually edit the code if you need to, and leave comments for the robot even outside of diffs.&lt;/p&gt;

&lt;p&gt;That’s exactly why Delta feels less like yet another little chat window bolted onto an editor and more like an attempt to build the next generation of code editor.&lt;/p&gt;

&lt;p&gt;Here, the agent isn’t an add-on to the familiar workflow anymore. It becomes one of the core entities of the system, alongside the code (or perhaps even above it), Git, and the developer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
