@@ -1207,6 +1207,9 @@ route("GET", "/billing/subscription", handleGetSubscription);
12071207route ( "POST" , "/billing/cancel" , handleCancelSubscription ) ;
12081208route ( "GET" , "/billing/transactions" , handleListTransactions ) ;
12091209
1210+ // ── BYOK AI Diagnostics ──
1211+ route ( "GET" , "/ai/byok-test" , handleBYOKTest ) ;
1212+
12101213// ═════════════════════════════════════════════════════════════════════════════════════
12111214// § 13. MAIN ENTRY POINT
12121215// ═════════════════════════════════════════════════════════════════════════════════════
@@ -6970,3 +6973,71 @@ async function handleListTransactions(request, env, ctx) {
69706973 const data = await res . json ( ) ;
69716974 return apiResponse ( data ) ;
69726975}
6976+
6977+ /**
6978+ * GET /ai/byok-test — Diagnostic endpoint to verify BYOK AI configuration.
6979+ * Returns provider info, model, latency, and a sample response.
6980+ */
6981+ async function handleBYOKTest ( request , env , ctx ) {
6982+ requireAuth ( ctx ) ;
6983+ const tenantId = ctx . tenantId ;
6984+
6985+ // 1. Fetch AI config
6986+ const aiConfig = await getCompanyAIConfig ( env , tenantId ) ;
6987+
6988+ if ( ! aiConfig ) {
6989+ return apiResponse ( {
6990+ status : "default" ,
6991+ message : "No custom AI configured. Using Cloudflare Workers AI (default)." ,
6992+ provider : "cloudflare" ,
6993+ model : CF_DEFAULT_MODEL ,
6994+ byok_active : false ,
6995+ } ) ;
6996+ }
6997+
6998+ // 2. Resolve provider details
6999+ const { baseUrl, model } = resolveProviderDefaults ( aiConfig ) ;
7000+
7001+ // 3. Send a tiny test prompt
7002+ const testMessages = [
7003+ { role : "system" , content : "You are a helpful assistant. Respond in exactly one sentence." } ,
7004+ { role : "user" , content : "Say hello and confirm which AI model you are." } ,
7005+ ] ;
7006+
7007+ const startTime = Date . now ( ) ;
7008+ try {
7009+ const result = aiConfig . provider === "cloudflare"
7010+ ? await env . AI . run ( aiConfig . cfModel || CF_DEFAULT_MODEL , { messages : testMessages , max_tokens : 100 } )
7011+ : await callExternalLLM ( aiConfig , testMessages , 100 , false ) ;
7012+
7013+ const latencyMs = Date . now ( ) - startTime ;
7014+ const responseText = result ?. response || result ?. choices ?. [ 0 ] ?. message ?. content || "(empty response)" ;
7015+
7016+ console . log ( `[BYOK-TEST] SUCCESS for tenant=${ tenantId } : provider=${ aiConfig . provider } , model=${ model } , latency=${ latencyMs } ms` ) ;
7017+
7018+ return apiResponse ( {
7019+ status : "connected" ,
7020+ byok_active : true ,
7021+ provider : aiConfig . provider ,
7022+ model : model || aiConfig . cfModel || "(default)" ,
7023+ base_url : baseUrl || "(cloudflare native)" ,
7024+ latency_ms : latencyMs ,
7025+ response_snippet : responseText . slice ( 0 , 200 ) ,
7026+ message : `Custom AI (${ aiConfig . provider } ) is working. Response received in ${ latencyMs } ms.` ,
7027+ } ) ;
7028+ } catch ( err ) {
7029+ const latencyMs = Date . now ( ) - startTime ;
7030+ console . error ( `[BYOK-TEST] FAILED for tenant=${ tenantId } : provider=${ aiConfig . provider } , error=${ err . message } ` ) ;
7031+
7032+ return apiResponse ( {
7033+ status : "error" ,
7034+ byok_active : true ,
7035+ provider : aiConfig . provider ,
7036+ model : model || "(unknown)" ,
7037+ base_url : baseUrl || "(unknown)" ,
7038+ latency_ms : latencyMs ,
7039+ error : err . message ,
7040+ message : `Custom AI (${ aiConfig . provider } ) failed: ${ err . message } ` ,
7041+ } , HTTP . OK ) ; // Return 200 with error details for diagnostic visibility
7042+ }
7043+ }
0 commit comments