perf: fix model reload thrash and cut time-to-first-token

Ollama reloaded the chat model mid-message because plan/summary/router
calls sent divergent runner options (no num_ctx/num_batch) and omitted
keep_alive — the main cause of perceived slowness.

- share one keep-alive httpx.AsyncClient for all Ollama calls
- unify runner options (runner_options) + keep_alive on every model
  call, embeddings included
- drop the blocking LLM routing fallback (pure lexical heuristic)
- run RAG recall + plan + code-model pick in parallel inside the SSE
  stream, after the start event
- RAG cosine scoring off the event loop; cache /api/tags 30s and
  nvidia-smi 5s; frontend polls 2s->5s, warm poll backoff, dedup
  config fetch

feat: working session menu in TopBar (switch/create/rename/delete)
feat: workspace file deletion (DELETE /api/files + UI trash buttons)
docs: recommended Ollama env vars (KEEP_ALIVE, MAX_LOADED_MODELS...)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 4.8 committed 2026-07-18 13:58:18 +02:00
1 parent 552548e4b4
commit cb2872c78a
23 files changed
+672 -224

No files matched your search

+16 -2
View File
@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { downloadFile, type FileNode } from "../api/client";
import { DownloadIcon } from "../components/Icon";
import { DownloadIcon, TrashIcon } from "../components/Icon";
import { useStore } from "../store/useStore";
export function HistoryView({ onOpen }: { onOpen: () => void }) {
@@ -61,7 +61,14 @@ export function FilesView() {
}
function WorkspaceTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
const { openPreview, previewPath } = useStore();
const { openPreview, previewPath, removeFile } = useStore();
const confirmDelete = (node: FileNode) => {
const msg =
node.type === "dir"
? `Supprimer le dossier ${node.path} et tout son contenu ?`
: `Supprimer ${node.path} ?`;
if (window.confirm(msg)) void removeFile(node.path);
};
return (
<>
{nodes.map((node) => (
@@ -93,6 +100,13 @@ function WorkspaceTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
<DownloadIcon size={12} /> Télécharger
</button>
)}
<button
onClick={() => confirmDelete(node)}
className="flex items-center gap-1 border-2 border-line bg-base px-2 py-1 text-[11px] text-warn"
title={`Supprimer ${node.name}`}
>
<TrashIcon size={12} /> Supprimer
</button>
</div>
{node.children && <WorkspaceTree nodes={node.children} depth={depth + 1} />}
</div>