@@ -10,6 +10,7 @@ import type {
1010 EmbedContentParameters ,
1111 GenerateContentResponse ,
1212 GenerateContentParameters ,
13+ GenerateContentConfig ,
1314} from '@google/genai' ;
1415import type { Config } from '../config/config.js' ;
1516import type { ContentGenerator } from './contentGenerator.js' ;
@@ -81,6 +82,19 @@ export interface GenerateContentOptions {
8182 maxAttempts ?: number ;
8283}
8384
85+ interface _CommonGenerateOptions {
86+ modelConfigKey : ModelConfigKey ;
87+ contents : Content [ ] ;
88+ systemInstruction ?: string | Part | Part [ ] | Content ;
89+ abortSignal : AbortSignal ;
90+ promptId : string ;
91+ maxAttempts ?: number ;
92+ additionalProperties ?: {
93+ responseJsonSchema : Record < string , unknown > ;
94+ responseMimeType : string ;
95+ } ;
96+ }
97+
8498/**
8599 * A client dedicated to stateless, utility-focused LLM calls.
86100 */
@@ -104,7 +118,7 @@ export class BaseLlmClient {
104118 maxAttempts,
105119 } = options ;
106120
107- const { model, generateContentConfig } =
121+ const { model } =
108122 this . config . modelConfigService . getResolvedConfig ( modelConfigKey ) ;
109123
110124 const shouldRetryOnContent = ( response : GenerateContentResponse ) => {
@@ -123,18 +137,17 @@ export class BaseLlmClient {
123137
124138 const result = await this . _generateWithRetry (
125139 {
126- model ,
140+ modelConfigKey ,
127141 contents,
128- config : {
129- ...generateContentConfig ,
130- ...( systemInstruction && { systemInstruction } ) ,
142+ abortSignal,
143+ promptId,
144+ maxAttempts,
145+ systemInstruction,
146+ additionalProperties : {
131147 responseJsonSchema : schema ,
132148 responseMimeType : 'application/json' ,
133- abortSignal,
134149 } ,
135150 } ,
136- promptId ,
137- maxAttempts ,
138151 shouldRetryOnContent ,
139152 'generateJson' ,
140153 ) ;
@@ -205,80 +218,81 @@ export class BaseLlmClient {
205218 maxAttempts,
206219 } = options ;
207220
208- const { model, generateContentConfig } =
209- this . config . modelConfigService . getResolvedConfig ( modelConfigKey ) ;
210-
211221 const shouldRetryOnContent = ( response : GenerateContentResponse ) => {
212222 const text = getResponseText ( response ) ?. trim ( ) ;
213223 return ! text ; // Retry on empty response
214224 } ;
215225
216226 return this . _generateWithRetry (
217227 {
218- model ,
228+ modelConfigKey ,
219229 contents,
220- config : {
221- ...generateContentConfig ,
222- ...( systemInstruction && { systemInstruction } ) ,
223- abortSignal,
224- } ,
230+ systemInstruction,
231+ abortSignal,
232+ promptId,
233+ maxAttempts,
225234 } ,
226- promptId ,
227- maxAttempts ,
228235 shouldRetryOnContent ,
229236 'generateContent' ,
230237 ) ;
231238 }
232239
233240 private async _generateWithRetry (
234- requestParams : GenerateContentParameters ,
235- promptId : string ,
236- maxAttempts : number | undefined ,
241+ options : _CommonGenerateOptions ,
237242 shouldRetryOnContent : ( response : GenerateContentResponse ) => boolean ,
238243 errorContext : 'generateJson' | 'generateContent' ,
239244 ) : Promise < GenerateContentResponse > {
240- const abortSignal = requestParams . config ?. abortSignal ;
241-
242- // Define callback to fetch context dynamically since active model may get updated during retry loop
243- const getAvailabilityContext = createAvailabilityContextProvider (
244- this . config ,
245- ( ) => requestParams . model ,
246- ) ;
245+ const {
246+ modelConfigKey,
247+ contents,
248+ systemInstruction,
249+ abortSignal,
250+ promptId,
251+ maxAttempts,
252+ additionalProperties,
253+ } = options ;
247254
248255 const {
249256 model,
250- config : newConfig ,
257+ config : generateContentConfig ,
251258 maxAttempts : availabilityMaxAttempts ,
252- } = applyModelSelection (
259+ } = applyModelSelection ( this . config , modelConfigKey ) ;
260+
261+ let currentModel = model ;
262+ let currentGenerateContentConfig = generateContentConfig ;
263+
264+ // Define callback to fetch context dynamically since active model may get updated during retry loop
265+ const getAvailabilityContext = createAvailabilityContextProvider (
253266 this . config ,
254- requestParams . model ,
255- requestParams . config ,
267+ ( ) => currentModel ,
256268 ) ;
257- requestParams . model = model ;
258- if ( newConfig ) {
259- requestParams . config = newConfig ;
260- }
261- if ( abortSignal ) {
262- requestParams . config = { ...requestParams . config , abortSignal } ;
263- }
264269
265270 try {
266271 const apiCall = ( ) => {
267272 // Ensure we use the current active model
268273 // in case a fallback occurred in a previous attempt.
269274 const activeModel = this . config . getActiveModel ( ) ;
270- if ( activeModel !== requestParams . model ) {
271- requestParams . model = activeModel ;
275+ if ( activeModel !== currentModel ) {
276+ currentModel = activeModel ;
272277 // Re-resolve config if model changed during retry
273278 const { generateContentConfig } =
274279 this . config . modelConfigService . getResolvedConfig ( {
280+ ...modelConfigKey ,
275281 model : activeModel ,
276282 } ) ;
277- requestParams . config = {
278- ...requestParams . config ,
279- ...generateContentConfig ,
280- } ;
283+ currentGenerateContentConfig = generateContentConfig ;
281284 }
285+ const finalConfig : GenerateContentConfig = {
286+ ...currentGenerateContentConfig ,
287+ ...( systemInstruction && { systemInstruction } ) ,
288+ ...additionalProperties ,
289+ abortSignal,
290+ } ;
291+ const requestParams : GenerateContentParameters = {
292+ model : currentModel ,
293+ config : finalConfig ,
294+ contents,
295+ } ;
282296 return this . contentGenerator . generateContent ( requestParams , promptId ) ;
283297 } ;
284298
@@ -289,7 +303,7 @@ export class BaseLlmClient {
289303 getAvailabilityContext,
290304 onPersistent429 : this . config . isInteractive ( )
291305 ? ( authType , error ) =>
292- handleFallback ( this . config , requestParams . model , authType , error )
306+ handleFallback ( this . config , currentModel , authType , error )
293307 : undefined ,
294308 authType :
295309 this . authType ?? this . config . getContentGeneratorConfig ( ) ?. authType ,
@@ -307,14 +321,14 @@ export class BaseLlmClient {
307321 await reportError (
308322 error ,
309323 `API returned invalid content after all retries.` ,
310- requestParams . contents as Content [ ] ,
324+ contents ,
311325 `${ errorContext } -invalid-content` ,
312326 ) ;
313327 } else {
314328 await reportError (
315329 error ,
316330 `Error generating content via API.` ,
317- requestParams . contents as Content [ ] ,
331+ contents ,
318332 `${ errorContext } -api` ,
319333 ) ;
320334 }
0 commit comments