1- import OpenAI from "openai"
2-
31export type DiscoveredModel = {
42 id : string
53 name : string
@@ -15,6 +13,19 @@ export type ConnectionTestResult = {
1513 error ?: string
1614}
1715
16+ /**
17+ * Build headers for API requests
18+ */
19+ const buildHeaders = ( apiKey ?: string ) : HeadersInit => {
20+ const headers : HeadersInit = {
21+ "Content-Type" : "application/json" ,
22+ }
23+ if ( apiKey ) {
24+ headers [ "Authorization" ] = `Bearer ${ apiKey } `
25+ }
26+ return headers
27+ }
28+
1829/**
1930 * Discover available models from an OpenAI-compatible endpoint
2031 */
@@ -23,30 +34,38 @@ export const discoverModels = async (
2334 apiKey ?: string ,
2435) : Promise < ModelDiscoveryResult > => {
2536 try {
26- const client = new OpenAI ( {
27- baseURL : normalizeBaseUrl ( baseUrl ) ,
28- apiKey : apiKey || "not-required ",
29- dangerouslyAllowBrowser : true ,
37+ const normalizedUrl = normalizeBaseUrl ( baseUrl )
38+ const response = await fetch ( ` ${ normalizedUrl } /models` , {
39+ method : "GET ",
40+ headers : buildHeaders ( apiKey ) ,
3041 } )
3142
32- const response = await client . models . list ( )
33- const models : DiscoveredModel [ ] = [ ]
34-
35- for await ( const model of response ) {
36- models . push ( {
37- id : model . id ,
38- name : model . id ,
39- owned_by : model . owned_by ,
40- } )
43+ if ( ! response . ok ) {
44+ if ( response . status === 401 ) {
45+ return { success : false , error : "Invalid API key" }
46+ }
47+ if ( response . status === 404 ) {
48+ return { success : false , error : "Models endpoint not found. Check the base URL." }
49+ }
50+ return { success : false , error : `Server returned ${ response . status } : ${ response . statusText } ` }
4151 }
4252
53+ const data = await response . json ( )
54+ const modelList = data . data || data . models || [ ]
55+
56+ const models : DiscoveredModel [ ] = modelList . map ( ( model : any ) => ( {
57+ id : model . id || model . name ,
58+ name : model . id || model . name ,
59+ owned_by : model . owned_by ,
60+ } ) )
61+
4362 // Sort models alphabetically by id
4463 models . sort ( ( a , b ) => a . id . localeCompare ( b . id ) )
4564
4665 return { success : true , models }
4766 } catch ( error ) {
4867 const message = error instanceof Error ? error . message : "Failed to discover models"
49- return { success : false , error : message }
68+ return { success : false , error : formatConnectionError ( message ) }
5069 }
5170}
5271
@@ -56,51 +75,45 @@ export const discoverModels = async (
5675export const testCustomProviderConnection = async (
5776 baseUrl : string ,
5877 apiKey ?: string ,
59- modelId ?: string ,
6078) : Promise < ConnectionTestResult > => {
6179 try {
62- const client = new OpenAI ( {
63- baseURL : normalizeBaseUrl ( baseUrl ) ,
64- apiKey : apiKey || "not-required ",
65- dangerouslyAllowBrowser : true ,
80+ const normalizedUrl = normalizeBaseUrl ( baseUrl )
81+ const response = await fetch ( ` ${ normalizedUrl } /models` , {
82+ method : "GET ",
83+ headers : buildHeaders ( apiKey ) ,
6684 } )
6785
68- if ( modelId ) {
69- // If a model is specified, try a simple completion
70- await client . chat . completions . create ( {
71- model : modelId ,
72- messages : [ { role : "user" , content : "ping" } ] ,
73- max_tokens : 5 ,
74- } )
75- } else {
76- // Otherwise just list models to verify connection
77- const response = await client . models . list ( )
78- // Consume at least one item to verify the connection works
79- for await ( const _ of response ) {
80- break
86+ if ( ! response . ok ) {
87+ if ( response . status === 401 ) {
88+ return { valid : false , error : "Invalid API key" }
8189 }
90+ if ( response . status === 404 ) {
91+ return { valid : false , error : "Endpoint not found. Check the base URL." }
92+ }
93+ return { valid : false , error : `Server returned ${ response . status } : ${ response . statusText } ` }
8294 }
8395
8496 return { valid : true }
8597 } catch ( error ) {
8698 const message = error instanceof Error ? error . message : "Connection failed"
99+ return { valid : false , error : formatConnectionError ( message ) }
100+ }
101+ }
87102
88- // Check for common error types
89- if ( message . includes ( "401" ) || message . toLowerCase ( ) . includes ( "unauthorized" ) ) {
90- return { valid : false , error : "Invalid API key" }
91- }
92- if ( message . includes ( "404" ) || message . toLowerCase ( ) . includes ( "not found" ) ) {
93- return { valid : false , error : "Endpoint not found. Check the base URL." }
94- }
95- if ( message . includes ( "ECONNREFUSED" ) || message . toLowerCase ( ) . includes ( "connection refused" ) ) {
96- return { valid : false , error : "Connection refused. Is the server running?" }
97- }
98- if ( message . toLowerCase ( ) . includes ( "network" ) || message . toLowerCase ( ) . includes ( "fetch" ) ) {
99- return { valid : false , error : "Network error. Check the URL and try again." }
100- }
101-
102- return { valid : false , error : message }
103+ /**
104+ * Format connection error messages to be more user-friendly
105+ */
106+ const formatConnectionError = ( message : string ) : string => {
107+ if ( message . includes ( "ECONNREFUSED" ) || message . toLowerCase ( ) . includes ( "connection refused" ) ) {
108+ return "Connection refused. Is the server running?"
109+ }
110+ if ( message . toLowerCase ( ) . includes ( "network" ) || message . toLowerCase ( ) . includes ( "fetch failed" ) ) {
111+ return "Network error. Check the URL and try again."
112+ }
113+ if ( message . toLowerCase ( ) . includes ( "cors" ) ) {
114+ return "CORS error. The server may need to allow requests from this origin."
103115 }
116+ return message
104117}
105118
106119/**
0 commit comments