Skip to content

Commit 88da1a9

Browse files
russellwheatleymikehardy
authored andcommitted
response-helpers.ts
1 parent 0077c64 commit 88da1a9

File tree

2 files changed

+82
-13
lines changed

2 files changed

+82
-13
lines changed

packages/ai/lib/requests/response-helpers.ts

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import {
2121
FunctionCall,
2222
GenerateContentCandidate,
2323
GenerateContentResponse,
24-
VertexAIErrorCode,
24+
AIErrorCode,
25+
InlineDataPart,
2526
} from '../types';
26-
import { VertexAIError } from '../errors';
27+
import { AIError } from '../errors';
2728
import { logger } from '../logger';
2829

2930
/**
@@ -62,8 +63,8 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
6263
);
6364
}
6465
if (hadBadFinishReason(response.candidates[0]!)) {
65-
throw new VertexAIError(
66-
VertexAIErrorCode.RESPONSE_ERROR,
66+
throw new AIError(
67+
AIErrorCode.RESPONSE_ERROR,
6768
`Response error: ${formatBlockErrorMessage(
6869
response,
6970
)}. Response body stored in error.response`,
@@ -74,8 +75,8 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
7475
}
7576
return getText(response);
7677
} else if (response.promptFeedback) {
77-
throw new VertexAIError(
78-
VertexAIErrorCode.RESPONSE_ERROR,
78+
throw new AIError(
79+
AIErrorCode.RESPONSE_ERROR,
7980
`Text not available. ${formatBlockErrorMessage(response)}`,
8081
{
8182
response,
@@ -84,6 +85,40 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
8485
}
8586
return '';
8687
};
88+
(response as EnhancedGenerateContentResponse).inlineDataParts = ():
89+
| InlineDataPart[]
90+
| undefined => {
91+
if (response.candidates && response.candidates.length > 0) {
92+
if (response.candidates.length > 1) {
93+
logger.warn(
94+
`This response had ${response.candidates.length} ` +
95+
`candidates. Returning data from the first candidate only. ` +
96+
`Access response.candidates directly to use the other candidates.`,
97+
);
98+
}
99+
if (hadBadFinishReason(response.candidates[0]!)) {
100+
throw new AIError(
101+
AIErrorCode.RESPONSE_ERROR,
102+
`Response error: ${formatBlockErrorMessage(
103+
response,
104+
)}. Response body stored in error.response`,
105+
{
106+
response,
107+
},
108+
);
109+
}
110+
return getInlineDataParts(response);
111+
} else if (response.promptFeedback) {
112+
throw new AIError(
113+
AIErrorCode.RESPONSE_ERROR,
114+
`Data not available. ${formatBlockErrorMessage(response)}`,
115+
{
116+
response,
117+
},
118+
);
119+
}
120+
return undefined;
121+
};
87122
(response as EnhancedGenerateContentResponse).functionCalls = () => {
88123
if (response.candidates && response.candidates.length > 0) {
89124
if (response.candidates.length > 1) {
@@ -94,8 +129,8 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
94129
);
95130
}
96131
if (hadBadFinishReason(response.candidates[0]!)) {
97-
throw new VertexAIError(
98-
VertexAIErrorCode.RESPONSE_ERROR,
132+
throw new AIError(
133+
AIErrorCode.RESPONSE_ERROR,
99134
`Response error: ${formatBlockErrorMessage(
100135
response,
101136
)}. Response body stored in error.response`,
@@ -106,8 +141,8 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
106141
}
107142
return getFunctionCalls(response);
108143
} else if (response.promptFeedback) {
109-
throw new VertexAIError(
110-
VertexAIErrorCode.RESPONSE_ERROR,
144+
throw new AIError(
145+
AIErrorCode.RESPONSE_ERROR,
111146
`Function call not available. ${formatBlockErrorMessage(response)}`,
112147
{
113148
response,
@@ -125,7 +160,7 @@ export function addHelpers(response: GenerateContentResponse): EnhancedGenerateC
125160
export function getText(response: GenerateContentResponse): string {
126161
const textStrings = [];
127162
if (response.candidates?.[0]?.content?.parts) {
128-
for (const part of response.candidates?.[0].content?.parts) {
163+
for (const part of response.candidates?.[0]?.content?.parts) {
129164
if (part.text) {
130165
textStrings.push(part.text);
131166
}
@@ -139,7 +174,7 @@ export function getText(response: GenerateContentResponse): string {
139174
}
140175

141176
/**
142-
* Returns <code>{@link FunctionCall}</code>s associated with first candidate.
177+
* Returns {@link FunctionCall}s associated with first candidate.
143178
*/
144179
export function getFunctionCalls(response: GenerateContentResponse): FunctionCall[] | undefined {
145180
const functionCalls: FunctionCall[] = [];
@@ -157,6 +192,31 @@ export function getFunctionCalls(response: GenerateContentResponse): FunctionCal
157192
}
158193
}
159194

195+
/**
196+
* Returns {@link InlineDataPart}s in the first candidate if present.
197+
*
198+
* @internal
199+
*/
200+
export function getInlineDataParts(
201+
response: GenerateContentResponse,
202+
): InlineDataPart[] | undefined {
203+
const data: InlineDataPart[] = [];
204+
205+
if (response.candidates?.[0]?.content?.parts) {
206+
for (const part of response.candidates?.[0]?.content?.parts) {
207+
if (part.inlineData) {
208+
data.push(part);
209+
}
210+
}
211+
}
212+
213+
if (data.length > 0) {
214+
return data;
215+
} else {
216+
return undefined;
217+
}
218+
}
219+
160220
const badFinishReasons = [FinishReason.RECITATION, FinishReason.SAFETY];
161221

162222
function hadBadFinishReason(candidate: GenerateContentCandidate): boolean {

packages/ai/lib/types/responses.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { Content, FunctionCall } from './content';
18+
import { Content, FunctionCall, InlineDataPart } from './content';
1919
import { BlockReason, FinishReason, HarmCategory, HarmProbability, HarmSeverity } from './enums';
2020

2121
/**
@@ -51,6 +51,15 @@ export interface EnhancedGenerateContentResponse extends GenerateContentResponse
5151
* Throws if the prompt or candidate was blocked.
5252
*/
5353
text: () => string;
54+
/**
55+
* Aggregates and returns all {@link InlineDataPart}s from the {@link GenerateContentResponse}'s
56+
* first candidate.
57+
*
58+
* @returns An array of {@link InlineDataPart}s containing data from the response, if available.
59+
*
60+
* @throws If the prompt or candidate was blocked.
61+
*/
62+
inlineDataParts: () => InlineDataPart[] | undefined;
5463
functionCalls: () => FunctionCall[] | undefined;
5564
}
5665

0 commit comments

Comments
 (0)