@tstdl/base 0.93.160 → 0.93.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { vertexAI } from '@genkit-ai/google-genai';
|
|
2
2
|
import { GenkitError, modelRef } from 'genkit';
|
|
3
3
|
import { genkitPlugin } from 'genkit/plugin';
|
|
4
|
-
import { shuffle } from '../../utils/array/index.js';
|
|
4
|
+
import { distinct, shuffle } from '../../utils/array/index.js';
|
|
5
5
|
import { isInstanceOf, isNullOrUndefined } from '../../utils/type-guards.js';
|
|
6
6
|
import { millisecondsPerMinute, millisecondsPerSecond } from '../../utils/units.js';
|
|
7
7
|
const pluginKey = 'vertexai-multi-location';
|
|
8
8
|
const geminiModelReference = vertexAI.model('gemini-2.5-flash');
|
|
9
9
|
export function vertexAiMultiLocation(options) {
|
|
10
|
+
if (options.locations.length == 0) {
|
|
11
|
+
throw new GenkitError({
|
|
12
|
+
status: 'INVALID_ARGUMENT',
|
|
13
|
+
message: 'At least one location must be provided for vertexAiMultiLocation',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
10
16
|
const locationConfigs = options.locations.map((location) => {
|
|
11
17
|
const circuitBreakerKey = `genkit:vertex-ai:location:${location}`;
|
|
12
18
|
const tokenLimitCircuitBreakerKey = `${circuitBreakerKey}:token-limit`;
|
|
@@ -41,16 +47,19 @@ export function vertexAiMultiLocation(options) {
|
|
|
41
47
|
const shuffledConfigs = shuffle([...locationConfigs]);
|
|
42
48
|
let lastError;
|
|
43
49
|
let isLargeRequest = false;
|
|
50
|
+
const skippedLocations = [];
|
|
44
51
|
for (const { location, circuitBreaker, tokenLimitCircuitBreaker } of shuffledConfigs) {
|
|
45
52
|
const check = await circuitBreaker.check();
|
|
46
53
|
if (!check.allowed) {
|
|
47
54
|
options.logger.warn(`Location ${location} is currently unhealthy. Skipping...`);
|
|
55
|
+
skippedLocations.push({ location, reason: 'unhealthy' });
|
|
48
56
|
continue;
|
|
49
57
|
}
|
|
50
58
|
if (isLargeRequest) {
|
|
51
59
|
const tokenCheck = await tokenLimitCircuitBreaker.check();
|
|
52
60
|
if (!tokenCheck.allowed) {
|
|
53
61
|
options.logger.warn(`Location ${location} is known to have a low token limit. Skipping for this large request...`);
|
|
62
|
+
skippedLocations.push({ location, reason: 'known to have low token limits' });
|
|
54
63
|
continue;
|
|
55
64
|
}
|
|
56
65
|
}
|
|
@@ -88,6 +97,14 @@ export function vertexAiMultiLocation(options) {
|
|
|
88
97
|
}
|
|
89
98
|
}
|
|
90
99
|
}
|
|
100
|
+
if (isNullOrUndefined(lastError)) {
|
|
101
|
+
const uniqueReasons = distinct(skippedLocations.map((s) => s.reason));
|
|
102
|
+
const reasonsString = uniqueReasons.join(' or ');
|
|
103
|
+
throw new GenkitError({
|
|
104
|
+
status: 'UNAVAILABLE',
|
|
105
|
+
message: `All locations were skipped because they are ${reasonsString}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
91
108
|
throw lastError;
|
|
92
109
|
});
|
|
93
110
|
};
|
|
@@ -138,4 +138,42 @@ describe('Genkit vertexai-multi-location Plugin Tests', () => {
|
|
|
138
138
|
});
|
|
139
139
|
expect(response2.text).toBe('success from cb-success');
|
|
140
140
|
});
|
|
141
|
+
it('should throw if no locations are provided', async () => {
|
|
142
|
+
expect(() => {
|
|
143
|
+
vertexAiMultiLocation({
|
|
144
|
+
locations: [],
|
|
145
|
+
circuitBreakerProvider: cbProvider,
|
|
146
|
+
logger,
|
|
147
|
+
});
|
|
148
|
+
}).toThrow('At least one location must be provided');
|
|
149
|
+
});
|
|
150
|
+
it('should throw if all locations are unhealthy', async () => {
|
|
151
|
+
const ai2 = genkit({
|
|
152
|
+
plugins: [
|
|
153
|
+
vertexAiMultiLocation({
|
|
154
|
+
locations: ['unhealthy-1'],
|
|
155
|
+
circuitBreakerProvider: cbProvider,
|
|
156
|
+
logger,
|
|
157
|
+
circuitBreakerConfig: { resetTimeout: 1000000, threshold: 1 },
|
|
158
|
+
}),
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
// Manually trip the circuit breaker
|
|
162
|
+
const cb = cbProvider.provide('genkit:vertex-ai:location:unhealthy-1', { threshold: 1, resetTimeout: 1000000 });
|
|
163
|
+
await cb.recordFailure();
|
|
164
|
+
ai2.defineModel({
|
|
165
|
+
name: 'vertexai/gemini-2.5-flash',
|
|
166
|
+
}, async () => {
|
|
167
|
+
return {
|
|
168
|
+
message: {
|
|
169
|
+
role: 'model',
|
|
170
|
+
content: [{ text: 'success' }],
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
await expect(ai2.generate({
|
|
175
|
+
model: 'vertexai-multi-location/gemini-2.5-flash',
|
|
176
|
+
prompt: 'test',
|
|
177
|
+
})).rejects.toThrow('All locations were skipped because they are unhealthy');
|
|
178
|
+
});
|
|
141
179
|
});
|