feat: playbook tools, RAG, vision, voice, chat controls, faster boot

- Tool-using playbooks: read-only tool registry (search_memory,
  search_history, search_documents, list_models, get_time), per-playbook
  allowlist, agentic loop, and a "running tool" status indicator.
- Document ingest / RAG: documents table + chunker + embed/cosine retrieval
  reusing the existing stack; Documents page (upload/paste, viewer, delete);
  top-k chunks injected into the system prompt.
- Vision chat: attach images, base64 into /api/chat.
- Voice I/O: Web Speech dictation + read-aloud (browser-native, no backend).
- Chat controls: stop, regenerate, edit-and-resend; num_ctx knob in Settings.
- Per-playbook model override.
- History polish: per-conversation + bulk ShareGPT export.
- Faster ncp start: UI up first, Ollama warms in the background, with a
  per-phase timing readout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jon
2026-07-23 13:31:48 -05:00
co-authored by Claude Opus 4.8
parent f514d3dbe5
commit f509034fdd
15 changed files with 1061 additions and 96 deletions
+22 -9
View File
@@ -301,20 +301,23 @@ def stop_service(svc: Service) -> bool:
# -- ollama --------------------------------------------------------------------
def start_ollama() -> None:
def start_ollama(background: bool = False) -> None:
"""Driven through the backend endpoint (the path the control panel uses)
rather than launching the binary, because OllamaManager owns model and GPU
selection. Requires the backend to be up.
Long timeout: the endpoint blocks until the model is warmed - weights read
off disk into RAM/VRAM - which the web UI's own "Loading model..." button
state calls out as routinely taking about a minute, not just the Ollama
process launching."""
background=True (`ncp start`): the endpoint returns as soon as `ollama serve`
is up and warms the model in a background task, so boot finishes in seconds
and the model loads concurrently into the first chat.
background=False (`ncp start --ai`): blocks until the model is warmed - weights
read off disk into RAM/VRAM, routinely about a minute - so "started" means the
AI can actually answer."""
print("Starting OLLAMA...")
req = urllib.request.Request("http://localhost:8000/ollama/start", method="POST")
url = "http://localhost:8000/ollama/start" + ("?background=true" if background else "")
req = urllib.request.Request(url, method="POST")
try:
urllib.request.urlopen(req, timeout=180).read(1)
print("NEXUS OLLAMA STARTED")
print("NEXUS OLLAMA WARMING (background)" if background else "NEXUS OLLAMA STARTED")
except Exception as e:
print(f" OLLAMA start failed: {e}")
@@ -350,14 +353,24 @@ def cmd_start(target) -> None:
elif target in ("--ai", "-a"):
start_ollama()
elif target in (None, "", "all"):
# Memory + backend in parallel, both ready before the frontend starts.
# Bring the UI up first, then warm Ollama in the background — the model
# loads concurrently and into the first chat instead of blocking boot.
t0 = time.perf_counter()
launch(SERVICES["memory"])
launch(SERVICES["backend"])
wait_for_port(SERVICES["memory"])
wait_for_port(SERVICES["backend"])
start_ollama()
t_services = time.perf_counter()
launch(SERVICES["frontend"])
wait_for_port(SERVICES["frontend"])
t_frontend = time.perf_counter()
start_ollama(background=True)
t_ollama = time.perf_counter()
print("\nBoot timing:")
print(f" services (memory+backend) : {t_services - t0:5.1f}s")
print(f" frontend (UI ready) : {t_frontend - t_services:5.1f}s")
print(f" ollama kickoff (bg warm) : {t_ollama - t_frontend:5.1f}s")
print(f" total to interactive : {t_ollama - t0:5.1f}s")
else:
show_help()