@@ -51,13 +51,35 @@ import { isAsync, matchPollRoute } from './model-server/async-request'
5151import { sanitizeChatMessages } from './model-server/chat-messages'
5252import { parseMultipart } from './model-server/multipart'
5353import { tagLlmEntries , modelEntry , ollamaMirror } from './model-server/models-list'
54+ import { buildGatewayModalities , type GatewayModalities } from './model-server/health'
5455
5556const UPSTREAM_HOST = '127.0.0.1'
5657const UPSTREAM_PORT = LLAMA_SERVER_PORT // bundled llama-server (see llm.ts)
5758const MAX_UPLOAD = 200 * 1024 * 1024 // 200MB upload cap (audio / init image)
5859
5960let server : http . Server | null = null
6061
62+ async function liveGatewayModalities ( imageAvailable : boolean ) : Promise < GatewayModalities > {
63+ let installed : string [ ] = [ ]
64+ try {
65+ const models = await import ( './models-manager' )
66+ installed = await models . listInstalled ( )
67+ } catch {
68+ /* unavailable model registry means optional modalities are not ready */
69+ }
70+ const transcription = getActiveModal ( 'transcription' )
71+ const speech = getActiveModal ( 'speech' )
72+ const chat = llm . modelsExist ( )
73+ return buildGatewayModalities ( {
74+ chat,
75+ vision : chat && llm . hasVision ( ) ,
76+ embeddings : true ,
77+ transcription : ! ! whisperModel ( ) || ( ! ! transcription && installed . includes ( transcription ) ) ,
78+ speech : ! ! speech && installed . includes ( speech ) ,
79+ image : imageAvailable
80+ } )
81+ }
82+
6183function json ( res : http . ServerResponse , status : number , body : unknown ) : void {
6284 res . writeHead ( status , { 'Content-Type' : 'application/json' } )
6385 res . end ( JSON . stringify ( body ) )
@@ -893,7 +915,7 @@ async function handleImageEdit(
893915export function startModelServer ( port = GATEWAY_PORT ) : void {
894916 if ( server ) return
895917
896- server = http . createServer ( ( req , res ) => {
918+ server = http . createServer ( async ( req , res ) => {
897919 res . setHeader ( 'Access-Control-Allow-Origin' , '*' )
898920 res . setHeader ( 'Access-Control-Allow-Headers' , '*' )
899921 res . setHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, OPTIONS' )
@@ -921,21 +943,14 @@ export function startModelServer(port = GATEWAY_PORT): void {
921943
922944 if ( url === '/' || url === '/health' ) {
923945 const img = imageGenStatus ( )
946+ const modalities = await liveGatewayModalities ( img . available )
924947 json ( res , 200 , {
925948 name : 'Off Grid AI — local model gateway' ,
926949 openai_compatible : true ,
927950 base_url : `http://127.0.0.1:${ port } /v1` ,
928951 docs : `http://127.0.0.1:${ port } /docs` ,
929952 mcp : `http://127.0.0.1:${ port } /mcp` ,
930- modalities : {
931- text : 'ready' ,
932- vision_understanding : 'ready' ,
933- embeddings : 'ready' ,
934- transcription : 'ready' ,
935- speech : 'ready' ,
936- image_generation : img . available ? 'ready' : 'not_installed' ,
937- image_edit : img . available ? 'ready' : 'not_installed'
938- } ,
953+ modalities,
939954 image_models : img . models ,
940955 image_reason : img . available ? undefined : img . reason
941956 } )
@@ -944,15 +959,8 @@ export function startModelServer(port = GATEWAY_PORT): void {
944959
945960 if ( url === '/openapi.json' ) {
946961 const img = imageGenStatus ( )
947- const mods = {
948- text : 'ready' ,
949- vision_understanding : 'ready' ,
950- embeddings : 'ready' ,
951- transcription : 'ready' ,
952- speech : 'ready' ,
953- image_generation : img . available ? 'ready' : 'not_installed'
954- }
955- json ( res , 200 , openApiSpec ( port , mods , img . models ) )
962+ const modalities = await liveGatewayModalities ( img . available )
963+ json ( res , 200 , openApiSpec ( port , modalities , img . models ) )
956964 return
957965 }
958966
0 commit comments