aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-10 21:02:01 +0200
committerDanilo M. <danix@danix.xyz>2026-08-10 21:02:01 +0200
commit3f984ccb8ec25ef3b06109d638eed4022db82d45 (patch)
tree9c1b5331ba85a91f7f6d4bb37ac701948639f7fc
parent1faadea207429ecfe1d4580fa4f6a4c1c40d0dd5 (diff)
downloadllamachat-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.md33
-rw-r--r--README.md58
-rw-r--r--llamachat/config.py31
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
diff --git a/README.md b/README.md
index ada0d5a..5df70d6 100644
--- a/README.md
+++ b/README.md
@@ -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