From fd327107df2bf7878febd6ceeef75eadb8e94782 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Mon, 25 May 2026 02:28:05 +0200 Subject: [PATCH] fix(router/discovery): always probe ollama capabilities, cache is optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DiscoverOllama() interpreted a nil probeCache as 'skip probing entirely' rather than 'probe but don't cache.' cmd/gnoma/main.go's synchronous discovery path passes nil, so every ollama-discovered model got SupportsTools=false (the Go zero value), regardless of what ollama actually reported in its capabilities field. The symptom: filterFeasible rejected every ollama arm for any tool-requiring task with reason=tools_required_but_unsupported, even when ollama itself reported the model as tool-capable. Verified via curl: qwen3:14b advertises capabilities=[completion, tools, thinking] and has 'tools' in its template, but the gnoma arm shipped with tool_use_capability=false. Fix: always run probeOllamaModel; treat probeCache as an optional memoisation aid only. nil cache now means 'no caching across calls' not 'no probing.' For users with many models, passing a real cache still avoids redundant HTTP calls — semantics for that path are unchanged. Surfaced via the new filterFeasible Debug logging from the previous commit, which made the per-arm rejection reasons visible. --- internal/router/discovery.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/router/discovery.go b/internal/router/discovery.go index 6f1a740..afd05fe 100644 --- a/internal/router/discovery.go +++ b/internal/router/discovery.go @@ -93,16 +93,27 @@ func DiscoverOllama(ctx context.Context, baseURL string, probeCache map[string]O Size: m.Size, } + // Always probe; the cache is optional. Previously nil-cache was + // treated as "skip probing entirely", which left SupportsTools + // at its zero value (false) for every model — every ollama- + // discovered arm then got marked as tool-unsupported and + // rejected by filterFeasible for any tool-requiring task. main.go + // passes nil from the synchronous discovery path; we still want + // real probe data there. + var result OllamaProbeResult if probeCache != nil { - result, ok := probeCache[m.Name] - if !ok { + if cached, ok := probeCache[m.Name]; ok { + result = cached + } else { result = probeOllamaModel(ctx, baseURL, m.Name) probeCache[m.Name] = result } - dm.SupportsTools = result.SupportsTools - dm.SupportsVision = result.SupportsVision - dm.ContextSize = result.ContextSize + } else { + result = probeOllamaModel(ctx, baseURL, m.Name) } + dm.SupportsTools = result.SupportsTools + dm.SupportsVision = result.SupportsVision + dm.ContextSize = result.ContextSize if dm.ContextSize == 0 { dm.ContextSize = defaultOllamaContextSize