getcursor / cursor

The AI Code Editor
https://cursor.com
25.16k stars 1.57k forks source link

Fails when Using Claude Models #1647

Open labolado opened 2 months ago

labolado commented 2 months ago

When I use a custom OpenAI base URL and OpenAI key, an error occurs if my model name uses the Claude-related model name (claude-3-5-sonnet-20240620): You're using your own API key, and we got the following error from Anthropic: {"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}}

PS: I've disabled the Anthropic Api Key.

版本: 0.39.5 提交: 1.91.1 日期: 274e2e5d572bd0b99449183635a0f94c7b9f54d0 Electron: 2024-08-14T15:49:16.270Z ElectronBuildId: 29.4.0 Chromium: undefined Node.js: 122.0.6261.156 V8: 20.9.0 OS: 12.2.281.27-electron.0

jkryanchou commented 2 months ago

I have met the same issue. Is there any solution on it?

labolado commented 2 months ago

I have met the same issue. Is there any solution on it?

There is no solution yet.

labolado commented 2 months ago

Use Cloudflare Workers to proxy your API and alias the model name.


addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

const BASE_URL = 'https://my_api_url'
const MODEL_ALIASES = {
  'mymodel': 'claude-3-5-sonnet-20240620'
}

async function handleRequest(request) {
  const url = new URL(request.url)
  const path = url.pathname
  const queryParams = url.search

  console.log(`Received request: ${request.method} ${path}`)

  if (request.method === 'GET' && path === '/') {
    return new Response("Claude API Proxy is running!", { status: 200 })
  }

  if ((request.method === 'POST' && path === '/v1/chat/completions') ||
      (request.method === 'GET' && path === '/v1/models')) {
    return await proxyRequest(request, path, queryParams)
  }

  console.log(`Unhandled route: ${path}`)
  return new Response(JSON.stringify({ error: 'Not Found', path: path }), { 
    status: 404,
    headers: { 'Content-Type': 'application/json' }
  })
}

async function proxyRequest(request, path, queryParams) {
  const authHeader = request.headers.get('Authorization')
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    console.log('Missing or invalid Authorization header')
    return new Response(JSON.stringify({ error: 'Valid Authorization header is required' }), { 
      status: 401,
      headers: { 'Content-Type': 'application/json' }
    })
  }

  const apiKey = authHeader.split(' ')[1]

  const url = BASE_URL + path + queryParams
  const headers = {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }

  let body = request.body
  if (path === '/v1/chat/completions' && request.method === 'POST') {
    const requestData = await request.json()
    console.log(`Request data: ${JSON.stringify(requestData)}`)
    if (requestData.model && MODEL_ALIASES[requestData.model]) {
      requestData.model = MODEL_ALIASES[requestData.model]
    }
    body = JSON.stringify(requestData)
  }

  try {
    console.log(`Sending request to: ${url}`)
    const response = await fetch(url, {
      method: request.method,
      headers: headers,
      body: body
    })

    const responseData = await response.text()
    console.log(`Received response: ${responseData}`)
    return new Response(responseData, {
      status: response.status,
      headers: {
        'Content-Type': 'application/json'
      }
    })
  } catch (error) {
    console.error(`Error: ${error.message}`)
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: {
        'Content-Type': 'application/json'
      }
    })
  }
}
SKDDJ commented 1 month ago

Use Cloudflare Workers to proxy your API and alias the model name.


addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

const BASE_URL = 'https://my_api_url'
const MODEL_ALIASES = {
  'mymodel': 'claude-3-5-sonnet-20240620'
}

async function handleRequest(request) {
  const url = new URL(request.url)
  const path = url.pathname
  const queryParams = url.search

  console.log(`Received request: ${request.method} ${path}`)

  if (request.method === 'GET' && path === '/') {
    return new Response("Claude API Proxy is running!", { status: 200 })
  }

  if ((request.method === 'POST' && path === '/v1/chat/completions') ||
      (request.method === 'GET' && path === '/v1/models')) {
    return await proxyRequest(request, path, queryParams)
  }

  console.log(`Unhandled route: ${path}`)
  return new Response(JSON.stringify({ error: 'Not Found', path: path }), { 
    status: 404,
    headers: { 'Content-Type': 'application/json' }
  })
}

async function proxyRequest(request, path, queryParams) {
  const authHeader = request.headers.get('Authorization')
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    console.log('Missing or invalid Authorization header')
    return new Response(JSON.stringify({ error: 'Valid Authorization header is required' }), { 
      status: 401,
      headers: { 'Content-Type': 'application/json' }
    })
  }

  const apiKey = authHeader.split(' ')[1]

  const url = BASE_URL + path + queryParams
  const headers = {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }

  let body = request.body
  if (path === '/v1/chat/completions' && request.method === 'POST') {
    const requestData = await request.json()
    console.log(`Request data: ${JSON.stringify(requestData)}`)
    if (requestData.model && MODEL_ALIASES[requestData.model]) {
      requestData.model = MODEL_ALIASES[requestData.model]
    }
    body = JSON.stringify(requestData)
  }

  try {
    console.log(`Sending request to: ${url}`)
    const response = await fetch(url, {
      method: request.method,
      headers: headers,
      body: body
    })

    const responseData = await response.text()
    console.log(`Received response: ${responseData}`)
    return new Response(responseData, {
      status: response.status,
      headers: {
        'Content-Type': 'application/json'
      }
    })
  } catch (error) {
    console.error(`Error: ${error.message}`)
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: {
        'Content-Type': 'application/json'
      }
    })
  }
}

Hi @labolado, may I ask which custom API do you use—-CloseAI, OpenRoute, or others? I use CloseAI, but I'm encountering issues with Claude with your script. Could you provide more information? Thanks.