mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
Keep Ollama chat streams alive
This commit is contained in:
1 parent
e9c83e3a0d
commit
cdbb5f03fa
2 files changed
+51
-15
No files matched your search
+43
-13
@@ -8,8 +8,10 @@ Flux :
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from contextlib import suppress
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
@@ -58,14 +60,43 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
tools_meta: list[dict] = []
|
||||
error_message = ""
|
||||
|
||||
queue: asyncio.Queue[dict | None] = asyncio.Queue()
|
||||
|
||||
async def produce_events() -> None:
|
||||
try:
|
||||
async for event in run_agent(
|
||||
model,
|
||||
convo,
|
||||
options=agent_config.ollama_options(cfg),
|
||||
enabled_tools=agent_config.enabled_tool_names(cfg),
|
||||
confirm_shell=cfg.get("confirm_shell", True),
|
||||
):
|
||||
await queue.put(event)
|
||||
except Exception as exc:
|
||||
logger.exception("Échec inattendu du flux de chat")
|
||||
await queue.put(
|
||||
{
|
||||
"type": "error",
|
||||
"message": f"Erreur interne du chat : {exc}",
|
||||
}
|
||||
)
|
||||
finally:
|
||||
await queue.put(None)
|
||||
|
||||
producer = asyncio.create_task(produce_events())
|
||||
try:
|
||||
async for ev in run_agent(
|
||||
model,
|
||||
convo,
|
||||
options=agent_config.ollama_options(cfg),
|
||||
enabled_tools=agent_config.enabled_tool_names(cfg),
|
||||
confirm_shell=cfg.get("confirm_shell", True),
|
||||
):
|
||||
while True:
|
||||
try:
|
||||
ev = await asyncio.wait_for(queue.get(), timeout=15.0)
|
||||
except TimeoutError:
|
||||
# Empêche OpenResty/Nginx/Cloudflare de fermer le SSE pendant
|
||||
# le chargement parfois long d'un modèle Ollama.
|
||||
yield _sse("ping", {"status": "waiting"})
|
||||
continue
|
||||
|
||||
if ev is None:
|
||||
break
|
||||
|
||||
etype = ev.pop("type")
|
||||
if etype in (
|
||||
"token",
|
||||
@@ -82,12 +113,11 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
elif etype == "final":
|
||||
final_content = ev["content"]
|
||||
tools_meta = ev["tools"]
|
||||
except Exception as exc:
|
||||
# Après l'envoi des en-têtes SSE, une exception non gérée coupe le
|
||||
# socket sans explication et laisse l'interface bloquée.
|
||||
logger.exception("Échec inattendu du flux de chat")
|
||||
error_message = f"Erreur interne du chat : {exc}"
|
||||
yield _sse("error", {"message": error_message})
|
||||
finally:
|
||||
if not producer.done():
|
||||
producer.cancel()
|
||||
with suppress(asyncio.CancelledError):
|
||||
await producer
|
||||
|
||||
if final_content or tools_meta:
|
||||
db.add_message(
|
||||
|
||||
@@ -190,8 +190,11 @@ export async function streamChat(
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
} catch (err) {
|
||||
const raw = err instanceof Error ? err.message : "serveur Loki injoignable";
|
||||
handlers.onError(
|
||||
err instanceof Error ? err.message : "serveur Loki injoignable"
|
||||
/network error|failed to fetch/i.test(raw)
|
||||
? "Serveur Loki injoignable. Vérifiez le reverse proxy et le conteneur."
|
||||
: raw
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -271,8 +274,11 @@ export async function streamChat(
|
||||
} catch (err) {
|
||||
if (!failed) {
|
||||
failed = true;
|
||||
const raw = err instanceof Error ? err.message : "connexion interrompue";
|
||||
handlers.onError(
|
||||
err instanceof Error ? err.message : "connexion au chat interrompue"
|
||||
/network error|failed to fetch/i.test(raw)
|
||||
? "Connexion interrompue pendant le chargement du modèle. Vérifiez OLLAMA_HOST et le délai du reverse proxy."
|
||||
: raw
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user