
    v.jK                        d dl mZmZmZmZmZ d dlZd dlZd dlZd dl	m
Z
 d dlmZmZ  ee      Zddddd	d
dddddddd
dddddddddddgZd Zd ZdZej)                  d      d        Zej)                  d       d!        Zd"Zd#Zd$ Zd%Zej)                  d&      d'        Zej)                  d(      d)        Zej)                  d*      d+        Zej)                  d,      d-        Zej)                  d.      d/        Zej)                  d0      d1        Z ej)                  d2      d3        Z!ej)                  d4      d5        Z"ej)                  d6      d7        Z#d8Z$d9Z%d:Z&d;Z'd<Z(ej)                  d=      d>        Z)ej)                  d?      d@        Z*ej)                  dA      dB        Z+dCZ,dDZ-dE Z.ej)                  dF      dG        Z/ej)                  dH      dI        Z0ej)                  dJdKgL      ej)                  dMdKgL      dN               Z1ej)                  dOdKgL      dP        Z2ej)                  dQdRgL      dS        Z3ej)                  dT      dU        Z4ej)                  dVdKgL      dW        Z5dXZ6dYdZd[d\d]d^dYd_d`d\d]d^dYdadbd\d]d^dcdddedfd]d^dcdgdhdfd]d^dcdidjdfd]d^dcdkdldfd]d^gZ7dm Z8dn Z9ej)                  dodRgL      dp        Z:ej)                  dodKgL      dq        Z;edrk(  rejy                  dsdtduv       yy)w    )Flaskjsonifyrequestrender_template_stringResponseN)urlopen)URLError	HTTPErrorz
hermes-vpsz
Hermes VPSzProduktive Hermes-VPS-Instanz.u   ⚡z*https://hermes.agentsolutions-mallorca.comsystemdzhermes-dashboard.service)idnamedesciconurl
start_typesystemd_serviceworkerWorkeru6   Worker-Watcher für KI-Aufgaben und laufende Prozesse.u   🤖z*https://worker.agentsolutions-mallorca.comzhermes-worker-watcher.servicemirofishMiroFishu9   Multi-Agenten-Simulation für Analyse und Marktforschung.u   🐟z,https://mirofish.agentsolutions-mallorca.comdockerz/opt/struktur/mirofish)r   r   r   r   r   r   	containercomposec                 |   	 | j                  d      dk(  r@t        j                  dd| d   gddd      }|j                  j	                         d	k(  rd
S dS | j                  d      dk(  rDt        j                  dddd| d   gddd      }|j                  j	                         }|dk(  rd
S dS y# t
        $ r Y yw xY w)Nr   r   	systemctlz	is-activer   T   capture_outputtexttimeoutactiveonlineoffliner   inspectz--formatz{{.State.Status}}r   running	unbekannt)get
subprocessrunstdoutstrip	Exception)serviceresultstatuss      ,/opt/struktur/agent-solutions-cockpit/app.py
get_statusr1   '   s    ;;|$	1^^k73D+EF#$F  &}}224@8OiO;;|$0^^9j2Ew{G[\#$F ]]((*F%28A	A s%   AB/ B/ AB/ ,B/ /	B;:B;c                 j   	 | j                  d      dk(  r.t        j                  d|| d   gddd      }|j                  dk(  S | j                  d      d	k(  rM| d
   }|dk(  rg d}n|dk(  rg d}n|dk(  rg d}nyt        j                  ||ddd      }|j                  dk(  S y# t        $ r Y yw xY w)Nr   r   r   r   T   r   r   r   r   start)r   r   upz-dstop)r   r   r6   restart)r   r   r7   F<   )cwdr   r   r    )r'   r(   r)   
returncoder,   )r-   actionr.   compose_dircmds        r0   service_actionr>   =   s    ;;|$	1^^fg.?&@A#$F $$));;|$0!),K 76!39$6^^#F $$)) s   AB& 7B& <(B& &	B21B2u,!  <!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Solutions · Agents</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { background: #0f1117; color: #e2e8f0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; min-height: 100vh; }
  header { padding: 2rem 3rem; border-bottom: 1px solid #1e2330; }
  header h1 { font-size: 1.5rem; font-weight: 600; color: #fff; }
  header span { font-size: 0.85rem; color: #fff; }
  .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem; padding: 3rem; }
  .card { background: #161b27; border: 1px solid #1e2330; border-radius: 12px; padding: 1.5rem; }
  .card-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 1rem; }
  .card-icon { font-size: 2rem; }
  .badge { font-size: 0.7rem; padding: 0.25rem 0.7rem; border-radius: 99px; font-weight: 600; }
  .badge.online { background: #1a3a2a; color: #4ade80; border: 1px solid #166534; }
  .badge.offline { background: #3a1a1a; color: #f87171; border: 1px solid #7f1d1d; }
  .badge.loading { background: #2a2a1a; color: #fbbf24; border: 1px solid #78350f; }
  .badge.planned { background: #1a2a3a; color: #93c5fd; border: 1px solid #1e3a5f; }
  .card-title { font-size: 1.1rem; font-weight: 600; color: #fff; margin-bottom: 0.4rem; }
  .card-desc { font-size: 0.85rem; color: #fff; line-height: 1.5; margin-bottom: 1.2rem; }
  .card-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
  .btn { font-size: 0.75rem; padding: 0.4rem 0.9rem; border-radius: 6px; border: none; cursor: pointer; font-weight: 500; transition: all 0.15s; }
  .btn-open { background: #1e3a5f; color: #60a5fa; }
  .btn-open:hover { background: #1e4a7f; }
  .btn-restart { background: #1e3a2a; color: #4ade80; }
  .btn-restart:hover { background: #1e5a3a; }
  .btn-stop { background: #3a1e1e; color: #f87171; }
  .btn-stop:hover { background: #5a1e1e; }
  .btn-start { background: #1a3a2a; color: #4ade80; }
  .btn-start:hover { background: #1a5a3a; }
  .btn:disabled { opacity: 0.4; cursor: not-allowed; }

  .modal-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.7); z-index:1000; align-items:center; justify-content:center; }
  .modal-overlay.visible { display:flex; }
  .modal { background:#1e2330; border:1px solid #2d3748; border-radius:14px; padding:2rem; min-width:420px; max-width:95vw; }
  .modal h2 { font-size:1.1rem; font-weight:700; color:#fff; margin-bottom:1.2rem; }
  .modal select { width:100%; padding:0.5rem 0.75rem; background:#0f1117; border:1px solid #2d3748; border-radius:7px; color:#e2e8f0; font-size:0.9rem; margin-bottom:1rem; }
  .modal-actions { display:flex; gap:0.75rem; justify-content:flex-end; margin-top:0.5rem; }
  .btn-cancel { background:#1e2330; border:1px solid #2d3748; color:#fff; padding:0.45rem 1.1rem; border-radius:7px; cursor:pointer; font-size:0.85rem; }
  .btn-save { background:#1e3a5f; color:#60a5fa; border:none; padding:0.45rem 1.1rem; border-radius:7px; cursor:pointer; font-size:0.85rem; font-weight:600; }
  .current-model { font-size:0.8rem; color:#fff; margin-bottom:1rem; }

  footer { text-align: center; padding: 2rem; color: #334155; font-size: 0.8rem; }
</style>
</head>
<body>
<header style="display:flex; align-items:center; justify-content:space-between;">
  <div>
    <h1>Agents</h1>
    <span>Werkzeuge & Dienste — VPS 31.70.68.228</span>
  </div>
  <a href="https://agentsolutions-mallorca.com" target="_blank" rel="noopener noreferrer" style="font-size:0.85rem;color:#3b82f6;text-decoration:none;border:1px solid #1e3a5f;padding:0.4rem 1rem;border-radius:8px;">&#8592; Agent Solutions</a>
</header>

<div class="grid" id="grid">
  {% for s in services %}
  <div class="card" id="card-{{s.id}}">
    <div class="card-header">
      <div class="card-icon">{{s.icon}}</div>
      <span class="badge loading" id="badge-{{s.id}}">{% if s.get('external') %}extern{% elif s.get('planned') %}geplant{% else %}...{% endif %}</span>
    </div>
    <div class="card-title">{{s.name}}</div>
    <div class="card-desc">{{s.desc}}</div>
    <div class="card-actions">
      <a href="{{s.url}}" target="_blank" rel="noopener noreferrer"><button class="btn btn-open">Öffnen</button></a>
      {% if not s.get('external') %}
      <button class="btn btn-restart" onclick="action('{{s.id}}','restart')">Restart</button>
      <button class="btn btn-stop" onclick="action('{{s.id}}','stop')">Stop</button>
      <button class="btn btn-start" onclick="action('{{s.id}}','start')">Start</button>
      {% if false %}
      <button class="btn" style="background:#3a1a1a;color:#f87171;font-weight:700;" onclick="hermesJobsReset()">&#128465; Jobs Reset</button>
      {% endif %}
      {% if s.id == 'mirofish' %}
      <button class="btn" style="background:#1e2a3a;color:#a78bfa;" onclick="openMirofishModal()">🧠 Modell</button>
      {% endif %}
      {% endif %}
    </div>
  </div>
  {% endfor %}
  <div class="card" id="card-home-scout">
    <div class="card-header"><div class="card-icon">🔎</div><span class="badge planned">lokal</span></div>
    <div class="card-title">Home Scout</div>
    <div class="card-desc">Startet die vorhandene lokale Scout-Desktop-Version.</div>
    <div class="card-actions"><a href="hermes-scout://start"><button class="btn btn-open">Starten</button></a></div>
  </div>
  <a class="card" href="/routing" target="_blank" rel="noopener noreferrer" style="text-decoration:none;color:inherit;">
    <div class="card-header"><div class="card-icon">🧭</div><span class="badge planned">read-only</span></div>
    <div class="card-title">Routing</div>
    <div class="card-desc">Hermes Routing Control Plane: Entscheidungen, Modelle, Feedback und Hygiene.</div>
    <div class="card-actions"><span class="btn btn-open">Dashboard öffnen ↗</span></div>
  </a>
</div>
<footer>Agent Solutions · VPS IONOS · 31.70.68.228</footer>
<script>
function updateStatus() {
  fetch('/api/status').then(r=>r.json()).then(data => {
    data.forEach(s => {
      const b = document.getElementById('badge-'+s.id);
      b.textContent = s.status;
      b.className = 'badge ' + (s.status === 'online' ? 'online' : s.status === 'offline' ? 'offline' : s.status === 'geplant' ? 'planned' : 'loading');
    });
  });
}
function action(id, act) {
  const b = document.getElementById('badge-'+id);
  b.textContent = '...';
  b.className = 'badge loading';
  fetch('/api/action', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({id, action: act})
  }).then(()=> setTimeout(updateStatus, 3000));
}
updateStatus();
setInterval(updateStatus, 15000);
</script>

<div class="modal-overlay" id="mirofish-modal">
  <div class="modal">
    <h2>🐟 MiroFish — Modell wechseln</h2>
    <div class="current-model">Aktuell: <span id="modal-current-model">...</span></div>
    <select id="modal-model-select"></select>
    <div class="modal-actions">
      <button class="btn-cancel" onclick="closeMirofishModal()">Abbrechen</button>
      <button class="btn-save" id="btn-modal-save" onclick="saveMirofishModel()">Speichern &amp; Neustart</button>
    </div>
  </div>
</div>

<script>
async function openMirofishModal() {
  document.getElementById('mirofish-modal').classList.add('visible');
  const res = await fetch('/api/mirofish/model');
  const data = await res.json();
  document.getElementById('modal-current-model').textContent = data.current;
  const sel = document.getElementById('modal-model-select');
  sel.innerHTML = data.models.map(m =>
    `<option value="${m.model}" ${m.model===data.current?'selected':''}>[${m.provider}] ${m.label}</option>`
  ).join('');
}
function closeMirofishModal() {
  document.getElementById('mirofish-modal').classList.remove('visible');
}
async function saveMirofishModel() {
  const btn = document.getElementById('btn-modal-save');
  btn.disabled = true; btn.textContent = '⏳ Neustart...';
  const model = document.getElementById('modal-model-select').value;
  const res = await fetch('/api/mirofish/model', {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({model})});
  const data = await res.json();
  if (data.ok) { btn.textContent = '✅ Gespeichert'; setTimeout(closeMirofishModal, 1500); }
  else { btn.textContent = '❌ Fehler'; btn.disabled = false; }
}
</script>

</body>
</html>/c                  ,    t        t        t              S )N)services)r   HTMLSERVICES     r0   indexrF      s    !$::rE   z/api/statusc            	          t        t        D  cg c]&  } | d   | j                  d      rdn
t        |       d( c}       S c c} w )Nr   plannedgeplant)r   r/   )r   rC   r'   r1   )ss    r0   r/   r/     s:    kstfg1T7AEE)<LyR\]^R_atuuts   +A z'/opt/struktur/projects-dashboard/app.pyz4/opt/struktur/00_INFRASTRUKTUR/service-registry.jsonc                  H   dddg} 	 ddl }|j                  t        t        d      j	                               }|j
                  D ]  }t        ||j                        st        d |j                  D              s7|j                  |j                        D ]g  }t        |t              s|j                  d	      s&| j                  d
t        |d	         z   dt        |j                  d|d	               z   d       i  | S  	 | S # t         t"        t$        f$ r Y | S w xY w)zERead existing project metadata; no project names are hard-coded here.globalzGlobaler Wissensraum)r   labelr   Nutf-8encodingc              3   <   K   | ]  }t        |d d      dk(    yw)r   NPROJECTS)getattr).0ts     r0   	<genexpr>z$_knowledge_scopes.<locals>.<genexpr>  s     3o]^GAtT4Jj4X3os   r   zproject:z	Projekt: r   )astparseopenPROJECTS_REGISTRYreadbody
isinstanceAssignanytargetsliteral_evalvaluedictr'   appendstrOSErrorSyntaxError
ValueError)scopesrW   treenodeitems        r0   _knowledge_scopesrm   
  s-   (>?@F
yy/'BGGIJII 	D$

+3obfbnbn3o0o,,TZZ8 FD!$-$((4.Z#d4j/-IT_befjfnfnouw{  }A  xB  gC  cD  UD  'E  FF  M	 M [*- Ms1   AD	  D	 =.D	 ,D	 >AD	 D	 	D! D!u  <!doctype html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Agent Solutions · Wissen</title><style>
body{margin:0;background:#0f1117;color:#e2e8f0;font:16px system-ui,sans-serif}header{padding:2rem 3rem;border-bottom:1px solid #1e2330}header h1{margin:0;color:#fff}.wrap{max-width:1200px;margin:auto;padding:2rem 3rem}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:1rem}.card{background:#161b27;border:1px solid #26324a;border-radius:12px;padding:1.3rem}.card h2{margin:.2rem 0 .5rem;color:#fff;font-size:1.1rem}.card p{color:#fff;line-height:1.5;font-size:.9rem}.btn{display:inline-block;background:#1e3a5f;color:#93c5fd;text-decoration:none;padding:.55rem .9rem;border-radius:7px;font-weight:700}.back{color:#93c5fd;text-decoration:none}.note{color:#fff;margin-bottom:1.5rem}
</style></head><body><header><a class="back" href="/" target="_blank" rel="noopener noreferrer">← Agents</a><h1>Wissen</h1><span>Zentraler Wissensbereich · bestehende Pipeline und Datenpfade</span></header><main class="wrap"><p class="note">Wissensfunktionen sind hier gebündelt. Es wird keine zweite Pipeline angelegt; die vorhandenen Seiten und APIs bleiben die technische Grundlage.</p><div class="grid">
<a class="card" href="/wissen/fragen" target="_blank" rel="noopener noreferrer"><h2>1. Wissensfragen</h2><p>Fragen mit explizitem Projekt-/Wissensraum-Scope.</p><span class="btn">Öffnen</span></a>
<a class="card" href="/wissen/pipeline" target="_blank" rel="noopener noreferrer"><h2>2. Wissenspipeline</h2><p>Bestehendes Pipeline-Dashboard, unverändert wiederverwendet.</p><span class="btn">Öffnen</span></a>
<a class="card" href="/wissen/historie" target="_blank" rel="noopener noreferrer"><h2>3. Historie / Änderungen</h2><p>Bestehende Eingangs- und Änderungshistorie.</p><span class="btn">Öffnen</span></a>
</div></main></body></html>z/wissenc                  $    t        t        d      S )N	text/htmlmimetype)r   KNOWLEDGE_HUB_HTMLrD   rE   r0   wissenrs   "  s    &==rE   z/api/knowledge/scopesc                  (    t        t                     S N)r   rm   rD   rE   r0   knowledge_scopesrv   &  s    $&''rE   z/wissen/fragenc                      t               S ru   wissenspipeline_fragenrD   rE   r0   wissen_fragen_aliasrz   *      !##rE   z/wissen/pipelinec                      t               S ru   wissenspipelinerD   rE   r0   wissen_pipeline_aliasr   .      rE   z/wissen/bestandc                      t               S ru   r}   rD   rE   r0   wissen_bestand_aliasr   2  r   rE   z/wissen/quellenc                      t               S ru   rx   rD   rE   r0   wissen_quellen_aliasr   6  r{   rE   z/wissen/pruefstatusc                      t               S ru   r}   rD   rE   r0   wissen_pruefstatus_aliasr   :  r   rE   z/wissen/historiec                      t               S ru   )wissenspipeline_hinzufuegenrD   rE   r0   wissen_historie_aliasr   >  s    &((rE   z/wissen/scopec                      t               S ru   rx   rD   rE   r0   wissen_scope_aliasr   B  r{   rE   zB/opt/struktur/knowledge-pipeline-aggregator-staging/dashboard.htmlz"http://127.0.0.1:8655/v1/dashboardz>/opt/struktur/agent-solutions-cockpit/knowledge_questions.htmlz'http://127.0.0.1:8655/api/knowledge/askzhttp://127.0.0.1:8655z/wissenspipelinec                      	 t        t        d      j                         } d}| j                  dd      } | j                  dd|z   d      } t	        | d	      S # t
        t        f$ r t	        d
dd      cY S w xY w)NrN   rO   u  <nav style="display:flex;align-items:center;gap:18px;flex-wrap:wrap;padding:14px 22px;background:#111827;border-bottom:1px solid #26324a;font-family:system-ui,sans-serif"><a href="/wissen" style="color:#93c5fd;text-decoration:none;font-weight:700">← Wissen</a><a href="/" target="_blank" rel="noopener noreferrer" style="color:#93c5fd;text-decoration:none;font-weight:700">← Agent Solutions</a><a href="/wissenspipeline" target="_blank" rel="noopener noreferrer" style="color:#60a5fa;text-decoration:none;font-weight:700">Dashboard</a><a href="/wissenspipeline/fragen" target="_blank" rel="noopener noreferrer" style="color:#60a5fa;text-decoration:none;font-weight:700">Wissen fragen</a><a href="/wissenspipeline/hinzufuegen" target="_blank" rel="noopener noreferrer" style="color:#60a5fa;text-decoration:none;font-weight:700">Wissen hinzuf&uuml;gen</a><span style="color:#e2e8f0;font-weight:700">Wissenspipeline</span><span style="color:#fff;font-size:12px">read-only · keine Import-/Recovery-Aktionen</span></nav>u.   <title>Wissenspipeline · Dashboard V2</title>u1   <title>Agent Solutions · Wissenspipeline</title>z<body>   ro   rp   u    Wissenspipeline nicht verfügbar  
text/plainr/   rq   )rY   PIPELINE_HTMLr[   replacer   rf   UnicodeError)pagenavs     r0   r~   r~   L  s    _MG499; Q||L  OB  C||Hhna8{33\" _:3Q]^^_s   AA A87A8z/wissenspipeline/fragenc                      	 t        t        t        d      j                         d      S # t        t
        f$ r t        ddd      cY S w xY w)	NrN   rO   ro   rp   u1   Wissenspipeline-Frageoberfläche nicht verfügbarr   r   r   )r   rY   KNOWLEDGE_QUESTIONS_HTMLr[   rf   r   rD   rE   r0   ry   ry   W  sO    p5HMMOZeff\" pKTWbnoops   ), AAz/wissenspipeline/hinzufuegenc                      	 t        t        dd      j                         d      S # t        t        f$ r t        ddd	      cY S w xY w)
Nz8/opt/struktur/agent-solutions-cockpit/knowledge_add.htmlrN   rO   ro   rp   u   Wissenseingang nicht verfügbarr   r   r   )r   rY   r[   rf   r   rD   rE   r0   r   r   ^  sY    ^Wbijooq  }H  I  	I\" ^9#P\]]^s   %( AAz//home/hermes/.hermes/routing-control/routing.dbu  <!doctype html>
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Agent Solutions · Routing</title>
<style>
body{margin:0;background:#0f1117;color:#e2e8f0;font:14px system-ui,sans-serif}header{padding:1.5rem 3rem;border-bottom:1px solid #1e2330;display:flex;align-items:center;justify-content:space-between}h1,h2{color:#fff;margin:0 0 .7rem}h1{font-size:1.5rem}.sub,.muted{color:#fff}.back{color:#93c5fd;text-decoration:none;border:1px solid #1e3a5f;padding:.4rem .8rem;border-radius:7px}.wrap{padding:1.8rem 3rem;max-width:1500px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;margin-bottom:1.5rem}.card,section{background:#161b27;border:1px solid #26324a;border-radius:10px;padding:1rem}.metric{font-size:1.8rem;font-weight:700;color:#fff}.label{color:#fff;font-size:.82rem}.two{display:grid;grid-template-columns:repeat(auto-fit,minmax(360px,1fr));gap:1rem;margin-bottom:1.5rem}table{width:100%;border-collapse:collapse;font-size:.82rem}th,td{text-align:left;padding:.55rem .45rem;border-bottom:1px solid #26324a;vertical-align:top}th{color:#93c5fd;font-weight:600;white-space:nowrap}td{color:#dbe4f0}.scroll{overflow:auto;max-height:650px}.pill{display:inline-block;padding:.15rem .45rem;border-radius:5px;background:#26324a;color:#fff}.sev-critical{color:#fca5a5}.sev-error{color:#fb923c}.sev-warning{color:#fbbf24}.sev-info{color:#93c5fd}.empty{color:#fff;padding:.5rem 0}
</style></head><body><header><div><h1>🧭 Hermes Routing Control Plane</h1><div class="sub">Read-only Auswertung · Datenquelle: routing.db</div></div><a class="back" href="/" target="_blank" rel="noopener noreferrer">← Agent Solutions</a></header>
<main class="wrap"><div class="grid">{% for label,value in metrics %}<div class="card"><div class="label">{{label}}</div><div class="metric">{{value}}</div></div>{% endfor %}</div>
<div class="two"><section><h2>Modellverteilung</h2><table><tr><th>Modellgruppe</th><th>Anzahl</th></tr>{% for row in model_distribution %}<tr><td>{{row.label}}</td><td>{{row.count}}</td></tr>{% endfor %}</table></section>
<section><h2>Router vs Legacy</h2><table><tr><th>Quelle</th><th>Anzahl</th></tr>{% for row in source_distribution %}<tr><td>{{row.label}}</td><td>{{row.count}}</td></tr>{% endfor %}</table></section></div>
<section><h2>Letzte 20 Routingentscheidungen</h2><div class="scroll"><table><tr><th>Zeit</th><th>Board</th><th>Task</th><th>Assignee</th><th>Modell</th><th>Reasoning</th><th>Risk</th><th>Confidence</th><th>Begründung</th></tr>{% for row in decisions %}<tr><td>{{row.time}}</td><td>{{row.board}}</td><td>{{row.task}}</td><td>{{row.assignee}}</td><td>{{row.model}}</td><td>{{row.reasoning}}</td><td>{{row.risk}}</td><td>{{row.confidence}}</td><td>{{row.reasons}}</td></tr>{% endfor %}</table></div></section>
<div class="two"><section><h2>Feedback-Auswertung</h2><h3>Nach Modell</h3><table><tr><th>Modell</th><th>Erfolge</th><th>Gesamt</th><th>Quote</th></tr>{% for row in feedback_model %}<tr><td>{{row.label}}</td><td>{{row.success}}</td><td>{{row.total}}</td><td>{{row.rate}}</td></tr>{% endfor %}</table><h3>Nach Familie</h3><table><tr><th>Familie</th><th>Erfolge</th><th>Gesamt</th><th>Quote</th></tr>{% for row in feedback_family %}<tr><td>{{row.label}}</td><td>{{row.success}}</td><td>{{row.total}}</td><td>{{row.rate}}</td></tr>{% endfor %}</table></section>
<section><h2>Hygiene-Befunde nach Severity</h2><table><tr><th>Severity</th><th>Anzahl</th></tr>{% for row in hygiene_summary %}<tr><td class="sev-{{row.label|lower}}">{{row.label}}</td><td>{{row.count}}</td></tr>{% endfor %}</table><h3>Befunde</h3><table><tr><th>Severity</th><th>Kategorie</th><th>Item</th><th>Detail</th></tr>{% for row in hygiene %}<tr><td class="sev-{{row.severity|lower}}">{{row.severity}}</td><td>{{row.category}}</td><td>{{row.item}}</td><td>{{row.detail}}</td></tr>{% endfor %}</table></section></div></main></body></html>c                     dd l } ddlmm  | j                  dt        z   dz   d      }| j
                  |_        	 |j                  d      j                         }|j                  d      j                         }|j                  d	      j                         |j                  d
      j                         }|j                          d2d}d }dD ci c]  }|d }}dddd}	|D ]J  }
| ||
d         xx   dz  cc<   |
d   xs dj                         }|	|dk(  rdn|dk(  rdndxx   dz  cc<   L fd}fd}g }|D ]  }
dj                  t        j                  |
d   xs d            }|
d   dnt#        |
d         d}|j%                   ||
d         |
d    |
d!   |
d"   xs d#|
d   xs d#|
d$   xs d#|
d%    d&|
d'    d(||xs d#d)	         ||d*g d+      }d,t'        |      fd|	d   fd|	d   fd-t'              fd.t'        |      fgdD cg c]
  }|||   d/ c}dD cg c]
  }||	|   d/ c}| |d       |d0      ||D 
cg c]  }
t)        |
       c}
d1S # |j                          w xY wc c}w # t        t        f$ r t!        |
d   xs d      }Y 8w xY wc c}w c c}w c c}
w )3Nr   )datetimetimezonezfile:z?mode=roT)urizSELECT board, task_id, assignee, model, reasoning, risk_score, risk_class, confidence, reasons_json, source, created_at FROM decisions ORDER BY created_at DESC LIMIT 20z#SELECT model, source FROM decisionsz+SELECT model, family, success FROM feedbackzNSELECT severity, category, item, detail FROM hygiene ORDER BY observed_at DESCc                     i }| D ]#  }||   xs d}|j                  |d      dz   ||<   % |t        |      }|D cg c]  }||j                  |d      d c}S c c}w )Nsonstiger   r   rM   count)r'   sorted)rowskeylabelscountsrowrb   rM   s          r0   count_byz_routing_rows.<locals>.count_by  sq     	5CH*
E"JJua014F5M	5 >F^FMSTE%&**UA*>?TTTs   Ac                 f    | xs dj                         }dD ]  }|j                         |v s|c S  y)N )LunaSolAstrar   )lower)modelrb   rM   s      r0   model_groupz"_routing_rows.<locals>.model_group  s;    "##%- 	E{{}%	 rE   )r   r   r   r   )RouterLegacyr   r   r   sourcer   routerr   legacyr   r   c           
      >   i }D ]I  }||    xs d}|j                  |ddg      }|dxx   t        |d   xs d      z  cc<   |dxx   dz  cc<   K t        |j                               D cg c]"  \  }}||d   |d   d|d   z  |d   z  ddd$ c}}S c c}}w )	Nr&   r   successr   d   z.1f%)rM   r   totalrate)
setdefaultintr   items)r   groupsr   rM   rl   valuesfeedbacks         r0   feedback_tablez%_routing_rows.<locals>.feedback_table  s     	>CH+E$$UQF3DGs3y>.Q//GaA	> RX  Y_  Ye  Ye  Yg  Rh  i  AN  AF  HN%F1Iq	WZ]cde]fWfiopqirWrtwUxxySz{  i  	i  is   .'Bc                     j                  t        |       j                        j                  d      S # t        t
        t        f$ r t        | xs d      cY S w xY w)Nz%Y-%m-%d %H:%M:%S UTCr   )fromtimestampr   utcstrftime	TypeErrorrh   OverflowErrorre   )rb   r   r   s    r0   	timestampz _routing_rows.<locals>.timestamp  sJ    **3u:x||DMMNeff:}5Nc%+2>N7NNs   36 #AAz, reasons_jsonz[]
confidencez.2f
created_atboardtask_idassigneeu   —	reasoning
risk_classz (
risk_score))	timer   taskr   r   r   riskr   reasonsseverity)criticalerrorwarninginfoGesamtentscheidungenu   Feedback-EinträgezHygiene-Befunder   family)metricsmodel_distributionsource_distribution	decisionsfeedback_modelfeedback_familyhygiene_summaryhygieneru   )sqlite3r   r   connect
ROUTING_DBRowrow_factoryexecutefetchallcloser   joinjsonloadsr   rh   re   floatrd   lenrc   )r   dbr   all_decisionsr   r   r   rM   model_countssource_countsr   r   r   r   	formattedr   r   r   r   r   r   s                     @@@r0   _routing_rowsr   u  s   +	:-
:	EB[[BNJJ   J  K  T  T  V	

#HIRRT::KLUUW**mnwwy

U +OOE1HOLO A1=M o[W./14/h-%2,,.&H"4hfPXFX(^himnnioiO I KyyC,?,G4!HIW|,4RU3|CT=UVY<Z
)C,=">W_bcl_m{~  @J  |K  |T  OT  _b  cj  _k  _t  ot  CF  GR  CS  C\  W\  il  my  iz  h{  {}  ~A  BN  ~O  }P  PQ  fR  bl  y@  yI  DI  J  	K	K
 w
4\]O/]1CDxQ^_gQhFilt  wD  EM  wN  lO  Rf  hk  lt  hu  Qv  yJ  LO  PW  LX  xY  Z  oS  rT  fk  }B  MY  Z_  M`  sa  rT  kK  mL  bg  x}  HU  V[  H\  n]  mL  [d  xF  GN  xO  dr  s{  d|  Q	`	  C
J
  m	K
  |		  n	r	  s	v	  n	w	  m	K
  L
  L
M 	
 P& :&P#c.6I6OR2PP rT  mL  m	K
s7    A<I/ 
J
+J	J13J6J;/J	!J.-J.z/routingc                      	 t        t        fi t               S # t        t        j
                  f$ r t        ddd      cY S w xY w)Nu   Routing-Daten nicht verfügbarr   r   r   )r   ROUTING_HTMLr   rf   r   Errorr   rD   rE   r0   routingr     sD    ]%lFmoFFW]]# ]8|\\]s    'AAz/favicon.icoc                      t        d      S )N   )r/   )r   rD   rE   r0   faviconr     s    3rE   z/api/knowledge/askPOST)methodsz/wissenspipeline/fragen/askc                     	 t        j                  d      xs i } t        j                  t	        | j                  dd            t	        | j                  d      xs d      t	        | j                  d      xs d      t	        | j                  d	      xs d
      t	        | j                  d      xs | j                  d	      xs d
      t	        | j                  d      xs | j                  d	      xs d
      d      j                  d      }t        ddg      j                  t        |ddid      }t        |d      5 }|j                         }d d d        t        dd      S # 1 sw Y   xY w# t        $ r$}t        d|j                  d      dfcY d }~S d }~wt         t"        t$        f$ r t        ddi      dfcY S w xY w) NT)silentquestionr   modeinternal	freshnessautoscoperL   project_scopeknowledge_space)r  r  r  r	  r
  r  rN   zurllib.requestRequest)fromlistContent-Typeapplication/jsonr   dataheadersmethod   r       r   knowledge_query_upstream_error)r   r/   i  r   knowledge_query_unavailabler   )r   get_jsonr   dumpsre   r'   encode
__import__r  KNOWLEDGE_ASK_APIr   r[   r   r
   r   coder	   TimeoutErrorrf   )r  payloadupstream_requestupstreamr\   excs         r0   wissenspipeline_fragen_askr$    s   ET*0b

DHHZ,C(DcRVRZRZ[aRbRpfpNq  AD  EI  EM  EM  NY  EZ  Ed  ^d  Ae  ps  tx  t|  t|  }D  tE  tQ  IQ  pR  eh  im  iq  iq  rA  iB  ic  FJ  FN  FN  OV  FW  ic  [c  ed  y|  }A  }E  }E  FW  }X  }y  \`  \d  \d  el  \m  }y  qy  yz  {  |  C  C  DK  L#$4	{KSSG#$67 T H %s3 	!xD	!S3EFF	! 	!  Z @#((STVYYYlG, E =>?DDEs<   EE9 E-E9 -E62E9 9	G
F!G
!&G
	G
z/api/knowledge/ingest/<kind>c                    | dvrt        ddi      dfS 	 ddlm} d| z   }t        j                  d	      }d
t        j
                  j                  d
d      i} |t        |z   ||d      }t        |d      5 }t        |j                         |j                  d      cd d d        S # 1 sw Y   y xY w# t        $ rg}|j                         }n4# t        $ r( t        j                  ddi      j!                         }Y nw xY wt        ||j"                  d      cY d }~S d }~wt$        t&        t(        f$ r t        ddi      dfcY S w xY w)N)r   r   filer   unknown_ingest_type  r   )r  z/api/knowledge/ingest/F)cacher  r  r   r  r  r  r   knowledge_ingest_upstream_errorknowledge_ingest_unavailabler   )r   urllib.requestr  r   get_datar  r'   KNOWLEDGE_INGEST_APIr   r   r[   r/   r
   r,   r   r  r  r  r	   r  rf   )	kindr  pathr\   r  r"  responser#  r   s	            r0   wissenspipeline_ingestr2    s0   (( 567<<E*%d*E* 3 3NCU VW-d2WTZ[Xc* 	`hHMMO8??L^_	` 	` 	` MXXZWb$**g6W-X"Y"`"`"b'bsxx9KLLlG, E >?@DDEs`   A#B4 8&B(	B4 (B1-B4 1B4 4	E=CD.C?<D>C??DE&EE/api/knowledge/ingest/historyGETc                      	 t        t        dz   d      5 } t        | j                         | j                  d      cd d d        S # 1 sw Y   y xY w# t
        t        t        t        f$ r t        ddi      dfcY S w xY w)	Nr3     r  r  r   r   r+  r   )
r   r.  r   r[   r/   r
   r	   r  rf   r   )r1  s    r0   wissenspipeline_ingest_historyr7    s|    E)*IIRTU 	`YaHMMO8??L^_	` 	` 	`xw7 E >?@DDEs-   A &A	A AA A (A=<A=z/v1/dashboardc                      	 t        t        d      5 } | j                         }d d d        t        dd      S # 1 sw Y   xY w# t        t
        t        t        f$ r t        ddd      d	fcY S w xY w)
N-   r  r  r  r   knowledge_pipeline_unavailableT)r   	read_onlyr   )	r   PIPELINE_APIr[   r   r
   r	   r  rf   r   )r"  r   s     r0   wissenspipeline_apir=    sw    \\2. 	&(mmoG	&6HII	& 	& xw7 \!APTUVX[[[\s$   A :A AA )A21A2z/api/actionc                      t         j                  t        fdt        D        d       } | st	        ddi      dfS t        | d         }t	        d|i      S )Nc              3   :   K   | ]  }|d    d    k(  s|  yw)r   NrD   )rT   rJ   r  s     r0   rV   zaction.<locals>.<genexpr>  s      A!1T7d4j+@AAs   okFr(  r;   )r   r   nextrC   r   r>   )r-   r@  r  s     @r0   r;   r;     sT    <<DAxA4HGe}%s**	h	0BD":rE   z/opt/struktur/mirofish/.envOpenAIu%   GPT-4.1-mini (empfohlen, ~3€/15Rdn)zgpt-4.1-mini-2025-04-14zhttps://api.openai.com/v1r   )providerrM   r   base_urlapi_keyu   GPT-4.1 (~15€/15Rdn)zgpt-4.1-2025-04-14u   GPT-4o (~30€/15Rdn)zgpt-4o
OpenRouteru   DeepSeek-V3 (~2€/15Rdn)zdeepseek/deepseek-chatzhttps://openrouter.ai/api/v1u   DeepSeek-R1 (~5€/15Rdn)zdeepseek/deepseek-reasoneru   Qwen2.5-72B (~2€/15Rdn)zqwen/qwen2.5-72b-instructu   Kimi K2 (~2€/15Rdn)zmoonshotai/kimi-k2c                      	 t        t              5 } | D ]A  }|j                  d      s|j                         j	                  dd      d   c cd d d        S  	 d d d        y# 1 sw Y   yxY w#  Y yxY w)NzLLM_MODEL_NAME==r   r&   )rY   MIROFISH_ENV
startswithr+   split)flines     r0   _read_mirofish_modelrN    s{    , 	91 9??#45::<--c15a88	9 	99	9 	9 s9   A. A"$A"	A. A"A. "A+'A. +A. .A2c                    t        t              5 }|j                         }d d d        | d   | d   | d   | d   | d   d}g }D ]k  }d}|j                         D ]@  \  }}|j	                  |dz         s|j                  |dz   |z   t        d      z          d} n |r[|j                  |       m t        t        d	      5 }|j                  |       d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)
NrE  rD  r   )LLM_API_KEYLLM_BASE_URLLLM_MODEL_NAMEOPENAI_API_KEYOPENAI_API_BASE_URLFrH  
   Tw)rY   rI  	readlinesr   rJ  rd   chr
writelines)		model_cfgrL  lineskeys	new_linesrM  replacedkvs	            r0   _write_mirofish_envra    s   	l	 q  )3(4(1(3(4D I #JJL 	DAqq3w'  S1s2w!67		
 T"# 
lC	   A	Y   ' &   s   CC)C&)C2z/api/mirofish/modelc                  :    t               } t        | t        d      S )N)currentmodels)rN  r   MIROFISH_MODELS)rc  s    r0   mirofish_model_getrf    s    "$Gw/BCCrE   c                      t         j                  } | j                  d      t        fdt        D        d       }|st        ddd      dfS t        |       t        j                  ddd	
       t        dd      S )Nr   c              3   4   K   | ]  }|d    k(  s|  yw)r   NrD   )rT   mmodel_ids     r0   rV   z%mirofish_model_set.<locals>.<genexpr>(  s     EaajH.DEs   FzUnbekanntes Modell)r@  r   i  z<cd /opt/struktur/mirofish && docker compose restart mirofishTr8   )shellr    )r@  r   )	r   r   r'   rA  re  r   ra  r(   r)   )r  cfgrj  s     @r0   mirofish_model_setrm  $  sp    <<Dxx H
E?Et
LCe.BCDcIINNQY]gij$233rE   __main__z	127.0.0.1i  F)hostportdebug)=flaskr   r   r   r   r   r(   r   r   r,  r   urllib.errorr	   r
   __name__apprC   r1   r>   rB   routerF   r/   rZ   SERVICE_REGISTRYrm   rr   rs   rv   rz   r   r   r   r   r   r   r   r<  r   r  r.  r~   ry   r   r   r   r   r   r   r$  r2  r7  r=  r;   rI  re  rN  ra  rf  rm  r)   rD   rE   r0   <module>rx     s   K K     " ,Ho 0;5 H;: K=+	'>,D^@ 3; ; =v v > I    9> > "#( $( $ $     $ $  ! " ) ) ?$ $ U3[ = . _ _ $%p &p )*^ +^ ?
f1L
h :] ] >    &2(6(;E < 3E )F8<E =E$ *UG<E =E ?\ \ =6(+ , -(OZs  D_  pr  s(@[o  E`  qs  t(?[c  E`  qs  t(C[s  Ec  pr  s(C[w  Ec  pr  s(C[v  Ec  pr  s(?[o  Ec  pr  s	 .  5'2D 3D  6(34 44 zGG4uG5 rE   