diff --git a/cmd/gateway_providers.go b/cmd/gateway_providers.go index 30a17cac..5a3a8d8b 100644 --- a/cmd/gateway_providers.go +++ b/cmd/gateway_providers.go @@ -317,7 +317,8 @@ func registerProvidersFromDB(registry *providers.Registry, provStore store.Provi host = "http://localhost:11434" } numCtx := resolveOllamaNumCtx(&p, config.DockerLocalhost(host), "") - prov := providers.NewOllamaProvider(p.Name, config.DockerLocalhost(host), "llama3.3", numCtx, nil) + prov := providers.NewOllamaProvider(p.Name, config.DockerLocalhost(host), "llama3.3", numCtx, nil). + WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) registry.RegisterForTenant(p.TenantID, prov) slog.Info("registered provider from DB", "name", p.Name) continue @@ -397,7 +398,8 @@ func registerProvidersFromDB(registry *providers.Registry, provStore store.Provi base = "https://ollama.com" } numCtx := resolveOllamaNumCtx(&p, base, p.APIKey) - prov := providers.NewOllamaProvider(p.Name, base, "llama3.3", numCtx, nil) + prov := providers.NewOllamaProvider(p.Name, base, "llama3.3", numCtx, nil). + WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) registry.RegisterForTenant(p.TenantID, prov) case store.ProviderNovita: base := p.APIBase @@ -442,6 +444,7 @@ func registerProvidersFromDB(registry *providers.Registry, provStore store.Provi base, model := openAIProviderDefaults(p.ProviderType, p.APIBase) prov := providers.NewOpenAIProvider(p.Name, p.APIKey, base, model) prov.WithProviderType(p.ProviderType) + prov.WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) if p.ProviderType == store.ProviderOpenRouter { prov.WithSiteInfo("https://goclaw.sh", "GoClaw") } diff --git a/internal/http/providers.go b/internal/http/providers.go index 546cebf3..9539d631 100644 --- a/internal/http/providers.go +++ b/internal/http/providers.go @@ -255,7 +255,8 @@ func (h *ProvidersHandler) registerInMemory(p *store.LLMProviderData) providerRu } dockerHost := config.DockerLocalhost(host) numCtx := h.resolveOllamaNumCtx(p, dockerHost, "") - prov := providers.NewOllamaProvider(p.Name, dockerHost, "llama3.3", numCtx, nil) + prov := providers.NewOllamaProvider(p.Name, dockerHost, "llama3.3", numCtx, nil). + WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) h.providerReg.RegisterForTenant(p.TenantID, prov) return providerRuntimeRegistered } @@ -352,11 +353,13 @@ func (h *ProvidersHandler) registerInMemory(p *store.LLMProviderData) providerRu base = "https://ollama.com" } numCtx := h.resolveOllamaNumCtx(p, base, p.APIKey) - prov := providers.NewOllamaProvider(p.Name, base, "llama3.3", numCtx, nil) + prov := providers.NewOllamaProvider(p.Name, base, "llama3.3", numCtx, nil). + WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) h.providerReg.RegisterForTenant(p.TenantID, prov) default: base, model := openAIProviderDefaults(p.ProviderType, apiBase) prov := providers.NewOpenAIProvider(p.Name, p.APIKey, base, model) + prov.WithThinkingEnabled(store.ParseThinkingEnabled(p.Settings)) h.providerReg.RegisterForTenant(p.TenantID, prov) } return providerRuntimeRegistered diff --git a/internal/providers/ollama.go b/internal/providers/ollama.go index 75998846..c767d072 100644 --- a/internal/providers/ollama.go +++ b/internal/providers/ollama.go @@ -22,6 +22,11 @@ type OllamaProvider struct { numCtx *int client *ollamaapi.Client retryConfig RetryConfig + + // thinkingEnabled is the provider-level override for whether requests + // should ask Ollama to emit visible reasoning/thinking tokens. + // nil = default off (see buildRequest). + thinkingEnabled *bool } // NewOllamaProvider creates an OllamaProvider. @@ -57,6 +62,20 @@ func NewOllamaProvider(name, apiBase, defaultModel string, numCtx *int, httpClie } } +// WithThinkingEnabled sets the provider-level override for whether native +// Ollama chat requests should ask the model to emit visible reasoning +// ("think") tokens. nil (not calling this) preserves the existing default +// of disabling thinking. +func (p *OllamaProvider) WithThinkingEnabled(enabled *bool) *OllamaProvider { + p.thinkingEnabled = enabled + return p +} + +// ThinkingEnabled returns the configured provider-level thinking override, or nil if not set. +func (p *OllamaProvider) ThinkingEnabled() *bool { + return p.thinkingEnabled +} + // Name returns the provider identifier. func (p *OllamaProvider) Name() string { return p.name } @@ -200,6 +219,16 @@ func (p *OllamaProvider) buildRequest(req ChatRequest, stream bool) *ollamaapi.C Stream: &stream, } + // Thinking visibility: default off (models like qwq/deepseek-r1 have + // thinking on by default and goclaw suppresses it to avoid bloated + // chain-of-thought responses), unless the provider config explicitly + // enables it via settings.thinking_enabled=true. + thinkingEnabled := false + if p.thinkingEnabled != nil { + thinkingEnabled = *p.thinkingEnabled + } + ollamaReq.Think = &ollamaapi.ThinkValue{Value: thinkingEnabled} + // Inject tools. for _, td := range req.Tools { if td.Type != "function" || td.Function == nil { @@ -301,4 +330,3 @@ func mapDoneReason(reason string) string { return "stop" } } - diff --git a/internal/providers/ollama_test.go b/internal/providers/ollama_test.go new file mode 100644 index 00000000..d4e6413a --- /dev/null +++ b/internal/providers/ollama_test.go @@ -0,0 +1,64 @@ +package providers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestOllamaBuildRequest_ThinkDefaultsToFalse verifies that when no +// provider-level thinking override is configured, buildRequest disables +// thinking by default (matches the OpenAI-compat Ollama path). +func TestOllamaBuildRequest_ThinkDefaultsToFalse(t *testing.T) { + p := NewOllamaProvider("ollama", "http://localhost:11434", "llama3.3", nil, nil) + req := ChatRequest{Messages: []Message{{Role: "user", Content: "hi"}}} + ollamaReq := p.buildRequest(req, false) + + require.NotNil(t, ollamaReq.Think) + value, ok := ollamaReq.Think.Value.(bool) + require.True(t, ok, "expected Think.Value to be a bool") + assert.False(t, value) +} + +// TestOllamaBuildRequest_ThinkOverride verifies that WithThinkingEnabled +// correctly sets ollamaReq.Think for both true and false overrides. +func TestOllamaBuildRequest_ThinkOverride(t *testing.T) { + trueVal := true + falseVal := false + + cases := []struct { + name string + override *bool + want bool + }{ + {name: "override true enables thinking", override: &trueVal, want: true}, + {name: "override false disables thinking", override: &falseVal, want: false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + p := NewOllamaProvider("ollama", "http://localhost:11434", "llama3.3", nil, nil). + WithThinkingEnabled(tc.override) + req := ChatRequest{Messages: []Message{{Role: "user", Content: "hi"}}} + ollamaReq := p.buildRequest(req, false) + + require.NotNil(t, ollamaReq.Think) + value, ok := ollamaReq.Think.Value.(bool) + require.True(t, ok, "expected Think.Value to be a bool") + assert.Equal(t, tc.want, value) + }) + } +} + +// TestOllamaThinkingEnabledAccessor verifies the WithThinkingEnabled / +// ThinkingEnabled getter/setter round-trip. +func TestOllamaThinkingEnabledAccessor(t *testing.T) { + p := NewOllamaProvider("ollama", "http://localhost:11434", "llama3.3", nil, nil) + assert.Nil(t, p.ThinkingEnabled()) + + trueVal := true + p.WithThinkingEnabled(&trueVal) + require.NotNil(t, p.ThinkingEnabled()) + assert.True(t, *p.ThinkingEnabled()) +} diff --git a/internal/providers/openai_config.go b/internal/providers/openai_config.go index 64106ba8..d8fcdb67 100644 --- a/internal/providers/openai_config.go +++ b/internal/providers/openai_config.go @@ -8,22 +8,23 @@ import ( // OpenAIProvider implements Provider for OpenAI-compatible APIs // (OpenAI, Groq, OpenRouter, DeepSeek, VLLM, etc.) type OpenAIProvider struct { - name string - apiKey string - apiBase string - chatPath string // defaults to "/chat/completions" - authPrefix string // auth header prefix, defaults to "Bearer " if empty - defaultModel string - providerType string // DB provider_type (e.g. "gemini_native", "openai", "minimax_native") - siteURL string // optional site URL for provider identification (e.g. OpenRouter HTTP-Referer) - siteTitle string // optional site title for provider identification (e.g. OpenRouter X-Title) - extraHeaders map[string]string // static headers set on every outgoing request (e.g. fixed User-Agent for kimi_coding) - client *http.Client - retryConfig RetryConfig - middlewares RequestMiddleware // composed middleware chain (nil = no-op) - registry ModelRegistry // model resolution registry (nil = skip) - noAuthHeader bool // when true, doRequest() skips setting Authorization (e.g. Vertex OAuth transport injects its own) - ollamaNumCtx *int // optional Ollama options.num_ctx override (nil = use queried or default value) + name string + apiKey string + apiBase string + chatPath string // defaults to "/chat/completions" + authPrefix string // auth header prefix, defaults to "Bearer " if empty + defaultModel string + providerType string // DB provider_type (e.g. "gemini_native", "openai", "minimax_native") + siteURL string // optional site URL for provider identification (e.g. OpenRouter HTTP-Referer) + siteTitle string // optional site title for provider identification (e.g. OpenRouter X-Title) + extraHeaders map[string]string // static headers set on every outgoing request (e.g. fixed User-Agent for kimi_coding) + client *http.Client + retryConfig RetryConfig + middlewares RequestMiddleware // composed middleware chain (nil = no-op) + registry ModelRegistry // model resolution registry (nil = skip) + noAuthHeader bool // when true, doRequest() skips setting Authorization (e.g. Vertex OAuth transport injects its own) + ollamaNumCtx *int // optional Ollama options.num_ctx override (nil = use queried or default value) + thinkingEnabled *bool // provider-level override for "think" on Ollama endpoints (nil = default off) } func NewOpenAIProvider(name, apiKey, apiBase, defaultModel string) *OpenAIProvider { @@ -143,6 +144,20 @@ func (p *OpenAIProvider) OllamaNumCtx() *int { return p.ollamaNumCtx } +// WithThinkingEnabled sets the provider-level override for whether Ollama +// endpoints should be asked to emit visible reasoning/thinking tokens +// (sets body["think"] in buildRequestBody). nil (not calling this) preserves +// the existing default of disabling thinking on Ollama endpoints. +func (p *OpenAIProvider) WithThinkingEnabled(enabled *bool) *OpenAIProvider { + p.thinkingEnabled = enabled + return p +} + +// ThinkingEnabled returns the configured provider-level thinking override, or nil if not set. +func (p *OpenAIProvider) ThinkingEnabled() *bool { + return p.thinkingEnabled +} + func (p *OpenAIProvider) Name() string { return p.name } func (p *OpenAIProvider) DefaultModel() string { return p.defaultModel } diff --git a/internal/providers/openai_request.go b/internal/providers/openai_request.go index f395cd7a..61de8d87 100644 --- a/internal/providers/openai_request.go +++ b/internal/providers/openai_request.go @@ -298,9 +298,17 @@ func (p *OpenAIProvider) buildRequestBody(model string, req ChatRequest, stream } slog.Debug("ollama.request: final request body (first 500 chars)", "provider", p.name, "model", model, "body_prefix", raw) } - // Disable thinking by default; only enable if caller explicitly requests it. - if level, _ := req.Options[OptThinkingLevel].(string); level == "" || level == "off" { - body["think"] = false + // Thinking visibility: provider-level override (settings.thinking_enabled) + // takes precedence; otherwise disable thinking by default (models like + // qwq/deepseek-r1 have thinking on by default) unless the caller + // explicitly requests a reasoning effort level. + switch { + case p.thinkingEnabled != nil: + body["think"] = *p.thinkingEnabled + default: + if level, _ := req.Options[OptThinkingLevel].(string); level == "" || level == "off" { + body["think"] = false + } } } diff --git a/internal/providers/openai_test.go b/internal/providers/openai_test.go index aedbcb2b..c915c01e 100644 --- a/internal/providers/openai_test.go +++ b/internal/providers/openai_test.go @@ -293,7 +293,6 @@ func TestBuildRequestBody_MultimodalWithVideoURL(t *testing.T) { } } - func TestBuildRequestBody_TogetherDetectedByProviderType(t *testing.T) { // Together behind reverse proxy — detected by providerType, not URL. p := NewOpenAIProvider("my-proxy", "key", "https://proxy.internal/v1", "") @@ -705,3 +704,51 @@ func TestNonOllamaSupportsThinkingTrue(t *testing.T) { t.Fatal("SupportsThinking() must return true for non-Ollama OpenAI-compat providers") } } + +func TestBuildRequestBody_OllamaThinkingEnabledOverride(t *testing.T) { + // Provider-level settings.thinking_enabled overrides the default off + // behavior for Ollama endpoints, regardless of OptThinkingLevel. + trueVal := true + falseVal := false + + cases := []struct { + name string + override *bool + want bool + }{ + {name: "override true enables thinking", override: &trueVal, want: true}, + {name: "override false disables thinking", override: &falseVal, want: false}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + p := NewOpenAIProvider("ollama", "", "http://localhost:11434/v1", ""). + WithThinkingEnabled(tc.override) + req := ChatRequest{Messages: []Message{{Role: "user", Content: "hi"}}} + body := p.buildRequestBody("qwq:32b", req, false) + think, ok := body["think"] + if !ok { + t.Fatalf("expected think field in request body") + } + if think != tc.want { + t.Fatalf("think = %v, want %v", think, tc.want) + } + }) + } +} + +func TestBuildRequestBody_OllamaThinkingEnabledOverrideTakesPrecedenceOverLevel(t *testing.T) { + // Even when OptThinkingLevel requests non-off reasoning, an explicit + // provider-level thinking_enabled=false must still win. + falseVal := false + p := NewOpenAIProvider("ollama", "", "http://localhost:11434/v1", ""). + WithThinkingEnabled(&falseVal) + req := ChatRequest{ + Messages: []Message{{Role: "user", Content: "reason about this"}}, + Options: map[string]any{OptThinkingLevel: "high"}, + } + body := p.buildRequestBody("qwq:32b", req, false) + if think, ok := body["think"]; !ok || think != false { + t.Fatalf("expected think=false when provider override is false, got %v (present=%v)", think, ok) + } +} diff --git a/internal/store/provider_store.go b/internal/store/provider_store.go index c8bd0788..2f1a0e99 100644 --- a/internal/store/provider_store.go +++ b/internal/store/provider_store.go @@ -194,6 +194,24 @@ func ParseEmbeddingSettings(settings json.RawMessage) *EmbeddingSettings { return s.Embedding } +// ParseThinkingEnabled extracts the provider-level override for whether the +// provider should be asked to emit visible reasoning/thinking tokens (e.g. +// Ollama native "think" field, OpenAI-compat "think" for Ollama endpoints). +// Returns nil when unset in settings JSONB, meaning "use provider default" +// (currently off for Ollama). Explicit true/false overrides that default. +func ParseThinkingEnabled(settings json.RawMessage) *bool { + if len(settings) == 0 { + return nil + } + var s struct { + ThinkingEnabled *bool `json:"thinking_enabled"` + } + if json.Unmarshal(settings, &s) != nil { + return nil + } + return s.ThinkingEnabled +} + // ParseChatGPTOAuthProviderSettings extracts provider-level Codex pool defaults from settings JSONB. func ParseChatGPTOAuthProviderSettings(settings json.RawMessage) *ChatGPTOAuthProviderSettings { if len(settings) == 0 { diff --git a/internal/store/provider_store_test.go b/internal/store/provider_store_test.go new file mode 100644 index 00000000..dfca4903 --- /dev/null +++ b/internal/store/provider_store_test.go @@ -0,0 +1,68 @@ +package store + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseThinkingEnabled(t *testing.T) { + trueVal := true + falseVal := false + + tests := []struct { + name string + settings json.RawMessage + want *bool + }{ + { + name: "unset settings", + settings: nil, + want: nil, + }, + { + name: "empty object", + settings: json.RawMessage(`{}`), + want: nil, + }, + { + name: "explicit true", + settings: json.RawMessage(`{"thinking_enabled":true}`), + want: &trueVal, + }, + { + name: "explicit false", + settings: json.RawMessage(`{"thinking_enabled":false}`), + want: &falseVal, + }, + { + name: "coexists with other settings keys", + settings: json.RawMessage(`{"num_ctx":8192,"thinking_enabled":true}`), + want: &trueVal, + }, + { + name: "malformed json", + settings: json.RawMessage(`{not valid json`), + want: nil, + }, + { + name: "wrong type is ignored", + settings: json.RawMessage(`{"thinking_enabled":"yes"}`), + want: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := ParseThinkingEnabled(tc.settings) + if tc.want == nil { + assert.Nil(t, got) + return + } + require.NotNil(t, got) + assert.Equal(t, *tc.want, *got) + }) + } +} diff --git a/ui/web/src/i18n/locales/en/providers.json b/ui/web/src/i18n/locales/en/providers.json index 72ca3eb7..77518cb0 100644 --- a/ui/web/src/i18n/locales/en/providers.json +++ b/ui/web/src/i18n/locales/en/providers.json @@ -141,7 +141,12 @@ "description": "Ollama-specific settings", "numCtx": "Context Window (tokens)", "numCtxPlaceholder": "Leave empty to auto-detect from model", - "numCtxHelp": "Maximum number of tokens. Leave empty to auto-detect via Ollama API." + "numCtxHelp": "Maximum number of tokens. Leave empty to auto-detect via Ollama API.", + "thinkingEnabled": "Reasoning / Thinking Tokens", + "thinkingEnabledDefault": "Default (off)", + "thinkingEnabledOn": "On", + "thinkingEnabledOff": "Off", + "thinkingEnabledHelp": "Controls whether this provider asks the model to emit visible reasoning (\"thinking\") tokens. Default is off to avoid bloated chain-of-thought output from models like qwq or deepseek-r1." }, "oauth": { "checkingStatus": "Checking authentication status...", diff --git a/ui/web/src/i18n/locales/ko/providers.json b/ui/web/src/i18n/locales/ko/providers.json index b3d9d9f2..536de5ea 100644 --- a/ui/web/src/i18n/locales/ko/providers.json +++ b/ui/web/src/i18n/locales/ko/providers.json @@ -141,7 +141,12 @@ "description": "Ollama 고유 설정", "numCtx": "컨텍스트 윈도우(토큰)", "numCtxPlaceholder": "모델에서 자동 감지하려면 비워두세요", - "numCtxHelp": "최대 토큰 수입니다. Ollama API를 통해 자동 감지하려면 비워두세요." + "numCtxHelp": "최대 토큰 수입니다. Ollama API를 통해 자동 감지하려면 비워두세요.", + "thinkingEnabled": "추론 / 사고 토큰", + "thinkingEnabledDefault": "기본값 (끔)", + "thinkingEnabledOn": "켜짐", + "thinkingEnabledOff": "꺼짐", + "thinkingEnabledHelp": "이 제공자가 모델에게 표시되는 추론(\"사고\") 토큰을 생성하도록 요청할지 여부를 제어합니다. qwq나 deepseek-r1 같은 모델의 장황한 사고 과정 출력을 방지하기 위해 기본값은 꺼짐입니다." }, "oauth": { "checkingStatus": "인증 상태 확인 중...", diff --git a/ui/web/src/i18n/locales/vi/providers.json b/ui/web/src/i18n/locales/vi/providers.json index 430ed3ec..5556c010 100644 --- a/ui/web/src/i18n/locales/vi/providers.json +++ b/ui/web/src/i18n/locales/vi/providers.json @@ -146,7 +146,12 @@ "description": "Các cài đặt dành riêng cho Ollama", "numCtx": "Cửa sổ ngữ cảnh (tokens)", "numCtxPlaceholder": "Bỏ trống để tự động phát hiện từ mô hình", - "numCtxHelp": "Số lượng token tối đa. Bỏ trống để tự động phát hiện qua API Ollama." + "numCtxHelp": "Số lượng token tối đa. Bỏ trống để tự động phát hiện qua API Ollama.", + "thinkingEnabled": "Token suy luận / tư duy", + "thinkingEnabledDefault": "Mặc định (tắt)", + "thinkingEnabledOn": "Bật", + "thinkingEnabledOff": "Tắt", + "thinkingEnabledHelp": "Kiểm soát việc nhà cung cấp này có yêu cầu mô hình phát ra token suy luận (\"tư duy\") hiển thị hay không. Mặc định là tắt để tránh đầu ra chuỗi suy luận cồng kềnh từ các mô hình như qwq hoặc deepseek-r1." }, "oauth": { "checkingStatus": "Đang kiểm tra trạng thái xác thực...", diff --git a/ui/web/src/i18n/locales/zh/providers.json b/ui/web/src/i18n/locales/zh/providers.json index 154896c9..3167610f 100644 --- a/ui/web/src/i18n/locales/zh/providers.json +++ b/ui/web/src/i18n/locales/zh/providers.json @@ -155,7 +155,12 @@ "description": "Ollama 特定设置", "numCtx": "上下文窗口(标记数)", "numCtxPlaceholder": "留空自动从模型检测", - "numCtxHelp": "最大标记数。留空以通过 Ollama API 自动检测。" + "numCtxHelp": "最大标记数。留空以通过 Ollama API 自动检测。", + "thinkingEnabled": "推理 / 思考标记", + "thinkingEnabledDefault": "默认(关闭)", + "thinkingEnabledOn": "开启", + "thinkingEnabledOff": "关闭", + "thinkingEnabledHelp": "控制该提供商是否要求模型输出可见的推理(“思考”)标记。默认关闭,以避免 qwq、deepseek-r1 等模型产生冗长的思维链输出。" }, "oauth": { "checkingStatus": "正在检查认证状态...", diff --git a/ui/web/src/pages/providers/provider-detail/provider-advanced-dialog.tsx b/ui/web/src/pages/providers/provider-detail/provider-advanced-dialog.tsx index 451cb0e4..38c283e5 100644 --- a/ui/web/src/pages/providers/provider-detail/provider-advanced-dialog.tsx +++ b/ui/web/src/pages/providers/provider-detail/provider-advanced-dialog.tsx @@ -40,6 +40,10 @@ function deriveState(provider: ProviderData) { acpPermMode: (s?.perm_mode as string) || "approve-all", acpWorkDir: (s?.work_dir as string) || "", numCtx: (s?.num_ctx as string) || "", + thinkingEnabled: + typeof s?.thinking_enabled === "boolean" + ? (s.thinking_enabled as boolean) + : null, }; } @@ -66,6 +70,7 @@ export function ProviderAdvancedDialog({ const [acpPermMode, setAcpPermMode] = useState(init.acpPermMode); const [acpWorkDir, setAcpWorkDir] = useState(init.acpWorkDir); const [numCtx, setNumCtx] = useState(init.numCtx); + const [thinkingEnabled, setThinkingEnabled] = useState(init.thinkingEnabled); // Re-sync when dialog opens useEffect(() => { @@ -78,6 +83,7 @@ export function ProviderAdvancedDialog({ setAcpPermMode(s.acpPermMode); setAcpWorkDir(s.acpWorkDir); setNumCtx(s.numCtx); + setThinkingEnabled(s.thinkingEnabled); }, [open, provider]); @@ -105,6 +111,7 @@ export function ProviderAdvancedDialog({ if (provider.provider_type === "ollama" || provider.provider_type === "ollama_cloud") { const settings: Record = {}; if (numCtx.trim()) settings.num_ctx = parseInt(numCtx.trim(), 10); + if (thinkingEnabled !== null) settings.thinking_enabled = thinkingEnabled; if (Object.keys(settings).length > 0) data.settings = settings; } } @@ -187,6 +194,31 @@ export function ProviderAdvancedDialog({ />

{t("ollama.numCtxHelp")}

+
+ + +

{t("ollama.thinkingEnabledHelp")}

+
)}