mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-18 20:57:05 +02:00
Theme neo-brutaliste + panneau raisonnement + repli sans outils
UI : reskin complet vers un theme neo-brutaliste clair (bordures noires 3px, ombres dures, accent orange, polices Press Start 2P + DM Mono). - tailwind.config / index.css / index.html : palette, polices, ombres, scrollbars - TopBar, ActivityBar, LeftPanel, ChatPanel, PreviewPanel, SettingsView, ToolCard, ModelSelector, Icon : restyle complet Chat : panneau « Raisonnement » repliable et redimensionnable affichant le thinking de l'agent en direct (diffuse depuis le backend, persiste dans meta). Robustesse Ollama : les echecs de generation de parseur d'outils (« Unable to generate parser for this template ») declenchent desormais le repli automatique en conversation simple sans outils, au lieu d'un 400. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
e8e6a1446c
commit
b4fc8cee3d
16 files changed
+488
-419
No files matched your search
+21
-2
@@ -26,9 +26,22 @@ MAX_TOOL_REPAIR_ATTEMPTS = 2
|
||||
|
||||
|
||||
def _tools_not_supported(exc: OllamaError) -> bool:
|
||||
"""Détecte l'erreur Ollama renvoyée par un modèle sans function calling."""
|
||||
"""Détecte un modèle incapable de function calling.
|
||||
|
||||
Deux cas :
|
||||
- Ollama refuse explicitement (« does not support tools ») ;
|
||||
- Ollama n'arrive pas à dériver un parseur d'appels d'outils du template
|
||||
du modèle (« Unable to generate parser for this template ») — fréquent
|
||||
sur des modèles exotiques dont le template Jinja lève une exception.
|
||||
Dans les deux cas, on retombe sur une conversation simple, sans outils.
|
||||
"""
|
||||
message = str(exc).lower()
|
||||
return "does not support tools" in message or "does not support tool" in message
|
||||
return (
|
||||
"does not support tools" in message
|
||||
or "does not support tool" in message
|
||||
or "unable to generate parser for this template" in message
|
||||
or "automatic parser generation failed" in message
|
||||
)
|
||||
|
||||
|
||||
def _invalid_tool_arguments(exc: OllamaError) -> bool:
|
||||
@@ -70,6 +83,7 @@ async def run_agent(
|
||||
tools = None
|
||||
collected: list[dict] = []
|
||||
text_parts: list[str] = []
|
||||
thinking_parts: list[str] = []
|
||||
active_tools = tools
|
||||
tool_fallback_used = False
|
||||
tool_repair_attempts = 0
|
||||
@@ -107,6 +121,9 @@ async def run_agent(
|
||||
thinking = msg.get("thinking", "")
|
||||
if thinking:
|
||||
thinking_buf += thinking
|
||||
# Diffuse le raisonnement en direct pour l'afficher
|
||||
# dans le panneau repliable du chat.
|
||||
yield {"type": "thinking", "content": thinking}
|
||||
if not thinking_status_sent:
|
||||
thinking_status_sent = True
|
||||
yield {"type": "status", "message": "Réflexion…"}
|
||||
@@ -179,6 +196,7 @@ async def run_agent(
|
||||
assistant_turn: dict = {"role": "assistant", "content": content_buf}
|
||||
if thinking_buf:
|
||||
assistant_turn["thinking"] = thinking_buf
|
||||
thinking_parts.append(thinking_buf)
|
||||
if tool_calls:
|
||||
assistant_turn["tool_calls"] = tool_calls
|
||||
convo.append(assistant_turn)
|
||||
@@ -300,4 +318,5 @@ async def run_agent(
|
||||
"content": final_content,
|
||||
"tools": collected,
|
||||
"stats": final_stats,
|
||||
"thinking": "\n\n".join(thinking_parts).strip(),
|
||||
}
|
||||
@@ -59,6 +59,7 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
final_content = ""
|
||||
tools_meta: list[dict] = []
|
||||
stats_meta: dict | None = None
|
||||
thinking_meta = ""
|
||||
error_message = ""
|
||||
|
||||
queue: asyncio.Queue[dict | None] = asyncio.Queue()
|
||||
@@ -101,6 +102,7 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
etype = ev.pop("type")
|
||||
if etype in (
|
||||
"token",
|
||||
"thinking",
|
||||
"status",
|
||||
"notice",
|
||||
"tool_call",
|
||||
@@ -115,6 +117,7 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
final_content = ev["content"]
|
||||
tools_meta = ev["tools"]
|
||||
stats_meta = ev.get("stats")
|
||||
thinking_meta = ev.get("thinking", "")
|
||||
finally:
|
||||
if not producer.done():
|
||||
producer.cancel()
|
||||
@@ -127,6 +130,8 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
meta["tools"] = tools_meta
|
||||
if stats_meta:
|
||||
meta["stats"] = stats_meta
|
||||
if thinking_meta:
|
||||
meta["thinking"] = thinking_meta
|
||||
db.add_message(
|
||||
req.session_id,
|
||||
"assistant",
|
||||
|
||||
Reference in new issue
Block a user