This commit is contained in:
Connor Johnstone committed 2026-11-02 09:58:48 -05:00
1 parent 6b9b1697f2
commit 631987c62d
11 files changed
+116 -442

No files matched your search

+3 -1
View File
@@ -7,7 +7,7 @@
- [Reasoning effort per model](litellm-extra-body-reasoning.md) — GLM-5.3 defaults to reasoning_effort=max and cannot disable thinking; gpt-oss needs chat_template_kwargs via extra_body
- [Reasoning vs completion tokens](reasoning-tokens-vs-completion-tokens.md) — reasoning comes out of max_tokens, and providers disagree about counting it
- [Open WebUI image generation](owui-image-generation.md) — works via LiteLLM; the default 512x512 size is rejected by every image model
- [Local code autocomplete](local-code-autocomplete.md) — FIM via /v1/completions; check rbw first; never tune for decode; ncmoe 22 + 8 s timeout since Viseron took VRAM
- [Local code autocomplete](local-code-autocomplete.md) — retired 2026-10-29; FIM via /v1/completions, never tune for decode, if it comes back
- [NPU setting clamps memory](npu-setting-clamps-memory.md) — MSI NPU/AI BIOS options cost 17% DRAM bandwidth and the NPU is unusable by llama.cpp anyway.
- [mainframe bad core](mainframe-bad-core.md) — APIC 20 (cpu10, an E-core) throws fatal MCEs; offlined by a unit, RMA candidate.
- [crowdsec bans own devices](crowdsec-bans-own-devices.md) — "no external access" is usually crowdsec banning his own phone; http_path includes the query string
@@ -20,3 +20,5 @@
- [Podman NFS binds go blind](podman-nfs-bind-rprivate-blind.md) — a mount landing after container start leaves containers permanently empty; podman ps looks fine
- [dotbot force deletes live dirs](dotbot-force-deletes-live-dirs.md) — install replaces a real dir at a link target; the Claude memory dir is now one of them
- [Discuss before adding content](discuss-before-adding-content.md) — in his notes, record what he said; suggest extras in chat, add only on agreement
- [Container storage on /home](container-storage-on-home.md) — bind mounts since 2026-10-29; overlay origin xattrs and conmon KillMode traps
- [Caddy 2.11.6 cuts HTTP/1.1 streams at 60 s](caddy-2116-http1-stream-cut.md) — OpenCode ECONNRESET; upgraded to 2.11.7; test --http1.1 vs --http2 first
@@ -0,0 +1,23 @@
---
name: caddy-2116-http1-stream-cut
description: Caddy 2.11.6 cut HTTP/1.1 streamed responses to POSTs at exactly 60 s (OpenCode ECONNRESET); fixed by upgrading to 2.11.7 on 2026-10-29
metadata:
type: reference
---
Caddy 2.11.6 added a default 1-minute idle read timeout that cancels an
HTTP/1.1 response still streaming 60 s after the request body was read
(caddyserver/caddy#8103). Through llm.rcjohnstone.com this ended every LLM
stream longer than a minute: OpenCode (HTTP/1.1) reported `ECONNRESET` and
retried, Caddy logged `aborting with incomplete response ... reading: context
canceled`, and Strata logged `disconnect, cancel=True` at 60 s. HTTP/2 clients
were unaffected. Fixed by recreating connor_caddy_1 on 2.11.7.
Ruled out first, all measured: Caddy `write` and `read_header` timeouts (no
change), LiteLLM (177 s stream fine over HTTP/1.1 direct), Strata, the network
(the cut reproduced from mainframe itself with `curl --http1.1`).
**How to apply:** a stream that dies at exactly 60 s through Caddy is this,
not the model. Test with `curl --http1.1` and `--http2` before anything else.
Caddy floats on `caddy:latest` and podman-stack-update refreshes it weekly, so
a regression can arrive the same way.
@@ -0,0 +1,39 @@
---
name: container-storage-on-home
description: Rootful podman and docker storage live on the SATA LVM via fstab bind mounts since 2026-10-29; how the move was done and the two traps it hit
metadata:
type: project
---
Since 2026-10-29, `/var/lib/containers` and `/var/lib/docker` are bind mounts
of `/home/var-lib/containers` and `/home/var-lib/docker` (fstab,
`x-systemd.requires-mounts-for=/home`). The NVMe is kept for models. Paths and
configs are unchanged, so podman, docker and restic see the same paths.
Scripts and logs from the move: `~/podman-move-2026-10-29/` (cutover2.sh,
fixcopy.sh, mounttest.py).
**Why:** Connor wants the only fast SSD free for LLM weights.
**How to apply — two traps, both hit on the first attempt:**
1. Copied overlay layers do not mount on a different filesystem. Overlay
`index` is on by default in this kernel (`/sys/module/overlay/parameters/index`
= Y). Each container's upper `diff` root carries `trusted.overlay.origin`,
and each layer's `work/index` carries `trusted.overlay.upper`; both are file
handles from the old filesystem. Every `podman start` failed with
`overlayfs: failed to verify upper root origin`. Fix: remove that xattr and
delete `work/index` after the final rsync (fixcopy.sh).
2. `podman start` run from a transient systemd unit with default KillMode puts
conmon in that unit's cgroup, and systemd kills every conmon when the unit
ends. This produced the stale-`running` state from CLAUDE.md item 3a for all
51 containers. Always use `systemd-run -p KillMode=process`.
Also: `mount(8)` from util-linux 2.42 fails any overlay with more than 8
lowerdirs, with no kernel message, even on healthy storage. Test overlay mounts
with mount(2) directly (mounttest.py), and run a control against known-good
layers before trusting a failure. And any podman call between the final rsync
and an rsync verify touches `storage.lock` and `db.sql` and fails the verify.
Jellyfin must start before Home Assistant; both bind udp/1900 and whichever
starts second fails.
@@ -13,7 +13,7 @@ The LiteLLM stack serves two profiles with different permitted providers:
formally banned by his office but should be treated as prohibited until the
security office gives written approval. That restriction is about HOSTED
PRC-origin models (Moonshot/Kimi etc.). A PRC-origin model running locally
(qwen3-coder-30b on llama-swap) is fine for work, since nothing leaves the
(a Qwen model on llama-swap) is fine for work, since nothing leaves the
machine. Connor's ruling, 2026-10-22.
- **Personal** — `anthropic/*`, `openai/*`, and local.
- `zen/*` is blocked for both, see [[zen-tos-hold]].
@@ -1,10 +1,15 @@
---
name: local-code-autocomplete
description: "Local code autocomplete (FIM) works on mainframe; qwen3-coder-30b is the best model for it despite being written off for capacity"
description: "RETIRED 2026-10-29. Local FIM autocomplete on mainframe (minuet + qwen3-coder-30b): what was measured, kept in case it comes back"
metadata:
type: reference
---
Retired 2026-10-29: Connor found the completions not worth having. minuet was
removed from nvim and qwen3-coder-30b from llama-swap, LiteLLM and disk. The
full config is archived in `~/guides/llm/archive/local-tier-2026-10.md`. What
follows is the state before removal.
Measured 2026-09-18 (llmbench suite 6b, `fim`).
**Local code autocomplete is viable on this box.** Route: a raw
-221
View File
@@ -64,8 +64,6 @@ vim.pack.add({
{ src = "https://github.com/nvim-treesitter/nvim-treesitter" },
-- copilot deliberately off on every machine
-- { src = "https://github.com/github/copilot.vim" },
-- ...and this is what replaced it: FIM completion off the local box.
{ src = "https://github.com/milanglacier/minuet-ai.nvim" },
})
require("mason").setup()
@@ -93,225 +91,6 @@ vim.lsp.config("rust_analyzer", {
}
})
-- ------------------------------------------------------------------------------
-- Local FIM autocomplete (minuet-ai -> LiteLLM -> llama-swap -> qwen3-coder-30b)
-- ------------------------------------------------------------------------------
--
-- Replaces copilot, which is off on every machine deliberately. Nothing leaves
-- the LAN: the model runs on mainframe's 3060 and `llm.rcjohnstone.com` has no
-- public A record -- internal CA, and Caddy source-IP gates it to 10.0.0.0/24,
-- 10.0.1.0/24 and the podman subnets.
--
-- ⚠ ROUTE: `/v1/completions`, NOT `/infill`. Measured 2026-09-18 through the
-- proxy: `/v1/completions` 200 with FIM tokens in a raw prompt, `/infill` 404
-- because LiteLLM does not expose it. `llama.vim` wants `/infill`, which would
-- mean talking to llama-server directly and bypassing the virtual key -- and on
-- the work laptop that key IS the policy boundary that makes Anthropic and PRC
-- models 403. Not worth routing around for an editor plugin.
--
-- ⚠ WHY qwen3-coder-30b: it is the best local FIM model by measurement (0.511
-- exact / 0.826 similarity against qwen3-next-80b's 0.302), and FIM is a
-- VOCABULARY feature -- gpt-oss-20b and gemma3-12b have no FIM tokens and
-- cannot do this at all. Its "skipped for capacity" verdict was about the 128k
-- agentic floor and does not bind here; autocomplete needs a few thousand
-- tokens of context, not 128k.
--
-- ⚠ EXPECT AN OCCASIONAL MULTI-SECOND PAUSE. llama-swap runs `-np 1` with a
-- ttl, so if another model was used in between, the next completion pays a
-- full reload. Measured: ~10 s cold, 0.7-1.3 s warm. Accepted deliberately --
-- concurrent agentic work is not viable on this hardware anyway.
-- ⚠ ALWAYS call setup(). The first version of this guarded the whole require()
-- behind "is the key present", which meant that with no key the module never
-- loaded, `:Minuet` was never registered, and nvim gave NO indication why --
-- the exact failure it hit in practice. `:Minuet` is created at module load,
-- not inside setup(), and the plugin already notifies clearly when the key is
-- missing. Let it do that job instead of silently doing nothing.
--
-- The key resolves lazily and is cached: env var first, then rbw, matching the
-- opencode convention of reading keys at launch rather than writing them to
-- disk. `false` is cached for "looked and found nothing" so a locked rbw costs
-- one probe per session rather than one per keystroke.
local _minuet_key
local function minuet_key()
if _minuet_key ~= nil then
return _minuet_key or nil
end
local k = vim.env.LITELLM_KEY_NVIM
if k and k ~= "" then
_minuet_key = k
return k
end
if vim.fn.executable("rbw") == 1 then
vim.fn.system({ "rbw", "unlocked" })
if vim.v.shell_error == 0 then
-- ⚠ The work laptop MUST use the work key: it is enforced
-- server-side to 403 Anthropic and hosted PRC-origin models, and a
-- personal key here would silently remove that guarantee for
-- everything nvim sends.
local host = vim.uv.os_gethostname() or ""
local entry = host:lower():match("sctfw004") and "litellm-key-work"
or "litellm-key-personal"
local out = vim.fn.system({ "rbw", "get", entry })
if vim.v.shell_error == 0 and vim.trim(out) ~= "" then
_minuet_key = vim.trim(out)
return _minuet_key
end
end
end
_minuet_key = false
return nil
end
local minuet_base = vim.env.LITELLM_BASE
if not minuet_base or minuet_base == "" then
minuet_base = "https://llm.rcjohnstone.com"
end
-- ⚠ pcall, because a hard require here takes the WHOLE config down with it.
-- `vim.pack.add` installs on first launch, and that needs to reach github --
-- which is not a given on a work-issued machine behind a proxy. Without this,
-- a failed install means no MinuetStatus, no colorscheme, no anything below
-- this line, and an error that points at minuet rather than at the network.
local minuet_ok, minuet = pcall(require, "minuet")
if minuet_ok then
minuet.setup({
provider = "openai_fim_compatible",
-- Default is 'warn', which hides request failures. If completions stop
-- appearing, set this to 'debug' and the reason shows up in `:messages`.
notify = "warn",
-- Default 3. Measured 2026-10-22 at -ncmoe 22: a fresh ~3k-token context
-- takes 4.8 s to prefill, the same context again 0.8 s. At 3 s the first
-- completion in a file times out, and it may not leave a cache behind.
request_timeout = 8,
-- Ghost text: there is no completion engine here (no nvim-cmp, no blink)
-- and virtual text is the copilot-shaped one.
virtualtext = {
-- Every filetype. The ignore list is UI buffers, where typing would
-- send a request per keystroke into a picker.
auto_trigger_ft = { "*" },
auto_trigger_ignore_ft = { "TelescopePrompt", "oil" },
keymap = {
-- Accept and dismiss are <C-l> and <C-h>, mapped below.
accept = false,
accept_line = false,
prev = "<A-[>",
next = "<A-]>",
dismiss = false,
},
},
provider_options = {
openai_fim_compatible = {
-- A FUNCTION, not an env var name: this is what lets the key come
-- from rbw when the shell did not export one.
api_key = minuet_key,
name = "litellm",
end_point = minuet_base .. "/v1/completions",
model = "qwen3-coder-30b",
-- ⚠ KEEP THIS SMALL. Measured on the real path at 48: `def add`
-- came back with `sub` and `mul` attached, so most of the latency
-- was spent generating text that would be discarded. Raise it only
-- if completions are being truncated mid-expression.
optional = {
max_tokens = 32,
top_p = 0.9,
stop = { "<|fim_pad|>", "<|endoftext|>", "\n\n" },
},
-- ⚠ THREE POSITIONAL ARGS, not a context table. minuet calls
-- this as `template.prompt(before, after, opts)` -- the first
-- version took `(ctx)` and died with "attempt to concatenate
-- field 'lines_after' (a nil value)" on the first real
-- completion, while loading perfectly cleanly. A config that
-- loads is not a config that works.
--
-- `suffix = false` because the whole FIM prompt is built here;
-- letting minuet also send a `suffix` field would give the server
-- two competing descriptions of the same gap.
template = {
prompt = function(before, after, _opts)
return "<|fim_prefix|>" .. (before or "")
.. "<|fim_suffix|>" .. (after or "")
.. "<|fim_middle|>"
end,
suffix = false,
},
},
},
})
-- <C-l> accepts one line; a second <C-l> within 300 ms accepts the rest, so a
-- double tap takes the whole suggestion. <C-h> dismisses. With no suggestion
-- showing, <C-h> deletes a character as usual and <C-l> does nothing (its
-- default inserts a literal ^L).
local minuet_vt = require("minuet.virtualtext").action
local minuet_last_accept = 0
vim.keymap.set("i", "<C-l>", function()
if not minuet_vt.is_visible() then
return ""
end
-- hrtime, not uv.now(): now() is cached per loop iteration and can miss
-- the gap between two key presses.
local now = vim.uv.hrtime() / 1e6
local n_lines = 1
if now - minuet_last_accept < 300 then
n_lines = nil
end
minuet_last_accept = now
vim.schedule(function() minuet_vt.accept(n_lines) end)
return ""
end, { expr = true, silent = true, desc = "Accept minuet line; double tap accepts all" })
vim.keymap.set("i", "<C-h>", function()
if minuet_vt.is_visible() then
vim.schedule(minuet_vt.dismiss)
return ""
end
return "<C-h>"
end, { expr = true, silent = true, desc = "Dismiss minuet suggestion, else delete" })
end
-- Ghost text, dimmer than a comment.
--
-- minuet links MinuetVirtualText -> Comment, which in `vague` is #606079 --
-- the same colour as real comments, so a suggestion reads as if it were
-- already part of the file. This drops it toward the background.
--
-- Reference points in this colourscheme, for tuning:
-- Normal #cdcdcd (actual code)
-- Comment #606079 (minuet's default -- too prominent)
-- MINUET #4a4a5c <- here
-- Whitespace #333738 (about as dim as stays legible)
--
-- ⚠ MUST be re-applied on ColorScheme. Loading a colourscheme runs
-- `highlight clear`, which drops custom groups -- so setting this once at
-- startup works until the first `:colorscheme` and then silently stops.
local MINUET_FG = "#4a4a5c"
local function minuet_highlight()
vim.api.nvim_set_hl(0, "MinuetVirtualText", { fg = MINUET_FG, italic = true })
end
vim.api.nvim_create_autocmd("ColorScheme", {
group = vim.api.nvim_create_augroup("MinuetHighlight", { clear = true }),
callback = minuet_highlight,
})
minuet_highlight()
-- Registered UNCONDITIONALLY, so it can report the case where the plugin
-- itself is missing. A status command that only exists when everything
-- already works is not a status command.
vim.api.nvim_create_user_command("MinuetStatus", function()
local k = minuet_key()
local lines = {
"plugin: " .. (minuet_ok and "loaded" or "NOT INSTALLED -- run :lua vim.pack.update()"),
"endpoint: " .. minuet_base .. "/v1/completions",
"model: qwen3-coder-30b",
"key: " .. (k and ("found (" .. #k .. " chars)") or "NOT FOUND"),
}
if not k then
table.insert(lines, "")
table.insert(lines, "Set LITELLM_KEY_NVIM in ~/.secrets.zsh, or unlock rbw")
table.insert(lines, "(`rbw unlock`) so it can be read from there.")
end
vim.notify(table.concat(lines, "\n"), k and vim.log.levels.INFO or vim.log.levels.WARN)
end, { desc = "Show minuet FIM autocomplete status" })
vim.cmd("colorscheme vague")
-- KEYMAPS
+14 -67
View File
@@ -9,11 +9,11 @@
"apiKey": "{env:LITELLM_KEY}"
},
"models": {
"gpt-oss-20b": {
"name": "gpt-oss-20b (local, resident)",
"qwen3.8-flash-next-iq3s-med": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, medium reasoning)",
"limit": {
"context": 65536,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -22,11 +22,11 @@
"cache_write": 0.0
}
},
"qwen3-coder-30b": {
"name": "qwen3-coder-30b (local, on demand)",
"qwen3.8-flash-next-iq3s": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, default reasoning - slow)",
"limit": {
"context": 32768,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -48,32 +48,6 @@
"cache_write": 0.0
}
},
"local/librarian": {
"name": "Librarian - local gpt-oss",
"limit": {
"context": 65536,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"local/explore": {
"name": "Explore - local gpt-oss",
"limit": {
"context": 65536,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"muse-glimmer-30b-low": {
"name": "Muse Glimmer 30B (local, low reasoning)",
"limit": {
@@ -87,32 +61,6 @@
"cache_write": 0.0
}
},
"qwen3.8-27b-gsq-s-low": {
"name": "Qwen3.8-27B GSQ IQ3_S (local, low reasoning)",
"limit": {
"context": 131072,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"qwen3.8-27b-gsq-s-med": {
"name": "Qwen3.8-27B GSQ IQ3_S (local, medium reasoning)",
"limit": {
"context": 131072,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"muse-glimmer-30b": {
"name": "Muse Glimmer 30B (local, full reasoning - slow)",
"limit": {
@@ -132,23 +80,22 @@
"enabled_providers": [
"litellm"
],
"model": "litellm/local/librarian",
"small_model": "litellm/local/librarian",
"model": "litellm/muse-glimmer-30b",
"agent": {
"dev": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"architect": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"review": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"duck": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"brainstorm": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
}
}
}
+14 -67
View File
@@ -9,11 +9,11 @@
"apiKey": "{env:LITELLM_KEY}"
},
"models": {
"gpt-oss-20b": {
"name": "gpt-oss-20b (local, resident)",
"qwen3.8-flash-next-iq3s-med": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, medium reasoning)",
"limit": {
"context": 65536,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -22,11 +22,11 @@
"cache_write": 0.0
}
},
"qwen3-coder-30b": {
"name": "qwen3-coder-30b (local, on demand)",
"qwen3.8-flash-next-iq3s": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, default reasoning - slow)",
"limit": {
"context": 32768,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -48,32 +48,6 @@
"cache_write": 0.0
}
},
"local/librarian": {
"name": "Librarian - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"local/explore": {
"name": "Explore - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"anthropic/claude-opus-5": {
"name": "claude-opus-5 (Anthropic direct)",
"limit": {
@@ -243,32 +217,6 @@
"cache_write": 0.0
}
},
"qwen3.8-27b-gsq-s-low": {
"name": "Qwen3.8-27B GSQ IQ3_S (local, low reasoning)",
"limit": {
"context": 131072,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"qwen3.8-27b-gsq-s-med": {
"name": "Qwen3.8-27B GSQ IQ3_S (local, medium reasoning)",
"limit": {
"context": 131072,
"output": 8192
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"muse-glimmer-30b": {
"name": "Muse Glimmer 30B (local, full reasoning - slow)",
"limit": {
@@ -288,23 +236,22 @@
"enabled_providers": [
"litellm"
],
"model": "litellm/qwen3.8-27b-gsq-s-med",
"small_model": "litellm/local/librarian",
"model": "litellm/muse-glimmer-30b",
"agent": {
"dev": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"architect": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"review": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"duck": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
},
"brainstorm": {
"model": "litellm/qwen3.8-27b-gsq-s-med"
"model": "litellm/muse-glimmer-30b"
}
}
}
+8 -35
View File
@@ -9,11 +9,11 @@
"apiKey": "{env:LITELLM_KEY}"
},
"models": {
"gpt-oss-20b": {
"name": "gpt-oss-20b (local, resident)",
"qwen3.8-flash-next-iq3s-med": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, medium reasoning)",
"limit": {
"context": 65536,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -22,11 +22,11 @@
"cache_write": 0.0
}
},
"qwen3-coder-30b": {
"name": "qwen3-coder-30b (local, on demand)",
"qwen3.8-flash-next-iq3s": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, default reasoning - slow)",
"limit": {
"context": 32768,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -48,32 +48,6 @@
"cache_write": 0.0
}
},
"local/librarian": {
"name": "Librarian - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"local/explore": {
"name": "Explore - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"chatgpt/gpt-5.6-sol": {
"name": "gpt-5.6-sol (ChatGPT Plus, flat fee)",
"limit": {
@@ -211,7 +185,6 @@
"litellm"
],
"model": "litellm/chatgpt/gpt-5.6-terra-low",
"small_model": "litellm/local/librarian",
"permission": {
"read": {
"*": "ask",
+8 -35
View File
@@ -9,11 +9,11 @@
"apiKey": "{env:LITELLM_KEY}"
},
"models": {
"gpt-oss-20b": {
"name": "gpt-oss-20b (local, resident)",
"qwen3.8-flash-next-iq3s-med": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, medium reasoning)",
"limit": {
"context": 65536,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -22,11 +22,11 @@
"cache_write": 0.0
}
},
"qwen3-coder-30b": {
"name": "qwen3-coder-30b (local, on demand)",
"qwen3.8-flash-next-iq3s": {
"name": "qwen3.8-flash-next IQ3_S (local Strata, default reasoning - slow)",
"limit": {
"context": 32768,
"output": 8192
"context": 196608,
"output": 32768
},
"cost": {
"input": 0.0,
@@ -61,32 +61,6 @@
"cache_write": 0.0
}
},
"local/librarian": {
"name": "Librarian - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"local/explore": {
"name": "Explore - local gpt-oss",
"limit": {
"context": 65536,
"output": 4096
},
"cost": {
"input": 0.0,
"output": 0.0,
"cache_read": 0.0,
"cache_write": 0.0
}
},
"chatgpt/gpt-5.6-sol": {
"name": "gpt-5.6-sol (ChatGPT Plus, flat fee)",
"limit": {
@@ -224,7 +198,6 @@
"litellm"
],
"model": "litellm/chatgpt/gpt-5.6-terra-low",
"small_model": "litellm/local/librarian",
"permission": {
"read": {
"*": "ask",
-14
View File
@@ -11,20 +11,6 @@ export LASTFM_API_SECRET=""
# syntax, so an unset value becomes an empty string and every request 401s.
export LITELLM_KEY=""
# ------------------------------------------------------------------------------
# nvim FIM autocomplete (minuet-ai -> LiteLLM -> qwen3-coder-30b)
# ------------------------------------------------------------------------------
# ⚠ WHICH KEY DEPENDS ON THE MACHINE, and on the work laptop it is the policy
# boundary, not a preference. `litellm-key-work` is enforced server-side to 403
# Anthropic and PRC-origin models; a personal key here would silently remove
# that guarantee for anything nvim sends.
#
# sctfw004 (work) rbw get litellm-key-work
# mainframe / system76 rbw get litellm-key-personal
#
# The NAS does not need it -- nvim disables the plugin silently when unset.
export LITELLM_KEY_NVIM="$(rbw get litellm-key-work 2>/dev/null)"
# Where the proxy lives. Mainframe talks to itself; every other machine uses
# the internal name, which has no public A record and is source-IP gated.
# export LITELLM_BASE="https://llm.rcjohnstone.com"