diff --git a/internal/loki/chat_conversation.go b/internal/loki/chat_conversation.go index ca8bc5e..180df49 100644 --- a/internal/loki/chat_conversation.go +++ b/internal/loki/chat_conversation.go @@ -574,9 +574,17 @@ func (c *Conversation) generate(ctx context.Context, caps Caps, temperature floa // Contexte du projet actif (description, index mémoire, index des trackers), // même traitement : injecté dans la vue envoyée, jamais persisté — voir // projectSystemMessages. - final := append(projectSystemMessages(), msgs...) - if sp := readSysPrompt(); sp != "" { - final = append([]Message{{Role: "system", Content: sp}}, final...) + // + // Mode agent OFF = modèle BRUT (AJEAN 0.13.13) : ni l'un ni l'autre. Sans + // agent, on veut parler au modèle nu, comme à un llama-server direct. Le + // prompt du preset et l'index mémoire décrivent des outils que ce mode n'a + // pas : le modèle se mettait à écrire des en clair. + final := msgs + if caps.Agent { + final = append(projectSystemMessages(), msgs...) + if sp := readSysPrompt(); sp != "" { + final = append([]Message{{Role: "system", Content: sp}}, final...) + } } // newBase : vue modèle publiée par une compaction survenue PENDANT le tour. diff --git a/internal/loki/llm_client.go b/internal/loki/llm_client.go index 40e6796..06b8bcc 100644 --- a/internal/loki/llm_client.go +++ b/internal/loki/llm_client.go @@ -1094,7 +1094,12 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps if ch.FinishReason != "" { finishReason = ch.FinishReason } - if len(ch.Delta.ToolCalls) > 0 { + // Un tool_call n'est retenu que si des outils ont VRAIMENT été annoncés ce + // tour-ci (même condition que le payload). Sinon — mode agent coupé, ou + // relance sans outils après un 500 — le moteur peut quand même parser un + // appel que le modèle a émis de lui-même ; l'exécuter donnait « outil + // inconnu », réinjecté puis mis en boucle. On l'ignore (AJEAN 0.13.12). + if len(tools) > 0 && !disableTools && len(ch.Delta.ToolCalls) > 0 { // Un appel d'outil clôt le texte : on vide MAINTENANT le reliquat // retenu par la garde « » (voir plus bas). Sinon il n'était // émis qu'en fin de flux, donc APRÈS l'événement d'outil, et l'UI