2026-06-15 01:08:05 -04:00
// REAL survey: send an image to EVERY chat LLM (all providers with active creds) and
// record how each model behaves vs its declared vision capability.
// Purpose: discover where non-vision models 400 on image input (need auto-strip) and
// where capability data is wrong. This is a SURVEY: it logs a table, it does not fail
// on capability mismatches (only real harness errors throw).
//
// RUN_REAL=1 npx vitest run --config tests/vitest.config.js tests/translator/real/vision-capability-survey.real.test.js
// RUN_REAL=1 REAL_PROVIDERS=mistral,nvidia npx vitest run ... (optional filter)
import { describe , it , expect , afterAll } from "vitest" ;
import { getProviderCredentials } from "../../../src/sse/services/auth.js" ;
import { checkAndRefreshToken } from "../../../src/sse/services/tokenRefresh.js" ;
import { handleChatCore } from "../../../open-sse/handlers/chatCore.js" ;
import { getModelsByProviderId } from "../../../open-sse/config/providerModels.js" ;
import { getCapabilitiesForModel } from "../../../open-sse/providers/capabilities.js" ;
const RUN _REAL = process . env . RUN _REAL === "1" ;
const TIMEOUT _MS = 90000 ;
const CRED _ISSUE = [ 401 , 402 , 403 , 429 ] ;
const NON _CHAT _KINDS = new Set ( [ "embedding" , "image" , "imageToText" , "tts" , "stt" , "video" , "music" , "webSearch" ] ) ;
const PROVIDER _FILTER = ( process . env . REAL _PROVIDERS || "" )
. split ( "," ) . map ( ( s ) => s . trim ( ) ) . filter ( Boolean ) ;
// Valid 16x16 red PNG (1x1 is rejected as malformed by some strict providers).
const PNG _DATA _URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAFklEQVR4nGO4I2JDEmIY1TCqYfhqAAAeBCwQ8YdREQAAAABJRU5ErkJggg==" ;
// Tiny silent WAV (44-byte header, no samples) — enough to probe audioInput acceptance.
const WAV _B64 = "UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=" ;
2026-06-15 07:18:04 -04:00
// Stable public image URL (probe remote-URL handling vs base64).
const IMAGE _REMOTE _URL = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" ;
// Tiny valid PDF (probe file/document handling).
const PDF _B64 = "JVBERi0xLjEKMSAwIG9iajw8L1R5cGUvQ2F0YWxvZy9QYWdlcyAyIDAgUj4+ZW5kb2JqCjIgMCBvYmo8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PmVuZG9iagozIDAgb2JqPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9NZWRpYUJveFswIDAgMjAwIDIwMF0+PmVuZG9iagp4cmVmCjAgNAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA1MiAwMDAwMCBuIAowMDAwMDAwMTAxIDAwMDAwIG4gCnRyYWlsZXI8PC9TaXplIDQvUm9vdCAxIDAgUj4+CnN0YXJ0eHJlZgoxNjYKJSVFT0Y=" ;
const PDF _DATA _URI = ` data:application/pdf;base64, ${ PDF _B64 } ` ;
2026-06-15 01:08:05 -04:00
// Capability probes: each sends one modality-specific content block. cap = capability flag tested.
const CAPABILITY _PROBES = {
vision : {
cap : "vision" ,
content : [
{ type : "text" , text : "What is in this image? One word." } ,
{ type : "image_url" , image _url : { url : PNG _DATA _URI } } ,
] ,
} ,
audio : {
cap : "audioInput" ,
content : [
{ type : "text" , text : "Transcribe this audio. One word." } ,
{ type : "input_audio" , input _audio : { data : WAV _B64 , format : "wav" } } ,
] ,
} ,
2026-06-15 07:18:04 -04:00
imageUrl : {
cap : "vision" ,
content : [
{ type : "text" , text : "What is in this image? One word." } ,
{ type : "image_url" , image _url : { url : IMAGE _REMOTE _URL } } ,
] ,
} ,
file : {
cap : "pdf" ,
content : [
{ type : "text" , text : "Summarize this document. One word." } ,
{ type : "file" , file : { filename : "doc.pdf" , file _data : PDF _DATA _URI } } ,
] ,
} ,
2026-06-15 01:08:05 -04:00
} ;
// Set PROBES=vision,audio (default vision only to limit live quota).
const ACTIVE _PROBES = ( process . env . PROBES || "vision" ) . split ( "," ) . map ( ( s ) => s . trim ( ) ) . filter ( Boolean ) ;
// Status/message patterns that indicate account/credential issues (not a vision-capability signal).
const CRED _MSG _RE = /subscription|unauthorized|invalid api key|invalid access token|insufficient|credits|payment|spending|organization policy|disallowed|quota|exhausted|not supported when using|not available for integrator|requires a subscription|model.*not found|does not exist|not yet known|requires a role/i ;
// Collected rows for the end-of-run summary table.
const results = [ ] ;
async function drainSSE ( response ) {
if ( ! response ? . body ) return "" ;
const reader = response . body . getReader ( ) ;
const decoder = new TextDecoder ( ) ;
let out = "" ;
while ( true ) {
const { done , value } = await reader . read ( ) ;
if ( done ) break ;
out += decoder . decode ( value , { stream : true } ) ;
}
return out ;
}
function chatModels ( providerId ) {
return getModelsByProviderId ( providerId ) . filter ( ( m ) => ! NON _CHAT _KINDS . has ( m . kind || m . type || "llm" ) ) ;
}
function targetProviders ( ) {
try {
const Database = require ( "better-sqlite3" ) ;
const os = require ( "os" ) ;
const path = require ( "path" ) ;
const dbPath = process . env . DATA _DIR
? path . join ( process . env . DATA _DIR , "db" , "data.sqlite" )
: path . join ( os . homedir ( ) , ".9router" , "db" , "data.sqlite" ) ;
const db = new Database ( dbPath , { readonly : true } ) ;
const rows = db . prepare ( "SELECT DISTINCT provider FROM providerConnections WHERE isActive = 1" ) . all ( ) ;
db . close ( ) ;
let list = rows . map ( ( r ) => r . provider ) . sort ( ) ;
if ( PROVIDER _FILTER . length ) list = list . filter ( ( p ) => PROVIDER _FILTER . includes ( p ) ) ;
return list ;
} catch {
return [ ] ;
}
}
describe . skipIf ( ! RUN _REAL ) ( "REAL vision capability survey" , ( ) => {
const providers = RUN _REAL ? targetProviders ( ) : [ ] ;
it ( "has active providers in DB" , ( ) => {
expect ( providers . length ) . toBeGreaterThan ( 0 ) ;
} ) ;
for ( const providerId of providers ) {
for ( const m of ( RUN _REAL ? chatModels ( providerId ) : [ ] ) ) {
const model = m . id ;
for ( const probeName of ACTIVE _PROBES ) {
const probe = CAPABILITY _PROBES [ probeName ] ;
it . concurrent ( ` ${ probeName } | ${ providerId } / ${ model } ` , async ( ) => {
const caps = getCapabilitiesForModel ( providerId , model ) ;
const capOn = ! ! caps [ probe . cap ] ;
const credentials = await getProviderCredentials ( providerId , new Set ( ) , model ) ;
if ( ! credentials || credentials . allRateLimited ) {
results . push ( { probe : probeName , providerId , model , cap : capOn , status : "no-cred" , verdict : "skip" } ) ;
return expect ( true ) . toBe ( true ) ;
}
const refreshed = await checkAndRefreshToken ( providerId , credentials ) ;
const result = await handleChatCore ( {
body : {
model : ` ${ providerId } / ${ model } ` ,
stream : true ,
max _tokens : 64 ,
messages : [ { role : "user" , content : probe . content } ] ,
} ,
modelInfo : { provider : providerId , model } ,
credentials : refreshed ,
connectionId : credentials . connectionId ,
} ) ;
const status = Number ( result . status ) || ( result . success ? 200 : 0 ) ;
const errMsg = result . success ? "" : String ( result . error || "" ) ;
const credIssue = CRED _ISSUE . includes ( status ) || CRED _MSG _RE . test ( errMsg ) ;
const ok = result . success ;
// Classify outcome vs declared capability (cred/account noise filtered out).
let verdict ;
if ( credIssue ) verdict = "skip-cred" ;
else if ( ok && capOn ) verdict = ` ok- ${ probeName } ` ;
else if ( ok && ! capOn ) verdict = ` ok-no ${ probeName } -tolerated ` ; // provider silently ignored modality
else if ( ! ok && ! capOn ) verdict = ` FAIL-no ${ probeName } -needs-strip ` ; // target problem
else verdict = ` FAIL- ${ probeName } -capdata-wrong ` ; // declared cap but rejected
if ( ok ) await drainSSE ( result . response ) . catch ( ( ) => { } ) ;
results . push ( { probe : probeName , providerId , model , cap : capOn , status , verdict , error : ok ? "" : String ( result . error || "" ) . slice ( 0 , 80 ) } ) ;
// Survey never fails on capability outcome.
return expect ( true ) . toBe ( true ) ;
} , TIMEOUT _MS ) ;
}
}
}
afterAll ( ( ) => {
if ( ! results . length ) return ;
// FAIL groups first (most actionable), then ok/skip; alphabetical within rank.
const rank = ( v ) => ( v . startsWith ( "FAIL" ) ? 0 : v . startsWith ( "ok" ) ? 1 : 2 ) ;
const groups = [ ... new Set ( results . map ( ( r ) => r . verdict ) ) ]
. sort ( ( a , b ) => rank ( a ) - rank ( b ) || a . localeCompare ( b ) ) ;
console . log ( "\n================ CAPABILITY SURVEY ================" ) ;
for ( const g of groups ) {
const rows = results . filter ( ( r ) => r . verdict === g ) ;
if ( ! rows . length ) continue ;
console . log ( ` \n ### ${ g } ( ${ rows . length } ) ` ) ;
for ( const r of rows ) {
console . log ( ` [ ${ r . status } ] ${ r . probe } ${ r . providerId } / ${ r . model } cap= ${ r . cap } ${ r . error ? ` :: ${ r . error } ` : "" } ` ) ;
}
}
console . log ( "\n================ END SURVEY ================\n" ) ;
} ) ;
} ) ;