Skip to content

Commit e5e5f90

Browse files
committed
feat(blacklist): added ability to blacklist models & providers
1 parent 3ecf7a1 commit e5e5f90

File tree

9 files changed

+74
-38
lines changed

9 files changed

+74
-38
lines changed

apps/sim/app/api/providers/ollama/models/route.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { env } from '@/lib/core/config/env'
44
import type { ModelsObject } from '@/providers/ollama/types'
5+
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
56

67
const logger = createLogger('OllamaModelsAPI')
78
const OLLAMA_HOST = env.OLLAMA_URL || 'http://localhost:11434'
89

910
/**
1011
* Get available Ollama models
1112
*/
12-
export async function GET(request: NextRequest) {
13+
export async function GET(_request: NextRequest) {
14+
if (isProviderBlacklisted('ollama')) {
15+
logger.info('Ollama provider is blacklisted, returning empty models')
16+
return NextResponse.json({ models: [] })
17+
}
18+
1319
try {
1420
logger.info('Fetching Ollama models', {
1521
host: OLLAMA_HOST,
@@ -31,10 +37,12 @@ export async function GET(request: NextRequest) {
3137
}
3238

3339
const data = (await response.json()) as ModelsObject
34-
const models = data.models.map((model) => model.name)
40+
const allModels = data.models.map((model) => model.name)
41+
const models = filterBlacklistedModels(allModels)
3542

3643
logger.info('Successfully fetched Ollama models', {
3744
count: models.length,
45+
filtered: allModels.length - models.length,
3846
models,
3947
})
4048

apps/sim/app/api/providers/openrouter/models/route.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
3-
import { filterBlacklistedModels } from '@/providers/utils'
3+
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
44

55
const logger = createLogger('OpenRouterModelsAPI')
66

@@ -30,6 +30,11 @@ export interface OpenRouterModelInfo {
3030
}
3131

3232
export async function GET(_request: NextRequest) {
33+
if (isProviderBlacklisted('openrouter')) {
34+
logger.info('OpenRouter provider is blacklisted, returning empty models')
35+
return NextResponse.json({ models: [], modelInfo: {} })
36+
}
37+
3338
try {
3439
const response = await fetch('https://openrouter.ai/api/v1/models', {
3540
headers: { 'Content-Type': 'application/json' },

apps/sim/app/api/providers/vllm/models/route.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { env } from '@/lib/core/config/env'
4+
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
45

56
const logger = createLogger('VLLMModelsAPI')
67

78
/**
89
* Get available vLLM models
910
*/
10-
export async function GET(request: NextRequest) {
11+
export async function GET(_request: NextRequest) {
12+
if (isProviderBlacklisted('vllm')) {
13+
logger.info('vLLM provider is blacklisted, returning empty models')
14+
return NextResponse.json({ models: [] })
15+
}
16+
1117
const baseUrl = (env.VLLM_BASE_URL || '').replace(/\/$/, '')
1218

1319
if (!baseUrl) {
@@ -42,10 +48,12 @@ export async function GET(request: NextRequest) {
4248
}
4349

4450
const data = (await response.json()) as { data: Array<{ id: string }> }
45-
const models = data.data.map((model) => `vllm/${model.id}`)
51+
const allModels = data.data.map((model) => `vllm/${model.id}`)
52+
const models = filterBlacklistedModels(allModels)
4653

4754
logger.info('Successfully fetched vLLM models', {
4855
count: models.length,
56+
filtered: allModels.length - models.length,
4957
models,
5058
})
5159

apps/sim/blocks/blocks/agent.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
44
import type { BlockConfig } from '@/blocks/types'
55
import { AuthMode } from '@/blocks/types'
66
import {
7-
getAllModelProviders,
7+
getBaseModelProviders,
88
getHostedModels,
99
getMaxTemperature,
1010
getProviderIcon,
@@ -417,7 +417,7 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
417417
condition: () => ({
418418
field: 'model',
419419
value: (() => {
420-
const allModels = Object.keys(getAllModelProviders())
420+
const allModels = Object.keys(getBaseModelProviders())
421421
return allModels.filter(
422422
(model) => supportsTemperature(model) && getMaxTemperature(model) === 1
423423
)
@@ -434,7 +434,7 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
434434
condition: () => ({
435435
field: 'model',
436436
value: (() => {
437-
const allModels = Object.keys(getAllModelProviders())
437+
const allModels = Object.keys(getBaseModelProviders())
438438
return allModels.filter(
439439
(model) => supportsTemperature(model) && getMaxTemperature(model) === 2
440440
)
@@ -555,7 +555,7 @@ Example 3 (Array Input):
555555
if (!model) {
556556
throw new Error('No model selected')
557557
}
558-
const tool = getAllModelProviders()[model]
558+
const tool = getBaseModelProviders()[model]
559559
if (!tool) {
560560
throw new Error(`Invalid model selected: ${model}`)
561561
}

apps/sim/blocks/blocks/evaluator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
44
import type { BlockConfig, ParamType } from '@/blocks/types'
55
import type { ProviderId } from '@/providers/types'
66
import {
7-
getAllModelProviders,
7+
getBaseModelProviders,
88
getHostedModels,
99
getProviderIcon,
1010
providers,
@@ -357,7 +357,7 @@ export const EvaluatorBlock: BlockConfig<EvaluatorResponse> = {
357357
if (!model) {
358358
throw new Error('No model selected')
359359
}
360-
const tool = getAllModelProviders()[model as ProviderId]
360+
const tool = getBaseModelProviders()[model as ProviderId]
361361
if (!tool) {
362362
throw new Error(`Invalid model selected: ${model}`)
363363
}

apps/sim/blocks/blocks/router.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
33
import { AuthMode, type BlockConfig } from '@/blocks/types'
44
import type { ProviderId } from '@/providers/types'
55
import {
6-
getAllModelProviders,
6+
getBaseModelProviders,
77
getHostedModels,
88
getProviderIcon,
99
providers,
@@ -324,7 +324,7 @@ export const RouterBlock: BlockConfig<RouterResponse> = {
324324
if (!model) {
325325
throw new Error('No model selected')
326326
}
327-
const tool = getAllModelProviders()[model as ProviderId]
327+
const tool = getBaseModelProviders()[model as ProviderId]
328328
if (!tool) {
329329
throw new Error(`Invalid model selected: ${model}`)
330330
}
@@ -508,7 +508,7 @@ export const RouterV2Block: BlockConfig<RouterV2Response> = {
508508
if (!model) {
509509
throw new Error('No model selected')
510510
}
511-
const tool = getAllModelProviders()[model as ProviderId]
511+
const tool = getBaseModelProviders()[model as ProviderId]
512512
if (!tool) {
513513
throw new Error(`Invalid model selected: ${model}`)
514514
}

apps/sim/lib/core/config/env.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export const env = createEnv({
8787
ELEVENLABS_API_KEY: z.string().min(1).optional(), // ElevenLabs API key for text-to-speech in deployed chat
8888
SERPER_API_KEY: z.string().min(1).optional(), // Serper API key for online search
8989
EXA_API_KEY: z.string().min(1).optional(), // Exa AI API key for enhanced online search
90-
DEEPSEEK_MODELS_ENABLED: z.boolean().optional().default(false), // Enable Deepseek models in UI (defaults to false for compliance)
90+
BLACKLISTED_PROVIDERS: z.string().optional(), // Comma-separated provider IDs to hide (e.g., "openai,anthropic")
91+
BLACKLISTED_MODELS: z.string().optional(), // Comma-separated model names/prefixes to hide (e.g., "gpt-4,claude-*")
9192

9293
// Azure Configuration - Shared credentials with feature-specific models
9394
AZURE_OPENAI_ENDPOINT: z.string().url().optional(), // Shared Azure OpenAI service endpoint

apps/sim/providers/utils.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createLogger, type Logger } from '@sim/logger'
22
import type { ChatCompletionChunk } from 'openai/resources/chat/completions'
33
import type { CompletionUsage } from 'openai/resources/completions'
4-
import { getEnv, isTruthy } from '@/lib/core/config/env'
4+
import { env } from '@/lib/core/config/env'
55
import { isHosted } from '@/lib/core/config/feature-flags'
66
import { isCustomTool } from '@/executor/constants'
77
import {
@@ -131,6 +131,9 @@ function filterBlacklistedModelsFromProviderMap(
131131
): Record<string, ProviderId> {
132132
const filtered: Record<string, ProviderId> = {}
133133
for (const [model, providerId] of Object.entries(providerMap)) {
134+
if (isProviderBlacklisted(providerId)) {
135+
continue
136+
}
134137
if (!isModelBlacklisted(model)) {
135138
filtered[model] = providerId
136139
}
@@ -192,35 +195,42 @@ export function getProviderModels(providerId: ProviderId): string[] {
192195
return getProviderModelsFromDefinitions(providerId)
193196
}
194197

195-
interface ModelBlacklist {
196-
models: string[]
197-
prefixes: string[]
198-
envOverride?: string
198+
function getBlacklistedProviders(): string[] {
199+
if (!env.BLACKLISTED_PROVIDERS) return []
200+
return env.BLACKLISTED_PROVIDERS.split(',').map((p) => p.trim().toLowerCase())
199201
}
200202

201-
const MODEL_BLACKLISTS: ModelBlacklist[] = [
202-
{
203-
models: ['deepseek-chat', 'deepseek-v3', 'deepseek-r1'],
204-
prefixes: ['openrouter/deepseek', 'openrouter/tngtech'],
205-
envOverride: 'DEEPSEEK_MODELS_ENABLED',
206-
},
207-
]
203+
export function isProviderBlacklisted(providerId: string): boolean {
204+
const blacklist = getBlacklistedProviders()
205+
return blacklist.includes(providerId.toLowerCase())
206+
}
207+
208+
/**
209+
* Get the list of blacklisted models from env var.
210+
* BLACKLISTED_MODELS supports:
211+
* - Exact model names: "gpt-4,claude-3-opus"
212+
* - Prefix patterns with *: "claude-*,gpt-4-*" (matches models starting with that prefix)
213+
*/
214+
function getBlacklistedModels(): { models: string[]; prefixes: string[] } {
215+
if (!env.BLACKLISTED_MODELS) return { models: [], prefixes: [] }
216+
217+
const entries = env.BLACKLISTED_MODELS.split(',').map((m) => m.trim().toLowerCase())
218+
const models = entries.filter((e) => !e.endsWith('*'))
219+
const prefixes = entries.filter((e) => e.endsWith('*')).map((e) => e.slice(0, -1))
220+
221+
return { models, prefixes }
222+
}
208223

209224
function isModelBlacklisted(model: string): boolean {
210225
const lowerModel = model.toLowerCase()
226+
const blacklist = getBlacklistedModels()
211227

212-
for (const blacklist of MODEL_BLACKLISTS) {
213-
if (blacklist.envOverride && isTruthy(getEnv(blacklist.envOverride))) {
214-
continue
215-
}
216-
217-
if (blacklist.models.includes(lowerModel)) {
218-
return true
219-
}
228+
if (blacklist.models.includes(lowerModel)) {
229+
return true
230+
}
220231

221-
if (blacklist.prefixes.some((prefix) => lowerModel.startsWith(prefix))) {
222-
return true
223-
}
232+
if (blacklist.prefixes.some((prefix) => lowerModel.startsWith(prefix))) {
233+
return true
224234
}
225235

226236
return false

helm/sim/values.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ app:
117117
ALLOWED_LOGIN_EMAILS: "" # Comma-separated list of allowed email addresses for login
118118
ALLOWED_LOGIN_DOMAINS: "" # Comma-separated list of allowed email domains for login
119119

120+
# LLM Provider/Model Restrictions (leave empty if not restricting)
121+
BLACKLISTED_PROVIDERS: "" # Comma-separated provider IDs to hide from UI (e.g., "openai,anthropic,google")
122+
BLACKLISTED_MODELS: "" # Comma-separated model names/prefixes to hide (e.g., "gpt-4,claude-*")
123+
120124
# SSO Configuration (Enterprise Single Sign-On)
121125
# Set to "true" AFTER running the SSO registration script
122126
SSO_ENABLED: "" # Enable SSO authentication ("true" to enable)

0 commit comments

Comments
 (0)