diff --git a/.env.local.example b/.env.local.example index e52eec0c..956b921d 100644 --- a/.env.local.example +++ b/.env.local.example @@ -17,6 +17,12 @@ NEXT_PUBLIC_COMPOSIO_USER_ID=user@example.com # NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=your_mapbox_public_token_here # AI Provider API Keys +# GPT-5.5 (Azure Foundry) +# AZURE_ENDPOINT should be the full V1 endpoint, e.g. https://your-resource.services.ai.azure.com/openai/v1 +AZURE_ENDPOINT=your_azure_endpoint_here +AZURE_API_KEY=your_azure_api_key_here +AZURE_DEPLOYMENT_NAME=gpt-5.5 + # Gemini 3.1 Pro (Google Generative AI) GEMINI_3_PRO_API_KEY=your_gemini_3_pro_api_key_here diff --git a/components/settings/components/model-selection-form.tsx b/components/settings/components/model-selection-form.tsx index bc6164ba..9abb9676 100644 --- a/components/settings/components/model-selection-form.tsx +++ b/components/settings/components/model-selection-form.tsx @@ -58,6 +58,14 @@ const models = [ badge: "Expert", badgeVariant: "outline" as const, }, + { + id: "GPT-5.5", + name: "GPT-5.5", + description: "The next generation of OpenAI models, exclusively via Azure Foundry, delivering superior reasoning and efficiency.", + icon: Cpu, + badge: "Premium", + badgeVariant: "default" as const, + }, ]; export function ModelSelectionForm({ form }: ModelSelectionFormProps) { diff --git a/lib/actions/suggest.ts b/lib/actions/suggest.ts index 8555461c..6bcd5342 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/model' import { MapData } from '@/components/map/map-data-context' export async function getSuggestions( diff --git a/lib/agents/inquire.tsx b/lib/agents/inquire.tsx index 9026bbc0..16a09609 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/model'; // 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..76f73e8a 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/model' interface CacheEntry { data: PartialRelated; diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index ce801af4..d8f16a2f 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/model' 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 d456fe49..14f9690c 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/model' import { z } from 'zod' import { tavily } from '@tavily/core' diff --git a/lib/agents/task-manager.tsx b/lib/agents/task-manager.tsx index 90a72b67..96dbfba5 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/model' // 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..a6af442b 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/model' export async function writer( dynamicSystemPrompt: string, // New parameter diff --git a/lib/auth/get-current-user.ts b/lib/auth/get-current-user.ts index 6d08ba09..764b68f0 100644 --- a/lib/auth/get-current-user.ts +++ b/lib/auth/get-current-user.ts @@ -58,25 +58,23 @@ export async function getSupabaseUserAndSessionOnServer(): Promise<{ return { user: null, session: null, error: new Error('Missing Supabase environment variables') }; } - const cookieStore = cookies(); + const cookieStore = await cookies(); const supabase = createServerClient(supabaseUrl, supabaseAnonKey, { cookies: { async get(name: string): Promise { - const cookie = (await cookieStore).get(name); // Use the correct get method - return cookie?.value; // Return the value or undefined + const cookie = cookieStore.get(name); + return cookie?.value; }, async set(name: string, value: string, options: CookieOptions): Promise { try { - const store = await cookieStore; - store.set({ name, value, ...options }); // Set cookie with options + cookieStore.set({ name, value, ...options }); } catch (error) { // console.warn(`[Auth] Failed to set cookie ${name}:`, error); } }, async remove(name: string, options: CookieOptions): Promise { try { - const store = await cookieStore; - store.set({ name, value: '', ...options, maxAge: 0 }); // Delete cookie by setting maxAge to 0 + cookieStore.set({ name, value: '', ...options, maxAge: 0 }); } catch (error) { // console.warn(`[Auth] Failed to delete cookie ${name}:`, error); } @@ -124,4 +122,4 @@ export async function getCurrentUserIdOnServer(): Promise { return null; } return user?.id || null; -} \ No newline at end of file +} diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 15565281..5733ad4b 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[]) { @@ -20,107 +15,3 @@ export function generateUUID(): string { * Returns a UUID v4 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 { - return xai('grok-4-fast-non-reasoning'); - } 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-3.1-pro-preview'); - } 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: Gemini -> Grok -> Bedrock -> OpenAI - if (gemini3ProApiKey) { - const google = createGoogleGenerativeAI({ - apiKey: gemini3ProApiKey, - }); - try { - return google('gemini-3.1-pro-preview'); - } catch (error) { - console.warn('Gemini 3.1 Pro API unavailable, falling back to next provider:', error); - } - } - - if (xaiApiKey) { - const xai = createXai({ - apiKey: xaiApiKey, - baseURL: 'https://api.x.ai/v1', - }); - try { - return xai('grok-4-fast-non-reasoning'); - } catch (error) { - console.warn('xAI API unavailable, falling back to next provider:'); - } - } - - 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/lib/utils/model.ts b/lib/utils/model.ts new file mode 100644 index 00000000..b3fe7272 --- /dev/null +++ b/lib/utils/model.ts @@ -0,0 +1,119 @@ +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; + const azureEndpoint = process.env.AZURE_ENDPOINT; + const azureApiKey = process.env.AZURE_API_KEY; + const azureDeploymentName = process.env.AZURE_DEPLOYMENT_NAME || 'gpt-5.5'; + + if (selectedModel) { + switch (selectedModel) { + case 'Grok 4.2': + if (xaiApiKey) { + const xai = createXai({ + apiKey: xaiApiKey, + baseURL: 'https://api.x.ai/v1', + }); + return xai('grok-4-fast-non-reasoning'); + } 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, + }); + return google('gemini-3.1-pro-preview'); + } 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.'); + } + + case 'GPT-5.5': + if (azureApiKey && azureEndpoint) { + const azure = createOpenAI({ + baseURL: azureEndpoint, + apiKey: azureApiKey, + }); + return azure(azureDeploymentName); + } else { + console.error('User selected "GPT-5.5" but AZURE_API_KEY or AZURE_ENDPOINT is not set.'); + throw new Error('Selected model is not configured.'); + } + + default: + console.warn(`Unknown selected model: ${selectedModel}, falling back to default.`); + } + } + + // === Default fallback order: Azure → Gemini → Grok → Bedrock → OpenAI === + if (azureApiKey && azureEndpoint) { + const azure = createOpenAI({ + baseURL: azureEndpoint, + apiKey: azureApiKey, + }); + return azure(azureDeploymentName); + } + + if (gemini3ProApiKey) { + const google = createGoogleGenerativeAI({ + apiKey: gemini3ProApiKey, + }); + return google('gemini-3.1-pro-preview'); + } + + if (xaiApiKey) { + const xai = createXai({ + apiKey: xaiApiKey, + baseURL: 'https://api.x.ai/v1', + }); + return xai('grok-4-fast-non-reasoning'); + } + + if (awsAccessKeyId && awsSecretAccessKey) { + const bedrock = createAmazonBedrock({ + bedrockOptions: { + region: awsRegion, + credentials: { + accessKeyId: awsAccessKeyId, + secretAccessKey: awsSecretAccessKey, + }, + }, + }); + return bedrock(bedrockModelId, { + additionalModelRequestFields: { top_k: 350 }, + }); + } + + // Final fallback + if (!openaiApiKey) { + throw new Error('No model providers are configured. Please set at least one API key.'); + } + + const openai = createOpenAI({ apiKey: openaiApiKey }); + return openai('gpt-4o'); +} diff --git a/mapbox_mcp/hooks.ts b/mapbox_mcp/hooks.ts index 797f8852..420e5e54 100644 --- a/mapbox_mcp/hooks.ts +++ b/mapbox_mcp/hooks.ts @@ -1,6 +1,6 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { generateText } from 'ai'; -import { getModel } from '@/lib/utils'; +import { getModel } from '@/lib/utils/model'; // Define Tool type locally if needed type Tool = {