You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Deploy Bot 575408748c sw: cache v2 + network-first para shell e /content (evita tela branca com cache antigo apos deploy) 1 hour ago
app sw: cache v2 + network-first para shell e /content (evita tela branca com cache antigo apos deploy) 1 hour ago
audio regeneracao das variantes lentas restantes (487/487 com rate real -30%) 4 hours ago
content anotacoes de pronuncia: campo 'pronuncia' nas frases + tool tools/pronuncia.mjs + dicas no juiz 1 hour ago
server anotacoes de pronuncia: campo 'pronuncia' nas frases + tool tools/pronuncia.mjs + dicas no juiz 1 hour ago
tools anotacoes de pronuncia: campo 'pronuncia' nas frases + tool tools/pronuncia.mjs + dicas no juiz 1 hour ago
.env.example ASR local com faster-whisper (proxy LiteLLM nao aceita audio) + integracao judge/transcribe 5 hours ago
.gitignore Italiano Vivo MVP: spec, monorepo app+server, 18 missoes (bambino+viaggio), audio TTS edge-tts, judge 3-eixos CEFR, SRS, PWA offline 5 hours ago
README.md audio duplo real: variante lenta (TTS rate -30%) para todas as 487 frases + player tartaruga/coelho 4 hours ago
SPEC.md Italiano Vivo MVP: spec, monorepo app+server, 18 missoes (bambino+viaggio), audio TTS edge-tts, judge 3-eixos CEFR, SRS, PWA offline 5 hours ago
package-lock.json Italiano Vivo MVP: spec, monorepo app+server, 18 missoes (bambino+viaggio), audio TTS edge-tts, judge 3-eixos CEFR, SRS, PWA offline 5 hours ago
package.json Italiano Vivo MVP: spec, monorepo app+server, 18 missoes (bambino+viaggio), audio TTS edge-tts, judge 3-eixos CEFR, SRS, PWA offline 5 hours ago

README.md

🇮🇹 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

# Instalar dependências do monorepo
npm install

# Configurar variáveis de ambiente
# rode SEMPRE a partir de server/ (o dotenv le server/.env pelo cwd):
cd server && npm run dev   # ou node dist/index.js
# Edite server/.env com sua chave da API

Desenvolvimento

# Roda app + server simultaneamente
npm run dev

Build

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:

{
  "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:

{
  "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)

Dois ritmos de áudio 🐢🐇

Cada frase do catálogo possui dois arquivos de áudio gerados com velocidades de fala reais (não playbackRate):

  • <key>.mp3 — velocidade natural (padrão do TTS)
  • <key>--slow.mp3 — fala desacelerada em -30% via edge-tts --rate=-30%

Por que rate real e não playback?

Crianças aprendem ouvindo devagar de verdade: a prosódia, entonação e pausas naturais são preservadas quando o TTS gera a fala mais lenta desde a síntese. Playback acelerado/desacelerado distorce formantes e perde a cadência natural da língua.

Regerar áudios

# Gerar apenas variantes lentas (idempotente, pula >500 bytes)
node tools/gen-audio.mjs --only slow

# Gerar apenas variantes rápidas/naturais
node tools/gen-audio.mjs --only fast

# Gerar ambas (default)
node tools/gen-audio.mjs

No player da app, dois botões lado a lado: 🐢 lento e 🐇 naturale. Se o arquivo lento falhar (404), fallback silencioso para o natural.

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

  • Esqueleto do monorepo (app PWA + server LiteLLM)
  • Geração de conteúdo: Trilha Bambino (B1-B12) + Viaggio (V1-V6) = 18 missões
  • Pipeline de geração de áudio TTS (edge-tts) — ~479 arquivos mp3
  • Judge 3-eixos CEFR com tolerância estilo criança
  • 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.