62 lines
4.9 KiB
Markdown
62 lines
4.9 KiB
Markdown
# KI: Claude schlägt vor, der Nutzer entscheidet
|
|
|
|
Since 2026-10-24. Organisation help with Claude (Anthropic) across diora: books and magazines
|
|
first; podcasts, news/blogs and projects follow (same building blocks).
|
|
|
|
## The key: the user's own, end-to-end encrypted, only on the devices
|
|
|
|
Two providers, chosen in the settings (decided 2026-10-25): **Anthropic** (the official SDK) or
|
|
**OpenRouter** (the same Claude model, `anthropic/claude-opus-5.5`). OpenRouter's
|
|
Anthropic-compatible endpoint refuses the headers a browser call needs (`anthropic-version` …,
|
|
checked by a CORS preflight), so diora talks to OpenRouter's chat completions instead: plain
|
|
`fetch`, `response_format` json_schema (strict), `reasoning.effort`; no fallback on refusals, and
|
|
OpenRouter sees what is sent too. The vault keeps `{provider, anthropic_key, openrouter_key}`
|
|
(an old `{anthropic_key}` still reads as Anthropic); a key is checked before it is kept
|
|
(Anthropic: the model's info; OpenRouter: `GET /api/v1/key`).
|
|
|
|
Decided with the user: the API key is entered in the app ("KI-Assistent", the ✦ of a
|
|
feature or the app menu) and stored like the books — encrypted with the book key on the device,
|
|
kept by the server as a blob it can't read:
|
|
|
|
- `GET/POST /accounts/vault/<name>/` (`accounts.UserVault`, name `ai`): `{ct, iv}`; `{ct: ''}`
|
|
removes it. Its own store, not the library prefs (`/books/prefs/` is rewritten whole when
|
|
the folder order is saved).
|
|
- The devices decrypt it into memory only and call Claude **directly** from the page
|
|
(browser, the apps' WebView, the desktop). The diora server sees neither the key nor what is
|
|
sent; Anthropic sees what a feature needs (titles, authors, folder names …). Anthropic bills
|
|
the user.
|
|
- Without the book key on a device (or after a password change, which changes the book key)
|
|
the key can't be read: the settings ask for it again. Not on diora Reader (e-readers).
|
|
- Server-side jobs (in the background, without an app open) were left out on purpose: they
|
|
would need the key on the server. The project Radar has its own server key (docs/projects.md).
|
|
Podcast articles wait on the server as a wish until a device with the key is open
|
|
(docs/transcription.md).
|
|
|
|
## Building blocks (`static/js/ai.js`, an ES module)
|
|
|
|
- The official SDK, `@anthropic-ai/sdk` 0.133.0, bundled for the browser with esbuild 0.28.2:
|
|
`static/vendor/anthropic-sdk-0.133.0.mjs` (licence beside it). `dangerouslyAllowBrowser`:
|
|
the key is the user's own. To update: `npm install @anthropic-ai/sdk@<v> esbuild` in an empty
|
|
folder, `esbuild entry.mjs --bundle --format=esm --platform=browser --target=es2020 --minify`
|
|
with `export { default } from '@anthropic-ai/sdk'`, and change the import in ai.js.
|
|
- `DioraAI.ask({system, prompt, schema, effort})`: model `claude-opus-5-5`, structured output
|
|
(`output_config.format` json_schema), effort `low` by default, streamed, `fallbacks: "default"`
|
|
(beta `server-side-fallback-2026-07-01`) for declined requests; refusals and over-long
|
|
answers become plain German messages.
|
|
- `ask` also takes `onProgress(words)` (words written so far while streaming; OpenRouter is not streamed and reports once at the end)
|
|
and `signal` (an `AbortSignal`), both optional; `DioraAI.modelName()` is the model of the chosen provider. Used by the podcast articles.
|
|
- `DioraAI.review({title, intro, items, onApply})`: the suggestions with checkboxes — nothing
|
|
changes until the user ticks and presses "Übernehmen".
|
|
- `DioraAI.settings()`, `withKey(fn)`, `progress()`, `showError()`, `toast()`; the sheets close
|
|
with Android's back key; ⚙ in each sheet opens the settings.
|
|
|
|
## Features
|
|
|
|
| area | where | what Claude gets | what it suggests |
|
|
|---|---|---|---|
|
|
| Bücher, Zeitschriften | ✦ in the library's head (`static/js/app/55-library-tools.js`) | per book: id, title, author, folders, series, magazine mark; the existing folders | folders to add (existing first; new ones only for ≥ 3 books), series with volume, cleaned title/author where broken, magazine mark |
|
|
| Podcasts | ✦ in Abos (diora-android `web-native/static/native-podcast.js`, copied to `static/podcasts/`) | per subscription: title, author, folders, level, heard in 12 months, publishing interval, three newest episode titles | folders to add (≤ 2), level Stamm / Strom / Auf Abruf (only with a clear reason) |
|
|
| Nachrichten, Blogs | ✦ in the feeds' bar (`static/js/feeds.js`; the apps' section bar presses it) | per source: title, address, kind, Ressort or folders, recent titles shown | Ressort for news, folders for blogs, news ↔ blog when clearly wrong |
|
|
| Podcast-Artikel | ✦ Artikel in an episode (`static/js/podcast-article.js`, docs/transcription.md) | the episode's transcript (feed or Whisper), title, author, shownotes, chapters | an article from the speakers' standpoint with summary and sections, saved to the episode and to Blogs (no review sheet: it is new text, removable) |
|
|
|
|
Applied through `_updateBookMeta` (the encrypted meta, as when editing by hand).
|