From 229f98b55a8aa9c41754639396a3ff756b7e32b1 Mon Sep 17 00:00:00 2001 From: Manu Goyal Date: Wed, 4 Dec 2024 11:46:29 -0800 Subject: [PATCH] Custom error type for bad request errors. --- packages/proxy/src/proxy.ts | 29 +++++++++++++++++------------ packages/proxy/src/util.ts | 6 ++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/proxy/src/proxy.ts b/packages/proxy/src/proxy.ts index 396c5d8..547d94c 100644 --- a/packages/proxy/src/proxy.ts +++ b/packages/proxy/src/proxy.ts @@ -13,6 +13,7 @@ import { APISecret, } from "@schema"; import { + ProxyBadRequestError, flattenChunks, flattenChunksArray, getRandomInt, @@ -171,7 +172,7 @@ export async function proxyV1({ const authToken = parseAuthHeader(proxyHeaders); if (!authToken) { - throw new Error("Missing Authentication header"); + throw new ProxyBadRequestError("Missing Authentication header"); } // Caching is enabled by default, but let the user disable it @@ -363,7 +364,7 @@ export async function proxyV1({ } if (streamFormat === "vercel-ai" && !isStreaming) { - throw new Error( + throw new ProxyBadRequestError( "Vercel AI format requires the stream parameter to be set to true", ); } @@ -778,7 +779,7 @@ async function fetchModelLoop( endpointUrl = "/completions"; break; default: - throw new Error( + throw new ProxyBadRequestError( `Unsupported model ${model} (must be chat or completion for /auto endpoint)`, ); } @@ -895,7 +896,7 @@ async function fetchModelLoop( if (lastException) { throw lastException; } else { - throw new Error( + throw new ProxyBadRequestError( `No API keys found (for ${model}). You can configure API secrets at https://www.braintrust.dev/app/settings?subroute=secrets`, ); } @@ -955,7 +956,7 @@ async function fetchModel( console.assert(method === "POST"); return await fetchGoogle("POST", url, headers, bodyData, secret); default: - throw new Error(`Unsupported model provider ${format}`); + throw new ProxyBadRequestError(`Unsupported model provider ${format}`); } } @@ -973,7 +974,7 @@ async function fetchOpenAI( secret.metadata.api_base) || EndpointProviderToBaseURL[secret.type]; if (baseURL === null) { - throw new Error( + throw new ProxyBadRequestError( `Unsupported provider ${secret.name} (${secret.type}) (must specify base url)`, ); } @@ -989,7 +990,7 @@ async function fetchOpenAI( model.replace("gpt-3.5", "gpt-35"), )}`; } else { - throw new Error( + throw new ProxyBadRequestError( `Azure provider ${secret.id} must have a deployment or model specified`, ); } @@ -1180,7 +1181,9 @@ async function fetchAnthropic( headers["x-api-key"] = secret.secret; if (isEmpty(bodyData)) { - throw new Error("Anthropic request must have a valid JSON-parsable body"); + throw new ProxyBadRequestError( + "Anthropic request must have a valid JSON-parsable body", + ); } const { @@ -1201,7 +1204,7 @@ async function fetchAnthropic( m.role === "function" || ("function_call" in m && !isEmpty(m.function_call)) ) { - throw new Error( + throw new ProxyBadRequestError( "Anthropic does not support function messages or function_calls", ); } else if (m.role === "tool") { @@ -1217,7 +1220,7 @@ async function fetchAnthropic( !translatedRole || !(translatedRole === "user" || translatedRole === "assistant") ) { - throw new Error(`Unsupported Anthropic role ${role}`); + throw new ProxyBadRequestError(`Unsupported Anthropic role ${role}`); } messages.push({ @@ -1332,7 +1335,9 @@ async function fetchGoogle( console.assert(url === "/chat/completions"); if (isEmpty(bodyData)) { - throw new Error("Google request must have a valid JSON-parsable body"); + throw new ProxyBadRequestError( + "Google request must have a valid JSON-parsable body", + ); } const { @@ -1522,7 +1527,7 @@ function parseEnumHeader( ): (typeof headerTypes)[number] { const header = value && value.toLowerCase(); if (header && !headerTypes.includes(header as T)) { - throw new Error( + throw new ProxyBadRequestError( `Invalid ${headerName} header '${header}'. Must be one of ${headerTypes.join( ", ", )}`, diff --git a/packages/proxy/src/util.ts b/packages/proxy/src/util.ts index 36799bd..be9a748 100644 --- a/packages/proxy/src/util.ts +++ b/packages/proxy/src/util.ts @@ -52,3 +52,9 @@ export function isEmpty(a: any): a is null | undefined { export function getRandomInt(max: number) { return Math.floor(Math.random() * max); } + +export class ProxyBadRequestError extends Error { + constructor(public message: string) { + super(message); + } +}