From cfbbfa51689a971b9be83d93e2946cf2f7c9cc9f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 15:11:28 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(docs):=20stop=20the=20Groq?= =?UTF-8?q?=20client=20from=20failing=20`next=20build`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `next build` evaluates every route module while collecting page data, so the module-scope `new ChatGroq()` in `src/lib/ai/providers.ts` threw "Groq API key not found" on any build machine without `GROQ_API_KEY`, failing the Vercel build at "Collecting page data for /api/chat". - Construct the chat and embedding models lazily, on first use, and cache them. A missing key now surfaces as a `MissingApiKeyError` at request time instead of at import time. - `/api/chat` answers 503 with that message rather than crashing when the key is absent. - Declare `GROQ_API_KEY` and `OPENAI_API_KEY` in the `build` task's `env` so turbo passes the platform environment variables through (turbo warned that it was dropping them). - Declare `@radix-ui/react-tabs`, which `components/ui/tabs.tsx` imports directly but which was only ever resolved transitively; a cold install fails to compile without it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Qk1775Kp6y6mLQXosPUght --- apps/docs/package.json | 1 + apps/docs/src/app/api/chat/route.ts | 12 +++++++- apps/docs/src/lib/ai/providers.ts | 48 ++++++++++++++++++++++++----- turbo.json | 1 + 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/apps/docs/package.json b/apps/docs/package.json index 8f4b304b..b175ce2f 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -39,6 +39,7 @@ "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-hover-card": "^1.1.15", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.21", "@radix-ui/react-tooltip": "^1.2.8", "@shikijs/rehype": "^4.0.0", "@shikijs/transformers": "^4.0.0", diff --git a/apps/docs/src/app/api/chat/route.ts b/apps/docs/src/app/api/chat/route.ts index 59820ad8..8b62c1b6 100644 --- a/apps/docs/src/app/api/chat/route.ts +++ b/apps/docs/src/app/api/chat/route.ts @@ -1,6 +1,6 @@ import { type CoreMessage } from 'ai' import { toUIMessageStream } from '@ai-sdk/langchain' -import { chatModel } from '@/lib/ai/providers' +import { MissingApiKeyError, getChatModel } from '@/lib/ai/providers' import { provideLinks } from '@/lib/ai/tools/provide-links' import { searchDocs } from '@/lib/ai/tools/search-docs' import { getPageContent } from '@/lib/ai/tools/get-page-content' @@ -49,6 +49,16 @@ function stringifyContent(content: CoreMessage['content']): string { } export async function POST(request: Request) { + let chatModel: ReturnType + try { + chatModel = getChatModel() + } catch (error) { + if (error instanceof MissingApiKeyError) { + return new Response(error.message, { status: 503 }) + } + throw error + } + const { messages }: { messages: CoreMessage[] } = await request.json() const tools = [provideLinks, searchDocs, getPageContent] diff --git a/apps/docs/src/lib/ai/providers.ts b/apps/docs/src/lib/ai/providers.ts index ff0cebba..09b01e3f 100644 --- a/apps/docs/src/lib/ai/providers.ts +++ b/apps/docs/src/lib/ai/providers.ts @@ -1,11 +1,43 @@ import { ChatGroq } from '@langchain/groq' import { OpenAIEmbeddings } from '@langchain/openai' -export const chatModel = new ChatGroq({ - model: 'llama-3.3-70b-versatile', - temperature: 0, -}) - -export const embeddingModel = new OpenAIEmbeddings({ - model: 'text-embedding-3-small', -}) +/** + * Providers are created lazily, on first use. + * + * `new ChatGroq()` / `new OpenAIEmbeddings()` throw when their API key is + * missing, and Next.js evaluates every route module while collecting page data + * during `next build`. Instantiating at module scope therefore fails the build + * on any machine without the keys (CI, Vercel preview builds). Constructing on + * demand keeps the build key-free and moves the failure to the request that + * actually needs the model. + */ + +let cachedChatModel: ChatGroq | undefined +let cachedEmbeddingModel: OpenAIEmbeddings | undefined + +export class MissingApiKeyError extends Error { + constructor(envVar: string) { + super( + `${envVar} is not set. Add it to your environment to enable the docs assistant.` + ) + this.name = 'MissingApiKeyError' + } +} + +export function getChatModel(): ChatGroq { + if (!process.env.GROQ_API_KEY) throw new MissingApiKeyError('GROQ_API_KEY') + cachedChatModel ??= new ChatGroq({ + model: 'llama-3.3-70b-versatile', + temperature: 0, + }) + return cachedChatModel +} + +export function getEmbeddingModel(): OpenAIEmbeddings { + if (!process.env.OPENAI_API_KEY) + throw new MissingApiKeyError('OPENAI_API_KEY') + cachedEmbeddingModel ??= new OpenAIEmbeddings({ + model: 'text-embedding-3-small', + }) + return cachedEmbeddingModel +} diff --git a/turbo.json b/turbo.json index 2c75dfb4..619bd820 100644 --- a/turbo.json +++ b/turbo.json @@ -7,6 +7,7 @@ "tasks": { "build": { "dependsOn": ["^build"], + "env": ["GROQ_API_KEY", "OPENAI_API_KEY"], "outputs": ["dist/**", ".next/**", "!.next/cache/**", "build/**"] }, "dev": {