|
|
# 🇮🇹 Italiano Vivo
|
|
|
|
|
|
App de italiano **situacional-first** com voz livre e scoring de inteligibilidade.
|
|
|
Aprenda como uma criança: errando mas se comunicando.
|
|
|
|
|
|
## Visão
|
|
|
|
|
|
- **Scoring em 3 eixos**: Sentido transmitido / Naturalidade / Precisão gramatical
|
|
|
- **Tolerância estilo criança**: erro gramatical que não impede entendimento = sucesso comunicativo
|
|
|
- **Voz livre**: grave, receba feedback estruturado, nunca "Errado"
|
|
|
- **Salvavidas (Salvavita)**: treine estratégias de comunicação quando a palavra some
|
|
|
- **SRS de chunks**: revisão espaçada de frases, não palavras isoladas
|
|
|
- **PWA offline**: catálogo, áudio e SRS funcionam sem conexão
|
|
|
- **CEFR can-do**: badges baseados em competências reais ("você consegue reclamar de um item com defeito")
|
|
|
|
|
|
## Como Rodar
|
|
|
|
|
|
### Pré-requisitos
|
|
|
- Node.js ≥ 20
|
|
|
- LiteLLM proxy rodando (ou configure `LITELLM_BASE_URL` para outro endpoint OpenAI-compatible)
|
|
|
|
|
|
### Instalação
|
|
|
|
|
|
```bash
|
|
|
# Instalar dependências do monorepo
|
|
|
npm install
|
|
|
|
|
|
# Configurar variáveis de ambiente
|
|
|
cp .env.example server/.env
|
|
|
# Edite server/.env com sua chave da API
|
|
|
```
|
|
|
|
|
|
### Desenvolvimento
|
|
|
|
|
|
```bash
|
|
|
# Roda app + server simultaneamente
|
|
|
npm run dev
|
|
|
```
|
|
|
|
|
|
- App: http://localhost:5173
|
|
|
- Server: http://localhost:8787
|
|
|
- Health check: http://localhost:8787/health
|
|
|
|
|
|
### Build
|
|
|
|
|
|
```bash
|
|
|
npm run build # Build server + app
|
|
|
npm run typecheck # Verificação de tipos
|
|
|
```
|
|
|
|
|
|
## Endpoints do Server
|
|
|
|
|
|
| Método | Rota | Descrição |
|
|
|
|--------|------|-----------|
|
|
|
| GET | `/health` | Status do servidor |
|
|
|
| POST | `/api/judge` | Avalia fala do aluno em 3 eixos (sentido, naturalidade, gramática) |
|
|
|
| POST | `/api/transcribe` | Transcreve áudio via modelo omni (ASR tolerante a sotaque) |
|
|
|
| GET | `/api/tts-proxy?text=...&voice=elsa\|diego` | Serve áudio TTS pré-gerado do cache local |
|
|
|
|
|
|
### POST /api/judge
|
|
|
|
|
|
**Entrada:**
|
|
|
```json
|
|
|
{
|
|
|
"missionId": "bambino-m01",
|
|
|
"scenario": "Al bar",
|
|
|
"learnerGoalPt": "Pedir um café",
|
|
|
"expectedIt": "Vorrei un caffè, per favore",
|
|
|
"utteranceText": "io volere caffe",
|
|
|
"transcriptRaw": "io volere caffe",
|
|
|
"audioBase64": "..."
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**Saída:**
|
|
|
```json
|
|
|
{
|
|
|
"meaning_conveyed": "full",
|
|
|
"naturalness": 45,
|
|
|
"natural_version": "Vorrei un caffè, per favore",
|
|
|
"grammar": [
|
|
|
{
|
|
|
"it_said": "io volere",
|
|
|
"problem_pt": "Infinitivo em vez de conjugação",
|
|
|
"fix_it": "vorrei / voglio",
|
|
|
"severity": "minor"
|
|
|
}
|
|
|
],
|
|
|
"next_move": {
|
|
|
"italian": "Certo! Un caffè. Lo vuole al banco o al tavolo?",
|
|
|
"portuguese": "Claro! Um café. Quer no balcão ou na mesa?"
|
|
|
},
|
|
|
"praise_or_push": "Quase lá! Um italiano diria: Vorrei un caffè, per favore"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## Estrutura de Pastas
|
|
|
|
|
|
```
|
|
|
cursoitaliano/
|
|
|
├── app/ # PWA React + Vite + TypeScript
|
|
|
│ ├── public/ # manifest.webmanifest, ícones
|
|
|
│ └── src/
|
|
|
│ ├── db/db.ts # IndexedDB via Dexie
|
|
|
│ ├── engine/srs.ts # SM-2 simplificado
|
|
|
│ ├── types/content.ts # Tipos do conteúdo JSON
|
|
|
│ ├── App.tsx # Componente principal
|
|
|
│ ├── main.tsx # Entry point
|
|
|
│ └── styles.css # Design system
|
|
|
├── server/ # Node + Express + TypeScript
|
|
|
│ ├── cache/audio/ # Áudios TTS pré-gerados (offline)
|
|
|
│ └── src/
|
|
|
│ ├── lib/llm.ts # Cliente LiteLLM singleton
|
|
|
│ ├── routes/
|
|
|
│ │ ├── judge.ts # Motor de feedback 3 eixos
|
|
|
│ │ ├── transcribe.ts # ASR via omni model
|
|
|
│ │ └── tts.ts # Proxy de áudio TTS
|
|
|
│ └── index.ts # Express app
|
|
|
├── content/ # JSONs versionados (gerados por pipeline)
|
|
|
├── audio/ # Assets de áudio por missão
|
|
|
├── SPEC.md # Especificação completa do produto
|
|
|
├── .env.example # Template de variáveis de ambiente
|
|
|
└── package.json # Monorepo root (npm workspaces)
|
|
|
```
|
|
|
|
|
|
## Modelos Utilizados
|
|
|
|
|
|
| Modelo | Uso | Variável |
|
|
|
|--------|-----|----------|
|
|
|
| qwen3.8-max | Juiz de feedback, geração de missões | `CHAT_MODEL` |
|
|
|
| qwen3.8-flash | Drills rápidos, fallback de quota | `FAST_MODEL` |
|
|
|
| qwen3-omni-flash | Transcrição de áudio (ASR) | `OMNI_MODEL` |
|
|
|
|
|
|
Todos acessados via LiteLLM proxy (`LITELLM_BASE_URL`).
|
|
|
|
|
|
## Roadmap
|
|
|
|
|
|
- [x] Esqueleto do monorepo (app PWA + server LiteLLM)
|
|
|
- [x] Geração de conteúdo: Trilha Bambino (B1-B12) + Viaggio (V1-V6) = 18 missões
|
|
|
- [x] Pipeline de geração de áudio TTS (edge-tts) — ~479 arquivos mp3
|
|
|
- [x] Judge 3-eixos CEFR com tolerância estilo criança
|
|
|
- [x] SRS de chunks com Dexie/IndexedDB
|
|
|
- [ ] Service Worker para modo offline completo
|
|
|
- [ ] Minijogo "descreva sem nomear" (circunlóquio)
|
|
|
- [ ] Modo carro/shadowing com medição de lag
|
|
|
- [ ] Trilhas restantes (Viaggio M7–M15, Vivere completa)
|
|
|
- [ ] Preparatório CILS/CELI B1 cidadania
|
|
|
- [ ] Wrapper Android (Capacitor)
|
|
|
- [ ] Calibração do judge com votos "isso estava certo"
|
|
|
|
|
|
## Licença
|
|
|
|
|
|
Privado — uso pessoal.
|