diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-31 11:03:51 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-31 11:03:51 +0200 |
| commit | 9182c84b8ac9d905a9d553b95155a993338568b8 (patch) | |
| tree | c1a56b7b6a49099b25e7a2dee4e0f908d5a986c5 /README.md | |
| parent | 325a6a7c12832d8b9fb9b67567e8a1c496dc1330 (diff) | |
| download | llamachat-9182c84b8ac9d905a9d553b95155a993338568b8.tar.gz llamachat-9182c84b8ac9d905a9d553b95155a993338568b8.zip | |
feat: add context meter and file-based system prompts
Two additions to the chat window.
A context meter in the top bar shows how much of the active model's window
the next request will occupy: system prompt, prior turns, attachments and
the current draft. The router does not report a usable context size in
router mode (/props returns n_ctx 0), so the limit comes from ctx-size in
presets.ini. Streaming requests now ask for usage statistics, which makes
the figure exact after each reply at no extra round trip; before that it is
an estimate marked with a leading ~.
The estimate reads low on models with a reasoning budget, since thinking
tokens cannot be known before the reply arrives. The tooltip says so.
System prompts are markdown files in the prompts/ directory beside the
config, one per file, so they can be edited in an editor and kept in
version control. default.md is global, any other file is a named preset
that replaces it rather than adding to it, and a conversation may instead
carry one-off text or opt out entirely. The choice is stored per session so
reopening a chat restores the prompt it was built with.
Prompt names are reduced to safe filenames, so a name like ../../etc/passwd
cannot write outside the prompts directory, and a name that reduces to
nothing is rejected rather than creating a dotfile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 73 |
1 files changed, 70 insertions, 3 deletions
@@ -25,6 +25,14 @@ persistent process and toggles like a scratchpad from a Hyprland keybind. thinking separately from its answer. It shows as a one-line `▸ thinking` summary above the reply, and clicking that expands it. Each reply collapses independently and the state is remembered per bubble. +- **Context meter** in the top bar showing how much of the model's context + window the next request will use. It estimates while you type and becomes + exact after each reply, turning amber at 75% and red at 90%. +- **System prompts** kept as markdown files in `~/.config/llamachat/prompts/`. + A global `default.md` applies to new chats, named presets replace it, and + any conversation can take a one-off custom prompt or none at all. The + choice is stored per session, so reopening an old chat restores the prompt + it was built with. - **Tray icon** for show/hide/quit, hosted by waybar's tray module. Not in this version: RAG or embedding search over history, multi-user @@ -118,6 +126,10 @@ db = "" default_model = "" request_timeout = 300 +# System prompt for new conversations: a preset name from prompts/, +# "none" for no system prompt, or empty for the global default.md. +default_prompt = "" + attach_ctx_fraction = 0.5 chars_per_token = 3.5 ``` @@ -252,7 +264,8 @@ there is no second thread. ### Database schema ```sql -sessions (id, mode, title, model, created_at, updated_at) +sessions (id, mode, title, model, prompt_name, prompt_custom, + created_at, updated_at) messages (id, session_id, role, content, reasoning, created_at) attachments (id, message_id, path, kind, mime, size, sha256, thumb, truncated) messages_fts -- FTS5 external-content table over messages.content @@ -265,8 +278,16 @@ finishes. `reasoning` holds the model's thinking, kept in its own column for two reasons: the FTS index covers `content` only, so thinking text cannot bury real search hits, and only `content` is replayed when continuing a -conversation, which is what these APIs expect. Databases created before this -column existed gain it automatically on open. +conversation, which is what these APIs expect. + +`prompt_name` records which system prompt a conversation was built with, and +`prompt_custom` holds the text when that prompt is a one-off rather than a +file. Storing the name rather than the resolved text means editing a preset +affects the conversations that use it, which is usually what you want; a +conversation that needed frozen wording should use a custom prompt. + +Databases created before any of these columns existed gain them +automatically on open. Attachment *content* lives inline in `messages.content`, since that is what was actually sent to the model. The `attachments` table records provenance: @@ -278,6 +299,52 @@ is gone. Full image bytes are not stored. Search input is tokenised and quoted before it reaches FTS5, so punctuation that would otherwise be read as query syntax cannot cause an error. +### System prompts + +Prompts are markdown files in `~/.config/llamachat/prompts/`, one per file, +created on first run: + +``` +prompts/ + default.md the global prompt, used unless something overrides it + coding.md a named preset + terse.md another +``` + +They are plain files on purpose, so they can be edited in an editor, diffed, +and kept in version control. The `…` button beside the picker opens a dialog +that does the same thing from inside the app. + +The picker offers every file plus two extras. A named preset **replaces** the +global prompt rather than being appended to it. `custom for this chat…` takes +one-off text stored with that conversation, and `no system prompt` sends none +at all. + +`default_prompt` in `config.toml` chooses what new conversations start with: +a preset name, `"none"`, or empty for `default.md`. + +The choice is recorded per session, so reopening a chat restores the prompt +it was built with rather than silently applying whatever is selected now. + +### The context meter + +The bar in the top bar shows how much of the active model's context window +the next request will occupy: system prompt, prior turns, attachments, and +whatever is currently in the input box. + +While you type it is an estimate derived from `chars_per_token`, shown with a +leading `~`. When a reply finishes, the server reports the real token counts +in the stream and the meter switches to those, dropping the `~`. + +Expect the estimate to read low on models with a reasoning budget. Thinking +tokens count against the context but cannot be known before the reply +arrives, so a model that thinks for 2000 tokens will jump well past the +estimate once it answers. The exact figure after each turn is the one to +trust. + +The limit comes from `ctx-size` in `presets.ini`. Models the router offers +but that file does not describe show no limit. + ### Markdown rendering Replies are parsed by `QTextDocument.setMarkdown()`, so there is no markdown |
