<?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: OtterCyborg</title>
    <description>The latest articles on DEV Community by OtterCyborg (@ottercyborg).</description>
    <link>https://dev.to/ottercyborg</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%2F585316%2F6d83ab46-c261-41c8-88d5-a51e74d4d21f.jpeg</url>
      <title>DEV Community: OtterCyborg</title>
      <link>https://dev.to/ottercyborg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ottercyborg"/>
    <language>en</language>
    <item>
      <title>I Got Hit by Sha1-Hulud: How I Rebuilt My Development Environment from the Ashes</title>
      <dc:creator>OtterCyborg</dc:creator>
      <pubDate>Thu, 27 Nov 2025 06:40:18 +0000</pubDate>
      <link>https://dev.to/ottercyborg/i-got-hit-by-shai-hulud-how-i-rebuilt-my-development-environment-from-the-ashes-3ac2</link>
      <guid>https://dev.to/ottercyborg/i-got-hit-by-shai-hulud-how-i-rebuilt-my-development-environment-from-the-ashes-3ac2</guid>
      <description>&lt;h2&gt;
  
  
  I Got Hit by Sha1-Hulud: How I Rebuilt My Development Environment from the Ashes
&lt;/h2&gt;

&lt;p&gt;Yesterday, my MacBook started running hot. Not the usual "Chrome has 47 tabs open" kind of hot—the "something is very wrong" kind of hot. I opened Activity Monitor and saw a process called trufflehog consuming 700% CPU.&lt;br&gt;
That's when I knew: I was compromised.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Attack
&lt;/h2&gt;

&lt;p&gt;It started innocently enough. I installed Postman CLI to test some APIs—a completely normal thing developers do every day. What I didn't know was that one of the npm packages in my dependency chain had been poisoned. The malware created a tunnel, exfiltrated my SSH keys, and was actively scanning my machine for secrets.&lt;br&gt;
I had become another victim of &lt;strong&gt;Sha1-Hulud&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Aftermath
&lt;/h2&gt;

&lt;p&gt;I had to revoke everything. Every SSH key. Every API token. Every secret. Then I nuked my Mac entirely and reinstalled the OS fresh.&lt;br&gt;
All of this happened in a single day. Standing in front of a blank machine yesterday evening, I had a choice: set everything up the same way I always had, or fundamentally rethink my approach to development environment security.&lt;br&gt;
I chose the latter. Here's the setup I built in just a few hours.&lt;/p&gt;
&lt;h2&gt;
  
  
  First Principle: No Node.js on the Host
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable truth: running npm install on your host machine is playing Russian roulette. Any package in your dependency tree—or any of &lt;em&gt;their&lt;/em&gt; dependencies—could execute arbitrary code during installation via lifecycle scripts like preinstall and postinstall.&lt;br&gt;
My first rule for the new setup: &lt;strong&gt;No Node.js. No npm. No Bun. Nothing JavaScript on my host environment.&lt;/strong&gt;&lt;br&gt;
This might sound extreme, but consider what you're actually trusting when you run npm install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The package author&lt;/li&gt;
&lt;li&gt;Every maintainer of every transitive dependency&lt;/li&gt;
&lt;li&gt;npm's infrastructure&lt;/li&gt;
&lt;li&gt;Every developer account that has publish access to any of those packages&lt;/li&gt;
&lt;li&gt;The security of every one of their machines and credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⠀That's an impossibly large attack surface.&lt;/p&gt;
&lt;h2&gt;
  
  
  Second Principle: Declarative, Not Imperative
&lt;/h2&gt;

&lt;p&gt;I also removed Homebrew. Before you close this tab in disgust, hear me out.&lt;br&gt;
Homebrew is fantastic for quickly installing tools. But it creates a sprawling web of dependencies in /usr/local that's nearly impossible to audit. After months of use, you have no idea what's actually installed or why.&lt;br&gt;
Instead, I switched to &lt;strong&gt;Nix&lt;/strong&gt; with a declarative configuration. Everything I need is defined in a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kn"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c"&gt;# Container&lt;/span&gt;
    &lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="c"&gt;# Apple Container&lt;/span&gt;

    &lt;span class="c"&gt;# Rust development&lt;/span&gt;
    &lt;span class="nv"&gt;rustc&lt;/span&gt;
    &lt;span class="nv"&gt;cargo&lt;/span&gt;
    &lt;span class="nv"&gt;rustfmt&lt;/span&gt;
    &lt;span class="nv"&gt;rust-analyzer&lt;/span&gt;
    &lt;span class="nv"&gt;clippy&lt;/span&gt;

    &lt;span class="c"&gt;# Python development&lt;/span&gt;
    &lt;span class="nv"&gt;python312&lt;/span&gt;
    &lt;span class="nv"&gt;python312Packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;pip&lt;/span&gt;
    &lt;span class="nv"&gt;python312Packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;virtualenv&lt;/span&gt;

    &lt;span class="c"&gt;# Common development tools&lt;/span&gt;
    &lt;span class="nv"&gt;git&lt;/span&gt;
    &lt;span class="nv"&gt;bat&lt;/span&gt;

    &lt;span class="c"&gt;# Editor&lt;/span&gt;
    &lt;span class="nv"&gt;neovim&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. My entire host environment is defined in roughly 20 lines. If I need to nuke and rebuild my machine again, I can be back to a working environment in minutes, not hours.&lt;br&gt;
Here's what my /usr/local/bin looks like now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls /usr/local/bin
determinate-nixd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One binary (because I installed nix using determinate installer). Compare that to the hundreds of executables Homebrew scatters across your system.&lt;br&gt;
Nix gives me several security advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility&lt;/strong&gt;: My environment is identical every time I build it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Packages are stored in /nix/store with unique hashes based on their entire dependency tree&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditability&lt;/strong&gt;: I can see exactly what's installed by reading one file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy rollback&lt;/strong&gt;: Made a mistake? Roll back to any previous generation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Third Principle: Protect Your SSH Keys
&lt;/h2&gt;

&lt;p&gt;Here's something I didn't fully appreciate before the attack: any program on your machine can read your SSH keys. There's no permission prompt, no confirmation dialog. If malware lands on your system, your keys are immediately compromised.&lt;br&gt;
I now use &lt;strong&gt;Secretive&lt;/strong&gt;, an open-source macOS app that stores SSH keys in the Secure Enclave—the same hardware security module that protects Face ID data. The private key literally cannot be exported; it never exists in accessible memory.&lt;br&gt;
Even better, it requires biometric authentication for every operation. Every git push, every ssh connection requires a Touch ID confirmation. It adds a bit of friction, but that friction is the point. If malware tries to use my keys, I'll know immediately—and I can deny it.&lt;br&gt;
The Secure Enclave approach means even if an attacker gets root access to my machine, they still can't extract my SSH keys. The hardware won't allow it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fourth Principle: Contain the Danger
&lt;/h1&gt;

&lt;p&gt;I still need to work with Node.js projects. The JavaScript ecosystem isn't going anywhere, and many of the tools I use daily are built on it. The solution: &lt;strong&gt;containerization&lt;/strong&gt;.&lt;br&gt;
Apple recently released their own containerization framework, and it's philosophically different from Docker. Instead of running all containers in a single shared Linux VM, Apple's approach spins up a dedicated lightweight VM for each container. This provides hypervisor-level isolation—each container is as isolated as a full virtual machine, but with sub-second startup times.&lt;br&gt;
For my work, I use a hybrid setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web applications run entirely inside containers (npm is trapped there forever)&lt;/li&gt;
&lt;li&gt;For Tauri apps, the web portion is containerized and served over the network&lt;/li&gt;
&lt;li&gt;The native shell runs on my host, connecting to the containerized web server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup means JavaScript code never runs on my host machine. Even if a malicious npm package executes, it's sandboxed inside a container with no access to my SSH keys, no access to my credentials, nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Workflow
&lt;/h2&gt;

&lt;p&gt;Here's what development looks like now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone a repo&lt;/strong&gt;: Git prompts for Touch ID to use my Secure Enclave-protected key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start the dev environment&lt;/strong&gt;: Apple Container spins up an isolated Linux VM in under a second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install dependencies&lt;/strong&gt;: npm install runs inside the container, isolated from everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push changes&lt;/strong&gt;: Another Touch ID confirmation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Is it more friction? Yes, marginally. Is it more secure? Absolutely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lessons Learned
&lt;/h1&gt;

&lt;p&gt;The Sha1-Hulud attack taught me something uncomfortable: the convenience-first approach to development tooling is a liability. We've built an ecosystem where running a single command can execute arbitrary code from thousands of strangers.&lt;br&gt;
Some might argue this is paranoid. To them, I'd point out that supply chain attacks are increasing in sophistication and frequency. The npm ecosystem specifically has been hit repeatedly—Sha1-Hulud is just the latest and most severe example.&lt;br&gt;
Here's my advice for anyone who wants to take security more seriously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assume npm packages can and will be compromised&lt;/strong&gt;. Treat npm install as a potentially hostile operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run JavaScript in containers&lt;/strong&gt;. It's the only way to meaningfully limit the blast radius.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect your SSH keys with hardware&lt;/strong&gt;. Secretive (or a YubiKey) makes key theft orders of magnitude harder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make your environment declarative and reproducible&lt;/strong&gt;. You should be able to rebuild from scratch quickly, because someday you might have to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accept the friction&lt;/strong&gt;. Security that's easily bypassed isn't security.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I spent a few hours setting all this up yesterday. The next person hit by a supply chain attack will spend days—or weeks—on incident response and recovery. The math works out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Forward
&lt;/h2&gt;

&lt;p&gt;Eventually, I want to get even more aggressive. Running Python directly on my host still makes me nervous; pip packages have their own security issues. The ideal end state might be a completely containerized development environment where only Nix-managed tools run on the host.&lt;br&gt;
But for now, this setup represents a reasonable balance. I can still be productive while significantly reducing my attack surface.&lt;br&gt;
The sandworm got me once. It won't get me again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you were affected by Sha1-Hulud, the key remediation steps are: revoke and regenerate all npm tokens, GitHub PATs, SSH keys, and cloud credentials. Search for repositories named "Sha1-Hulud" created under your accounts. Review for unauthorized workflows or suspicious commits. And consider whether your development environment security model is actually working for you.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>development</category>
    </item>
    <item>
      <title>Neovim: My Setup for Developer Assistant with LLMs</title>
      <dc:creator>OtterCyborg</dc:creator>
      <pubDate>Sun, 11 Aug 2024 12:28:26 +0000</pubDate>
      <link>https://dev.to/ottercyborg/neovim-my-setup-for-developer-assistant-with-local-language-models-2nim</link>
      <guid>https://dev.to/ottercyborg/neovim-my-setup-for-developer-assistant-with-local-language-models-2nim</guid>
      <description>&lt;p&gt;I've been using VS Code for years, it's awesome. I thought I would never leave it until the end of my career. But everything changed since the day my colleague presented his awesome Neovim setup. It's pretty cool, but tough for me, a newbie in the &lt;code&gt;Keyboard does everything&lt;/code&gt; space.&lt;/p&gt;

&lt;p&gt;I spent my whole weekend setting up my own Neovim IDE. It's even worse since there are so many outdated configs that couldn't run on my potato machine. But that's another story, in this post, I'll just share something I couldn't miss in an IDE in this Age of AI: "Copilot".&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;There are many options out there. In this post, I will share my current setup with &lt;a href="https://github.com/David-Kunz/gen.nvim" rel="noopener noreferrer"&gt;https://github.com/David-Kunz/gen.nvim&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  gen.nvim
&lt;/h3&gt;

&lt;p&gt;Generate text using LLMs with customizable prompts&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FDavid-Kunz%2Fgen.nvim%2Fassets%2F1009936%2F79f17157-9327-484a-811b-2d71ceb8fbe3" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FDavid-Kunz%2Fgen.nvim%2Fassets%2F1009936%2F79f17157-9327-484a-811b-2d71ceb8fbe3" alt="gen_nvim"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Video
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://youtu.be/FIZt7MinpMY?si=KChSuJJDyrcTdYiM" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1009936%2F273126287-7b5f2b40-c678-47c5-8f21-edf9516f6034.jpg" alt="Local LLMs in Neovim: gen.nvim"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Requires
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ollama.ai/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; with an appropriate model, e.g. &lt;a href="https://ollama.com/library/llama3.1" rel="noopener noreferrer"&gt;&lt;code&gt;llama3.1&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://ollama.ai/library/mistral" rel="noopener noreferrer"&gt;&lt;code&gt;mistral&lt;/code&gt;&lt;/a&gt;, etc. (Simple setup)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/janhq/cortex.git" rel="noopener noreferrer"&gt;Cortex&lt;/a&gt; for more advanced setup, e.g., switching models between different engines &amp;amp; providers such as Llama.cpp, OpenAI, and Claude.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;Curl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;First thing first, let's install the plugin: &lt;/p&gt;

&lt;p&gt;Example with Lazy&lt;/p&gt;

&lt;p&gt;-- Minimal configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;"David-Kunz/gen.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&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="s2"&gt;"mistral"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- The default model to use.&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;Voila! You now have the code gen plugin installed. You can now run the &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; Mistral model on your machine to see how it works.&lt;/p&gt;

&lt;p&gt;Select a block of code that you want to ask, then do &lt;code&gt;:Gen&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flrsqylfv70pq8fb2qpwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flrsqylfv70pq8fb2qpwh.png" alt="Prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generate text using LLMs with customizable prompts&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FDavid-Kunz%2Fgen.nvim%2Fassets%2F1009936%2F79f17157-9327-484a-811b-2d71ceb8fbe3" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FDavid-Kunz%2Fgen.nvim%2Fassets%2F1009936%2F79f17157-9327-484a-811b-2d71ceb8fbe3" alt="gen_nvim"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The author also published a YouTube video that explains it better, please check it out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Let's do a more advanced setup.
&lt;/h2&gt;

&lt;p&gt;It's cool, right? But that's not my current productive setup. Sometimes my low-spec machine does not have enough VRAM to run bigger models, so I have to switch to remote endpoints.&lt;/p&gt;

&lt;p&gt;Let's customize the plugin configuration to connect to other endpoints, such as OpenAI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;"David-Kunz/gen.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&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="s2"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- The default model to use.&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"api.openai.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"443"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"curl --silent --no-buffer -X POST https://"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/v1/chat/completions -d $body"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;" -H 'Content-Type: application/json'"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;" -H 'Authorization: Bearer sk-xxxx'"&lt;/span&gt; &lt;span class="c1"&gt;-- OpenAI API Key&lt;/span&gt;
    &lt;span class="k"&gt;end&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;Let's try chatting with the model again and see how it works. This prompt is just to ensure we are making requests to the OpenAI endpoint instead of Ollama.&lt;br&gt;
Prompt:&lt;br&gt;
&lt;a href="https://media.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%2F2123028iap5xbzcf5rwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F2123028iap5xbzcf5rwq.png" alt="OpenAI-Prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;a href="https://media.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%2F0pa2v70q40lj5ym1gmyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0pa2v70q40lj5ym1gmyy.png" alt="OpenAI-Result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All good.&lt;/p&gt;
&lt;h2&gt;
  
  
  Switching between configurations is tough though...
&lt;/h2&gt;

&lt;p&gt;Sometimes I like to try out new models. They can be run locally or just remotely. How can I switch between those options without reconfiguring everything again and again?&lt;/p&gt;

&lt;p&gt;This is the one you are looking for. &lt;a href="https://github.com/janhq/cortex" rel="noopener noreferrer"&gt;https://github.com/janhq/cortex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I consider this a very good candidate for my use cases, since it supports multiple engines, both local and remote.&lt;/p&gt;

&lt;p&gt;Now for the installation, I chose the npm option since I'm more familiar with the JS ecosystem. (It turned out that I can install this package inside my JS applications as well.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; cortexso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run the server! 🚀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cortex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.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%2Foz2o44jxh4h2kwboaoe6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Foz2o44jxh4h2kwboaoe6.png" alt="Run Cortex"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The server is ready! Now pull any models from the Cortex &lt;a href="https://huggingface.co/cortex" rel="noopener noreferrer"&gt;Hugging Face Repository&lt;/a&gt;. There are so many models that have been republished, as well as remote providers that you will want to try out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cortex pull llama3.1
cortex pull gpt-4o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can also pull any of your favorite HuggingFace models. E.g. &lt;code&gt;cortex pull google/gemma-7b-GGUF&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Voila, now I have two models pulled.&lt;br&gt;
&lt;a href="https://media.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%2Fpek2kjcxrbm36j2yo37s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpek2kjcxrbm36j2yo37s.png" alt="Model List"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, isn't OpenAI's models required to run with an API Key? Here is how to set it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cortex engines openai set apiKey sk-your_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You forgot something, huh? We need to run the models the same way Ollama does.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cortex run llama3.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Only local models require this run step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Time to update our gen.nvim configurations 🚀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;"David-Kunz/gen.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&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="s2"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- The default model to use.&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- The cortex server host address.&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1337"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- The cortex server port number.&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"curl --silent --no-buffer -X POST http://"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/v1/chat/completions -d $body"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;" -H 'Content-Type: application/json'"&lt;/span&gt;
    &lt;span class="k"&gt;end&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;Let's chat with our new friend ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fahbu22ve0vo8v8q3en94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fahbu22ve0vo8v8q3en94.png" alt="llama3.1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hold on, how to switch between models? 🤔
&lt;/h2&gt;

&lt;p&gt;How do you switch between models from different engines that are installed?&lt;/p&gt;

&lt;p&gt;gen.nvim supports a model selection function to switch between models. Let's map a key for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;A-m&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"gen"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;select_model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Select gen.nvim model"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not work yet, since it makes requests to Ollama by default. Now we customize it to fetch models from the Cortex server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="s2"&gt;"David-Kunz/gen.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&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="s2"&gt;"llama3.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1337"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"curl --silent --no-buffer -X POST http://"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/v1/chat/completions -d $body"&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;" -H 'Content-Type: application/json'"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;list_models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"curl --silent --no-buffer http://"&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/v1/models"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="nb"&gt;table.sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
    &lt;span class="k"&gt;end&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;Let's try &lt;code&gt;Alt + m&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fyd66lq34auf1vdmhjkt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyd66lq34auf1vdmhjkt6.png" alt="Model Selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🤘🤘🤘 Cool, right? Seamlessly switching between models, from local to remote models and between different providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup allows you to leverage both local and cloud-based AI models, enhancing your coding workflow with intelligent assistance.&lt;br&gt;
As you become more comfortable with this setup, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating custom prompts for your specific development needs&lt;/li&gt;
&lt;li&gt;Exploring different models to find the best fit for various tasks&lt;/li&gt;
&lt;li&gt;All of the linked projects are open-source, please don't forget to drop a star and help contribute.
Please try different models, create custom prompts, and adapt the configuration to your unique workflow. Happy coding!&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
