diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-10 21:02:01 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-10 21:02:01 +0200 |
| commit | 3f984ccb8ec25ef3b06109d638eed4022db82d45 (patch) | |
| tree | 9c1b5331ba85a91f7f6d4bb37ac701948639f7fc | |
| parent | 1faadea207429ecfe1d4580fa4f6a4c1c40d0dd5 (diff) | |
| download | llamachat-3f984ccb8ec25ef3b06109d638eed4022db82d45.tar.gz llamachat-3f984ccb8ec25ef3b06109d638eed4022db82d45.zip | |
docs: document external providers, keys and cost
The generated config ships the provider block commented out so a fresh
install never reaches an endpoint nobody configured. The README states
plainly that every turn resends the whole conversation, which is free
locally and billed per message on a cloud provider.
| -rw-r--r-- | CHANGELOG.md | 33 | ||||
| -rw-r--r-- | README.md | 58 | ||||
| -rw-r--r-- | llamachat/config.py | 31 |
3 files changed, 121 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 451092f..d3df977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,39 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- External OpenAI-compatible providers. The local llama.cpp router stays the + default; adding a `[providers.*]` table brings cloud models into the picker + as `provider:model`. The generated config ships the provider block commented + out, so a fresh install never reaches an endpoint nobody configured. +- Three ways to supply a provider API key: `pass:name` reads from the password + store, `env:VAR` reads from the environment, and a literal `sk-...` string + is used as-is. `pass:` is preferred because it keeps secrets out of + `config.toml`. +- Lazy key resolution. The password store or environment is only consulted on + the first request to a provider, so a local-only session never triggers a + pinentry. +- Model filtering per provider. The `filter` key keeps only models whose id + contains one of the configured substrings, case-insensitively. Providers + list hundreds of models, and without a filter the picker is unusable. +- A per-model settings dialog for context size, vision support and input/output + prices. Values are saved to `models.ini` beside `config.toml`, so provider + defaults can be overridden per model. +- Per-conversation cost readout with projection. The top bar shows what has + been spent so far plus what the current draft would add, based on hand-entered + prices. This exists to make visible that a long conversation on a cloud + provider is billed for its whole history on every turn. +- Three new columns on `messages`: `prompt_tokens`, `completion_tokens` and + `model`. Token counts come from the stream's final usage chunk and power the + cost readout; the model column records which model actually replied, so a + switched conversation prices each turn correctly. + +Tool calling and reasoning output vary between providers. Web search on a +cloud model may not work as reliably as it does with a local router. + ## [0.3.0] - 2026-08-01 ### Changed @@ -43,7 +43,7 @@ persistent process and toggles like a scratchpad from a Hyprland keybind. - **Tray icon** for show/hide/quit, hosted by waybar's tray module. Not in this version: RAG or embedding search over history, multi-user -support, any cloud or non-local backend, remote access. +support, remote access. ## Requirements @@ -446,6 +446,62 @@ None of that stops a poisoned snippet from influencing what the model says. If a top result asserts something false, a 9B may repeat it. That is why the sources stay visible rather than being hidden behind the answer. +### External providers + +llamachat can talk to OpenAI-compatible cloud providers alongside the local +router. Cloud models appear in the picker as `provider:model`. Local models +stay bare, so an existing local setup needs no changes. + +Providers live in `[providers.*]` tables. The generated config ships a +commented together.ai block; uncomment it and set `api_key` to enable it: + +```toml +[providers.together] +base_url = "https://api.together.xyz" +api_key = "pass:api/together" +filter = ["qwen", "deepseek"] +ctx_size = 32768 +price_in = 0.60 +price_out = 0.60 +``` + +`api_key` accepts three forms: + +- `pass:api/together` reads the key from the password store. This is + preferred, because the secret never sits in a config file that might be + committed or copied. +- `env:TOGETHER_KEY` reads the key from the environment. +- `sk-...` is the literal key, written into `config.toml` itself. Avoid + this: it puts a secret in a plain-text file. + +Keys are resolved lazily, on the first request to that provider. A session +that only touches local models never triggers a pinentry or reads an +environment variable. + +`filter` keeps only models whose id contains one of the given strings, +case-insensitively. Providers list hundreds of models, and without a filter +the picker is unusable. Omit `filter` to list everything. + +`ctx_size`, `vision`, `price_in` and `price_out` prefill the per-model +settings dialog. Prices are US dollars per million tokens. The dialog saves +anything you enter to `models.ini` beside `config.toml`, so the provider +block can stay minimal. `models.ini` is machine-written cache, not +user-editable config. + +The cost readout in the top bar shows what has been spent in the current +conversation plus what the next message would add. It is approximate, based +entirely on the hand-entered prices in the provider block or the per-model +dialog. It is useful for seeing when a long conversation is becoming +expensive. + +**A conversation resends its whole history every turn.** That is free with a +local router, but on a cloud provider every message is billed for the entire +conversation so far. The projection in the cost label exists to make that +visible before you send. + +Tool calling and reasoning output vary between providers. Web search on a +cloud model may not work as reliably as it does with a local router. + ### The context meter The bar in the top bar shows how much of the active model's context window diff --git a/llamachat/config.py b/llamachat/config.py index bc7bd24..4add5ed 100644 --- a/llamachat/config.py +++ b/llamachat/config.py @@ -223,6 +223,37 @@ def write_default(path: Path = CONFIG_PATH) -> Path: f'search_snippet_chars = {DEFAULTS["search_snippet_chars"]}\n' f'search_timeout = {DEFAULTS["search_timeout"]}\n' f'max_searches = {DEFAULTS["max_searches"]}\n' + '\n' + '# External providers. The local router is a provider named "local",\n' + '# synthesized from base_url above when no [providers.local] exists.\n' + '# Any OpenAI-compatible endpoint works.\n' + '#\n' + '# Cloud models appear in the picker as provider:model. Local ones\n' + '# stay bare, so nothing about the local setup changes.\n' + '#\n' + '# api_key accepts three forms:\n' + '# "pass:api/together" read from the password store (preferred)\n' + '# "env:TOGETHER_KEY" read from the environment\n' + '# "sk-..." the key itself, in this file\n' + '# It is read lazily, on the first request to that provider, so a\n' + '# local-only session never unlocks the password store.\n' + '#\n' + '# filter keeps only models whose id contains one of these strings,\n' + '# case-insensitively. Providers list hundreds of models; without a\n' + '# filter the picker is unusable. Omit it to list them all.\n' + '#\n' + '# ctx_size, vision, price_in and price_out prefill the per-model\n' + '# dialog. Prices are US dollars per million tokens. Everything the\n' + '# dialog saves goes to models.ini beside this file, so none of\n' + '# these has to be set here.\n' + '#\n' + '# [providers.together]\n' + '# base_url = "https://api.together.xyz"\n' + '# api_key = "pass:api/together"\n' + '# filter = ["qwen", "deepseek"]\n' + '# ctx_size = 32768\n' + '# price_in = 0.60\n' + '# price_out = 0.60\n' ) return path |
