@@ -156,24 +156,28 @@ export class ChatCompletionStream
156
156
for ( const { delta, finish_reason, index, logprobs = null , ...other } of chunk . choices ) {
157
157
let choice = snapshot . choices [ index ] ;
158
158
if ( ! choice ) {
159
- snapshot . choices [ index ] = { finish_reason, index, message : delta , logprobs, ...other } ;
160
- continue ;
159
+ choice = snapshot . choices [ index ] = { finish_reason, index, message : { } , logprobs, ...other } ;
161
160
}
162
161
163
162
if ( logprobs ) {
164
163
if ( ! choice . logprobs ) {
165
- choice . logprobs = logprobs ;
166
- } else if ( logprobs . content ) {
167
- choice . logprobs . content ??= [ ] ;
168
- choice . logprobs . content . push ( ...logprobs . content ) ;
164
+ choice . logprobs = Object . assign ( { } , logprobs ) ;
165
+ } else {
166
+ const { content, ...rest } = logprobs ;
167
+ Object . assign ( choice . logprobs , rest ) ;
168
+ if ( content ) {
169
+ choice . logprobs . content ??= [ ] ;
170
+ choice . logprobs . content . push ( ...content ) ;
171
+ }
169
172
}
170
173
}
171
174
172
175
if ( finish_reason ) choice . finish_reason = finish_reason ;
173
176
Object . assign ( choice , other ) ;
174
177
175
178
if ( ! delta ) continue ; // Shouldn't happen; just in case.
176
- const { content, function_call, role, tool_calls } = delta ;
179
+ const { content, function_call, role, tool_calls, ...rest } = delta ;
180
+ Object . assign ( choice . message , rest ) ;
177
181
178
182
if ( content ) choice . message . content = ( choice . message . content || '' ) + content ;
179
183
if ( role ) choice . message . role = role ;
@@ -190,8 +194,9 @@ export class ChatCompletionStream
190
194
}
191
195
if ( tool_calls ) {
192
196
if ( ! choice . message . tool_calls ) choice . message . tool_calls = [ ] ;
193
- for ( const { index, id, type, function : fn } of tool_calls ) {
197
+ for ( const { index, id, type, function : fn , ... rest } of tool_calls ) {
194
198
const tool_call = ( choice . message . tool_calls [ index ] ??= { } ) ;
199
+ Object . assign ( tool_call , rest ) ;
195
200
if ( id ) tool_call . id = id ;
196
201
if ( type ) tool_call . type = type ;
197
202
if ( fn ) tool_call . function ??= { arguments : '' } ;
@@ -248,59 +253,72 @@ export class ChatCompletionStream
248
253
}
249
254
250
255
function finalizeChatCompletion ( snapshot : ChatCompletionSnapshot ) : ChatCompletion {
251
- const { id, choices, created, model } = snapshot ;
256
+ const { id, choices, created, model, system_fingerprint , ... rest } = snapshot ;
252
257
return {
258
+ ...rest ,
253
259
id,
254
- choices : choices . map ( ( { message, finish_reason, index, logprobs } ) : ChatCompletion . Choice => {
255
- if ( ! finish_reason ) throw new OpenAIError ( `missing finish_reason for choice ${ index } ` ) ;
256
- const { content = null , function_call, tool_calls } = message ;
257
- const role = message . role as 'assistant' ; // this is what we expect; in theory it could be different which would make our types a slight lie but would be fine.
258
- if ( ! role ) throw new OpenAIError ( `missing role for choice ${ index } ` ) ;
259
- if ( function_call ) {
260
- const { arguments : args , name } = function_call ;
261
- if ( args == null ) throw new OpenAIError ( `missing function_call.arguments for choice ${ index } ` ) ;
262
- if ( ! name ) throw new OpenAIError ( `missing function_call.name for choice ${ index } ` ) ;
260
+ choices : choices . map (
261
+ ( { message, finish_reason, index, logprobs, ...choiceRest } ) : ChatCompletion . Choice => {
262
+ if ( ! finish_reason ) throw new OpenAIError ( `missing finish_reason for choice ${ index } ` ) ;
263
+ const { content = null , function_call, tool_calls, ...messageRest } = message ;
264
+ const role = message . role as 'assistant' ; // this is what we expect; in theory it could be different which would make our types a slight lie but would be fine.
265
+ if ( ! role ) throw new OpenAIError ( `missing role for choice ${ index } ` ) ;
266
+ if ( function_call ) {
267
+ const { arguments : args , name } = function_call ;
268
+ if ( args == null ) throw new OpenAIError ( `missing function_call.arguments for choice ${ index } ` ) ;
269
+ if ( ! name ) throw new OpenAIError ( `missing function_call.name for choice ${ index } ` ) ;
270
+ return {
271
+ ...choiceRest ,
272
+ message : { content, function_call : { arguments : args , name } , role } ,
273
+ finish_reason,
274
+ index,
275
+ logprobs,
276
+ } ;
277
+ }
278
+ if ( tool_calls ) {
279
+ return {
280
+ ...choiceRest ,
281
+ index,
282
+ finish_reason,
283
+ logprobs,
284
+ message : {
285
+ ...messageRest ,
286
+ role,
287
+ content,
288
+ tool_calls : tool_calls . map ( ( tool_call , i ) => {
289
+ const { function : fn , type, id, ...toolRest } = tool_call ;
290
+ const { arguments : args , name, ...fnRest } = fn || { } ;
291
+ if ( id == null )
292
+ throw new OpenAIError ( `missing choices[${ index } ].tool_calls[${ i } ].id\n${ str ( snapshot ) } ` ) ;
293
+ if ( type == null )
294
+ throw new OpenAIError ( `missing choices[${ index } ].tool_calls[${ i } ].type\n${ str ( snapshot ) } ` ) ;
295
+ if ( name == null )
296
+ throw new OpenAIError (
297
+ `missing choices[${ index } ].tool_calls[${ i } ].function.name\n${ str ( snapshot ) } ` ,
298
+ ) ;
299
+ if ( args == null )
300
+ throw new OpenAIError (
301
+ `missing choices[${ index } ].tool_calls[${ i } ].function.arguments\n${ str ( snapshot ) } ` ,
302
+ ) ;
303
+
304
+ return { ...toolRest , id, type, function : { ...fnRest , name, arguments : args } } ;
305
+ } ) ,
306
+ } ,
307
+ } ;
308
+ }
263
309
return {
264
- message : { content, function_call : { arguments : args , name } , role } ,
310
+ ...choiceRest ,
311
+ message : { ...messageRest , content, role } ,
265
312
finish_reason,
266
313
index,
267
314
logprobs,
268
315
} ;
269
- }
270
- if ( tool_calls ) {
271
- return {
272
- index,
273
- finish_reason,
274
- logprobs,
275
- message : {
276
- role,
277
- content,
278
- tool_calls : tool_calls . map ( ( tool_call , i ) => {
279
- const { function : fn , type, id } = tool_call ;
280
- const { arguments : args , name } = fn || { } ;
281
- if ( id == null )
282
- throw new OpenAIError ( `missing choices[${ index } ].tool_calls[${ i } ].id\n${ str ( snapshot ) } ` ) ;
283
- if ( type == null )
284
- throw new OpenAIError ( `missing choices[${ index } ].tool_calls[${ i } ].type\n${ str ( snapshot ) } ` ) ;
285
- if ( name == null )
286
- throw new OpenAIError (
287
- `missing choices[${ index } ].tool_calls[${ i } ].function.name\n${ str ( snapshot ) } ` ,
288
- ) ;
289
- if ( args == null )
290
- throw new OpenAIError (
291
- `missing choices[${ index } ].tool_calls[${ i } ].function.arguments\n${ str ( snapshot ) } ` ,
292
- ) ;
293
-
294
- return { id, type, function : { name, arguments : args } } ;
295
- } ) ,
296
- } ,
297
- } ;
298
- }
299
- return { message : { content : content , role } , finish_reason, index, logprobs } ;
300
- } ) ,
316
+ } ,
317
+ ) ,
301
318
created,
302
319
model,
303
320
object : 'chat.completion' ,
321
+ ...( system_fingerprint ? { system_fingerprint } : { } ) ,
304
322
} ;
305
323
}
306
324
@@ -333,6 +351,18 @@ export interface ChatCompletionSnapshot {
333
351
* The model to generate the completion.
334
352
*/
335
353
model : string ;
354
+
355
+ // Note we do not include an "object" type on the snapshot,
356
+ // because the object is not a valid "chat.completion" until finalized.
357
+ // object: 'chat.completion';
358
+
359
+ /**
360
+ * This fingerprint represents the backend configuration that the model runs with.
361
+ *
362
+ * Can be used in conjunction with the `seed` request parameter to understand when
363
+ * backend changes have been made that might impact determinism.
364
+ */
365
+ system_fingerprint ?: string ;
336
366
}
337
367
338
368
export namespace ChatCompletionSnapshot {
0 commit comments