Resumen de Cambios Implementados

Backend v1.2.1
Mejoras en gestión de API keys multi-proveedor:

Nuevo endpoint /api/ai/api-keys: Retorna todas las API keys guardadas por proveedor (enmascaradas para seguridad)

Formato: {"openai": {"has_key": true, "masked_key": "sk-proj...xyz", "is_active": false}}
Solo administradores pueden acceder
Endpoint /api/ai/configuration mejorado:

Ahora preserva API keys existentes cuando se cambia de proveedor
Si ya existe configuración para un proveedor, solo actualiza el modelo y activa ese proveedor
Solo requiere API key nueva si no existe configuración previa para ese proveedor
Validación: no acepta API keys vacías para nuevos proveedores
Persistencia de configuraciones:

Cada proveedor (OpenAI, Anthropic, Gemini) mantiene su registro en la base de datos
Solo uno tiene is_active=True a la vez
Al cambiar de proveedor, se desactiva el anterior pero NO se elimina
Frontend v1.2.6
UX mejorada para configuración de IA:

Indicadores visuales en botones de proveedor:

Badge "✓ ACTIVO" en verde para el proveedor actualmente activo
Badge "Configurado" en gris para proveedores con API key guardada pero inactivos
Sin badges para proveedores no configurados
Selector de modelos inteligente:

Solo muestra modelo seleccionado si el proveedor está activo
Al hacer click en un proveedor inactivo, NO se pre-selecciona ningún modelo
Solo al GUARDAR se activa el proveedor con el modelo seleccionado
Input de API key con contexto:

Muestra key enmascarada si ya existe: ✓ Ya tienes una API key guardada: sk-proj...xyz
Permite dejar vacío para mantener la key actual
Solo requiere key nueva si el proveedor no tiene una guardada
Flujo de trabajo mejorado:

Click en proveedor → Cambia tab de formulario
Si ya tiene key guardada → Se muestra enmascarada, puede mantenerla
Seleccionar modelo → Click en "Guardar Configuración"
Solo entonces se ACTIVA ese proveedor y modelo
Beneficios
No re-ingresar API keys: Al cambiar entre proveedores, las keys se preservan
Claridad visual: Solo el proveedor activo muestra badge verde y modelo seleccionado
Seguridad: API keys enmascaradas en la UI (sk-proj...xyz)
Flexibilidad: Configurar los 3 proveedores y cambiar entre ellos sin perder configuración
Versiones actualizadas:

Backend: 1.2.0 → 1.2.1
Frontend: 1.2.5 → 1.2.6
Service Worker: cache v1.2.6
This commit is contained in:
2025-12-04 11:52:38 -03:00
parent 24eb039302
commit 7f2e9add29
5 changed files with 145 additions and 41 deletions

View File

@@ -276,7 +276,7 @@ def extract_pdf_text_smart(pdf_content: bytes, max_chars: int = None) -> dict:
}
BACKEND_VERSION = "1.2.0"
BACKEND_VERSION = "1.2.1"
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
# S3/MinIO configuration
@@ -2782,20 +2782,50 @@ def get_ai_configuration(
return config
@app.get("/api/ai/api-keys")
def get_all_api_keys(
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
"""Obtener todas las API keys guardadas (sin mostrar las keys completas)"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Solo administradores pueden ver API keys")
configs = db.query(models.AIConfiguration).all()
result = {}
for config in configs:
# Solo devolver si tiene API key guardada (enmascarada)
if config.api_key:
masked_key = config.api_key[:8] + "..." + config.api_key[-4:] if len(config.api_key) > 12 else "***"
result[config.provider] = {
"has_key": True,
"masked_key": masked_key,
"is_active": config.is_active
}
return result
@app.post("/api/ai/configuration", response_model=schemas.AIConfiguration)
def create_ai_configuration(
config: schemas.AIConfigurationCreate,
db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_user)
):
"""Crear o actualizar configuración de IA"""
"""Crear o actualizar configuración de IA - ACTIVA el proveedor seleccionado"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Solo administradores pueden configurar IA")
# Desactivar configuraciones anteriores
# Desactivar TODAS las configuraciones
db.query(models.AIConfiguration).update({"is_active": False})
# Determinar modelo por defecto según el proveedor si no se especifica
# Buscar si ya existe configuración para este proveedor
existing_config = db.query(models.AIConfiguration).filter(
models.AIConfiguration.provider == config.provider
).first()
# Determinar modelo por defecto si no se especifica
model_name = config.model_name
if not model_name:
if config.provider == "openai":
@@ -2807,19 +2837,31 @@ def create_ai_configuration(
else:
model_name = "default"
# Crear nueva configuración
new_config = models.AIConfiguration(
provider=config.provider,
api_key=config.api_key,
model_name=model_name,
is_active=True
)
db.add(new_config)
db.commit()
db.refresh(new_config)
return new_config
if existing_config:
# Actualizar configuración existente
# Solo actualizar API key si se proporciona una nueva (no vacía)
if config.api_key and config.api_key.strip():
existing_config.api_key = config.api_key
existing_config.model_name = model_name
existing_config.is_active = True # Activar este proveedor
db.commit()
db.refresh(existing_config)
return existing_config
else:
# Crear nueva configuración (requiere API key)
if not config.api_key or not config.api_key.strip():
raise HTTPException(status_code=400, detail="API key es requerida para nuevo proveedor")
new_config = models.AIConfiguration(
provider=config.provider,
api_key=config.api_key,
model_name=model_name,
is_active=True # Activar este proveedor
)
db.add(new_config)
db.commit()
db.refresh(new_config)
return new_config
@app.put("/api/ai/configuration/{config_id}", response_model=schemas.AIConfiguration)