diff --git a/lib/actions/chat.ts b/lib/actions/chat.ts index 6c5bc745..67a9ca8f 100644 --- a/lib/actions/chat.ts +++ b/lib/actions/chat.ts @@ -21,7 +21,8 @@ import { db } from '@/lib/db' import { users, chats, chatParticipants } from '@/lib/db/schema' import { eq, or, and, sql } from 'drizzle-orm' import { getCurrentUserIdOnServer } from '@/lib/auth/get-current-user' -import { getModel, normalizeMessageContent } from '../utils' +import { normalizeMessageContent } from '../utils' +import { getModel } from '../utils/server' import { executiveSummaryAgent } from '../agents/report/executive-summary' import { strategicSynthesisAgent } from '../agents/report/strategic-synthesis' diff --git a/lib/actions/suggest.ts b/lib/actions/suggest.ts index 8555461c..418868e2 100644 --- a/lib/actions/suggest.ts +++ b/lib/actions/suggest.ts @@ -3,7 +3,7 @@ import { createStreamableUI, createStreamableValue } from 'ai/rsc' import { CoreMessage, LanguageModel, streamObject } from 'ai' import { PartialRelated, relatedSchema } from '@/lib/schema/related' -import { getModel } from '../utils' +import { getModel } from '../utils/server' import { MapData } from '@/components/map/map-data-context' export async function getSuggestions( diff --git a/lib/actions/system-prompt.ts b/lib/actions/system-prompt.ts index ae933a99..859e8978 100644 --- a/lib/actions/system-prompt.ts +++ b/lib/actions/system-prompt.ts @@ -4,7 +4,7 @@ import { getCurrentUserIdOnServer } from '@/lib/auth/get-current-user'; import { z } from 'zod'; import { createJob, updateJob, getJobByIdAndUserId } from './system-prompt-db'; import { getFirecrawlClient } from '@/lib/agents/tools/firecrawl'; -import { getModel } from '@/lib/utils'; +import { getModel } from '@/lib/utils/server'; import { generateText } from 'ai'; const domainSchema = z.string().min(1).refine((val) => { diff --git a/lib/agents/inquire.tsx b/lib/agents/inquire.tsx index 9026bbc0..f781708e 100644 --- a/lib/agents/inquire.tsx +++ b/lib/agents/inquire.tsx @@ -2,7 +2,7 @@ import { Copilot } from '@/components/copilot'; import { createStreamableUI, createStreamableValue } from 'ai/rsc'; import { CoreMessage, LanguageModel, streamObject } from 'ai'; import { PartialInquiry, inquirySchema } from '@/lib/schema/inquiry'; -import { getModel } from '../utils'; +import { getModel } from '../utils/server'; // Define a plain object type for the inquiry prop interface InquiryProp { diff --git a/lib/agents/query-suggestor.tsx b/lib/agents/query-suggestor.tsx index 7cb8e50c..db36ecc3 100644 --- a/lib/agents/query-suggestor.tsx +++ b/lib/agents/query-suggestor.tsx @@ -3,7 +3,7 @@ import { CoreMessage, LanguageModel, streamObject } from 'ai' import { PartialRelated, relatedSchema } from '@/lib/schema/related' import { Section } from '@/components/section' import SearchRelated from '@/components/search-related' -import { getModel } from '../utils' +import { getModel } from '../utils/server' interface CacheEntry { data: PartialRelated; diff --git a/lib/agents/report/executive-summary.ts b/lib/agents/report/executive-summary.ts index b2512bc2..573ce37e 100644 --- a/lib/agents/report/executive-summary.ts +++ b/lib/agents/report/executive-summary.ts @@ -1,5 +1,5 @@ import { generateText } from 'ai' -import { getModel } from '@/lib/utils' +import { getModel } from '@/lib/utils/server' export async function executiveSummaryAgent(crossSessionContext: string, activeMessages: any[]) { try { diff --git a/lib/agents/report/strategic-synthesis.ts b/lib/agents/report/strategic-synthesis.ts index 6e86411f..ad28a1ae 100644 --- a/lib/agents/report/strategic-synthesis.ts +++ b/lib/agents/report/strategic-synthesis.ts @@ -1,5 +1,5 @@ import { generateText } from 'ai' -import { getModel } from '@/lib/utils' +import { getModel } from '@/lib/utils/server' export async function strategicSynthesisAgent(sensorFusionFindings: any[], strategicContent: any[]) { try { diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 81d00fa8..ca7fc2e7 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -10,7 +10,7 @@ import { import { Section } from '@/components/section' import { BotMessage } from '@/components/message' import { getTools } from './tools' -import { getModel } from '../utils' +import { getModel } from '../utils/server' import { MapProvider } from '@/lib/store/settings' import { DrawnFeature } from './resolution-search' diff --git a/lib/agents/resolution-search.tsx b/lib/agents/resolution-search.tsx index 3615c6e0..0bbe3c1c 100644 --- a/lib/agents/resolution-search.tsx +++ b/lib/agents/resolution-search.tsx @@ -1,5 +1,5 @@ import { CoreMessage, streamObject } from 'ai' -import { getModel } from '@/lib/utils' +import { getModel } from '@/lib/utils/server' import { tavily } from '@tavily/core' import { resolutionSearchSchema } from '@/lib/schema/resolution-search' diff --git a/lib/agents/task-manager.tsx b/lib/agents/task-manager.tsx index 90a72b67..dbf59f0d 100644 --- a/lib/agents/task-manager.tsx +++ b/lib/agents/task-manager.tsx @@ -1,6 +1,6 @@ import { CoreMessage, generateObject, LanguageModel } from 'ai' import { nextActionSchema } from '../schema/next-action' -import { getModel } from '../utils' +import { getModel } from '../utils/server' // Decide whether inquiry is required for the user input export async function taskManager(messages: CoreMessage[]) { diff --git a/lib/agents/writer.tsx b/lib/agents/writer.tsx index f4e4d0ac..93f02afa 100644 --- a/lib/agents/writer.tsx +++ b/lib/agents/writer.tsx @@ -2,7 +2,7 @@ import { createStreamableUI, createStreamableValue } from 'ai/rsc' import { CoreMessage, LanguageModel, streamText as nonexperimental_streamText } from 'ai' import { Section } from '@/components/section' import { BotMessage } from '@/components/message' -import { getModel } from '../utils' +import { getModel } from '../utils/server' export async function writer( dynamicSystemPrompt: string, // New parameter diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 079fdc86..1c7d7b40 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -1,10 +1,5 @@ import { type ClassValue, clsx } from 'clsx' import { twMerge } from 'tailwind-merge' -import { getSelectedModel } from '@/lib/actions/users' -import { createOpenAI } from '@ai-sdk/openai' -import { createGoogleGenerativeAI } from '@ai-sdk/google' -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { createXai } from '@ai-sdk/xai'; import { v4 as uuidv4 } from 'uuid'; export function cn(...inputs: ClassValue[]) { @@ -21,111 +16,6 @@ export function generateUUID(): string { */ export { generateUUID as nanoid }; -export async function getModel(requireVision: boolean = false) { - const selectedModel = await getSelectedModel(); - - const xaiApiKey = process.env.XAI_API_KEY; - const gemini3ProApiKey = process.env.GEMINI_3_PRO_API_KEY; - const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID; - const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; - const awsRegion = process.env.AWS_REGION; - const bedrockModelId = process.env.BEDROCK_MODEL_ID || 'anthropic.claude-3-5-sonnet-20241022-v2:0'; - const openaiApiKey = process.env.OPENAI_API_KEY; - - if (selectedModel) { - switch (selectedModel) { - case 'Grok 4.2': - if (xaiApiKey) { - const xai = createXai({ - apiKey: xaiApiKey, - baseURL: 'https://api.x.ai/v1', - }); - try { - const modelId = requireVision ? 'grok-vision-beta' : 'grok-2-1212'; - return xai(modelId); - } catch (error) { - console.error('Selected model "Grok 4.2" is configured but failed to initialize.', error); - throw new Error('Failed to initialize selected model.'); - } - } else { - console.error('User selected "Grok 4.2" but XAI_API_KEY is not set.'); - throw new Error('Selected model is not configured.'); - } - case 'Gemini 3': - case 'Gemini 3.1 Pro': - if (gemini3ProApiKey) { - const google = createGoogleGenerativeAI({ - apiKey: gemini3ProApiKey, - }); - try { - return google('gemini-1.5-pro-latest'); - } catch (error) { - console.error('Selected model "Gemini 3.1 Pro" is configured but failed to initialize.', error); - throw new Error('Failed to initialize selected model.'); - } - } else { - console.error('User selected "Gemini 3.1 Pro" but GEMINI_3_PRO_API_KEY is not set.'); - throw new Error('Selected model is not configured.'); - } - case 'GPT-5.1': - if (openaiApiKey) { - const openai = createOpenAI({ - apiKey: openaiApiKey, - }); - return openai('gpt-4o'); - } else { - console.error('User selected "GPT-5.1" but OPENAI_API_KEY is not set.'); - throw new Error('Selected model is not configured.'); - } - } - } - - // Default behavior: Grok -> Gemini -> Bedrock -> OpenAI - if (xaiApiKey) { - const xai = createXai({ - apiKey: xaiApiKey, - baseURL: 'https://api.x.ai/v1', - }); - try { - const modelId = requireVision ? 'grok-vision-beta' : 'grok-latest'; - return xai(modelId); - } catch (error) { - console.warn('xAI API unavailable, falling back to next provider:'); - } - } - - if (gemini3ProApiKey) { - const google = createGoogleGenerativeAI({ - apiKey: gemini3ProApiKey, - }); - try { - return google('gemini-1.5-pro-latest'); - } catch (error) { - console.warn('Gemini 3.1 Pro API unavailable, falling back to next provider:', error); - } - } - - if (awsAccessKeyId && awsSecretAccessKey) { - const bedrock = createAmazonBedrock({ - bedrockOptions: { - region: awsRegion, - credentials: { - accessKeyId: awsAccessKeyId, - secretAccessKey: awsSecretAccessKey, - }, - }, - }); - const model = bedrock(bedrockModelId, { - additionalModelRequestFields: { top_k: 350 }, - }); - return model; - } - - const openai = createOpenAI({ - apiKey: openaiApiKey, - }); - return openai('gpt-4o'); -} /** * Normalizes and sanitizes message content to plain text. diff --git a/lib/utils/server.ts b/lib/utils/server.ts new file mode 100644 index 00000000..7f6cb638 --- /dev/null +++ b/lib/utils/server.ts @@ -0,0 +1,111 @@ +import { getSelectedModel } from '@/lib/actions/users' +import { createOpenAI } from '@ai-sdk/openai' +import { createGoogleGenerativeAI } from '@ai-sdk/google' +import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' +import { createXai } from '@ai-sdk/xai' + +export async function getModel(requireVision: boolean = false) { + const selectedModel = await getSelectedModel(); + + const xaiApiKey = process.env.XAI_API_KEY; + const gemini3ProApiKey = process.env.GEMINI_3_PRO_API_KEY; + const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID; + const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; + const awsRegion = process.env.AWS_REGION; + const bedrockModelId = process.env.BEDROCK_MODEL_ID || 'anthropic.claude-3-5-sonnet-20241022-v2:0'; + const openaiApiKey = process.env.OPENAI_API_KEY; + + if (selectedModel) { + switch (selectedModel) { + case 'Grok 4.2': + if (xaiApiKey) { + const xai = createXai({ + apiKey: xaiApiKey, + baseURL: 'https://api.x.ai/v1', + }); + try { + const modelId = requireVision ? 'grok-vision-beta' : 'grok-2-1212'; + return xai(modelId); + } catch (error) { + console.error('Selected model "Grok 4.2" is configured but failed to initialize.', error); + throw new Error('Failed to initialize selected model.'); + } + } else { + console.error('User selected "Grok 4.2" but XAI_API_KEY is not set.'); + throw new Error('Selected model is not configured.'); + } + case 'Gemini 3': + case 'Gemini 3.1 Pro': + if (gemini3ProApiKey) { + const google = createGoogleGenerativeAI({ + apiKey: gemini3ProApiKey, + }); + try { + return google('gemini-1.5-pro-latest'); + } catch (error) { + console.error('Selected model "Gemini 3.1 Pro" is configured but failed to initialize.', error); + throw new Error('Failed to initialize selected model.'); + } + } else { + console.error('User selected "Gemini 3.1 Pro" but GEMINI_3_PRO_API_KEY is not set.'); + throw new Error('Selected model is not configured.'); + } + case 'GPT-5.1': + if (openaiApiKey) { + const openai = createOpenAI({ + apiKey: openaiApiKey, + }); + return openai('gpt-4o'); + } else { + console.error('User selected "GPT-5.1" but OPENAI_API_KEY is not set.'); + throw new Error('Selected model is not configured.'); + } + } + } + + // Default behavior: Grok -> Gemini -> Bedrock -> OpenAI + if (xaiApiKey) { + const xai = createXai({ + apiKey: xaiApiKey, + baseURL: 'https://api.x.ai/v1', + }); + try { + const modelId = requireVision ? 'grok-vision-beta' : 'grok-latest'; + return xai(modelId); + } catch (error) { + console.warn('xAI API unavailable, falling back to next provider:'); + } + } + + if (gemini3ProApiKey) { + const google = createGoogleGenerativeAI({ + apiKey: gemini3ProApiKey, + }); + try { + return google('gemini-1.5-pro-latest'); + } catch (error) { + console.warn('Gemini 3.1 Pro API unavailable, falling back to next provider:', error); + } + } + + if (awsAccessKeyId && awsSecretAccessKey) { + const bedrock = createAmazonBedrock({ + bedrockOptions: { + region: awsRegion, + credentials: { + accessKeyId: awsAccessKeyId, + secretAccessKey: awsSecretAccessKey, + }, + }, + }); + const model = bedrock(bedrockModelId, { + additionalModelRequestFields: { top_k: 350 }, + }); + return model; + } + + const openai = createOpenAI({ + apiKey: openaiApiKey, + }); + return openai('gpt-4o'); +} diff --git a/mapbox_mcp/hooks.ts b/mapbox_mcp/hooks.ts index 797f8852..799fb588 100644 --- a/mapbox_mcp/hooks.ts +++ b/mapbox_mcp/hooks.ts @@ -1,6 +1,8 @@ +'use client'; + import { useState, useCallback, useRef, useEffect } from 'react'; import { generateText } from 'ai'; -import { getModel } from '@/lib/utils'; +import { getModel } from '@/lib/utils/server'; // Define Tool type locally if needed type Tool = {