model-params.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import {
  2. type ModelInfo,
  3. type ProviderSettings,
  4. type VerbosityLevel,
  5. type ReasoningEffortExtended,
  6. ANTHROPIC_DEFAULT_MAX_TOKENS,
  7. } from "@roo-code/types"
  8. import {
  9. DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS,
  10. DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS,
  11. GEMINI_25_PRO_MIN_THINKING_TOKENS,
  12. shouldUseReasoningBudget,
  13. shouldUseReasoningEffort,
  14. getModelMaxOutputTokens,
  15. } from "../../shared/api"
  16. import {
  17. type AnthropicReasoningParams,
  18. type OpenAiReasoningParams,
  19. type GeminiReasoningParams,
  20. type OpenRouterReasoningParams,
  21. getAnthropicReasoning,
  22. getOpenAiReasoning,
  23. getGeminiReasoning,
  24. getOpenRouterReasoning,
  25. } from "./reasoning"
  26. type Format = "anthropic" | "openai" | "gemini" | "openrouter"
  27. type GetModelParamsOptions<T extends Format> = {
  28. format: T
  29. modelId: string
  30. model: ModelInfo
  31. settings: ProviderSettings
  32. defaultTemperature?: number
  33. }
  34. type BaseModelParams = {
  35. maxTokens: number | undefined
  36. temperature: number | undefined
  37. reasoningEffort: ReasoningEffortExtended | undefined
  38. reasoningBudget: number | undefined
  39. verbosity: VerbosityLevel | undefined
  40. tools?: boolean
  41. }
  42. type AnthropicModelParams = {
  43. format: "anthropic"
  44. reasoning: AnthropicReasoningParams | undefined
  45. } & BaseModelParams
  46. type OpenAiModelParams = {
  47. format: "openai"
  48. reasoning: OpenAiReasoningParams | undefined
  49. } & BaseModelParams
  50. type GeminiModelParams = {
  51. format: "gemini"
  52. reasoning: GeminiReasoningParams | undefined
  53. } & BaseModelParams
  54. type OpenRouterModelParams = {
  55. format: "openrouter"
  56. reasoning: OpenRouterReasoningParams | undefined
  57. } & BaseModelParams
  58. export type ModelParams = AnthropicModelParams | OpenAiModelParams | GeminiModelParams | OpenRouterModelParams
  59. // Function overloads for specific return types
  60. export function getModelParams(options: GetModelParamsOptions<"anthropic">): AnthropicModelParams
  61. export function getModelParams(options: GetModelParamsOptions<"openai">): OpenAiModelParams
  62. export function getModelParams(options: GetModelParamsOptions<"gemini">): GeminiModelParams
  63. export function getModelParams(options: GetModelParamsOptions<"openrouter">): OpenRouterModelParams
  64. export function getModelParams({
  65. format,
  66. modelId,
  67. model,
  68. settings,
  69. defaultTemperature = 0,
  70. }: GetModelParamsOptions<Format>): ModelParams {
  71. const {
  72. modelMaxTokens: customMaxTokens,
  73. modelMaxThinkingTokens: customMaxThinkingTokens,
  74. modelTemperature: customTemperature,
  75. reasoningEffort: customReasoningEffort,
  76. verbosity: customVerbosity,
  77. } = settings
  78. // Use the centralized logic for computing maxTokens
  79. const maxTokens = getModelMaxOutputTokens({
  80. modelId,
  81. model,
  82. settings,
  83. format,
  84. })
  85. let temperature = customTemperature ?? defaultTemperature
  86. let reasoningBudget: ModelParams["reasoningBudget"] = undefined
  87. let reasoningEffort: ModelParams["reasoningEffort"] = undefined
  88. let verbosity: VerbosityLevel | undefined = customVerbosity
  89. if (shouldUseReasoningBudget({ model, settings })) {
  90. // Check if this is a Gemini 2.5 Pro model
  91. const isGemini25Pro = modelId.includes("gemini-2.5-pro")
  92. // If `customMaxThinkingTokens` is not specified use the default.
  93. // For Gemini 2.5 Pro, default to 128 instead of 8192
  94. const defaultThinkingTokens = isGemini25Pro
  95. ? GEMINI_25_PRO_MIN_THINKING_TOKENS
  96. : DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS
  97. reasoningBudget = customMaxThinkingTokens ?? defaultThinkingTokens
  98. // Reasoning cannot exceed 80% of the `maxTokens` value.
  99. // maxTokens should always be defined for reasoning budget models, but add a guard just in case
  100. if (maxTokens && reasoningBudget > Math.floor(maxTokens * 0.8)) {
  101. reasoningBudget = Math.floor(maxTokens * 0.8)
  102. }
  103. // Reasoning cannot be less than minimum tokens.
  104. // For Gemini 2.5 Pro models, the minimum is 128 tokens
  105. // For other models, the minimum is 1024 tokens
  106. const minThinkingTokens = isGemini25Pro ? GEMINI_25_PRO_MIN_THINKING_TOKENS : 1024
  107. if (reasoningBudget < minThinkingTokens) {
  108. reasoningBudget = minThinkingTokens
  109. }
  110. // Let's assume that "Hybrid" reasoning models require a temperature of
  111. // 1.0 since Anthropic does.
  112. temperature = 1.0
  113. } else if (shouldUseReasoningEffort({ model, settings })) {
  114. // "Traditional" reasoning models use the `reasoningEffort` parameter.
  115. const effort = (customReasoningEffort ?? model.reasoningEffort) as
  116. | ReasoningEffortExtended
  117. | "disable"
  118. | undefined
  119. // Capability and settings checks are handled by shouldUseReasoningEffort.
  120. // Here we simply propagate the resolved effort into the params, while
  121. // still treating "disable" as an omission.
  122. if (effort && effort !== "disable") {
  123. reasoningEffort = effort as ReasoningEffortExtended
  124. }
  125. }
  126. const params: BaseModelParams = { maxTokens, temperature, reasoningEffort, reasoningBudget, verbosity }
  127. if (format === "anthropic") {
  128. return {
  129. format,
  130. ...params,
  131. reasoning: getAnthropicReasoning({ model, reasoningBudget, reasoningEffort, settings }),
  132. }
  133. } else if (format === "openai") {
  134. // Special case for o1 and o3-mini, which don't support temperature.
  135. // TODO: Add a `supportsTemperature` field to the model info.
  136. if (modelId.startsWith("o1") || modelId.startsWith("o3-mini")) {
  137. params.temperature = undefined
  138. }
  139. return {
  140. format,
  141. ...params,
  142. reasoning: getOpenAiReasoning({ model, reasoningBudget, reasoningEffort, settings }),
  143. tools: model.supportsNativeTools,
  144. }
  145. } else if (format === "gemini") {
  146. return {
  147. format,
  148. ...params,
  149. reasoning: getGeminiReasoning({ model, reasoningBudget, reasoningEffort, settings }),
  150. }
  151. } else {
  152. // Special case for o1-pro, which doesn't support temperature.
  153. // Note that OpenRouter's `supported_parameters` field includes
  154. // `temperature`, which is probably a bug.
  155. // TODO: Add a `supportsTemperature` field to the model info and populate
  156. // it appropriately in the OpenRouter fetcher.
  157. if (modelId === "openai/o1-pro") {
  158. params.temperature = undefined
  159. }
  160. return {
  161. format,
  162. ...params,
  163. reasoning: getOpenRouterReasoning({ model, reasoningBudget, reasoningEffort, settings }),
  164. }
  165. }
  166. }