@@ -1051,4 +1051,139 @@ describe("OpenAICompatibleEmbedder", () => {
10511051 expect ( result . error ) . toBe ( "embeddings:validation.configurationError" )
10521052 } )
10531053 } )
1054+
1055+ describe ( "Gemini compatibility" , ( ) => {
1056+ let geminiEmbedder : OpenAICompatibleEmbedder
1057+ const geminiBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai/"
1058+ const geminiApiKey = "test-gemini-api-key"
1059+ const geminiModelId = "gemini-embedding-001"
1060+
1061+ beforeEach ( ( ) => {
1062+ vitest . clearAllMocks ( )
1063+ geminiEmbedder = new OpenAICompatibleEmbedder ( geminiBaseUrl , geminiApiKey , geminiModelId )
1064+ } )
1065+
1066+ it ( "should NOT include encoding_format for Gemini endpoints" , async ( ) => {
1067+ const testTexts = [ "Hello world" ]
1068+ const mockResponse = {
1069+ data : [ { embedding : [ 0.1 , 0.2 , 0.3 ] } ] ,
1070+ usage : { prompt_tokens : 10 , total_tokens : 15 } ,
1071+ }
1072+ mockEmbeddingsCreate . mockResolvedValue ( mockResponse )
1073+
1074+ await geminiEmbedder . createEmbeddings ( testTexts )
1075+
1076+ // Verify that encoding_format is NOT included for Gemini
1077+ expect ( mockEmbeddingsCreate ) . toHaveBeenCalledWith ( {
1078+ input : testTexts ,
1079+ model : geminiModelId ,
1080+ // encoding_format should NOT be present
1081+ } )
1082+
1083+ // Verify the call doesn't have encoding_format property
1084+ const callArgs = mockEmbeddingsCreate . mock . calls [ 0 ] [ 0 ]
1085+ expect ( callArgs ) . not . toHaveProperty ( "encoding_format" )
1086+ } )
1087+
1088+ it ( "should still include encoding_format for non-Gemini OpenAI-compatible endpoints" , async ( ) => {
1089+ // Create a non-Gemini embedder
1090+ const regularEmbedder = new OpenAICompatibleEmbedder ( testBaseUrl , testApiKey , testModelId )
1091+
1092+ const testTexts = [ "Hello world" ]
1093+ const mockResponse = {
1094+ data : [ { embedding : [ 0.1 , 0.2 , 0.3 ] } ] ,
1095+ usage : { prompt_tokens : 10 , total_tokens : 15 } ,
1096+ }
1097+ mockEmbeddingsCreate . mockResolvedValue ( mockResponse )
1098+
1099+ await regularEmbedder . createEmbeddings ( testTexts )
1100+
1101+ // Verify that encoding_format IS included for non-Gemini endpoints
1102+ expect ( mockEmbeddingsCreate ) . toHaveBeenCalledWith ( {
1103+ input : testTexts ,
1104+ model : testModelId ,
1105+ encoding_format : "base64" ,
1106+ } )
1107+ } )
1108+
1109+ it ( "should correctly identify Gemini URLs" , ( ) => {
1110+ const geminiUrls = [
1111+ "https://generativelanguage.googleapis.com/v1beta/openai/" ,
1112+ "https://generativelanguage.googleapis.com/v1/openai/" ,
1113+ "https://generativelanguage.googleapis.com/v2/embeddings" ,
1114+ ]
1115+
1116+ geminiUrls . forEach ( ( url ) => {
1117+ const embedder = new OpenAICompatibleEmbedder ( url , geminiApiKey , geminiModelId )
1118+ const isGemini = ( embedder as any ) . isGeminiUrl ( url )
1119+ expect ( isGemini ) . toBe ( true )
1120+ } )
1121+ } )
1122+
1123+ it ( "should not identify non-Gemini URLs as Gemini" , ( ) => {
1124+ const nonGeminiUrls = [
1125+ "https://api.openai.com/v1" ,
1126+ "https://api.example.com/embeddings" ,
1127+ "https://myinstance.openai.azure.com/openai/deployments/my-deployment/embeddings" ,
1128+ "http://localhost:8080" ,
1129+ ]
1130+
1131+ nonGeminiUrls . forEach ( ( url ) => {
1132+ const embedder = new OpenAICompatibleEmbedder ( url , testApiKey , testModelId )
1133+ const isGemini = ( embedder as any ) . isGeminiUrl ( url )
1134+ expect ( isGemini ) . toBe ( false )
1135+ } )
1136+ } )
1137+
1138+ it ( "should validate Gemini configuration without encoding_format" , async ( ) => {
1139+ const mockResponse = {
1140+ data : [ { embedding : [ 0.1 , 0.2 , 0.3 ] } ] ,
1141+ usage : { prompt_tokens : 2 , total_tokens : 2 } ,
1142+ }
1143+ mockEmbeddingsCreate . mockResolvedValue ( mockResponse )
1144+
1145+ const result = await geminiEmbedder . validateConfiguration ( )
1146+
1147+ expect ( result . valid ) . toBe ( true )
1148+ expect ( result . error ) . toBeUndefined ( )
1149+
1150+ // Verify validation call doesn't include encoding_format
1151+ const callArgs = mockEmbeddingsCreate . mock . calls [ 0 ] [ 0 ]
1152+ expect ( callArgs ) . not . toHaveProperty ( "encoding_format" )
1153+ } )
1154+
1155+ it ( "should handle direct HTTP requests for Gemini full URLs without encoding_format" , async ( ) => {
1156+ const geminiFullUrl = "https://generativelanguage.googleapis.com/v1beta/openai/embeddings"
1157+ const fullUrlEmbedder = new OpenAICompatibleEmbedder ( geminiFullUrl , geminiApiKey , geminiModelId )
1158+
1159+ const mockFetch = vitest . fn ( ) . mockResolvedValue ( {
1160+ ok : true ,
1161+ json : async ( ) => ( {
1162+ data : [ { embedding : [ 0.1 , 0.2 , 0.3 ] } ] ,
1163+ usage : { prompt_tokens : 10 , total_tokens : 15 } ,
1164+ } ) ,
1165+ } )
1166+ global . fetch = mockFetch
1167+
1168+ await fullUrlEmbedder . createEmbeddings ( [ "test" ] )
1169+
1170+ // Check that the request body doesn't include encoding_format
1171+ expect ( mockFetch ) . toHaveBeenCalledWith (
1172+ geminiFullUrl ,
1173+ expect . objectContaining ( {
1174+ method : "POST" ,
1175+ body : JSON . stringify ( {
1176+ input : [ "test" ] ,
1177+ model : geminiModelId ,
1178+ // encoding_format should NOT be present
1179+ } ) ,
1180+ } ) ,
1181+ )
1182+
1183+ // Verify the actual body content
1184+ const callArgs = mockFetch . mock . calls [ 0 ] [ 1 ]
1185+ const bodyContent = JSON . parse ( callArgs . body )
1186+ expect ( bodyContent ) . not . toHaveProperty ( "encoding_format" )
1187+ } )
1188+ } )
10541189} )
0 commit comments