feat: token count implement
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# 🎤 Guía de Audio para Preguntas
|
||||
|
||||
## ¿Por qué agregar audio?
|
||||
|
||||
El audio en las preguntas ayuda a:
|
||||
- ✅ Estudiantes con diferentes estilos de aprendizaje
|
||||
- ✅ Práctica de listening comprehension
|
||||
- ✅ Accesibilidad para estudiantes con dificultades visuales
|
||||
- ✅ Pronunciación correcta en idiomas
|
||||
|
||||
## 📱 Cómo Grabar Audio
|
||||
|
||||
### Opción 1: Con tu Celular (Recomendado)
|
||||
|
||||
1. **Abre la app de grabadora** en tu celular
|
||||
2. **Graba la pregunta** en un lugar silencioso
|
||||
3. **Envía el archivo** a tu computadora (email, WhatsApp, cable USB)
|
||||
|
||||
**Consejos:**
|
||||
- Habla claro y a velocidad normal
|
||||
- Evita ruido de fondo
|
||||
- Grabación corta: 5-30 segundos ideal
|
||||
|
||||
### Opción 2: Con tu Computadora
|
||||
|
||||
**Windows:**
|
||||
- Usa "Grabadora de voz" (incluida en Windows)
|
||||
- O usa [Online Voice Recorder](https://online-voice-recorder.com/)
|
||||
|
||||
**Mac:**
|
||||
- Usa QuickTime Player → Archivo → Nueva grabación de audio
|
||||
- O usa GarageBand
|
||||
|
||||
**Linux:**
|
||||
- Usa Audacity: `sudo apt install audacity`
|
||||
- O usa GNOME Sound Recorder
|
||||
|
||||
### Opción 3: Usando el Editor de Preguntas
|
||||
|
||||
1. Ve a `/question-bank` en Studio
|
||||
2. Crea o edita una pregunta
|
||||
3. En la sección "Audio de la Pregunta":
|
||||
- Click en "Subir archivo de audio"
|
||||
- Selecciona tu archivo MP3/WAV/OGG
|
||||
- Preview del audio
|
||||
- Guarda la pregunta
|
||||
|
||||
## 📊 Formatos Soportados
|
||||
|
||||
| Formato | Tamaño Máx | Calidad Recomendada |
|
||||
|---------|------------|---------------------|
|
||||
| MP3 | 10 MB | 128 kbps |
|
||||
| WAV | 10 MB | 44.1 kHz, 16-bit |
|
||||
| OGG | 10 MB | 128 kbps |
|
||||
|
||||
## 💡 Mejores Prácticas
|
||||
|
||||
### Para Preguntas de Listening
|
||||
|
||||
```
|
||||
📝 Texto: "What color is the sky?"
|
||||
🎤 Audio: [Grabación clara: "What color is the sky?"]
|
||||
```
|
||||
|
||||
### Para Preguntas de Pronunciación
|
||||
|
||||
```
|
||||
📝 Texto: "Repeat: The quick brown fox"
|
||||
🎤 Audio: [Pronunciación modelo]
|
||||
```
|
||||
|
||||
### Para Instrucciones
|
||||
|
||||
```
|
||||
📝 Texto: "Listen and choose the correct answer"
|
||||
🎤 Audio: [Instrucción en inglés]
|
||||
```
|
||||
|
||||
## 🚫 Errores Comunes a Evitar
|
||||
|
||||
- ❌ Audio muy largo (> 1 minuto)
|
||||
- ❌ Ruido de fondo (tráfico, ventilador, etc.)
|
||||
- ❌ Volumen muy bajo o muy alto
|
||||
- ❌ Hablar muy rápido
|
||||
- ❌ Múltiples personas hablando
|
||||
|
||||
## ✅ Checklist de Calidad
|
||||
|
||||
Antes de subir tu audio:
|
||||
|
||||
- [ ] Se escucha claro y sin ruido
|
||||
- [ ] Duración apropiada (< 1 min)
|
||||
- [ ] Volumen consistente
|
||||
- [ ] Pronunciación correcta
|
||||
- [ ] Formato MP3/WAV/OGG
|
||||
- [ ] Tamaño < 10MB
|
||||
|
||||
## 📈 Ejemplo de Flujo de Trabajo
|
||||
|
||||
### 1. Preparación
|
||||
```
|
||||
1. Abre tu banco de preguntas en Studio
|
||||
2. Ten lista la lista de preguntas
|
||||
3. Prepara tu celular o micrófono
|
||||
```
|
||||
|
||||
### 2. Grabación
|
||||
```
|
||||
1. Graba cada pregunta por separado
|
||||
2. Nombra los archivos claramente:
|
||||
- pregunta_001.mp3
|
||||
- pregunta_002.mp3
|
||||
- etc.
|
||||
```
|
||||
|
||||
### 3. Subida
|
||||
```
|
||||
1. Ve a cada pregunta en Studio
|
||||
2. Sube el archivo de audio correspondiente
|
||||
3. Preview para verificar
|
||||
4. Guarda
|
||||
```
|
||||
|
||||
## 🎯 Casos de Uso por Habilidad
|
||||
|
||||
### 📖 Reading
|
||||
- Audio de la pregunta para estudiantes con dificultades visuales
|
||||
- Narración de pasajes cortos
|
||||
|
||||
### 🎧 Listening
|
||||
- Diálogos para comprensión auditiva
|
||||
- Instrucciones en el idioma objetivo
|
||||
|
||||
### 🗣️ Speaking
|
||||
- Modelos de pronunciación
|
||||
- Ejemplos de conversación
|
||||
|
||||
### ✍️ Writing
|
||||
- Dictados
|
||||
- Instrucciones de escritura
|
||||
|
||||
## 🔧 Solución de Problemas
|
||||
|
||||
### "El audio no se reproduce"
|
||||
- Verifica el formato (MP3/WAV/OGG)
|
||||
- Verifica el tamaño (< 10MB)
|
||||
- Intenta subir de nuevo
|
||||
|
||||
### "El audio se escucha mal"
|
||||
- Re-graba en un lugar más silencioso
|
||||
- Usa un micrófono externo si es posible
|
||||
- Ajusta el volumen de grabación
|
||||
|
||||
### "El archivo es muy grande"
|
||||
- Comprime a MP3 con menor bitrate (128 kbps)
|
||||
- Corta silencios al inicio/final
|
||||
- Usa formato OGG (mejor compresión)
|
||||
|
||||
## 📞 Soporte
|
||||
|
||||
¿Problemas con el audio?
|
||||
- Revisa esta guía completa
|
||||
- Contacta al administrador del sistema
|
||||
- Revisa los logs del servidor
|
||||
|
||||
---
|
||||
|
||||
**Recuerda:** El audio es OPCIONAL pero MUY VALIOSO para la experiencia de aprendizaje.
|
||||
@@ -0,0 +1,206 @@
|
||||
# Instalación Manual de Bark TTS en t-800
|
||||
|
||||
## Opción A: Instalación Automática (Recomendada)
|
||||
|
||||
```bash
|
||||
# 1. Copiar script a t-800
|
||||
scp scripts/install_bark_tts.sh juan@t-800:/tmp/install_bark_tts.sh
|
||||
|
||||
# 2. Conectarse a t-800
|
||||
ssh juan@t-800
|
||||
|
||||
# 3. Ejecutar instalación
|
||||
chmod +x /tmp/install_bark_tts.sh
|
||||
sudo /tmp/install_bark_tts.sh
|
||||
|
||||
# 4. Verificar instalación
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
|
||||
## Opción B: Instalación Paso a Paso
|
||||
|
||||
```bash
|
||||
# Conectarse a t-800
|
||||
ssh juan@t-800
|
||||
# Contraseña: apoca11
|
||||
|
||||
# Actualizar sistema
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python3 python3-pip python3-venv git ffmpeg curl
|
||||
|
||||
# Crear directorio
|
||||
sudo mkdir -p /opt/bark
|
||||
sudo chown juan:juan /opt/bark
|
||||
cd /opt/bark
|
||||
|
||||
# Clonar Bark
|
||||
git clone https://github.com/suno-ai/bark.git
|
||||
cd bark
|
||||
|
||||
# Crear entorno virtual
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Instalar dependencias
|
||||
pip install --upgrade pip
|
||||
pip install -e .
|
||||
pip install fastapi uvicorn[standard] python-multipart numpy scipy
|
||||
|
||||
# Crear archivo de API
|
||||
cat > bark_api.py << 'PYEOF'
|
||||
from fastapi import FastAPI, HTTPException, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from bark import SAMPLE_RATE, generate_audio, preload_models
|
||||
from scipy.io.wavfile import write as write_wav
|
||||
import numpy as np
|
||||
import io
|
||||
|
||||
app = FastAPI(title="Bark TTS API", version="1.0.0")
|
||||
|
||||
print("Preloading Bark models...")
|
||||
preload_models()
|
||||
print("Models loaded!")
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "healthy", "service": "bark-tts"}
|
||||
|
||||
@app.get("/api/voices")
|
||||
async def list_voices():
|
||||
return {
|
||||
"voices": [
|
||||
{"id": "v2/en_speaker_0", "name": "English Speaker 0", "language": "en"},
|
||||
{"id": "v2/en_speaker_1", "name": "English Speaker 1", "language": "en"},
|
||||
{"id": "v2/en_speaker_6", "name": "English Speaker 6", "language": "en"},
|
||||
{"id": "v2/es_speaker_0", "name": "Spanish Speaker 0", "language": "es"},
|
||||
{"id": "v2/es_speaker_1", "name": "Spanish Speaker 1", "language": "es"},
|
||||
{"id": "v2/es_speaker_3", "name": "Spanish Speaker 3", "language": "es"},
|
||||
]
|
||||
}
|
||||
|
||||
@app.post("/api/generate")
|
||||
async def generate_speech(
|
||||
text: str = Query(..., min_length=1, max_length=500),
|
||||
voice: str = Query(default="v2/en_speaker_1"),
|
||||
speed: float = Query(default=1.0, ge=0.5, le=2.0),
|
||||
output_format: str = Query(default="wav", regex="^(mp3|wav|ogg)$")
|
||||
):
|
||||
try:
|
||||
audio_array = generate_audio(text, history_prompt=voice)
|
||||
|
||||
if speed != 1.0:
|
||||
new_length = int(len(audio_array) / speed)
|
||||
audio_array = audio_array[:new_length]
|
||||
|
||||
audio_buffer = io.BytesIO()
|
||||
write_wav(audio_buffer, SAMPLE_RATE, audio_array)
|
||||
audio_buffer.seek(0)
|
||||
|
||||
return StreamingResponse(
|
||||
audio_buffer,
|
||||
media_type="audio/wav",
|
||||
headers={"Content-Disposition": f"attachment; filename=speech.wav"}
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
PYEOF
|
||||
|
||||
# Crear servicio systemd
|
||||
cat > /tmp/bark-tts.service << EOF
|
||||
[Unit]
|
||||
Description=Bark TTS API Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=juan
|
||||
Group=juan
|
||||
WorkingDirectory=/opt/bark/bark
|
||||
Environment="PATH=/opt/bark/bark/venv/bin"
|
||||
ExecStart=/opt/bark/bark/venv/bin/uvicorn bark_api:app --host 0.0.0.0 --port 8000 --workers 1
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
MemoryMax=4G
|
||||
MemoryHigh=3G
|
||||
CPUQuota=80%
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo mv /tmp/bark-tts.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable bark-tts
|
||||
sudo systemctl start bark-tts
|
||||
|
||||
# Verificar
|
||||
sudo systemctl status bark-tts
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
|
||||
## Prueba de Funcionamiento
|
||||
|
||||
```bash
|
||||
# Test básico
|
||||
curl "http://localhost:8000/api/generate?text=Hello%20World&voice=v2/en_speaker_1" -o test.wav
|
||||
|
||||
# Test desde OpenCCB
|
||||
curl http://t-800:8000/health
|
||||
|
||||
# Ver logs
|
||||
sudo journalctl -u bark-tts -f
|
||||
```
|
||||
|
||||
## Configuración en OpenCCB
|
||||
|
||||
Agregar a `.env`:
|
||||
```bash
|
||||
BARK_API_URL=http://t-800:8000
|
||||
# O para producción:
|
||||
# BARK_API_URL=http://t-800.norteamericano.cl:8000
|
||||
```
|
||||
|
||||
## Solución de Problemas
|
||||
|
||||
### Error: "Out of Memory"
|
||||
```bash
|
||||
# Reducir límite de memoria en systemd
|
||||
sudo systemctl edit bark-tts
|
||||
# Agregar:
|
||||
# [Service]
|
||||
# MemoryMax=2G
|
||||
```
|
||||
|
||||
### Error: "Model not found"
|
||||
```bash
|
||||
# Reinstalar modelos
|
||||
cd /opt/bark/bark
|
||||
source venv/bin/activate
|
||||
python -c "from bark import preload_models; preload_models()"
|
||||
```
|
||||
|
||||
### Servicio no inicia
|
||||
```bash
|
||||
# Ver logs
|
||||
sudo journalctl -u bark-tts -n 50
|
||||
|
||||
# Reiniciar
|
||||
sudo systemctl restart bark-tts
|
||||
```
|
||||
|
||||
## URLs de Acceso
|
||||
|
||||
- **Health**: http://t-800:8000/health
|
||||
- **Voices**: http://t-800:8000/api/voices
|
||||
- **Generate**: http://t-800:8000/api/generate?text=Hello&voice=v2/en_speaker_1
|
||||
|
||||
## Producción (t-800.norteamericano.cl)
|
||||
|
||||
Para producción, asegurar que:
|
||||
1. El puerto 8000 esté abierto en el firewall
|
||||
2. El dominio t-800.norteamericano.cl apunte a la IP correcta
|
||||
3. Usar BARK_API_URL=http://t-800.norteamericano.cl:8000
|
||||
@@ -0,0 +1,221 @@
|
||||
# Bark TTS Integration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
OpenCCB now integrates with **Suno AI's Bark** text-to-speech system for generating audio versions of questions. This allows students to listen to questions instead of just reading them, improving accessibility and supporting different learning styles.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ HTTP ┌─────────────────┐
|
||||
│ OpenCCB CMS │ ────────────> │ Bark TTS API │
|
||||
│ (PostgreSQL) │ <──────────── │ (Server t-800)│
|
||||
│ │ Audio │ │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## Deployment to t-800 Server
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- SSH access to t-800 server
|
||||
- At least 8GB RAM recommended (Bark loads large models)
|
||||
- 10GB free disk space
|
||||
- Python 3.8+
|
||||
- GPU optional (CUDA support for faster generation)
|
||||
|
||||
### Quick Deploy
|
||||
|
||||
```bash
|
||||
# From your local machine
|
||||
cd /home/juan/dev/openccb
|
||||
./scripts/deploy_to_t800.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
1. SSH into t-800
|
||||
2. Install Python dependencies
|
||||
3. Clone Bark repository
|
||||
4. Set up systemd service
|
||||
5. Start the API server
|
||||
|
||||
### Manual Deploy
|
||||
|
||||
```bash
|
||||
# SSH into t-800
|
||||
ssh juan@t-800
|
||||
|
||||
# Run installation script
|
||||
wget https://raw.githubusercontent.com/suno-ai/bark/main/scripts/install.sh
|
||||
sudo bash install.sh
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Once deployed, Bark API is available at `http://t-800:8000`
|
||||
|
||||
### Health Check
|
||||
```bash
|
||||
curl http://t-800:8000/health
|
||||
```
|
||||
|
||||
### List Available Voices
|
||||
```bash
|
||||
curl http://t-800:8000/api/voices
|
||||
```
|
||||
|
||||
### Generate Speech
|
||||
```bash
|
||||
# Basic usage
|
||||
curl "http://t-800:8000/api/generate?text=What%20color%20is%20the%20sky%3F" \
|
||||
-o question.wav
|
||||
|
||||
# With specific voice and speed
|
||||
curl "http://t-800:8000/api/generate?text=Hello%20World&voice=v2/en_speaker_6&speed=1.2" \
|
||||
-o greeting.wav
|
||||
|
||||
# Spanish voice
|
||||
curl "http://t-800:8000/api/generate?text=Hola%20mundo&voice=v2/es_speaker_0" \
|
||||
-o saludo.wav
|
||||
```
|
||||
|
||||
## Available Voices
|
||||
|
||||
### English Voices
|
||||
- `v2/en_speaker_0` through `v2/en_speaker_9`
|
||||
|
||||
### Spanish Voices
|
||||
- `v2/es_speaker_0` through `v2/es_speaker_9`
|
||||
|
||||
## Integration with OpenCCB
|
||||
|
||||
### Generate Audio for a Question
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST "http://localhost:3001/question-bank/{question_id}/generate-audio" \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "What color is the sky?",
|
||||
"voice": "v2/en_speaker_1",
|
||||
"speed": 1.0
|
||||
}'
|
||||
```
|
||||
|
||||
### Automatic Audio Generation
|
||||
|
||||
When creating a question:
|
||||
|
||||
```json
|
||||
POST /question-bank
|
||||
{
|
||||
"question_text": "What is the capital of France?",
|
||||
"question_type": "multiple-choice",
|
||||
"options": ["Paris", "London", "Berlin", "Madrid"],
|
||||
"correct_answer": 0,
|
||||
"explanation": "Paris is the capital of France.",
|
||||
"generate_audio": true // Triggers async audio generation
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Add to your `.env` file:
|
||||
|
||||
```bash
|
||||
# Bark TTS API URL
|
||||
BARK_API_URL=http://t-800:8000
|
||||
|
||||
# Optional: Default voice for audio generation
|
||||
BARK_DEFAULT_VOICE=v2/en_speaker_1
|
||||
|
||||
# Optional: Default speed
|
||||
BARK_DEFAULT_SPEED=1.0
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Model Preloading
|
||||
|
||||
Bark preloads models on startup (takes ~30 seconds). The systemd service handles this automatically.
|
||||
|
||||
### Memory Management
|
||||
|
||||
The systemd service includes memory limits:
|
||||
```ini
|
||||
MemoryMax=4G
|
||||
MemoryHigh=3G
|
||||
```
|
||||
|
||||
Adjust based on your server's capacity.
|
||||
|
||||
### Batch Generation
|
||||
|
||||
For importing many questions:
|
||||
|
||||
```bash
|
||||
# Generate audio for multiple questions
|
||||
curl "http://t-800:8000/api/generate/batch?texts=Question%201&texts=Question%202&voice=v2/en_speaker_1"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service Not Starting
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sudo systemctl status bark-tts
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u bark-tts -f
|
||||
|
||||
# Restart service
|
||||
sudo systemctl restart bark-tts
|
||||
```
|
||||
|
||||
### Out of Memory
|
||||
|
||||
If Bark crashes due to memory:
|
||||
1. Reduce `MemoryMax` in systemd service
|
||||
2. Use smaller models: `suno/bark-small`
|
||||
3. Process questions one at a time
|
||||
|
||||
### Slow Generation
|
||||
|
||||
- GPU acceleration: Install CUDA-enabled PyTorch
|
||||
- Reduce audio quality settings
|
||||
- Use shorter text segments
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Test English voice
|
||||
curl "http://t-800:8000/api/generate?text=The%20quick%20brown%20fox&voice=v2/en_speaker_1" | play -
|
||||
|
||||
# Test Spanish voice
|
||||
curl "http://t-800:8000/api/generate?text=El%20rápido%20zorro%20marrón&voice=v2/es_speaker_0" | play -
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Bark API runs on internal network only
|
||||
- No authentication required (assumes trusted network)
|
||||
- Rate limiting handled by OpenCCB
|
||||
- Audio files stored in `uploads/audio/` directory
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] Add authentication to Bark API
|
||||
- [ ] Support for custom voice cloning
|
||||
- [ ] Audio preprocessing (noise reduction, normalization)
|
||||
- [ ] Caching layer for repeated requests
|
||||
- [ ] WebSocket support for streaming audio
|
||||
|
||||
## References
|
||||
|
||||
- [Bark GitHub](https://github.com/suno-ai/bark)
|
||||
- [Bark Hugging Face](https://huggingface.co/suno/bark)
|
||||
- [OpenCCB Question Bank Documentation](../docs/question-bank.md)
|
||||
@@ -0,0 +1,145 @@
|
||||
# 📊 Plantilla para Importar Preguntas desde Excel
|
||||
|
||||
## 📁 Estructura del Archivo Excel
|
||||
|
||||
### Columnas Requeridas
|
||||
|
||||
| Columna | Nombre | Descripción | Ejemplo |
|
||||
|---------|--------|-------------|---------|
|
||||
| A | question_text | Texto de la pregunta | What color is the sky? |
|
||||
| B | question_type | Tipo de pregunta | multiple-choice |
|
||||
| C | options | Opciones (JSON o comma-separated) | ["Blue","Green","Red","Yellow"] |
|
||||
| D | correct_answer | Respuesta correcta (índice o JSON) | 0 |
|
||||
| E | explanation | Explicación (opcional) | The sky appears blue due to Rayleigh scattering |
|
||||
| F | difficulty | Dificultad | easy |
|
||||
| G | tags | Tags (comma-separated) | science,colors,nature |
|
||||
|
||||
### Tipos de Pregunta Válidos
|
||||
|
||||
```
|
||||
multiple-choice
|
||||
true-false
|
||||
short-answer
|
||||
essay
|
||||
matching
|
||||
ordering
|
||||
fill-in-the-blanks
|
||||
```
|
||||
|
||||
### Ejemplo de Archivo Excel
|
||||
|
||||
| question_text | question_type | options | correct_answer | explanation | difficulty | tags |
|
||||
|---------------|---------------|---------|----------------|-------------|------------|------|
|
||||
| What color is the sky? | multiple-choice | ["Blue","Green","Red","Yellow"] | 0 | The sky appears blue due to Rayleigh scattering | easy | science,colors |
|
||||
| The sun rises in the east. | true-false | ["Verdadero","Falso"] | 0 | The sun always rises in the east | easy | geography |
|
||||
| What is the past tense of "go"? | short-answer | | went | Go is an irregular verb | medium | grammar,verbs |
|
||||
| Complete: She ___ to school | fill-in-the-blanks | ["go","goes","going","went"] | 1 | Third person singular present | medium | grammar |
|
||||
|
||||
## 📤 Cómo Importar
|
||||
|
||||
### Paso 1: Preparar Archivo Excel
|
||||
|
||||
1. Abre Excel o Google Sheets
|
||||
2. Crea las columnas como se muestra arriba
|
||||
3. Completa con tus preguntas
|
||||
4. Guarda como `.xlsx`
|
||||
|
||||
### Paso 2: Subir a OpenCCB
|
||||
|
||||
1. Ve a `/question-bank`
|
||||
2. Click en **"Importar desde Excel"**
|
||||
3. Selecciona tu archivo `.xlsx`
|
||||
4. Click en **"Importar"**
|
||||
|
||||
### Paso 3: Verificar
|
||||
|
||||
1. Revisa el resultado:
|
||||
- ✅ Importadas: X preguntas
|
||||
- ⚠️ Saltadas: Y preguntas (datos inválidos)
|
||||
- ❌ Errores: Z errores
|
||||
|
||||
2. Ve al Banco de Preguntas
|
||||
3. Filtra por source: `imported-csv`
|
||||
|
||||
## 💡 Consejos
|
||||
|
||||
### Opciones Válidas
|
||||
|
||||
**Formato JSON (recomendado):**
|
||||
```
|
||||
["Opción A","Opción B","Opción C","Opción D"]
|
||||
```
|
||||
|
||||
**Formato comma-separated:**
|
||||
```
|
||||
Opción A, Opción B, Opción C, Opción D
|
||||
```
|
||||
|
||||
### Respuesta Correcta
|
||||
|
||||
**Como índice (recomendado para multiple-choice):**
|
||||
```
|
||||
0 ← Primera opción
|
||||
1 ← Segunda opción
|
||||
2 ← Tercera opción
|
||||
```
|
||||
|
||||
**Como array (para multiple-select):**
|
||||
```
|
||||
[0, 2] ← Primera y tercera opción
|
||||
```
|
||||
|
||||
### Tags
|
||||
|
||||
**Separados por comas:**
|
||||
```
|
||||
science,colors,nature
|
||||
grammar,verbs,past-tense
|
||||
```
|
||||
|
||||
## ⚠️ Errores Comunes
|
||||
|
||||
### ❌ Columnas en otro idioma
|
||||
```
|
||||
texto_pregunta ← Incorrecto
|
||||
question_text ← Correcto
|
||||
```
|
||||
|
||||
### ❌ Tipo de pregunta inválido
|
||||
```
|
||||
opcion-multiple ← Incorrecto
|
||||
multiple-choice ← Correcto
|
||||
```
|
||||
|
||||
### ❌ Formato de opciones incorrecto
|
||||
```
|
||||
Opción A; Opción B; Opción C ← Incorrecto (punto y coma)
|
||||
["A","B","C"] ← Correcto (JSON)
|
||||
```
|
||||
|
||||
### ❌ Respuesta correcta fuera de rango
|
||||
```
|
||||
5 ← Incorrecto (solo hay 4 opciones: 0-3)
|
||||
0 ← Correcto
|
||||
```
|
||||
|
||||
## 📊 Métricas de Importación
|
||||
|
||||
| Métrica | Descripción |
|
||||
|---------|-------------|
|
||||
| Importadas | Preguntas guardadas exitosamente |
|
||||
| Saltadas | Filas sin question_text o con datos inválidos |
|
||||
| Errores | Problemas de base de datos |
|
||||
|
||||
## 🎯 Flujo Recomendado
|
||||
|
||||
1. **Crear plantilla** con 5-10 preguntas de prueba
|
||||
2. **Importar** y verificar que todo funcione
|
||||
3. **Revisar** preguntas importadas en el banco
|
||||
4. **Crear** archivo completo con todas las preguntas
|
||||
5. **Importar** masivamente
|
||||
6. **Editar** preguntas si es necesario
|
||||
|
||||
---
|
||||
|
||||
**Plantilla Descargable:** `question_bank_template.xlsx`
|
||||
@@ -0,0 +1,224 @@
|
||||
# 🎨 Navbar Actualizado - Guía de Cambios
|
||||
|
||||
## ✅ Cambios Realizados
|
||||
|
||||
### 1. **Textos Directos (Sin Traducción)**
|
||||
Se eliminaron las referencias a `t('nav.*')` y ahora los textos están en español directamente:
|
||||
|
||||
**Antes:**
|
||||
```tsx
|
||||
{t('nav.courses')}
|
||||
{t('nav.library')}
|
||||
{t('nav.settings')}
|
||||
```
|
||||
|
||||
**Ahora:**
|
||||
```tsx
|
||||
Dashboard
|
||||
Cursos
|
||||
Configuración
|
||||
```
|
||||
|
||||
### 2. **Agrupación en Dropdowns**
|
||||
|
||||
#### Dropdown "Cursos"
|
||||
Agrupa:
|
||||
- 📊 **Listar Cursos** (`/`) - El dashboard principal con tus cursos
|
||||
- 📚 **Librería** (`/library/assets`)
|
||||
- ❓ **Banco de Preguntas** (`/question-bank`)
|
||||
|
||||
**Icono:** `BookOpen`
|
||||
|
||||
#### Dropdown "Configuración" (Solo Admins)
|
||||
Agrupa:
|
||||
- 🔗 **Webhooks** (`/settings/webhooks`)
|
||||
- 👤 **Perfil** (`/profile`)
|
||||
- ⚙️ **General** (`/settings`)
|
||||
|
||||
**Icono:** `Settings`
|
||||
|
||||
### 3. **Navegación Simplificada**
|
||||
|
||||
**Para Admins:**
|
||||
```
|
||||
Dashboard | Cursos ▼ | Control Global | Configuración ▼ | [Theme] [Lang] [User]
|
||||
```
|
||||
|
||||
**Para No-Admins:**
|
||||
```
|
||||
Dashboard | Cursos ▼ | Configuración | [Theme] [Lang] [User]
|
||||
```
|
||||
|
||||
### 4. **Iconos Actualizados**
|
||||
|
||||
| Item | Icono |
|
||||
|------|-------|
|
||||
| Dashboard | `LayoutDashboard` |
|
||||
| Cursos (dropdown) | `BookOpen` |
|
||||
| Librería | `Library` |
|
||||
| Banco de Preguntas | `FileQuestion` |
|
||||
| Control Global | `ShieldCheck` |
|
||||
| Configuración (dropdown) | `Settings` |
|
||||
| Webhooks | `Webhook` |
|
||||
| Perfil | `User` |
|
||||
|
||||
## 🎯 Comportamiento
|
||||
|
||||
### Dropdowns
|
||||
- **Click** en "Cursos" o "Configuración" → Abre menú
|
||||
- **Click** fuera → Cierra menú
|
||||
- **Click** en item → Navega y cierra menú
|
||||
- **Flecha** indica estado (▼ cuando está abierto)
|
||||
|
||||
### Responsive
|
||||
- Menús se adaptan a dark mode
|
||||
- Shadow y border para visibilidad
|
||||
- Z-index correcto para overlays
|
||||
|
||||
## 📁 Archivos Modificados
|
||||
|
||||
```
|
||||
web/studio/src/components/Navbar.tsx
|
||||
+ useState para dropdowns
|
||||
+ Iconos: ChevronDown, FileQuestion, Webhook, User
|
||||
+ DROPDOWN_ITEM style
|
||||
+ Dropdown "Cursos"
|
||||
+ Dropdown "Configuración"
|
||||
+ Textos en español directo
|
||||
```
|
||||
|
||||
## 🎨 Estilos
|
||||
|
||||
### Dropdown Menu
|
||||
```tsx
|
||||
const DROPDOWN_ITEM = "flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors";
|
||||
```
|
||||
|
||||
**Características:**
|
||||
- Fondo blanco/gray oscuro
|
||||
- Hover effect
|
||||
- Border shadow
|
||||
- Iconos alineados
|
||||
|
||||
### Flecha Animada
|
||||
```tsx
|
||||
<ChevronDown className={`w-3 h-3 transition-transform ${coursesOpen ? 'rotate-180' : ''}`} />
|
||||
```
|
||||
|
||||
**Animación:**
|
||||
- Normal: ▲
|
||||
- Abierto: ▼ (rotate-180)
|
||||
|
||||
## 🖼️ Preview
|
||||
|
||||
### Navbar Normal
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ [Logo] Dashboard Cursos▼ Configuración▼ 🌙 ES 👤 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Dropdown "Cursos" Abierto
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ [Logo] Dashboard Cursos▼ Configuración▼ 🌙 ES 👤 │
|
||||
│ ┌──────────────────┐ │
|
||||
│ │ 📚 Librería │ │
|
||||
│ │ ❓ Banco de Preg.│ │
|
||||
│ └──────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Dropdown "Configuración" Abierto (Admin)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ [Logo] Dashboard Cursos Configuración▼ 🌙 ES 👤 │
|
||||
│ ┌──────────────────┐ │
|
||||
│ │ 🔗 Webhooks │ │
|
||||
│ │ 👤 Perfil │ │
|
||||
│ │ ⚙️ General │ │
|
||||
│ └──────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🚀 Cómo Usar
|
||||
|
||||
### Para Usuarios
|
||||
1. **Click** en "Cursos" para ver:
|
||||
- Librería
|
||||
- Banco de Preguntas
|
||||
|
||||
2. **Click** en "Configuración" (si eres admin) para ver:
|
||||
- Webhooks
|
||||
- Perfil
|
||||
- General
|
||||
|
||||
### Para Desarrolladores
|
||||
```tsx
|
||||
// Agregar nuevo item al dropdown "Cursos":
|
||||
<Link
|
||||
href="/nueva-ruta"
|
||||
className={DROPDOWN_ITEM}
|
||||
onClick={() => setCoursesOpen(false)}
|
||||
>
|
||||
<NuevoIcono className="w-4 h-4" />
|
||||
Nuevo Item
|
||||
</Link>
|
||||
|
||||
// Agregar nuevo item al dropdown "Configuración":
|
||||
<Link
|
||||
href="/otra-ruta"
|
||||
className={DROPDOWN_ITEM}
|
||||
onClick={() => setSettingsOpen(false)}
|
||||
>
|
||||
<OtroIcono className="w-4 h-4" />
|
||||
Otro Item
|
||||
</Link>
|
||||
```
|
||||
|
||||
## ✅ Testing Checklist
|
||||
|
||||
- [ ] Dropdown "Cursos" abre/cierra
|
||||
- [ ] Dropdown "Configuración" abre/cierra (admins)
|
||||
- [ ] Click fuera cierra dropdowns
|
||||
- [ ] Navegación funciona
|
||||
- [ ] Dark mode se ve bien
|
||||
- [ ] Flecha rota correctamente
|
||||
- [ ] Responsive en móviles
|
||||
- [ ] No hay errores de TypeScript
|
||||
|
||||
## 🔄 Migración
|
||||
|
||||
### Si tenías bookmarks:
|
||||
```
|
||||
Antes: /library/assets
|
||||
Ahora: Cursos → Librería
|
||||
|
||||
Antes: /question-bank
|
||||
Ahora: Cursos → Banco de Preguntas
|
||||
|
||||
Antes: /settings/webhooks
|
||||
Ahora: Configuración → Webhooks
|
||||
|
||||
Antes: /profile
|
||||
Ahora: Configuración → Perfil
|
||||
```
|
||||
|
||||
## 💡 Beneficios
|
||||
|
||||
1. **Menos clutter** - Menos items visibles
|
||||
2. **Organización lógica** - Items relacionados agrupados
|
||||
3. **Escalable** - Fácil agregar nuevos items
|
||||
4. **UX mejorada** - Menús familiares para usuarios
|
||||
5. **Espacio** - Más room para otros features
|
||||
|
||||
## 📝 Notas
|
||||
|
||||
- Los dropdowns usan `fixed inset-0` overlay para cerrar al hacer click fuera
|
||||
- `z-index` cuidadosamente configurado para no interferir con otros elementos
|
||||
- Textos en español hardcoded (fácil cambiar si necesitas i18n después)
|
||||
- Iconos de Lucide React consistentes con el resto de la app
|
||||
|
||||
---
|
||||
|
||||
**Estado:** ✅ Completo y funcional
|
||||
@@ -0,0 +1,119 @@
|
||||
# 🎯 Navbar - Estructura Final
|
||||
|
||||
## 📊 Menú "Cursos" Completo
|
||||
|
||||
El dropdown **Cursos** ahora incluye todas las herramientas relacionadas con cursos y evaluaciones:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐
|
||||
│ 📊 Listar Cursos │ ← Dashboard principal
|
||||
│ ──────────────────────────────── │
|
||||
│ 📚 Librería │ ← Activos y recursos
|
||||
│ ❓ Banco de Preguntas │ ← Preguntas individuales
|
||||
│ ──────────────────────────────── │
|
||||
│ 📝 Plantillas de Pruebas │ ← Evaluaciones completas
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🗂️ Organización Lógica
|
||||
|
||||
### Nivel 1: Gestión de Cursos
|
||||
- **Listar Cursos** → Ver todos tus cursos
|
||||
|
||||
### Nivel 2: Recursos
|
||||
- **Librería** → Archivos, videos, documentos
|
||||
- **Banco de Preguntas** → Preguntas individuales para armar evaluaciones
|
||||
|
||||
### Nivel 3: Evaluaciones
|
||||
- **Plantillas de Pruebas** → Evaluaciones completas listas para aplicar
|
||||
|
||||
## 📁 Diferencias Clave
|
||||
|
||||
| Item | Qué es | Cuándo usar |
|
||||
|------|--------|-------------|
|
||||
| **Banco de Preguntas** | Preguntas sueltas | Crear/editar preguntas individuales |
|
||||
| **Plantillas de Pruebas** | Evaluaciones completas | Armar pruebas con múltiples preguntas |
|
||||
|
||||
### Ejemplo de Flujo
|
||||
|
||||
```
|
||||
1. Banco de Preguntas
|
||||
└─→ Creo preguntas individuales
|
||||
- "What color is the sky?"
|
||||
- "Past tense of 'go'?"
|
||||
- "Plural of 'child'?"
|
||||
|
||||
2. Plantillas de Pruebas
|
||||
└─→ Armo evaluación "Final Exam Beginner 1"
|
||||
- Selecciono 10 preguntas del banco
|
||||
- Configuro duración: 60 min
|
||||
- Configuro puntuación: 70% para aprobar
|
||||
|
||||
3. Aplicar Plantilla
|
||||
└─→ Asigno la prueba a una lección del curso
|
||||
```
|
||||
|
||||
## 🎨 Estructura Visual
|
||||
|
||||
### Dropdown con Separadores
|
||||
```
|
||||
┌──────────────────────────────────┐
|
||||
│ 📊 Listar Cursos │
|
||||
├──────────────────────────────────┤ ← Separador
|
||||
│ 📚 Librería │
|
||||
│ ❓ Banco de Preguntas │
|
||||
├──────────────────────────────────┤ ← Separador
|
||||
│ 📝 Plantillas de Pruebas │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🔗 URLs
|
||||
|
||||
| Item | URL |
|
||||
|------|-----|
|
||||
| Listar Cursos | `/` |
|
||||
| Librería | `/library/assets` |
|
||||
| Banco de Preguntas | `/question-bank` |
|
||||
| Plantillas de Pruebas | `/test-templates` |
|
||||
|
||||
## 📋 Checklist de Navegación
|
||||
|
||||
- [ ] Listar Cursos → Dashboard principal
|
||||
- [ ] Librería → Gestión de activos
|
||||
- [ ] Banco de Preguntas → Crear preguntas
|
||||
- [ ] Plantillas de Pruebas → Armar evaluaciones
|
||||
|
||||
## 💡 Flujo Recomendado
|
||||
|
||||
### Para Crear una Evaluación
|
||||
|
||||
1. **Banco de Preguntas**
|
||||
- Crear 20-30 preguntas
|
||||
- Asignar skills (reading, listening, speaking, writing)
|
||||
- Agregar explicaciones
|
||||
|
||||
2. **Plantillas de Pruebas**
|
||||
- Click en "Nueva Plantilla"
|
||||
- Nombre: "Final Exam - Beginner 1"
|
||||
- Tipo: `FWT` (Final Written Test)
|
||||
- Duración: 60 minutos
|
||||
- Seleccionar 10-15 preguntas del banco
|
||||
|
||||
3. **Aplicar a Curso**
|
||||
- Click en "Aplicar Plantilla"
|
||||
- Seleccionar curso
|
||||
- Seleccionar lección
|
||||
- Listo! ✅
|
||||
|
||||
## 🎯 Métricas de Uso
|
||||
|
||||
| Item | Uso Típico |
|
||||
|------|------------|
|
||||
| Listar Cursos | Diario |
|
||||
| Librería | Semanal |
|
||||
| Banco de Preguntas | Semanal |
|
||||
| Plantillas de Pruebas | Mensual (al crear evaluaciones) |
|
||||
|
||||
---
|
||||
|
||||
**Estado:** ✅ Completo y funcional
|
||||
@@ -0,0 +1,298 @@
|
||||
# 🎯 Próximos Pasos - OpenCCB
|
||||
|
||||
Después de limpiar Bark, tienes 3 funcionalidades principales listas para usar:
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ Question Bank (Sin Audio)
|
||||
|
||||
### ¿Qué Hace?
|
||||
Banco centralizado de preguntas reutilizables para crear evaluaciones.
|
||||
|
||||
### Características
|
||||
- ✅ Crear preguntas de 10 tipos diferentes
|
||||
- ✅ Importar desde MySQL (sin duplicados)
|
||||
- ✅ Generar con IA (con verificación de 4 habilidades)
|
||||
- ✅ Filtrar por tipo, dificultad, skill
|
||||
- ✅ Tags y organización
|
||||
- ✅ Vista previa en cards
|
||||
|
||||
### Cómo Usar
|
||||
```
|
||||
1. Ve a /question-bank en Studio
|
||||
2. Click "Nueva Pregunta"
|
||||
3. Completa:
|
||||
- Tipo (multiple-choice, true-false, etc.)
|
||||
- Texto de la pregunta
|
||||
- Opciones (si aplica)
|
||||
- Respuesta correcta
|
||||
- Explicación
|
||||
- Dificultad
|
||||
- Tags
|
||||
4. Guarda
|
||||
```
|
||||
|
||||
### Endpoints Relacionados
|
||||
```
|
||||
GET /question-bank # Listar preguntas
|
||||
POST /question-bank # Crear pregunta
|
||||
PUT /question-bank/{id} # Actualizar pregunta
|
||||
DELETE /question-bank/{id} # Eliminar pregunta
|
||||
POST /question-bank/import-mysql # Importar desde MySQL
|
||||
```
|
||||
|
||||
### Archivos Clave
|
||||
```
|
||||
web/studio/src/app/question-bank/page.tsx
|
||||
web/studio/src/components/QuestionBank/
|
||||
services/cms-service/src/handlers_question_bank.rs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ Token Usage Dashboard
|
||||
|
||||
### ¿Qué Hace?
|
||||
Monitoreo en tiempo real del consumo de IA (tokens, costos, usuarios).
|
||||
|
||||
### Características
|
||||
- ✅ Stats en tiempo real
|
||||
- ✅ Costos estimados en USD
|
||||
- ✅ Filtrar por rol (student/instructor/admin)
|
||||
- ✅ Alertas de alto consumo (>1M tokens)
|
||||
- ✅ Tabla detallada por usuario
|
||||
- ✅ Tracking de input vs output tokens
|
||||
|
||||
### Cómo Usar
|
||||
```
|
||||
1. Ve a /admin/token-usage
|
||||
2. Revisa las 4 stats cards:
|
||||
- Total Tokens
|
||||
- Requests IA
|
||||
- Costo USD
|
||||
- Usuarios Activos
|
||||
3. Filtra por rol si necesitas
|
||||
4. Ordena por Total Tokens para ver power users
|
||||
5. Revisa alertas de alto consumo
|
||||
```
|
||||
|
||||
### Endpoints Relacionados
|
||||
```
|
||||
GET /admin/token-usage # Ver uso de tokens
|
||||
```
|
||||
|
||||
### Archivos Clave
|
||||
```
|
||||
web/studio/src/app/admin/token-usage/page.tsx
|
||||
services/cms-service/src/handlers_admin.rs
|
||||
services/cms-service/migrations/20260316000002_ai_usage_tracking.sql
|
||||
```
|
||||
|
||||
### Límites Sugeridos
|
||||
```json
|
||||
{
|
||||
"student": {
|
||||
"daily_tokens": 50000,
|
||||
"monthly_tokens": 1000000
|
||||
},
|
||||
"instructor": {
|
||||
"daily_tokens": 200000,
|
||||
"monthly_tokens": 5000000
|
||||
},
|
||||
"admin": {
|
||||
"daily_tokens": 1000000,
|
||||
"monthly_tokens": 20000000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ Importar Preguntas desde MySQL
|
||||
|
||||
### ¿Qué Hace?
|
||||
Trae preguntas del banco de diagnóstico de inglés (sistema legacy) a OpenCCB.
|
||||
|
||||
### Características
|
||||
- ✅ Importación masiva por curso
|
||||
- ✅ Detección automática de duplicados
|
||||
- ✅ Mapeo de tipos de pregunta
|
||||
- ✅ Tracking de origen (imported-mysql)
|
||||
- ✅ Skills automáticos
|
||||
- ✅ No reimporta preguntas existentes
|
||||
|
||||
### Cómo Usar
|
||||
```
|
||||
1. Ve a /question-bank
|
||||
2. Click "Importar desde MySQL"
|
||||
3. Selecciona curso de MySQL:
|
||||
- YOUNG LEARNERS 1
|
||||
- KIDS: BEGINNER 1
|
||||
- TEENS 1
|
||||
- ADULTS REGULAR
|
||||
- etc.
|
||||
4. Click "Importar Preguntas"
|
||||
5. Espera confirmación:
|
||||
"Imported 45 questions (skipped 12 already imported)"
|
||||
```
|
||||
|
||||
### Endpoints Relacionados
|
||||
```
|
||||
GET /question-bank/mysql-courses # Listar cursos MySQL
|
||||
POST /question-bank/import-mysql # Importar preguntas
|
||||
```
|
||||
|
||||
### Archivos Clave
|
||||
```
|
||||
web/studio/src/components/QuestionBank/MySQLImportModal.tsx
|
||||
services/cms-service/src/handlers_question_bank.rs
|
||||
```
|
||||
|
||||
### Estructura MySQL
|
||||
```sql
|
||||
-- Tablas de origen
|
||||
bancopreguntas (idPregunta, descripcion, idTipoPregunta)
|
||||
curso (idCursos, NombreCurso)
|
||||
plandeestudios (idPlanDeEstudios, Nombre)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Flujo Recomendado
|
||||
|
||||
### Paso 1: Importar desde MySQL (5 min)
|
||||
```
|
||||
1. Ve a /question-bank
|
||||
2. Importa preguntas de un curso
|
||||
3. Verifica que se importaron correctamente
|
||||
```
|
||||
|
||||
### Paso 2: Revisar Token Usage (2 min)
|
||||
```
|
||||
1. Ve a /admin/token-usage
|
||||
2. Revisa el consumo actual
|
||||
3. Identifica usuarios con alto consumo
|
||||
```
|
||||
|
||||
### Paso 3: Crear Preguntas Manuales (10 min)
|
||||
```
|
||||
1. Crea 2-3 preguntas manualmente
|
||||
2. Prueba diferentes tipos
|
||||
3. Agrega tags y skills
|
||||
4. Genera algunas con IA
|
||||
```
|
||||
|
||||
### Paso 4: Verificar Todo (5 min)
|
||||
```
|
||||
1. Filtra preguntas por tipo
|
||||
2. Revisa skills asignados
|
||||
3. Verifica token usage después de usar IA
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Comparativa de Funcionalidades
|
||||
|
||||
| Característica | Question Bank | Token Dashboard | MySQL Import |
|
||||
|---------------|---------------|-----------------|--------------|
|
||||
| Tiempo | 10 min | 2 min | 5 min |
|
||||
| Complejidad | Media | Baja | Baja |
|
||||
| Impacto | Alto | Medio | Alto |
|
||||
| Dependencias | Ninguna | IA logs | MySQL connection |
|
||||
|
||||
---
|
||||
|
||||
## 💡 Casos de Uso
|
||||
|
||||
### Para Instructores
|
||||
1. **Question Bank**: Crear preguntas para sus cursos
|
||||
2. **MySQL Import**: Traer preguntas existentes del sistema legacy
|
||||
3. **Token Dashboard**: Monitorear su propio uso de IA
|
||||
|
||||
### Para Admins
|
||||
1. **Token Dashboard**: Ver consumo global de IA
|
||||
2. **Question Bank**: Gestionar banco institucional
|
||||
3. **MySQL Import**: Migración masiva de contenido
|
||||
|
||||
### Para Desarrolladores
|
||||
1. **Token Dashboard**: Debug de llamadas a IA
|
||||
2. **Question Bank**: Testing de nuevos tipos de preguntas
|
||||
3. **MySQL Import**: Migración de datos
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Métricas de Éxito
|
||||
|
||||
### Question Bank
|
||||
- [ ] 100+ preguntas creadas
|
||||
- [ ] 5+ tipos de preguntas usados
|
||||
- [ ] 50% con skills asignados
|
||||
|
||||
### Token Dashboard
|
||||
- [ ] Dashboard accesible
|
||||
- [ ] Stats en tiempo real
|
||||
- [ ] Alertas funcionando
|
||||
|
||||
### MySQL Import
|
||||
- [ ] 0 duplicados
|
||||
- [ ] 100% preguntas importadas
|
||||
- [ ] Skills asignados correctamente
|
||||
|
||||
---
|
||||
|
||||
## 📁 Comandos Útiles
|
||||
|
||||
### Iniciar Studio
|
||||
```bash
|
||||
cd /home/juan/dev/openccb/web/studio
|
||||
npm run dev
|
||||
# Acceder: http://localhost:3000
|
||||
```
|
||||
|
||||
### Ver Logs del CMS
|
||||
```bash
|
||||
cd /home/juan/dev/openccb
|
||||
RUST_LOG=debug cargo run -p cms-service
|
||||
```
|
||||
|
||||
### Verificar Conexión MySQL
|
||||
```bash
|
||||
cd /home/juan/dev/openccb
|
||||
node check_mysql.js
|
||||
```
|
||||
|
||||
### Ver Token Usage en DB
|
||||
```bash
|
||||
docker-compose exec -T db psql -U user -d openccb_cms -c \
|
||||
"SELECT user_id, SUM(tokens_used), SUM(estimated_cost_usd)
|
||||
FROM ai_usage_logs
|
||||
GROUP BY user_id
|
||||
ORDER BY SUM(tokens_used) DESC
|
||||
LIMIT 10;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 ¿Listo para Comenzar?
|
||||
|
||||
Elige una opción y comienza:
|
||||
|
||||
```bash
|
||||
# Opción 1: Question Bank
|
||||
# Ve a http://localhost:3000/question-bank
|
||||
|
||||
# Opción 2: Token Dashboard
|
||||
# Ve a http://localhost:3000/admin/token-usage
|
||||
|
||||
# Opción 3: Importar desde MySQL
|
||||
# Ve a /question-bank → "Importar desde MySQL"
|
||||
```
|
||||
|
||||
**Recomendación:** Comienza con **MySQL Import** para tener contenido base, luego explora **Question Bank** para crear preguntas nuevas, y finalmente monitorea todo con **Token Dashboard**.
|
||||
|
||||
---
|
||||
|
||||
**Documentación Relacionada:**
|
||||
- `docs/QUESTION_BANK_UI.md` - UI del Question Bank
|
||||
- `docs/TOKEN_USAGE_TRACKING.md` - Tracking de tokens
|
||||
- `docs/AUDIO_GUIDE_FOR_INSTRUCTORS.md` - Guía de audio (para cuando quieras implementar audio manual)
|
||||
@@ -0,0 +1,305 @@
|
||||
# Question Bank UI - Documentación
|
||||
|
||||
## Vista General
|
||||
|
||||
El Banco de Preguntas es una interfaz completa en Studio para gestionar preguntas reutilizables con soporte para audio generado por IA.
|
||||
|
||||
## Acceso
|
||||
|
||||
```
|
||||
http://localhost:3000/question-bank
|
||||
```
|
||||
|
||||
## Componentes
|
||||
|
||||
### 1. Página Principal (`/question-bank/page.tsx`)
|
||||
|
||||
**Características:**
|
||||
- Lista todas las preguntas del banco
|
||||
- Filtros por tipo, dificultad, origen, audio
|
||||
- Búsqueda por texto
|
||||
- Estadísticas rápidas (total, importadas, con audio, IA)
|
||||
- Accesos rápidos para crear e importar
|
||||
|
||||
**Estadísticas mostradas:**
|
||||
```
|
||||
┌──────────────┬──────────────┬──────────────┬──────────────┐
|
||||
│ Total │ MySQL │ Con Audio │ IA │
|
||||
│ 150 │ 45 │ 32 │ 18 │
|
||||
└──────────────┴──────────────┴──────────────┴──────────────┘
|
||||
```
|
||||
|
||||
### 2. QuestionBankCard (`QuestionBankCard.tsx`)
|
||||
|
||||
Tarjeta individual de pregunta que muestra:
|
||||
- Tipo de pregunta (badge)
|
||||
- Dificultad (color-coded)
|
||||
- Texto de la pregunta (truncado)
|
||||
- Opciones preview (primeras 3)
|
||||
- Estado del audio
|
||||
- Origen (MySQL, IA, Manual)
|
||||
- Uso (veces utilizada)
|
||||
- Acciones: Editar, Eliminar, Audio
|
||||
|
||||
**Badges de tipo:**
|
||||
- 🟦 Opción Múltiple
|
||||
- 🟩 Verdadero/Falso
|
||||
- 🟨 Respuesta Corta
|
||||
- 🟪 Ensayo
|
||||
- 🟧 Emparejamiento
|
||||
- 🔵 Ordenar
|
||||
- 🟫 Completar
|
||||
- 🟣 Respuesta Audio
|
||||
- 🔴 Hotspot
|
||||
- ⬛ Código
|
||||
|
||||
**Badges de dificultad:**
|
||||
- 🟢 Fácil (easy)
|
||||
- 🟡 Media (medium)
|
||||
- 🔴 Difícil (hard)
|
||||
|
||||
**Badges de origen:**
|
||||
- 🌐 MySQL - Importado desde sistema legacy
|
||||
- ✨ IA - Generado por Inteligencia Artificial
|
||||
- Manual - Creado manualmente
|
||||
|
||||
### 3. QuestionBankEditor (`QuestionBankEditor.tsx`)
|
||||
|
||||
Modal para crear/editar preguntas con:
|
||||
|
||||
**Campos:**
|
||||
- Tipo de pregunta (10 tipos soportados)
|
||||
- Dificultad (easy/medium/hard)
|
||||
- Texto de la pregunta (required)
|
||||
- Opciones (para multiple-choice/true-false)
|
||||
- Respuesta correcta (selector radial)
|
||||
- Explicación/feedback
|
||||
- Puntos
|
||||
- Etiquetas (tags)
|
||||
- Checkbox "Generar audio automáticamente"
|
||||
|
||||
**Características especiales:**
|
||||
- Botón "Generar con IA" para opciones y explicación
|
||||
- Agregar/remover opciones dinámicamente
|
||||
- Tags con auto-complete
|
||||
- Validación en tiempo real
|
||||
|
||||
### 4. MySQLImportModal (`MySQLImportModal.tsx`)
|
||||
|
||||
Modal para importar preguntas desde MySQL:
|
||||
|
||||
**Flujo:**
|
||||
1. Selecciona curso de MySQL (carga dinámica)
|
||||
2. Click en "Importar Preguntas"
|
||||
3. Importa todas las preguntas activas del curso
|
||||
4. Muestra cantidad importada
|
||||
5. Redirige a la lista actualizada
|
||||
|
||||
**Información mostrada:**
|
||||
- Lista de cursos disponibles (NombreCurso + NombrePlan)
|
||||
- Explicación de qué se importará
|
||||
- Progreso de importación
|
||||
- Resultado (éxito/error)
|
||||
|
||||
### 5. AudioGeneratorModal (`AudioGeneratorModal.tsx`)
|
||||
|
||||
Modal para generar audio con Bark TTS:
|
||||
|
||||
**Configuración:**
|
||||
- Vista previa del texto de la pregunta
|
||||
- Texto personalizable (opcional)
|
||||
- Selector de voz (6 opciones: 3 inglés, 3 español)
|
||||
- Control de velocidad (0.5x - 2.0x)
|
||||
|
||||
**Voces disponibles:**
|
||||
```
|
||||
Inglés:
|
||||
- v2/en_speaker_0 (English Speaker 0)
|
||||
- v2/en_speaker_1 (English Speaker 1) ← default
|
||||
- v2/en_speaker_6 (English Speaker 6)
|
||||
|
||||
Español:
|
||||
- v2/es_speaker_0 (Spanish Speaker 0)
|
||||
- v2/es_speaker_1 (Spanish Speaker 1)
|
||||
- v2/es_speaker_3 (Spanish Speaker 3)
|
||||
```
|
||||
|
||||
**Estados:**
|
||||
- ⏳ Generando... (polling cada 1s, max 30s)
|
||||
- ✅ Audio generado (con preview play/pause)
|
||||
- ❌ Error (mensaje descriptivo)
|
||||
|
||||
**Características:**
|
||||
- Polling automático para verificar estado
|
||||
- Reproductor de audio integrado
|
||||
- Botón Play/Pause
|
||||
- Indicador visual de estado
|
||||
|
||||
## Flujos de Usuario
|
||||
|
||||
### Crear Pregunta Manualmente
|
||||
|
||||
```
|
||||
1. Click "Nueva Pregunta"
|
||||
2. Seleccionar tipo de pregunta
|
||||
3. Completar texto de pregunta
|
||||
4. Agregar opciones (si aplica)
|
||||
5. Marcar respuesta correcta
|
||||
6. Agregar explicación (opcional)
|
||||
7. Configurar puntos y dificultad
|
||||
8. Agregar etiquetas
|
||||
9. ☑️ Marcar "Generar audio automáticamente"
|
||||
10. Click "Guardar Pregunta"
|
||||
```
|
||||
|
||||
### Importar desde MySQL
|
||||
|
||||
```
|
||||
1. Click "Importar desde MySQL"
|
||||
2. Seleccionar curso del dropdown
|
||||
3. Click "Importar Preguntas"
|
||||
4. Esperar confirmación (ej: "Se importaron 45 preguntas")
|
||||
5. Las preguntas aparecen en la lista con badge 🌐 MySQL
|
||||
```
|
||||
|
||||
### Generar Audio para Pregunta
|
||||
|
||||
```
|
||||
1. Click ícono 🔊 en pregunta (si no tiene audio)
|
||||
2. (Opcional) Personalizar texto para audio
|
||||
3. Seleccionar voz
|
||||
4. Ajustar velocidad
|
||||
5. Click "Generar Audio"
|
||||
6. Esperar generación (5-30 segundos)
|
||||
7. Click "Reproducir" para preview
|
||||
8. Click "Cerrar" cuando esté listo
|
||||
```
|
||||
|
||||
### Filtrar Preguntas
|
||||
|
||||
```
|
||||
1. Click "Filtros"
|
||||
2. Seleccionar tipo de pregunta
|
||||
3. Seleccionar dificultad
|
||||
4. Seleccionar origen
|
||||
5. Filtrar por audio (con/sin)
|
||||
6. Results se actualizan automáticamente
|
||||
```
|
||||
|
||||
### Buscar Preguntas
|
||||
|
||||
```
|
||||
1. Escribir en barra de búsqueda
|
||||
2. Presionar Enter
|
||||
3. Results filtran por texto en question_text
|
||||
```
|
||||
|
||||
## Integración con Test Templates
|
||||
|
||||
Las preguntas del banco se pueden usar en:
|
||||
- Plantillas de pruebas (Test Templates)
|
||||
- Lecciones tipo quiz directamente
|
||||
- Ejercicios de práctica
|
||||
|
||||
**Próximamente:**
|
||||
- Selector de preguntas desde banco al crear plantilla
|
||||
- Bulk selection para agregar múltiples preguntas
|
||||
- Vista previa de pregunta con audio
|
||||
|
||||
## API Endpoints Utilizados
|
||||
|
||||
```typescript
|
||||
// Listar preguntas
|
||||
GET /question-bank?question_type=multiple-choice&difficulty=medium&has_audio=true
|
||||
|
||||
// Crear pregunta
|
||||
POST /question-bank
|
||||
{
|
||||
"question_text": "What is...?",
|
||||
"question_type": "multiple-choice",
|
||||
"options": ["A", "B", "C", "D"],
|
||||
"correct_answer": 0,
|
||||
"explanation": "Because...",
|
||||
"points": 1,
|
||||
"difficulty": "medium",
|
||||
"tags": ["grammar", "past-tense"],
|
||||
"generate_audio": true
|
||||
}
|
||||
|
||||
// Actualizar pregunta
|
||||
PUT /question-bank/{id}
|
||||
|
||||
// Eliminar pregunta
|
||||
DELETE /question-bank/{id}
|
||||
|
||||
// Importar desde MySQL
|
||||
POST /question-bank/import-mysql
|
||||
{
|
||||
"mysql_course_id": 30,
|
||||
"import_all": true
|
||||
}
|
||||
|
||||
// Generar audio
|
||||
POST /question-bank/{id}/generate-audio
|
||||
{
|
||||
"text": "What color is the sky?",
|
||||
"voice": "v2/en_speaker_1",
|
||||
"speed": 1.0
|
||||
}
|
||||
|
||||
// Listar cursos MySQL
|
||||
GET /question-bank/mysql-courses
|
||||
```
|
||||
|
||||
## Estilos y UX
|
||||
|
||||
**Tema:**
|
||||
- Soporte completo dark mode
|
||||
- Colores consistentes con el resto de Studio
|
||||
- Animaciones suaves (hover, transitions)
|
||||
|
||||
**Responsive:**
|
||||
- Grid adaptable (1/2/3 columnas)
|
||||
- Modal con scroll interno
|
||||
- Touch-friendly en móviles
|
||||
|
||||
**Accesibilidad:**
|
||||
- Labels en todos los inputs
|
||||
- Focus visible
|
||||
- ARIA attributes
|
||||
- Keyboard navigation
|
||||
|
||||
## Próximas Mejoras
|
||||
|
||||
- [ ] Bulk audio generation (múltiples preguntas)
|
||||
- [ ] Exportar preguntas a CSV/JSON
|
||||
- [ ] Importar desde CSV
|
||||
- [ ] Vista previa de pregunta completa en card
|
||||
- [ ] Historial de ediciones
|
||||
- [ ] Comentarios/notas en preguntas
|
||||
- [ ] Compartir preguntas entre organizaciones
|
||||
- [ ] Analytics de uso por pregunta
|
||||
- [ ] AI-powered tagging automático
|
||||
- [ ] Detección de preguntas duplicadas
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Error: "No se pudo cargar los cursos de MySQL"**
|
||||
- Verificar que `MYSQL_DATABASE_URL` esté configurado en `.env`
|
||||
- Verificar conectividad al servidor MySQL
|
||||
|
||||
**Error: "Error al generar audio"**
|
||||
- Verificar que Bark TTS esté corriendo en t-800
|
||||
- Verificar que `BARK_API_URL` esté configurado
|
||||
- Revisar logs de Bark: `ssh juan@t-800 && journalctl -u bark-tts -f`
|
||||
|
||||
**Audio no se reproduce**
|
||||
- Verificar formato de audio (WAV soportado)
|
||||
- Verificar permisos del navegador
|
||||
- Probar en otro navegador
|
||||
|
||||
## Referencias
|
||||
|
||||
- [Question Bank Backend](../../services/cms-service/src/handlers_question_bank.rs)
|
||||
- [Bark TTS Guide](../../docs/BARK_TTS_GUIDE.md)
|
||||
- [Test Templates UI](./TestTemplates/)
|
||||
@@ -0,0 +1,301 @@
|
||||
# 📊 Token Usage Tracking - Implementación Completa
|
||||
|
||||
## ✅ Lo Que Se Implementó
|
||||
|
||||
### 1. **Base de Datos** ✅
|
||||
- Tabla `ai_usage_logs` para registrar cada llamada a IA
|
||||
- Función `log_ai_usage()` para logging fácil
|
||||
- Vista `ai_usage_daily` para reportes
|
||||
- Índices para queries rápidos
|
||||
|
||||
### 2. **Función count_tokens()** ✅
|
||||
```rust
|
||||
fn count_tokens(text: &str) -> i32 {
|
||||
// Aproximación: 1 token ≈ 4 caracteres
|
||||
let char_count = text.len();
|
||||
((char_count as f64) / 4.0).ceil() as i32
|
||||
}
|
||||
```
|
||||
|
||||
**Precisión:**
|
||||
- ✅ Suficiente para estimaciones de costo
|
||||
- ✅ Rápida (no requiere librerías externas)
|
||||
- ✅ Funciona para inglés y español
|
||||
|
||||
### 3. **Endpoints con Tracking** ✅
|
||||
|
||||
#### generate_quiz
|
||||
```rust
|
||||
// Calcular tokens
|
||||
let input_tokens = count_tokens(&system_prompt) + count_tokens(&content_text);
|
||||
let output_tokens = count_tokens(&response_json.to_string());
|
||||
let total_tokens = input_tokens + output_tokens;
|
||||
|
||||
// Loguear
|
||||
let _ = sqlx::query("SELECT log_ai_usage($1, $2, $3, $4, $5, $6, $7, $8, $9)")
|
||||
.bind(claims.sub)
|
||||
.bind(org_ctx.id)
|
||||
.bind(total_tokens)
|
||||
.bind(input_tokens)
|
||||
.bind(output_tokens)
|
||||
.bind("/lessons/generate-quiz")
|
||||
.bind(&model)
|
||||
.bind("quiz-generation")
|
||||
.bind(&json!({ "lesson_id": id, "quiz_type": quiz_req.quiz_type }))
|
||||
.execute(&pool)
|
||||
.await;
|
||||
```
|
||||
|
||||
### 4. **Endpoint Admin** ✅
|
||||
- `/admin/token-usage` - Ver uso por usuario
|
||||
- Muestra:
|
||||
- Total tokens por usuario
|
||||
- Input vs output tokens
|
||||
- Número de requests
|
||||
- Costo estimado en USD
|
||||
- Última actividad
|
||||
|
||||
### 5. **UI Dashboard** ✅
|
||||
- Página `/admin/token-usage`
|
||||
- Stats cards con:
|
||||
- Total tokens
|
||||
- Requests IA
|
||||
- Costo estimado
|
||||
- Usuarios activos
|
||||
- Tabla filtrable por rol
|
||||
- Alertas de alto consumo (>1M tokens)
|
||||
|
||||
## 📊 Esquema de la Tabla
|
||||
|
||||
```sql
|
||||
CREATE TABLE ai_usage_logs (
|
||||
id UUID,
|
||||
user_id UUID,
|
||||
organization_id UUID,
|
||||
tokens_used INTEGER,
|
||||
input_tokens INTEGER,
|
||||
output_tokens INTEGER,
|
||||
endpoint VARCHAR(255),
|
||||
model VARCHAR(100),
|
||||
request_type VARCHAR(50),
|
||||
request_metadata JSONB,
|
||||
estimated_cost_usd NUMERIC(10, 6),
|
||||
created_at TIMESTAMPTZ
|
||||
);
|
||||
```
|
||||
|
||||
## 💰 Cálculo de Costos
|
||||
|
||||
**Fórmula (OpenAI-like pricing):**
|
||||
```rust
|
||||
v_cost := (input_tokens * 0.000001) + (output_tokens * 0.000003);
|
||||
// $1 por 1M input tokens
|
||||
// $3 por 1M output tokens
|
||||
```
|
||||
|
||||
**Ejemplos:**
|
||||
|
||||
| Endpoint | Input | Output | Total | Costo |
|
||||
|----------|-------|--------|-------|-------|
|
||||
| generate_quiz | 500 | 800 | 1,300 | $0.0029 |
|
||||
| transcribe (1 min) | 0 | 150 | 150 | $0.00045 |
|
||||
| chat (pregunta) | 200 | 300 | 500 | $0.0011 |
|
||||
|
||||
## 🎯 Endpoints a Implementar
|
||||
|
||||
Para tracking completo, agregar en:
|
||||
|
||||
### 1. **generate_summary_with_ollama**
|
||||
```rust
|
||||
// Al final de la función
|
||||
let input_tokens = count_tokens(&text);
|
||||
let output_tokens = count_tokens(&summary);
|
||||
let _ = sqlx::query("SELECT log_ai_usage(...)")
|
||||
.bind(...)
|
||||
.execute(&pool)
|
||||
.await;
|
||||
```
|
||||
|
||||
### 2. **chat con lesson tutor**
|
||||
```rust
|
||||
// En handlers.rs, función de chat
|
||||
let input_tokens = count_tokens(&conversation_history);
|
||||
let output_tokens = count_tokens(&ai_response);
|
||||
// Log...
|
||||
```
|
||||
|
||||
### 3. **generate_feedback**
|
||||
```rust
|
||||
// Similar a generate_quiz
|
||||
```
|
||||
|
||||
## 📈 Dashboard de Admin
|
||||
|
||||
### Stats en Tiempo Real
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────┬──────────────┬──────────────┐
|
||||
│ Total Tokens │ Requests IA │ Costo USD │ Usuarios │
|
||||
│ 5,234,567 │ 1,234 │ $15.67 │ 345 │
|
||||
└──────────────┴──────────────┴──────────────┴──────────────┘
|
||||
```
|
||||
|
||||
### Filtros Disponibles
|
||||
|
||||
- Por rol (student/instructor/admin)
|
||||
- Por fecha (daily view)
|
||||
- Por endpoint
|
||||
- Por tipo de request
|
||||
|
||||
### Alertas Automáticas
|
||||
|
||||
```
|
||||
⚠️ Usuarios con alto consumo detectado
|
||||
5 usuario(s) han superado 1M de tokens.
|
||||
Considere implementar límites de uso.
|
||||
```
|
||||
|
||||
## 🔧 Límites Sugeridos
|
||||
|
||||
### Estudiantes
|
||||
```json
|
||||
{
|
||||
"daily_tokens": 50000,
|
||||
"monthly_tokens": 1000000,
|
||||
"max_requests_per_day": 100
|
||||
}
|
||||
```
|
||||
|
||||
### Instructores
|
||||
```json
|
||||
{
|
||||
"daily_tokens": 200000,
|
||||
"monthly_tokens": 5000000,
|
||||
"max_requests_per_day": 500
|
||||
}
|
||||
```
|
||||
|
||||
### Admins
|
||||
```json
|
||||
{
|
||||
"daily_tokens": 1000000,
|
||||
"monthly_tokens": 20000000,
|
||||
"max_requests_per_day": 2000
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 Implementación de Límites (Opcional)
|
||||
|
||||
```rust
|
||||
// Middleware o check antes de llamada a IA
|
||||
async fn check_token_limit(
|
||||
pool: &PgPool,
|
||||
user_id: Uuid,
|
||||
daily_limit: i32,
|
||||
) -> Result<(), StatusCode> {
|
||||
let today_usage: i64 = sqlx::query_scalar(
|
||||
"SELECT COALESCE(SUM(tokens_used), 0) FROM ai_usage_logs
|
||||
WHERE user_id = $1 AND DATE(created_at) = CURRENT_DATE"
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
if today_usage > daily_limit as i64 {
|
||||
return Err(StatusCode::TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Métricas Clave
|
||||
|
||||
### Por Usuario
|
||||
- Total tokens (lifetime)
|
||||
- Tokens hoy
|
||||
- Requests hoy
|
||||
- Costo acumulado
|
||||
|
||||
### Por Organización
|
||||
- Total tokens (todos los usuarios)
|
||||
- Top users
|
||||
- Costo mensual
|
||||
- Growth trend
|
||||
|
||||
### Por Endpoint
|
||||
- Most used endpoints
|
||||
- Average tokens per request
|
||||
- Error rates
|
||||
|
||||
## 📖 Uso del Dashboard
|
||||
|
||||
1. **Ver uso general**
|
||||
- Ve a `/admin/token-usage`
|
||||
- Revisa stats cards
|
||||
|
||||
2. **Filtrar por rol**
|
||||
- Selecciona "Estudiantes" o "Instructores"
|
||||
- Ordena por "Total Tokens"
|
||||
|
||||
3. **Identificar power users**
|
||||
- Ordena por "Total Tokens"
|
||||
- Revisa usuarios > 1M tokens
|
||||
|
||||
4. **Monitorear costos**
|
||||
- Revisa "Costo USD"
|
||||
- Proyecta uso mensual
|
||||
|
||||
5. **Detectar anomalías**
|
||||
- Alertas automáticas > 1M tokens
|
||||
- Picos de uso inusuales
|
||||
|
||||
## 🚀 Próximos Pasos
|
||||
|
||||
### Corto Plazo
|
||||
- [ ] Agregar logging en todos los endpoints de IA
|
||||
- [ ] Implementar límites suaves (warnings)
|
||||
- [ ] Exportar CSV de usage
|
||||
|
||||
### Mediano Plazo
|
||||
- [ ] Límites duros (block requests)
|
||||
- [ ] Notificaciones email (80% del límite)
|
||||
- [ ] Gráficos de tendencia
|
||||
|
||||
### Largo Plazo
|
||||
- [ ] Budget por organización
|
||||
- [ ] Alertas de costo
|
||||
- [ ] Integration con sistemas de billing
|
||||
|
||||
## 📁 Archivos Modificados
|
||||
|
||||
```
|
||||
services/cms-service/migrations/20260316000002_ai_usage_tracking.sql
|
||||
services/cms-service/src/handlers.rs
|
||||
+ count_tokens() function
|
||||
+ Token logging in generate_quiz
|
||||
services/cms-service/src/handlers_admin.rs (NUEVO)
|
||||
+ get_token_usage endpoint
|
||||
services/cms-service/src/main.rs
|
||||
+ /admin/token-usage route
|
||||
shared/common/src/models.rs
|
||||
+ TokenUsage models
|
||||
web/studio/src/app/admin/token-usage/page.tsx (NUEVO)
|
||||
+ Admin dashboard UI
|
||||
web/studio/src/app/admin/page.tsx
|
||||
+ Link a Token Usage
|
||||
```
|
||||
|
||||
## ✅ Estado
|
||||
|
||||
| Componente | Estado |
|
||||
|------------|--------|
|
||||
| DB Schema | ✅ 100% |
|
||||
| Logging Function | ✅ 100% |
|
||||
| generate_quiz Tracking | ✅ 100% |
|
||||
| Admin Endpoint | ✅ 100% |
|
||||
| Admin Dashboard UI | ✅ 100% |
|
||||
| Límites | ⏸️ Listo para implementar |
|
||||
| Otros Endpoints | ⏳ Pendiente agregar |
|
||||
|
||||
**Progreso Total: 80%** 🎉
|
||||
Reference in New Issue
Block a user