11import { createLogger , type Logger } from '@sim/logger'
22import type { ChatCompletionChunk } from 'openai/resources/chat/completions'
33import type { CompletionUsage } from 'openai/resources/completions'
4- import { getEnv , isTruthy } from '@/lib/core/config/env'
4+ import { env } from '@/lib/core/config/env'
55import { isHosted } from '@/lib/core/config/feature-flags'
66import { isCustomTool } from '@/executor/constants'
77import {
@@ -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 }
@@ -152,22 +155,39 @@ export function getAllModelProviders(): Record<string, ProviderId> {
152155
153156export function getProviderFromModel ( model : string ) : ProviderId {
154157 const normalizedModel = model . toLowerCase ( )
155- if ( normalizedModel in getAllModelProviders ( ) ) {
156- return getAllModelProviders ( ) [ normalizedModel ]
157- }
158158
159- for ( const [ providerId , config ] of Object . entries ( providers ) ) {
160- if ( config . modelPatterns ) {
161- for ( const pattern of config . modelPatterns ) {
162- if ( pattern . test ( normalizedModel ) ) {
163- return providerId as ProviderId
159+ let providerId : ProviderId | null = null
160+
161+ if ( normalizedModel in getAllModelProviders ( ) ) {
162+ providerId = getAllModelProviders ( ) [ normalizedModel ]
163+ } else {
164+ for ( const [ id , config ] of Object . entries ( providers ) ) {
165+ if ( config . modelPatterns ) {
166+ for ( const pattern of config . modelPatterns ) {
167+ if ( pattern . test ( normalizedModel ) ) {
168+ providerId = id as ProviderId
169+ break
170+ }
164171 }
165172 }
173+ if ( providerId ) break
166174 }
167175 }
168176
169- logger . warn ( `No provider found for model: ${ model } , defaulting to ollama` )
170- return 'ollama'
177+ if ( ! providerId ) {
178+ logger . warn ( `No provider found for model: ${ model } , defaulting to ollama` )
179+ providerId = 'ollama'
180+ }
181+
182+ if ( isProviderBlacklisted ( providerId ) ) {
183+ throw new Error ( `Provider "${ providerId } " is not available` )
184+ }
185+
186+ if ( isModelBlacklisted ( normalizedModel ) ) {
187+ throw new Error ( `Model "${ model } " is not available` )
188+ }
189+
190+ return providerId
171191}
172192
173193export function getProvider ( id : string ) : ProviderMetadata | undefined {
@@ -192,35 +212,42 @@ export function getProviderModels(providerId: ProviderId): string[] {
192212 return getProviderModelsFromDefinitions ( providerId )
193213}
194214
195- interface ModelBlacklist {
196- models : string [ ]
197- prefixes : string [ ]
198- envOverride ?: string
215+ function getBlacklistedProviders ( ) : string [ ] {
216+ if ( ! env . BLACKLISTED_PROVIDERS ) return [ ]
217+ return env . BLACKLISTED_PROVIDERS . split ( ',' ) . map ( ( p ) => p . trim ( ) . toLowerCase ( ) )
199218}
200219
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- ]
220+ export function isProviderBlacklisted ( providerId : string ) : boolean {
221+ const blacklist = getBlacklistedProviders ( )
222+ return blacklist . includes ( providerId . toLowerCase ( ) )
223+ }
224+
225+ /**
226+ * Get the list of blacklisted models from env var.
227+ * BLACKLISTED_MODELS supports:
228+ * - Exact model names: "gpt-4,claude-3-opus"
229+ * - Prefix patterns with *: "claude-*,gpt-4-*" (matches models starting with that prefix)
230+ */
231+ function getBlacklistedModels ( ) : { models : string [ ] ; prefixes : string [ ] } {
232+ if ( ! env . BLACKLISTED_MODELS ) return { models : [ ] , prefixes : [ ] }
233+
234+ const entries = env . BLACKLISTED_MODELS . split ( ',' ) . map ( ( m ) => m . trim ( ) . toLowerCase ( ) )
235+ const models = entries . filter ( ( e ) => ! e . endsWith ( '*' ) )
236+ const prefixes = entries . filter ( ( e ) => e . endsWith ( '*' ) ) . map ( ( e ) => e . slice ( 0 , - 1 ) )
237+
238+ return { models, prefixes }
239+ }
208240
209241function isModelBlacklisted ( model : string ) : boolean {
210242 const lowerModel = model . toLowerCase ( )
243+ const blacklist = getBlacklistedModels ( )
211244
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- }
245+ if ( blacklist . models . includes ( lowerModel ) ) {
246+ return true
247+ }
220248
221- if ( blacklist . prefixes . some ( ( prefix ) => lowerModel . startsWith ( prefix ) ) ) {
222- return true
223- }
249+ if ( blacklist . prefixes . some ( ( prefix ) => lowerModel . startsWith ( prefix ) ) ) {
250+ return true
224251 }
225252
226253 return false
0 commit comments