Amélioration
This commit is contained in:
1 parent
add66a9310
commit
e0145c8727
46 files changed
+305
No files matched your search
@@ -7,6 +7,10 @@ import { fileURLToPath } from 'url';
|
||||
import sharp from 'sharp';
|
||||
import db from '../db/index.js';
|
||||
import { HttpError } from '../middleware/errorHandler.js';
|
||||
import { getDefaultActiveProvider } from '../ai/providerConfig.js';
|
||||
import { decryptSecret } from '../ai/crypto.js';
|
||||
import { lookupInstitution } from '../ai/providers/anthropic.js';
|
||||
import { audit } from '../utils/audit.js';
|
||||
|
||||
/**
|
||||
* routes/institutionsReferentiel.js — Référentiel admin des institutions financières
|
||||
@@ -281,4 +285,68 @@ router.delete('/:id/icone', (req, res, next) => {
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
|
||||
// ── POST /api/institutions-referentiel/ai-lookup ────────────────────────────
|
||||
// Icône "baguette magique" d'InstitutionModal (demande Olivier 27/09/26) : recherche
|
||||
// web automatique via le connecteur IA (Admin > Intelligence artificielle), pour
|
||||
// pré-remplir au minimum description + domiciliation depuis le nom (et l'URL si
|
||||
// déjà saisie) tapés dans le formulaire — fonctionne aussi bien en création (avant
|
||||
// tout enregistrement) qu'en édition. Réponse toujours 200 : { ok:false, error }
|
||||
// n'est pas une erreur HTTP mais un échec applicatif (IA désactivée, aucun
|
||||
// fournisseur actif, type de fournisseur non pris en charge...) — même précédent
|
||||
// que POST /providers/:id/test dans adminAi.js.
|
||||
const AiLookupSchema = z.object({
|
||||
nom: z.string().min(1).max(200),
|
||||
url: z.string().url().optional().or(z.literal('')).nullable(),
|
||||
});
|
||||
|
||||
router.post('/ai-lookup', async (req, res, next) => {
|
||||
try {
|
||||
const { nom, url } = AiLookupSchema.parse(req.body);
|
||||
|
||||
const config = db.prepare('SELECT actif FROM ai_config WHERE id = 1').get();
|
||||
if (!config?.actif) {
|
||||
return res.json({ ok: false, error: "L'Assistant IA n'est pas activé (Admin > Intelligence artificielle)." });
|
||||
}
|
||||
|
||||
const provider = getDefaultActiveProvider();
|
||||
if (!provider) {
|
||||
return res.json({ ok: false, error: "Aucun fournisseur IA actif n'est configuré." });
|
||||
}
|
||||
// Seul le fournisseur "anthropic" (natif) expose l'outil de recherche web
|
||||
// serveur utilisé par lookupInstitution() — un fournisseur openai_compatible
|
||||
// n'a aucune garantie d'accès web réel selon le modèle/service derrière
|
||||
// base_url, on préfère donc refuser explicitement plutôt que de renvoyer une
|
||||
// réponse non vérifiée présentée comme une recherche internet.
|
||||
if (provider.type !== 'anthropic') {
|
||||
return res.json({
|
||||
ok: false,
|
||||
error: `La recherche web automatique n'est prise en charge que pour un fournisseur Anthropic (natif) — le fournisseur actif ("${provider.nom}") est de type "${provider.type}".`,
|
||||
});
|
||||
}
|
||||
|
||||
let apiKey;
|
||||
try {
|
||||
apiKey = decryptSecret(provider.api_key_chiffree);
|
||||
} catch (e) {
|
||||
throw new HttpError(500, `Impossible de déchiffrer la clé API : ${e.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await lookupInstitution({ apiKey, model: provider.modele, nom, url: url || undefined });
|
||||
audit(req, {
|
||||
action: 'institution_ai_lookup', category: 'ai', actorId: req.user.id,
|
||||
details: { nom, ok: true, dureeMs: result.durationMs, sourcesCount: result.sourcesCount },
|
||||
});
|
||||
res.json({ ok: true, description: result.description, domiciliation: result.domiciliation, dureeMs: result.durationMs });
|
||||
} catch (e) {
|
||||
audit(req, {
|
||||
action: 'institution_ai_lookup', category: 'ai', actorId: req.user.id,
|
||||
details: { nom, ok: false, error: e.message },
|
||||
});
|
||||
res.json({ ok: false, error: e.message });
|
||||
}
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in new issue
Block a user