Update app.py
Browse files
app.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
"""
|
| 2 |
π NVIDIA AI Multi-Model Space
|
| 3 |
-
- Tutti i modelli NVIDIA NIM
|
| 4 |
-
- Memoria breve e lunga
|
| 5 |
-
- API REST GET/POST + SSE
|
| 6 |
- Server MCP integrato
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
import os
|
|
@@ -12,15 +14,18 @@ import time
|
|
| 12 |
import uuid
|
| 13 |
import base64
|
| 14 |
import asyncio
|
| 15 |
-
import
|
| 16 |
from typing import Optional, AsyncGenerator
|
| 17 |
|
| 18 |
import gradio as gr
|
| 19 |
import httpx
|
| 20 |
from dotenv import load_dotenv
|
| 21 |
from fastapi import FastAPI, Request, Query, HTTPException
|
| 22 |
-
from fastapi.responses import
|
|
|
|
|
|
|
| 23 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 24 |
from sse_starlette.sse import EventSourceResponse
|
| 25 |
from pydantic import BaseModel, Field
|
| 26 |
|
|
@@ -389,6 +394,11 @@ app.add_middleware(
|
|
| 389 |
allow_headers=["*"],
|
| 390 |
)
|
| 391 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
|
| 393 |
# ββ Pydantic Models βββββββββββββββββββββββββββββββββββββββββββ
|
| 394 |
|
|
@@ -433,15 +443,42 @@ class MemoryRequest(BaseModel):
|
|
| 433 |
summary: Optional[str] = None
|
| 434 |
|
| 435 |
|
| 436 |
-
#
|
|
|
|
|
|
|
| 437 |
|
| 438 |
-
@app.get("/")
|
| 439 |
async def root():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
return {
|
| 441 |
"service": "π NVIDIA AI Multi-Model Space",
|
| 442 |
"version": "2.0.0",
|
| 443 |
"models_available": len(NVIDIA_MODELS),
|
| 444 |
"endpoints": {
|
|
|
|
|
|
|
| 445 |
"docs": "/docs",
|
| 446 |
"api_docs_html": "/api-docs",
|
| 447 |
"models": "/v1/models",
|
|
@@ -466,6 +503,10 @@ async def health():
|
|
| 466 |
}
|
| 467 |
|
| 468 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
@app.get("/v1/models")
|
| 470 |
async def list_models(category: Optional[str] = None):
|
| 471 |
"""Lista tutti i modelli disponibili, filtrabile per categoria"""
|
|
@@ -490,7 +531,9 @@ async def get_model_info(model_id: str):
|
|
| 490 |
return {"model_id": model_id, **NVIDIA_MODELS[model_id]}
|
| 491 |
|
| 492 |
|
| 493 |
-
#
|
|
|
|
|
|
|
| 494 |
|
| 495 |
@app.post("/v1/chat")
|
| 496 |
async def chat_post(req: ChatRequest):
|
|
@@ -503,7 +546,6 @@ async def chat_post(req: ChatRequest):
|
|
| 503 |
if req.stream:
|
| 504 |
return await _stream_response(req, session_id)
|
| 505 |
|
| 506 |
-
# Costruisci contesto con memoria
|
| 507 |
if req.use_memory:
|
| 508 |
memory.add_short(session_id, "user", req.message)
|
| 509 |
messages = memory.get_full_context(session_id, req.system_prompt)
|
|
@@ -536,8 +578,6 @@ async def chat_post(req: ChatRequest):
|
|
| 536 |
raise HTTPException(500, detail=str(e))
|
| 537 |
|
| 538 |
|
| 539 |
-
# ββ Chat GET βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 540 |
-
|
| 541 |
@app.get("/v1/chat/get")
|
| 542 |
async def chat_get(
|
| 543 |
message: str = Query(..., description="Messaggio utente"),
|
|
@@ -569,8 +609,6 @@ async def chat_get(
|
|
| 569 |
return await chat_post(req)
|
| 570 |
|
| 571 |
|
| 572 |
-
# ββ Chat SSE Stream βββββββββββββββββββββββββββββββββββββββββββ
|
| 573 |
-
|
| 574 |
@app.post("/v1/chat/stream")
|
| 575 |
async def chat_stream_post(req: ChatRequest):
|
| 576 |
"""Endpoint dedicato per streaming SSE via POST"""
|
|
@@ -619,7 +657,6 @@ async def _stream_response(req: ChatRequest, session_id: str):
|
|
| 619 |
async def event_generator():
|
| 620 |
full_response = []
|
| 621 |
try:
|
| 622 |
-
# Evento di inizio
|
| 623 |
yield {
|
| 624 |
"event": "start",
|
| 625 |
"data": json.dumps({
|
|
@@ -642,12 +679,10 @@ async def _stream_response(req: ChatRequest, session_id: str):
|
|
| 642 |
"data": json.dumps({"token": chunk})
|
| 643 |
}
|
| 644 |
|
| 645 |
-
# Salva in memoria
|
| 646 |
complete = "".join(full_response)
|
| 647 |
if req.use_memory:
|
| 648 |
memory.add_short(session_id, "assistant", complete)
|
| 649 |
|
| 650 |
-
# Evento di fine
|
| 651 |
yield {
|
| 652 |
"event": "done",
|
| 653 |
"data": json.dumps({
|
|
@@ -667,7 +702,9 @@ async def _stream_response(req: ChatRequest, session_id: str):
|
|
| 667 |
return EventSourceResponse(event_generator())
|
| 668 |
|
| 669 |
|
| 670 |
-
#
|
|
|
|
|
|
|
| 671 |
|
| 672 |
@app.post("/v1/memory")
|
| 673 |
async def manage_memory(req: MemoryRequest):
|
|
@@ -830,7 +867,6 @@ async def mcp_endpoint(request: Request):
|
|
| 830 |
"error": {"code": code, "message": message}
|
| 831 |
})
|
| 832 |
|
| 833 |
-
# ββ Initialize ββ
|
| 834 |
if method == "initialize":
|
| 835 |
return _mcp_response({
|
| 836 |
"protocolVersion": "2024-11-05",
|
|
@@ -845,11 +881,9 @@ async def mcp_endpoint(request: Request):
|
|
| 845 |
}
|
| 846 |
})
|
| 847 |
|
| 848 |
-
# ββ Tools List ββ
|
| 849 |
elif method == "tools/list":
|
| 850 |
return _mcp_response({"tools": MCP_TOOLS})
|
| 851 |
|
| 852 |
-
# ββ Tools Call ββ
|
| 853 |
elif method == "tools/call":
|
| 854 |
tool_name = params.get("name", "")
|
| 855 |
tool_args = params.get("arguments", {})
|
|
@@ -862,7 +896,6 @@ async def mcp_endpoint(request: Request):
|
|
| 862 |
except Exception as e:
|
| 863 |
return _mcp_error(-32000, str(e))
|
| 864 |
|
| 865 |
-
# ββ Resources List ββ
|
| 866 |
elif method == "resources/list":
|
| 867 |
return _mcp_response({
|
| 868 |
"resources": [
|
|
@@ -881,7 +914,6 @@ async def mcp_endpoint(request: Request):
|
|
| 881 |
]
|
| 882 |
})
|
| 883 |
|
| 884 |
-
# ββ Resources Read ββ
|
| 885 |
elif method == "resources/read":
|
| 886 |
uri = params.get("uri", "")
|
| 887 |
if uri == "nvidia://models":
|
|
@@ -1010,19 +1042,7 @@ async def api_docs_page():
|
|
| 1010 |
.param-table { width: 100%; border-collapse: collapse; margin: 10px 0; }
|
| 1011 |
.param-table th, .param-table td { padding: 10px; border: 1px solid var(--border); text-align: left; }
|
| 1012 |
.param-table th { background: var(--code-bg); color: var(--accent); }
|
| 1013 |
-
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.8em; margin: 2px; }
|
| 1014 |
-
.badge-llm { background: #1a5276; color: #85c1e9; }
|
| 1015 |
-
.badge-code { background: #1e4620; color: #82e0aa; }
|
| 1016 |
-
.badge-vision { background: #4a235a; color: #d7bde2; }
|
| 1017 |
-
.badge-reasoning { background: #7d3c00; color: #f0b27a; }
|
| 1018 |
-
.tab-container { margin: 20px 0; }
|
| 1019 |
-
.tabs { display: flex; gap: 5px; margin-bottom: -1px; }
|
| 1020 |
-
.tab { padding: 10px 20px; background: var(--code-bg); border: 1px solid var(--border); border-bottom: none; border-radius: 8px 8px 0 0; cursor: pointer; color: #888; }
|
| 1021 |
-
.tab.active { background: var(--card); color: var(--accent); border-color: var(--accent); }
|
| 1022 |
-
.tab-content { display: none; background: var(--card); border: 1px solid var(--border); border-radius: 0 8px 8px 8px; padding: 20px; }
|
| 1023 |
-
.tab-content.active { display: block; }
|
| 1024 |
.note { background: #1a3a1a; border-left: 4px solid var(--accent); padding: 15px; margin: 15px 0; border-radius: 0 8px 8px 0; }
|
| 1025 |
-
.warning { background: #3a2a1a; border-left: 4px solid #f0b27a; padding: 15px; margin: 15px 0; border-radius: 0 8px 8px 0; }
|
| 1026 |
a { color: var(--accent); }
|
| 1027 |
</style>
|
| 1028 |
</head>
|
|
@@ -1034,163 +1054,56 @@ async def api_docs_page():
|
|
| 1034 |
|
| 1035 |
<div class="note">
|
| 1036 |
<strong>Base URL:</strong> <code>https://YOUR-SPACE.hf.space</code><br>
|
| 1037 |
-
<strong>Autenticazione:</strong> API key NVIDIA pre-configurata (default)
|
| 1038 |
</div>
|
| 1039 |
|
| 1040 |
-
<h2>π Endpoints
|
| 1041 |
<div class="endpoint">
|
| 1042 |
<table class="param-table">
|
| 1043 |
<tr><th>Metodo</th><th>Endpoint</th><th>Descrizione</th></tr>
|
| 1044 |
-
<tr><td><span class="method get">GET</span></td><td><code>/
|
|
|
|
|
|
|
|
|
|
| 1045 |
<tr><td><span class="method get">GET</span></td><td><code>/v1/chat/get</code></td><td>Chat via GET</td></tr>
|
| 1046 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/chat</code></td><td>Chat via POST</td></tr>
|
| 1047 |
-
<tr><td><span class="method get">GET</span></td><td><code>/v1/chat/stream</code></td><td>
|
| 1048 |
-
<tr><td><span class="method post">POST</span></td><td><code>/v1/chat/stream</code></td><td>
|
| 1049 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/memory</code></td><td>Gestione memoria</td></tr>
|
| 1050 |
-
<tr><td><span class="method get">GET</span></td><td><code>/v1/memory/{session_id}</code></td><td>Stats memoria sessione</td></tr>
|
| 1051 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/mcp</code></td><td>Server MCP (JSON-RPC)</td></tr>
|
| 1052 |
</table>
|
| 1053 |
</div>
|
| 1054 |
|
| 1055 |
-
<h2>π¬ Chat
|
| 1056 |
-
|
| 1057 |
-
<h3>POST /v1/chat</h3>
|
| 1058 |
<div class="endpoint">
|
| 1059 |
-
<
|
| 1060 |
-
<p>Chat completo con supporto memoria e streaming opzionale.</p>
|
| 1061 |
-
|
| 1062 |
-
<pre><code># cURL - Chat semplice
|
| 1063 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/chat \\
|
| 1064 |
-H "Content-Type: application/json" \\
|
| 1065 |
-d '{
|
| 1066 |
"message": "Spiegami la relatività",
|
| 1067 |
"model": "meta/llama-3.1-405b-instruct",
|
| 1068 |
"session_id": "user-123",
|
| 1069 |
-
"temperature": 0.7,
|
| 1070 |
"use_memory": true
|
| 1071 |
}'</code></pre>
|
| 1072 |
-
|
| 1073 |
-
<pre><code># Python
|
| 1074 |
-
import requests
|
| 1075 |
-
|
| 1076 |
-
resp = requests.post("https://YOUR-SPACE.hf.space/v1/chat", json={
|
| 1077 |
-
"message": "Ciao! Come funziona la memoria?",
|
| 1078 |
-
"model": "meta/llama-3.1-405b-instruct",
|
| 1079 |
-
"session_id": "my-session",
|
| 1080 |
-
"use_memory": True
|
| 1081 |
-
})
|
| 1082 |
-
print(resp.json()["response"])</code></pre>
|
| 1083 |
-
|
| 1084 |
-
<pre><code># JavaScript (fetch)
|
| 1085 |
-
const resp = await fetch('https://YOUR-SPACE.hf.space/v1/chat', {
|
| 1086 |
-
method: 'POST',
|
| 1087 |
-
headers: {'Content-Type': 'application/json'},
|
| 1088 |
-
body: JSON.stringify({
|
| 1089 |
-
message: 'Hello!',
|
| 1090 |
-
model: 'meta/llama-3.1-405b-instruct',
|
| 1091 |
-
session_id: 'js-session'
|
| 1092 |
-
})
|
| 1093 |
-
});
|
| 1094 |
-
const data = await resp.json();
|
| 1095 |
-
console.log(data.response);</code></pre>
|
| 1096 |
</div>
|
| 1097 |
|
| 1098 |
-
<
|
| 1099 |
<div class="endpoint">
|
| 1100 |
-
<
|
| 1101 |
-
<p>Chat via query parameters β comodo per test rapidi.</p>
|
| 1102 |
-
<pre><code># Browser o cURL
|
| 1103 |
-
curl "https://YOUR-SPACE.hf.space/v1/chat/get?message=Ciao&model=meta/llama-3.1-8b-instruct&session_id=test"</code></pre>
|
| 1104 |
-
</div>
|
| 1105 |
-
|
| 1106 |
-
<h2>π‘ Streaming SSE</h2>
|
| 1107 |
-
<div class="endpoint">
|
| 1108 |
-
<span class="method get">GET</span> <span class="url">/v1/chat/stream?message=...</span><br>
|
| 1109 |
-
<span class="method post">POST</span> <span class="url">/v1/chat/stream</span>
|
| 1110 |
-
<p>Risposta in tempo reale via Server-Sent Events.</p>
|
| 1111 |
-
|
| 1112 |
-
<p><strong>Eventi SSE:</strong></p>
|
| 1113 |
-
<ul>
|
| 1114 |
-
<li><code>event: start</code> β Inizio generazione (contiene session_id, model)</li>
|
| 1115 |
-
<li><code>event: token</code> β Singolo token generato</li>
|
| 1116 |
-
<li><code>event: done</code> β Fine (contiene risposta completa + stats memoria)</li>
|
| 1117 |
-
<li><code>event: error</code> β Errore</li>
|
| 1118 |
-
</ul>
|
| 1119 |
-
|
| 1120 |
-
<pre><code># JavaScript - EventSource (GET)
|
| 1121 |
const es = new EventSource(
|
| 1122 |
-
'
|
| 1123 |
);
|
| 1124 |
-
|
| 1125 |
-
|
| 1126 |
-
const {token} = JSON.parse(e.data);
|
| 1127 |
-
process.stdout.write(token); // o document.body.innerHTML += token;
|
| 1128 |
-
});
|
| 1129 |
-
|
| 1130 |
-
es.addEventListener('done', (e) => {
|
| 1131 |
-
const {full_response, memory_stats} = JSON.parse(e.data);
|
| 1132 |
-
console.log('\\n--- DONE ---');
|
| 1133 |
-
console.log('Stats:', memory_stats);
|
| 1134 |
-
es.close();
|
| 1135 |
});
|
|
|
|
| 1136 |
|
| 1137 |
-
|
| 1138 |
-
|
| 1139 |
-
es.close();
|
| 1140 |
-
});</code></pre>
|
| 1141 |
-
|
| 1142 |
-
<pre><code># Python - SSE streaming con httpx
|
| 1143 |
-
import httpx
|
| 1144 |
-
import json
|
| 1145 |
-
|
| 1146 |
-
with httpx.stream('GET', 'https://YOUR-SPACE.hf.space/v1/chat/stream',
|
| 1147 |
-
params={'message': 'Ciao!', 'session_id': 'py-sse'}) as r:
|
| 1148 |
-
for line in r.iter_lines():
|
| 1149 |
-
if line.startswith('data: '):
|
| 1150 |
-
data = json.loads(line[6:])
|
| 1151 |
-
if 'token' in data:
|
| 1152 |
-
print(data['token'], end='', flush=True)</code></pre>
|
| 1153 |
-
|
| 1154 |
-
<pre><code># cURL - SSE streaming
|
| 1155 |
-
curl -N "https://YOUR-SPACE.hf.space/v1/chat/stream?message=Ciao&session_id=curl-test"</code></pre>
|
| 1156 |
-
</div>
|
| 1157 |
-
|
| 1158 |
-
<h2>π§ Memoria API</h2>
|
| 1159 |
-
<div class="endpoint">
|
| 1160 |
-
<p>Il sistema di memoria gestisce automaticamente:</p>
|
| 1161 |
-
<ul>
|
| 1162 |
-
<li><strong>Memoria Breve:</strong> ultimi 20 messaggi della sessione attiva (in RAM)</li>
|
| 1163 |
-
<li><strong>Memoria Lunga:</strong> messaggi precedenti salvati in SQLite con limite token</li>
|
| 1164 |
-
<li><strong>Riassunti:</strong> possibilità di salvare riassunti di sessione</li>
|
| 1165 |
-
</ul>
|
| 1166 |
-
|
| 1167 |
-
<pre><code># Statistiche memoria
|
| 1168 |
-
curl https://YOUR-SPACE.hf.space/v1/memory/my-session
|
| 1169 |
-
|
| 1170 |
-
# Cronologia completa
|
| 1171 |
-
curl https://YOUR-SPACE.hf.space/v1/memory/my-session/history
|
| 1172 |
-
|
| 1173 |
-
# Pulisci memoria breve
|
| 1174 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/memory \\
|
| 1175 |
-
-H "Content-Type: application/json" \\
|
| 1176 |
-
-d '{"session_id": "my-session", "action": "clear_short"}'
|
| 1177 |
-
|
| 1178 |
-
# Elimina tutta la sessione
|
| 1179 |
-
curl -X DELETE https://YOUR-SPACE.hf.space/v1/sessions/my-session
|
| 1180 |
-
|
| 1181 |
-
# Salva riassunto
|
| 1182 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/memory \\
|
| 1183 |
-
-H "Content-Type: application/json" \\
|
| 1184 |
-
-d '{"session_id": "my-session", "action": "save_summary", "summary": "L utente sta imparando Python"}'</code></pre>
|
| 1185 |
</div>
|
| 1186 |
|
| 1187 |
-
<h2>π
|
| 1188 |
<div class="endpoint">
|
| 1189 |
-
<
|
| 1190 |
-
<p>Endpoint MCP (Model Context Protocol) per integrazioni con Claude Desktop, Cursor, etc.</p>
|
| 1191 |
-
|
| 1192 |
-
<pre><code># Configurazione MCP per Claude Desktop / Cursor
|
| 1193 |
-
// In mcp_settings.json:
|
| 1194 |
{
|
| 1195 |
"mcpServers": {
|
| 1196 |
"nvidia-ai": {
|
|
@@ -1199,78 +1112,6 @@ curl -X POST https://YOUR-SPACE.hf.space/v1/memory \\
|
|
| 1199 |
}
|
| 1200 |
}
|
| 1201 |
}</code></pre>
|
| 1202 |
-
|
| 1203 |
-
<pre><code># Test MCP - Initialize
|
| 1204 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/mcp \\
|
| 1205 |
-
-H "Content-Type: application/json" \\
|
| 1206 |
-
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
|
| 1207 |
-
|
| 1208 |
-
# Test MCP - List Tools
|
| 1209 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/mcp \\
|
| 1210 |
-
-H "Content-Type: application/json" \\
|
| 1211 |
-
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
|
| 1212 |
-
|
| 1213 |
-
# Test MCP - Call Tool
|
| 1214 |
-
curl -X POST https://YOUR-SPACE.hf.space/v1/mcp \\
|
| 1215 |
-
-H "Content-Type: application/json" \\
|
| 1216 |
-
-d '{
|
| 1217 |
-
"jsonrpc":"2.0","id":3,
|
| 1218 |
-
"method":"tools/call",
|
| 1219 |
-
"params":{
|
| 1220 |
-
"name":"nvidia_chat",
|
| 1221 |
-
"arguments":{
|
| 1222 |
-
"message":"What is quantum computing?",
|
| 1223 |
-
"model":"meta/llama-3.1-70b-instruct",
|
| 1224 |
-
"session_id":"mcp-test"
|
| 1225 |
-
}
|
| 1226 |
-
}
|
| 1227 |
-
}'</code></pre>
|
| 1228 |
-
|
| 1229 |
-
<h3>Tools MCP Disponibili:</h3>
|
| 1230 |
-
<table class="param-table">
|
| 1231 |
-
<tr><th>Tool</th><th>Descrizione</th></tr>
|
| 1232 |
-
<tr><td><code>nvidia_chat</code></td><td>Chat con qualsiasi modello NVIDIA + memoria</td></tr>
|
| 1233 |
-
<tr><td><code>nvidia_chat_stream</code></td><td>Info per streaming SSE</td></tr>
|
| 1234 |
-
<tr><td><code>list_models</code></td><td>Lista modelli (filtrabile per categoria)</td></tr>
|
| 1235 |
-
<tr><td><code>memory_stats</code></td><td>Statistiche memoria sessione</td></tr>
|
| 1236 |
-
<tr><td><code>memory_clear</code></td><td>Pulisci memoria (short/all)</td></tr>
|
| 1237 |
-
<tr><td><code>get_conversation_history</code></td><td>Cronologia completa conversazione</td></tr>
|
| 1238 |
-
</table>
|
| 1239 |
-
</div>
|
| 1240 |
-
|
| 1241 |
-
<h2>π€ Modelli Disponibili</h2>
|
| 1242 |
-
<div class="endpoint">
|
| 1243 |
-
<p>
|
| 1244 |
-
<span class="badge badge-llm">LLM</span>
|
| 1245 |
-
<span class="badge badge-code">Code</span>
|
| 1246 |
-
<span class="badge badge-vision">Vision</span>
|
| 1247 |
-
<span class="badge badge-reasoning">Reasoning</span>
|
| 1248 |
-
e altri...
|
| 1249 |
-
</p>
|
| 1250 |
-
<pre><code># Lista tutti i modelli
|
| 1251 |
-
curl https://YOUR-SPACE.hf.space/v1/models
|
| 1252 |
-
|
| 1253 |
-
# Filtra per categoria
|
| 1254 |
-
curl "https://YOUR-SPACE.hf.space/v1/models?category=Code"
|
| 1255 |
-
|
| 1256 |
-
# Info singolo modello
|
| 1257 |
-
curl https://YOUR-SPACE.hf.space/v1/models/meta/llama-3.1-405b-instruct</code></pre>
|
| 1258 |
-
</div>
|
| 1259 |
-
|
| 1260 |
-
<h2>π Schema Parametri Chat</h2>
|
| 1261 |
-
<div class="endpoint">
|
| 1262 |
-
<table class="param-table">
|
| 1263 |
-
<tr><th>Parametro</th><th>Tipo</th><th>Default</th><th>Descrizione</th></tr>
|
| 1264 |
-
<tr><td><code>message</code></td><td>string</td><td><em>richiesto</em></td><td>Messaggio dell'utente</td></tr>
|
| 1265 |
-
<tr><td><code>model</code></td><td>string</td><td>meta/llama-3.1-405b-instruct</td><td>ID modello NVIDIA</td></tr>
|
| 1266 |
-
<tr><td><code>session_id</code></td><td>string</td><td>auto-generated</td><td>ID sessione per memoria</td></tr>
|
| 1267 |
-
<tr><td><code>system_prompt</code></td><td>string</td><td>"Sei un assistente AI..."</td><td>System prompt</td></tr>
|
| 1268 |
-
<tr><td><code>temperature</code></td><td>float</td><td>0.7</td><td>Creatività (0.0 - 2.0)</td></tr>
|
| 1269 |
-
<tr><td><code>max_tokens</code></td><td>int</td><td>4096</td><td>Max token risposta</td></tr>
|
| 1270 |
-
<tr><td><code>top_p</code></td><td>float</td><td>0.9</td><td>Nucleus sampling</td></tr>
|
| 1271 |
-
<tr><td><code>use_memory</code></td><td>bool</td><td>true</td><td>Usa sistema memoria</td></tr>
|
| 1272 |
-
<tr><td><code>stream</code></td><td>bool</td><td>false</td><td>Abilita SSE streaming</td></tr>
|
| 1273 |
-
</table>
|
| 1274 |
</div>
|
| 1275 |
|
| 1276 |
</div>
|
|
@@ -1280,7 +1121,7 @@ curl https://YOUR-SPACE.hf.space/v1/models/meta/llama-3.1-405b-instruct</code></
|
|
| 1280 |
|
| 1281 |
|
| 1282 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1283 |
-
# GRADIO UI
|
| 1284 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1285 |
|
| 1286 |
def get_model_choices():
|
|
@@ -1315,10 +1156,11 @@ async def gradio_chat(
|
|
| 1315 |
else:
|
| 1316 |
messages = [{"role": "system", "content": system_prompt}]
|
| 1317 |
for h in history:
|
| 1318 |
-
if h
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
|
|
|
| 1322 |
messages.append({"role": "user", "content": message})
|
| 1323 |
|
| 1324 |
full_response = ""
|
|
@@ -1341,9 +1183,8 @@ async def gradio_chat(
|
|
| 1341 |
|
| 1342 |
def get_memory_info(session_id: str):
|
| 1343 |
if not session_id:
|
| 1344 |
-
return "
|
| 1345 |
-
|
| 1346 |
-
return json.dumps(stats, indent=2, ensure_ascii=False)
|
| 1347 |
|
| 1348 |
|
| 1349 |
def clear_session_memory(session_id: str):
|
|
@@ -1353,8 +1194,6 @@ def clear_session_memory(session_id: str):
|
|
| 1353 |
return f"ποΈ Memoria sessione '{session_id}' eliminata"
|
| 1354 |
|
| 1355 |
|
| 1356 |
-
# ββ Build Gradio Interface ββββββββββββββββββββββββββββββββββββ
|
| 1357 |
-
|
| 1358 |
with gr.Blocks(
|
| 1359 |
title="π NVIDIA AI Multi-Model Space",
|
| 1360 |
theme=gr.themes.Soft(
|
|
@@ -1372,7 +1211,7 @@ with gr.Blocks(
|
|
| 1372 |
# π NVIDIA AI Multi-Model Space
|
| 1373 |
**Chat con tutti i modelli NVIDIA NIM** β’ Memoria Breve/Lunga β’ API REST β’ SSE Streaming β’ MCP Server
|
| 1374 |
|
| 1375 |
-
|
| 1376 |
""")
|
| 1377 |
|
| 1378 |
with gr.Tab("π¬ Chat"):
|
|
@@ -1382,7 +1221,6 @@ with gr.Blocks(
|
|
| 1382 |
height=600,
|
| 1383 |
type="messages",
|
| 1384 |
show_copy_button=True,
|
| 1385 |
-
avatar_images=(None, "https://upload.wikimedia.org/wikipedia/sco/thumb/2/21/Nvidia_logo.svg/200px-Nvidia_logo.svg.png"),
|
| 1386 |
placeholder="Seleziona un modello e inizia a chattare..."
|
| 1387 |
)
|
| 1388 |
msg = gr.Textbox(
|
|
@@ -1425,7 +1263,6 @@ with gr.Blocks(
|
|
| 1425 |
|
| 1426 |
mem_output = gr.JSON(label="π Memoria", visible=True)
|
| 1427 |
|
| 1428 |
-
# Chat handler
|
| 1429 |
msg.submit(
|
| 1430 |
gradio_chat,
|
| 1431 |
inputs=[msg, chatbot, model, system_prompt, temperature, max_tokens, session_id, use_memory],
|
|
@@ -1458,8 +1295,7 @@ with gr.Blocks(
|
|
| 1458 |
with gr.Tab("π§ Memoria"):
|
| 1459 |
gr.Markdown("""
|
| 1460 |
## π§ Gestione Memoria
|
| 1461 |
-
|
| 1462 |
-
- **Memoria Breve**: ultimi 20 messaggi (configurabile)
|
| 1463 |
- **Memoria Lunga**: messaggi piΓΉ vecchi salvati in SQLite
|
| 1464 |
- **Riassunti**: riassunti di sessione persistenti
|
| 1465 |
""")
|
|
@@ -1470,7 +1306,7 @@ with gr.Blocks(
|
|
| 1470 |
stats_btn = gr.Button("π Mostra Statistiche")
|
| 1471 |
clear_mem_btn = gr.Button("ποΈ Elimina Sessione", variant="stop")
|
| 1472 |
|
| 1473 |
-
mem_display = gr.
|
| 1474 |
|
| 1475 |
stats_btn.click(get_memory_info, inputs=[mem_session], outputs=[mem_display])
|
| 1476 |
clear_mem_btn.click(clear_session_memory, inputs=[mem_session], outputs=[mem_display])
|
|
@@ -1479,15 +1315,13 @@ with gr.Blocks(
|
|
| 1479 |
gr.Markdown("""
|
| 1480 |
## π Documentazione API
|
| 1481 |
|
| 1482 |
-
π **[Apri documentazione completa](/api-docs)**
|
| 1483 |
-
|
| 1484 |
-
π **[OpenAPI / Swagger UI](/docs)** (auto-generata da FastAPI)
|
| 1485 |
|
| 1486 |
-
|
| 1487 |
|
| 1488 |
### Quick Start
|
| 1489 |
|
| 1490 |
-
#### Chat via GET
|
| 1491 |
```
|
| 1492 |
GET /v1/chat/get?message=Ciao&model=meta/llama-3.1-8b-instruct&session_id=test
|
| 1493 |
```
|
|
@@ -1504,24 +1338,12 @@ with gr.Blocks(
|
|
| 1504 |
curl -N "/v1/chat/stream?message=Raccontami+una+storia"
|
| 1505 |
```
|
| 1506 |
|
| 1507 |
-
#### MCP
|
| 1508 |
```bash
|
| 1509 |
curl -X POST /v1/mcp \\
|
| 1510 |
-H "Content-Type: application/json" \\
|
| 1511 |
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
| 1512 |
```
|
| 1513 |
-
|
| 1514 |
-
### Configurazione MCP per Claude Desktop / Cursor
|
| 1515 |
-
```json
|
| 1516 |
-
{
|
| 1517 |
-
"mcpServers": {
|
| 1518 |
-
"nvidia-ai": {
|
| 1519 |
-
"url": "https://YOUR-SPACE.hf.space/v1/mcp",
|
| 1520 |
-
"transport": "http"
|
| 1521 |
-
}
|
| 1522 |
-
}
|
| 1523 |
-
}
|
| 1524 |
-
```
|
| 1525 |
""")
|
| 1526 |
|
| 1527 |
|
|
@@ -1532,12 +1354,6 @@ with gr.Blocks(
|
|
| 1532 |
app = gr.mount_gradio_app(app, demo, path="/ui")
|
| 1533 |
|
| 1534 |
|
| 1535 |
-
# Redirect root HTML to UI for browser visits
|
| 1536 |
-
@app.get("/app", response_class=HTMLResponse)
|
| 1537 |
-
async def redirect_to_ui():
|
| 1538 |
-
return '<html><head><meta http-equiv="refresh" content="0;url=/ui"></head></html>'
|
| 1539 |
-
|
| 1540 |
-
|
| 1541 |
if __name__ == "__main__":
|
| 1542 |
import uvicorn
|
| 1543 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 1 |
"""
|
| 2 |
π NVIDIA AI Multi-Model Space
|
| 3 |
+
- Tutti i modelli NVIDIA NIM (35+)
|
| 4 |
+
- Memoria breve (RAM) e lunga (SQLite)
|
| 5 |
+
- API REST GET/POST + SSE Streaming
|
| 6 |
- Server MCP integrato
|
| 7 |
+
- UI Web moderna alla root
|
| 8 |
+
- Interfaccia Gradio su /ui
|
| 9 |
"""
|
| 10 |
|
| 11 |
import os
|
|
|
|
| 14 |
import uuid
|
| 15 |
import base64
|
| 16 |
import asyncio
|
| 17 |
+
from pathlib import Path
|
| 18 |
from typing import Optional, AsyncGenerator
|
| 19 |
|
| 20 |
import gradio as gr
|
| 21 |
import httpx
|
| 22 |
from dotenv import load_dotenv
|
| 23 |
from fastapi import FastAPI, Request, Query, HTTPException
|
| 24 |
+
from fastapi.responses import (
|
| 25 |
+
StreamingResponse, JSONResponse, HTMLResponse, FileResponse
|
| 26 |
+
)
|
| 27 |
from fastapi.middleware.cors import CORSMiddleware
|
| 28 |
+
from fastapi.staticfiles import StaticFiles
|
| 29 |
from sse_starlette.sse import EventSourceResponse
|
| 30 |
from pydantic import BaseModel, Field
|
| 31 |
|
|
|
|
| 394 |
allow_headers=["*"],
|
| 395 |
)
|
| 396 |
|
| 397 |
+
# Mount statici se la cartella esiste
|
| 398 |
+
STATIC_DIR = Path(__file__).parent / "static"
|
| 399 |
+
if STATIC_DIR.exists():
|
| 400 |
+
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
| 401 |
+
|
| 402 |
|
| 403 |
# ββ Pydantic Models βββββββββββββββββββββββββββββββββββββββββββ
|
| 404 |
|
|
|
|
| 443 |
summary: Optional[str] = None
|
| 444 |
|
| 445 |
|
| 446 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 447 |
+
# ROOT UI + INFO
|
| 448 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 449 |
|
| 450 |
+
@app.get("/", response_class=HTMLResponse)
|
| 451 |
async def root():
|
| 452 |
+
"""Serve la UI web principale (static/index.html)"""
|
| 453 |
+
ui_file = STATIC_DIR / "index.html"
|
| 454 |
+
if ui_file.exists():
|
| 455 |
+
return FileResponse(str(ui_file))
|
| 456 |
+
# Fallback se manca il file
|
| 457 |
+
return HTMLResponse("""
|
| 458 |
+
<!DOCTYPE html><html><head><title>NVIDIA AI</title></head>
|
| 459 |
+
<body style="font-family:sans-serif;background:#0a0e14;color:#e4e6eb;padding:40px">
|
| 460 |
+
<h1>π NVIDIA AI Multi-Model</h1>
|
| 461 |
+
<p>UI non trovata (static/index.html mancante).</p>
|
| 462 |
+
<ul>
|
| 463 |
+
<li><a href="/ui" style="color:#76b900">Interfaccia Gradio</a></li>
|
| 464 |
+
<li><a href="/docs" style="color:#76b900">Swagger UI</a></li>
|
| 465 |
+
<li><a href="/api-docs" style="color:#76b900">API Docs HTML</a></li>
|
| 466 |
+
<li><a href="/info" style="color:#76b900">Info JSON</a></li>
|
| 467 |
+
</ul>
|
| 468 |
+
</body></html>
|
| 469 |
+
""")
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
@app.get("/info")
|
| 473 |
+
async def info():
|
| 474 |
+
"""Info del servizio (JSON)"""
|
| 475 |
return {
|
| 476 |
"service": "π NVIDIA AI Multi-Model Space",
|
| 477 |
"version": "2.0.0",
|
| 478 |
"models_available": len(NVIDIA_MODELS),
|
| 479 |
"endpoints": {
|
| 480 |
+
"ui": "/",
|
| 481 |
+
"gradio": "/ui",
|
| 482 |
"docs": "/docs",
|
| 483 |
"api_docs_html": "/api-docs",
|
| 484 |
"models": "/v1/models",
|
|
|
|
| 503 |
}
|
| 504 |
|
| 505 |
|
| 506 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 507 |
+
# MODELS
|
| 508 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 509 |
+
|
| 510 |
@app.get("/v1/models")
|
| 511 |
async def list_models(category: Optional[str] = None):
|
| 512 |
"""Lista tutti i modelli disponibili, filtrabile per categoria"""
|
|
|
|
| 531 |
return {"model_id": model_id, **NVIDIA_MODELS[model_id]}
|
| 532 |
|
| 533 |
|
| 534 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 535 |
+
# CHAT ENDPOINTS
|
| 536 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 537 |
|
| 538 |
@app.post("/v1/chat")
|
| 539 |
async def chat_post(req: ChatRequest):
|
|
|
|
| 546 |
if req.stream:
|
| 547 |
return await _stream_response(req, session_id)
|
| 548 |
|
|
|
|
| 549 |
if req.use_memory:
|
| 550 |
memory.add_short(session_id, "user", req.message)
|
| 551 |
messages = memory.get_full_context(session_id, req.system_prompt)
|
|
|
|
| 578 |
raise HTTPException(500, detail=str(e))
|
| 579 |
|
| 580 |
|
|
|
|
|
|
|
| 581 |
@app.get("/v1/chat/get")
|
| 582 |
async def chat_get(
|
| 583 |
message: str = Query(..., description="Messaggio utente"),
|
|
|
|
| 609 |
return await chat_post(req)
|
| 610 |
|
| 611 |
|
|
|
|
|
|
|
| 612 |
@app.post("/v1/chat/stream")
|
| 613 |
async def chat_stream_post(req: ChatRequest):
|
| 614 |
"""Endpoint dedicato per streaming SSE via POST"""
|
|
|
|
| 657 |
async def event_generator():
|
| 658 |
full_response = []
|
| 659 |
try:
|
|
|
|
| 660 |
yield {
|
| 661 |
"event": "start",
|
| 662 |
"data": json.dumps({
|
|
|
|
| 679 |
"data": json.dumps({"token": chunk})
|
| 680 |
}
|
| 681 |
|
|
|
|
| 682 |
complete = "".join(full_response)
|
| 683 |
if req.use_memory:
|
| 684 |
memory.add_short(session_id, "assistant", complete)
|
| 685 |
|
|
|
|
| 686 |
yield {
|
| 687 |
"event": "done",
|
| 688 |
"data": json.dumps({
|
|
|
|
| 702 |
return EventSourceResponse(event_generator())
|
| 703 |
|
| 704 |
|
| 705 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 706 |
+
# MEMORY MANAGEMENT
|
| 707 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 708 |
|
| 709 |
@app.post("/v1/memory")
|
| 710 |
async def manage_memory(req: MemoryRequest):
|
|
|
|
| 867 |
"error": {"code": code, "message": message}
|
| 868 |
})
|
| 869 |
|
|
|
|
| 870 |
if method == "initialize":
|
| 871 |
return _mcp_response({
|
| 872 |
"protocolVersion": "2024-11-05",
|
|
|
|
| 881 |
}
|
| 882 |
})
|
| 883 |
|
|
|
|
| 884 |
elif method == "tools/list":
|
| 885 |
return _mcp_response({"tools": MCP_TOOLS})
|
| 886 |
|
|
|
|
| 887 |
elif method == "tools/call":
|
| 888 |
tool_name = params.get("name", "")
|
| 889 |
tool_args = params.get("arguments", {})
|
|
|
|
| 896 |
except Exception as e:
|
| 897 |
return _mcp_error(-32000, str(e))
|
| 898 |
|
|
|
|
| 899 |
elif method == "resources/list":
|
| 900 |
return _mcp_response({
|
| 901 |
"resources": [
|
|
|
|
| 914 |
]
|
| 915 |
})
|
| 916 |
|
|
|
|
| 917 |
elif method == "resources/read":
|
| 918 |
uri = params.get("uri", "")
|
| 919 |
if uri == "nvidia://models":
|
|
|
|
| 1042 |
.param-table { width: 100%; border-collapse: collapse; margin: 10px 0; }
|
| 1043 |
.param-table th, .param-table td { padding: 10px; border: 1px solid var(--border); text-align: left; }
|
| 1044 |
.param-table th { background: var(--code-bg); color: var(--accent); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1045 |
.note { background: #1a3a1a; border-left: 4px solid var(--accent); padding: 15px; margin: 15px 0; border-radius: 0 8px 8px 0; }
|
|
|
|
| 1046 |
a { color: var(--accent); }
|
| 1047 |
</style>
|
| 1048 |
</head>
|
|
|
|
| 1054 |
|
| 1055 |
<div class="note">
|
| 1056 |
<strong>Base URL:</strong> <code>https://YOUR-SPACE.hf.space</code><br>
|
| 1057 |
+
<strong>Autenticazione:</strong> API key NVIDIA pre-configurata (default)
|
| 1058 |
</div>
|
| 1059 |
|
| 1060 |
+
<h2>π Endpoints</h2>
|
| 1061 |
<div class="endpoint">
|
| 1062 |
<table class="param-table">
|
| 1063 |
<tr><th>Metodo</th><th>Endpoint</th><th>Descrizione</th></tr>
|
| 1064 |
+
<tr><td><span class="method get">GET</span></td><td><code>/</code></td><td>UI Web principale</td></tr>
|
| 1065 |
+
<tr><td><span class="method get">GET</span></td><td><code>/ui</code></td><td>Interfaccia Gradio</td></tr>
|
| 1066 |
+
<tr><td><span class="method get">GET</span></td><td><code>/docs</code></td><td>Swagger UI</td></tr>
|
| 1067 |
+
<tr><td><span class="method get">GET</span></td><td><code>/v1/models</code></td><td>Lista modelli</td></tr>
|
| 1068 |
<tr><td><span class="method get">GET</span></td><td><code>/v1/chat/get</code></td><td>Chat via GET</td></tr>
|
| 1069 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/chat</code></td><td>Chat via POST</td></tr>
|
| 1070 |
+
<tr><td><span class="method get">GET</span></td><td><code>/v1/chat/stream</code></td><td>SSE streaming via GET</td></tr>
|
| 1071 |
+
<tr><td><span class="method post">POST</span></td><td><code>/v1/chat/stream</code></td><td>SSE streaming via POST</td></tr>
|
| 1072 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/memory</code></td><td>Gestione memoria</td></tr>
|
|
|
|
| 1073 |
<tr><td><span class="method post">POST</span></td><td><code>/v1/mcp</code></td><td>Server MCP (JSON-RPC)</td></tr>
|
| 1074 |
</table>
|
| 1075 |
</div>
|
| 1076 |
|
| 1077 |
+
<h2>π¬ Chat</h2>
|
|
|
|
|
|
|
| 1078 |
<div class="endpoint">
|
| 1079 |
+
<pre><code>curl -X POST https://YOUR-SPACE.hf.space/v1/chat \\
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1080 |
-H "Content-Type: application/json" \\
|
| 1081 |
-d '{
|
| 1082 |
"message": "Spiegami la relatività",
|
| 1083 |
"model": "meta/llama-3.1-405b-instruct",
|
| 1084 |
"session_id": "user-123",
|
|
|
|
| 1085 |
"use_memory": true
|
| 1086 |
}'</code></pre>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1087 |
</div>
|
| 1088 |
|
| 1089 |
+
<h2>π‘ SSE Streaming</h2>
|
| 1090 |
<div class="endpoint">
|
| 1091 |
+
<pre><code># JavaScript EventSource
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1092 |
const es = new EventSource(
|
| 1093 |
+
'/v1/chat/stream?message=Ciao&session_id=demo'
|
| 1094 |
);
|
| 1095 |
+
es.addEventListener('token', (e) => {
|
| 1096 |
+
console.log(JSON.parse(e.data).token);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1097 |
});
|
| 1098 |
+
es.addEventListener('done', (e) => es.close());</code></pre>
|
| 1099 |
|
| 1100 |
+
<pre><code># cURL
|
| 1101 |
+
curl -N "https://YOUR-SPACE.hf.space/v1/chat/stream?message=Ciao"</code></pre>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1102 |
</div>
|
| 1103 |
|
| 1104 |
+
<h2>π MCP Server</h2>
|
| 1105 |
<div class="endpoint">
|
| 1106 |
+
<pre><code># Config Claude Desktop / Cursor
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1107 |
{
|
| 1108 |
"mcpServers": {
|
| 1109 |
"nvidia-ai": {
|
|
|
|
| 1112 |
}
|
| 1113 |
}
|
| 1114 |
}</code></pre>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1115 |
</div>
|
| 1116 |
|
| 1117 |
</div>
|
|
|
|
| 1121 |
|
| 1122 |
|
| 1123 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1124 |
+
# GRADIO UI (secondaria, su /ui)
|
| 1125 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1126 |
|
| 1127 |
def get_model_choices():
|
|
|
|
| 1156 |
else:
|
| 1157 |
messages = [{"role": "system", "content": system_prompt}]
|
| 1158 |
for h in history:
|
| 1159 |
+
if isinstance(h, dict):
|
| 1160 |
+
if h.get("role") == "user":
|
| 1161 |
+
messages.append({"role": "user", "content": h["content"]})
|
| 1162 |
+
elif h.get("role") == "assistant":
|
| 1163 |
+
messages.append({"role": "assistant", "content": h["content"]})
|
| 1164 |
messages.append({"role": "user", "content": message})
|
| 1165 |
|
| 1166 |
full_response = ""
|
|
|
|
| 1183 |
|
| 1184 |
def get_memory_info(session_id: str):
|
| 1185 |
if not session_id:
|
| 1186 |
+
return {"error": "Inserisci un Session ID"}
|
| 1187 |
+
return memory.get_stats(session_id)
|
|
|
|
| 1188 |
|
| 1189 |
|
| 1190 |
def clear_session_memory(session_id: str):
|
|
|
|
| 1194 |
return f"ποΈ Memoria sessione '{session_id}' eliminata"
|
| 1195 |
|
| 1196 |
|
|
|
|
|
|
|
| 1197 |
with gr.Blocks(
|
| 1198 |
title="π NVIDIA AI Multi-Model Space",
|
| 1199 |
theme=gr.themes.Soft(
|
|
|
|
| 1211 |
# π NVIDIA AI Multi-Model Space
|
| 1212 |
**Chat con tutti i modelli NVIDIA NIM** β’ Memoria Breve/Lunga β’ API REST β’ SSE Streaming β’ MCP Server
|
| 1213 |
|
| 1214 |
+
π [UI Web Principale](/) | π [API Docs HTML](/api-docs) | π§ [Swagger UI](/docs) | π [MCP Endpoint](/v1/mcp)
|
| 1215 |
""")
|
| 1216 |
|
| 1217 |
with gr.Tab("π¬ Chat"):
|
|
|
|
| 1221 |
height=600,
|
| 1222 |
type="messages",
|
| 1223 |
show_copy_button=True,
|
|
|
|
| 1224 |
placeholder="Seleziona un modello e inizia a chattare..."
|
| 1225 |
)
|
| 1226 |
msg = gr.Textbox(
|
|
|
|
| 1263 |
|
| 1264 |
mem_output = gr.JSON(label="π Memoria", visible=True)
|
| 1265 |
|
|
|
|
| 1266 |
msg.submit(
|
| 1267 |
gradio_chat,
|
| 1268 |
inputs=[msg, chatbot, model, system_prompt, temperature, max_tokens, session_id, use_memory],
|
|
|
|
| 1295 |
with gr.Tab("π§ Memoria"):
|
| 1296 |
gr.Markdown("""
|
| 1297 |
## π§ Gestione Memoria
|
| 1298 |
+
- **Memoria Breve**: ultimi 20 messaggi (in RAM)
|
|
|
|
| 1299 |
- **Memoria Lunga**: messaggi piΓΉ vecchi salvati in SQLite
|
| 1300 |
- **Riassunti**: riassunti di sessione persistenti
|
| 1301 |
""")
|
|
|
|
| 1306 |
stats_btn = gr.Button("π Mostra Statistiche")
|
| 1307 |
clear_mem_btn = gr.Button("ποΈ Elimina Sessione", variant="stop")
|
| 1308 |
|
| 1309 |
+
mem_display = gr.JSON(label="Risultato")
|
| 1310 |
|
| 1311 |
stats_btn.click(get_memory_info, inputs=[mem_session], outputs=[mem_display])
|
| 1312 |
clear_mem_btn.click(clear_session_memory, inputs=[mem_session], outputs=[mem_display])
|
|
|
|
| 1315 |
gr.Markdown("""
|
| 1316 |
## π Documentazione API
|
| 1317 |
|
| 1318 |
+
π **[Apri documentazione completa](/api-docs)**
|
|
|
|
|
|
|
| 1319 |
|
| 1320 |
+
π **[OpenAPI / Swagger UI](/docs)**
|
| 1321 |
|
| 1322 |
### Quick Start
|
| 1323 |
|
| 1324 |
+
#### Chat via GET
|
| 1325 |
```
|
| 1326 |
GET /v1/chat/get?message=Ciao&model=meta/llama-3.1-8b-instruct&session_id=test
|
| 1327 |
```
|
|
|
|
| 1338 |
curl -N "/v1/chat/stream?message=Raccontami+una+storia"
|
| 1339 |
```
|
| 1340 |
|
| 1341 |
+
#### MCP
|
| 1342 |
```bash
|
| 1343 |
curl -X POST /v1/mcp \\
|
| 1344 |
-H "Content-Type: application/json" \\
|
| 1345 |
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
| 1346 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1347 |
""")
|
| 1348 |
|
| 1349 |
|
|
|
|
| 1354 |
app = gr.mount_gradio_app(app, demo, path="/ui")
|
| 1355 |
|
| 1356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1357 |
if __name__ == "__main__":
|
| 1358 |
import uvicorn
|
| 1359 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|