@@ -90,6 +90,109 @@ describe('TtsRuntimeAdapter', () => {
9090 } )
9191 } )
9292
93+ it ( 'retries a Vertex 200 with no audio and succeeds on the next attempt' , async ( ) => {
94+ const pcm = Buffer . from ( [ 1 , 0 , 2 , 0 ] )
95+ const fetchMock = vi
96+ . fn ( )
97+ . mockResolvedValueOnce (
98+ new Response (
99+ JSON . stringify ( { candidates : [ { content : { parts : [ ] } } ] } ) ,
100+ {
101+ status : 200 ,
102+ headers : { 'content-type' : 'application/json' } ,
103+ } ,
104+ ) ,
105+ )
106+ . mockResolvedValueOnce (
107+ new Response (
108+ JSON . stringify ( {
109+ candidates : [
110+ {
111+ content : {
112+ parts : [
113+ {
114+ inlineData : {
115+ data : pcm . toString ( 'base64' ) ,
116+ mimeType : 'audio/pcm;rate=24000;channels=1' ,
117+ } ,
118+ } ,
119+ ] ,
120+ } ,
121+ } ,
122+ ] ,
123+ } ) ,
124+ { status : 200 , headers : { 'content-type' : 'application/json' } } ,
125+ ) ,
126+ )
127+ vi . stubGlobal ( 'fetch' , fetchMock )
128+
129+ const adapter = new TtsRuntimeAdapter ( {
130+ provider : 'vertex' ,
131+ providerType : AIProviderType . GoogleVertex ,
132+ projectId : 'example-project' ,
133+ apiKey : 'vertex-key' ,
134+ endpoint :
135+ 'https://aiplatform.googleapis.com/v1/projects/example-project/locations/global/endpoints/openapi' ,
136+ model : 'gemini-3.1-flash-tts-preview' ,
137+ retryDelayMs : 0 ,
138+ } )
139+ const result = await adapter . generateSpeech ( {
140+ input : '你好' ,
141+ language : 'zh' ,
142+ voice : 'Kore' ,
143+ speed : 1 ,
144+ } )
145+
146+ expect ( fetchMock ) . toHaveBeenCalledTimes ( 2 )
147+ expect ( result . buffer . subarray ( 44 ) ) . toEqual ( pcm )
148+ } )
149+
150+ it ( 'uses Vertex audio from a later part when the first part has no inline data' , async ( ) => {
151+ const pcm = Buffer . from ( [ 1 , 0 , 2 , 0 ] )
152+ const fetchMock = vi . fn ( ) . mockResolvedValue (
153+ new Response (
154+ JSON . stringify ( {
155+ candidates : [
156+ {
157+ content : {
158+ parts : [
159+ { text : '' } ,
160+ {
161+ inlineData : {
162+ data : pcm . toString ( 'base64' ) ,
163+ mimeType : 'audio/pcm;rate=24000;channels=1' ,
164+ } ,
165+ } ,
166+ ] ,
167+ } ,
168+ } ,
169+ ] ,
170+ } ) ,
171+ { status : 200 , headers : { 'content-type' : 'application/json' } } ,
172+ ) ,
173+ )
174+ vi . stubGlobal ( 'fetch' , fetchMock )
175+
176+ const adapter = new TtsRuntimeAdapter ( {
177+ provider : 'vertex' ,
178+ providerType : AIProviderType . GoogleVertex ,
179+ projectId : 'example-project' ,
180+ apiKey : 'vertex-key' ,
181+ endpoint :
182+ 'https://aiplatform.googleapis.com/v1/projects/example-project/locations/global/endpoints/openapi' ,
183+ model : 'gemini-3.1-flash-tts-preview' ,
184+ } )
185+ const result = await adapter . generateSpeech ( {
186+ input : '你好' ,
187+ language : 'zh' ,
188+ voice : 'Kore' ,
189+ speed : 1 ,
190+ } )
191+
192+ expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 )
193+ expect ( result . buffer . subarray ( 44 ) ) . toEqual ( pcm )
194+ } )
195+
93196 it ( 'rejects unsupported provider protocols instead of falling back' , ( ) => {
94197 expect (
95198 ( ) =>
0 commit comments