ApplyThinking applies thinking configuration to a request body. This is the unified entry point for all providers. It follows the processing order defined in FR25: route check → model capability query → config extraction → validation → application. Suffix Priority: When the model name includes a t
(body []byte, model string, fromFormat string, toFormat string, providerKey string)
| 162 | // // Without suffix - uses body config |
| 163 | // result, err := thinking.ApplyThinking(body, "gemini-2.5-pro", "gemini", "gemini", "gemini") |
| 164 | func ApplyThinking(body []byte, model string, fromFormat string, toFormat string, providerKey string) ([]byte, error) { |
| 165 | providerFormat := strings.ToLower(strings.TrimSpace(toFormat)) |
| 166 | providerKey = strings.ToLower(strings.TrimSpace(providerKey)) |
| 167 | if providerKey == "" { |
| 168 | providerKey = providerFormat |
| 169 | } |
| 170 | fromFormat = strings.ToLower(strings.TrimSpace(fromFormat)) |
| 171 | if fromFormat == "" { |
| 172 | fromFormat = providerFormat |
| 173 | } |
| 174 | // 1. Route check: Get provider applier |
| 175 | applier := GetProviderApplier(providerFormat) |
| 176 | if applier == nil { |
| 177 | log.WithFields(log.Fields{ |
| 178 | "provider": providerFormat, |
| 179 | "model": model, |
| 180 | }).Debug("thinking: unknown provider, passthrough |") |
| 181 | return body, nil |
| 182 | } |
| 183 | |
| 184 | // 2. Parse suffix and get modelInfo |
| 185 | suffixResult := ParseSuffix(model) |
| 186 | baseModel := suffixResult.ModelName |
| 187 | // Use provider-specific lookup to handle capability differences across providers. |
| 188 | modelInfo := registry.LookupModelInfo(baseModel, providerKey) |
| 189 | |
| 190 | // 3. Model capability check |
| 191 | // Unknown models are treated as user-defined so thinking config can still be applied. |
| 192 | // The upstream service is responsible for validating the configuration. |
| 193 | if IsUserDefinedModel(modelInfo) { |
| 194 | return applyUserDefinedModel(body, modelInfo, fromFormat, providerFormat, suffixResult) |
| 195 | } |
| 196 | if modelInfo.Thinking == nil { |
| 197 | config := extractThinkingConfig(body, providerFormat) |
| 198 | if hasThinkingConfig(config) { |
| 199 | log.WithFields(log.Fields{ |
| 200 | "model": baseModel, |
| 201 | "provider": providerFormat, |
| 202 | }).Debug("thinking: model does not support thinking, stripping config |") |
| 203 | return StripThinkingConfig(body, providerFormat), nil |
| 204 | } |
| 205 | log.WithFields(log.Fields{ |
| 206 | "provider": providerFormat, |
| 207 | "model": baseModel, |
| 208 | }).Debug("thinking: model does not support thinking, passthrough |") |
| 209 | return body, nil |
| 210 | } |
| 211 | |
| 212 | // 4. Get config: suffix priority over body |
| 213 | var config ThinkingConfig |
| 214 | if suffixResult.HasSuffix { |
| 215 | config = parseSuffixToConfig(suffixResult.RawSuffix, providerFormat, model) |
| 216 | log.WithFields(log.Fields{ |
| 217 | "provider": providerFormat, |
| 218 | "model": model, |
| 219 | "mode": config.Mode, |
| 220 | "budget": config.Budget, |
| 221 | "level": config.Level, |