refacto: dashboard Flask Blueprints + templates Jinja2, engine run/lore_enricher mis a jour
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<div class="info-bar">
|
||||
<span style="font-weight:600">🎲 Règles Sim — Propositions LLM</span>
|
||||
<div class="lore-stat-bar" style="margin:0">
|
||||
<span class="lore-stat">En attente : <span style="color:var(--amber)">{{ sim_stats.pending }}</span></span>
|
||||
<span class="lore-stat">Acceptées : <span style="color:var(--green)">{{ sim_stats.accepted }}</span></span>
|
||||
<span class="lore-stat">Rejetées : <span style="color:var(--red)">{{ sim_stats.rejected }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lore-filters">
|
||||
<label>Type :</label>
|
||||
<select onchange="location.href='/sim-proposals?type='+this.value+'&status='+document.getElementById('sp-status').value">
|
||||
<option value="" {{ 'selected' if not filt_type else '' }}>— tous —</option>
|
||||
{% for t in sim_types %}
|
||||
<option value="{{ t }}" {{ 'selected' if filt_type==t else '' }}>{{ t }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label>Statut :</label>
|
||||
<select id="sp-status" onchange="location.href='/sim-proposals?type='+document.querySelector('[onchange]').value+'&status='+this.value">
|
||||
<option value="pending" {{ 'selected' if filt_status=='pending' else '' }}>pending</option>
|
||||
<option value="accepted" {{ 'selected' if filt_status=='accepted' else '' }}>accepted</option>
|
||||
<option value="rejected" {{ 'selected' if filt_status=='rejected' else '' }}>rejected</option>
|
||||
<option value="" {{ 'selected' if not filt_status else '' }}>— tous —</option>
|
||||
</select>
|
||||
<span style="font-size:12px;color:var(--dim)">{{ proposals|length }} proposition(s)</span>
|
||||
</div>
|
||||
|
||||
{% if not proposals %}
|
||||
<div class="panel" style="color:var(--dim);text-align:center;padding:24px">
|
||||
Aucune proposition.<br>
|
||||
<small style="color:var(--border)">Lancer : <code>python3 sim_enricher.py --type all</code> sur Ampère</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for p in proposals %}
|
||||
<div class="lore-card">
|
||||
<div class="lore-meta">
|
||||
<span>#{{ p.id }}</span>
|
||||
<span class="badge-{{ p.status }}">{{ p.status | upper }}</span>
|
||||
<span class="change-type-badge">{{ p.proposal_type }}</span>
|
||||
<span style="color:var(--dim)">{{ p.created_at.strftime('%d/%m %H:%M') if p.created_at else '' }}</span>
|
||||
</div>
|
||||
|
||||
<h3>{{ p.title }}</h3>
|
||||
<p style="color:var(--text);margin:6px 0;font-size:13px">{{ p.description }}</p>
|
||||
|
||||
{% if p.rationale %}
|
||||
<p style="color:var(--dim);font-size:12px;font-style:italic">💡 {{ p.rationale }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if p.json_patch %}
|
||||
<div class="diff-new" style="font-size:11px;white-space:pre-wrap;max-height:200px;overflow:auto">{{ p.json_patch | tojson(indent=2) }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if p.status == 'pending' %}
|
||||
<div class="lore-actions" style="margin-top:10px">
|
||||
<form method="POST" action="/sim-proposals/action" style="display:inline">
|
||||
<input type="hidden" name="id" value="{{ p.id }}">
|
||||
<input type="hidden" name="action" value="accept">
|
||||
<input type="hidden" name="filt_type" value="{{ filt_type }}">
|
||||
<input type="hidden" name="filt_status" value="{{ filt_status }}">
|
||||
<button type="submit" class="btn btn-green" onclick="return confirm('Accepter et indexer dans fallout_sim_enriched ?')">✓ Accepter</button>
|
||||
</form>
|
||||
<form method="POST" action="/sim-proposals/action" style="display:inline-block;vertical-align:top">
|
||||
<input type="hidden" name="id" value="{{ p.id }}">
|
||||
<input type="hidden" name="action" value="reject">
|
||||
<input type="hidden" name="filt_type" value="{{ filt_type }}">
|
||||
<input type="hidden" name="filt_status" value="{{ filt_status }}">
|
||||
<textarea name="reject_reason" placeholder="Motif du rejet (optionnel — améliore le feedback futur)" style="display:block;width:260px;height:38px;margin-bottom:4px;background:var(--bg);border:1px solid var(--border);color:var(--text);font-size:11px;padding:4px 6px;border-radius:3px;resize:vertical"></textarea>
|
||||
<button type="submit" class="btn btn-red">✗ Rejeter</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<button type="button" class="btn" onclick="toggleDiscuss({{ p.id }})" style="font-size:12px;margin-top:8px">💬 Discuter</button>
|
||||
<div id="discuss-{{ p.id }}" style="display:none;margin-top:12px;border-top:1px solid var(--border);padding-top:10px">
|
||||
{% for msg in proposal_threads.get(p.id, []) %}
|
||||
<div style="margin-bottom:8px;padding:6px 10px;border-radius:4px;{% if msg.role=='user' %}background:#0d1117;border-left:2px solid var(--amber){% else %}background:#050d05;border-left:2px solid var(--green){% endif %}">
|
||||
<span style="font-size:10px;color:var(--dim);display:block;margin-bottom:3px">{{ '👤 Toi' if msg.role=='user' else '🤖 LLM' }} — {{ msg.created_at.strftime('%d/%m %H:%M') if msg.created_at else '' }}</span>
|
||||
<span style="font-size:12px;white-space:pre-wrap">{{ msg.content }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div style="display:flex;flex-direction:column;gap:6px;margin-top:8px">
|
||||
<textarea id="discuss-msg-{{ p.id }}" placeholder="Ex: Réduis l'impact de 50%…" style="background:var(--bg);border:1px solid var(--border);color:var(--text);font-size:12px;padding:6px;border-radius:3px;height:54px;resize:vertical"></textarea>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">
|
||||
<span style="font-size:11px;color:var(--dim)">temp: <input type="range" id="discuss-temp-{{ p.id }}" min="0" max="1" step="0.05" value="0.75" style="width:70px;vertical-align:middle" oninput="document.getElementById('discuss-temp-val-{{ p.id }}').textContent=this.value"> <span id="discuss-temp-val-{{ p.id }}">0.75</span></span>
|
||||
<span style="font-size:11px;color:var(--dim)">tokens: <input type="number" id="discuss-np-{{ p.id }}" value="512" min="128" max="2048" step="128" style="width:58px;background:var(--bg);border:1px solid var(--border);color:var(--text);font-size:11px;padding:2px 4px"></span>
|
||||
<button class="btn btn-green" onclick="submitDiscuss({{ p.id }})" style="font-size:12px">▶ Envoyer</button>
|
||||
<span id="discuss-spinner-{{ p.id }}" style="display:none;color:var(--amber);font-size:12px">⏳ Génération…</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<script>
|
||||
function toggleDiscuss(id) {
|
||||
var el = document.getElementById('discuss-' + id);
|
||||
el.style.display = el.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
function submitDiscuss(id) {
|
||||
var msg = document.getElementById('discuss-msg-' + id).value.trim();
|
||||
var temp = parseFloat(document.getElementById('discuss-temp-' + id).value);
|
||||
var np = parseInt(document.getElementById('discuss-np-' + id).value);
|
||||
if (!msg) { alert('Message vide'); return; }
|
||||
var spin = document.getElementById('discuss-spinner-' + id);
|
||||
spin.style.display = 'inline';
|
||||
fetch('/sim-proposals/' + id + '/discuss', {
|
||||
method: 'POST', headers: {'Content-Type':'application/json'},
|
||||
body: JSON.stringify({message: msg, temperature: temp, num_predict: np})
|
||||
}).then(function(r){ return r.json(); }).then(function(d){
|
||||
spin.style.display = 'none';
|
||||
if (d.error) { alert(d.error); return; }
|
||||
location.reload();
|
||||
}).catch(function(e){ spin.style.display = 'none'; alert('Erreur réseau : ' + e); });
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user