miglioramento UI e RAG
This commit is contained in:
parent
7095e83b41
commit
939a3d11a7
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 487 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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 dell’utente 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 l’utente
|
||||||
|
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)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||||
|
|
|
||||||
621
app.py
621
app.py
|
|
@ -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]
|
chunk_size = 2000
|
||||||
return response.get('embedding', [])
|
overlap = 200
|
||||||
except: return []
|
|
||||||
|
points = []
|
||||||
async def index_document(file_name: str, content: str, collection_name: str) -> bool:
|
for i in range(0, len(content), chunk_size - overlap):
|
||||||
try:
|
chunk = content[i : i + chunk_size]
|
||||||
await ensure_collection(collection_name)
|
embedding_data = get_bge_embeddings(chunk)
|
||||||
embedding = await get_embeddings(content)
|
|
||||||
if not embedding: return False
|
|
||||||
|
|
||||||
qdrant = await get_qdrant_client()
|
if embedding_data:
|
||||||
await qdrant.upsert(
|
points.append(PointStruct(
|
||||||
collection_name=collection_name,
|
|
||||||
points=[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()
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
if points:
|
||||||
|
await client.upsert(collection_name=collection_name, points=points)
|
||||||
|
return len(points)
|
||||||
|
return 0
|
||||||
|
|
||||||
async def search_qdrant(query: str, collection: str) -> str:
|
async def search_hybrid(query: str, collection_name: str, limit: int = 4) -> str:
|
||||||
try:
|
client = AsyncQdrantClient(url=QDRANT_URL)
|
||||||
client = await get_qdrant_client()
|
if not await client.collection_exists(collection_name): return ""
|
||||||
if not await client.collection_exists(collection): return ""
|
|
||||||
emb = await get_embeddings(query)
|
query_emb = get_bge_embeddings(query)
|
||||||
if not emb: return ""
|
if not query_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])
|
# CORREZIONE QUI: Usiamo l'oggetto Prefetch importato correttamente
|
||||||
except: return ""
|
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;">
|
||||||
await cl.Message(
|
👤 <b>{profile['name']}</b> | 🔧 {profile['role'].upper()} | 📁 {profile['workspace']}
|
||||||
content=f"👋 Ciao **{profile['name']}**!\n"
|
</div>
|
||||||
f"Ruolo: `{profile['role']}` | Workspace: `{profile['workspace']}`\n"
|
"""
|
||||||
|
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()
|
).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 dell’utente 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 l’utente
|
||||||
|
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")
|
profile = cl.user_session.get("profile")
|
||||||
show_code = cl.user_session.get("show_code")
|
|
||||||
|
|
||||||
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 "image" in element.mime:
|
||||||
if text:
|
images_for_vision.append(file_path)
|
||||||
await index_document(element.name, text, rag_collection)
|
msg_img = cl.Message(
|
||||||
await cl.Message(content=f"✅ **{element.name}** indicizzato.").send()
|
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."
|
||||||
|
)
|
||||||
|
|
||||||
# 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="")
|
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}],
|
# 3. GENERAZIONE
|
||||||
options={"temperature": temperature},
|
try:
|
||||||
stream=True
|
client_async = ollama.AsyncClient(host=OLLAMA_URL)
|
||||||
)
|
stream = await client_async.chat(
|
||||||
|
model=selected_model,
|
||||||
full_resp = ""
|
messages=[
|
||||||
async for chunk in stream:
|
{"role": "system", "content": system_prompt},
|
||||||
token = chunk['message']['content']
|
{
|
||||||
full_resp += token
|
"role": "user",
|
||||||
await msg.stream_token(token)
|
"content": f"Domanda: {message.content}\n\n{final_context}",
|
||||||
await msg.update()
|
},
|
||||||
|
],
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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$
|
||||||
|
|
@ -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
133
error.log
|
|
@ -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"
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,7 @@ aiofiles>=23.0.0
|
||||||
sniffio
|
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
|
||||||
|
|
|
||||||
|
|
@ -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!")
|
||||||
Loading…
Reference in New Issue