Skip to content

Commit 828c6d3

Browse files
sararobcopybara-github
authored andcommitted
feat: Add HttpRetryOptions to client
FUTURE_COPYBARA_INTEGRATE_REVIEW=#1090 from googleapis:release-please--branches--main--components--genai bbf62d2 PiperOrigin-RevId: 831413818
1 parent 38cac5b commit 828c6d3

11 files changed

+793
-0
lines changed

api-report/genai-node.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,7 @@ export interface HttpOptions {
17981798
baseUrl?: string;
17991799
extraBody?: Record<string, unknown>;
18001800
headers?: Record<string, string>;
1801+
retryOptions?: HttpRetryOptions;
18011802
timeout?: number;
18021803
}
18031804

@@ -1810,6 +1811,16 @@ export class HttpResponse {
18101811
responseInternal: Response;
18111812
}
18121813

1814+
// @public
1815+
export interface HttpRetryOptions {
1816+
attempts?: number;
1817+
expBase?: number;
1818+
httpStatusCodes?: number[];
1819+
initialDelay?: number;
1820+
jitter?: number;
1821+
maxDelay?: number;
1822+
}
1823+
18131824
// @public
18141825
interface Image_2 {
18151826
gcsUri?: string;

api-report/genai-web.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,7 @@ export interface HttpOptions {
17981798
baseUrl?: string;
17991799
extraBody?: Record<string, unknown>;
18001800
headers?: Record<string, string>;
1801+
retryOptions?: HttpRetryOptions;
18011802
timeout?: number;
18021803
}
18031804

@@ -1810,6 +1811,16 @@ export class HttpResponse {
18101811
responseInternal: Response;
18111812
}
18121813

1814+
// @public
1815+
export interface HttpRetryOptions {
1816+
attempts?: number;
1817+
expBase?: number;
1818+
httpStatusCodes?: number[];
1819+
initialDelay?: number;
1820+
jitter?: number;
1821+
maxDelay?: number;
1822+
}
1823+
18131824
// @public
18141825
interface Image_2 {
18151826
gcsUri?: string;

api-report/genai.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,7 @@ export interface HttpOptions {
17981798
baseUrl?: string;
17991799
extraBody?: Record<string, unknown>;
18001800
headers?: Record<string, string>;
1801+
retryOptions?: HttpRetryOptions;
18011802
timeout?: number;
18021803
}
18031804

@@ -1810,6 +1811,16 @@ export class HttpResponse {
18101811
responseInternal: Response;
18111812
}
18121813

1814+
// @public
1815+
export interface HttpRetryOptions {
1816+
attempts?: number;
1817+
expBase?: number;
1818+
httpStatusCodes?: number[];
1819+
initialDelay?: number;
1820+
jitter?: number;
1821+
maxDelay?: number;
1822+
}
1823+
18131824
// @public
18141825
interface Image_2 {
18151826
gcsUri?: string;

test/system/node/client_test.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
FunctionDeclaration,
1616
GenerateContentResponse,
1717
HttpOptions,
18+
HttpRetryOptions,
1819
Modality,
1920
Part,
2021
} from '../../../src/types.js';
@@ -630,7 +631,162 @@ describe('Client Tests', () => {
630631
}),
631632
);
632633
});
634+
it('ML Dev should generate content with retry options provided to the client', async () => {
635+
const retryOptions: HttpRetryOptions = {
636+
attempts: 3,
637+
initialDelay: 1000,
638+
maxDelay: 5000,
639+
httpStatusCodes: [500, 503],
640+
};
641+
const httpOptionsWithRetryOptions: HttpOptions = httpOptions;
642+
httpOptionsWithRetryOptions.retryOptions = retryOptions;
643+
644+
const client = new GoogleGenAI({
645+
vertexai: false,
646+
apiKey: GOOGLE_API_KEY,
647+
httpOptions: httpOptionsWithRetryOptions,
648+
});
649+
const response = await client.models.generateContent({
650+
model: MODEL,
651+
contents: 'why is the sky blue?',
652+
config: {
653+
maxOutputTokens: 200,
654+
candidateCount: 1,
655+
thinkingConfig: {thinkingBudget: 50},
656+
},
657+
});
658+
expect(response.candidates!.length).toBe(
659+
1,
660+
'Expected 1 candidate got ' + response.candidates!.length,
661+
);
662+
expect(response.usageMetadata!.candidatesTokenCount).toBeLessThanOrEqual(
663+
250,
664+
'Expected candidatesTokenCount to be less than or equal to 250, got ' +
665+
response.usageMetadata!.candidatesTokenCount,
666+
);
667+
console.info(
668+
'ML Dev should generate content with retry options provided to the client\n',
669+
response.text,
670+
);
671+
}, 60000);
633672
});
673+
it('Vertex AI should generate content with retry options provided to the client', async () => {
674+
const retryOptions: HttpRetryOptions = {
675+
attempts: 3,
676+
initialDelay: 1000,
677+
maxDelay: 5000,
678+
httpStatusCodes: [500, 503],
679+
};
680+
const httpOptionsWithRetryOptions: HttpOptions = httpOptions;
681+
httpOptionsWithRetryOptions.retryOptions = retryOptions;
682+
683+
const client = new GoogleGenAI({
684+
vertexai: true,
685+
project: GOOGLE_CLOUD_PROJECT,
686+
location: GOOGLE_CLOUD_LOCATION,
687+
httpOptions: httpOptionsWithRetryOptions,
688+
});
689+
const response = await client.models.generateContent({
690+
model: MODEL,
691+
contents: 'why is the sky blue?',
692+
config: {
693+
maxOutputTokens: 200,
694+
candidateCount: 1,
695+
thinkingConfig: {thinkingBudget: 50},
696+
},
697+
});
698+
expect(response.candidates!.length).toBe(
699+
1,
700+
'Expected 1 candidate got ' + response.candidates!.length,
701+
);
702+
expect(response.usageMetadata!.candidatesTokenCount).toBeLessThanOrEqual(
703+
250,
704+
'Expected candidatesTokenCount to be less than or equal to 250, got ' +
705+
response.usageMetadata!.candidatesTokenCount,
706+
);
707+
console.info(
708+
'Vertex AI should generate content with retry options provided to the client\n',
709+
response.text,
710+
);
711+
}, 30000);
712+
it('ML Dev should generate content with per-request retry options', async () => {
713+
const retryOptions: HttpRetryOptions = {
714+
attempts: 3,
715+
initialDelay: 1000,
716+
maxDelay: 5000,
717+
httpStatusCodes: [500, 503],
718+
};
719+
const httpOptionsWithRetryOptions: HttpOptions = httpOptions;
720+
httpOptionsWithRetryOptions.retryOptions = retryOptions;
721+
722+
const client = new GoogleGenAI({
723+
vertexai: false,
724+
apiKey: GOOGLE_API_KEY,
725+
httpOptions: httpOptionsWithRetryOptions,
726+
});
727+
const response = await client.models.generateContent({
728+
model: MODEL,
729+
contents: 'why is the sky blue?',
730+
config: {
731+
maxOutputTokens: 200,
732+
candidateCount: 1,
733+
thinkingConfig: {thinkingBudget: 50},
734+
httpOptions: httpOptionsWithRetryOptions,
735+
},
736+
});
737+
expect(response.candidates!.length).toBe(
738+
1,
739+
'Expected 1 candidate got ' + response.candidates!.length,
740+
);
741+
expect(response.usageMetadata!.candidatesTokenCount).toBeLessThanOrEqual(
742+
250,
743+
'Expected candidatesTokenCount to be less than or equal to 250, got ' +
744+
response.usageMetadata!.candidatesTokenCount,
745+
);
746+
console.info(
747+
'ML Dev should generate content with per-request retry options\n',
748+
response.text,
749+
);
750+
}, 30000);
751+
it('Vertex AI should generate content with per-request retry options', async () => {
752+
const retryOptions: HttpRetryOptions = {
753+
attempts: 3,
754+
initialDelay: 1000,
755+
maxDelay: 5000,
756+
httpStatusCodes: [500, 503],
757+
};
758+
const httpOptionsWithRetryOptions: HttpOptions = httpOptions;
759+
httpOptionsWithRetryOptions.retryOptions = retryOptions;
760+
761+
const client = new GoogleGenAI({
762+
vertexai: false,
763+
apiKey: GOOGLE_API_KEY,
764+
httpOptions,
765+
});
766+
const response = await client.models.generateContent({
767+
model: MODEL,
768+
contents: 'why is the sky blue?',
769+
config: {
770+
maxOutputTokens: 200,
771+
candidateCount: 1,
772+
thinkingConfig: {thinkingBudget: 50},
773+
httpOptions: httpOptionsWithRetryOptions,
774+
},
775+
});
776+
expect(response.candidates!.length).toBe(
777+
1,
778+
'Expected 1 candidate got ' + response.candidates!.length,
779+
);
780+
expect(response.usageMetadata!.candidatesTokenCount).toBeLessThanOrEqual(
781+
250,
782+
'Expected candidatesTokenCount to be less than or equal to 250, got ' +
783+
response.usageMetadata!.candidatesTokenCount,
784+
);
785+
console.info(
786+
'Vertex AI should generate content with per-request retry options\n',
787+
response.text,
788+
);
789+
}, 30000);
634790
it('Vertex AI can use JSON schema in parametersJsonSchema to build FunctionDeclaration', async () => {
635791
const stringArgument = z.object({
636792
firstString: z.string(),
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{
2+
"recordID": "Client_Tests_ML_Dev_should_generate_content_with_per-request_retry_options",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "POST",
7+
"url": "/v1beta/models/gemini-2.5-flash:generateContent",
8+
"request": "POST /v1beta/models/gemini-2.5-flash:generateContent HTTP/1.1",
9+
"headers": {
10+
"Accept": "*/*",
11+
"Accept-Encoding": "gzip, deflate",
12+
"Accept-Language": "*",
13+
"Connection": "keep-alive",
14+
"Content-Length": "173",
15+
"Content-Type": "application/json",
16+
"Sec-Fetch-Mode": "cors",
17+
"Test-Name": "Client Tests ML Dev should generate content with per-request retry options"
18+
},
19+
"bodySegments": [
20+
{
21+
"contents": [
22+
{
23+
"parts": [
24+
{
25+
"text": "why is the sky blue?"
26+
}
27+
],
28+
"role": "user"
29+
}
30+
],
31+
"generationConfig": {
32+
"candidateCount": 1,
33+
"maxOutputTokens": 200,
34+
"thinkingConfig": {
35+
"thinkingBudget": 50
36+
}
37+
}
38+
}
39+
],
40+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
41+
"serverAddress": "generativelanguage.googleapis.com",
42+
"port": 443,
43+
"protocol": "https"
44+
},
45+
"shaSum": "550d48a2850cd10f154574b79ea5854d49697f1cad20c1c9728b5173ba6bd925",
46+
"response": {
47+
"statusCode": 200,
48+
"headers": {
49+
"Content-Encoding": "gzip",
50+
"Content-Length": "680",
51+
"Content-Type": "application/json; charset=UTF-8",
52+
"Date": "Thu, 13 Nov 2025 19:42:26 GMT",
53+
"Server": "scaffolding on HTTPServer2",
54+
"Server-Timing": "gfet4t7; dur=1628",
55+
"Vary": "Origin, X-Origin, Referer",
56+
"X-Content-Type-Options": "nosniff",
57+
"X-Frame-Options": "SAMEORIGIN",
58+
"X-Xss-Protection": "0"
59+
},
60+
"bodySegments": [
61+
{
62+
"candidates": [
63+
{
64+
"content": {
65+
"parts": [
66+
{
67+
"text": "The sky is blue primarily due to a phenomenon called **Rayleigh Scattering**.\n\nHere's a breakdown of why it happens:\n\n1. **Sunlight is White Light:** Sunlight, when it reaches Earth, appears white to our eyes. However, it's actually made up of all the colors of the rainbow (red, orange, yellow, green, blue, indigo, violet). Each color has a different wavelength (blue/violet have shorter, smaller wavelengths, while red/orange have longer, larger wavelengths).\n\n2. **Earth's Atmosphere:** Our planet is surrounded by a thick layer of gases – the atmosphere. This atmosphere contains tiny molecules, mainly nitrogen (N2) and oxygen (O2"
68+
}
69+
],
70+
"role": "model"
71+
},
72+
"finishReason": "MAX_TOKENS",
73+
"index": 0
74+
}
75+
],
76+
"modelVersion": "gemini-2.5-flash",
77+
"responseId": "ojQWad2SFf-gz7IPnpHRgAk",
78+
"usageMetadata": {
79+
"candidatesTokenCount": 147,
80+
"promptTokenCount": 7,
81+
"promptTokensDetails": [
82+
{
83+
"modality": "TEXT",
84+
"tokenCount": 7
85+
}
86+
],
87+
"thoughtsTokenCount": 51,
88+
"totalTokenCount": 205
89+
}
90+
}
91+
]
92+
}
93+
}
94+
]
95+
}

0 commit comments

Comments
 (0)