mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 15:08:16 +02:00
Raisonnement : NUDGE_IN_TOOL décide sur le verdict de la forme de la requête
La sonde de gabarit gardait le verdict de la DERNIÈRE forme sondée (outils, chat_template_kwargs, reasoning_effort), quelle qu'elle soit. NUDGE_IN_TOOL pouvait donc glisser le rappel de budget dans un résultat d'outil sur la foi d'un « préfixe instable » conclu pour une autre forme — un autre jeu d'outils, la réflexion coupée, un autre niveau — dont le rendu n'a rien à voir. - Les verdicts sont aussi rangés par forme (empreinte tplShape, au plus 16, la plus ancienne cède) ; tplCapsFor rend celui de la forme demandée. - NUDGE_IN_TOOL calcule la forme de la requête courante (mêmes outils, kwargs et niveau que ceux envoyés) et ne place le rappel dans le résultat que si CETTE forme est « instable » pour ce modèle ; forme jamais sondée ou inconnue : message à part, comme sans la clé. Une relance outils coupés ou tool_choice « none » (forme que la sonde ne voit jamais) se replie aussi. - L'affichage (/api/perf/summary) et REASONING_ECHO gardent le dernier verdict, comme avant. Sans la clé, rien ne change. - Tests : verdict par forme dans la sonde, autre forme / autres kwargs / autre niveau refusés, bout à bout avec le verdict d'une autre forme (échouait sur l'ancien code). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac40a2556a
commit
4b659ac186
6 files changed
+154
-20
No files matched your search
@@ -473,8 +473,10 @@ Ajoutées par ce fork :
|
|||||||
et tout le tour est rendu autrement), ce rappel fait recalculer toute la
|
et tout le tour est rendu autrement), ce rappel fait recalculer toute la
|
||||||
boucle d'outils du tour. Avec `on`, le rappel part au bout du dernier résultat
|
boucle d'outils du tour. Avec `on`, le rappel part au bout du dernier résultat
|
||||||
d'outil, et seul ce bout est à calculer — sur le moteur local, et seulement
|
d'outil, et seul ce bout est à calculer — sur le moteur local, et seulement
|
||||||
si la sonde de gabarit a conclu que le rendu de ce modèle bouge (« inconnu » :
|
si la sonde de gabarit a conclu que le rendu de ce modèle bouge pour la forme
|
||||||
message à part, comme sans la clé). Le texte du rappel ne change pas, sa
|
de cette requête — mêmes outils, `chat_template_kwargs` et niveau de
|
||||||
|
raisonnement (« inconnu » ou forme jamais sondée : message à part, comme
|
||||||
|
sans la clé). Le texte du rappel ne change pas, sa
|
||||||
place si : un modèle entraîné à se méfier des consignes lues dans une sortie
|
place si : un modèle entraîné à se méfier des consignes lues dans une sortie
|
||||||
d'outil peut moins bien le suivre. À comparer (tours qui concluent après le
|
d'outil peut moins bien le suivre. À comparer (tours qui concluent après le
|
||||||
rappel) avant de l'adopter.
|
rappel) avant de l'adopter.
|
||||||
|
|||||||
@@ -407,36 +407,65 @@ func TestKeptImagesDebutDeTour(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NUDGE_IN_TOOL : seulement avec la clé, sur le moteur local, si la sonde a dit
|
// NUDGE_IN_TOOL : seulement avec la clé, sur le moteur local, si la sonde a dit
|
||||||
// « instable » pour CE modèle.
|
// « instable » pour CE modèle et pour la forme de CETTE requête.
|
||||||
func TestNudgeInToolOn(t *testing.T) {
|
func TestNudgeInToolOn(t *testing.T) {
|
||||||
freshTplProbe(t)
|
freshTplProbe(t)
|
||||||
cfg := map[string]string{"MODEL": "/models/qwen35.gguf"}
|
cfg := map[string]string{"MODEL": "/models/qwen35.gguf"}
|
||||||
|
tools := []Tool{readTool(), grepTool()}
|
||||||
|
kwargs := map[string]any{"enable_thinking": true}
|
||||||
set := func(r tplProbeResult) {
|
set := func(r tplProbeResult) {
|
||||||
tplProbeMu.Lock()
|
tplProbeMu.Lock()
|
||||||
tplProbeLast = &r
|
tplProbeLast = &r
|
||||||
|
tplProbeKeepShape(newTplShape(tools, kwargs, "").hash(), r)
|
||||||
tplProbeMu.Unlock()
|
tplProbeMu.Unlock()
|
||||||
}
|
}
|
||||||
|
on := func(cfg map[string]string, ep chatEndpoint) bool { return nudgeInToolOn(cfg, ep, tools, kwargs, "") }
|
||||||
set(tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo})
|
set(tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo})
|
||||||
if nudgeInToolOn(cfg, chatEndpoint{}) {
|
if on(cfg, chatEndpoint{}) {
|
||||||
t.Fatal("actif sans la clé")
|
t.Fatal("actif sans la clé")
|
||||||
}
|
}
|
||||||
cfg["NUDGE_IN_TOOL"] = "on"
|
cfg["NUDGE_IN_TOOL"] = "on"
|
||||||
if !nudgeInToolOn(cfg, chatEndpoint{}) {
|
if !on(cfg, chatEndpoint{}) {
|
||||||
t.Fatal("inactif avec la clé sur un gabarit instable")
|
t.Fatal("inactif avec la clé sur un gabarit instable")
|
||||||
}
|
}
|
||||||
if nudgeInToolOn(cfg, chatEndpoint{External: true}) {
|
if on(cfg, chatEndpoint{External: true}) {
|
||||||
t.Fatal("actif sur un preset externe")
|
t.Fatal("actif sur un preset externe")
|
||||||
}
|
}
|
||||||
|
// Le verdict « instable » d'une forme ne vaut pas pour une autre : autres
|
||||||
|
// outils, autres kwargs ou autre niveau de raisonnement — inconnu, repli.
|
||||||
|
for name, other := range map[string]func() bool{
|
||||||
|
"autres outils": func() bool { return nudgeInToolOn(cfg, chatEndpoint{}, []Tool{readTool()}, kwargs, "") },
|
||||||
|
"autres kwargs": func() bool {
|
||||||
|
return nudgeInToolOn(cfg, chatEndpoint{}, tools, map[string]any{"enable_thinking": false}, "")
|
||||||
|
},
|
||||||
|
"autre effort": func() bool { return nudgeInToolOn(cfg, chatEndpoint{}, tools, kwargs, "high") },
|
||||||
|
} {
|
||||||
|
if other() {
|
||||||
|
t.Errorf("%s : actif sur le verdict d'une autre forme", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, r := range []tplProbeResult{
|
for _, r := range []tplProbeResult{
|
||||||
{Model: "qwen35.gguf", PrefixStable: tplUnknown},
|
{Model: "qwen35.gguf", PrefixStable: tplUnknown},
|
||||||
{Model: "qwen35.gguf", PrefixStable: tplYes},
|
{Model: "qwen35.gguf", PrefixStable: tplYes},
|
||||||
{Model: "autre.gguf", PrefixStable: tplNo},
|
{Model: "autre.gguf", PrefixStable: tplNo},
|
||||||
} {
|
} {
|
||||||
set(r)
|
set(r)
|
||||||
if nudgeInToolOn(cfg, chatEndpoint{}) {
|
if on(cfg, chatEndpoint{}) {
|
||||||
t.Fatalf("actif pour %+v", r)
|
t.Fatalf("actif pour %+v", r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// La dernière forme sondée (tplProbeLast) ne décide plus : une autre forme
|
||||||
|
// sondée « stable » ensuite ne change rien au verdict de la nôtre, et
|
||||||
|
// inversement.
|
||||||
|
set(tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo})
|
||||||
|
tplProbeMu.Lock()
|
||||||
|
last := tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplYes}
|
||||||
|
tplProbeLast = &last
|
||||||
|
tplProbeKeepShape(newTplShape(nil, kwargs, "").hash(), last)
|
||||||
|
tplProbeMu.Unlock()
|
||||||
|
if !on(cfg, chatEndpoint{}) {
|
||||||
|
t.Error("verdict de notre forme écrasé par la dernière forme sondée")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNudgeIntoTool(t *testing.T) {
|
func TestNudgeIntoTool(t *testing.T) {
|
||||||
@@ -468,11 +497,17 @@ func TestRappelDeBudgetDansLeResultat(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Le verdict de la sonde pour la forme exacte de la requête du tour : mêmes
|
||||||
|
// outils, mêmes chat_template_kwargs, même niveau de raisonnement.
|
||||||
|
caps := Caps{Agent: true}
|
||||||
|
_, effort, _, kwargs := turnReasoning(ReadConfig())
|
||||||
tplProbeMu.Lock()
|
tplProbeMu.Lock()
|
||||||
tplProbeLast = &tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo}
|
r := tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo}
|
||||||
|
tplProbeLast = &r
|
||||||
|
tplProbeKeepShape(newTplShape(EnabledTools(caps), kwargs, effort).hash(), r)
|
||||||
tplProbeMu.Unlock()
|
tplProbeMu.Unlock()
|
||||||
reqs := scriptedServer(t, sseGlobCall, sseChunk("fini")+sseStop)
|
reqs := scriptedServer(t, sseGlobCall, sseChunk("fini")+sseStop)
|
||||||
extra, err := runChat(t.Context(), []Message{um("cherche")}, 0.7, Caps{Agent: true}, func(StreamEvent) bool { return true })
|
extra, err := runChat(t.Context(), []Message{um("cherche")}, 0.7, caps, func(StreamEvent) bool { return true })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -486,6 +521,32 @@ func TestRappelDeBudgetDansLeResultat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Le verdict « instable » d'une AUTRE forme (dernière sondée, ici sans
|
||||||
|
// outils) ne décide pas pour ce tour : rappel à part, comme sans la clé.
|
||||||
|
func TestRappelDeBudgetVerdictDUneAutreForme(t *testing.T) {
|
||||||
|
withWorkspace(t)
|
||||||
|
freshTplProbe(t)
|
||||||
|
for k, v := range map[string]string{"AGENT_BUDGET": "1", "NUDGE_IN_TOOL": "on", "MODEL": "/models/qwen35.gguf"} {
|
||||||
|
if err := SetConfigKey(k, v); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, effort, _, kwargs := turnReasoning(ReadConfig())
|
||||||
|
tplProbeMu.Lock()
|
||||||
|
r := tplProbeResult{Model: "qwen35.gguf", PrefixStable: tplNo}
|
||||||
|
tplProbeLast = &r
|
||||||
|
tplProbeKeepShape(newTplShape(nil, kwargs, effort).hash(), r)
|
||||||
|
tplProbeMu.Unlock()
|
||||||
|
reqs := scriptedServer(t, sseGlobCall, sseChunk("fini")+sseStop)
|
||||||
|
if _, err := runChat(t.Context(), []Message{um("cherche")}, 0.7, Caps{Agent: true}, func(StreamEvent) bool { return true }); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sent := reqMessages(t, reqs()[1])
|
||||||
|
if last := sent[len(sent)-1]; last.Role != "user" {
|
||||||
|
t.Fatalf("rappel glissé dans le résultat sur le verdict d'une autre forme : %+v", last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// En cours de tour, au seuil : les images gardées partent d'abord, et le
|
// En cours de tour, au seuil : les images gardées partent d'abord, et le
|
||||||
// résumé n'est pas demandé si leur retrait suffit.
|
// résumé n'est pas demandé si leur retrait suffit.
|
||||||
func TestKeepImagesRetireesEnCoursDeTour(t *testing.T) {
|
func TestKeepImagesRetireesEnCoursDeTour(t *testing.T) {
|
||||||
|
|||||||
@@ -165,9 +165,14 @@ var (
|
|||||||
// Attente entre deux essais sur un 503 (modèle en chargement).
|
// Attente entre deux essais sur un 503 (modèle en chargement).
|
||||||
tplProbeBackoff = time.Second
|
tplProbeBackoff = time.Second
|
||||||
|
|
||||||
tplProbeMu sync.Mutex
|
tplProbeMu sync.Mutex
|
||||||
tplProbeCache = map[string]tplProbeResult{}
|
tplProbeCache = map[string]tplProbeResult{}
|
||||||
tplProbeLast *tplProbeResult
|
tplProbeLast *tplProbeResult
|
||||||
|
// tplProbeByShape : le dernier verdict de chaque forme de requête
|
||||||
|
// (tplShape.hash). tplProbeLast est celui de la dernière forme sondée,
|
||||||
|
// quelle qu'elle soit : bon pour l'affichage, pas pour décider d'une
|
||||||
|
// requête d'une autre forme (tplCapsFor).
|
||||||
|
tplProbeByShape = map[string]tplProbeResult{}
|
||||||
tplProbeRunning bool
|
tplProbeRunning bool
|
||||||
tplProbeTried time.Time
|
tplProbeTried time.Time
|
||||||
tplProbeLogged string
|
tplProbeLogged string
|
||||||
@@ -215,6 +220,20 @@ func tplCapsCurrent() (tplProbeResult, bool) {
|
|||||||
return *tplProbeLast, true
|
return *tplProbeLast, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tplCapsFor : le dernier verdict pour CETTE forme de requête (mêmes outils,
|
||||||
|
// mêmes chat_template_kwargs, même reasoning_effort). Pas de verdict pour elle
|
||||||
|
// — jamais sondée, ou sortie du cache — : rien, et l'appelant se replie comme
|
||||||
|
// sur « inconnu ». Rien non plus pour un preset externe.
|
||||||
|
func tplCapsFor(shape tplShape) (tplProbeResult, bool) {
|
||||||
|
if externalActive() {
|
||||||
|
return tplProbeResult{}, false
|
||||||
|
}
|
||||||
|
tplProbeMu.Lock()
|
||||||
|
defer tplProbeMu.Unlock()
|
||||||
|
r, ok := tplProbeByShape[shape.hash()]
|
||||||
|
return r, ok
|
||||||
|
}
|
||||||
|
|
||||||
// tplProbeEnsure : verdict pour la forme donnée, depuis le cache ou une sonde.
|
// tplProbeEnsure : verdict pour la forme donnée, depuis le cache ou une sonde.
|
||||||
func tplProbeEnsure(ctx context.Context, shape tplShape) tplProbeResult {
|
func tplProbeEnsure(ctx context.Context, shape tplShape) tplProbeResult {
|
||||||
if resolveChatEndpoint().External {
|
if resolveChatEndpoint().External {
|
||||||
@@ -232,7 +251,7 @@ func tplProbeEnsure(ctx context.Context, shape tplShape) tplProbeResult {
|
|||||||
props, err := p.props(ctx)
|
props, err := p.props(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r := tplUnknownResult("/props illisible : " + err.Error())
|
r := tplUnknownResult("/props illisible : " + err.Error())
|
||||||
tplProbeStore("", r)
|
tplProbeStore("", shape.hash(), r)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
h := fnv.New64a()
|
h := fnv.New64a()
|
||||||
@@ -245,6 +264,7 @@ func tplProbeEnsure(ctx context.Context, shape tplShape) tplProbeResult {
|
|||||||
tplProbeMu.Lock()
|
tplProbeMu.Lock()
|
||||||
if r, ok := tplProbeCache[key]; ok {
|
if r, ok := tplProbeCache[key]; ok {
|
||||||
tplProbeLast = &r
|
tplProbeLast = &r
|
||||||
|
tplProbeKeepShape(shape.hash(), r)
|
||||||
tplProbeMu.Unlock()
|
tplProbeMu.Unlock()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -265,14 +285,30 @@ func tplProbeEnsure(ctx context.Context, shape tplShape) tplProbeResult {
|
|||||||
if fresh {
|
if fresh {
|
||||||
fmt.Fprintln(os.Stderr, line)
|
fmt.Fprintln(os.Stderr, line)
|
||||||
}
|
}
|
||||||
tplProbeStore(key, r)
|
tplProbeStore(key, shape.hash(), r)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func tplProbeStore(key string, r tplProbeResult) {
|
// tplProbeKeepShape note le verdict d'une forme (tplProbeMu tenu). Au plus
|
||||||
|
// tplProbeCacheMax formes : la plus ancienne cède sa place.
|
||||||
|
func tplProbeKeepShape(shape string, r tplProbeResult) {
|
||||||
|
if _, ok := tplProbeByShape[shape]; !ok && len(tplProbeByShape) >= tplProbeCacheMax {
|
||||||
|
oldest := ""
|
||||||
|
for k, v := range tplProbeByShape {
|
||||||
|
if oldest == "" || v.At.Before(tplProbeByShape[oldest].At) {
|
||||||
|
oldest = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(tplProbeByShape, oldest)
|
||||||
|
}
|
||||||
|
tplProbeByShape[shape] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
func tplProbeStore(key, shape string, r tplProbeResult) {
|
||||||
tplProbeMu.Lock()
|
tplProbeMu.Lock()
|
||||||
defer tplProbeMu.Unlock()
|
defer tplProbeMu.Unlock()
|
||||||
tplProbeLast = &r
|
tplProbeLast = &r
|
||||||
|
tplProbeKeepShape(shape, r)
|
||||||
if key == "" || !r.cacheable {
|
if key == "" || !r.cacheable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func freshTplProbe(t *testing.T) {
|
|||||||
tplProbeMu.Lock()
|
tplProbeMu.Lock()
|
||||||
tplProbeCache = map[string]tplProbeResult{}
|
tplProbeCache = map[string]tplProbeResult{}
|
||||||
tplProbeLast = nil
|
tplProbeLast = nil
|
||||||
|
tplProbeByShape = map[string]tplProbeResult{}
|
||||||
tplProbeRunning = false
|
tplProbeRunning = false
|
||||||
tplProbeTried = time.Time{}
|
tplProbeTried = time.Time{}
|
||||||
tplProbeLogged = ""
|
tplProbeLogged = ""
|
||||||
@@ -447,3 +448,33 @@ func TestSondeGabaritDeclencheeParRunChat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Le verdict est rangé par forme de requête : la dernière forme sondée ne
|
||||||
|
// décide pas pour une autre (NUDGE_IN_TOOL), et une forme jamais sondée n'a
|
||||||
|
// pas de verdict.
|
||||||
|
func TestSondeGabaritVerdictParForme(t *testing.T) {
|
||||||
|
testHome(t)
|
||||||
|
freshTplProbe(t)
|
||||||
|
f := &fakeTpl{mode: "qwen3"}
|
||||||
|
f.start(t)
|
||||||
|
a := newTplShape(probeTools(), nil, "")
|
||||||
|
b := newTplShape(nil, nil, "")
|
||||||
|
if r := tplProbeEnsure(context.Background(), a); r.PrefixStable != tplNo {
|
||||||
|
t.Fatalf("forme A : %+v", r)
|
||||||
|
}
|
||||||
|
f.mu.Lock()
|
||||||
|
f.mode = "keep"
|
||||||
|
f.mu.Unlock()
|
||||||
|
if r := tplProbeEnsure(context.Background(), b); r.PrefixStable != tplYes {
|
||||||
|
t.Fatalf("forme B : %+v", r)
|
||||||
|
}
|
||||||
|
if r, ok := tplCapsCurrent(); !ok || r.PrefixStable != tplYes {
|
||||||
|
t.Errorf("affichage : la dernière sondée, B : %+v", r)
|
||||||
|
}
|
||||||
|
if r, ok := tplCapsFor(a); !ok || r.PrefixStable != tplNo {
|
||||||
|
t.Errorf("forme A écrasée par B : %+v %v", r, ok)
|
||||||
|
}
|
||||||
|
if _, ok := tplCapsFor(newTplShape(probeTools(), map[string]any{"enable_thinking": false}, "")); ok {
|
||||||
|
t.Error("verdict pour une forme jamais sondée")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1417,8 +1417,10 @@ func runChatLoop(ctx context.Context, kt *keepImages, messages []Message, tools
|
|||||||
// NUDGE_IN_TOOL (opt-in, gabarits dont le rendu bouge quand un message
|
// NUDGE_IN_TOOL (opt-in, gabarits dont le rendu bouge quand un message
|
||||||
// user s'ajoute) : au bout du dernier résultat d'outil. Sinon, ou si
|
// user s'ajoute) : au bout du dernier résultat d'outil. Sinon, ou si
|
||||||
// la forme ne s'y prête pas, un message à part comme toujours.
|
// la forme ne s'y prête pas, un message à part comme toujours.
|
||||||
|
// La sonde ne connaît que la forme normale (outils annoncés, ni coupés
|
||||||
|
// ni neutralisés) : une relance d'une autre forme se replie.
|
||||||
placed := false
|
placed := false
|
||||||
if nudgeInToolOn(chatCfg, ep) {
|
if !disableTools && !toolChoiceNone && nudgeInToolOn(chatCfg, ep, tools, reasoningKwargs, reasoningEffort) {
|
||||||
messages, extra, placed = nudgeIntoTool(messages, extra, msg)
|
messages, extra, placed = nudgeIntoTool(messages, extra, msg)
|
||||||
}
|
}
|
||||||
if !placed {
|
if !placed {
|
||||||
|
|||||||
@@ -174,9 +174,11 @@ func withTrailingHint(msgs []Message, hint string) []Message {
|
|||||||
// l'opt-in, à n'activer qu'après avoir comparé l'obéissance aux rappels.
|
// l'opt-in, à n'activer qu'après avoir comparé l'obéissance aux rappels.
|
||||||
//
|
//
|
||||||
// Moteur local seulement, et seulement si la sonde de gabarit
|
// Moteur local seulement, et seulement si la sonde de gabarit
|
||||||
// (chat_tplprobe.go) a conclu « préfixe instable » pour CE modèle : « inconnu »
|
// (chat_tplprobe.go) a conclu « préfixe instable » pour CE modèle et pour la
|
||||||
// ou un autre modèle laissent le message à part.
|
// forme de CETTE requête (outils, chat_template_kwargs, reasoning_effort : le
|
||||||
func nudgeInToolOn(cfg map[string]string, ep chatEndpoint) bool {
|
// rendu en dépend). « inconnu », un autre modèle ou une forme jamais sondée
|
||||||
|
// laissent le message à part.
|
||||||
|
func nudgeInToolOn(cfg map[string]string, ep chatEndpoint, tools []Tool, kwargs map[string]any, effort string) bool {
|
||||||
switch strings.ToLower(strings.TrimSpace(cfg["NUDGE_IN_TOOL"])) {
|
switch strings.ToLower(strings.TrimSpace(cfg["NUDGE_IN_TOOL"])) {
|
||||||
case "on", "1", "true", "yes", "oui":
|
case "on", "1", "true", "yes", "oui":
|
||||||
default:
|
default:
|
||||||
@@ -185,7 +187,7 @@ func nudgeInToolOn(cfg map[string]string, ep chatEndpoint) bool {
|
|||||||
if ep.External {
|
if ep.External {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
r, ok := tplCapsCurrent()
|
r, ok := tplCapsFor(newTplShape(tools, kwargs, effort))
|
||||||
return ok && r.PrefixStable == tplNo && r.Model != "" && r.Model == reasoningEchoModel(cfg)
|
return ok && r.PrefixStable == tplNo && r.Model != "" && r.Model == reasoningEchoModel(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user