Compare commits

...

2 Commits

Author SHA1 Message Date
AI Station Server b2ff4238af readme add 2026-01-01 18:06:28 +01:00
AI Station Server 939a3d11a7 miglioramento UI e RAG 2026-01-01 17:33:39 +01:00
59 changed files with 2291 additions and 714 deletions

View File

@ -24,7 +24,7 @@ allow_origins = ["*"]
[features] [features]
# Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript) # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
unsafe_allow_html = false unsafe_allow_html = true
# Process and display mathematical expressions. This can clash with "$" characters in messages. # Process and display mathematical expressions. This can clash with "$" characters in messages.
latex = false latex = false
@ -57,7 +57,7 @@ reaction_on_message_received = false
# 3. For specific file extensions: # 3. For specific file extensions:
# accept = { "application/octet-stream" = [".xyz", ".pdb"] } # accept = { "application/octet-stream" = [".xyz", ".pdb"] }
# Note: Using "*/*" is not recommended as it may cause browser warnings # Note: Using "*/*" is not recommended as it may cause browser warnings
accept = ["*/*"] accept = ["*"]
max_files = 20 max_files = 20
max_size_mb = 500 max_size_mb = 500
@ -86,11 +86,11 @@ reaction_on_message_received = false
[UI] [UI]
# Name of the assistant. # Name of the assistant.
name = "Assistant" name = "Ai Station DFFM"
# default_theme = "dark" default_theme = "dark"
# layout = "wide" layout = "wide"
default_sidebar_state = "open" default_sidebar_state = "open"
@ -104,6 +104,14 @@ cot = "full"
# The CSS file can be served from the public directory or via an external link. # The CSS file can be served from the public directory or via an external link.
# custom_css = "/public/test.css" # custom_css = "/public/test.css"
# CSS personalizzato
custom_css = "/public/custom.css"
# Logo custom
[UI.theme]
primary_color = "#0066CC" # Colore brand
background_color = "#1a1a1a"
# Specify additional attributes for a custom CSS file # Specify additional attributes for a custom CSS file
# custom_css_attributes = "media=\"print\"" # custom_css_attributes = "media=\"print\""

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

View File

@ -0,0 +1,463 @@
import os
import re
import uuid
import shutil
import requests
import time
import json
from datetime import datetime
from typing import Optional, Dict, List, Any
import chainlit as cl
import ollama
from docling.document_converter import DocumentConverter
from qdrant_client import AsyncQdrantClient
# CORREZIONE IMPORT: Importiamo le classi necessarie direttamente dalla libreria
from qdrant_client.models import PointStruct, Distance, VectorParams, SparseVectorParams, Prefetch
from chainlit.data.sql_alchemy import SQLAlchemyDataLayer
from chainlit.types import ThreadDict
from functools import lru_cache
# === FIX IMPORT ROBUSTO ===
try:
from chainlit.data.storage_clients import BaseStorageClient
except ImportError:
try:
from chainlit.data.base import BaseStorageClient
except ImportError:
from chainlit.data.storage_clients.base import BaseStorageClient
# === CONFIGURAZIONE ===
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station")
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://192.168.1.243:11434")
QDRANT_URL = os.getenv("QDRANT_URL", "http://qdrant:6333")
BGE_API_URL = os.getenv("BGE_API_URL", "http://192.168.1.243:8001/embed")
VISION_MODEL = "minicpm-v"
DEFAULT_TEXT_MODEL = "glm-4.6:cloud"
WORKSPACES_DIR = "./workspaces"
STORAGE_DIR = "./.files"
os.makedirs(STORAGE_DIR, exist_ok=True)
os.makedirs(WORKSPACES_DIR, exist_ok=True)
# === MAPPING UTENTI ===
USER_PROFILES = {
"giuseppe@defranceschi.pro": { "role": "admin", "name": "Giuseppe", "workspace": "admin_workspace", "rag_collection": "admin_docs", "capabilities": ["debug", "all"], "show_code": True },
"giuseppe.defranceschi@gmail.com": { "role": "admin", "name": "Giuseppe", "workspace": "admin_workspace", "rag_collection": "admin_docs", "capabilities": ["debug", "all"], "show_code": True },
"federica.tecchio@gmail.com": { "role": "business", "name": "Federica", "workspace": "business_workspace", "rag_collection": "contabilita", "capabilities": ["basic_chat"], "show_code": False },
"riccardob545@gmail.com": { "role": "engineering", "name": "Riccardo", "workspace": "engineering_workspace", "rag_collection": "engineering_docs", "capabilities": ["code"], "show_code": True },
"giuliadefranceschi05@gmail.com": { "role": "architecture", "name": "Giulia", "workspace": "architecture_workspace", "rag_collection": "architecture_manuals", "capabilities": ["visual"], "show_code": False }
}
# === STORAGE CLIENT ===
class LocalStorageClient(BaseStorageClient):
def __init__(self, storage_path: str):
self.storage_path = storage_path
os.makedirs(storage_path, exist_ok=True)
async def upload_file(self, object_key: str, data: bytes, mime: str = "application/octet-stream", overwrite: bool = True) -> Dict[str, str]:
file_path = os.path.join(self.storage_path, object_key)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "wb") as f: f.write(data)
return {"object_key": object_key, "url": f"/files/{object_key}"}
async def get_read_url(self, object_key: str) -> str: return f"/files/{object_key}"
async def delete_file(self, object_key: str) -> bool:
path = os.path.join(self.storage_path, object_key)
if os.path.exists(path): os.remove(path); return True
return False
async def close(self): pass
@cl.data_layer
def get_data_layer():
return SQLAlchemyDataLayer(conninfo=DATABASE_URL, storage_provider=LocalStorageClient(STORAGE_DIR))
# === OAUTH & UTILS ===
@cl.oauth_callback
def oauth_callback(provider_id: str, token: str, raw_user_data: Dict[str, str], default_user: cl.User) -> Optional[cl.User]:
if provider_id == "google":
email = raw_user_data.get("email", "").lower()
profile = USER_PROFILES.get(email, USER_PROFILES.get("guest", {"role": "guest", "name": "Guest", "workspace": "guest", "rag_collection": "public", "show_code": False}))
default_user.metadata.update({"role": profile["role"], "workspace": profile["workspace"], "rag_collection": profile["rag_collection"], "show_code": profile["show_code"], "display_name": profile["name"]})
return default_user
return default_user
def create_workspace(workspace_name: str) -> str:
path = os.path.join(WORKSPACES_DIR, workspace_name)
os.makedirs(path, exist_ok=True)
return path
# === CORE: DOCLING ===
def process_file_with_docling(file_path: str) -> str:
try:
converter = DocumentConverter()
result = converter.convert(file_path)
return result.document.export_to_markdown()
except Exception as e:
print(f"❌ Docling Error: {e}")
return ""
# === CORE: BGE-M3 CLIENT ===
def get_bge_embeddings(text: str) -> Optional[Dict[str, Any]]:
try:
payload = {"texts": [text[:8000]]}
response = requests.post(BGE_API_URL, json=payload, timeout=30)
response.raise_for_status()
data = response.json().get("data", [])
if data:
return data[0]
return None
except Exception as e:
print(f"❌ BGE API Error: {e}")
return None
# === CORE: QDRANT ===
async def ensure_collection(collection_name: str):
client = AsyncQdrantClient(url=QDRANT_URL)
if not await client.collection_exists(collection_name):
await client.create_collection(
collection_name=collection_name,
vectors_config={"dense": VectorParams(size=1024, distance=Distance.COSINE)},
sparse_vectors_config={"sparse": SparseVectorParams()}
)
async def index_document(file_name: str, content: str, collection_name: str):
await ensure_collection(collection_name)
client = AsyncQdrantClient(url=QDRANT_URL)
chunk_size = 2000
overlap = 200
points = []
for i in range(0, len(content), chunk_size - overlap):
chunk = content[i : i + chunk_size]
embedding_data = get_bge_embeddings(chunk)
if embedding_data:
points.append(PointStruct(
id=str(uuid.uuid4()),
vector={
"dense": embedding_data["dense"],
"sparse": embedding_data["sparse"]
},
payload={
"file_name": file_name,
"content": chunk,
"indexed_at": datetime.now().isoformat()
}
))
if points:
await client.upsert(collection_name=collection_name, points=points)
return len(points)
return 0
async def search_hybrid(query: str, collection_name: str, limit: int = 4) -> str:
client = AsyncQdrantClient(url=QDRANT_URL)
if not await client.collection_exists(collection_name): return ""
query_emb = get_bge_embeddings(query)
if not query_emb: return ""
# CORREZIONE QUI: Usiamo l'oggetto Prefetch importato correttamente
results = await client.query_points(
collection_name=collection_name,
prefetch=[
Prefetch(
query=query_emb["sparse"],
using="sparse",
limit=limit * 2
)
],
query=query_emb["dense"],
using="dense",
limit=limit
)
context = []
for hit in results.points:
context.append(f"--- DA {hit.payload['file_name']} ---\n{hit.payload['content']}")
return "\n\n".join(context)
# === Caching Embeddings ===
@lru_cache(maxsize=1000)
def get_bge_embeddings_cached(text: str):
"""Cache per query ripetute"""
return get_bge_embeddings(text)
# === CHAINLIT HANDLERS ===
@cl.on_chat_start
async def start():
# 1. Profilo utente
user = cl.user_session.get("user")
email = user.identifier if user else "guest"
profile = USER_PROFILES.get(email, USER_PROFILES["giuseppe@defranceschi.pro"])
cl.user_session.set("profile", profile)
create_workspace(profile["workspace"])
# 2. Badge HTML personalizzato
role_color = {
"admin": "#e74c3c",
"engineering": "#3498db",
"business": "#2ecc71",
"architecture": "#9b59b6",
}.get(profile["role"], "#95a5a6")
badge_html = f"""
<div style="background:{role_color}; padding:8px; border-radius:8px; margin-bottom:16px;">
👤 <b>{profile['name']}</b> | 🔧 {profile['role'].upper()} | 📁 {profile['workspace']}
</div>
"""
await cl.Message(content=badge_html).send()
# 3. Settings UI
settings = await cl.ChatSettings(
[
cl.input_widget.Slider(
id="top_k",
label="Numero Documenti RAG",
initial=4,
min=1,
max=10,
step=1,
),
cl.input_widget.Select(
id="vision_detail",
label="Dettaglio Analisi Immagini",
values=["auto", "low", "high"],
initial_value="auto",
),
cl.input_widget.TextInput(
id="system_instruction",
label="Istruzione Sistema Custom (opzionale)",
initial="",
placeholder="Es: Rispondi sempre in formato tecnico...",
),
cl.input_widget.Select(
id="model",
label="Modello di Ragionamento",
values=[DEFAULT_TEXT_MODEL, "llama3.2", "mistral", "qwen2.5-coder:32b"],
initial_value=DEFAULT_TEXT_MODEL,
),
cl.input_widget.Slider(
id="temperature",
label="Creatività (Temperatura)",
initial=0.3,
min=0,
max=1,
step=0.1,
),
cl.input_widget.Switch(
id="rag_enabled",
label="Usa Conoscenza Documenti (RAG)",
initial=True,
),
]
).send()
cl.user_session.set("settings", settings)
# 4. Messaggio iniziale (opzionale)
await cl.Message(
content=(
f"🚀 **Vision-RAG Hybrid System Online**\n"
f"Utente: {profile['name']} | Workspace: {profile['workspace']}\n"
f"Engine: Docling + BGE-M3 + {VISION_MODEL}"
)
).send()
cl.user_session.set("settings", settings)
await cl.Message(f"🚀 **Vision-RAG Hybrid System Online**\nUtente: {profile['name']} | Workspace: {profile['workspace']}\nEngine: Docling + BGE-M3 + {VISION_MODEL}").send()
@cl.on_settings_update
async def setup_agent(settings):
cl.user_session.set("settings", settings)
await cl.Message(content=f"✅ Impostazioni aggiornate: Modello {settings['model']}").send()
async def log_metrics(metrics: dict):
# Versione minima: log su stdout
print("[METRICS]", metrics)
# In futuro puoi:
# - salvarle in Postgres
# - mandarle a Prometheus / Grafana
# - scriverle su file JSON per analisi settimanale
# - Resume Chat Handler
@cl.on_chat_resume
async def on_chat_resume(thread: ThreadDict):
"""
Viene chiamato quando l'utente clicca 'Riprendi' su una chat archiviata.
Chainlit carica già i messaggi nella UI, qui puoi solo ripristinare la sessione.
"""
# Se vuoi, puoi recuperare l'identifier dellutente dal thread
user_identifier = thread.get("userIdentifier")
profile = USER_PROFILES.get(
user_identifier,
USER_PROFILES["giuseppe@defranceschi.pro"],
)
cl.user_session.set("profile", profile)
# Puoi anche ripristinare eventuale stato custom (es: impostazioni di default)
# oppure semplicemente salutare lutente
await cl.Message(
content="👋 Bentornato! Possiamo riprendere da questa conversazione."
).send()
@cl.on_message
async def main(message: cl.Message):
start_time = time.time()
profile = cl.user_session.get("profile")
settings = cl.user_session.get("settings", {})
selected_model = settings.get("model", DEFAULT_TEXT_MODEL)
temperature = settings.get("temperature", 0.3)
rag_enabled = settings.get("rag_enabled", True)
workspace = create_workspace(profile["workspace"])
images_for_vision = []
doc_context = ""
rag_context = "" # ← la inizializzi qui, così esiste sempre
# 1. GESTIONE FILE
if message.elements:
for element in message.elements:
file_path = os.path.join(workspace, element.name)
shutil.copy(element.path, file_path)
if "image" in element.mime:
images_for_vision.append(file_path)
msg_img = cl.Message(
content=f"👁️ Analizzo immagine **{element.name}** con {VISION_MODEL}..."
)
await msg_img.send()
with open(file_path, "rb") as img_file:
img_bytes = img_file.read()
client_sync = ollama.Client(host=OLLAMA_URL)
res = client_sync.chat(
model=VISION_MODEL,
messages=[{
"role": "user",
"content": (
"Analizza questa immagine tecnica. Trascrivi testi, codici "
"e descrivi diagrammi o tabelle in dettaglio."
),
"images": [img_bytes],
}],
)
desc = res["message"]["content"]
doc_context += f"\n\n[DESCRIZIONE IMMAGINE {element.name}]:\n{desc}"
msg_img.content = f"✅ Immagine analizzata:\n{desc[:200]}..."
await msg_img.update()
elif element.name.endswith((".pdf", ".docx")):
msg_doc = cl.Message(
content=f"📄 Leggo **{element.name}** con Docling (tabelle/formule)..."
)
await msg_doc.send()
markdown_content = process_file_with_docling(file_path)
if markdown_content:
chunks = await index_document(
element.name, markdown_content, profile["rag_collection"]
)
msg_doc.content = (
f"✅ **{element.name}**: Convertito e salvato {chunks} "
"frammenti nel DB vettoriale."
)
doc_context += (
f"\n\n[CONTENUTO FILE {element.name}]:\n"
f"{markdown_content[:1000]}..."
)
else:
msg_doc.content = f"❌ Errore lettura {element.name}"
await msg_doc.update()
# 2. RAG RETRIEVAL
if rag_enabled and not images_for_vision:
rag_context = await search_hybrid(
message.content, profile["rag_collection"]
)
final_context = ""
if rag_context:
final_context += f"CONTESTO RAG:\n{rag_context}\n"
if doc_context:
final_context += f"CONTESTO SESSIONE CORRENTE:\n{doc_context}\n"
system_prompt = (
"Sei un assistente tecnico esperto. Usa il contesto fornito "
"(incluso Markdown di tabelle e descrizioni immagini) per "
"rispondere con precisione. Cita i documenti fonte."
)
msg = cl.Message(content="")
await msg.send()
error = None
# 3. GENERAZIONE
try:
client_async = ollama.AsyncClient(host=OLLAMA_URL)
stream = await client_async.chat(
model=selected_model,
messages=[
{"role": "system", "content": system_prompt},
{
"role": "user",
"content": f"Domanda: {message.content}\n\n{final_context}",
},
],
options={"temperature": temperature},
stream=True,
)
async for chunk in stream:
content = chunk["message"]["content"]
await msg.stream_token(content)
await msg.update()
except Exception as e:
error = str(e)
await msg.stream_token(f"❌ Errore AI: {error}")
await msg.update()
# 4. SALVATAGGIO CODICE
if profile["show_code"]:
code_blocks = re.findall(r"``````", msg.content, re.DOTALL)
if code_blocks:
for i, code in enumerate(code_blocks):
fname = f"script_{datetime.now().strftime('%H%M%S')}_{i}.py"
with open(os.path.join(workspace, fname), "w") as f:
f.write(code.strip())
await cl.Message(
content=f"💾 Script salvato: `{fname}`"
).send()
# 5. METRICHE (ALLA FINE)
elapsed = time.time() - start_time
# Se rag_context è una stringa concatenata, puoi stimare i "rag_hits"
# contando i separatori che usi in search_hybrid (es. '--- DA ')
if rag_context:
rag_hits = rag_context.count("--- DA ")
else:
rag_hits = 0
metrics = {
"response_time": elapsed,
"rag_hits": rag_hits,
"model": selected_model,
"user_role": profile["role"],
"error": error,
}
await log_metrics(metrics)

View File

@ -3,9 +3,12 @@ FROM python:3.11-slim
WORKDIR /app WORKDIR /app
# Installa dipendenze sistema # Installa dipendenze sistema
# Aggiunte libgl1 e libglib2.0-0 per il supporto Docling/CV2
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
gcc \ gcc \
postgresql-client \ postgresql-client \
libgl1 \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Copia requirements e installa # Copia requirements e installa

593
README.md
View File

@ -1,325 +1,342 @@
# AI Station - Document Analysis Platform # AI Station DFFM - Vision-RAG Hybrid System
## 📋 Overview Sistema AI multi-utente con supporto RAG (Retrieval-Augmented Generation), analisi immagini e gestione documenti avanzata, basato su Chainlit, Ollama e BGE-M3.
**AI Station** è una piattaforma di analisi documentale basata su AI che utilizza **Retrieval-Augmented Generation (RAG)** per analizzare PDF e documenti testuali con il modello **GLM-4.6:Cloud**. ## 🌟 Features
### Core AI
- **RAG Hybrid Search** con BGE-M3 (dense + sparse embeddings)
- **Vision Analysis** tramite MiniCPM-V per OCR e descrizione immagini
- **Document Processing** con Docling (PDF, DOCX) con preservazione tabelle/formule
- **Multi-Model Support** (Ollama locale + cloud models)
- **Streaming Responses** con latenza ridotta
### Multi-Utente
- **OAuth2 Google** con profili personalizzati per ruolo
- **Workspace isolati** per utente/team
- **RAG Collections dedicate** per knowledge base separate
- **Permessi granulari** (admin, engineering, business, architecture)
### UI/UX
- **Badge ruolo personalizzato** con colori dedicati
- **Settings dinamici** (temperatura, top_k RAG, modello, istruzioni custom)
- **Chat history persistente** con ripresa conversazioni
- **Auto-save codice Python** estratto dalle risposte
- **Metriche real-time** (response time, RAG hits, errori)
### Performance
- **Caching embeddings** (LRU cache 1000 query)
- **Chunking intelligente** (2000 char con overlap 200)
- **Async operations** su Qdrant e Ollama
- **PostgreSQL** per persistenza thread e metadata
## 🏗️ Architettura
### Hardware Setup
#### AI-SRV (Chainlit VM)
- **IP**: 192.168.1.244
- **CPU**: 16 core (QEMU Virtual)
- **RAM**: 64 GB
- **Storage**: 195 GB
- **Ruolo**: Host Chainlit app + PostgreSQL + Qdrant
#### AI-Server (GPU Workstation)
- **IP**: 192.168.1.243
- **CPU**: Intel Core Ultra 7 265 (20 core, max 6.5 GHz)
- **RAM**: 32 GB
- **GPU**: NVIDIA RTX A1000 (8 GB VRAM)
- **Storage**: 936 GB NVMe
- **Ruolo**: Ollama models + BGE-M3 embeddings service
### Stack Tecnologico ### Stack Tecnologico
- **Backend**: Python + Chainlit (LLM UI framework)
- **LLM**: GLM-4.6:Cloud (via Ollama Cloud)
- **Vector DB**: Qdrant (semantic search)
- **PDF Processing**: PyMuPDF (fitz)
- **Database**: PostgreSQL + SQLAlchemy ORM
- **Containerization**: Docker Compose
- **Embeddings**: nomic-embed-text (via Ollama local)
--- ┌─────────────────────────────────────────┐
│ Chainlit UI (ai-srv) │
│ Badge + Settings + Chat History │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Python Backend (app.py) │
│ - OAuth2 Google │
│ - Multi-user profiles │
│ - File processing orchestration │
└─┬────────┬──────────┬──────────┬────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────┐ ┌─────┐ ┌────────┐ ┌──────────┐
│ PG │ │Qdrant│ │ Ollama │ │ BGE API │
│ │ │Vector│ │ GPU │ │ CPU │
│ │ │ DB │ │ Server │ │ Server │
└─────┘ └─────┘ └────────┘ └──────────┘
ai-srv ai-srv ai-server ai-server
## 🚀 Quick Start text
### Prerequisites ## 📋 Requisiti
- Docker & Docker Compose
- Ollama installed locally (for embeddings)
- Ollama Cloud account (for glm-4.6:cloud)
### 1⃣ Clone & Setup ### Sistema
- Docker 24.x+ con Docker Compose
- Accesso a Google Cloud Console (per OAuth2)
- 2 server (o VM) con networking condiviso
### Modelli Ollama (da installare su ai-server)
```bash ```bash
git clone git@github.com:your-username/ai-station.git ollama pull minicpm-v # Vision model (5.5 GB)
ollama pull glm-4.6:cloud # Cloud reasoning
ollama pull qwen2.5-coder:32b # Code generation (9 GB)
ollama pull llama3.2 # Fast general purpose (4.7 GB)
🚀 Installazione
1. Clone Repository
bash
git clone <your-repo>
cd ai-station cd ai-station
2. Configurazione Ambiente
Crea .env:
# Configure environment bash
cat > .env << 'EOF' # Database
DATABASE_URL=postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station DATABASE_URL=postgresql+asyncpg://ai_user:CHANGE_ME@postgres:5432/ai_station
# AI Services
OLLAMA_URL=http://192.168.1.243:11434 OLLAMA_URL=http://192.168.1.243:11434
QDRANT_URL=http://qdrant:6333 QDRANT_URL=http://qdrant:6333
EOF BGE_API_URL=http://192.168.1.243:8001/embed
```
### 2⃣ Authenticate Ollama Cloud # OAuth Google
```bash OAUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
ollama signin OAUTH_GOOGLE_CLIENT_SECRET=your-secret
# Follow the link to authenticate with your Ollama account CHAINLIT_AUTH_SECRET=$(openssl rand -base64 32)
```
### 3⃣ Start Services 3. Configurazione OAuth Google
```bash Vai su Google Cloud Console
docker compose up -d
Crea nuovo progetto → API e servizi → Credenziali
Crea "ID client OAuth 2.0"
Aggiungi URI autorizzati:
https://ai.dffm.it/auth/oauth/google/callback
http://localhost:8000/auth/oauth/google/callback (dev)
Copia Client ID e Secret in .env
4. Personalizza Utenti
Modifica app.py → USER_PROFILES:
python
USER_PROFILES = {
"tuo.email@example.com": {
"role": "admin",
"name": "Nome",
"workspace": "workspace_name",
"rag_collection": "docs_collection",
"capabilities": ["debug", "all"],
"show_code": True,
},
# ... altri utenti
}
5. Deploy
bash
# Build e avvio
docker compose up -d --build
# Verifica logs
docker compose logs -f chainlit-app docker compose logs -f chainlit-app
# Dovresti vedere:
# ✅ Tutte le tabelle create con successo.
# Your app is available at http://localhost:8000
6. Setup BGE-M3 Service (su ai-server)
bash
# Installa dependencies
pip install fastapi uvicorn FlagEmbedding torch
# Salva il file bge_service.py (vedi docs/)
python bge_service.py
# Listening on http://0.0.0.0:8001
🎯 Utilizzo
Login
Accedi via browser: https://ai.dffm.it (o http://localhost:8000)
Click su "Continue with Google"
Autorizza con account configurato in USER_PROFILES
Chat con RAG
Carica PDF/DOCX → Sistema li indicizza automaticamente
Fai domande → Risposta con contesto dai documenti
Regola top_k (numero documenti) via settings
Analisi Immagini
Carica screenshot/diagrammi
Il sistema:
Estrae testo (OCR)
Descrive grafici/tabelle
Usa descrizione come contesto per rispondere
Settings Disponibili
Numero Documenti RAG (1-10): Quanti chunk recuperare
Modello: Scegli tra locale/cloud
Temperatura (0-1): Creatività risposta
RAG Enabled: On/Off recupero documenti
Istruzione Custom: Prompt system personalizzato
Ripresa Chat
Sidebar → Chat History
Click su conversazione → "Riprendi"
Continua da dove avevi lasciato
📊 Metriche
Ogni risposta logga (stdout):
json
{
"response_time": 18.65,
"rag_hits": 4,
"model": "glm-4.6:cloud",
"user_role": "admin",
"error": null
}
Raccogli con:
bash
docker logs ai-station-app | grep METRICS > metrics.log
🔧 Troubleshooting
RAG non trova documenti
Verifica collection name in USER_PROFILES[email]["rag_collection"]
Controlla Qdrant: curl http://localhost:6333/collections
Badge HTML non si vede
Abilita in .chainlit/config.toml:
``` ```
### 4⃣ Access UI ```text
Navigate to: **http://localhost:8000** [features]
unsafe_allow_html = true
--- Modello Ollama non risponde
bash
## 📁 Project Structure # Testa connessione
curl http://192.168.1.243:11434/api/tags
# Verifica modello disponibile
ollama list
BGE embeddings fail
``` ```
```bash
# Testa API
curl -X POST http://192.168.1.243:8001/embed \
-H "Content-Type: application/json" \
-d '{"texts": ["test"]}'
```
📁 Struttura Progetto
```bash
ai-station/ ai-station/
├── app.py # Main Chainlit application ├── app.py # Main Chainlit app
├── requirements.txt # Python dependencies ├── init_db.py # Database schema init
├── docker-compose.yml # Docker services config ├── requirements.txt # Python deps
├── .env # Environment variables (gitignored) ├── Dockerfile # Container config
├── workspaces/ # User workspace directories ├── docker-compose.yaml # Multi-service orchestration
│ └── admin/ # Admin user files ├── .chainlit/
└── README.md # This file │ └── config.toml # UI/features config
├── public/
│ └── custom.css # Custom styling
├── workspaces/ # User file storage (volume)
│ ├── admin_workspace/
│ ├── engineering_workspace/
│ └── ...
└── .files/ # Chainlit storage (volume)
``` ```
🔐 Sicurezza
---
## 🔧 Features
### ✅ Implemented
- **PDF Upload & Processing**: Extract text from PDF documents using PyMuPDF
- **Document Indexing**: Automatic chunking and semantic indexing via Qdrant
- **RAG Search**: Retrieve relevant document chunks based on semantic similarity
- **Intelligent Analysis**: GLM-4.6:Cloud analyzes documents with full context
- **Code Extraction**: Automatically save Python code blocks from responses
- **Chat History**: Persistent conversation storage via SQLAlchemy
- **Streaming Responses**: Real-time token streaming via Chainlit
### 🔄 Workflow
1. User uploads PDF or TXT file
2. System extracts text and creates semantic chunks
3. Chunks indexed in Qdrant vector database
4. User asks questions about documents
5. RAG retrieves relevant chunks
6. GLM-4.6:Cloud analyzes with full context
7. Streaming response to user
---
## 📊 Technical Details
### Document Processing Pipeline
```
PDF Upload
PyMuPDF Text Extraction
Text Chunking (1500 chars, 200 char overlap)
nomic-embed-text Embeddings (Ollama local)
Qdrant Vector Storage
Semantic Search on User Query
GLM-4.6:Cloud Analysis with RAG Context
Chainlit Streaming Response
```
### Key Functions
| Function | Purpose |
|----------|---------|
| `extract_text_from_pdf()` | Convert PDF to text using PyMuPDF |
| `chunk_text()` | Split text into overlapping chunks |
| `get_embeddings()` | Generate embeddings via Ollama |
| `index_document()` | Store chunks in Qdrant |
| `search_qdrant()` | Retrieve relevant context |
| `on_message()` | Process user queries with RAG |
---
## 🔐 Environment Variables
```env
DATABASE_URL=postgresql+asyncpg://user:pass@postgres:5432/ai_station
OLLAMA_URL=http://192.168.1.243:11434 # Local Ollama for embeddings
QDRANT_URL=http://qdrant:6333 # Vector database
```
**Note**: GLM-4.6:Cloud authentication is handled automatically via `ollama signin`
---
## 🐳 Docker Services
| Service | Port | Purpose |
|---------|------|---------|
| `chainlit-app` | 8000 | Chainlit UI & API |
| `postgres` | 5432 | Conversation persistence |
| `qdrant` | 6333 | Vector database |
| `ollama` | 11434 | Local embeddings (external) |
Start/Stop:
```bash ```bash
docker compose up -d # Start all services OAuth2 obbligatorio (no accesso anonimo)
docker compose down # Stop all services
docker compose logs -f # View logs Workspace isolation (file separati per utente)
docker compose restart # Restart services
HTML sanitization (configurable via unsafe_allow_html)
Environment secrets (.env mai committato)
PostgreSQL passwords cambiate da default
``` ```
---
## 📝 Usage Examples 🚦 Roadmap
### Example 1: Analyze Tax Document
```
User: "Qual è l'importo totale del documento?"
AI Station:
✅ Extracts PDF content
✅ Searches relevant sections
✅ Analyzes with GLM-4.6:Cloud
📄 Returns: "Based on the document, the total amount is..."
```
### Example 2: Multi-Document Analysis
```
1. Upload multiple PDFs (invoices, contracts)
2. All documents automatically indexed
3. Query across all documents simultaneously
4. RAG retrieves most relevant chunks
5. GLM-4.6:Cloud synthesizes answer
```
---
## 🛠️ Development
### Install Dependencies
```bash ```bash
pip install -r requirements.txt Re-ranking con cross-encoder
Query expansion automatica
Feedback loop (👍👎 su risposte)
Export conversazioni PDF/Markdown
Multi-query RAG parallelo
Prometheus/Grafana monitoring
Adaptive chunking per tipo documento
Audio input support
``` ```
### Requirements
``` ## 📝 Licenza
chainlit==1.3.2 ```tect
pydantic==2.9.2 MIT License - vedi file [LICENSE](LICENSE) per dettagli.
ollama>=0.1.0 Crea file LICENSE nella root del progetto
asyncpg>=0.29.0 text
psycopg2-binary MIT License
qdrant-client>=1.10.0
sqlalchemy>=2.0.0 Copyright (c) 2026 DFFM / Giuseppe De Franceschi
greenlet>=3.0.0
sniffio Permission is hereby granted, free of charge, to any person obtaining a copy
aiohttp of this software and associated documentation files (the "Software"), to deal
alembic in the Software without restriction, including without limitation the rights
pymupdf to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
python-dotenv copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
``` ```
### Local Testing (without Docker) 👥 Contributors
```bash Giuseppe De Franceschi - @defranceschi
# Start Ollama, PostgreSQL, Qdrant manually
ollama serve &
chainlit run app.py
```
--- 🙏 Credits
Chainlit - UI framework
## 🔄 Model Details Ollama - LLM runtime
### GLM-4.6:Cloud Qdrant - Vector DB
- **Provider**: Zhipu AI via Ollama Cloud
- **Capabilities**: Long context, reasoning, multilingual
- **Cost**: Free tier available
- **Authentication**: Device key (automatic via `ollama signin`)
### nomic-embed-text BGE-M3 - Embeddings
- **Local embedding model** for chunking/retrieval
- **Dimensions**: 768
- **Speed**: Fast, runs locally
- **Used for**: RAG semantic search
--- Docling - Document processing
## 📈 Monitoring & Logs ## **Status**: 🔨 Pre-Production | **Last Update**: 2026-01-01
### Check Service Health
```bash
# View all logs
docker compose logs
# Follow live logs
docker compose logs -f chainlit-app
# Check specific container
docker inspect ai-station-chainlit-app
```
### Common Issues
| Issue | Solution |
|-------|----------|
| `unauthorized` error | Run `ollama signin` on server |
| Database connection failed | Check PostgreSQL is running |
| Qdrant unavailable | Verify `docker-compose up` completed |
| PDF not extracted | Ensure PyMuPDF installed: `pip install pymupdf` |
---
## 🚀 Deployment
### Production Checklist
- [ ] Set secure PostgreSQL credentials in `.env`
- [ ] Enable SSL/TLS for Chainlit endpoints
- [ ] Configure CORS for frontend
- [ ] Setup log aggregation (ELK, Datadog, etc.)
- [ ] Implement rate limiting
- [ ] Add API authentication
- [ ] Configure backup strategy for Qdrant
### Cloud Deployment Options
- **AWS**: ECS + RDS + VectorDB
- **Google Cloud**: Cloud Run + Cloud SQL
- **DigitalOcean**: App Platform + Managed Databases
---
## 📚 API Reference
### REST Endpoints (via Chainlit)
- `POST /api/chat` - Send message with context
- `GET /api/threads` - List conversations
- `POST /api/upload` - Upload document
### WebSocket
- Real-time streaming responses via Chainlit protocol
---
## 🔮 Future Features
- [ ] OAuth2 Google authentication
- [ ] Document metadata extraction (dates, amounts, entities)
- [ ] Advanced search filters (type, date range, language)
- [ ] Export results (PDF, CSV, JSON)
- [ ] Analytics dashboard
- [ ] Multi-language support
- [ ] Document versioning
- [ ] Compliance reporting (GDPR, audit trails)
---
## 📞 Support
### Troubleshooting
1. Check logs: `docker compose logs chainlit-app`
2. Verify Ollama authentication: `ollama show glm-4.6:cloud`
3. Test Qdrant connection: `curl http://localhost:6333/health`
4. Inspect PostgreSQL: `docker compose exec postgres psql -U ai_user -d ai_station`
### Performance Tips
- Increase chunk overlap for better context retrieval
- Adjust embedding model based on latency requirements
- Monitor Qdrant memory usage for large document sets
- Implement caching for frequent queries
---
## 📄 License
MIT License - See LICENSE file
## 👤 Author
AI Station Team
---
**Last Updated**: December 26, 2025
**Version**: 1.0.0
**Status**: Production Ready ✅

607
app.py
View File

@ -2,17 +2,22 @@ import os
import re import re
import uuid import uuid
import shutil import shutil
import requests
import time
import json
from datetime import datetime from datetime import datetime
from typing import Optional, Dict, List from typing import Optional, Dict, List, Any
import chainlit as cl import chainlit as cl
import ollama import ollama
import fitz # PyMuPDF from docling.document_converter import DocumentConverter
from qdrant_client import AsyncQdrantClient from qdrant_client import AsyncQdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams # CORREZIONE IMPORT: Importiamo le classi necessarie direttamente dalla libreria
from qdrant_client.models import PointStruct, Distance, VectorParams, SparseVectorParams, Prefetch
from chainlit.data.sql_alchemy import SQLAlchemyDataLayer from chainlit.data.sql_alchemy import SQLAlchemyDataLayer
from chainlit.types import ThreadDict
from functools import lru_cache
# === FIX IMPORT ROBUSTO === # === FIX IMPORT ROBUSTO ===
# Gestisce le differenze tra le versioni di Chainlit 2.x
try: try:
from chainlit.data.storage_clients import BaseStorageClient from chainlit.data.storage_clients import BaseStorageClient
except ImportError: except ImportError:
@ -25,320 +30,434 @@ except ImportError:
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station") DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station")
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://192.168.1.243:11434") OLLAMA_URL = os.getenv("OLLAMA_URL", "http://192.168.1.243:11434")
QDRANT_URL = os.getenv("QDRANT_URL", "http://qdrant:6333") QDRANT_URL = os.getenv("QDRANT_URL", "http://qdrant:6333")
BGE_API_URL = os.getenv("BGE_API_URL", "http://192.168.1.243:8001/embed")
VISION_MODEL = "minicpm-v"
DEFAULT_TEXT_MODEL = "glm-4.6:cloud"
WORKSPACES_DIR = "./workspaces" WORKSPACES_DIR = "./workspaces"
STORAGE_DIR = "./.files" STORAGE_DIR = "./.files"
os.makedirs(STORAGE_DIR, exist_ok=True) os.makedirs(STORAGE_DIR, exist_ok=True)
os.makedirs(WORKSPACES_DIR, exist_ok=True) os.makedirs(WORKSPACES_DIR, exist_ok=True)
# === MAPPING UTENTI E RUOLI === # === MAPPING UTENTI ===
USER_PROFILES = { USER_PROFILES = {
"giuseppe@defranceschi.pro": { "giuseppe@defranceschi.pro": { "role": "admin", "name": "Giuseppe", "workspace": "admin_workspace", "rag_collection": "admin_docs", "capabilities": ["debug", "all"], "show_code": True },
"role": "admin", "giuseppe.defranceschi@gmail.com": { "role": "admin", "name": "Giuseppe", "workspace": "admin_workspace", "rag_collection": "admin_docs", "capabilities": ["debug", "all"], "show_code": True },
"name": "Giuseppe", "federica.tecchio@gmail.com": { "role": "business", "name": "Federica", "workspace": "business_workspace", "rag_collection": "contabilita", "capabilities": ["basic_chat"], "show_code": False },
"workspace": "admin_workspace", "riccardob545@gmail.com": { "role": "engineering", "name": "Riccardo", "workspace": "engineering_workspace", "rag_collection": "engineering_docs", "capabilities": ["code"], "show_code": True },
"rag_collection": "admin_docs", "giuliadefranceschi05@gmail.com": { "role": "architecture", "name": "Giulia", "workspace": "architecture_workspace", "rag_collection": "architecture_manuals", "capabilities": ["visual"], "show_code": False }
"capabilities": ["debug", "system_prompts", "user_management", "all_models"],
"show_code": True
},
"federica.tecchio@gmail.com": {
"role": "business",
"name": "Federica",
"workspace": "business_workspace",
"rag_collection": "contabilita",
"capabilities": ["pdf_upload", "basic_chat"],
"show_code": False
},
"giuseppe.defranceschi@gmail.com": {
"role": "admin",
"name": "Giuseppe",
"workspace": "admin_workspace",
"rag_collection": "admin_docs",
"capabilities": ["debug", "system_prompts", "user_management", "all_models"],
"show_code": True
},
"riccardob545@gmail.com": {
"role": "engineering",
"name": "Riccardo",
"workspace": "engineering_workspace",
"rag_collection": "engineering_docs",
"capabilities": ["code_execution", "data_viz", "advanced_chat"],
"show_code": True
},
"giuliadefranceschi05@gmail.com": {
"role": "architecture",
"name": "Giulia",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual_chat", "pdf_upload", "image_gen"],
"show_code": False
}
} }
# === CUSTOM LOCAL STORAGE CLIENT (FIXED) ===# Questa classe ora implementa tutti i metodi astratti richiesti da Chainlit 2.8.3 # === STORAGE CLIENT ===
class LocalStorageClient(BaseStorageClient): class LocalStorageClient(BaseStorageClient):
"""Storage locale su filesystem per file/elementi"""
def __init__(self, storage_path: str): def __init__(self, storage_path: str):
self.storage_path = storage_path self.storage_path = storage_path
os.makedirs(storage_path, exist_ok=True) os.makedirs(storage_path, exist_ok=True)
async def upload_file(self, object_key: str, data: bytes, mime: str = "application/octet-stream", overwrite: bool = True) -> Dict[str, str]:
async def upload_file(
self,
object_key: str,
data: bytes,
mime: str = "application/octet-stream",
overwrite: bool = True,
) -> Dict[str, str]:
file_path = os.path.join(self.storage_path, object_key) file_path = os.path.join(self.storage_path, object_key)
os.makedirs(os.path.dirname(file_path), exist_ok=True) os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "wb") as f: with open(file_path, "wb") as f: f.write(data)
f.write(data)
return {"object_key": object_key, "url": f"/files/{object_key}"} return {"object_key": object_key, "url": f"/files/{object_key}"}
async def get_read_url(self, object_key: str) -> str: return f"/files/{object_key}"
# Implementazione metodi obbligatori mancanti nella versione precedente
async def get_read_url(self, object_key: str) -> str:
return f"/files/{object_key}"
async def delete_file(self, object_key: str) -> bool: async def delete_file(self, object_key: str) -> bool:
file_path = os.path.join(self.storage_path, object_key) path = os.path.join(self.storage_path, object_key)
if os.path.exists(file_path): if os.path.exists(path): os.remove(path); return True
os.remove(file_path)
return True
return False return False
async def close(self): pass
async def close(self):
pass
# === DATA LAYER ===
@cl.data_layer @cl.data_layer
def get_data_layer(): def get_data_layer():
return SQLAlchemyDataLayer( return SQLAlchemyDataLayer(conninfo=DATABASE_URL, storage_provider=LocalStorageClient(STORAGE_DIR))
conninfo=DATABASE_URL,
user_thread_limit=1000,
storage_provider=LocalStorageClient(storage_path=STORAGE_DIR)
)
# === OAUTH CALLBACK === # === OAUTH & UTILS ===
@cl.oauth_callback @cl.oauth_callback
def oauth_callback( def oauth_callback(provider_id: str, token: str, raw_user_data: Dict[str, str], default_user: cl.User) -> Optional[cl.User]:
provider_id: str,
token: str,
raw_user_data: Dict[str, str],
default_user: cl.User,
) -> Optional[cl.User]:
if provider_id == "google": if provider_id == "google":
email = raw_user_data.get("email", "").lower() email = raw_user_data.get("email", "").lower()
profile = USER_PROFILES.get(email, USER_PROFILES.get("guest", {"role": "guest", "name": "Guest", "workspace": "guest", "rag_collection": "public", "show_code": False}))
# Verifica se utente è autorizzato (opzionale: blocca se non in lista) default_user.metadata.update({"role": profile["role"], "workspace": profile["workspace"], "rag_collection": profile["rag_collection"], "show_code": profile["show_code"], "display_name": profile["name"]})
# if email not in USER_PROFILES:
# return None
# Recupera profilo o usa default Guest
profile = USER_PROFILES.get(email, get_user_profile("guest"))
default_user.metadata.update({
"picture": raw_user_data.get("picture", ""),
"role": profile["role"],
"workspace": profile["workspace"],
"rag_collection": profile["rag_collection"],
"capabilities": profile["capabilities"],
"show_code": profile["show_code"],
"display_name": profile["name"]
})
return default_user return default_user
return default_user return default_user
# === UTILITY FUNCTIONS ===
def get_user_profile(user_email: str) -> Dict:
return USER_PROFILES.get(user_email.lower(), {
"role": "guest",
"name": "Ospite",
"workspace": "guest_workspace",
"rag_collection": "documents",
"capabilities": [],
"show_code": False
})
def create_workspace(workspace_name: str) -> str: def create_workspace(workspace_name: str) -> str:
path = os.path.join(WORKSPACES_DIR, workspace_name) path = os.path.join(WORKSPACES_DIR, workspace_name)
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
return path return path
def save_code_to_file(code: str, workspace: str) -> str:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name = f"code_{timestamp}.py"
file_path = os.path.join(WORKSPACES_DIR, workspace, file_name)
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
return file_path
def extract_text_from_pdf(pdf_path: str) -> str: # === CORE: DOCLING ===
def process_file_with_docling(file_path: str) -> str:
try: try:
doc = fitz.open(pdf_path) converter = DocumentConverter()
text = "\n".join([page.get_text() for page in doc]) result = converter.convert(file_path)
doc.close() return result.document.export_to_markdown()
return text except Exception as e:
except Exception: print(f"❌ Docling Error: {e}")
return "" return ""
# === QDRANT FUNCTIONS === # === CORE: BGE-M3 CLIENT ===
async def get_qdrant_client() -> AsyncQdrantClient: def get_bge_embeddings(text: str) -> Optional[Dict[str, Any]]:
return AsyncQdrantClient(url=QDRANT_URL) try:
payload = {"texts": [text[:8000]]}
response = requests.post(BGE_API_URL, json=payload, timeout=30)
response.raise_for_status()
data = response.json().get("data", [])
if data:
return data[0]
return None
except Exception as e:
print(f"❌ BGE API Error: {e}")
return None
# === CORE: QDRANT ===
async def ensure_collection(collection_name: str): async def ensure_collection(collection_name: str):
client = await get_qdrant_client() client = AsyncQdrantClient(url=QDRANT_URL)
if not await client.collection_exists(collection_name): if not await client.collection_exists(collection_name):
await client.create_collection( await client.create_collection(
collection_name=collection_name, collection_name=collection_name,
vectors_config=VectorParams(size=768, distance=Distance.COSINE) vectors_config={"dense": VectorParams(size=1024, distance=Distance.COSINE)},
sparse_vectors_config={"sparse": SparseVectorParams()}
) )
async def get_embeddings(text: str) -> list: async def index_document(file_name: str, content: str, collection_name: str):
client = ollama.Client(host=OLLAMA_URL) await ensure_collection(collection_name)
try: client = AsyncQdrantClient(url=QDRANT_URL)
response = client.embed(model='nomic-embed-text', input=text[:2000])
if 'embeddings' in response: return response['embeddings'][0]
return response.get('embedding', [])
except: return []
async def index_document(file_name: str, content: str, collection_name: str) -> bool: chunk_size = 2000
try: overlap = 200
await ensure_collection(collection_name)
embedding = await get_embeddings(content)
if not embedding: return False
qdrant = await get_qdrant_client() points = []
await qdrant.upsert( for i in range(0, len(content), chunk_size - overlap):
collection_name=collection_name, chunk = content[i : i + chunk_size]
points=[PointStruct( embedding_data = get_bge_embeddings(chunk)
if embedding_data:
points.append(PointStruct(
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
vector=embedding, vector={
payload={"file_name": file_name, "content": content[:3000], "indexed_at": datetime.now().isoformat()} "dense": embedding_data["dense"],
)] "sparse": embedding_data["sparse"]
) },
return True payload={
except: return False "file_name": file_name,
"content": chunk,
"indexed_at": datetime.now().isoformat()
}
))
async def search_qdrant(query: str, collection: str) -> str: if points:
try: await client.upsert(collection_name=collection_name, points=points)
client = await get_qdrant_client() return len(points)
if not await client.collection_exists(collection): return "" return 0
emb = await get_embeddings(query)
if not emb: return "" async def search_hybrid(query: str, collection_name: str, limit: int = 4) -> str:
res = await client.query_points(collection_name=collection, query=emb, limit=3) client = AsyncQdrantClient(url=QDRANT_URL)
return "\n\n".join([hit.payload['content'] for hit in res.points if hit.payload]) if not await client.collection_exists(collection_name): return ""
except: return ""
query_emb = get_bge_embeddings(query)
if not query_emb: return ""
# CORREZIONE QUI: Usiamo l'oggetto Prefetch importato correttamente
results = await client.query_points(
collection_name=collection_name,
prefetch=[
Prefetch(
query=query_emb["sparse"],
using="sparse",
limit=limit * 2
)
],
query=query_emb["dense"],
using="dense",
limit=limit
)
context = []
for hit in results.points:
context.append(f"--- DA {hit.payload['file_name']} ---\n{hit.payload['content']}")
return "\n\n".join(context)
# === Caching Embeddings ===
@lru_cache(maxsize=1000)
def get_bge_embeddings_cached(text: str):
"""Cache per query ripetute"""
return get_bge_embeddings(text)
# === CHAINLIT HANDLERS === # === CHAINLIT HANDLERS ===
@cl.on_chat_start @cl.on_chat_start
async def on_chat_start(): async def start():
# 1. Profilo utente
user = cl.user_session.get("user") user = cl.user_session.get("user")
email = user.identifier if user else "guest"
profile = USER_PROFILES.get(email, USER_PROFILES["giuseppe@defranceschi.pro"])
if not user: cl.user_session.set("profile", profile)
# Fallback locale se non c'è auth
user_email = "guest@local"
profile = get_user_profile(user_email)
else:
user_email = user.identifier
# I metadati sono già popolati dalla callback oauth
profile = USER_PROFILES.get(user_email, get_user_profile("guest"))
# Salva in sessione
cl.user_session.set("email", user_email)
cl.user_session.set("role", profile["role"])
cl.user_session.set("workspace", profile["workspace"])
cl.user_session.set("rag_collection", profile["rag_collection"])
cl.user_session.set("show_code", profile["show_code"])
create_workspace(profile["workspace"]) create_workspace(profile["workspace"])
# === SETTINGS WIDGETS === # 2. Badge HTML personalizzato
settings_widgets = [ role_color = {
cl.input_widget.Select( "admin": "#e74c3c",
id="model", "engineering": "#3498db",
label="Modello AI", "business": "#2ecc71",
values=["glm-4.6:cloud", "llama3.2", "mistral", "qwen2.5-coder:32b"], "architecture": "#9b59b6",
initial_value="glm-4.6:cloud", }.get(profile["role"], "#95a5a6")
),
cl.input_widget.Slider(
id="temperature",
label="Temperatura",
initial=0.7, min=0, max=2, step=0.1,
),
]
if profile["role"] == "admin":
settings_widgets.append(cl.input_widget.Switch(id="rag_enabled", label="Abilita RAG", initial=True))
await cl.ChatSettings(settings_widgets).send() badge_html = f"""
<div style="background:{role_color}; padding:8px; border-radius:8px; margin-bottom:16px;">
👤 <b>{profile['name']}</b> | 🔧 {profile['role'].upper()} | 📁 {profile['workspace']}
</div>
"""
await cl.Message(content=badge_html).send()
await cl.Message( # 3. Settings UI
content=f"👋 Ciao **{profile['name']}**!\n" settings = await cl.ChatSettings(
f"Ruolo: `{profile['role']}` | Workspace: `{profile['workspace']}`\n" [
cl.input_widget.Slider(
id="top_k",
label="Numero Documenti RAG",
initial=4,
min=1,
max=10,
step=1,
),
cl.input_widget.Select(
id="vision_detail",
label="Dettaglio Analisi Immagini",
values=["auto", "low", "high"],
initial_value="auto",
),
cl.input_widget.TextInput(
id="system_instruction",
label="Istruzione Sistema Custom (opzionale)",
initial="",
placeholder="Es: Rispondi sempre in formato tecnico...",
),
cl.input_widget.Select(
id="model",
label="Modello di Ragionamento",
values=[DEFAULT_TEXT_MODEL, "llama3.2", "mistral", "qwen2.5-coder:32b"],
initial_value=DEFAULT_TEXT_MODEL,
),
cl.input_widget.Slider(
id="temperature",
label="Creatività (Temperatura)",
initial=0.3,
min=0,
max=1,
step=0.1,
),
cl.input_widget.Switch(
id="rag_enabled",
label="Usa Conoscenza Documenti (RAG)",
initial=True,
),
]
).send() ).send()
@cl.on_settings_update
async def on_settings_update(settings):
cl.user_session.set("settings", settings) cl.user_session.set("settings", settings)
await cl.Message(content="✅ Impostazioni aggiornate").send()
# 4. Messaggio iniziale (opzionale)
await cl.Message(
content=(
f"🚀 **Vision-RAG Hybrid System Online**\n"
f"Utente: {profile['name']} | Workspace: {profile['workspace']}\n"
f"Engine: Docling + BGE-M3 + {VISION_MODEL}"
)
).send()
cl.user_session.set("settings", settings)
await cl.Message(f"🚀 **Vision-RAG Hybrid System Online**\nUtente: {profile['name']} | Workspace: {profile['workspace']}\nEngine: Docling + BGE-M3 + {VISION_MODEL}").send()
@cl.on_settings_update
async def setup_agent(settings):
cl.user_session.set("settings", settings)
await cl.Message(content=f"✅ Impostazioni aggiornate: Modello {settings['model']}").send()
async def log_metrics(metrics: dict):
# Versione minima: log su stdout
print("[METRICS]", metrics)
# In futuro puoi:
# - salvarle in Postgres
# - mandarle a Prometheus / Grafana
# - scriverle su file JSON per analisi settimanale
# - Resume Chat Handler
@cl.on_chat_resume
async def on_chat_resume(thread: ThreadDict):
"""
Viene chiamato quando l'utente clicca 'Riprendi' su una chat archiviata.
Chainlit carica già i messaggi nella UI, qui puoi solo ripristinare la sessione.
"""
# Se vuoi, puoi recuperare l'identifier dellutente dal thread
user_identifier = thread.get("userIdentifier")
profile = USER_PROFILES.get(
user_identifier,
USER_PROFILES["giuseppe@defranceschi.pro"],
)
cl.user_session.set("profile", profile)
# Puoi anche ripristinare eventuale stato custom (es: impostazioni di default)
# oppure semplicemente salutare lutente
await cl.Message(
content="👋 Bentornato! Possiamo riprendere da questa conversazione."
).send()
@cl.on_message @cl.on_message
async def on_message(message: cl.Message): async def main(message: cl.Message):
workspace = cl.user_session.get("workspace") start_time = time.time()
rag_collection = cl.user_session.get("rag_collection")
user_role = cl.user_session.get("role")
show_code = cl.user_session.get("show_code")
profile = cl.user_session.get("profile")
settings = cl.user_session.get("settings", {}) settings = cl.user_session.get("settings", {})
model = settings.get("model", "glm-4.6:cloud")
temperature = settings.get("temperature", 0.7) selected_model = settings.get("model", DEFAULT_TEXT_MODEL)
rag_enabled = settings.get("rag_enabled", True) if user_role == "admin" else True temperature = settings.get("temperature", 0.3)
rag_enabled = settings.get("rag_enabled", True)
workspace = create_workspace(profile["workspace"])
images_for_vision = []
doc_context = ""
rag_context = "" # ← la inizializzi qui, così esiste sempre
# 1. GESTIONE FILE # 1. GESTIONE FILE
if message.elements: if message.elements:
for element in message.elements: for element in message.elements:
dest = os.path.join(WORKSPACES_DIR, workspace, element.name) file_path = os.path.join(workspace, element.name)
shutil.copy(element.path, dest) shutil.copy(element.path, file_path)
if element.name.endswith(".pdf"):
text = extract_text_from_pdf(dest)
if text:
await index_document(element.name, text, rag_collection)
await cl.Message(content=f"✅ **{element.name}** indicizzato.").send()
# 2. RAG if "image" in element.mime:
context = "" images_for_vision.append(file_path)
if rag_enabled: msg_img = cl.Message(
context = await search_qdrant(message.content, rag_collection) content=f"👁️ Analizzo immagine **{element.name}** con {VISION_MODEL}..."
)
await msg_img.send()
system_prompt = "Sei un assistente esperto." with open(file_path, "rb") as img_file:
if context: system_prompt += f"\n\nCONTESTO:\n{context}" img_bytes = img_file.read()
client_sync = ollama.Client(host=OLLAMA_URL)
res = client_sync.chat(
model=VISION_MODEL,
messages=[{
"role": "user",
"content": (
"Analizza questa immagine tecnica. Trascrivi testi, codici "
"e descrivi diagrammi o tabelle in dettaglio."
),
"images": [img_bytes],
}],
)
desc = res["message"]["content"]
doc_context += f"\n\n[DESCRIZIONE IMMAGINE {element.name}]:\n{desc}"
msg_img.content = f"✅ Immagine analizzata:\n{desc[:200]}..."
await msg_img.update()
elif element.name.endswith((".pdf", ".docx")):
msg_doc = cl.Message(
content=f"📄 Leggo **{element.name}** con Docling (tabelle/formule)..."
)
await msg_doc.send()
markdown_content = process_file_with_docling(file_path)
if markdown_content:
chunks = await index_document(
element.name, markdown_content, profile["rag_collection"]
)
msg_doc.content = (
f"✅ **{element.name}**: Convertito e salvato {chunks} "
"frammenti nel DB vettoriale."
)
doc_context += (
f"\n\n[CONTENUTO FILE {element.name}]:\n"
f"{markdown_content[:1000]}..."
)
else:
msg_doc.content = f"❌ Errore lettura {element.name}"
await msg_doc.update()
# 2. RAG RETRIEVAL
if rag_enabled and not images_for_vision:
rag_context = await search_hybrid(
message.content, profile["rag_collection"]
)
final_context = ""
if rag_context:
final_context += f"CONTESTO RAG:\n{rag_context}\n"
if doc_context:
final_context += f"CONTESTO SESSIONE CORRENTE:\n{doc_context}\n"
system_prompt = (
"Sei un assistente tecnico esperto. Usa il contesto fornito "
"(incluso Markdown di tabelle e descrizioni immagini) per "
"rispondere con precisione. Cita i documenti fonte."
)
# 3. GENERAZIONE
client = ollama.AsyncClient(host=OLLAMA_URL)
msg = cl.Message(content="") msg = cl.Message(content="")
await msg.send() await msg.send()
stream = await client.chat( error = None
model=model,
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": message.content}],
options={"temperature": temperature},
stream=True
)
full_resp = "" # 3. GENERAZIONE
async for chunk in stream: try:
token = chunk['message']['content'] client_async = ollama.AsyncClient(host=OLLAMA_URL)
full_resp += token stream = await client_async.chat(
await msg.stream_token(token) model=selected_model,
await msg.update() messages=[
{"role": "system", "content": system_prompt},
{
"role": "user",
"content": f"Domanda: {message.content}\n\n{final_context}",
},
],
options={"temperature": temperature},
stream=True,
)
async for chunk in stream:
content = chunk["message"]["content"]
await msg.stream_token(content)
await msg.update()
except Exception as e:
error = str(e)
await msg.stream_token(f"❌ Errore AI: {error}")
await msg.update()
# 4. SALVATAGGIO CODICE # 4. SALVATAGGIO CODICE
if show_code: if profile["show_code"]:
blocks = re.findall(r"``````", full_resp, re.DOTALL) code_blocks = re.findall(r"``````", msg.content, re.DOTALL)
elements = [] if code_blocks:
for code in blocks: for i, code in enumerate(code_blocks):
path = save_code_to_file(code.strip(), workspace) fname = f"script_{datetime.now().strftime('%H%M%S')}_{i}.py"
elements.append(cl.File(name=os.path.basename(path), path=path, display="inline")) with open(os.path.join(workspace, fname), "w") as f:
if elements: f.write(code.strip())
await cl.Message(content="💾 Codice salvato", elements=elements).send() await cl.Message(
content=f"💾 Script salvato: `{fname}`"
).send()
# 5. METRICHE (ALLA FINE)
elapsed = time.time() - start_time
# Se rag_context è una stringa concatenata, puoi stimare i "rag_hits"
# contando i separatori che usi in search_hybrid (es. '--- DA ')
if rag_context:
rag_hits = rag_context.count("--- DA ")
else:
rag_hits = 0
metrics = {
"response_time": elapsed,
"rag_hits": rag_hits,
"model": selected_model,
"user_role": profile["role"],
"error": error,
}
await log_metrics(metrics)

344
bck/app-bck212.py Normal file
View File

@ -0,0 +1,344 @@
import os
import re
import uuid
import shutil
from datetime import datetime
from typing import Optional, Dict, List
import chainlit as cl
import ollama
import fitz # PyMuPDF
from qdrant_client import AsyncQdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from chainlit.data.sql_alchemy import SQLAlchemyDataLayer
# === FIX IMPORT ROBUSTO ===
# Gestisce le differenze tra le versioni di Chainlit 2.x
try:
from chainlit.data.storage_clients import BaseStorageClient
except ImportError:
try:
from chainlit.data.base import BaseStorageClient
except ImportError:
from chainlit.data.storage_clients.base import BaseStorageClient
# === CONFIGURAZIONE ===
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station")
OLLAMA_URL = os.getenv("OLLAMA_URL", "http://192.168.1.243:11434")
QDRANT_URL = os.getenv("QDRANT_URL", "http://qdrant:6333")
WORKSPACES_DIR = "./workspaces"
STORAGE_DIR = "./.files"
os.makedirs(STORAGE_DIR, exist_ok=True)
os.makedirs(WORKSPACES_DIR, exist_ok=True)
# === MAPPING UTENTI E RUOLI ===
USER_PROFILES = {
"giuseppe@defranceschi.pro": {
"role": "admin",
"name": "Giuseppe",
"workspace": "admin_workspace",
"rag_collection": "admin_docs",
"capabilities": ["debug", "system_prompts", "user_management", "all_models"],
"show_code": True
},
"federica.tecchio@gmail.com": {
"role": "business",
"name": "Federica",
"workspace": "business_workspace",
"rag_collection": "contabilita",
"capabilities": ["pdf_upload", "basic_chat"],
"show_code": False
},
"giuseppe.defranceschi@gmail.com": {
"role": "admin",
"name": "Giuseppe",
"workspace": "admin_workspace",
"rag_collection": "admin_docs",
"capabilities": ["debug", "system_prompts", "user_management", "all_models"],
"show_code": True
},
"riccardob545@gmail.com": {
"role": "engineering",
"name": "Riccardo",
"workspace": "engineering_workspace",
"rag_collection": "engineering_docs",
"capabilities": ["code_execution", "data_viz", "advanced_chat"],
"show_code": True
},
"giuliadefranceschi05@gmail.com": {
"role": "architecture",
"name": "Giulia",
"workspace": "architecture_workspace",
"rag_collection": "architecture_manuals",
"capabilities": ["visual_chat", "pdf_upload", "image_gen"],
"show_code": False
}
}
# === CUSTOM LOCAL STORAGE CLIENT (FIXED) ===# Questa classe ora implementa tutti i metodi astratti richiesti da Chainlit 2.8.3
class LocalStorageClient(BaseStorageClient):
"""Storage locale su filesystem per file/elementi"""
def __init__(self, storage_path: str):
self.storage_path = storage_path
os.makedirs(storage_path, exist_ok=True)
async def upload_file(
self,
object_key: str,
data: bytes,
mime: str = "application/octet-stream",
overwrite: bool = True,
) -> Dict[str, str]:
file_path = os.path.join(self.storage_path, object_key)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "wb") as f:
f.write(data)
return {"object_key": object_key, "url": f"/files/{object_key}"}
# Implementazione metodi obbligatori mancanti nella versione precedente
async def get_read_url(self, object_key: str) -> str:
return f"/files/{object_key}"
async def delete_file(self, object_key: str) -> bool:
file_path = os.path.join(self.storage_path, object_key)
if os.path.exists(file_path):
os.remove(file_path)
return True
return False
async def close(self):
pass
# === DATA LAYER ===
@cl.data_layer
def get_data_layer():
return SQLAlchemyDataLayer(
conninfo=DATABASE_URL,
user_thread_limit=1000,
storage_provider=LocalStorageClient(storage_path=STORAGE_DIR)
)
# === OAUTH CALLBACK ===
@cl.oauth_callback
def oauth_callback(
provider_id: str,
token: str,
raw_user_data: Dict[str, str],
default_user: cl.User,
) -> Optional[cl.User]:
if provider_id == "google":
email = raw_user_data.get("email", "").lower()
# Verifica se utente è autorizzato (opzionale: blocca se non in lista)
# if email not in USER_PROFILES:
# return None
# Recupera profilo o usa default Guest
profile = USER_PROFILES.get(email, get_user_profile("guest"))
default_user.metadata.update({
"picture": raw_user_data.get("picture", ""),
"role": profile["role"],
"workspace": profile["workspace"],
"rag_collection": profile["rag_collection"],
"capabilities": profile["capabilities"],
"show_code": profile["show_code"],
"display_name": profile["name"]
})
return default_user
return default_user
# === UTILITY FUNCTIONS ===
def get_user_profile(user_email: str) -> Dict:
return USER_PROFILES.get(user_email.lower(), {
"role": "guest",
"name": "Ospite",
"workspace": "guest_workspace",
"rag_collection": "documents",
"capabilities": [],
"show_code": False
})
def create_workspace(workspace_name: str) -> str:
path = os.path.join(WORKSPACES_DIR, workspace_name)
os.makedirs(path, exist_ok=True)
return path
def save_code_to_file(code: str, workspace: str) -> str:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name = f"code_{timestamp}.py"
file_path = os.path.join(WORKSPACES_DIR, workspace, file_name)
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
return file_path
def extract_text_from_pdf(pdf_path: str) -> str:
try:
doc = fitz.open(pdf_path)
text = "\n".join([page.get_text() for page in doc])
doc.close()
return text
except Exception:
return ""
# === QDRANT FUNCTIONS ===
async def get_qdrant_client() -> AsyncQdrantClient:
return AsyncQdrantClient(url=QDRANT_URL)
async def ensure_collection(collection_name: str):
client = await get_qdrant_client()
if not await client.collection_exists(collection_name):
await client.create_collection(
collection_name=collection_name,
vectors_config=VectorParams(size=768, distance=Distance.COSINE)
)
async def get_embeddings(text: str) -> list:
client = ollama.Client(host=OLLAMA_URL)
try:
response = client.embed(model='nomic-embed-text', input=text[:2000])
if 'embeddings' in response: return response['embeddings'][0]
return response.get('embedding', [])
except: return []
async def index_document(file_name: str, content: str, collection_name: str) -> bool:
try:
await ensure_collection(collection_name)
embedding = await get_embeddings(content)
if not embedding: return False
qdrant = await get_qdrant_client()
await qdrant.upsert(
collection_name=collection_name,
points=[PointStruct(
id=str(uuid.uuid4()),
vector=embedding,
payload={"file_name": file_name, "content": content[:3000], "indexed_at": datetime.now().isoformat()}
)]
)
return True
except: return False
async def search_qdrant(query: str, collection: str) -> str:
try:
client = await get_qdrant_client()
if not await client.collection_exists(collection): return ""
emb = await get_embeddings(query)
if not emb: return ""
res = await client.query_points(collection_name=collection, query=emb, limit=3)
return "\n\n".join([hit.payload['content'] for hit in res.points if hit.payload])
except: return ""
# === CHAINLIT HANDLERS ===
@cl.on_chat_start
async def on_chat_start():
user = cl.user_session.get("user")
if not user:
# Fallback locale se non c'è auth
user_email = "guest@local"
profile = get_user_profile(user_email)
else:
user_email = user.identifier
# I metadati sono già popolati dalla callback oauth
profile = USER_PROFILES.get(user_email, get_user_profile("guest"))
# Salva in sessione
cl.user_session.set("email", user_email)
cl.user_session.set("role", profile["role"])
cl.user_session.set("workspace", profile["workspace"])
cl.user_session.set("rag_collection", profile["rag_collection"])
cl.user_session.set("show_code", profile["show_code"])
create_workspace(profile["workspace"])
# === SETTINGS WIDGETS ===
settings_widgets = [
cl.input_widget.Select(
id="model",
label="Modello AI",
values=["glm-4.6:cloud", "llama3.2", "mistral", "qwen2.5-coder:32b"],
initial_value="glm-4.6:cloud",
),
cl.input_widget.Slider(
id="temperature",
label="Temperatura",
initial=0.7, min=0, max=2, step=0.1,
),
]
if profile["role"] == "admin":
settings_widgets.append(cl.input_widget.Switch(id="rag_enabled", label="Abilita RAG", initial=True))
await cl.ChatSettings(settings_widgets).send()
await cl.Message(
content=f"👋 Ciao **{profile['name']}**!\n"
f"Ruolo: `{profile['role']}` | Workspace: `{profile['workspace']}`\n"
).send()
@cl.on_settings_update
async def on_settings_update(settings):
cl.user_session.set("settings", settings)
await cl.Message(content="✅ Impostazioni aggiornate").send()
@cl.on_message
async def on_message(message: cl.Message):
workspace = cl.user_session.get("workspace")
rag_collection = cl.user_session.get("rag_collection")
user_role = cl.user_session.get("role")
show_code = cl.user_session.get("show_code")
settings = cl.user_session.get("settings", {})
model = settings.get("model", "glm-4.6:cloud")
temperature = settings.get("temperature", 0.7)
rag_enabled = settings.get("rag_enabled", True) if user_role == "admin" else True
# 1. GESTIONE FILE
if message.elements:
for element in message.elements:
dest = os.path.join(WORKSPACES_DIR, workspace, element.name)
shutil.copy(element.path, dest)
if element.name.endswith(".pdf"):
text = extract_text_from_pdf(dest)
if text:
await index_document(element.name, text, rag_collection)
await cl.Message(content=f"✅ **{element.name}** indicizzato.").send()
# 2. RAG
context = ""
if rag_enabled:
context = await search_qdrant(message.content, rag_collection)
system_prompt = "Sei un assistente esperto."
if context: system_prompt += f"\n\nCONTESTO:\n{context}"
# 3. GENERAZIONE
client = ollama.AsyncClient(host=OLLAMA_URL)
msg = cl.Message(content="")
await msg.send()
stream = await client.chat(
model=model,
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": message.content}],
options={"temperature": temperature},
stream=True
)
full_resp = ""
async for chunk in stream:
token = chunk['message']['content']
full_resp += token
await msg.stream_token(token)
await msg.update()
# 4. SALVATAGGIO CODICE
if show_code:
blocks = re.findall(r"``````", full_resp, re.DOTALL)
elements = []
for code in blocks:
path = save_code_to_file(code.strip(), workspace)
elements.append(cl.File(name=os.path.basename(path), path=path, display="inline"))
if elements:
await cl.Message(content="💾 Codice salvato", elements=elements).send()

39
bck/cpu.txt Normal file
View File

@ -0,0 +1,39 @@
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 40 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 16
On-line CPU(s) list: 0-15
Vendor ID: GenuineIntel
Model name: QEMU Virtual CPU version 2.5+
CPU family: 15
Model: 107
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 4
Stepping: 1
BogoMIPS: 4999.99
Flags: fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm constant_tsc nopl xtopology cpuid tsc_known_freq pni ssse3 cx16 sse4_1 sse4_2 x2apic popcnt aes hypervisor lahf_lm cpuid_fault pti
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 512 KiB (16 instances)
L1i cache: 512 KiB (16 instances)
L2 cache: 64 MiB (16 instances)
L3 cache: 64 MiB (4 instances)
NUMA node(s): 1
NUMA node0 CPU(s): 0-15
Vulnerability Gather data sampling: Not affected
Vulnerability Itlb multihit: KVM: Mitigation: VMX unsupported
Vulnerability L1tf: Mitigation; PTE Inversion
Vulnerability Mds: Vulnerable: Clear CPU buffers attempted, no microcode; SMT Host state unknown
Vulnerability Meltdown: Mitigation; PTI
Vulnerability Mmio stale data: Unknown: No mitigations
Vulnerability Reg file data sampling: Not affected
Vulnerability Retbleed: Not affected
Vulnerability Spec rstack overflow: Not affected
Vulnerability Spec store bypass: Vulnerable
Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2: Mitigation; Retpolines; STIBP disabled; RSB filling; PBRSB-eIBRS Not affected; BHI Retpoline
Vulnerability Srbds: Not affected
Vulnerability Tsx async abort: Not affected
Vulnerability Vmscape: Not affected

717
bck/error.log Normal file
View File

@ -0,0 +1,717 @@
ai-station-app | [INFO] 2025-12-31 10:20:23,724 [RapidOCR] download_file.py:82: Download size: 25.67MB
ai-station-app | [INFO] 2025-12-31 10:20:27,451 [RapidOCR] download_file.py:95: Successfully saved to: /usr/local/lib/python3.11/site-packages/rapidocr/models/ch_PP-OCRv4_rec_infer.pth
ai-station-app | [INFO] 2025-12-31 10:20:27,460 [RapidOCR] main.py:50: Using /usr/local/lib/python3.11/site-packages/rapidocr/models/ch_PP-OCRv4_rec_infer.pth
ai-station-app | 2025-12-31 10:20:28 - Auto OCR model selected rapidocr with torch.
ai-station-app | 2025-12-31 10:20:28 - Loading plugin 'docling_defaults'
ai-station-app | 2025-12-31 10:20:28 - Registered layout engines: ['docling_layout_default', 'docling_experimental_table_crops_layout']
ai-station-app | 2025-12-31 10:20:28 - Accelerator device: 'cpu'
ai-station-app | 2025-12-31 10:20:59 - Loading plugin 'docling_defaults'
ai-station-app | 2025-12-31 10:20:59 - Registered table structure engines: ['docling_tableformer']
ai-station-app | 2025-12-31 10:22:00 - Accelerator device: 'cpu'
ai-station-app | 2025-12-31 10:22:02 - Processing document esempio manuale Omron.pdf
ai-station-app | 2025-12-31 10:23:07 - Finished converting document esempio manuale Omron.pdf in 173.75 sec.
ai-station-app | 2025-12-31 10:23:07 - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
ai-station-app | 2025-12-31 10:23:07 - An unexpected error occurred:
ai-station-app | 2025-12-31 10:23:07 - Translation file for it-IT not found. Using default translation en-US.
ai-station-qdrant | 2025-12-31T10:23:07.266024Z INFO actix_web::middleware::logger: 172.18.0.4 "GET /collections/admin_docs/exists HTTP/1.1" 200 69 "-" "python-client/1.16.2 python/3.11.14" 0.001423
ai-station-app | 2025-12-31 10:23:07 - HTTP Request: GET http://qdrant:6333/collections/admin_docs/exists "HTTP/1.1 200 OK"
ai-station-qdrant | 2025-12-31T10:23:07.422109Z INFO storage::content_manager::toc::collection_meta_ops: Creating collection admin_docs
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-426' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-429' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-432' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-434' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-437' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-439' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-441' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-443' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-446' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-448' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-450' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-452' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-454' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-457' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-459' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-461' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-463' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-465' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-467' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-469' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-471' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-474' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-478' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-480' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-482' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-484' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-486' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-488' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-490' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-492' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-494' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-496' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-498' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-501' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-503' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | 2025-12-31 10:23:07 - Task exception was never retrieved
ai-station-app | future: <Task finished name='Task-505' coro=<AsyncServer._handle_event_internal() done, defined at /usr/local/lib/python3.11/site-packages/socketio/async_server.py:605> exception=ValueError('Session not found')>
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/asyncio/tasks.py", line 277, in __step
ai-station-app | result = coro.send(None)
ai-station-app | ^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 607, in _handle_event_internal
ai-station-app | r = await server._trigger_event(data[0], namespace, sid, *data[1:])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/socketio/async_server.py", line 634, in _trigger_event
ai-station-app | ret = await handler(*args)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/socket.py", line 323, in window_message
ai-station-app | session = WebsocketSession.require(sid)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/session.py", line 354, in require
ai-station-app | raise ValueError("Session not found")
ai-station-app | ValueError: Session not found
ai-station-app | post request handler error
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 306, in handle_request
ai-station-app | await socket.handle_post_request(environ)
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 109, in handle_post_request
ai-station-app | p = payload.Payload(encoded_payload=body)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/payload.py", line 13, in __init__
ai-station-app | self.decode(encoded_payload)
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/payload.py", line 44, in decode
ai-station-app | raise ValueError('Too many packets in payload')
ai-station-app | ValueError: Too many packets in payload
ai-station-app | 2025-12-31 10:23:07 - post request handler error
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/async_server.py", line 306, in handle_request
ai-station-app | await socket.handle_post_request(environ)
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/async_socket.py", line 109, in handle_post_request
ai-station-app | p = payload.Payload(encoded_payload=body)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/payload.py", line 13, in __init__
ai-station-app | self.decode(encoded_payload)
ai-station-app | File "/usr/local/lib/python3.11/site-packages/engineio/payload.py", line 44, in decode
ai-station-app | raise ValueError('Too many packets in payload')
ai-station-app | ValueError: Too many packets in payload
ai-station-qdrant | 2025-12-31T10:23:07.898915Z INFO actix_web::middleware::logger: 172.18.0.4 "PUT /collections/admin_docs HTTP/1.1" 200 57 "-" "python-client/1.16.2 python/3.11.14" 0.478796
ai-station-app | 2025-12-31 10:23:07 - HTTP Request: PUT http://qdrant:6333/collections/admin_docs "HTTP/1.1 200 OK"
ai-station-app | 2025-12-31 10:23:08 - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
ai-station-qdrant | 2025-12-31T10:23:12.256007Z INFO actix_web::middleware::logger: 172.18.0.4 "PUT /collections/admin_docs/points?wait=true HTTP/1.1" 200 84 "-" "python-client/1.16.2 python/3.11.14" 0.128378
ai-station-app | 2025-12-31 10:23:12 - HTTP Request: PUT http://qdrant:6333/collections/admin_docs/points?wait=true "HTTP/1.1 200 OK"
ai-station-app | 2025-12-31 10:23:12 - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
ai-station-qdrant | 2025-12-31T10:23:12.413564Z INFO actix_web::middleware::logger: 172.18.0.4 "GET /collections/admin_docs/exists HTTP/1.1" 200 68 "-" "python-client/1.16.2 python/3.11.14" 0.006418
ai-station-app | 2025-12-31 10:23:12 - HTTP Request: GET http://qdrant:6333/collections/admin_docs/exists "HTTP/1.1 200 OK"
ai-station-app | 2025-12-31 10:23:12 - module 'chainlit.data' has no attribute 'qdrant_client'
ai-station-app | Traceback (most recent call last):
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/utils.py", line 57, in wrapper
ai-station-app | return await user_function(**params_values)
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/usr/local/lib/python3.11/site-packages/chainlit/callbacks.py", line 161, in with_parent_id
ai-station-app | await func(message)
ai-station-app | File "/app/app.py", line 250, in main
ai-station-app | rag_context = await search_hybrid(message.content, profile["rag_collection"])
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ai-station-app | File "/app/app.py", line 166, in search_hybrid
ai-station-app | cl.data.qdrant_client.models.Prefetch(
ai-station-app | ^^^^^^^^^^^^^^^^^^^^^
ai-station-app | AttributeError: module 'chainlit.data' has no attribute 'qdrant_client'
ai-station-app | 2025-12-31 10:23:28 - Translation file for it-IT not found. Using default translation en-US.
ai-station-postgres | 2025-12-31 10:23:35.822 UTC [28] LOG: checkpoint starting: time
ai-station-postgres | 2025-12-31 10:23:37.312 UTC [28] LOG: checkpoint complete: wrote 17 buffers (0.1%); 0 WAL file(s) added, 0 removed, 0 recycled; write=1.428 s, sync=0.019 s, total=1.491 s; sync files=11, longest=0.009 s, average=0.002 s; distance=68 kB, estimate=68 kB
^C
giuseppe@ai-srv:~/ai-station$

View File

@ -1,5 +1,3 @@
version: '3.8'
services: services:
chainlit-app: chainlit-app:
build: . build: .
@ -12,7 +10,7 @@ services:
- DATABASE_URL=postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station - DATABASE_URL=postgresql+asyncpg://ai_user:secure_password_here@postgres:5432/ai_station
- OLLAMA_URL=http://192.168.1.243:11434 - OLLAMA_URL=http://192.168.1.243:11434
- QDRANT_URL=http://qdrant:6333 - QDRANT_URL=http://qdrant:6333
- BGE_API_URL=http://192.168.1.243:8001 - BGE_API_URL=http://192.168.1.243:8001/embed
volumes: volumes:
- ./workspaces:/app/workspaces - ./workspaces:/app/workspaces
- ./public:/app/public # ⬅️ VERIFICA QUESTO - ./public:/app/public # ⬅️ VERIFICA QUESTO

133
error.log
View File

@ -1,133 +0,0 @@
qdrant-1 | _ _
chainlit-app-1 | 2025-12-25 18:05:12 - INFO - chainlit - Your app is available at http://0.0.0.0:8000
postgres-1 |
postgres-1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres-1 |
postgres-1 | 2025-12-25 16:38:01.071 UTC [1] LOG: starting PostgreSQL 18.1 (Debian 18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
qdrant-1 | __ _ __| |_ __ __ _ _ __ | |_
qdrant-1 | / _` |/ _` | '__/ _` | '_ \| __|
qdrant-1 | | (_| | (_| | | | (_| | | | | |_
qdrant-1 | \__, |\__,_|_| \__,_|_| |_|\__|
postgres-1 | 2025-12-25 16:38:01.072 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres-1 | 2025-12-25 16:38:01.072 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres-1 | 2025-12-25 16:38:01.093 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres-1 | 2025-12-25 16:38:01.126 UTC [32] LOG: database system was shut down at 2025-12-25 14:34:55 UTC
postgres-1 | 2025-12-25 16:38:01.155 UTC [1] LOG: database system is ready to accept connections
qdrant-1 | |_|
qdrant-1 |
qdrant-1 | Access web UI at https://ui.qdrant.tech/?v=v1.0.0
qdrant-1 |
qdrant-1 | [2025-12-25T16:38:00.816Z INFO storage::content_manager::consensus::persistent] Initializing new raft state at ./storage/raft_state
qdrant-1 | [2025-12-25T16:38:00.861Z INFO qdrant] Distributed mode disabled
qdrant-1 | [2025-12-25T16:38:00.861Z INFO qdrant] Telemetry reporting enabled, id: e6113e43-627c-471d-8374-0f1b61799d76
qdrant-1 | [2025-12-25T16:38:00.872Z INFO qdrant::tonic] Qdrant gRPC listening on 6334
qdrant-1 | [2025-12-25T16:38:00.890Z INFO actix_server::builder] Starting 3 workers
qdrant-1 | [2025-12-25T16:38:00.890Z INFO actix_server::server] Actix runtime found; starting in Actix runtime
qdrant-1 | [2025-12-25T16:39:02.504Z INFO actix_server::server] SIGTERM received; starting graceful shutdown
qdrant-1 | [2025-12-25T16:39:02.505Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | [2025-12-25T16:39:02.508Z INFO actix_server::accept] Accept thread stopped
qdrant-1 | [2025-12-25T16:39:02.508Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | [2025-12-25T16:39:02.508Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | _ _
qdrant-1 | __ _ __| |_ __ __ _ _ __ | |_
qdrant-1 | / _` |/ _` | '__/ _` | '_ \| __|
qdrant-1 | | (_| | (_| | | | (_| | | | | |_
postgres-1 | 2025-12-25 16:39:02.495 UTC [1] LOG: received fast shutdown request
postgres-1 | 2025-12-25 16:39:02.505 UTC [1] LOG: aborting any active transactions
postgres-1 | 2025-12-25 16:39:02.521 UTC [1] LOG: background worker "logical replication launcher" (PID 35) exited with exit code 1
postgres-1 | 2025-12-25 16:39:02.521 UTC [30] LOG: shutting down
postgres-1 | 2025-12-25 16:39:02.533 UTC [30] LOG: checkpoint starting: shutdown immediate
chainlit-app-1 | 2025-12-25 18:05:25 - INFO - httpx - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
chainlit-app-1 | /app/app.py:43: UserWarning: Qdrant client version 1.16.2 is incompatible with server version 1.0.0. Major versions should match and minor version difference must not exceed 1. Set check_compatibility=False to skip version check.
chainlit-app-1 | return QdrantClient(url=QDRANT_URL)
qdrant-1 | \__, |\__,_|_| \__,_|_| |_|\__|
chainlit-app-1 | 2025-12-25 18:05:25 - INFO - httpx - HTTP Request: GET http://qdrant:6333/collections/documents "HTTP/1.1 404 Not Found"
chainlit-app-1 | 2025-12-25 18:06:08 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:06:10 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
qdrant-1 | |_|
qdrant-1 |
qdrant-1 | Access web UI at https://ui.qdrant.tech/?v=v1.0.0
qdrant-1 |
qdrant-1 | [2025-12-25T16:43:53.592Z INFO storage::content_manager::consensus::persistent] Loading raft state from ./storage/raft_state
qdrant-1 | [2025-12-25T16:43:53.612Z INFO qdrant] Distributed mode disabled
postgres-1 | 2025-12-25 16:39:02.601 UTC [30] LOG: checkpoint complete: wrote 0 buffers (0.0%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.019 s, sync=0.009 s, total=0.079 s; sync files=2, longest=0.005 s, average=0.005 s; distance=0 kB, estimate=0 kB; lsn=0/1BEF980, redo lsn=0/1BEF980
postgres-1 | 2025-12-25 16:39:02.644 UTC [1] LOG: database system is shut down
postgres-1 |
postgres-1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres-1 |
postgres-1 | 2025-12-25 16:43:53.946 UTC [1] LOG: starting PostgreSQL 18.1 (Debian 18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
postgres-1 | 2025-12-25 16:43:53.947 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres-1 | 2025-12-25 16:43:53.947 UTC [1] LOG: listening on IPv6 address "::", port 5432
qdrant-1 | [2025-12-25T16:43:53.612Z INFO qdrant] Telemetry reporting enabled, id: 2a83356a-9770-47d3-a0bd-638f75769522
qdrant-1 | [2025-12-25T16:43:53.615Z INFO qdrant::tonic] Qdrant gRPC listening on 6334
qdrant-1 | [2025-12-25T16:43:53.616Z INFO actix_server::builder] Starting 3 workers
postgres-1 | 2025-12-25 16:43:53.965 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres-1 | 2025-12-25 16:43:53.990 UTC [32] LOG: database system was shut down at 2025-12-25 16:39:02 UTC
postgres-1 | 2025-12-25 16:43:54.013 UTC [1] LOG: database system is ready to accept connections
postgres-1 | 2025-12-25 16:48:54.089 UTC [30] LOG: checkpoint starting: time
postgres-1 | 2025-12-25 16:48:54.175 UTC [30] LOG: checkpoint complete: wrote 0 buffers (0.0%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.036 s, sync=0.009 s, total=0.088 s; sync files=2, longest=0.005 s, average=0.005 s; distance=0 kB, estimate=0 kB; lsn=0/1BEFA88, redo lsn=0/1BEFA30
postgres-1 | 2025-12-25 16:56:42.002 UTC [1] LOG: received fast shutdown request
postgres-1 | 2025-12-25 16:56:42.018 UTC [1] LOG: aborting any active transactions
postgres-1 | 2025-12-25 16:56:42.026 UTC [1] LOG: background worker "logical replication launcher" (PID 35) exited with exit code 1
postgres-1 | 2025-12-25 16:56:42.030 UTC [30] LOG: shutting down
postgres-1 | 2025-12-25 16:56:42.039 UTC [30] LOG: checkpoint starting: shutdown immediate
postgres-1 | 2025-12-25 16:56:42.086 UTC [30] LOG: checkpoint complete: wrote 0 buffers (0.0%), wrote 0 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.004 s, sync=0.001 s, total=0.057 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=0 kB; lsn=0/1BEFB38, redo lsn=0/1BEFB38
postgres-1 | 2025-12-25 16:56:42.131 UTC [1] LOG: database system is shut down
postgres-1 |
postgres-1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres-1 |
chainlit-app-1 | 2025-12-25 18:06:10 - WARNING - chainlit - Translated markdown file for it-IT not found. Defaulting to chainlit.md.
chainlit-app-1 | 2025-12-25 18:06:13 - INFO - chainlit - Missing custom logo. Falling back to default logo.
chainlit-app-1 | 2025-12-25 18:06:21 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:06:21 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
qdrant-1 | [2025-12-25T16:43:53.617Z INFO actix_server::server] Actix runtime found; starting in Actix runtime
qdrant-1 | [2025-12-25T16:56:42.005Z INFO actix_server::server] SIGTERM received; starting graceful shutdown
qdrant-1 | [2025-12-25T16:56:42.006Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | [2025-12-25T16:56:42.006Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | [2025-12-25T16:56:42.007Z INFO actix_server::worker] Shutting down idle worker
qdrant-1 | [2025-12-25T16:56:42.007Z INFO actix_server::accept] Accept thread stopped
qdrant-1 | _ _
qdrant-1 | __ _ __| |_ __ __ _ _ __ | |_
qdrant-1 | / _` |/ _` | '__/ _` | '_ \| __|
qdrant-1 | | (_| | (_| | | | (_| | | | | |_
qdrant-1 | \__, |\__,_|_| \__,_|_| |_|\__|
qdrant-1 | |_|
qdrant-1 |
qdrant-1 | Access web UI at https://ui.qdrant.tech/?v=v1.0.0
qdrant-1 |
qdrant-1 | [2025-12-25T16:56:52.790Z INFO storage::content_manager::consensus::persistent] Loading raft state from ./storage/raft_state
qdrant-1 | [2025-12-25T16:56:52.796Z INFO qdrant] Distributed mode disabled
qdrant-1 | [2025-12-25T16:56:52.796Z INFO qdrant] Telemetry reporting enabled, id: f821b8ea-9ee5-497e-a172-dfebf253f7b1
qdrant-1 | [2025-12-25T16:56:52.797Z INFO qdrant::tonic] Qdrant gRPC listening on 6334
qdrant-1 | [2025-12-25T16:56:52.798Z INFO actix_server::builder] Starting 3 workers
qdrant-1 | [2025-12-25T16:56:52.798Z INFO actix_server::server] Actix runtime found; starting in Actix runtime
qdrant-1 | [2025-12-25T18:05:25.183Z INFO actix_web::middleware::logger] 172.18.0.4 "GET /collections/documents HTTP/1.1" 404 110 "-" "python-client/1.16.2 python/3.10.19" 0.007704
qdrant-1 | [2025-12-25T18:05:30.499Z INFO actix_web::middleware::logger] 172.18.0.4 "PUT /collections/documents HTTP/1.1" 200 71 "-" "python-client/1.16.2 python/3.10.19" 5.311157
qdrant-1 | [2025-12-25T18:06:22.662Z INFO actix_web::middleware::logger] 172.18.0.4 "GET /collections/documents HTTP/1.1" 200 413 "-" "python-client/1.16.2 python/3.10.19" 0.005606
postgres-1 | 2025-12-25 16:56:43.530 UTC [1] LOG: starting PostgreSQL 18.1 (Debian 18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
postgres-1 | 2025-12-25 16:56:43.532 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres-1 | 2025-12-25 16:56:43.532 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres-1 | 2025-12-25 16:56:43.552 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres-1 | 2025-12-25 16:56:43.585 UTC [32] LOG: database system was shut down at 2025-12-25 16:56:42 UTC
postgres-1 | 2025-12-25 16:56:43.616 UTC [1] LOG: database system is ready to accept connections
postgres-1 | 2025-12-25 17:01:43.645 UTC [30] LOG: checkpoint starting: time
postgres-1 | 2025-12-25 17:01:43.712 UTC [30] LOG: checkpoint complete: wrote 0 buffers (0.0%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.019 s, sync=0.009 s, total=0.068 s; sync files=2, longest=0.005 s, average=0.005 s; distance=0 kB, estimate=0 kB; lsn=0/1BEFC40, redo lsn=0/1BEFBE8
chainlit-app-1 | 2025-12-25 18:06:21 - WARNING - chainlit - Translated markdown file for it-IT not found. Defaulting to chainlit.md.
chainlit-app-1 | 2025-12-25 18:06:22 - INFO - httpx - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:06:22 - INFO - httpx - HTTP Request: GET http://qdrant:6333/collections/documents "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:06:28 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:06:36 - INFO - httpx - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:06:37 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/embed "HTTP/1.1 500 Internal Server Error"
chainlit-app-1 | 2025-12-25 18:06:39 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/chat "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:06:48 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:07:02 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:07:16 - INFO - httpx - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:07:22 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/embed "HTTP/1.1 500 Internal Server Error"
chainlit-app-1 | 2025-12-25 18:07:22 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/chat "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:07:49 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:07:54 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:08:15 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/chat "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:08:30 - WARNING - chainlit - Translation file for it-IT not found. Using parent translation it.
chainlit-app-1 | 2025-12-25 18:08:57 - INFO - httpx - HTTP Request: GET http://qdrant:6333 "HTTP/1.1 200 OK"
chainlit-app-1 | 2025-12-25 18:09:03 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/embed "HTTP/1.1 500 Internal Server Error"
chainlit-app-1 | 2025-12-25 18:09:03 - INFO - httpx - HTTP Request: POST http://192.168.1.243:11434/api/chat "HTTP/1.1 200 OK"

View File

@ -1,36 +1,12 @@
/* dFm AI Station - Perplexity Clean Style */ .user-badge {
:root { background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
--bg-color: #0B0F1A; padding: 4px 12px;
--card-color: #161B2C; border-radius: 12px;
--accent-color: #6366F1; font-size: 0.85em;
} }
body { background-color: var(--bg-color) !important; color: #F1F5F9 !important; } /* Evidenzia codice */
.message pre {
/* Header e Logo */ background: #2d2d2d;
header { border-left: 4px solid #0066CC;
background: rgba(11, 15, 26, 0.8) !important;
backdrop-filter: blur(8px);
border-bottom: 1px solid #23293F !important;
}
/* Sidebar */
.MuiDrawer-paper {
background-color: var(--bg-color) !important;
border-right: 1px solid #23293F !important;
}
/* Messaggi */
div[class*="user"] {
background: #1E253A !important;
border-radius: 12px !important;
border-left: 4px solid var(--accent-color) !important;
}
/* Input Area */
form {
background: var(--card-color) !important;
border-radius: 20px !important;
border: 1px solid #334155 !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.5) !important;
} }

View File

@ -27,3 +27,6 @@ sniffio
aiohttp aiohttp
boto3>=1.28.0 boto3>=1.28.0
azure-storage-file-datalake>=12.14.0 azure-storage-file-datalake>=12.14.0
docling
pillow
requests

23
script/test_vision.py Normal file
View File

@ -0,0 +1,23 @@
import httpx
import base64
import sys
# Configurazione
OLLAMA_URL = "http://192.168.1.243:11434"
MODEL = "minicpm-v"
print(f"👁️ Test Visione su {OLLAMA_URL} con modello {MODEL}...")
# 1. Controlla se il modello è caricato
try:
r = httpx.get(f"{OLLAMA_URL}/api/tags")
models = [m['name'] for m in r.json()['models']]
if MODEL not in str(models):
print(f"❌ Errore: Il modello {MODEL} non è stato trovato su Ollama!")
sys.exit(1)
print(f"✅ Modello {MODEL} trovato.")
except Exception as e:
print(f"❌ Errore connessione Ollama: {e}")
sys.exit(1)
print("🚀 Tutto pronto per l'implementazione!")