@f5-sales-demo/pi-ai 20.4.2 → 20.4.4
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.4",
|
|
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.4",
|
|
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";
|
|
@@ -61,7 +61,13 @@ export interface GoogleGeminiCliOptions extends StreamOptions {
|
|
|
61
61
|
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
|
+
const ANTIGRAVITY_VERTEX_ENDPOINT = "https://aiplatform.googleapis.com";
|
|
64
65
|
const ANTIGRAVITY_ENDPOINT_FALLBACKS = [ANTIGRAVITY_DAILY_ENDPOINT, ANTIGRAVITY_SANDBOX_ENDPOINT] as const;
|
|
66
|
+
const ANTIGRAVITY_VERTEX_MODELS: Readonly<Record<string, string>> = Object.freeze({
|
|
67
|
+
"gemini-3.6-flash-high": "gemini-3.6-flash",
|
|
68
|
+
"gemini-3.1-pro-high": "gemini-3.1-pro-preview",
|
|
69
|
+
"gemini-3.1-pro-high-vertex": "gemini-3.1-pro-preview",
|
|
70
|
+
});
|
|
65
71
|
|
|
66
72
|
/**
|
|
67
73
|
* Build a User-Agent string that identifies as Gemini CLI to unlock higher rate limits.
|
|
@@ -421,34 +427,36 @@ interface CloudCodeAssistRequest {
|
|
|
421
427
|
requestId?: string;
|
|
422
428
|
}
|
|
423
429
|
|
|
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;
|
|
430
|
+
interface GeminiGenerateContentResponse {
|
|
431
|
+
candidates?: Array<{
|
|
432
|
+
content?: {
|
|
433
|
+
role: string;
|
|
434
|
+
parts?: Array<{
|
|
435
|
+
text?: string;
|
|
436
|
+
thought?: boolean;
|
|
437
|
+
thoughtSignature?: string;
|
|
438
|
+
functionCall?: {
|
|
439
|
+
name: string;
|
|
440
|
+
args: Record<string, unknown>;
|
|
441
|
+
id?: string;
|
|
442
|
+
};
|
|
443
|
+
}>;
|
|
448
444
|
};
|
|
449
|
-
|
|
450
|
-
|
|
445
|
+
finishReason?: string;
|
|
446
|
+
}>;
|
|
447
|
+
usageMetadata?: {
|
|
448
|
+
promptTokenCount?: number;
|
|
449
|
+
candidatesTokenCount?: number;
|
|
450
|
+
thoughtsTokenCount?: number;
|
|
451
|
+
totalTokenCount?: number;
|
|
452
|
+
cachedContentTokenCount?: number;
|
|
451
453
|
};
|
|
454
|
+
modelVersion?: string;
|
|
455
|
+
responseId?: string;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
interface CloudCodeAssistResponseChunk extends GeminiGenerateContentResponse {
|
|
459
|
+
response?: GeminiGenerateContentResponse;
|
|
452
460
|
traceId?: string;
|
|
453
461
|
}
|
|
454
462
|
|
|
@@ -492,11 +500,22 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
492
500
|
const parsedCredentials = parseGeminiCliCredentials(apiKeyRaw);
|
|
493
501
|
const activeCredentials = await refreshGeminiCliCredentialsIfNeeded(parsedCredentials, isAntigravity);
|
|
494
502
|
const { accessToken, projectId } = activeCredentials;
|
|
503
|
+
const vertexModelId = isAntigravity ? ANTIGRAVITY_VERTEX_MODELS[model.id] : undefined;
|
|
504
|
+
const serviceName = vertexModelId ? "Vertex AI" : "Cloud Code Assist";
|
|
495
505
|
|
|
496
506
|
const baseUrl = model.baseUrl?.trim();
|
|
497
|
-
const endpoints =
|
|
498
|
-
|
|
499
|
-
|
|
507
|
+
const endpoints = vertexModelId
|
|
508
|
+
? [ANTIGRAVITY_VERTEX_ENDPOINT]
|
|
509
|
+
: baseUrl
|
|
510
|
+
? [baseUrl]
|
|
511
|
+
: isAntigravity
|
|
512
|
+
? ANTIGRAVITY_ENDPOINT_FALLBACKS
|
|
513
|
+
: [DEFAULT_ENDPOINT];
|
|
514
|
+
|
|
515
|
+
const cloudCodeRequest = buildRequest(model, context, projectId, options, isAntigravity);
|
|
516
|
+
let requestBody: CloudCodeAssistRequest | CloudCodeAssistRequest["request"] = vertexModelId
|
|
517
|
+
? cloudCodeRequest.request
|
|
518
|
+
: cloudCodeRequest;
|
|
500
519
|
const replacementPayload = await options?.onPayload?.(requestBody, model);
|
|
501
520
|
if (replacementPayload !== undefined) {
|
|
502
521
|
requestBody = replacementPayload as typeof requestBody;
|
|
@@ -534,7 +553,9 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
534
553
|
|
|
535
554
|
try {
|
|
536
555
|
const endpoint = endpoints[Math.min(attempt, endpoints.length - 1)];
|
|
537
|
-
requestUrl =
|
|
556
|
+
requestUrl = vertexModelId
|
|
557
|
+
? `${endpoint}/v1/projects/${encodeURIComponent(projectId)}/locations/global/publishers/google/models/${encodeURIComponent(vertexModelId)}:streamGenerateContent?alt=sse`
|
|
558
|
+
: `${endpoint}/v1internal:streamGenerateContent?alt=sse`;
|
|
538
559
|
response = await fetch(requestUrl, {
|
|
539
560
|
method: "POST",
|
|
540
561
|
headers: requestHeaders,
|
|
@@ -552,7 +573,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
552
573
|
if (response.status === 429) {
|
|
553
574
|
if (/quota|exhausted/i.test(errorText)) {
|
|
554
575
|
throw withHttpStatus(
|
|
555
|
-
new Error(
|
|
576
|
+
new Error(`${serviceName} API error (429): ${extractErrorMessage(errorText)}`),
|
|
556
577
|
429,
|
|
557
578
|
);
|
|
558
579
|
}
|
|
@@ -590,7 +611,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
590
611
|
|
|
591
612
|
// Not retryable or budget exceeded
|
|
592
613
|
throw withHttpStatus(
|
|
593
|
-
new Error(
|
|
614
|
+
new Error(`${serviceName} API error (${response.status}): ${extractErrorMessage(errorText)}`),
|
|
594
615
|
response.status,
|
|
595
616
|
);
|
|
596
617
|
} catch (error) {
|
|
@@ -665,8 +686,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
665
686
|
activeResponse.body!,
|
|
666
687
|
options?.signal,
|
|
667
688
|
)) {
|
|
668
|
-
const responseData = chunk.response;
|
|
669
|
-
if (!responseData) continue;
|
|
689
|
+
const responseData = chunk.response ?? chunk;
|
|
670
690
|
|
|
671
691
|
const candidate = responseData.candidates?.[0];
|
|
672
692
|
if (candidate?.content?.parts) {
|
|
@@ -878,7 +898,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
878
898
|
if (!currentResponse.ok) {
|
|
879
899
|
const retryErrorText = await currentResponse.text();
|
|
880
900
|
throw withHttpStatus(
|
|
881
|
-
new Error(
|
|
901
|
+
new Error(`${serviceName} API error (${currentResponse.status}): ${retryErrorText}`),
|
|
882
902
|
currentResponse.status,
|
|
883
903
|
);
|
|
884
904
|
}
|
|
@@ -896,7 +916,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
896
916
|
}
|
|
897
917
|
|
|
898
918
|
if (!receivedContent) {
|
|
899
|
-
throw new Error(
|
|
919
|
+
throw new Error(`${serviceName} API returned an empty response`);
|
|
900
920
|
}
|
|
901
921
|
|
|
902
922
|
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)}`,
|