Skip to content

Commit 634cb0c

Browse files
committed
Only ignore empty text parts in the aggregated response.
1 parent 043cfad commit 634cb0c

File tree

2 files changed

+8
-124
lines changed

2 files changed

+8
-124
lines changed

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

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

1818
import {
1919
aggregateResponses,
20-
deleteEmptyTextParts,
2120
getResponseStream,
2221
processStream
2322
} from './stream-reader';
@@ -34,7 +33,6 @@ import {
3433
GenerateContentResponse,
3534
HarmCategory,
3635
HarmProbability,
37-
Part,
3836
SafetyRating
3937
} from '../types';
4038

@@ -228,6 +226,7 @@ describe('processStream', () => {
228226
);
229227
const result = processStream(fakeResponse as Response);
230228
const aggregatedResponse = await result.response;
229+
console.log(aggregatedResponse.candidates?.[0].content.parts);
231230
expect(aggregatedResponse.text()).to.equal('1');
232231
expect(aggregatedResponse.candidates?.length).to.equal(1);
233232
expect(aggregatedResponse.candidates?.[0].content.parts.length).to.equal(1);
@@ -423,104 +422,3 @@ describe('aggregateResponses', () => {
423422
});
424423
});
425424
});
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/src/requests/stream-reader.ts

+7-21
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ async function getResponsePromise(
6363
return enhancedResponse;
6464
}
6565

66-
deleteEmptyTextParts(value);
6766
allResponses.push(value);
6867
}
6968
}
@@ -78,7 +77,6 @@ async function* generateResponseSequence(
7877
break;
7978
}
8079

81-
deleteEmptyTextParts(value);
8280
const enhancedResponse = createEnhancedContentResponse(value);
8381
yield enhancedResponse;
8482
}
@@ -187,7 +185,13 @@ export function aggregateResponses(
187185
}
188186
const newPart: Partial<Part> = {};
189187
for (const part of candidate.content.parts) {
190-
if (part.text) {
188+
if (part.text !== undefined) {
189+
// The backend can send empty text parts. If these are sent back
190+
// (e.g. in chat history), the backend will respond with an error.
191+
// To prevent this, ignore empty text parts.
192+
if (part.text === '') {
193+
continue;
194+
}
191195
newPart.text = part.text;
192196
}
193197
if (part.functionCall) {
@@ -206,21 +210,3 @@ export function aggregateResponses(
206210
}
207211
return aggregatedResponse;
208212
}
209-
210-
/**
211-
* The backend can send empty text parts, but if they are sent back (e.g. in a chat history) there
212-
* will be an error. To prevent this, filter out the empty text part from responses.
213-
*
214-
* See: https://github.com/firebase/firebase-js-sdk/issues/8714
215-
*/
216-
export function deleteEmptyTextParts(response: GenerateContentResponse): void {
217-
if (response.candidates) {
218-
response.candidates.forEach(candidate => {
219-
if (candidate.content && candidate.content.parts) {
220-
candidate.content.parts = candidate.content.parts.filter(
221-
part => part.text !== ''
222-
);
223-
}
224-
});
225-
}
226-
}

0 commit comments

Comments
 (0)