Ollie.nvim is an open-source, AI self-host backend Neovim plugin that integrates large language models into the editor through a modular routing and provider system.
It supports both local inference engines and remote APIs, enabling privacy-first and extensible AI workflows.
The architecture separates UI, request handling, and model providers, allowing developers to extend or replace backend engines without modifying core logic.
- Structured handlers — Discrete commands for explanation, debugging, fixing, and many more coming soon. Each handler owns its context and prompt logic independently.
- Streaming responses — Streaming incremental responses. No waiting for full completions.
- Async job architecture — All I/O runs off the main thread via Neovim's
vim.loop/vim.jobstart. The editor never blocks. - Buffer context management — Smart context assembly from the active buffer, visual selection, LSP diagnostics, and file metadata.
- Health system — Built-in
:checkhealth olliecovering hardware capability, internet reachability, model availability, and Ollama process status. - Security layer — Permission model, policy enforcement, and trust management for sensitive operations.
- Selector abstraction — Picker-agnostic model and provider selection. Works with
telescope.nvim,fzf-lua,vim.ui.select, or custom frontends. - Multi-provider support — You can add providers such as Anthropic (Claude API), OpenAI (Cloud API), Google (Cloud), Ollama (local inference). Switch providers per task. For building multi-provider support, check #Providers on doucmentation.md
┌─────────────────────────────────────────────┐
│ UI Layer │
├─────────────────────────────────────────────┤
│ Command Layer │
├─────────────────────────────────────────────┤
│ Handler Layer │
├─────────────────────────────────────────────┤
| Core router |
├─────────────────────────────────────────────┤
│ Provider Layer │
├─────────────────────────────────────────────┤
| Stream Engine |
├─────────────────────────────────────────────┤
│ Core System |
│ ┌──────────────────┬──┬──────────────────┐ │
│ │ Context Manager | │ Session Manager │ │
│ │ - buffer context | │ - chat history │ │
│ │ - selection | │ - persistence │ │
│ └──────────────────┴──┴──────────────────┘ │
├─────────────────────────────────────────────┤
│ Async Job Layer │
└─────────────────────────────────────────────┘
Default model is avexcoder_3b:latest from ollama alias. It is designed around a clean separation of concerns: providers handle model communication, security, permissions, handlers define task semantics, and the UI layer stays thin and replaceable. The goal is a plugin you can trust, extend, and run entirely on low-end till high-end devices. Its router dispatches requests asynchronously across providers and experimental hardware-aware routing — so you run the best model your environment can actually support.
NOTE: Run pkill ollama after exiting neovim.
ollie.nvim
├── providers/ # API clients for each LLM backend (cloud, local)
├── handler/ # Task logic: explain, and fix
├── core/ # Execution backend
├── parser/ # Response formatter
├── ui/ # Panel and floating window rendering
├── system/ # Boundaries and hardware-layer health verification
├── commands/ # User-facing command definitions
Design principles:
- Providers are stateless adapters. They translate requests to API shapes and return a stream or a string. They know nothing about Neovim buffers.
- Handlers are stateless functions. They assemble context, call a provider via the router, and write output. They know nothing about which provider is active.
- The router is the only component that knows both sides. Swapping a provider never touches handler code.
- The UI layer is purely presentational. It receives text and renders it. No business logic lives there.
- The health system is a first-class citizen, not an afterthought. If something is misconfigured,
:checkhealthshould tell you exactly what and why.
Requirements:
- Neovim >= 0.10
- curl (for HTTP providers)
lazy.nvim
return {
"avexcz/ollie.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
-- optional, for selector layer
"nvim-telescope/telescope.nvim",
},
opts = {},
}packer.nvim
return {
"avexcz/ollie.nvim",
requires = { "nvim-lua/plenary.nvim" },
config = function()
require("ollie").setup({})
end
} return {
"avexcz/ollie.nvim",
lazy = false,
event = "VimEnter",
dependencies = { "nvim-lua/plenary.nvim" },
config = function()
require("ollie").setup({
default_provider = "ollama",
default_model = "qwen2.5-coder:1.5b",
streaming = true,
})
end
}Requirements:
- Ollama installed
- Terminal
Ollie works with any Ollama-compatible model. If you don't have any model installed. Here is the setup.
Install Qwen 2.5 Coder 1.5B or 0.5b:
ollama pull qwen2.5-coder:1.5bAfter installation, make sure Ollama is running:
ollama serveYou can verify that the model is available:
ollama listThen configure Ollie in your init.lua:
require("ollie").setup({
default_provider = "ollama",
default_model = "qwen2.5-coder:1.5b",
streaming = true,
})If you prefer to use custom model instead, for better coding performance. Make sure models are installed on your local machine. For example, here are practical steps to be done.
Install any model:
ollama pull qwen2.5-coder:3bCreate the Ollie model:
git clone https://github.com/avexcz/ollie.nvim
cd ollie.nvim/Modelfile
ollama create avexcoder_3b -f usernamecoder_3bor simpler:
touch Modelfile
Configure your modelfile:
FROM qwen2.5-coder:3b
PARAMETER temperature 1
PARAMETER num_ctx 8192
SYSTEM """
You are Ollie, an AI coding assistant for Neovim.
<your idea here>
"""
Verify the installation and custom modelfile:
ollama listYou should see usernamecoder_3b:latest.
Then ollie configuration setup would be:
require("ollie").setup({
default_provider = "ollama",
default_model = "usernamecoder_3b", -- changeable
streaming = true,
})Or test/temporary run:
:OllieModel avexcoder_3b:latest
For more info check:
| Command | Description |
|---|---|
Ollie |
Open the dashboard |
:OllieChat |
Open the chat interface with the active provider and model |
:OllieChatContext |
Send the whole buffer and open the chat interface with the active provider and model |
:OllieFix |
Diagnose and apply a fix for the current error via selection |
OllieExplain |
Discuss the matter and provide details based on user query via selection |
:OllieModel |
Switch the active model via selector |
:OllieProvider |
Switch the active provider via selector |
:OllieSessions |
Browse and resume previous sessions |
OllieSessionDelete |
Delete single conversation history |
OllieSessionClear |
Clear entire conversation history |
:OllieHealth |
Validate providers, credentials, hardware health, recommanded model and dependencies |
If struggling with long commands, try setting up keymaps.
local input = require("ollie.ui.behaviour.input")
local selection = require("ollie.content.selection")
local ollie_fix = require("ollie.handler.fix.init")
local ollie_explain = require("ollie.handler.explain.init")
keymap.set("v", "<leader>o", "<cmd>Ollie<cr>", { desc = "Ollie"}) -- dashboard
keymap.set("v", "<leader>oc", "<cmd>OllieChat<cr>", { desc = "Ollie"}) -- Chat
keymap.set("v", "<leader>O", "<cmd>OllieChatContext<cr>", { desc = "Ollie"}) -- Chat with full context
keymap.set("x", "<leader>of", function() -- OllieFix
local selected, span = selection.get_selected_code()
input.ask({ preview = selected }, function(query)
ollie_fix.run_fix(query, selected, span, {})
end)
end, { desc = "Ollie" })
keymap.set("v", "<leader>oe", function() -- OllieExplain
local selected, span = selection.get_selected_code()
input.ask({ preview = selected }, function(query)
ollie_explain.run_explain(query, selected, span, {})
end)
end, { desc = "Ollie" })
Contributions are welcome. Before opening a PR, please read the following.
Project conventions:
- Lua files follow the existing module structure. New functionality belongs in an appropriate layer — provider logic in
providers/, user-facing operations inhandler/, rendering inui/. - No business logic in the UI layer. No Neovim API calls in providers.
- All async operations use
vim.looporvim.system. No blocking I/O on the main thread. - New providers must implement the provider interface defined in
providers/init.lua. - New handlers must be reachable via the router and must not hardcode a provider.
For contributing, please see Contribution for contributing guidance.
See documentation for more info.
See configuration for configuration.
See List of commands for understanding how to use commands.
Built for Neovim. Runs anywhere, is a Lua runtime and an HTTP connection (or local model), that can reach.



