Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ NEXT_PUBLIC_COMPOSIO_USER_ID=user@example.com
# NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=your_mapbox_public_token_here

# AI Provider API Keys
# Azure AI Foundry (Azure OpenAI)
AZURE_RESOURCE_NAME=your_azure_resource_name_here
AZURE_ENDPOINT=your_azure_endpoint_here
# Azure AI Foundry (OpenAI-compatible endpoint)
AZURE_ENDPOINT=https://your-resource.services.ai.azure.com/openai/v1
AZURE_API_KEY=your_azure_api_key_here
AZURE_DEPLOYMENT_NAME=gpt-5.5

Expand Down
13 changes: 0 additions & 13 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

202 changes: 73 additions & 129 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,181 +4,125 @@ 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 { createAzure } from '@ai-sdk/azure';
import { v4 as uuidv4 } from 'uuid';
import { createXai } from '@ai-sdk/xai'
import { v4 as uuidv4 } from 'uuid'
import { LanguageModel } from 'ai'

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

export function generateUUID(): string {
return uuidv4();
return uuidv4()
}

export { generateUUID as nanoid }

/**
* Re-export generateUUID as nanoid for shorter naming and compatibility with existing code.
* Returns a UUID v4 string.
* Toggle whether Azure should be the primary/default model
* Set USE_AZURE=true in your .env file to enable
*/
export { generateUUID as nanoid };
const USE_AZURE = process.env.USE_AZURE === 'true' || process.env.USE_AZURE === '1'

export async function getModel(requireVision: boolean = false): Promise<LanguageModel> {
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 azureResourceName = process.env.AZURE_RESOURCE_NAME;
const azureApiKey = process.env.AZURE_API_KEY;
const azureEndpoint = process.env.AZURE_ENDPOINT;
const azureDeploymentName = process.env.AZURE_DEPLOYMENT_NAME || 'gpt-5.5';
const selectedModel = await getSelectedModel()

// Environment variables
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 azureApiKey = process.env.AZURE_API_KEY
const azureEndpoint = process.env.AZURE_ENDPOINT
const azureDeploymentName = process.env.AZURE_DEPLOYMENT_NAME || 'gpt-5.5'

// ====================== USER SELECTED MODEL ======================
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.');
}
if (!xaiApiKey) throw new Error('Grok 4.2 selected but XAI_API_KEY not configured')
return createXai({ apiKey: xaiApiKey, baseURL: 'https://api.x.ai/v1' })('grok-4-fast-non-reasoning')

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.');
}
if (!gemini3ProApiKey) throw new Error('Gemini 3.1 Pro selected but GEMINI_3_PRO_API_KEY not configured')
return createGoogleGenerativeAI({ apiKey: gemini3ProApiKey })('gemini-3.1-pro-preview')

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.');
}
if (!openaiApiKey) throw new Error('GPT-5.1 selected but OPENAI_API_KEY not configured')
return createOpenAI({ apiKey: openaiApiKey })('gpt-4o')

case 'Azure GPT 5.5':
if (azureEndpoint && azureApiKey) {
const azureOpenAI = createOpenAI({
baseURL: azureEndpoint,
apiKey: azureApiKey,
});
try {
return azureOpenAI(azureDeploymentName);
} catch (error) {
console.error('Selected model "Azure GPT 5.5" (via endpoint) failed to initialize.', error);
throw new Error('Failed to initialize selected model.');
}
} else if (azureResourceName && azureApiKey) {
const azure = createAzure({
resourceName: azureResourceName,
apiKey: azureApiKey,
});
try {
return azure(azureDeploymentName) as unknown as LanguageModel;
} catch (error) {
console.error('Selected model "Azure GPT 5.5" (via resource) failed to initialize.', error);
throw new Error('Failed to initialize selected model.');
}
} else {
console.error('User selected "Azure GPT 5.5" but Azure environment variables are not set.');
throw new Error('Selected model is not configured.');
if (!azureEndpoint || !azureApiKey) {
throw new Error('Azure GPT 5.5 selected but AZURE_ENDPOINT or AZURE_API_KEY not configured')
}
}
}
return createOpenAI({
baseURL: azureEndpoint,
apiKey: azureApiKey,
compatibility: 'compatible',
})(azureDeploymentName)

// Default behavior: Azure -> Gemini -> Grok -> Bedrock -> OpenAI
if (azureEndpoint && azureApiKey) {
const azureOpenAI = createOpenAI({
baseURL: azureEndpoint,
apiKey: azureApiKey,
});
try {
return azureOpenAI(azureDeploymentName);
} catch (error) {
console.warn('Azure OpenAI API (via endpoint) unavailable, falling back to next provider:', error);
default:
console.warn(`Unknown selected model: ${selectedModel}, falling back to default`)
}
}

if (azureResourceName && azureApiKey) {
const azure = createAzure({
resourceName: azureResourceName,
apiKey: azureApiKey,
});
try {
return azure(azureDeploymentName) as unknown as LanguageModel;
} catch (error) {
console.warn('Azure OpenAI API (via resource) unavailable, falling back to next provider:', error);
// ====================== DEFAULT FALLBACK LOGIC ======================

// Azure is primary when toggle is enabled
if (USE_AZURE) {
if (azureEndpoint && azureApiKey) {
try {
const azureOpenai = createOpenAI({
baseURL: azureEndpoint,
apiKey: azureApiKey,
compatibility: 'compatible',
})
return azureOpenai(azureDeploymentName)
} catch (error) {
console.warn('Azure unavailable, falling back to other providers:', error)
}
} else {
console.warn('USE_AZURE=true but Azure credentials not configured. Falling back...')
}
}

// Fallback order when Azure is disabled or unavailable: Gemini → Grok → Bedrock → OpenAI
if (gemini3ProApiKey) {
const google = createGoogleGenerativeAI({
apiKey: gemini3ProApiKey,
});
try {
return google('gemini-3.1-pro-preview');
return createGoogleGenerativeAI({ apiKey: gemini3ProApiKey })('gemini-3.1-pro-preview')
} catch (error) {
console.warn('Gemini 3.1 Pro API unavailable, falling back to next provider:', error);
console.warn('Gemini unavailable:', error)
}
}

if (xaiApiKey) {
const xai = createXai({
apiKey: xaiApiKey,
baseURL: 'https://api.x.ai/v1',
});
try {
return xai('grok-4-fast-non-reasoning');
return createXai({ apiKey: xaiApiKey, baseURL: 'https://api.x.ai/v1' })('grok-4-fast-non-reasoning')
} catch (error) {
console.warn('xAI API unavailable, falling back to next provider:');
console.warn('xAI unavailable:', error)
}
}


if (awsAccessKeyId && awsSecretAccessKey) {
const bedrock = createAmazonBedrock({
bedrockOptions: {
region: awsRegion,
credentials: {
accessKeyId: awsAccessKeyId,
secretAccessKey: awsSecretAccessKey,
},
credentials: { accessKeyId: awsAccessKeyId, secretAccessKey: awsSecretAccessKey },
},
});
const model = bedrock(bedrockModelId, {
additionalModelRequestFields: { top_k: 350 },
});
return model;
})
return bedrock(bedrockModelId, { additionalModelRequestFields: { top_k: 350 } })
}

// Final fallback
if (openaiApiKey) {
return createOpenAI({ apiKey: openaiApiKey })('gpt-4o')
}

const openai = createOpenAI({
apiKey: openaiApiKey,
});
return openai('gpt-4o');
throw new Error(
'No AI provider configured. Please set USE_AZURE=true with Azure credentials, or configure another provider.'
)
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
"dependencies": {
"@ai-sdk/amazon-bedrock": "^1.1.6",
"@ai-sdk/anthropic": "^1.2.12",
"@ai-sdk/azure": "^3.0.64",
"@ai-sdk/google": "^1.2.22",
"@ai-sdk/openai": "^1.3.24",
"@ai-sdk/xai": "^1.2.18",
"@composio/core": "^0.3.3",

"@google-cloud/storage": "^7.18.0",
"@google/generative-ai": "^0.24.1",
"@heroicons/react": "^2.2.0",
Expand Down