From ad397ae1bdfc5fc810256015a69310ed5b8c5d27 Mon Sep 17 00:00:00 2001 From: Oskar Kapala Date: Tue, 9 Jun 2026 14:57:13 +0200 Subject: [PATCH] docs: spec, lexicon, roadmap --- README.md | 21 ++++++++ docs/LEXICON.md | 74 ++++++++++++++++++++++++++++ docs/ROADMAP.md | 36 ++++++++++++++ docs/SPEC.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 docs/LEXICON.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/SPEC.md diff --git a/README.md b/README.md index e69de29..097e0ae 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,21 @@ +# ipin-vr + +Generator prostych, **gramatycznie poprawnych** polskich poleceń do terapii afazji +(np. *„Połóż jabłko na stole"*) wraz z wypowiadaniem ich przez Piper TTS. + +Docelowo narzędzie ma działać w trybie mieszanej rzeczywistości na Meta Quest 3 +(offline). **Etap 1** (ten kod) realizuje wyłącznie rdzeń: generowanie poleceń i ich +wypowiadanie na PC. Bez rozpoznawania mowy (STT), bez VR. + +## Dokumentacja +- [`docs/SPEC.md`](docs/SPEC.md) — specyfikacja techniczna etapu 1 (cel buildu). +- [`docs/LEXICON.md`](docs/LEXICON.md) — zweryfikowany leksykon (formy gramatyczne). +- [`docs/ROADMAP.md`](docs/ROADMAP.md) — etapy i ograniczenia platformy docelowej. + +## Zasada naczelna +Poprawność fleksji jest nienegocjowalna. Wszystkie formy słów pochodzą wyłącznie +z ręcznie zweryfikowanego leksykonu; model językowy (gdy włączony) wybiera tylko +identyfikatory elementów, a nie generuje polskiego tekstu. + +## Status +Etap 1 — w budowie. Implementacja powstaje ściśle według `docs/SPEC.md`. diff --git a/docs/LEXICON.md b/docs/LEXICON.md new file mode 100644 index 0000000..1956f7a --- /dev/null +++ b/docs/LEXICON.md @@ -0,0 +1,74 @@ +# LEXICON — zweryfikowany leksykon polski + +Te formy są **ręcznie zweryfikowane** i stanowią jedyne źródło powierzchniowych form +słów. Implementacja przepisuje je **dokładnie** (z polskimi znakami), bez zmian. +Rozszerzanie list — patrz sekcja na końcu. + +## OBJECTS (przedmioty) +`gender`: `m` = męski nieżywotny, `f` = żeński, `n` = nijaki. +`acc` = forma w **bierniku** (dopełnienie czasownika: „połóż **acc**"). + +| id | nom (mianownik) | acc (biernik) | gender | +|---|---|---|---| +| jablko | jabłko | jabłko | n | +| kubek | kubek | kubek | m | +| lyzka | łyżka | łyżkę | f | +| ksiazka | książka | książkę | f | +| dlugopis | długopis | długopis | m | +| klucz | klucz | klucz | m | +| pilka | piłka | piłkę | f | +| butelka | butelka | butelkę | f | +| talerz | talerz | talerz | m | +| banan | banan | banan | m | + +## COLORS (kolory) +Forma przymiotnika w **bierniku**, zależna od rodzaju rzeczownika. + +| id | m | f | n | +|---|---|---|---| +| czerwony | czerwony | czerwoną | czerwone | +| zielony | zielony | zieloną | zielone | +| niebieski | niebieski | niebieską | niebieskie | +| zolty | żółty | żółtą | żółte | +| bialy | biały | białą | białe | +| czarny | czarny | czarną | czarne | + +## LOCATIONS (miejsca) +Gotowa fraza „**na** + miejscownik". Przechowywana w całości — bez odmiany w kodzie. + +| id | phrase | +|---|---| +| stol | na stole | +| krzeslo | na krześle | +| polka | na półce | +| podloga | na podłodze | +| biurko | na biurku | +| parapet | na parapecie | + +## VERBS (czasowniki) +Tryb rozkazujący, biorą biernik, neutralne semantycznie dla powyższych obiektów. + +| id | forma (wielką literą) | +|---|---| +| poloz | Połóż | +| umiesc | Umieść | + +W kolejnych klauzulach czasownik pisany małą literą (np. `umieść`). + +## Notatki gramatyczne +- Po czasownikach `połóż`/`umieść` dopełnienie jest w **bierniku**. + - męski nieżywotny i nijaki: biernik = mianownik (`kubek`, `jabłko`), + - żeński zwykle `-a → -ę` (`łyżka → łyżkę`). +- Przymiotnik **zgadza się** z rzeczownikiem co do rodzaju i przypadka + (`czerwone jabłko`, `czerwoną łyżkę`, `czerwony kubek`). +- Miejsce: konstrukcja `na` + **miejscownik** (`na stole`, `na krześle`). + +## Rozszerzanie +Dodając wpis, podaj poprawne formy ręcznie (nie polegaj na automatycznej odmianie): +- OBJECTS: `acc` i `gender`, +- COLORS: trzy formy biernika (m/f/n), +- LOCATIONS: pełna fraza „na + miejscownik", +- VERBS: forma rozkazująca biorąca biernik, pasująca do wszystkich obiektów. + +Zalecenie: dobór słów i progresję trudności powinien zatwierdzić logopeda lub +neurologopeda. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..0744473 --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,36 @@ +# ROADMAP + +## Etap 1 — Rdzeń na PC (obecny) +Generowanie poprawnych poleceń + Piper TTS, CLI, testy. Bez STT, bez VR. +Szczegóły: `docs/SPEC.md`. + +## Etap 2 — Tło mieszanej rzeczywistości (Meta Quest 3) +Passthrough + proste obiekty (np. stół, jabłko). Generator z etapu 1 przenoszony +bez zmian (sama logika treści jest niezależna od platformy). UI: natywny OpenXR. + +## Etap 3 — Interakcja i ocena +STT (rozpoznanie/repetycja), ocena wykonania polecenia (czy pacjent wskazał/położył +właściwy obiekt), sesje i statystyki postępu. + +## Ograniczenia platformy docelowej (do etapu 2/3) +Wartości ustalone wcześniej; do zweryfikowania na sprzęcie. + +| Parametr | Wartość | +|---|---| +| Limit pamięci aplikacji (Quest 3, PSS) | ~5,75 GiB (twardy, narzucony przez system) | +| CPU | Snapdragon XR2 Gen 2: 2 rdzenie performance + 4 efficiency (brak „prime") | +| Maks. CPU level | 6 | +| Tryb dual-core | niewspierany na Quest 3 | +| GPU | Adreno 740 | +| Inferencja LLM | CPU-only (backend OpenCL llama.cpp nie jest zwalidowany na Adreno 740; GPU obciążony renderowaniem) | +| Model LLM (cel) | Qwen2.5 1.5B Q4 (GGUF, llama.cpp) | +| STT (cel) | whisper.cpp Tiny | +| TTS | Piper (głos polski) | +| Tryb | 100% offline | + +Konsekwencja dla treści: leksykonowo-szablonowe generowanie z etapu 1 jest tu +przewagą — gwarantuje poprawność niezależnie od rozmiaru modelu na urządzeniu. + +## Dystrybucja +Cel: sideload / App Lab. Pełne wydanie w sklepie nie jest zakładane (stałe użycie +mikrofonu w etapie 3, lokalna inferencja, charakter asystujący). diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..e5d5790 --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,127 @@ +# SPEC — Etap 1 (PC, offline) + +Specyfikacja jest **wiążącym celem buildu**. Implementacja ma być z nią zgodna. + +## 1. Cel i zakres +Generować proste polskie polecenia typu *„Połóż jabłko na stole"* i opcjonalnie je +wypowiadać (Piper TTS). Typ ćwiczenia: **wykonywanie poleceń** (rozumienie ze słuchu). + +W zakresie etapu 1: +- generowanie poleceń (losowo lub przez LLM), +- wypowiadanie ich (TTS, opcjonalne), +- interfejs CLI. + +Poza zakresem etapu 1: STT, ocena wykonania, VR/MR, GUI. + +## 2. Zasady naczelne +1. **Poprawność fleksji jest krytyczna.** Wszystkie powierzchniowe formy słów + pochodzą wyłącznie z `docs/LEXICON.md`. Nie wolno generować ani modyfikować odmian. +2. **LLM zwraca tylko identyfikatory**, nigdy polski tekst. Tekst składa kod + z leksykonu. Dzięki temu mały model nie może zepsuć gramatyki. +3. **Offline.** Brak zależności sieciowych w czasie działania poza opcjonalnym, + lokalnym serwerem LLM (localhost). +4. **Defensywność TTS.** Brak Pipera lub głosu nie przerywa działania — demo działa + wtedy w trybie tekstowym. + +## 3. Stos i układ plików +Python 3.10+. Rdzeń i klient LLM korzystają wyłącznie z biblioteki standardowej +(LLM przez `urllib`, bez `requests`). Piper jest opcjonalny. + +``` +ipin-vr/ + README.md + requirements.txt # tylko opcjonalne: piper-tts; rdzeń = stdlib + .gitignore # venv, __pycache__, *.wav, *.onnx, *.onnx.json + docs/ + ipin_vr/ + __init__.py + __main__.py # uruchamia cli.main() + lexicon.py # dane z docs/LEXICON.md (przepisane DOKŁADNIE) + generator.py # Clause, Command, render(), random_command() + llm.py # generator oparty na llama.cpp (opcjonalny) + tts.py # Piper przez subprocess (opcjonalny, defensywny) + cli.py # argparse + pętla główna + tests/ + test_generator.py +``` +Uruchamianie: `python -m ipin_vr [opcje]`. + +## 4. Model danych +- `Clause(verb_id, object_id, location_id, color_id: str | None)` +- `Command(clauses: list[Clause], level: int)` + +Identyfikatory muszą istnieć w leksykonie; walidacja przy tworzeniu z danych LLM. + +## 5. Reguły renderowania +Klauzula = `Czasownik` + (`przymiotnik[rodzaj obiektu]` + spacja, jeśli kolor) + +`obiekt.acc` + spacja + `miejsce.fraza`. + +- Czasownik w **pierwszej** klauzuli pisany wielką literą; w kolejnych — małą. +- Klauzule łączone separatorem `" i "`. +- Całość zakończona kropką. +- Poziom 1: jedna klauzula, bez koloru. +- Poziom 2: jedna klauzula, z kolorem. +- Poziom 3: dwie klauzule o **różnych** obiektach; kolor opcjonalny w każdej. + +Przykłady poprawnych wyników: +- `Połóż jabłko na stole.` +- `Połóż żółtą piłkę na parapecie.` +- `Umieść niebieskie jabłko na stole.` +- `Połóż zieloną książkę na półce i umieść klucz na biurku.` + +## 6. Generator losowy +`random_command(level)` losuje czasownik, obiekt, miejsce (oraz kolor zależnie od +poziomu). Dla poziomu 3 gwarantuje różne obiekty w obu klauzulach. + +## 7. Generator LLM (opcjonalny) +Klient POST na endpoint zgodny z OpenAI (`/v1/chat/completions`, +domyślnie `http://localhost:8080/v1/chat/completions`). + +System prompt wymusza zwrot **wyłącznie** obiektu JSON: +```json +{"clauses":[{"verb":"","object":"","location":"","color":""}],"level":1} +``` +Przekaż modelowi dozwolone listy identyfikatorów (z leksykonu). Po odebraniu: +1. wyciągnij JSON, sparsuj, +2. **zwaliduj** każdy identyfikator względem leksykonu, +3. renderuj z leksykonu. + +Każdy błąd (timeout, brak serwera, niepoprawny JSON, nieznany identyfikator) ⇒ +**fallback** na `random_command(level)`. Tekst z LLM nigdy nie trafia na wyjście. + +## 8. TTS (opcjonalny, defensywny) +Piper przez `subprocess`: `piper --model --output_file `, tekst na stdin. +Odtwarzanie zależnie od OS: `aplay` (Linux), `afplay` (macOS), `winsound` (Windows). +Brak Pipera/głosu/odtwarzacza ⇒ wypisz `[TTS off: ]` i kontynuuj. + +## 9. CLI +| Flaga | Domyślnie | Opis | +|---|---|---| +| `--level {1,2,3}` | 1 | poziom trudności | +| `--count N` | 5 | liczba poleceń | +| `--delay S` | 4.0 | pauza między poleceniami (s) | +| `--voice PATH` | — | ścieżka do modelu Piper `.onnx` | +| `--no-audio` | — | wyłącza TTS | +| `--llm` | — | użyj LLM do wyboru poleceń | +| `--llm-endpoint URL` | localhost:8080 | endpoint llama.cpp | +| `--llm-model NAME` | `local` | nazwa modelu | +| `--seed N` | — | deterministyczny losowy generator | + +Każde polecenie wypisywane na stdout (numerowane) i — jeśli włączone audio — wypowiadane. + +## 10. Testy (pytest) +`tests/test_generator.py` sprawdza `render()` dla ustalonych speców względem +oczekiwanych stringów. Pokrycie obowiązkowe: +- kolor + rzeczownik dla każdego rodzaju (m, f, n), +- poprawny miejscownik miejsca, +- łączenie dwóch klauzul (poziom 3), +- kapitalizacja: pierwsza klauzula wielką, druga małą literą. + +## 11. Zależności +Rdzeń, generator i klient LLM: tylko biblioteka standardowa. `requirements.txt` +zawiera jedynie opcjonalne `piper-tts`. README opisuje pobranie głosu polskiego. + +## 12. Definition of done +- `pytest` przechodzi w całości. +- `python -m ipin_vr --level 3 --count 6 --no-audio` wypisuje 6 poprawnych poleceń. +- README zawiera działający szybki start (tryb bez audio) i instrukcje audio/LLM.