Files
big-agi/src/modules/backend/backend.router.ts
T

102 lines
4.1 KiB
TypeScript

import * as z from 'zod/v4';
import { Release } from '~/common/app.release';
import { createTRPCRouter, publicProcedure } from '~/server/trpc/trpc.server';
import { env } from '~/server/env.server';
import { fetchJsonOrTRPCThrow } from '~/server/trpc/trpc.router.fetchers';
// critical to make sure we `import type` here
import type { BackendCapabilities } from './store-backend-capabilities';
function sdbmHash(str: string): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = char + (hash << 6) + (hash << 16) - hash;
}
// Convert to unsigned 32-bit integer and then to hex string
return (hash >>> 0).toString(16);
}
function generateLlmEnvConfigHash(env: Record<string, unknown>): string {
const envAPIKeys = Object.keys(env) // get all env keys
.filter(key => !!env[key]) // minus the empty
.filter(key => key.includes('_API_')) // minus the non-API keys
.map(key => `${key}=${env[key]}`) // create key-value pairs
.sort(); // ignore order
const hashInputs = [
Release.Monotonics.Aix.toString(), // triggers at every change (large downstream effect, know what you are doing)
Release.TenantSlug.toString(), // triggers when branch changes
...envAPIKeys, // triggers when env keys change
];
return sdbmHash(hashInputs.join(';'));
}
/**
* This is the primary router for the backend. Mainly, this deals with letting
* the frontend know what capabilities are available, by virtue of being
* pre-configured in the servr. In the future this will evolve to a better
* server-side configuration system.
*/
export const backendRouter = createTRPCRouter({
/* List server-side capabilities (pre-configured by the deployer) */
listCapabilities: publicProcedure
.query(async ({ ctx: _unused }): Promise<BackendCapabilities> => {
return {
// llms
hasLlmAlibaba: !!env.ALIBABA_API_KEY || !!env.ALIBABA_API_HOST,
hasLlmAnthropic: !!env.ANTHROPIC_API_KEY,
hasLlmAzureOpenAI: !!env.AZURE_OPENAI_API_KEY && !!env.AZURE_OPENAI_API_ENDPOINT,
hasLlmBedrock: !!env.BEDROCK_BEARER_TOKEN || (!!env.BEDROCK_ACCESS_KEY_ID && !!env.BEDROCK_SECRET_ACCESS_KEY),
hasLlmDeepseek: !!env.DEEPSEEK_API_KEY,
hasLlmGemini: !!env.GEMINI_API_KEY,
hasLlmGroq: !!env.GROQ_API_KEY,
hasLlmLocalAIHost: !!env.LOCALAI_API_HOST,
hasLlmLocalAIKey: !!env.LOCALAI_API_KEY,
hasLlmMistral: !!env.MISTRAL_API_KEY,
hasLlmMoonshot: !!env.MOONSHOT_API_KEY,
hasLlmOllama: !!env.OLLAMA_API_HOST,
hasLlmOpenAI: !!env.OPENAI_API_KEY || !!env.OPENAI_API_HOST,
hasLlmOpenPipe: !!env.OPENPIPE_API_KEY,
hasLlmOpenRouter: !!env.OPENROUTER_API_KEY,
hasLlmPerplexity: !!env.PERPLEXITY_API_KEY,
hasLlmTogetherAI: !!env.TOGETHERAI_API_KEY,
hasLlmXAI: !!env.XAI_API_KEY,
// others
hasDB: (!!env.MDB_URI) || (!!env.POSTGRES_PRISMA_URL && !!env.POSTGRES_URL_NON_POOLING),
hasBrowsing: !!env.PUPPETEER_WSS_ENDPOINT,
hasGoogleCustomSearch: !!env.GOOGLE_CSE_ID && !!env.GOOGLE_CLOUD_API_KEY,
hasVoiceElevenLabs: !!env.ELEVENLABS_API_KEY,
// hashes
hashLlmReconfig: generateLlmEnvConfigHash(env),
// build data
build: Release.buildInfo('backend'),
};
}),
// The following are used for various OAuth integrations
/**
* Exchange the OpenRouter authorization code for an OpenRouter API Key
* Reference: https://openrouter.ai/docs/quickstart#oauth
*/
exchangeOpenRouterKey: publicProcedure
.input(z.object({ code: z.string() }))
.query(async ({ input }) => {
// Documented here: https://openrouter.ai/docs#oauth
return await fetchJsonOrTRPCThrow<{ key: string }, { code: string }>({
url: 'https://openrouter.ai/api/v1/auth/keys',
method: 'POST',
headers: { 'Content-Type': 'application/json' }, // important to fix 400 error
body: { code: input.code },
name: 'Backend.exchangeOpenRouterKey',
});
}),
});