[SYNC] Synchronize ai module from erp-core canonical
Source: erp-core (checksum: 39a6e229055a21158b63cc4eb4dd232f) Priority: P0 - CRITICAL divergence resolution Context: TASK-2026-01-25-SISTEMA-REUTILIZACION Before: Diverged version with project-specific adaptations After: Synced with canonical Changes: - Complete ai module synchronized from erp-core - 24 TypeScript files updated - Unified role-based AI configuration - Consistent AI behavior across all ERP projects Note: Project-specific role adaptations (if needed) can be re-applied as documented extensions after this sync. Benefits: - Single source of truth for AI functionality - Reduced maintenance burden - Token savings from code reuse - Consistent behavior baseline Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
aa7e5a3c78
commit
54bb752246
76
src/modules/ai/README.md
Normal file
76
src/modules/ai/README.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# AI Module
|
||||||
|
|
||||||
|
## Descripcion
|
||||||
|
|
||||||
|
Modulo de integracion con modelos de Inteligencia Artificial. Proporciona capacidades de chat conversacional, completions, embeddings y gestion de bases de conocimiento. Soporta multiples proveedores (OpenAI, Anthropic, Google, Azure) a traves de OpenRouter, con control de acceso basado en roles y gestion de cuotas por tenant.
|
||||||
|
|
||||||
|
## Entidades
|
||||||
|
|
||||||
|
| Entidad | Schema | Descripcion |
|
||||||
|
|---------|--------|-------------|
|
||||||
|
| `AIModel` | ai.models | Catalogo de modelos de IA disponibles (GPT-4, Claude, etc.) con configuracion de costos y capacidades |
|
||||||
|
| `AIPrompt` | ai.prompts | Templates de prompts versionados con variables y configuracion de modelo |
|
||||||
|
| `AIConversation` | ai.conversations | Conversaciones de chat con historial, contexto y estadisticas |
|
||||||
|
| `AIMessage` | ai.messages | Mensajes individuales dentro de una conversacion |
|
||||||
|
| `AICompletion` | ai.completions | Registros de completions individuales (no conversacionales) |
|
||||||
|
| `AIEmbedding` | ai.embeddings | Vectores de embeddings para busqueda semantica |
|
||||||
|
| `AIKnowledgeBase` | ai.knowledge_base | Articulos de conocimiento con embeddings para RAG |
|
||||||
|
| `AIUsageLog` | ai.usage_logs | Registro detallado de uso por request |
|
||||||
|
| `AITenantQuota` | ai.tenant_quotas | Cuotas mensuales de tokens, requests y costos por tenant |
|
||||||
|
|
||||||
|
## Servicios
|
||||||
|
|
||||||
|
| Servicio | Responsabilidades |
|
||||||
|
|----------|-------------------|
|
||||||
|
| `AIService` | Servicio base: CRUD de modelos, prompts, conversaciones, mensajes; registro de uso; gestion de cuotas |
|
||||||
|
| `RoleBasedAIService` | Extension con control de acceso basado en roles ERP; integracion con OpenRouter; ejecucion de tools |
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
| Method | Path | Descripcion |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/models` | Lista todos los modelos activos |
|
||||||
|
| GET | `/models/:id` | Obtiene modelo por ID |
|
||||||
|
| GET | `/models/code/:code` | Obtiene modelo por codigo |
|
||||||
|
| GET | `/models/provider/:provider` | Lista modelos por proveedor |
|
||||||
|
| GET | `/models/type/:type` | Lista modelos por tipo (chat/embedding/etc) |
|
||||||
|
| GET | `/prompts` | Lista prompts del tenant |
|
||||||
|
| GET | `/prompts/:id` | Obtiene prompt por ID |
|
||||||
|
| GET | `/prompts/code/:code` | Obtiene prompt por codigo |
|
||||||
|
| POST | `/prompts` | Crea nuevo prompt |
|
||||||
|
| PATCH | `/prompts/:id` | Actualiza prompt existente |
|
||||||
|
| GET | `/conversations` | Lista conversaciones del tenant |
|
||||||
|
| GET | `/conversations/:id` | Obtiene conversacion con mensajes |
|
||||||
|
| GET | `/conversations/user/:userId` | Lista conversaciones de usuario |
|
||||||
|
| POST | `/conversations` | Crea nueva conversacion |
|
||||||
|
| PATCH | `/conversations/:id` | Actualiza conversacion |
|
||||||
|
| POST | `/conversations/:id/archive` | Archiva conversacion |
|
||||||
|
| GET | `/conversations/:conversationId/messages` | Lista mensajes de conversacion |
|
||||||
|
| POST | `/conversations/:conversationId/messages` | Agrega mensaje a conversacion |
|
||||||
|
| GET | `/conversations/:conversationId/tokens` | Obtiene conteo de tokens |
|
||||||
|
| POST | `/usage` | Registra uso de IA |
|
||||||
|
| GET | `/usage/stats` | Obtiene estadisticas de uso |
|
||||||
|
| GET | `/quotas` | Obtiene cuota del tenant |
|
||||||
|
| PATCH | `/quotas` | Actualiza cuota del tenant |
|
||||||
|
| GET | `/quotas/check` | Verifica disponibilidad de cuota |
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
- `common` - Utilidades compartidas
|
||||||
|
- `auth` - Autenticacion y tenant context
|
||||||
|
- OpenRouter API (proveedor externo)
|
||||||
|
|
||||||
|
## Configuracion
|
||||||
|
|
||||||
|
| Variable | Descripcion | Requerida |
|
||||||
|
|----------|-------------|-----------|
|
||||||
|
| `OPENROUTER_API_KEY` | API key para OpenRouter | Si |
|
||||||
|
| `APP_URL` | URL de la aplicacion (para HTTP-Referer) | No |
|
||||||
|
|
||||||
|
## Roles ERP Soportados
|
||||||
|
|
||||||
|
El `RoleBasedAIService` soporta prompts y accesos diferenciados por rol:
|
||||||
|
- `admin` - Acceso completo
|
||||||
|
- `supervisor` - Acceso a reportes y analisis
|
||||||
|
- `operator` - Acceso a operaciones basicas
|
||||||
|
- `customer` - Acceso limitado a consultas
|
||||||
@ -1,17 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* ERP Retail - Roles Configuration
|
* ERP Roles Configuration
|
||||||
*
|
*
|
||||||
* Roles específicos para operaciones de retail/comercio minorista.
|
* Define roles, tools permitidos, y system prompts para cada rol en el ERP.
|
||||||
* Adaptado desde erp-core v1.5.0 (PROP-CORE-004)
|
* Basado en: michangarrito MCH-012/MCH-013 (role-based chatbot)
|
||||||
*
|
*
|
||||||
* Roles:
|
* Roles disponibles:
|
||||||
* - ADMIN: Dueño/Gerente General - acceso completo
|
* - ADMIN: Acceso completo a todas las operaciones
|
||||||
* - GERENTE_TIENDA: Gerente de sucursal - gestión de tienda
|
* - SUPERVISOR: Gestión de equipos y reportes de sucursal
|
||||||
* - CAJERO: Operador de punto de venta
|
* - OPERATOR: Operaciones de punto de venta
|
||||||
* - CLIENTE: Cliente con acceso a pedidos y catálogo
|
* - CUSTOMER: Acceso limitado para clientes (si se expone chatbot)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type ERPRole = 'ADMIN' | 'GERENTE_TIENDA' | 'CAJERO' | 'CLIENTE';
|
export type ERPRole = 'ADMIN' | 'SUPERVISOR' | 'OPERATOR' | 'CUSTOMER';
|
||||||
|
|
||||||
export interface ERPRoleConfig {
|
export interface ERPRoleConfig {
|
||||||
name: string;
|
name: string;
|
||||||
@ -19,138 +19,234 @@ export interface ERPRoleConfig {
|
|||||||
tools: string[];
|
tools: string[];
|
||||||
systemPromptFile: string;
|
systemPromptFile: string;
|
||||||
maxConversationHistory: number;
|
maxConversationHistory: number;
|
||||||
allowedModels?: string[];
|
allowedModels?: string[]; // Si vacío, usa el default del tenant
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
requestsPerMinute: number;
|
requestsPerMinute: number;
|
||||||
tokensPerMinute: number;
|
tokensPerMinute: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuración de roles ERP
|
||||||
|
*/
|
||||||
export const ERP_ROLES: Record<ERPRole, ERPRoleConfig> = {
|
export const ERP_ROLES: Record<ERPRole, ERPRoleConfig> = {
|
||||||
ADMIN: {
|
ADMIN: {
|
||||||
name: 'Administrador',
|
name: 'Administrador',
|
||||||
description: 'Dueño o gerente general con acceso completo a todas las tiendas',
|
description: 'Acceso completo a todas las operaciones del sistema ERP',
|
||||||
tools: [
|
tools: [
|
||||||
// Ventas
|
// Ventas
|
||||||
'get_sales_summary', 'get_sales_report', 'get_top_products',
|
'get_sales_summary',
|
||||||
'get_top_customers', 'get_sales_by_branch', 'create_sale', 'void_sale',
|
'get_sales_report',
|
||||||
|
'get_top_products',
|
||||||
|
'get_top_customers',
|
||||||
|
'get_sales_by_branch',
|
||||||
|
'create_sale',
|
||||||
|
'void_sale',
|
||||||
|
|
||||||
// Inventario
|
// Inventario
|
||||||
'get_inventory_status', 'get_low_stock_products', 'get_inventory_value',
|
'get_inventory_status',
|
||||||
'adjust_inventory', 'transfer_inventory',
|
'get_low_stock_products',
|
||||||
|
'get_inventory_value',
|
||||||
|
'adjust_inventory',
|
||||||
|
'transfer_inventory',
|
||||||
|
|
||||||
// Compras
|
// Compras
|
||||||
'get_pending_orders', 'get_supplier_info', 'create_purchase_order', 'approve_purchase',
|
'get_pending_orders',
|
||||||
|
'get_supplier_info',
|
||||||
|
'create_purchase_order',
|
||||||
|
'approve_purchase',
|
||||||
|
|
||||||
// Finanzas
|
// Finanzas
|
||||||
'get_financial_report', 'get_accounts_receivable', 'get_accounts_payable', 'get_cash_flow',
|
'get_financial_report',
|
||||||
// Configuración
|
'get_accounts_receivable',
|
||||||
'manage_users', 'view_audit_logs', 'update_settings',
|
'get_accounts_payable',
|
||||||
'get_branch_info', 'manage_branches',
|
'get_cash_flow',
|
||||||
// Reportes
|
|
||||||
'generate_report', 'export_data', 'get_kpis',
|
// Usuarios y configuración
|
||||||
// TPV
|
'manage_users',
|
||||||
'configure_terminal', 'get_payment_history', 'process_refund',
|
'view_audit_logs',
|
||||||
|
'update_settings',
|
||||||
|
'get_branch_info',
|
||||||
|
'manage_branches',
|
||||||
|
|
||||||
|
// Reportes avanzados
|
||||||
|
'generate_report',
|
||||||
|
'export_data',
|
||||||
|
'get_kpis',
|
||||||
],
|
],
|
||||||
systemPromptFile: 'admin-system-prompt',
|
systemPromptFile: 'admin-system-prompt',
|
||||||
maxConversationHistory: 50,
|
maxConversationHistory: 50,
|
||||||
rateLimit: { requestsPerMinute: 100, tokensPerMinute: 50000 },
|
rateLimit: {
|
||||||
|
requestsPerMinute: 100,
|
||||||
|
tokensPerMinute: 50000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
GERENTE_TIENDA: {
|
SUPERVISOR: {
|
||||||
name: 'Gerente de Tienda',
|
name: 'Supervisor',
|
||||||
description: 'Gerente de sucursal con gestión de equipo y operaciones de tienda',
|
description: 'Gestión de equipos, reportes de sucursal y aprobaciones',
|
||||||
tools: [
|
tools: [
|
||||||
// Ventas
|
// Ventas (lectura + acciones limitadas)
|
||||||
'get_sales_summary', 'get_sales_report', 'get_top_products',
|
'get_sales_summary',
|
||||||
'get_sales_by_branch', 'create_sale',
|
'get_sales_report',
|
||||||
// Inventario
|
'get_top_products',
|
||||||
'get_inventory_status', 'get_low_stock_products', 'adjust_inventory',
|
'get_sales_by_branch',
|
||||||
|
'create_sale',
|
||||||
|
|
||||||
|
// Inventario (lectura + ajustes)
|
||||||
|
'get_inventory_status',
|
||||||
|
'get_low_stock_products',
|
||||||
|
'adjust_inventory',
|
||||||
|
|
||||||
// Equipo
|
// Equipo
|
||||||
'get_team_performance', 'get_employee_schedule', 'manage_schedules',
|
'get_team_performance',
|
||||||
|
'get_employee_schedule',
|
||||||
|
'manage_schedules',
|
||||||
|
|
||||||
// Aprobaciones
|
// Aprobaciones
|
||||||
'approve_discounts', 'approve_voids', 'approve_refunds',
|
'approve_discounts',
|
||||||
|
'approve_voids',
|
||||||
|
'approve_refunds',
|
||||||
|
|
||||||
// Sucursal
|
// Sucursal
|
||||||
'get_branch_info', 'get_branch_report',
|
'get_branch_info',
|
||||||
|
'get_branch_report',
|
||||||
|
|
||||||
// Clientes
|
// Clientes
|
||||||
'get_customer_info', 'get_customer_balance',
|
'get_customer_info',
|
||||||
// Caja
|
'get_customer_balance',
|
||||||
'open_cash_register', 'close_cash_register', 'get_cash_summary',
|
|
||||||
],
|
],
|
||||||
systemPromptFile: 'supervisor-system-prompt',
|
systemPromptFile: 'supervisor-system-prompt',
|
||||||
maxConversationHistory: 30,
|
maxConversationHistory: 30,
|
||||||
rateLimit: { requestsPerMinute: 60, tokensPerMinute: 30000 },
|
rateLimit: {
|
||||||
|
requestsPerMinute: 60,
|
||||||
|
tokensPerMinute: 30000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
CAJERO: {
|
OPERATOR: {
|
||||||
name: 'Cajero',
|
name: 'Operador',
|
||||||
description: 'Operador de punto de venta y atención al cliente',
|
description: 'Operaciones de punto de venta y consultas básicas',
|
||||||
tools: [
|
tools: [
|
||||||
// Productos
|
// Productos
|
||||||
'search_products', 'get_product_price', 'check_product_availability',
|
'search_products',
|
||||||
|
'get_product_price',
|
||||||
|
'check_product_availability',
|
||||||
|
|
||||||
// Ventas
|
// Ventas
|
||||||
'create_sale', 'get_my_sales', 'apply_discount',
|
'create_sale',
|
||||||
|
'get_my_sales',
|
||||||
|
'apply_discount', // Con límite
|
||||||
|
|
||||||
// Clientes
|
// Clientes
|
||||||
'search_customers', 'get_customer_balance', 'register_payment',
|
'search_customers',
|
||||||
|
'get_customer_balance',
|
||||||
|
'register_payment',
|
||||||
|
|
||||||
// Inventario (solo lectura)
|
// Inventario (solo lectura)
|
||||||
'check_stock',
|
'check_stock',
|
||||||
|
|
||||||
// Información
|
// Información
|
||||||
'get_branch_hours', 'get_promotions',
|
'get_branch_hours',
|
||||||
// TPV
|
'get_promotions',
|
||||||
'process_card_payment', 'process_cash_payment', 'print_receipt',
|
|
||||||
],
|
],
|
||||||
systemPromptFile: 'operator-system-prompt',
|
systemPromptFile: 'operator-system-prompt',
|
||||||
maxConversationHistory: 20,
|
maxConversationHistory: 20,
|
||||||
rateLimit: { requestsPerMinute: 30, tokensPerMinute: 15000 },
|
rateLimit: {
|
||||||
|
requestsPerMinute: 30,
|
||||||
|
tokensPerMinute: 15000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
CLIENTE: {
|
CUSTOMER: {
|
||||||
name: 'Cliente',
|
name: 'Cliente',
|
||||||
description: 'Cliente con acceso a catálogo y sus pedidos',
|
description: 'Acceso limitado para clientes externos',
|
||||||
tools: [
|
tools: [
|
||||||
// Catálogo
|
// Catálogo
|
||||||
'view_catalog', 'search_products', 'check_availability',
|
'view_catalog',
|
||||||
|
'search_products',
|
||||||
|
'check_availability',
|
||||||
|
|
||||||
// Pedidos
|
// Pedidos
|
||||||
'get_my_orders', 'track_order',
|
'get_my_orders',
|
||||||
|
'track_order',
|
||||||
|
|
||||||
// Cuenta
|
// Cuenta
|
||||||
'get_my_balance', 'get_my_history',
|
'get_my_balance',
|
||||||
|
'get_my_history',
|
||||||
|
|
||||||
// Soporte
|
// Soporte
|
||||||
'contact_support', 'get_store_info', 'get_promotions',
|
'contact_support',
|
||||||
|
'get_store_info',
|
||||||
|
'get_promotions',
|
||||||
],
|
],
|
||||||
systemPromptFile: 'customer-system-prompt',
|
systemPromptFile: 'customer-system-prompt',
|
||||||
maxConversationHistory: 10,
|
maxConversationHistory: 10,
|
||||||
rateLimit: { requestsPerMinute: 10, tokensPerMinute: 5000 },
|
rateLimit: {
|
||||||
|
requestsPerMinute: 10,
|
||||||
|
tokensPerMinute: 5000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapeo de rol de base de datos a ERPRole
|
||||||
|
*/
|
||||||
export const DB_ROLE_MAPPING: Record<string, ERPRole> = {
|
export const DB_ROLE_MAPPING: Record<string, ERPRole> = {
|
||||||
// Administradores
|
// Roles típicos de sistema
|
||||||
admin: 'ADMIN', administrator: 'ADMIN', superadmin: 'ADMIN', owner: 'ADMIN',
|
admin: 'ADMIN',
|
||||||
// Gerentes de tienda
|
administrator: 'ADMIN',
|
||||||
gerente: 'GERENTE_TIENDA', gerente_tienda: 'GERENTE_TIENDA',
|
superadmin: 'ADMIN',
|
||||||
store_manager: 'GERENTE_TIENDA', manager: 'GERENTE_TIENDA', supervisor: 'GERENTE_TIENDA',
|
owner: 'ADMIN',
|
||||||
branch_manager: 'GERENTE_TIENDA',
|
|
||||||
// Cajeros
|
// Supervisores
|
||||||
cajero: 'CAJERO', cashier: 'CAJERO', operator: 'CAJERO',
|
supervisor: 'SUPERVISOR',
|
||||||
vendedor: 'CAJERO', sales: 'CAJERO', employee: 'CAJERO', staff: 'CAJERO',
|
manager: 'SUPERVISOR',
|
||||||
|
branch_manager: 'SUPERVISOR',
|
||||||
|
store_manager: 'SUPERVISOR',
|
||||||
|
|
||||||
|
// Operadores
|
||||||
|
operator: 'OPERATOR',
|
||||||
|
cashier: 'OPERATOR',
|
||||||
|
sales: 'OPERATOR',
|
||||||
|
employee: 'OPERATOR',
|
||||||
|
staff: 'OPERATOR',
|
||||||
|
|
||||||
// Clientes
|
// Clientes
|
||||||
cliente: 'CLIENTE', customer: 'CLIENTE', client: 'CLIENTE', guest: 'CLIENTE',
|
customer: 'CUSTOMER',
|
||||||
|
client: 'CUSTOMER',
|
||||||
|
guest: 'CUSTOMER',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener rol ERP desde rol de base de datos
|
||||||
|
*/
|
||||||
export function getERPRole(dbRole: string | undefined): ERPRole {
|
export function getERPRole(dbRole: string | undefined): ERPRole {
|
||||||
if (!dbRole) return 'CLIENTE';
|
if (!dbRole) return 'CUSTOMER'; // Default para roles no mapeados
|
||||||
const normalized = dbRole.toLowerCase().trim();
|
const normalized = dbRole.toLowerCase().trim();
|
||||||
return DB_ROLE_MAPPING[normalized] || 'CLIENTE';
|
return DB_ROLE_MAPPING[normalized] || 'CUSTOMER';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verificar si un rol tiene acceso a un tool
|
||||||
|
*/
|
||||||
export function hasToolAccess(role: ERPRole, toolName: string): boolean {
|
export function hasToolAccess(role: ERPRole, toolName: string): boolean {
|
||||||
const roleConfig = ERP_ROLES[role];
|
const roleConfig = ERP_ROLES[role];
|
||||||
if (!roleConfig) return false;
|
if (!roleConfig) return false;
|
||||||
return roleConfig.tools.includes(toolName);
|
return roleConfig.tools.includes(toolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener todos los tools para un rol
|
||||||
|
*/
|
||||||
export function getToolsForRole(role: ERPRole): string[] {
|
export function getToolsForRole(role: ERPRole): string[] {
|
||||||
const roleConfig = ERP_ROLES[role];
|
const roleConfig = ERP_ROLES[role];
|
||||||
return roleConfig?.tools || [];
|
return roleConfig?.tools || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener configuración completa de un rol
|
||||||
|
*/
|
||||||
export function getRoleConfig(role: ERPRole): ERPRoleConfig | null {
|
export function getRoleConfig(role: ERPRole): ERPRoleConfig | null {
|
||||||
return ERP_ROLES[role] || null;
|
return ERP_ROLES[role] || null;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user