@@ -1276,99 +1276,103 @@ export const layer = Layer.effect(
12761276 }
12771277
12781278 // extend database from config
1279- for ( const [ providerID , provider ] of configProviders ) {
1280- const existing = database [ providerID ]
1281- const parsed : Info = {
1282- id : ProviderV2 . ID . make ( providerID ) ,
1283- name : provider . name ?? existing ?. name ?? providerID ,
1284- env : provider . env ?? existing ?. env ?? [ ] ,
1285- options : mergeDeep ( existing ?. options ?? { } , provider . options ?? { } ) ,
1286- source : "config" ,
1287- models : existing ?. models ?? { } ,
1288- }
1279+ function applyConfig ( target : Record < string , Info > ) {
1280+ for ( const [ providerID , provider ] of configProviders ) {
1281+ const existing = target [ providerID ]
1282+ const parsed : Info = {
1283+ id : ProviderV2 . ID . make ( providerID ) ,
1284+ name : provider . name ?? existing ?. name ?? providerID ,
1285+ env : provider . env ?? existing ?. env ?? [ ] ,
1286+ options : mergeDeep ( existing ?. options ?? { } , provider . options ?? { } ) ,
1287+ source : "config" ,
1288+ models : existing ?. models ?? { } ,
1289+ }
12891290
1290- for ( const [ modelID , model ] of Object . entries ( provider . models ?? { } ) ) {
1291- const existingModel = parsed . models [ model . id ?? modelID ]
1292- const apiID = model . id ?? existingModel ?. api . id ?? modelID
1293- const apiNpm =
1294- model . provider ?. npm ??
1295- provider . npm ??
1296- existingModel ?. api . npm ??
1297- modelsDev [ providerID ] ?. npm ??
1298- "@ai-sdk/openai-compatible"
1299- const name = iife ( ( ) => {
1300- if ( model . name ) return model . name
1301- if ( model . id && model . id !== modelID ) return modelID
1302- return existingModel ?. name ?? modelID
1303- } )
1304- const parsedModel : Model = {
1305- id : ProviderV2 . ModelID . make ( modelID ) ,
1306- api : {
1307- id : apiID ,
1308- npm : apiNpm ,
1309- url : model . provider ?. api ?? provider ?. api ?? existingModel ?. api . url ?? modelsDev [ providerID ] ?. api ?? "" ,
1310- } ,
1311- status : model . status ?? existingModel ?. status ?? "active" ,
1312- name,
1313- providerID : ProviderV2 . ID . make ( providerID ) ,
1314- capabilities : {
1315- temperature : model . temperature ?? existingModel ?. capabilities . temperature ?? false ,
1316- reasoning : model . reasoning ?? existingModel ?. capabilities . reasoning ?? false ,
1317- attachment : model . attachment ?? existingModel ?. capabilities . attachment ?? false ,
1318- toolcall : model . tool_call ?? existingModel ?. capabilities . toolcall ?? true ,
1319- input : {
1320- text : model . modalities ?. input ?. includes ( "text" ) ?? existingModel ?. capabilities . input . text ?? true ,
1321- audio : model . modalities ?. input ?. includes ( "audio" ) ?? existingModel ?. capabilities . input . audio ?? false ,
1322- image : model . modalities ?. input ?. includes ( "image" ) ?? existingModel ?. capabilities . input . image ?? false ,
1323- video : model . modalities ?. input ?. includes ( "video" ) ?? existingModel ?. capabilities . input . video ?? false ,
1324- pdf : model . modalities ?. input ?. includes ( "pdf" ) ?? existingModel ?. capabilities . input . pdf ?? false ,
1291+ for ( const [ modelID , model ] of Object . entries ( provider . models ?? { } ) ) {
1292+ const existingModel = parsed . models [ model . id ?? modelID ]
1293+ const apiID = model . id ?? existingModel ?. api . id ?? modelID
1294+ const apiNpm =
1295+ model . provider ?. npm ??
1296+ provider . npm ??
1297+ existingModel ?. api . npm ??
1298+ modelsDev [ providerID ] ?. npm ??
1299+ "@ai-sdk/openai-compatible"
1300+ const name = iife ( ( ) => {
1301+ if ( model . name ) return model . name
1302+ if ( model . id && model . id !== modelID ) return modelID
1303+ return existingModel ?. name ?? modelID
1304+ } )
1305+ const parsedModel : Model = {
1306+ id : ProviderV2 . ModelID . make ( modelID ) ,
1307+ api : {
1308+ id : apiID ,
1309+ npm : apiNpm ,
1310+ url : model . provider ?. api ?? provider ?. api ?? existingModel ?. api . url ?? modelsDev [ providerID ] ?. api ?? "" ,
1311+ } ,
1312+ status : model . status ?? existingModel ?. status ?? "active" ,
1313+ name,
1314+ providerID : ProviderV2 . ID . make ( providerID ) ,
1315+ capabilities : {
1316+ temperature : model . temperature ?? existingModel ?. capabilities . temperature ?? false ,
1317+ reasoning : model . reasoning ?? existingModel ?. capabilities . reasoning ?? false ,
1318+ attachment : model . attachment ?? existingModel ?. capabilities . attachment ?? false ,
1319+ toolcall : model . tool_call ?? existingModel ?. capabilities . toolcall ?? true ,
1320+ input : {
1321+ text : model . modalities ?. input ?. includes ( "text" ) ?? existingModel ?. capabilities . input . text ?? true ,
1322+ audio : model . modalities ?. input ?. includes ( "audio" ) ?? existingModel ?. capabilities . input . audio ?? false ,
1323+ image : model . modalities ?. input ?. includes ( "image" ) ?? existingModel ?. capabilities . input . image ?? false ,
1324+ video : model . modalities ?. input ?. includes ( "video" ) ?? existingModel ?. capabilities . input . video ?? false ,
1325+ pdf : model . modalities ?. input ?. includes ( "pdf" ) ?? existingModel ?. capabilities . input . pdf ?? false ,
1326+ } ,
1327+ output : {
1328+ text : model . modalities ?. output ?. includes ( "text" ) ?? existingModel ?. capabilities . output . text ?? true ,
1329+ audio :
1330+ model . modalities ?. output ?. includes ( "audio" ) ?? existingModel ?. capabilities . output . audio ?? false ,
1331+ image :
1332+ model . modalities ?. output ?. includes ( "image" ) ?? existingModel ?. capabilities . output . image ?? false ,
1333+ video :
1334+ model . modalities ?. output ?. includes ( "video" ) ?? existingModel ?. capabilities . output . video ?? false ,
1335+ pdf : model . modalities ?. output ?. includes ( "pdf" ) ?? existingModel ?. capabilities . output . pdf ?? false ,
1336+ } ,
1337+ interleaved :
1338+ model . interleaved ??
1339+ existingModel ?. capabilities . interleaved ??
1340+ ( ! existingModel && apiNpm === "@ai-sdk/openai-compatible" && apiID . includes ( "deepseek" )
1341+ ? { field : "reasoning_content" }
1342+ : false ) ,
13251343 } ,
1326- output : {
1327- text : model . modalities ?. output ?. includes ( "text" ) ?? existingModel ?. capabilities . output . text ?? true ,
1328- audio :
1329- model . modalities ?. output ?. includes ( "audio" ) ?? existingModel ?. capabilities . output . audio ?? false ,
1330- image :
1331- model . modalities ?. output ?. includes ( "image" ) ?? existingModel ?. capabilities . output . image ?? false ,
1332- video :
1333- model . modalities ?. output ?. includes ( "video" ) ?? existingModel ?. capabilities . output . video ?? false ,
1334- pdf : model . modalities ?. output ?. includes ( "pdf" ) ?? existingModel ?. capabilities . output . pdf ?? false ,
1344+ cost : {
1345+ input : model ?. cost ?. input ?? existingModel ?. cost ?. input ?? 0 ,
1346+ output : model ?. cost ?. output ?? existingModel ?. cost ?. output ?? 0 ,
1347+ cache : {
1348+ read : model ?. cost ?. cache_read ?? existingModel ?. cost ?. cache . read ?? 0 ,
1349+ write : model ?. cost ?. cache_write ?? existingModel ?. cost ?. cache . write ?? 0 ,
1350+ } ,
13351351 } ,
1336- interleaved :
1337- model . interleaved ??
1338- existingModel ?. capabilities . interleaved ??
1339- ( ! existingModel && apiNpm === "@ai-sdk/openai-compatible" && apiID . includes ( "deepseek" )
1340- ? { field : "reasoning_content" }
1341- : false ) ,
1342- } ,
1343- cost : {
1344- input : model ?. cost ?. input ?? existingModel ?. cost ?. input ?? 0 ,
1345- output : model ?. cost ?. output ?? existingModel ?. cost ?. output ?? 0 ,
1346- cache : {
1347- read : model ?. cost ?. cache_read ?? existingModel ?. cost ?. cache . read ?? 0 ,
1348- write : model ?. cost ?. cache_write ?? existingModel ?. cost ?. cache . write ?? 0 ,
1352+ options : mergeDeep ( existingModel ?. options ?? { } , model . options ?? { } ) ,
1353+ limit : {
1354+ context : model . limit ?. context ?? existingModel ?. limit ?. context ?? 0 ,
1355+ input : model . limit ?. input ?? existingModel ?. limit ?. input ,
1356+ output : model . limit ?. output ?? existingModel ?. limit ?. output ?? 0 ,
13491357 } ,
1350- } ,
1351- options : mergeDeep ( existingModel ?. options ?? { } , model . options ?? { } ) ,
1352- limit : {
1353- context : model . limit ?. context ?? existingModel ?. limit ?. context ?? 0 ,
1354- input : model . limit ?. input ?? existingModel ?. limit ?. input ,
1355- output : model . limit ?. output ?? existingModel ?. limit ?. output ?? 0 ,
1356- } ,
1357- headers : mergeDeep ( existingModel ?. headers ?? { } , model . headers ?? { } ) ,
1358- family : model . family ?? existingModel ?. family ?? "" ,
1359- release_date : model . release_date ?? existingModel ?. release_date ?? "" ,
1360- variants : { } ,
1358+ headers : mergeDeep ( existingModel ?. headers ?? { } , model . headers ?? { } ) ,
1359+ family : model . family ?? existingModel ?. family ?? "" ,
1360+ release_date : model . release_date ?? existingModel ?. release_date ?? "" ,
1361+ variants : { } ,
1362+ }
1363+ const merged = mergeDeep ( ProviderTransform . variants ( parsedModel ) , model . variants ?? { } )
1364+ parsedModel . variants = mapValues (
1365+ pickBy ( merged , ( v ) => ! v . disabled ) ,
1366+ ( v ) => omit ( v , [ "disabled" ] ) ,
1367+ )
1368+ parsed . models [ modelID ] = parsedModel
13611369 }
1362- const merged = mergeDeep ( ProviderTransform . variants ( parsedModel ) , model . variants ?? { } )
1363- parsedModel . variants = mapValues (
1364- pickBy ( merged , ( v ) => ! v . disabled ) ,
1365- ( v ) => omit ( v , [ "disabled" ] ) ,
1366- )
1367- parsed . models [ modelID ] = parsedModel
1370+ target [ providerID ] = parsed
13681371 }
1369- database [ providerID ] = parsed
13701372 }
13711373
1374+ applyConfig ( database )
1375+
13721376 // load env
13731377 const envs = yield * env . all ( )
13741378 for ( const [ id , provider ] of Object . entries ( database ) ) {
@@ -1461,6 +1465,35 @@ export const layer = Layer.effect(
14611465 } )
14621466 }
14631467
1468+ for ( const hook of plugins ) {
1469+ const p = hook . provider
1470+ const models = p ?. models
1471+ if ( ! p || ! models ) continue
1472+
1473+ const providerID = ProviderV2 . ID . make ( p . id )
1474+ if ( disabled . has ( providerID ) ) continue
1475+
1476+ const provider = providers [ providerID ]
1477+ if ( ! provider ) continue
1478+ const pluginAuth = yield * auth . get ( providerID ) . pipe ( Effect . orDie )
1479+
1480+ provider . models = yield * Effect . promise ( async ( ) => {
1481+ const next = await models ( provider , { auth : pluginAuth } )
1482+ return Object . fromEntries (
1483+ Object . entries ( next ) . map ( ( [ id , model ] ) => [
1484+ id ,
1485+ {
1486+ ...model ,
1487+ id : ProviderV2 . ModelID . make ( id ) ,
1488+ providerID,
1489+ } ,
1490+ ] ) ,
1491+ )
1492+ } )
1493+ }
1494+
1495+ applyConfig ( providers )
1496+
14641497 for ( const [ id , provider ] of Object . entries ( providers ) ) {
14651498 const providerID = ProviderV2 . ID . make ( id )
14661499 if ( ! isProviderAllowed ( providerID ) ) {
0 commit comments