docs(readme): Rate-Limit, Ruhezeiten und now-Ausbruch dokumentieren
Endpunkt-Tabelle (discarded-Feld, Gesetz-State in /status), neuer Abschnitt zu Semantik (wer wartet, wer verfällt, was now darf), Rangordnung mute > now > Ruhezeit > Rate-Limit, Config-/Env-Referenz und die status-Zeilen der CLI.
This commit is contained in:
1 parent
5979d45710
commit
24ceff3f48
1 file changed
+49
-9
@@ -33,7 +33,7 @@ mit der kompletten Provider-Kette und Daemon-Anbindung (non-blocking über
|
|||||||
`talkd`, siehe unten), `mute`, `unmute`, `status`, `cancel`. Die übrigen
|
`talkd`, siehe unten), `mute`, `unmute`, `status`, `cancel`. Die übrigen
|
||||||
Befehle sind Stubs und melden `not implemented yet` (Exit-Code ≠ 0).
|
Befehle sind Stubs und melden `not implemented yet` (Exit-Code ≠ 0).
|
||||||
|
|
||||||
## Daemon talkd (Task 04)
|
## Daemon talkd (Task 04 + 05)
|
||||||
|
|
||||||
`talkd` besitzt die Ansage-Queue und spricht seriell ab — genau eine Ansage
|
`talkd` besitzt die Ansage-Queue und spricht seriell ab — genau eine Ansage
|
||||||
gleichzeitig, Reihenfolge `now` > `high` > `normal` > `low` (FIFO je
|
gleichzeitig, Reihenfolge `now` > `high` > `normal` > `low` (FIFO je
|
||||||
@@ -42,17 +42,17 @@ config `[server] port`, Env `TALK_PORT` (schlagen den Default).
|
|||||||
|
|
||||||
| Endpunkt | Wirkung |
|
| Endpunkt | Wirkung |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `POST /speak` | `{text, priority?, provider?, voice?, lang?, now?}` → `202 {"id":"…"}`; bei Mute: `202 {"id":"","muted":true}` (Ansage verworfen — dokumentiertes Verhalten) |
|
| `POST /speak` | `{text, priority?, provider?, voice?, lang?, now?}` → `202 {"id":"…"}`; bei Mute: `202 {"id":"","muted":true}`; bei Gesetz-Verwurf: `202 {"id":"","discarded":"rate_limit"\|"quiet_hours"}` (+ `"toast":true` im Toast-Modus) — verworfen, dokumentiertes Verhalten |
|
||||||
| `GET /status` | `{"version", "queue", "queue_ids", "current", "muted", "provider"}` |
|
| `GET /status` | `{"version", "queue", "queue_ids", "current", "muted", "provider", "rate_limit": {"interval", "allow_high_burst", "next_slot"}, "quiet_hours": {"window", "active", "mode", "until"}}` |
|
||||||
| `POST /cancel` | bricht die laufende Ansage ab + leert die Queue → `200 {"cancelled":n}` |
|
| `POST /cancel` | bricht die laufende Ansage ab + leert die Queue → `200 {"cancelled":n}` |
|
||||||
| `POST /mute` / `POST /unmute` | persistenter Mute-State → `200 {"muted":bool}` |
|
| `POST /mute` / `POST /unmute` | persistenter Mute-State → `200 {"muted":bool}` |
|
||||||
|
|
||||||
- **CLI-Verhalten:** `talk say` (und `talk "Text"`) erkennt den Daemon via
|
- **CLI-Verhalten:** `talk say` (und `talk "Text"`) erkennt den Daemon via
|
||||||
`GET /status`. Läuft er: enqueue + sofortige Rückkehr (non-blocking,
|
`GET /status`. Läuft er: enqueue + sofortige Rückkehr (non-blocking,
|
||||||
stdout erhält die Item-ID); `--wait` pollt bis zur Fertigstellung;
|
stdout erhält die Item-ID); `--wait` pollt bis zur Fertigstellung;
|
||||||
`--now` springt vor die Warteschlange; bei Mute sagt die CLI `muted`.
|
`--now` springt vor die Warteschlange; bei Mute sagt die CLI `muted`,
|
||||||
`talk mute`/`unmute`/`status`/`cancel` sind Daemon-Aufrufe mit plain
|
bei Gesetz-Verwurf `discarded: <grund>`. `talk mute`/`unmute`/`status`/
|
||||||
Output.
|
`cancel` sind Daemon-Aufrufe mit plain Output.
|
||||||
- **Mute ist persistent** (Datei `mute` im Config-Dir, Inhalt `true`/
|
- **Mute ist persistent** (Datei `mute` im Config-Dir, Inhalt `true`/
|
||||||
`false`) und überlebt Daemon-Restarts. `talk mute` ohne laufenden Daemon
|
`false`) und überlebt Daemon-Restarts. `talk mute` ohne laufenden Daemon
|
||||||
schreibt die Datei direkt — der Daemon liest sie bei jeder Ansage neu,
|
schreibt die Datei direkt — der Daemon liest sie bei jeder Ansage neu,
|
||||||
@@ -61,9 +61,49 @@ config `[server] port`, Env `TALK_PORT` (schlagen den Default).
|
|||||||
sequenziell weiter — serialisiert über die Lock-Datei `talk.lock` im
|
sequenziell weiter — serialisiert über die Lock-Datei `talk.lock` im
|
||||||
Config-Dir (Kollision = warten; verwaisste Locks gelten nach 60 s als
|
Config-Dir (Kollision = warten; verwaisste Locks gelten nach 60 s als
|
||||||
verfallen). Mute wird auch hier respektiert. Nichts bricht still.
|
verfallen). Mute wird auch hier respektiert. Nichts bricht still.
|
||||||
- Rate-Limit und Ruhezeiten folgen in Task 05; die Rate-Limit-Struktur
|
|
||||||
existiert bereits (`internal/queue`), ist in diesem Stand aber bewusst
|
### Rate-Limit + Ruhezeiten (Task 05)
|
||||||
noch nicht angebunden (Default: unbegrenzt).
|
|
||||||
|
Der Daemon erzwingt das Anti-Spam-Gesetz als Backstop unter allem
|
||||||
|
Prompt-Verhalten. **Rate-Limit:** Jede *gestartete* Ansage bucht ein
|
||||||
|
Zeitfenster — Default **1 Ansage / 30 s**. Bis zum Fenster-Ablauf gilt:
|
||||||
|
|
||||||
|
- `priority=low` wird **verworfen** (schon beim Eintritt, die 202-Antwort
|
||||||
|
nennt `discarded: "rate_limit"`); war das Fenster beim Eintritt noch
|
||||||
|
frei, entscheidet der Worker später.
|
||||||
|
- `normal` **parkt** in der Queue und spielt zum freien Slot (in `/status`
|
||||||
|
als `rate_limit.next_slot` sichtbar).
|
||||||
|
- `priority=high` darf mit `queue.allow_high_burst = true` (Default)
|
||||||
|
**bewusst verstoßen**; ohne Burst-Recht wartet high wie normal.
|
||||||
|
- `now:true` **umgeht auch das Rate-Limit** — der dokumentierte Ausbruch
|
||||||
|
für Dringendes (startet trotzdem ein neues Fenster).
|
||||||
|
|
||||||
|
**Ruhezeiten:** `quiet_hours = "18:00-08:00"` (lokale Zeit, überspannt
|
||||||
|
Mitternacht; leer/`off` = aus — bewusst kein Default-Fenster, Ruhezeiten
|
||||||
|
sind Opt-in wie Mute). In der Ruhezeit verfällt Audio, auch für high
|
||||||
|
(Burst ist ein Rate-Recht, kein Ruhe-Recht):
|
||||||
|
|
||||||
|
- `quiet_mode = "discard"` (Default): still verwerfen.
|
||||||
|
- `quiet_mode = "toast"`: Benachrichtigung statt Audio — derzeit eine
|
||||||
|
Protokoll-Zeile über den Notification-Pfad (OS-Toast folgt mit der
|
||||||
|
Notification-Anbindung).
|
||||||
|
- `now:true` **bricht auch die Ruhezeit** (bewusster Ausbruch).
|
||||||
|
|
||||||
|
**Rangordnung:** manuelles Mute gewinnt immer > `now` > Ruhezeit >
|
||||||
|
Rate-Limit. Konfiguration (config.toml, Env schlägt Datei):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[queue]
|
||||||
|
rate_limit = "30s" # Dauer; "0s" = unbegrenzt (TALK_RATE_LIMIT)
|
||||||
|
allow_high_burst = true # high darf verstoßen (TALK_ALLOW_HIGH_BURST)
|
||||||
|
|
||||||
|
quiet_hours = "18:00-08:00" # leer = aus; Env TALK_QUIET_HOURS ("off" = aus)
|
||||||
|
quiet_mode = "discard" # discard | toast (TALK_QUIET_MODE)
|
||||||
|
```
|
||||||
|
|
||||||
|
`talk status` zeigt den Gesetz-State: `rate-limit: 30s (high-burst
|
||||||
|
erlaubt), nächster slot …` und `ruhezeit: 18:00-08:00 (modus discard,
|
||||||
|
aktiv bis 08:00)`.
|
||||||
|
|
||||||
### Provider-Kette (Task 03)
|
### Provider-Kette (Task 03)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user