@@ -21,6 +21,7 @@ interface GenerateOptions<T> {
21
21
prompt ?: string ;
22
22
system ?: string ;
23
23
messages ?: CoreMessage [ ] ;
24
+ numRetries ?: number ;
24
25
}
25
26
26
27
export class ObjectGeneratorSafe {
@@ -135,8 +136,13 @@ export class ObjectGeneratorSafe {
135
136
prompt,
136
137
system,
137
138
messages,
139
+ numRetries = 0 ,
138
140
} = options ;
139
141
142
+ if ( ! model || ! schema ) {
143
+ throw new Error ( 'Model and schema are required parameters' ) ;
144
+ }
145
+
140
146
try {
141
147
// Primary attempt with main model
142
148
const result = await generateObject ( {
@@ -160,36 +166,56 @@ export class ObjectGeneratorSafe {
160
166
return errorResult ;
161
167
162
168
} catch ( parseError ) {
163
- // Second fallback: Try with fallback model if provided
164
- const fallbackModel = getModel ( 'fallback' ) ;
165
- if ( NoObjectGeneratedError . isInstance ( parseError ) ) {
166
- const failedOutput = ( parseError as any ) . text ;
167
- console . error ( `${ model } failed on object generation ${ failedOutput } -> manual parsing failed again -> trying fallback model` ) ;
169
+
170
+ if ( numRetries > 0 ) {
171
+ console . error ( `${ model } failed on object generation -> manual parsing failed -> retry with ${ numRetries - 1 } retries remaining` ) ;
172
+ return this . generateObject ( {
173
+ model,
174
+ schema,
175
+ prompt,
176
+ system,
177
+ messages,
178
+ numRetries : numRetries - 1
179
+ } ) ;
180
+ } else {
181
+ // Second fallback: Try with fallback model if provided
182
+ const fallbackModel = getModel ( 'fallback' ) ;
183
+ console . error ( `${ model } failed on object generation -> manual parsing failed -> trying fallback with distilled schema` ) ;
168
184
try {
185
+ let failedOutput = '' ;
186
+
187
+ if ( NoObjectGeneratedError . isInstance ( parseError ) ) {
188
+ failedOutput = ( parseError as any ) . text ;
189
+ // find last `"url":` appear in the string, which is the source of the problem
190
+ failedOutput = failedOutput . slice ( 0 , Math . min ( failedOutput . lastIndexOf ( '"url":' ) , 8000 ) ) ;
191
+ }
192
+
169
193
// Create a distilled version of the schema without descriptions
170
194
const distilledSchema = this . createDistilledSchema ( schema ) ;
171
- // find last `"url":` appear in the string, which is the source of the problem
172
- const tailoredOutput = failedOutput . slice ( 0 , Math . min ( failedOutput . lastIndexOf ( '"url":' ) , 8000 ) ) ;
173
195
174
196
const fallbackResult = await generateObject ( {
175
197
model : fallbackModel ,
176
198
schema : distilledSchema ,
177
- prompt : `Following the given JSON schema, extract the field from below: \n\n ${ tailoredOutput } ` ,
199
+ prompt : `Following the given JSON schema, extract the field from below: \n\n ${ failedOutput } ` ,
178
200
maxTokens : getToolConfig ( 'fallback' ) . maxTokens ,
179
201
temperature : getToolConfig ( 'fallback' ) . temperature ,
180
202
} ) ;
181
203
182
- this . tokenTracker . trackUsage ( model , fallbackResult . usage ) ;
183
- console . log ( 'Distilled schema parse success!' )
204
+ this . tokenTracker . trackUsage ( fallbackModel , fallbackResult . usage ) ; // Track against fallback model
205
+ console . log ( 'Distilled schema parse success!' ) ;
184
206
return fallbackResult ;
185
207
} catch ( fallbackError ) {
186
208
// If fallback model also fails, try parsing its error response
187
- return await this . handleGenerateObjectError < T > ( fallbackError ) ;
209
+ try {
210
+ const lastChanceResult = await this . handleGenerateObjectError < T > ( fallbackError ) ;
211
+ this . tokenTracker . trackUsage ( fallbackModel , lastChanceResult . usage ) ;
212
+ return lastChanceResult ;
213
+ } catch ( finalError ) {
214
+ console . error ( `All recovery mechanisms failed` ) ;
215
+ throw error ; // Throw original error for better debugging
216
+ }
188
217
}
189
218
}
190
-
191
- // If no fallback model or all attempts failed, throw the original error
192
- throw error ;
193
219
}
194
220
}
195
221
}
0 commit comments