fix: route all providers through proxy to patch thinking.type for Claude 4
Opus 4.7 requires thinking.type="adaptive" instead of "enabled". The CLI sends "enabled" which upstream APIs reject. Now all providers (including Anthropic-format) route through the proxy, which patches the thinking parameter before forwarding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,17 @@ import { openaiChatStreamToAnthropic } from './streaming/openaiChatStreamToAnthr
|
||||
import { openaiResponsesStreamToAnthropic } from './streaming/openaiResponsesStreamToAnthropic.js'
|
||||
import type { AnthropicRequest } from './transform/types.js'
|
||||
|
||||
/**
|
||||
* Patch thinking parameter for newer Claude models (4.x / Opus 4.7+).
|
||||
* These models require `thinking.type = "adaptive"` instead of `"enabled"`.
|
||||
*/
|
||||
function patchThinkingForModel(body: AnthropicRequest): void {
|
||||
if (!body.thinking) return
|
||||
if (body.thinking.type === 'enabled') {
|
||||
body.thinking.type = 'adaptive'
|
||||
}
|
||||
}
|
||||
|
||||
const providerService = new ProviderService()
|
||||
|
||||
export async function handleProxyRequest(req: Request, url: URL): Promise<Response> {
|
||||
@@ -53,21 +64,6 @@ export async function handleProxyRequest(req: Request, url: URL): Promise<Respon
|
||||
)
|
||||
}
|
||||
|
||||
if (config.apiFormat === 'anthropic') {
|
||||
return Response.json(
|
||||
{
|
||||
type: 'error',
|
||||
error: {
|
||||
type: 'invalid_request_error',
|
||||
message: providerId
|
||||
? `Provider "${providerId}" uses anthropic format — proxy not needed`
|
||||
: 'Active provider uses anthropic format — proxy not needed',
|
||||
},
|
||||
},
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
let body: AnthropicRequest
|
||||
try {
|
||||
@@ -82,8 +78,13 @@ export async function handleProxyRequest(req: Request, url: URL): Promise<Respon
|
||||
const isStream = body.stream === true
|
||||
const baseUrl = config.baseUrl.replace(/\/+$/, '')
|
||||
|
||||
// Always patch thinking parameter for newer models
|
||||
patchThinkingForModel(body)
|
||||
|
||||
try {
|
||||
if (config.apiFormat === 'openai_chat') {
|
||||
if (config.apiFormat === 'anthropic') {
|
||||
return await handleAnthropicPassthrough(req, body, baseUrl, config.apiKey, isStream)
|
||||
} else if (config.apiFormat === 'openai_chat') {
|
||||
return await handleOpenaiChat(body, baseUrl, config.apiKey, isStream)
|
||||
} else {
|
||||
return await handleOpenaiResponses(body, baseUrl, config.apiKey, isStream)
|
||||
@@ -103,6 +104,50 @@ export async function handleProxyRequest(req: Request, url: URL): Promise<Respon
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAnthropicPassthrough(
|
||||
originalReq: Request,
|
||||
body: AnthropicRequest,
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
isStream: boolean,
|
||||
): Promise<Response> {
|
||||
const url = `${baseUrl}/v1/messages`
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': apiKey,
|
||||
'anthropic-version': originalReq.headers.get('anthropic-version') || '2023-06-01',
|
||||
}
|
||||
const authHeader = originalReq.headers.get('authorization')
|
||||
if (authHeader) {
|
||||
headers['Authorization'] = authHeader
|
||||
}
|
||||
|
||||
const upstream = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
signal: AbortSignal.timeout(600_000),
|
||||
})
|
||||
|
||||
if (isStream && upstream.ok && upstream.body) {
|
||||
return new Response(upstream.body, {
|
||||
status: upstream.status,
|
||||
headers: {
|
||||
'Content-Type': upstream.headers.get('Content-Type') || 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const text = await upstream.text()
|
||||
return new Response(text, {
|
||||
status: upstream.status,
|
||||
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||
})
|
||||
}
|
||||
|
||||
async function handleOpenaiChat(
|
||||
body: AnthropicRequest,
|
||||
baseUrl: string,
|
||||
|
||||
@@ -247,7 +247,7 @@ export class ProviderService {
|
||||
provider: SavedProvider,
|
||||
options?: { proxyPath?: string },
|
||||
): Record<string, string> {
|
||||
const needsProxy = provider.apiFormat != null && provider.apiFormat !== 'anthropic'
|
||||
const needsProxy = provider.apiFormat != null
|
||||
const proxyPath = options?.proxyPath ?? '/proxy'
|
||||
const baseUrl = needsProxy
|
||||
? `http://127.0.0.1:${ProviderService.serverPort}${proxyPath}`
|
||||
|
||||
Reference in New Issue
Block a user