Bring-your-own keys for cloud providers (OpenAI-compatible)

Settings -> Providers takes keys for OpenAI, Anthropic (their official
OpenAI-compatibility layer), DeepSeek, Groq, OpenRouter and any custom
OpenAI-compatible endpoint. One adapter (lib/openai-compat) covers
listing, streaming chat, JSON generation and vision, and every model
flow can run on a provider: the chat picker lists each enabled
provider's models (labelled, remembered), and a "Study generation"
choice moves decks, homework, grading and attachment transcription onto
a provider model.

Keys stay in this browser exactly like the Ollama one
(freezmo.providers.v1): never stored server-side, never in a backup or
the sync payload, forwarded only with the request that needs them. The
provider test asserts the key reaches the provider while the routing
header stays on the app side.

New suite tests/providers-test.mjs (13 checks) stubs both protocols:
model listing, chat streaming, study generation and the Settings flow.
Privacy policy updated (en+cs). Contrast 0 findings, axe clean, mobile
126/126, chat 102/102, wizard 95/95, bubbles 34/34, regression 44/44,
i18n 13/13, native 33/33. Version 0.4.7, SW -> freezmo-v72.
This commit is contained in:
Martin Dvořák 2026-10-14 06:29:39 +00:00
commit 4372423167
28 changed files with 1538 additions and 150 deletions

View file

@ -37,9 +37,9 @@ const PRIVACY_EN: LegalDocument = {
],
},
{
heading: "Your Ollama API key",
heading: "Your API keys",
paragraphs: [
"The key is kept in this browser only. It is sent to this Freezmo server for the single model call you asked for, forwarded to Ollama, and forgotten: never written to disk, never logged, never part of a backup, sync payload or share link.",
"Every model key — the Ollama one and any cloud provider keys you add under Settings → Providers — is kept in this browser only. A key is sent to this Freezmo server for the single model call you asked for, forwarded to that provider, and forgotten: never written to disk, never logged, never part of a backup, sync payload or share link.",
],
},
{
@ -60,7 +60,7 @@ const PRIVACY_EN: LegalDocument = {
{
heading: "Third parties",
paragraphs: [
"Model calls go to the Ollama endpoint this instance is configured for, using your key — Ollama's own privacy terms apply to them. Reminders travel through your browser's push service (Google, Mozilla or Apple). There is nothing else: no ads, no analytics, no embeds.",
"Model calls go to the Ollama endpoint this instance is configured for, or — when you pick a cloud model — to that provider (OpenAI, Anthropic, DeepSeek, Groq, OpenRouter or your own endpoint) with the key from your browser. The provider's own privacy terms apply to those calls. Reminders travel through your browser's push service (Google, Mozilla or Apple). There is nothing else: no ads, no analytics, no embeds.",
],
},
{
@ -87,9 +87,9 @@ const PRIVACY_CS: LegalDocument = {
],
},
{
heading: "Tvůj API klíč Ollama",
heading: "Tvoje API klíče",
paragraphs: [
"Klíč je uložený jen v tomto prohlížeči. Posílá se tomuto serveru Freezmo pro jeden požadovaný modelový dotaz, server ho předá Ollamě a zapomene ho: nikdy se neukládá na disk, neloguje se a není součástí zálohy, synchronizace ani sdíleného odkazu.",
"Každý klíč k modelu — ten ollamový i klíče cloudových poskytovatelů z Nastavení → Poskytovatelé — je uložený jen v tomto prohlížeči. Posílá se tomuto serveru Freezmo pro jeden požadovaný modelový dotaz, server ho předá danému poskytovateli a zapomene ho: nikdy se neukládá na disk, neloguje se a není součástí zálohy, synchronizace ani sdíleného odkazu.",
],
},
{
@ -110,7 +110,7 @@ const PRIVACY_CS: LegalDocument = {
{
heading: "Třetí strany",
paragraphs: [
"Modelové dotazy jdou na Ollama endpoint, který je tato instance nastavená používat, a to s tvým klíčem — platí na ně vlastní podmínky Ollamy. Připomínky putují přes push službu tvého prohlížeče (Google, Mozilla nebo Apple). Nic jiného tu není: žádné reklamy, analytika ani vložený obsah.",
"Modelové dotazy jdou na Ollama endpoint, který je tato instance nastavená používat, nebo — když zvolíš cloudový model — danému poskytovateli (OpenAI, Anthropic, DeepSeek, Groq, OpenRouter nebo vlastní endpoint) s klíčem z tvého prohlížeče; na tato volání platí podmínky daného poskytovatele. Připomínky putují přes push službu tvého prohlížeče (Google, Mozilla nebo Apple). Nic jiného tu není: žádné reklamy, analytika ani vložený obsah.",
],
},
{
@ -138,7 +138,7 @@ const TERMS_EN: LegalDocument = {
{
heading: "Your key, your usage",
paragraphs: [
"You bring your own Ollama API key. Limits, costs and account standing are between you and Ollama. Never paste a key you do not own, and keep your own key to yourself.",
"You bring your own API keys. Limits, costs and account standing are between you and the provider (Ollama or a cloud one you added). Never paste a key you do not own, and keep your own key to yourself.",
],
},
{
@ -184,7 +184,7 @@ const TERMS_CS: LegalDocument = {
{
heading: "Tvůj klíč, tvoje spotřeba",
paragraphs: [
"Používáš vlastní API klíč Ollama. Limity, náklady a stav účtu jsou mezi tebou a Ollamou. Nikdy nevkládej klíč, který ti nepatří, a ten svůj nikomu nedávej.",
"Používáš vlastní API klíče. Limity, náklady a stav účtu jsou mezi tebou a daným poskytovatelem (Ollamou nebo cloudovým, kterého jsi přidal). Nikdy nevkládej klíč, který ti nepatří, a ten svůj nikomu nedávej.",
],
},
{