Revue : pistes ecartees et source (local/OpenRouter) gardees, historisation pour rejeu, carte verbeuse du conseiller

This commit is contained in:
Tallagrand Thierry
2026-10-08 05:43:54 +02:00
parent 78f434778b
commit b25770736f
7 changed files with 206 additions and 41 deletions

View File

@@ -7,7 +7,7 @@
// les autres a /api/services/<domain>/<service>.
// =====================================================
const { status, detail, pistes, rejetees, modelUsed, context } = $input.first().json;
const { status, detail, pistes, rejetees, ecartees, source, modelUsed, context } = $input.first().json;
function stampParis() {
const p = new Intl.DateTimeFormat('fr-FR', { timeZone: 'Europe/Paris', day: '2-digit', month: '2-digit',
@@ -22,12 +22,17 @@ const setText = (entity, value) => push('input_text', 'set_value', { entity_id:
setText('input_text.evcs_advisor_status', status + (detail ? `: ${detail}` : ''));
setText('input_text.evcs_advisor_last_run', stampParis());
setText('input_text.evcs_advisor_model', modelUsed);
setText('input_text.evcs_advisor_model', `${modelUsed} (${source === 'openrouter' ? 'OpenRouter, cloud' : 'local Spark'})`);
if (status === 'ok' && Array.isArray(pistes)) {
calls.push({ json: { state_entity: 'sensor.evcs_advisor_revue', data: {
state: context.jour,
attributes: { friendly_name: 'Revue du conseiller IA', pistes, rejetees, modele: modelUsed,
attributes: { friendly_name: 'Revue du conseiller IA', pistes, rejetees, ecartees: ecartees || [], modele: modelUsed,
source,
source_texte: source === 'openrouter'
? 'Repli OpenRouter (cloud) : le modele local de la Spark n a pas repondu, les constats ont ete envoyes a OpenRouter.'
: 'Modele local de la Spark : rien n est sorti de la maison.',
constats_relus: context.constats.length,
avertissement: 'Avis a verifier, pas des faits. Les constats chiffres sont dans sensor.evcs_revue_jour.' },
} } });
setText('input_text.evcs_advisor_reason',

View File

@@ -10,9 +10,16 @@
const resp = $input.first().json;
const context = $('BUILD_PROMPT').first().json.context;
// QUI A REPONDU (08/10, proprietaire : « OpenRouter, garde-le mais affiche-le
// clairement »). Le repli cloud n'est appele que si le modele local de la Spark
// echoue ; dans ce cas les constats ont quitte la maison et il faut le dire.
const viaCloud = (() => { try { return $('LLM_CLOUD').isExecuted === true; } catch { return false; } })()
|| typeof resp?.provider === 'string' || /^gen-/.test(String(resp?.id || ''));
const source = viaCloud ? 'openrouter' : 'local';
function fail(status, detail) {
return [{ json: { status, detail: (detail || '').toString().slice(0, 200), pistes: null, rejetees: 0,
modelUsed: (resp?.model || 'aucun').toString().slice(0, 60), context } }];
return [{ json: { status, detail: (detail || '').toString().slice(0, 200), pistes: null, rejetees: 0, ecartees: [],
source, modelUsed: (resp?.model || 'aucun').toString().slice(0, 60), context } }];
}
const content = resp?.choices?.[0]?.message?.content;
@@ -36,18 +43,23 @@ if (!Array.isArray(obj.pistes)) return fail('invalid', 'champ pistes absent');
const nombres = (s) => (String(s).match(/\d+(?:[.,]\d+)?/g) || []).map((x) => String(Number(x.replace(',', '.'))));
const parCode = new Map(context.constats.map((c) => [c.code, c]));
// Les pistes ECARTEES sont gardees avec leur motif : elles sont historisees
// pour rejouer le conseiller plus tard (regression) et juger s'il progresse.
const pistes = [];
let rejetees = 0;
const ecartees = [];
const ecarter = (code, piste, motif) => ecartees.push({ code, piste: piste.slice(0, 280), motif });
for (const p of obj.pistes) {
const code = (p?.code ?? '').toString().trim();
const piste = (p?.piste ?? '').toString().trim();
const c = parCode.get(code);
if (!c || !piste) { rejetees++; continue; }
if (!piste) { ecarter(code, '', 'piste vide'); continue; }
if (!c) { ecarter(code, piste, 'code inconnu ou constat non a examiner'); continue; }
const permis = new Set(nombres(`${c.texte} ${c.contexte}`));
if (nombres(piste).some((n) => !permis.has(n))) { rejetees++; continue; }
if (pistes.some((x) => x.code === code)) { rejetees++; continue; }
const inventes = nombres(piste).filter((n) => !permis.has(n));
if (inventes.length) { ecarter(code, piste, `chiffre absent du constat : ${inventes.join(', ')}`); continue; }
if (pistes.some((x) => x.code === code)) { ecarter(code, piste, 'doublon'); continue; }
pistes.push({ code, piste: piste.slice(0, 280) });
}
return [{ json: { status: 'ok', detail: '', pistes, rejetees,
return [{ json: { status: 'ok', detail: '', pistes, rejetees: ecartees.length, ecartees, source,
modelUsed: (resp.model || 'inconnu').toString().slice(0, 60), context } }];