localailb/assistant
0
1"""
2LocalAiLab Assistant โ by LocalAiLab
3smolagents + ChromaDB + Dynamic Hardware Support + Svelte Web UI
4=======================================================================
5Tabs:
6 ๐ฌ General Chat โ direct LLM conversation, no retrieval
7 ๐ RAG Chat โ retrieves from ChromaDB before answering
8 ๐ฌ Deep Research โ iterative multi-agent web research
9 ๐ผ๏ธ Vision Chat โ image upload, answers from the image only (no KB)
10 ๐๏ธ Speech to Text โ transcribe audio to text using Whisper
11 ๐ Data Analysis โ AI agent explores CSV/Excel, builds charts + report
12 ๐ Knowledge Base โ index / manage documents
13 โ๏ธ Settings โ system & model configuration
14 โน๏ธ About โ bilingual system info
15"""
16
17import os
18import socket
19import threading
20import warnings
21import webbrowser
22from pathlib import Path
23
24os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1")
25os.environ.setdefault("DO_NOT_TRACK", "1")
26# Force the non-GUI matplotlib backend app-wide (see run_webview.py โ the
27# tkinter splash must never collide with matplotlib's TkAgg auto-select in
28# the Data worker thread; `Tcl_AsyncDelete` would abort the process).
29os.environ.setdefault("MPLBACKEND", "Agg")
30
31try:
32 from dotenv import load_dotenv
33 from backend.paths import user_data_dir
34
35 user_env = str(user_data_dir() / ".env")
36 if os.path.exists(user_env):
37 load_dotenv(user_env)
38 else:
39 load_dotenv()
40except ImportError:
41 pass
42
43from backend import web_api
44
45APP_ICON = Path(__file__).parent / "ui" / "image" / "logo_round.png"
46
47
48def _find_free_port(start=7861):
49 for port in range(start, start + 20):
50 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
51 if s.connect_ex(("127.0.0.1", port)) != 0:
52 return port
53 return start
54
55
56warnings.filterwarnings("ignore", category=UserWarning, module="torch")
57
58if __name__ == "__main__":
59 import multiprocessing
60
61 multiprocessing.freeze_support()
62
63 # Shared Windows stdio hardening: replaces None OR broken-handle
64 # streams (a GUI-launched process can have a sys.stdout whose OS handle
65 # is invalid โ print() then raises OSError [WinError 6] and kills the
66 # run) and forces UTF-8 + errors=replace on the rest.
67 from backend.stdio_hardening import harden_stdio, harden_subprocesses
68 from backend.metadata_hardening import harden_metadata
69 harden_stdio()
70 harden_subprocesses()
71 harden_metadata()
72
73 port = _find_free_port()
74 # Open browser on startup
75 threading.Timer(0.8, lambda: webbrowser.open(f"http://127.0.0.1:{port}")).start()
76 web_api.run(host="127.0.0.1", port=port, log_level="info")
77 