Преглед изворни кода

Make AI features work with Claude 4.7 and newer models (#517)

Bernt Christian Egeland пре 1 дан
родитељ
комит
ffaea4cdea
2 измењених фајлова са 50 додато и 5 уклоњено
  1. 33 5
      src/__tests__/lib/ai-completion-tuning.test.ts
  2. 17 0
      src/lib/ai.ts

+ 33 - 5
src/__tests__/lib/ai-completion-tuning.test.ts

@@ -17,11 +17,39 @@ import { completionTuning } from '@/lib/ai'
 const config = (provider: string, model: string) => ({ provider, apiKey: 'key', model })
 
 describe('completionTuning', () => {
-  it('keeps max_tokens and temperature for the anthropic endpoint', () => {
-    expect(completionTuning(config('anthropic', 'claude-sonnet-4-6'), 2000, 0.7)).toEqual({
-      max_tokens: 2000,
-      temperature: 0.7,
-    })
+  it('keeps max_tokens and temperature for the Claude models that take one', () => {
+    for (const model of [
+      'claude-sonnet-4-6',
+      'claude-haiku-4-5',
+      'claude-haiku-4-5-20251001',
+      'claude-opus-4-1-20250805',
+      'claude-sonnet-4-20250514',
+      'claude-3-5-sonnet-20241022',
+    ]) {
+      expect(completionTuning(config('anthropic', model), 2000, 0.7), model).toEqual({
+        max_tokens: 2000,
+        temperature: 0.7,
+      })
+    }
+  })
+
+  it('sends no temperature to newer Claude models, which answer it with a 400', () => {
+    // Reproduced on claude-haiku-5-5: "temperature is deprecated for this model",
+    // which failed registration scans and Ask AI.
+    for (const model of [
+      'claude-haiku-5-5',
+      'claude-sonnet-5-5',
+      'claude-opus-5-5',
+      'claude-opus-4-7',
+      'claude-opus-4-8',
+      'claude-fable-5-1',
+      'claude-sonnet-6',
+    ]) {
+      const tuning = completionTuning(config('anthropic', model), 2000, 0.7)
+      expect(tuning, model).not.toHaveProperty('temperature')
+      // They think inside the same cap, so the answer gets headroom on top.
+      expect(tuning.max_tokens, model).toBeGreaterThan(2000)
+    }
   })
 
   it('keeps the classic parameters for an OpenAI-compatible server', () => {

+ 17 - 0
src/lib/ai.ts

@@ -83,11 +83,28 @@ function isReasoningModel(model: string): boolean {
   return /^(o\d|gpt-5)/i.test(model)
 }
 
+/**
+ * The Claude models that still take a temperature: the 3.x line and Claude 4
+ * up to 4.6, with or without a date in the id. Claude 4.7 and every later
+ * model (the 5 family, Fable, whatever comes next) answer a temperature with
+ * a 400, so it is only sent to the models listed here. Those later models
+ * also think before they answer, inside the same token cap, so they get the
+ * same headroom as OpenAI's reasoning models.
+ */
+function isLegacyClaude(model: string): boolean {
+  return /^claude-(?:[23]|instant)|^claude-(?:opus|sonnet|haiku)-4(?:-[0-6])?(?:-\d{8})?$/i.test(
+    model
+  )
+}
+
 export function completionTuning(
   config: AiConfig,
   maxTokens: number,
   temperature?: number
 ): { max_tokens?: number; max_completion_tokens?: number; temperature?: number } {
+  if (config.provider === 'anthropic' && !isLegacyClaude(config.model)) {
+    return { max_tokens: maxTokens + REASONING_HEADROOM }
+  }
   if (config.provider !== 'openai') {
     return { max_tokens: maxTokens, ...(temperature !== undefined && { temperature }) }
   }