perf+calitate sugestii k-NN: matvec numpy, vot top-5 cu prag calibrat, indicatori import
- embeddings: corpus ca matrice numpy cu norme precalculate; suggest_nearest
= un matvec (~0.6ms/query fata de ~500ms cosine pur-Python la 17k vectori)
- enrich_suggestions: vot ponderat cu similaritatea pe top-5 vecini (NUL =
eticheta proprie); prag 0.5 -> 0.88, calibrat LOO pe corpusul SILVER
(tools/mapare-llm/knn_calibrate.py): precizie 90.5% -> 93.1%, cod gresit
preselectat 7.2% -> 4.7%; sub prag abtinere -> preselectie fuzzy
- UI: codul sugerat de sistem afisat explicit cu sursa si scorul, separat de
lista fuzzy ("potrivire text"); indicator de progres reparat pe upload
(display:inline anula .htmx-indicator) si adaugat pe pasii 2->3 si
"Salveaza maparile"
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1392,7 +1392,10 @@ def _nemapate_pentru_submission(row, nomenclator: list[dict], conn=None) -> list
|
||||
"denumire": item.get("denumire"),
|
||||
"suggestions": suggest_codes(item.get("denumire"), nomenclator, limit=5),
|
||||
"sugestie_principala": None,
|
||||
"surse_sugestie": {"gold_partajat": None, "silver": None, "embedding": None, "nul": False},
|
||||
"surse_sugestie": {
|
||||
"gold_partajat": None, "silver": None,
|
||||
"embedding": None, "embedding_similaritate": None, "nul": False,
|
||||
},
|
||||
}
|
||||
# L14-S6: imbogatire cu GOLD partajat > SILVER > embeddings (SUGGESTION-ONLY, #13)
|
||||
if conn is not None:
|
||||
@@ -3034,7 +3037,10 @@ def _collect_unmapped_ops(preview_rows: list[dict], nomenclator: list[dict], con
|
||||
entry["suggestions"] = suggest_codes(entry["denumire"], nomenclator, limit=5)
|
||||
# Init default pe FIECARE entry -> contract template identic (conn=None inclus).
|
||||
entry["sugestie_principala"] = None
|
||||
entry["surse_sugestie"] = {"gold_partajat": None, "silver": None, "embedding": None, "nul": False}
|
||||
entry["surse_sugestie"] = {
|
||||
"gold_partajat": None, "silver": None,
|
||||
"embedding": None, "embedding_similaritate": None, "nul": False,
|
||||
}
|
||||
# L14-S6: imbogatire cu GOLD partajat > SILVER > embeddings (SUGGESTION-ONLY, #13)
|
||||
if conn is not None:
|
||||
enriched = enrich_suggestions(conn, entry["denumire"])
|
||||
|
||||
@@ -62,10 +62,17 @@
|
||||
{% else %}
|
||||
<span class="sugg-sursa sugg-sursa--similar" title="operatie similara deja vazuta (k-NN/exact)">similar</span>
|
||||
{% endif %}
|
||||
{# Codul sugerat de sistem, explicit (nu doar preselectat in dropdown);
|
||||
pentru k-NN si scorul de similaritate. #}
|
||||
<strong>{{ e.sugestie_principala.cod_prestatie }}</strong>
|
||||
{%- if e.sugestie_principala.sursa == 'embedding' and e.surse_sugestie.embedding_similaritate %}
|
||||
({{ (e.surse_sugestie.embedding_similaritate * 100)|round|int }}%)
|
||||
{%- endif %}
|
||||
{% elif e.surse_sugestie and e.surse_sugestie.nul %}
|
||||
<span class="sugg-sursa sugg-sursa--nul" title="pare non-operatie (ITP/plata/discount...)">non-operatie</span>
|
||||
{% endif %}
|
||||
{% if e.suggestions %}
|
||||
{% if e.sugestie_principala or (e.surse_sugestie and e.surse_sugestie.nul) %}<span class="muted">· potrivire text:</span>{% endif %}
|
||||
{% for s in e.suggestions[:3] %}
|
||||
<span class="sugg">{{ s.cod_prestatie }} ({{ s.score|round|int }}%)</span>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
|
||||
<form hx-post="/_import/{{ import_id }}/mapare-coloane"
|
||||
hx-target="#import-section"
|
||||
hx-swap="outerHTML">
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#mapcol-spinner">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
|
||||
<div style="margin-bottom:8px; display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
|
||||
@@ -162,6 +163,11 @@
|
||||
style="min-height:44px; padding:10px 24px; font-size:var(--fs-md);{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
|
||||
Salveaza si continua la preview
|
||||
</button>
|
||||
<span id="mapcol-spinner" class="htmx-indicator muted"
|
||||
style="font-size:var(--fs-sm);" role="status">
|
||||
<span class="spin-dot" aria-hidden="true"></span>
|
||||
se verifica randurile si se calculeaza sugestiile...
|
||||
</span>
|
||||
{% if not prima_inreg %}
|
||||
<span style="font-size:var(--fs-xs); color:var(--err);">
|
||||
Fisierul nu contine randuri de date — incarca un fisier cu cel putin o inregistrare.
|
||||
|
||||
@@ -81,7 +81,8 @@
|
||||
<span class="s-ok">ok</span> si maparea se retine pentru fisierele viitoare.
|
||||
</p>
|
||||
<form hx-post="/_import/{{ import_id }}/mapare-operatii"
|
||||
hx-target="#import-section" hx-swap="outerHTML">
|
||||
hx-target="#import-section" hx-swap="outerHTML"
|
||||
hx-indicator="#mapop-spinner">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
{% for e in unmapped_ops %}
|
||||
{%- set top = e.suggestions[0] if e.suggestions else None -%}
|
||||
@@ -106,9 +107,16 @@
|
||||
{% else %}
|
||||
<span class="sugg-sursa sugg-sursa--similar" title="operatie similara deja vazuta (k-NN/exact)">similar</span>
|
||||
{% endif %}
|
||||
{# Codul sugerat de sistem, explicit (nu doar preselectat in dropdown);
|
||||
pentru k-NN si scorul de similaritate. #}
|
||||
<strong>{{ e.sugestie_principala.cod_prestatie }}</strong>
|
||||
{%- if e.sugestie_principala.sursa == 'embedding' and e.surse_sugestie.embedding_similaritate %}
|
||||
({{ (e.surse_sugestie.embedding_similaritate * 100)|round|int }}%)
|
||||
{%- endif %}
|
||||
{% elif e.surse_sugestie and e.surse_sugestie.nul %}
|
||||
<span class="sugg-sursa sugg-sursa--nul" title="pare non-operatie (ITP/plata/discount...)">non-operatie</span>
|
||||
{% endif %}
|
||||
{% if e.suggestions and (e.sugestie_principala or (e.surse_sugestie and e.surse_sugestie.nul)) %}<span class="muted">· potrivire text:</span>{% endif %}
|
||||
{% for s in e.suggestions[:3] %}
|
||||
<span class="sugg">{{ s.cod_prestatie }} ({{ s.score|round|int }}%)</span>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
@@ -127,8 +135,13 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div style="margin-top:12px;">
|
||||
<div style="margin-top:12px; display:flex; align-items:center; gap:12px;">
|
||||
<button type="submit" style="min-height:44px;">Salveaza maparile</button>
|
||||
<span id="mapop-spinner" class="htmx-indicator muted"
|
||||
style="font-size:var(--fs-sm);" role="status">
|
||||
<span class="spin-dot" aria-hidden="true"></span>
|
||||
se salveaza si se recalculeaza preview-ul...
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -122,9 +122,12 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{# FARA display in stilul inline: ar suprascrie .htmx-indicator{display:none}
|
||||
si indicatorul ar ramane vizibil permanent (deci invizibil ca progres). #}
|
||||
<span id="upload-spinner" class="htmx-indicator muted"
|
||||
style="font-size:var(--fs-sm); margin-top:6px; display:inline;">
|
||||
se parseaza fisierul...
|
||||
style="font-size:var(--fs-sm); margin-top:6px;" role="status">
|
||||
<span class="spin-dot" aria-hidden="true"></span>
|
||||
se parseaza fisierul si se pregatesc sugestiile...
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -176,6 +176,11 @@
|
||||
flex-wrap:wrap; z-index:10; }
|
||||
/* Indicator HTMX — ascuns pana la request */
|
||||
.htmx-indicator { display:none; }
|
||||
/* Cerc rotitor pentru indicatorii de procesare (import: parsare + sugestii) */
|
||||
.spin-dot { display:inline-block; width:13px; height:13px; border:2px solid var(--line);
|
||||
border-top-color:var(--accent); border-radius:50%; vertical-align:-2px;
|
||||
margin-right:6px; animation:spin-dot .7s linear infinite; }
|
||||
@keyframes spin-dot { to { transform:rotate(360deg); } }
|
||||
/* Selector tema stil pill — icon + eticheta temei curente.
|
||||
Eticheta se ascunde pe <=560px (spatiu ingust), ramane iconita. */
|
||||
.tema-btn { display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 12px;
|
||||
|
||||
Reference in New Issue
Block a user