diff --git a/src/modules/llms/server/anthropic/anthropic.router.ts b/src/modules/llms/server/anthropic/anthropic.router.ts index 5f3b9fabc..72597d5b1 100644 --- a/src/modules/llms/server/anthropic/anthropic.router.ts +++ b/src/modules/llms/server/anthropic/anthropic.router.ts @@ -1,4 +1,5 @@ import * as z from 'zod/v4'; +import { TRPCError } from '@trpc/server'; import { createTRPCRouter, publicProcedure } from '~/server/trpc/trpc.server'; import { env } from '~/server/env'; @@ -131,7 +132,7 @@ export function anthropicAccess(access: AnthropicAccessSchema, apiPath: string, // break for the missing key only on the default host if (!anthropicKey && !(access.anthropicHost || env.ANTHROPIC_API_HOST)) - throw new Error('Missing Anthropic API Key. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing Anthropic API Key. Add it on the UI (Models Setup) or server side (your deployment).' }); // API host let anthropicHost = fixupHost(access.anthropicHost || env.ANTHROPIC_API_HOST || DEFAULT_ANTHROPIC_HOST, apiPath); @@ -141,7 +142,7 @@ export function anthropicAccess(access: AnthropicAccessSchema, apiPath: string, const heliKey = access.heliconeKey || env.HELICONE_API_KEY || false; if (heliKey) { if (!anthropicHost.includes(DEFAULT_ANTHROPIC_HOST) && !anthropicHost.includes(DEFAULT_HELICONE_ANTHROPIC_HOST)) - throw new Error(`The Helicone Anthropic Key has been provided, but the host is set to custom. Please fix it in the Models Setup page.`); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'The Helicone Anthropic Key has been provided, but the host is set to custom. Please fix it in the Models Setup page.' }); anthropicHost = `https://${DEFAULT_HELICONE_ANTHROPIC_HOST}`; } diff --git a/src/modules/llms/server/gemini/gemini.router.ts b/src/modules/llms/server/gemini/gemini.router.ts index 69f33de1c..93bb93473 100644 --- a/src/modules/llms/server/gemini/gemini.router.ts +++ b/src/modules/llms/server/gemini/gemini.router.ts @@ -1,4 +1,5 @@ import * as z from 'zod/v4'; +import { TRPCError } from '@trpc/server'; import { env } from '~/server/env'; import packageJson from '../../../../../package.json'; @@ -36,7 +37,7 @@ export function geminiAccess(access: GeminiAccessSchema, modelRefId: string | nu // update model-dependent paths if (apiPath.includes('{model=models/*}')) { if (!modelRefId) - throw new Error(`geminiAccess: modelRefId is required for ${apiPath}`); + throw new TRPCError({ code: 'BAD_REQUEST', message: `geminiAccess: modelRefId is required for ${apiPath}` }); apiPath = apiPath.replace('{model=models/*}', modelRefId); } diff --git a/src/modules/llms/server/openai/openai.router.ts b/src/modules/llms/server/openai/openai.router.ts index 9236454e1..9ff4609da 100644 --- a/src/modules/llms/server/openai/openai.router.ts +++ b/src/modules/llms/server/openai/openai.router.ts @@ -542,7 +542,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu alibabaOaiKey = getRandomKeyFromMultiKey(alibabaOaiKey); if (!alibabaOaiKey || !alibabaOaiHost) - throw new Error('Missing Alibaba API Key. Add it on the UI or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing Alibaba API Key. Add it on the UI or server side (your deployment).' }); return { headers: { @@ -567,7 +567,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu deepseekKey = getRandomKeyFromMultiKey(deepseekKey); if (!deepseekKey || !deepseekHost) - throw new Error('Missing Deepseek API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing Deepseek API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).' }); return { headers: { @@ -585,7 +585,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu let oaiHost = fixupHost(access.oaiHost || env.OPENAI_API_HOST || DEFAULT_OPENAI_HOST, apiPath); // warn if no key - only for default (non-overridden) hosts if (!oaiKey && oaiHost.indexOf(DEFAULT_OPENAI_HOST) !== -1) - throw new Error('Missing OpenAI API Key. Add it on the UI or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing OpenAI API Key. Add it on the UI or server side (your deployment).' }); // [Helicone] // We don't change the host (as we do on Anthropic's), as we expect the user to have a custom host. @@ -607,11 +607,11 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu // The expected path should be: /v1/// if (pathSegments.length < 3 || pathSegments.length > 4 || pathSegments[0] !== 'v1') - throw new Error('Cloudflare AI Gateway API Host is not valid. Please check the API Host field in the Models Setup page.'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Cloudflare AI Gateway API Host is not valid. Please check the API Host field in the Models Setup page.' }); const [_v1, accountTag, gatewayName, provider] = pathSegments; if (provider && provider !== 'openai') - throw new Error('Cloudflare AI Gateway only supports OpenAI as a provider.'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Cloudflare AI Gateway only supports OpenAI as a provider.' }); if (apiPath.startsWith('/v1')) apiPath = apiPath.replace('/v1', ''); @@ -638,7 +638,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu groqKey = getRandomKeyFromMultiKey(groqKey); if (!groqKey) - throw new Error('Missing Groq API Key. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing Groq API Key. Add it on the UI (Models Setup) or server side (your deployment).' }); return { headers: { @@ -683,7 +683,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu case 'openpipe': const openPipeKey = access.oaiKey || env.OPENPIPE_API_KEY || ''; if (!openPipeKey) - throw new Error('Missing OpenPipe API Key or Host. Add it on the UI or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing OpenPipe API Key or Host. Add it on the UI or server side (your deployment).' }); return { headers: { @@ -703,7 +703,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu orKey = getRandomKeyFromMultiKey(orKey); if (!orKey || !orHost) - throw new Error('Missing OpenRouter API Key or Host. Add it on the UI or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing OpenRouter API Key or Host. Add it on the UI or server side (your deployment).' }); return { headers: { @@ -723,7 +723,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu perplexityKey = getRandomKeyFromMultiKey(perplexityKey); if (!perplexityKey || !perplexityHost) - throw new Error('Missing Perplexity API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing Perplexity API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).' }); if (apiPath.startsWith('/v1')) apiPath = apiPath.replace('/v1', ''); @@ -746,7 +746,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu togetherKey = getRandomKeyFromMultiKey(togetherKey); if (!togetherKey || !togetherHost) - throw new Error('Missing TogetherAI API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing TogetherAI API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).' }); return { headers: { @@ -765,7 +765,8 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu xaiKey = getRandomKeyFromMultiKey(xaiKey); if (!xaiKey) - throw new Error('Missing xAI API Key. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new TRPCError({ code: 'BAD_REQUEST', message: 'Missing xAI API Key. Add it on the UI (Models Setup) or server side (your deployment).' }); + return { headers: { 'Content-Type': 'application/json',