Spaces:
Running
Running
Download main.py from ProzisTech/similarity: direct link, hf CLI and curl.
- Browser
- Download file 7.15 kB
-
https://huggingface.co/spaces/ProzisTech/similarity/resolve/main/main.py
- Command line
-
hf download hf://spaces/ProzisTech/similarity/main.py
-
curl -L -o main.py https://huggingface.co/spaces/ProzisTech/similarity/resolve/main/main.py
7.15 kB
| from contextlib import asynccontextmanager | |
| from typing import List | |
| from fastapi import FastAPI | |
| from fastapi.responses import HTMLResponse | |
| from models import RequestModel, ResponseModel | |
| from similarity import ( | |
| check_similarity, get_dashboard_data, get_machine_stats, | |
| start_memory_stats_reporter, start_memory_trimmer, | |
| ) | |
| _CACHE_MAX = 500 | |
| async def lifespan(app: FastAPI): | |
| start_memory_stats_reporter() | |
| start_memory_trimmer(interval_seconds=900) | |
| yield | |
| app = FastAPI(title="Prozis Similarity Service", version="1.0.0", lifespan=lifespan) | |
| _PAGE = ("""<!DOCTYPE html><html><head> | |
| <meta charset="UTF-8"> | |
| <title>Prozis Similarity Service</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@1,800&family=Inter:wght@400;500&display=swap" rel="stylesheet"> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| html,body{height:100%} | |
| body{background:#0d0d14;color:#e2e8f0;font-family:'Inter',system-ui,sans-serif;padding:.875rem 1.5rem; | |
| display:flex;flex-direction:column} | |
| header{display:flex;align-items:baseline;gap:1rem;margin-bottom:.75rem; | |
| padding-bottom:.5rem;border-bottom:1px solid #1a1a2e;flex-shrink:0} | |
| h1{font-family:'Barlow Condensed',sans-serif;font-weight:800;font-style:italic; | |
| font-size:1.85rem;color:#fff;letter-spacing:-.01em;line-height:1} | |
| .meta{color:#64748b;font-size:.72rem;font-family:'SF Mono','Fira Code',monospace} | |
| .meta b{color:#94a3b8} | |
| .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.5rem;flex-shrink:0} | |
| .card{background:#111827;border:1px solid #1e2942;border-top:2px solid var(--c); | |
| border-radius:8px;padding:.65rem .875rem} | |
| .card-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:.35rem} | |
| .pid{font-size:.62rem;color:#94a3b8;font-family:'SF Mono','Fira Code',monospace; | |
| text-transform:uppercase;letter-spacing:.06em} | |
| .cn{font-size:.6rem;color:#64748b;font-family:'SF Mono','Fira Code',monospace} | |
| .rss{font-size:1.5rem;font-weight:700;font-family:'SF Mono','Fira Code',monospace; | |
| color:var(--c);line-height:1;display:block} | |
| .card-bot{display:flex;justify-content:space-between;align-items:baseline;margin-top:.15rem} | |
| .unit{font-size:.6rem;color:#64748b;font-weight:400} | |
| .req{font-size:.75rem;color:#94a3b8;font-family:'SF Mono','Fira Code',monospace} | |
| .track{height:5px;background:#1e2942;border-radius:99px;margin-top:.45rem;overflow:hidden} | |
| .fill{height:100%;border-radius:99px;opacity:.7} | |
| .alerts-section{margin-top:.875rem;border-top:1px solid #1a1a2e;padding-top:.75rem; | |
| flex-shrink:0} | |
| .alerts-label{font-size:.65rem;color:#475569;font-family:'SF Mono','Fira Code',monospace; | |
| text-transform:uppercase;letter-spacing:.08em;margin-bottom:.4rem; | |
| display:flex;align-items:center;gap:.5rem} | |
| .alerts-dot{width:6px;height:6px;border-radius:50%;background:#4ade80;box-shadow:0 0 6px #4ade80} | |
| .alerts-dot.warn{background:#fab219;box-shadow:0 0 6px #fab219;animation:pulse 2s infinite} | |
| @keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}} | |
| .alerts-box{background:#111827;border:1px solid #1e2942;border-radius:6px; | |
| max-height:200px;overflow-y:auto;padding:.75rem .875rem; | |
| font-family:'Inter',system-ui,sans-serif;font-size:.82rem;line-height:2} | |
| .alert-line{display:flex;align-items:baseline;gap:.55rem;color:#f4f4f2} | |
| .alert-empty{color:#475569;font-family:'SF Mono','Fira Code',monospace;font-size:.75rem} | |
| </style> | |
| </head><body> | |
| <header> | |
| <h1>Prozis Similarity Service</h1> | |
| <span id="meta" class="meta"></span> | |
| </header> | |
| <div id="grid" class="grid"></div> | |
| <div class="alerts-section"> | |
| <div class="alerts-label"><span id="alerts-dot" class="alerts-dot"></span>Alertas</div> | |
| <div id="alerts" class="alerts-box"></div> | |
| </div> | |
| <script> | |
| var MAX=__CACHE_MAX__; | |
| function col(pct){return pct<50?'#4ade80':pct<80?'#fbbf24':'#f87171'} | |
| function workerCol(mb){return mb<800?'#4ade80':mb<1000?'#fbbf24':'#f87171'} | |
| function esc(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')} | |
| function alertColor(s){return /sem reportar|taxa de erro/i.test(s)?'#d03b3b':'#fab219'} | |
| function renderAlerts(alerts){ | |
| var box=document.getElementById('alerts'),dot=document.getElementById('alerts-dot'); | |
| if(!alerts||!alerts.length){ | |
| box.innerHTML='<div class="alert-empty">sem alertas</div>'; | |
| dot.className='alerts-dot'; | |
| return; | |
| } | |
| dot.className='alerts-dot warn'; | |
| box.innerHTML=alerts.map(function(a){ | |
| return '<div class="alert-line"><span style="color:'+alertColor(a)+'">●</span><span>'+esc(a)+'</span></div>'; | |
| }).join(''); | |
| } | |
| function render(d){ | |
| var w=d.workers,m=d.machine,h=d.health; | |
| var gb=(w.reduce(function(s,x){return s+x.rss_mb},0)/1024).toFixed(1); | |
| var rc=col(m.ram_percent),cc=col(m.cpu_percent); | |
| var ec=h.error_rate_pct>5?'#d03b3b':'#94a3b8'; | |
| document.getElementById('meta').innerHTML= | |
| '<b>'+w.length+'</b> workers · '+ | |
| '<b>'+gb+' GB</b> RSS · '+ | |
| 'RAM <b style="color:'+rc+'">'+m.ram_used_gb+'/'+m.ram_total_gb+' GB</b> · '+ | |
| 'CPU <b style="color:'+cc+'">'+m.cpu_percent+'%</b> · '+ | |
| '<b>'+h.throughput_per_min+'</b> req/min · '+ | |
| '<b style="color:'+ec+'">'+h.error_rate_pct+'%</b> erros · '+ | |
| '<b>'+h.avg_latency_ms+'</b> ms · '+ | |
| new Date().toTimeString().slice(0,8); | |
| var maxRss=Math.max.apply(null,w.map(function(x){return x.rss_mb}).concat([1])); | |
| document.getElementById('grid').innerHTML=w.map(function(x){ | |
| var c=workerCol(x.rss_mb),p=(x.rss_mb/maxRss*100).toFixed(0); | |
| return '<div class="card" style="--c:'+c+'">'+ | |
| '<div class="card-top"><span class="pid" title="pid '+x.pid+'">'+(x.name||('pid '+x.pid))+'</span><span class="cn">'+x.cache_size+'/'+MAX+'</span></div>'+ | |
| '<div class="rss">'+x.rss_mb+'</div>'+ | |
| '<div class="card-bot"><span class="unit">MB</span><span class="req">'+((x.requests||0)+' req')+'</span></div>'+ | |
| '<div class="track"><div class="fill" style="width:'+p+'%;background:'+c+'"></div></div>'+ | |
| '</div>'; | |
| }).join(''); | |
| renderAlerts(d.alerts); | |
| } | |
| function refresh(){ | |
| var xhr=new XMLHttpRequest(); | |
| xhr.open('GET','/stats'); | |
| xhr.onload=function(){ | |
| if(xhr.status===200){ | |
| try{var d=JSON.parse(xhr.responseText);if(d&&d.workers&&d.machine&&d.health)render(d);}catch(e){} | |
| } | |
| }; | |
| xhr.send(); | |
| } | |
| refresh();setInterval(refresh,5000); | |
| </script> | |
| </body></html>""").replace("__CACHE_MAX__", str(_CACHE_MAX)) | |
| async def stats_endpoint(): | |
| data = get_dashboard_data() | |
| data["machine"] = get_machine_stats() | |
| return data | |
| async def root(): | |
| return HTMLResponse(content=_PAGE) | |
| async def similarity(images: List[RequestModel], quadrant: bool = True): | |
| similarities = check_similarity(images, quadrant=quadrant) | |
| return similarities | |