Skip to content

Commit 708892a

Browse files
committed
final
1 parent 6769c10 commit 708892a

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

packages/vertexai/src/requests/stream-reader.test.ts

+120
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import {
1919
aggregateResponses,
20+
deleteEmptyTextParts,
2021
getResponseStream,
2122
processStream
2223
} from './stream-reader';
@@ -33,6 +34,7 @@ import {
3334
GenerateContentResponse,
3435
HarmCategory,
3536
HarmProbability,
37+
Part,
3638
SafetyRating
3739
} from '../types';
3840

@@ -220,6 +222,23 @@ describe('processStream', () => {
220222
}
221223
expect(foundCitationMetadata).to.be.true;
222224
});
225+
it('removes empty text parts', async () => {
226+
const fakeResponse = getMockResponseStreaming(
227+
'streaming-success-empty-text-part.txt'
228+
);
229+
const result = processStream(fakeResponse as Response);
230+
const aggregatedResponse = await result.response;
231+
expect(aggregatedResponse.text()).to.equal('1');
232+
expect(aggregatedResponse.candidates?.length).to.equal(1);
233+
expect(aggregatedResponse.candidates?.[0].content.parts.length).to.equal(1);
234+
235+
// The chunk with the empty text part will still go through the stream
236+
let numChunks = 0;
237+
for await (const _ of result.stream) {
238+
numChunks++;
239+
}
240+
expect(numChunks).to.equal(2);
241+
});
223242
});
224243

225244
describe('aggregateResponses', () => {
@@ -404,3 +423,104 @@ describe('aggregateResponses', () => {
404423
});
405424
});
406425
});
426+
427+
describe('deleteEmptyTextParts', () => {
428+
it('removes empty text parts from a single candidate', () => {
429+
const parts: Part[] = [
430+
{
431+
text: ''
432+
},
433+
{
434+
text: 'foo'
435+
}
436+
];
437+
const generateContentResponse: GenerateContentResponse = {
438+
candidates: [
439+
{
440+
index: 0,
441+
content: {
442+
role: 'model',
443+
parts
444+
}
445+
}
446+
]
447+
};
448+
449+
deleteEmptyTextParts(generateContentResponse);
450+
expect(generateContentResponse.candidates?.[0].content.parts).to.deep.equal(
451+
[
452+
{
453+
text: 'foo'
454+
}
455+
]
456+
);
457+
});
458+
it('removes empty text parts from all candidates', () => {
459+
const parts: Part[] = [
460+
{
461+
text: ''
462+
},
463+
{
464+
text: 'foo'
465+
}
466+
];
467+
const generateContentResponse: GenerateContentResponse = {
468+
candidates: [
469+
{
470+
index: 0,
471+
content: {
472+
role: 'model',
473+
parts
474+
}
475+
},
476+
{
477+
index: 1,
478+
content: {
479+
role: 'model',
480+
parts
481+
}
482+
}
483+
]
484+
};
485+
486+
deleteEmptyTextParts(generateContentResponse);
487+
expect(generateContentResponse.candidates?.[0].content.parts).to.deep.equal(
488+
[
489+
{
490+
text: 'foo'
491+
}
492+
]
493+
);
494+
expect(generateContentResponse.candidates?.[1].content.parts).to.deep.equal(
495+
[
496+
{
497+
text: 'foo'
498+
}
499+
]
500+
);
501+
});
502+
it('does not remove candidate even if all parts are removed', () => {
503+
const parts: Part[] = [
504+
{
505+
text: ''
506+
}
507+
];
508+
const generateContentResponse: GenerateContentResponse = {
509+
candidates: [
510+
{
511+
index: 0,
512+
content: {
513+
role: 'model',
514+
parts
515+
}
516+
}
517+
]
518+
};
519+
520+
deleteEmptyTextParts(generateContentResponse);
521+
expect(generateContentResponse.candidates?.length).to.equal(1);
522+
expect(generateContentResponse.candidates?.[0].content.parts).to.deep.equal(
523+
[]
524+
);
525+
});
526+
});

packages/vertexai/test-utils/mock-response.ts

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export function getMockResponseStreaming(
4949
filename: string,
5050
chunkLength: number = 20
5151
): Partial<Response> {
52+
if (!(filename in mocksLookup)) {
53+
throw Error(`Mock response file not found: '${filename}'`);
54+
}
5255
const fullText = mocksLookup[filename];
5356

5457
return {

0 commit comments

Comments
 (0)