@f5-sales-demo/pi-ai 20.4.2 → 20.4.3
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.
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/pi-ai",
|
|
4
|
-
"version": "20.4.
|
|
4
|
+
"version": "20.4.3",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@anthropic-ai/sdk": "^0.115",
|
|
47
47
|
"@aws-sdk/client-bedrock-runtime": "^3",
|
|
48
48
|
"@bufbuild/protobuf": "^2.11",
|
|
49
|
-
"@f5-sales-demo/pi-utils": "20.4.
|
|
49
|
+
"@f5-sales-demo/pi-utils": "20.4.3",
|
|
50
50
|
"@google/genai": "^2.15",
|
|
51
51
|
"@sinclair/typebox": "^0.34",
|
|
52
52
|
"@smithy/node-http-handler": "^4.4",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Google Gemini CLI / Antigravity provider.
|
|
3
3
|
* Shared implementation for both google-gemini-cli and google-antigravity providers.
|
|
4
|
-
* Uses
|
|
4
|
+
* Uses Cloud Code Assist plus explicit enterprise Vertex routes for supported Antigravity models.
|
|
5
5
|
*/
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { abortableSleep, readSseJson } from "@f5-sales-demo/pi-utils";
|
|
@@ -62,6 +62,9 @@ const DEFAULT_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
|
62
62
|
const ANTIGRAVITY_DAILY_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
|
|
63
63
|
const ANTIGRAVITY_SANDBOX_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
|
|
64
64
|
const ANTIGRAVITY_ENDPOINT_FALLBACKS = [ANTIGRAVITY_DAILY_ENDPOINT, ANTIGRAVITY_SANDBOX_ENDPOINT] as const;
|
|
65
|
+
const ANTIGRAVITY_VERTEX_MODELS: Readonly<Record<string, string>> = Object.freeze({
|
|
66
|
+
"gemini-3.1-pro-high-vertex": "gemini-3.1-pro-preview",
|
|
67
|
+
});
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
70
|
* Build a User-Agent string that identifies as Gemini CLI to unlock higher rate limits.
|
|
@@ -421,34 +424,36 @@ interface CloudCodeAssistRequest {
|
|
|
421
424
|
requestId?: string;
|
|
422
425
|
}
|
|
423
426
|
|
|
424
|
-
interface
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}>;
|
|
439
|
-
};
|
|
440
|
-
finishReason?: string;
|
|
441
|
-
}>;
|
|
442
|
-
usageMetadata?: {
|
|
443
|
-
promptTokenCount?: number;
|
|
444
|
-
candidatesTokenCount?: number;
|
|
445
|
-
thoughtsTokenCount?: number;
|
|
446
|
-
totalTokenCount?: number;
|
|
447
|
-
cachedContentTokenCount?: number;
|
|
427
|
+
interface GeminiGenerateContentResponse {
|
|
428
|
+
candidates?: Array<{
|
|
429
|
+
content?: {
|
|
430
|
+
role: string;
|
|
431
|
+
parts?: Array<{
|
|
432
|
+
text?: string;
|
|
433
|
+
thought?: boolean;
|
|
434
|
+
thoughtSignature?: string;
|
|
435
|
+
functionCall?: {
|
|
436
|
+
name: string;
|
|
437
|
+
args: Record<string, unknown>;
|
|
438
|
+
id?: string;
|
|
439
|
+
};
|
|
440
|
+
}>;
|
|
448
441
|
};
|
|
449
|
-
|
|
450
|
-
|
|
442
|
+
finishReason?: string;
|
|
443
|
+
}>;
|
|
444
|
+
usageMetadata?: {
|
|
445
|
+
promptTokenCount?: number;
|
|
446
|
+
candidatesTokenCount?: number;
|
|
447
|
+
thoughtsTokenCount?: number;
|
|
448
|
+
totalTokenCount?: number;
|
|
449
|
+
cachedContentTokenCount?: number;
|
|
451
450
|
};
|
|
451
|
+
modelVersion?: string;
|
|
452
|
+
responseId?: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
interface CloudCodeAssistResponseChunk extends GeminiGenerateContentResponse {
|
|
456
|
+
response?: GeminiGenerateContentResponse;
|
|
452
457
|
traceId?: string;
|
|
453
458
|
}
|
|
454
459
|
|
|
@@ -492,11 +497,16 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
492
497
|
const parsedCredentials = parseGeminiCliCredentials(apiKeyRaw);
|
|
493
498
|
const activeCredentials = await refreshGeminiCliCredentialsIfNeeded(parsedCredentials, isAntigravity);
|
|
494
499
|
const { accessToken, projectId } = activeCredentials;
|
|
500
|
+
const vertexModelId = isAntigravity ? ANTIGRAVITY_VERTEX_MODELS[model.id] : undefined;
|
|
501
|
+
const serviceName = vertexModelId ? "Vertex AI" : "Cloud Code Assist";
|
|
495
502
|
|
|
496
503
|
const baseUrl = model.baseUrl?.trim();
|
|
497
504
|
const endpoints = baseUrl ? [baseUrl] : isAntigravity ? ANTIGRAVITY_ENDPOINT_FALLBACKS : [DEFAULT_ENDPOINT];
|
|
498
505
|
|
|
499
|
-
|
|
506
|
+
const cloudCodeRequest = buildRequest(model, context, projectId, options, isAntigravity);
|
|
507
|
+
let requestBody: CloudCodeAssistRequest | CloudCodeAssistRequest["request"] = vertexModelId
|
|
508
|
+
? cloudCodeRequest.request
|
|
509
|
+
: cloudCodeRequest;
|
|
500
510
|
const replacementPayload = await options?.onPayload?.(requestBody, model);
|
|
501
511
|
if (replacementPayload !== undefined) {
|
|
502
512
|
requestBody = replacementPayload as typeof requestBody;
|
|
@@ -534,7 +544,9 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
534
544
|
|
|
535
545
|
try {
|
|
536
546
|
const endpoint = endpoints[Math.min(attempt, endpoints.length - 1)];
|
|
537
|
-
requestUrl =
|
|
547
|
+
requestUrl = vertexModelId
|
|
548
|
+
? `${endpoint}/v1/projects/${encodeURIComponent(projectId)}/locations/global/publishers/google/models/${encodeURIComponent(vertexModelId)}:streamGenerateContent?alt=sse`
|
|
549
|
+
: `${endpoint}/v1internal:streamGenerateContent?alt=sse`;
|
|
538
550
|
response = await fetch(requestUrl, {
|
|
539
551
|
method: "POST",
|
|
540
552
|
headers: requestHeaders,
|
|
@@ -552,7 +564,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
552
564
|
if (response.status === 429) {
|
|
553
565
|
if (/quota|exhausted/i.test(errorText)) {
|
|
554
566
|
throw withHttpStatus(
|
|
555
|
-
new Error(
|
|
567
|
+
new Error(`${serviceName} API error (429): ${extractErrorMessage(errorText)}`),
|
|
556
568
|
429,
|
|
557
569
|
);
|
|
558
570
|
}
|
|
@@ -590,7 +602,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
590
602
|
|
|
591
603
|
// Not retryable or budget exceeded
|
|
592
604
|
throw withHttpStatus(
|
|
593
|
-
new Error(
|
|
605
|
+
new Error(`${serviceName} API error (${response.status}): ${extractErrorMessage(errorText)}`),
|
|
594
606
|
response.status,
|
|
595
607
|
);
|
|
596
608
|
} catch (error) {
|
|
@@ -665,8 +677,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
665
677
|
activeResponse.body!,
|
|
666
678
|
options?.signal,
|
|
667
679
|
)) {
|
|
668
|
-
const responseData = chunk.response;
|
|
669
|
-
if (!responseData) continue;
|
|
680
|
+
const responseData = chunk.response ?? chunk;
|
|
670
681
|
|
|
671
682
|
const candidate = responseData.candidates?.[0];
|
|
672
683
|
if (candidate?.content?.parts) {
|
|
@@ -878,7 +889,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
878
889
|
if (!currentResponse.ok) {
|
|
879
890
|
const retryErrorText = await currentResponse.text();
|
|
880
891
|
throw withHttpStatus(
|
|
881
|
-
new Error(
|
|
892
|
+
new Error(`${serviceName} API error (${currentResponse.status}): ${retryErrorText}`),
|
|
882
893
|
currentResponse.status,
|
|
883
894
|
);
|
|
884
895
|
}
|
|
@@ -896,7 +907,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
896
907
|
}
|
|
897
908
|
|
|
898
909
|
if (!receivedContent) {
|
|
899
|
-
throw new Error(
|
|
910
|
+
throw new Error(`${serviceName} API returned an empty response`);
|
|
900
911
|
}
|
|
901
912
|
|
|
902
913
|
if (options?.signal?.aborted) {
|
|
@@ -8,6 +8,13 @@ const DEFAULT_ANTIGRAVITY_DISCOVERY_ENDPOINTS = [
|
|
|
8
8
|
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
9
9
|
] as const;
|
|
10
10
|
const FETCH_AVAILABLE_MODELS_PATH = "/v1internal:fetchAvailableModels";
|
|
11
|
+
const ANTIGRAVITY_VERTEX_ENDPOINT = "https://aiplatform.googleapis.com";
|
|
12
|
+
const ANTIGRAVITY_VERTEX_VARIANTS: Readonly<Record<string, { id: string; name: string }>> = Object.freeze({
|
|
13
|
+
"gemini-3.1-pro-high": {
|
|
14
|
+
id: "gemini-3.1-pro-high-vertex",
|
|
15
|
+
name: "Gemini 3.1 Pro High (Vertex, Antigravity)",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
11
18
|
|
|
12
19
|
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
13
20
|
const DEFAULT_MAX_TOKENS = 64_000;
|
|
@@ -222,12 +229,13 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
222
229
|
}
|
|
223
230
|
|
|
224
231
|
const supportsImages = model.supportsImages === true;
|
|
232
|
+
const vertexVariant = ANTIGRAVITY_VERTEX_VARIANTS[modelId];
|
|
225
233
|
models.push({
|
|
226
|
-
id: modelId,
|
|
227
|
-
name: model.displayName ? `${model.displayName} (Antigravity)` : modelId,
|
|
234
|
+
id: vertexVariant?.id ?? modelId,
|
|
235
|
+
name: vertexVariant?.name ?? (model.displayName ? `${model.displayName} (Antigravity)` : modelId),
|
|
228
236
|
api: "google-gemini-cli",
|
|
229
237
|
provider: "google-antigravity",
|
|
230
|
-
baseUrl: endpoint,
|
|
238
|
+
baseUrl: vertexVariant ? ANTIGRAVITY_VERTEX_ENDPOINT : endpoint,
|
|
231
239
|
reasoning: model.supportsThinking === true,
|
|
232
240
|
input: supportsImages ? ["text", "image"] : ["text"],
|
|
233
241
|
cost: {
|
|
@@ -30,6 +30,7 @@ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
|
30
30
|
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
31
31
|
const CLOUD_CODE_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
32
32
|
const TIER_LEGACY = "legacy-tier";
|
|
33
|
+
const TIER_STANDARD = "standard-tier";
|
|
33
34
|
const PROJECT_ONBOARD_MAX_ATTEMPTS = 5;
|
|
34
35
|
const PROJECT_ONBOARD_INTERVAL_MS = 2000;
|
|
35
36
|
const PROJECT_ID_PATTERN = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;
|
|
@@ -248,6 +249,25 @@ async function onboardProjectWithRetries(
|
|
|
248
249
|
);
|
|
249
250
|
}
|
|
250
251
|
|
|
252
|
+
async function loadCodeAssist(
|
|
253
|
+
endpoint: string,
|
|
254
|
+
headers: Record<string, string>,
|
|
255
|
+
projectRequest: AntigravityProjectRequest,
|
|
256
|
+
): Promise<LoadCodeAssistPayload> {
|
|
257
|
+
const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers,
|
|
260
|
+
body: JSON.stringify(projectRequest),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (!response.ok) {
|
|
264
|
+
const errorText = await response.text();
|
|
265
|
+
throw new Error(`loadCodeAssist failed: ${response.status} ${response.statusText}: ${errorText}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return (await response.json()) as LoadCodeAssistPayload;
|
|
269
|
+
}
|
|
270
|
+
|
|
251
271
|
async function discoverProject(
|
|
252
272
|
accessToken: string,
|
|
253
273
|
configuredProjectId: string | undefined,
|
|
@@ -263,31 +283,42 @@ async function discoverProject(
|
|
|
263
283
|
onProgress?.("Checking for existing project...");
|
|
264
284
|
const endpoint = CLOUD_CODE_ENDPOINT;
|
|
265
285
|
try {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
headers,
|
|
269
|
-
body: JSON.stringify(projectRequest),
|
|
270
|
-
});
|
|
286
|
+
const loadPayload = await loadCodeAssist(endpoint, headers, projectRequest);
|
|
287
|
+
const existingProject = readProjectId(loadPayload.cloudaicompanionProject);
|
|
271
288
|
|
|
272
|
-
if (!
|
|
273
|
-
|
|
274
|
-
|
|
289
|
+
if (!configuredProjectId) {
|
|
290
|
+
if (existingProject) {
|
|
291
|
+
return existingProject;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const tierId = getDefaultTierId(loadPayload.allowedTiers);
|
|
295
|
+
onProgress?.("Provisioning project...");
|
|
296
|
+
const onboardBody = {
|
|
297
|
+
tierId,
|
|
298
|
+
...projectRequest,
|
|
299
|
+
};
|
|
300
|
+
return await onboardProjectWithRetries(endpoint, headers, onboardBody, onProgress);
|
|
275
301
|
}
|
|
276
302
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
303
|
+
if (existingProject === configuredProjectId && loadPayload.currentTier?.id === TIER_STANDARD) {
|
|
304
|
+
return configuredProjectId;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const standardTierAvailable = loadPayload.allowedTiers?.some(tier => tier.id === TIER_STANDARD) ?? false;
|
|
308
|
+
if (!standardTierAvailable) {
|
|
309
|
+
throw new Error(`standard-tier is not available for configured project ${configuredProjectId}`);
|
|
281
310
|
}
|
|
282
311
|
|
|
283
|
-
const tierId = getDefaultTierId(loadPayload.allowedTiers);
|
|
284
312
|
onProgress?.("Provisioning project...");
|
|
285
313
|
const onboardBody = {
|
|
286
|
-
tierId,
|
|
314
|
+
tierId: TIER_STANDARD,
|
|
287
315
|
...projectRequest,
|
|
288
316
|
};
|
|
289
317
|
const provisionedProject = await onboardProjectWithRetries(endpoint, headers, onboardBody, onProgress);
|
|
290
|
-
|
|
318
|
+
if (provisionedProject !== configuredProjectId) {
|
|
319
|
+
throw new Error(`onboardUser returned project ${provisionedProject} instead of ${configuredProjectId}`);
|
|
320
|
+
}
|
|
321
|
+
return configuredProjectId;
|
|
291
322
|
} catch (error) {
|
|
292
323
|
throw new Error(
|
|
293
324
|
`Could not discover or provision an Antigravity project. ${error instanceof Error ? error.message : String(error)}`,
|