docs: Token limits user guide
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -0,0 +1,358 @@
|
|||||||
|
# Límites de Tokens Mensuales por Usuario
|
||||||
|
|
||||||
|
## Visión General
|
||||||
|
|
||||||
|
OpenCCB ahora soporta límites de tokens de IA mensuales configurables por usuario, permitiendo controlar el consumo de IA y prevenir usos excesivos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Características
|
||||||
|
|
||||||
|
### Configuración por Usuario
|
||||||
|
|
||||||
|
Cada usuario puede tener:
|
||||||
|
- **Límite mensual personalizado**: Número máximo de tokens que puede consumir por mes
|
||||||
|
- **Día de reset**: Día del mes cuando se reinicia el contador (1-28)
|
||||||
|
- **Ilimitado**: Configurando límite en 0
|
||||||
|
|
||||||
|
### Valores por Defecto
|
||||||
|
|
||||||
|
- **Límite**: 100,000 tokens/mes
|
||||||
|
- **Día de reset**: 1 (primero del mes)
|
||||||
|
- **Unlimited**: `monthly_token_limit = 0`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Esquema de Base de Datos
|
||||||
|
|
||||||
|
### Tabla `users` (Nuevas Columnas)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
monthly_token_limit INTEGER DEFAULT 100000
|
||||||
|
-- Límite mensual de tokens (0 = ilimitado)
|
||||||
|
|
||||||
|
token_limit_reset_day INTEGER DEFAULT 1
|
||||||
|
-- Día del mes para resetear (1-28)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vista `ai_usage_monthly`
|
||||||
|
|
||||||
|
Muestra el uso actual del mes por usuario:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM ai_usage_monthly
|
||||||
|
WHERE user_id = '{user-uuid}';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Columnas:**
|
||||||
|
- `user_id`: UUID del usuario
|
||||||
|
- `organization_id`: UUID de la organización
|
||||||
|
- `usage_month`: Mes de uso
|
||||||
|
- `total_tokens`: Total de tokens consumidos
|
||||||
|
- `input_tokens`: Tokens de entrada (prompts)
|
||||||
|
- `output_tokens`: Tokens de salida (respuestas)
|
||||||
|
- `total_requests`: Número de peticiones a IA
|
||||||
|
- `total_cost_usd`: Costo estimado en USD
|
||||||
|
|
||||||
|
### Funciones SQL
|
||||||
|
|
||||||
|
#### `check_token_limit(user_id, additional_tokens)`
|
||||||
|
|
||||||
|
Verifica si un usuario tiene tokens disponibles.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM check_token_limit(
|
||||||
|
'user-uuid'::UUID,
|
||||||
|
1000 -- Tokens adicionales a verificar
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Retorna:**
|
||||||
|
- `has_available_tokens`: BOOLEAN - ¿Tiene tokens disponibles?
|
||||||
|
- `monthly_limit`: INTEGER - Límite mensual configurado
|
||||||
|
- `used_tokens`: BIGINT - Tokens ya usados este mes
|
||||||
|
- `remaining_tokens`: BIGINT - Tokens restantes
|
||||||
|
- `reset_date`: TIMESTAMPTZ - Fecha del próximo reset
|
||||||
|
|
||||||
|
#### `get_user_usage_stats(user_id, months)`
|
||||||
|
|
||||||
|
Obtiene estadísticas históricas de uso.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM get_user_usage_stats(
|
||||||
|
'user-uuid'::UUID,
|
||||||
|
3 -- Últimos 3 meses
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Retorna:**
|
||||||
|
- `month_start`: DATE - Inicio del mes
|
||||||
|
- `total_tokens`: BIGINT - Tokens totales
|
||||||
|
- `input_tokens`: BIGINT - Tokens de entrada
|
||||||
|
- `output_tokens`: BIGINT - Tokens de salida
|
||||||
|
- `total_requests`: BIGINT - Número de peticiones
|
||||||
|
- `total_cost_usd`: NUMERIC - Costo estimado
|
||||||
|
- `monthly_limit`: INTEGER - Límite del mes
|
||||||
|
- `percentage_used`: NUMERIC - Porcentaje usado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### 1. Establecer Límite Mensual
|
||||||
|
|
||||||
|
```http
|
||||||
|
PUT /api/admin/users/{user_id}/token-limit
|
||||||
|
Authorization: Bearer {admin_token}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"monthly_token_limit": 50000,
|
||||||
|
"token_limit_reset_day": 15
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parámetros:**
|
||||||
|
- `monthly_token_limit` (integer): Límite mensual (0 = ilimitado)
|
||||||
|
- `token_limit_reset_day` (integer, opcional): Día de reset (1-28, default: 1)
|
||||||
|
|
||||||
|
**Respuesta:** `200 OK`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Ver Uso de Tokens de Usuario
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/admin/users/{user_id}/token-usage
|
||||||
|
Authorization: Bearer {admin_token}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Respuesta:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_id": "uuid",
|
||||||
|
"email": "user@example.com",
|
||||||
|
"full_name": "Nombre Usuario",
|
||||||
|
"monthly_token_limit": 100000,
|
||||||
|
"token_limit_reset_day": 1,
|
||||||
|
"used_tokens": 45678,
|
||||||
|
"total_requests": 234,
|
||||||
|
"total_cost_usd": 0.05,
|
||||||
|
"last_used": "2026-03-23T10:30:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Verificar Límite Disponible
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/admin/users/{user_id}/token-limit/check
|
||||||
|
Authorization: Bearer {admin_token}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Respuesta:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"has_available_tokens": true,
|
||||||
|
"monthly_limit": 100000,
|
||||||
|
"used_tokens": 45678,
|
||||||
|
"remaining_tokens": 54322,
|
||||||
|
"reset_date": "2026-04-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ejemplos de Uso
|
||||||
|
|
||||||
|
### Ejemplo 1: Establecer Límite de 50K Tokens
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X PUT "http://localhost:3001/api/admin/users/{user-id}/token-limit" \
|
||||||
|
-H "Authorization: Bearer {admin-token}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"monthly_token_limit": 50000,
|
||||||
|
"token_limit_reset_day": 1
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ejemplo 2: Verificar Tokens Disponibles
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:3001/api/admin/users/{user-id}/token-limit/check" \
|
||||||
|
-H "Authorization: Bearer {admin-token}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ejemplo 3: Obtener Uso del Mes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:3001/api/admin/users/{user-id}/token-usage" \
|
||||||
|
-H "Authorization: Bearer {admin-token}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ejemplo 4: SQL Directo
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Ver uso actual de todos los usuarios
|
||||||
|
SELECT
|
||||||
|
u.email,
|
||||||
|
u.full_name,
|
||||||
|
u.monthly_token_limit,
|
||||||
|
COALESCE(SUM(au.tokens_used), 0) as used_tokens,
|
||||||
|
u.monthly_token_limit - COALESCE(SUM(au.tokens_used), 0) as remaining_tokens,
|
||||||
|
CASE
|
||||||
|
WHEN u.monthly_token_limit = 0 THEN 'Unlimited'
|
||||||
|
ELSE ROUND((COALESCE(SUM(au.tokens_used), 0)::NUMERIC / u.monthly_token_limit * 100)::NUMERIC, 2) || '%'
|
||||||
|
END as usage_percentage
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN ai_usage_logs au ON u.id = au.user_id
|
||||||
|
AND au.created_at >= DATE_TRUNC('month', NOW())
|
||||||
|
GROUP BY u.id, u.email, u.full_name, u.monthly_token_limit
|
||||||
|
ORDER BY used_tokens DESC;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estrategias de Límites
|
||||||
|
|
||||||
|
### Por Rol de Usuario
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Estudiantes: 50K tokens/mes
|
||||||
|
UPDATE users SET monthly_token_limit = 50000 WHERE role = 'student';
|
||||||
|
|
||||||
|
-- Instructores: 200K tokens/mes
|
||||||
|
UPDATE users SET monthly_token_limit = 200000 WHERE role = 'instructor';
|
||||||
|
|
||||||
|
-- Admins: Ilimitado
|
||||||
|
UPDATE users SET monthly_token_limit = 0 WHERE role = 'admin';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Por Tipo de Plan
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Plan Básico: 25K tokens
|
||||||
|
UPDATE users SET monthly_token_limit = 25000 WHERE plan = 'basic';
|
||||||
|
|
||||||
|
-- Plan Pro: 100K tokens
|
||||||
|
UPDATE users SET monthly_token_limit = 100000 WHERE plan = 'pro';
|
||||||
|
|
||||||
|
-- Plan Enterprise: Ilimitado
|
||||||
|
UPDATE users SET monthly_token_limit = 0 WHERE plan = 'enterprise';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reset en Días Diferentes
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Reset el día 15 (mitad de mes)
|
||||||
|
UPDATE users SET token_limit_reset_day = 15 WHERE organization_id = 'org-uuid';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementación de Enforce
|
||||||
|
|
||||||
|
Para hacer cumplir los límites a nivel de API, verifica antes de cada solicitud de IA:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Ejemplo en handler de IA
|
||||||
|
pub async fn chat_with_tutor(...) -> Result<...> {
|
||||||
|
// 1. Verificar límite de tokens
|
||||||
|
let limit_check: TokenLimitCheck = sqlx::query_as(
|
||||||
|
"SELECT * FROM check_token_limit($1, 1000)" // 1000 tokens estimados
|
||||||
|
)
|
||||||
|
.bind(claims.sub)
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !limit_check.has_available_tokens {
|
||||||
|
return Err((
|
||||||
|
StatusCode::TOO_MANY_REQUESTS,
|
||||||
|
format!(
|
||||||
|
"Monthly token limit exceeded. Reset date: {}",
|
||||||
|
limit_check.reset_date
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Proceder con solicitud de IA
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// 3. Loguear uso (ya implementado)
|
||||||
|
sqlx::query("SELECT log_ai_usage(...)").execute(&pool).await?;
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Monitoreo y Alertas
|
||||||
|
|
||||||
|
### Dashboard de Uso
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Usuarios que han usado > 80% de su límite
|
||||||
|
SELECT
|
||||||
|
u.email,
|
||||||
|
u.full_name,
|
||||||
|
u.monthly_token_limit,
|
||||||
|
SUM(au.tokens_used) as used_tokens,
|
||||||
|
ROUND((SUM(au.tokens_used)::NUMERIC / NULLIF(u.monthly_token_limit, 0) * 100)::NUMERIC, 2) as percentage_used
|
||||||
|
FROM users u
|
||||||
|
JOIN ai_usage_logs au ON u.id = au.user_id
|
||||||
|
WHERE au.created_at >= DATE_TRUNC('month', NOW())
|
||||||
|
AND u.monthly_token_limit > 0
|
||||||
|
GROUP BY u.id, u.email, u.full_name, u.monthly_token_limit
|
||||||
|
HAVING SUM(au.tokens_used)::NUMERIC / NULLIF(u.monthly_token_limit, 0) > 0.8
|
||||||
|
ORDER BY percentage_used DESC;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notificación de Límite Alcanzado
|
||||||
|
|
||||||
|
Configura webhooks o emails cuando un usuario alcance el 80%, 90% y 100% de su límite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Usuario Reporta que No Puede Usar IA
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Verificar límite y uso
|
||||||
|
SELECT * FROM check_token_limit('user-uuid'::UUID, 0);
|
||||||
|
|
||||||
|
-- Ver historial de uso
|
||||||
|
SELECT * FROM get_user_usage_stats('user-uuid'::UUID, 1);
|
||||||
|
|
||||||
|
-- Ver logs de errores
|
||||||
|
SELECT * FROM ai_usage_logs
|
||||||
|
WHERE user_id = 'user-uuid'
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 10;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resetear Contador de Usuario
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Establecer límite ilimitado temporalmente
|
||||||
|
UPDATE users SET monthly_token_limit = 0 WHERE id = 'user-uuid';
|
||||||
|
|
||||||
|
-- O aumentar límite
|
||||||
|
UPDATE users SET monthly_token_limit = 200000 WHERE id = 'user-uuid';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- **Migración:** `services/cms-service/migrations/20260323000000_monthly_token_limits.sql`
|
||||||
|
- **Handlers:** `services/cms-service/src/handlers_admin.rs`
|
||||||
|
- **Endpoints:** `services/cms-service/src/main.rs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Fecha:** 2026-03-23
|
||||||
|
**Versión:** OpenCCB 0.2.1
|
||||||
Reference in New Issue
Block a user