@f5-sales-demo/pi-ai 21.15.1 → 21.17.0
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/CHANGELOG.md +3 -0
- package/package.json +2 -2
- package/src/models.json +3 -7
- package/src/provider-models/descriptors.ts +1 -1
- package/src/providers/google-shared.ts +3 -3
- package/src/providers/google-vertex.ts +13 -10
- package/src/providers/openai-codex-responses.ts +26 -9
- package/src/providers/openai-responses-shared.ts +16 -4
- package/src/types.ts +13 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
### Changed
|
|
8
8
|
|
|
9
|
+
- Replaced the bundled Vertex Gemini 3.7 Flash model and provider default with GA Gemini 3.8 Flash,
|
|
10
|
+
retaining HIGH thinking by default and the strict Vertex tool-call contract
|
|
11
|
+
([#3751](https://github.com/f5-sales-demo/xcsh/issues/3751)).
|
|
9
12
|
- Replaced Vertex Gemini 3.6 Flash with GA Gemini 3.7 Flash and made full Vertex Gemini Flash and Pro models default to HIGH thinking while preserving supported explicit effort overrides ([#3460](https://github.com/f5-sales-demo/xcsh/issues/3460)).
|
|
10
13
|
|
|
11
14
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/pi-ai",
|
|
4
|
-
"version": "21.
|
|
4
|
+
"version": "21.17.0",
|
|
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": "21.
|
|
49
|
+
"@f5-sales-demo/pi-utils": "21.17.0",
|
|
50
50
|
"@google/genai": "^2.15",
|
|
51
51
|
"@sinclair/typebox": "^0.34",
|
|
52
52
|
"@smithy/node-http-handler": "^4.4",
|
package/src/models.json
CHANGED
|
@@ -10316,9 +10316,9 @@
|
|
|
10316
10316
|
}
|
|
10317
10317
|
},
|
|
10318
10318
|
"google-vertex": {
|
|
10319
|
-
"gemini-3.
|
|
10320
|
-
"id": "gemini-3.
|
|
10321
|
-
"name": "Gemini 3.
|
|
10319
|
+
"gemini-3.8-flash": {
|
|
10320
|
+
"id": "gemini-3.8-flash",
|
|
10321
|
+
"name": "Gemini 3.8 Flash",
|
|
10322
10322
|
"api": "google-vertex",
|
|
10323
10323
|
"provider": "google-vertex",
|
|
10324
10324
|
"baseUrl": "https://{location}-aiplatform.googleapis.com",
|
|
@@ -10338,10 +10338,6 @@
|
|
|
10338
10338
|
"thinking": {
|
|
10339
10339
|
"mode": "google-level",
|
|
10340
10340
|
"supportedLevels": [
|
|
10341
|
-
{
|
|
10342
|
-
"effort": "minimal",
|
|
10343
|
-
"description": "Very brief reasoning"
|
|
10344
|
-
},
|
|
10345
10341
|
{
|
|
10346
10342
|
"effort": "low",
|
|
10347
10343
|
"description": "Light reasoning"
|
|
@@ -276,7 +276,7 @@ export const DEFAULT_MODEL_PER_PROVIDER: Record<KnownProvider, string> = {
|
|
|
276
276
|
"amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
|
|
277
277
|
"google-antigravity": "gemini-3-pro-high",
|
|
278
278
|
"google-gemini-cli": "gemini-2.5-pro",
|
|
279
|
-
"google-vertex": "gemini-3-
|
|
279
|
+
"google-vertex": "gemini-3.8-flash",
|
|
280
280
|
minimax: "MiniMax-M2.5",
|
|
281
281
|
"minimax-code": "MiniMax-M2.5",
|
|
282
282
|
"minimax-code-cn": "MiniMax-M2.5",
|
|
@@ -65,7 +65,7 @@ function resolveThoughtSignature(isSameProviderAndModel: boolean, signature: str
|
|
|
65
65
|
* Models with strict Google tool round trips require explicit matching call/response IDs.
|
|
66
66
|
*/
|
|
67
67
|
export function requiresToolCallId(modelId: string): boolean {
|
|
68
|
-
return modelId.startsWith("claude-") || modelId === "gemini-3.
|
|
68
|
+
return modelId.startsWith("claude-") || modelId === "gemini-3.8-flash";
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function getGeminiMajorVersion(modelId: string): number | undefined {
|
|
@@ -177,7 +177,7 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
|
|
177
177
|
...(requiresToolCallId(model.id) ? { id: block.id } : {}),
|
|
178
178
|
},
|
|
179
179
|
};
|
|
180
|
-
if (model.provider === "google-vertex" && model.id !== "gemini-3.
|
|
180
|
+
if (model.provider === "google-vertex" && model.id !== "gemini-3.8-flash" && part?.functionCall?.id) {
|
|
181
181
|
delete part.functionCall.id; // Vertex AI does not support 'id' in functionCall
|
|
182
182
|
}
|
|
183
183
|
if (effectiveSignature) {
|
|
@@ -230,7 +230,7 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
|
|
230
230
|
|
|
231
231
|
if (
|
|
232
232
|
model.provider === "google-vertex" &&
|
|
233
|
-
model.id !== "gemini-3.
|
|
233
|
+
model.id !== "gemini-3.8-flash" &&
|
|
234
234
|
functionResponsePart.functionResponse?.id
|
|
235
235
|
) {
|
|
236
236
|
delete functionResponsePart.functionResponse.id; // Vertex AI does not support 'id' in functionResponse
|
|
@@ -450,7 +450,7 @@ export async function resolveGoogleVertexProject(
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
export function resolveGoogleVertexLocation(options?: GoogleVertexOptions): string {
|
|
453
|
-
// Gemini 3.
|
|
453
|
+
// Gemini 3.8 Flash Corporate is served through the global endpoint only.
|
|
454
454
|
// Ignore inherited provider/environment locations so a request cannot drift.
|
|
455
455
|
void options;
|
|
456
456
|
return "global";
|
|
@@ -484,28 +484,31 @@ export function buildGoogleVertexParams(
|
|
|
484
484
|
options: GoogleVertexOptions = {},
|
|
485
485
|
): GenerateContentParameters {
|
|
486
486
|
const contents = convertMessages(model, context);
|
|
487
|
-
const
|
|
487
|
+
const isGemini38Flash = model.id === "gemini-3.8-flash";
|
|
488
|
+
if (isGemini38Flash && options.thinking?.level === "MINIMAL") {
|
|
489
|
+
throw new Error("MINIMAL thinking is not supported by Gemini 3.8 Flash");
|
|
490
|
+
}
|
|
488
491
|
|
|
489
492
|
const generationConfig: GoogleVertexSamplingConfig = {};
|
|
490
|
-
if (!
|
|
493
|
+
if (!isGemini38Flash && options.temperature !== undefined) {
|
|
491
494
|
generationConfig.temperature = options.temperature;
|
|
492
495
|
}
|
|
493
496
|
if (options.maxTokens !== undefined) {
|
|
494
497
|
generationConfig.maxOutputTokens = options.maxTokens;
|
|
495
498
|
}
|
|
496
|
-
if (!
|
|
499
|
+
if (!isGemini38Flash && options.topP !== undefined) {
|
|
497
500
|
generationConfig.topP = options.topP;
|
|
498
501
|
}
|
|
499
|
-
if (!
|
|
502
|
+
if (!isGemini38Flash && options.topK !== undefined) {
|
|
500
503
|
generationConfig.topK = options.topK;
|
|
501
504
|
}
|
|
502
|
-
if (!
|
|
505
|
+
if (!isGemini38Flash && options.minP !== undefined) {
|
|
503
506
|
generationConfig.minP = options.minP;
|
|
504
507
|
}
|
|
505
|
-
if (!
|
|
508
|
+
if (!isGemini38Flash && options.presencePenalty !== undefined) {
|
|
506
509
|
generationConfig.presencePenalty = options.presencePenalty;
|
|
507
510
|
}
|
|
508
|
-
if (!
|
|
511
|
+
if (!isGemini38Flash && options.repetitionPenalty !== undefined) {
|
|
509
512
|
generationConfig.repetitionPenalty = options.repetitionPenalty;
|
|
510
513
|
}
|
|
511
514
|
|
|
@@ -527,11 +530,11 @@ export function buildGoogleVertexParams(
|
|
|
527
530
|
config.toolConfig = undefined;
|
|
528
531
|
}
|
|
529
532
|
|
|
530
|
-
if (model.reasoning && (options.thinking?.enabled ||
|
|
533
|
+
if (model.reasoning && (options.thinking?.enabled || isGemini38Flash)) {
|
|
531
534
|
const cfg: ThinkingConfig = { includeThoughts: true };
|
|
532
535
|
if (options.thinking?.level !== undefined) {
|
|
533
536
|
cfg.thinkingLevel = THINKING_LEVEL_MAP[options.thinking.level];
|
|
534
|
-
} else if (
|
|
537
|
+
} else if (isGemini38Flash) {
|
|
535
538
|
cfg.thinkingLevel = ThinkingLevel.HIGH;
|
|
536
539
|
} else if (options.thinking?.budgetTokens !== undefined) {
|
|
537
540
|
cfg.thinkingBudget = options.thinking.budgetTokens;
|
|
@@ -63,7 +63,12 @@ import {
|
|
|
63
63
|
transformRequestBody,
|
|
64
64
|
} from "./openai-codex/request-transformer";
|
|
65
65
|
import { parseCodexError } from "./openai-codex/response-handler";
|
|
66
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
encodeTextSignatureV1,
|
|
68
|
+
mapOpenAIResponsesStopReason,
|
|
69
|
+
parseTextSignature,
|
|
70
|
+
resolveAssistantMessagePhase,
|
|
71
|
+
} from "./openai-responses-shared";
|
|
67
72
|
import { transformMessages } from "./transform-messages";
|
|
68
73
|
|
|
69
74
|
export interface OpenAICodexResponsesOptions extends StreamOptions {
|
|
@@ -747,11 +752,21 @@ function handleCodexStreamEvent(args: {
|
|
|
747
752
|
runtime.currentBlock = createOutputBlockForItem(item);
|
|
748
753
|
if (!runtime.currentBlock) return firstTokenTime;
|
|
749
754
|
output.content.push(runtime.currentBlock);
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
+
if (runtime.currentBlock.type === "text") {
|
|
756
|
+
stream.push({
|
|
757
|
+
type: "text_start",
|
|
758
|
+
contentIndex: blockIndex(),
|
|
759
|
+
phase: runtime.currentBlock.phase ?? "final_answer",
|
|
760
|
+
partial: output,
|
|
761
|
+
});
|
|
762
|
+
} else {
|
|
763
|
+
const type = getOutputBlockStartEventType(runtime.currentBlock);
|
|
764
|
+
if (type === "thinking_start") {
|
|
765
|
+
stream.push({ type, contentIndex: blockIndex(), partial: output });
|
|
766
|
+
} else {
|
|
767
|
+
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
768
|
+
}
|
|
769
|
+
}
|
|
755
770
|
return firstTokenTime;
|
|
756
771
|
}
|
|
757
772
|
|
|
@@ -838,7 +853,7 @@ function createOutputBlockForItem(item: CodexEventItem): CodexOutputBlock | null
|
|
|
838
853
|
return { type: "thinking", thinking: "" };
|
|
839
854
|
}
|
|
840
855
|
if (item.type === "message") {
|
|
841
|
-
return { type: "text", text: "" };
|
|
856
|
+
return { type: "text", text: "", phase: resolveAssistantMessagePhase(item.phase) };
|
|
842
857
|
}
|
|
843
858
|
if (item.type === "function_call") {
|
|
844
859
|
return {
|
|
@@ -986,12 +1001,14 @@ function handleOutputItemDone(
|
|
|
986
1001
|
runtime.currentBlock.text = item.content
|
|
987
1002
|
.map(content => (content.type === "output_text" ? content.text : content.refusal))
|
|
988
1003
|
.join("");
|
|
989
|
-
const phase = item.phase
|
|
1004
|
+
const phase = resolveAssistantMessagePhase(item.phase);
|
|
1005
|
+
runtime.currentBlock.phase = phase;
|
|
990
1006
|
runtime.currentBlock.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
991
1007
|
stream.push({
|
|
992
1008
|
type: "text_end",
|
|
993
1009
|
contentIndex: blockIndex(),
|
|
994
1010
|
content: runtime.currentBlock.text,
|
|
1011
|
+
phase,
|
|
995
1012
|
partial: output,
|
|
996
1013
|
});
|
|
997
1014
|
runtime.currentBlock = null;
|
|
@@ -2175,7 +2192,7 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex
|
|
|
2175
2192
|
content: [{ type: "output_text", text: textBlock.text.toWellFormed(), annotations: [] }],
|
|
2176
2193
|
status: "completed",
|
|
2177
2194
|
id: msgId,
|
|
2178
|
-
phase: parsedSignature?.phase,
|
|
2195
|
+
phase: parsedSignature?.phase ?? textBlock.phase,
|
|
2179
2196
|
} satisfies ResponseOutputMessage);
|
|
2180
2197
|
continue;
|
|
2181
2198
|
}
|
|
@@ -14,6 +14,7 @@ import { calculateCost } from "../models";
|
|
|
14
14
|
import type {
|
|
15
15
|
Api,
|
|
16
16
|
AssistantMessage,
|
|
17
|
+
AssistantMessagePhase,
|
|
17
18
|
ImageContent,
|
|
18
19
|
Model,
|
|
19
20
|
StopReason,
|
|
@@ -33,6 +34,10 @@ export function encodeTextSignatureV1(id: string, phase?: TextSignatureV1["phase
|
|
|
33
34
|
return JSON.stringify(payload);
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
export function resolveAssistantMessagePhase(phase: unknown): AssistantMessagePhase {
|
|
38
|
+
return phase === "commentary" ? "commentary" : "final_answer";
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
export function parseTextSignature(
|
|
37
42
|
signature: string | undefined,
|
|
38
43
|
): { id: string; phase?: TextSignatureV1["phase"] } | undefined {
|
|
@@ -152,7 +157,7 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
152
157
|
content: [{ type: "output_text", text: block.text.toWellFormed(), annotations: [] }],
|
|
153
158
|
status: "completed",
|
|
154
159
|
id: msgId,
|
|
155
|
-
phase: parsedSignature?.phase,
|
|
160
|
+
phase: parsedSignature?.phase ?? block.phase,
|
|
156
161
|
} satisfies ResponseOutputMessage);
|
|
157
162
|
continue;
|
|
158
163
|
}
|
|
@@ -262,9 +267,14 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
262
267
|
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
263
268
|
} else if (item.type === "message") {
|
|
264
269
|
currentItem = item;
|
|
265
|
-
currentBlock = { type: "text", text: "" };
|
|
270
|
+
currentBlock = { type: "text", text: "", phase: resolveAssistantMessagePhase(item.phase) };
|
|
266
271
|
output.content.push(currentBlock);
|
|
267
|
-
stream.push({
|
|
272
|
+
stream.push({
|
|
273
|
+
type: "text_start",
|
|
274
|
+
contentIndex: blockIndex(),
|
|
275
|
+
phase: currentBlock.phase ?? "final_answer",
|
|
276
|
+
partial: output,
|
|
277
|
+
});
|
|
268
278
|
} else if (item.type === "function_call") {
|
|
269
279
|
currentItem = item;
|
|
270
280
|
currentBlock = {
|
|
@@ -380,11 +390,13 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
380
390
|
currentBlock.text = item.content
|
|
381
391
|
.map(part => (part.type === "output_text" ? (part.text ?? "") : (part.refusal ?? "")))
|
|
382
392
|
.join("");
|
|
383
|
-
currentBlock.
|
|
393
|
+
currentBlock.phase = resolveAssistantMessagePhase(item.phase);
|
|
394
|
+
currentBlock.textSignature = encodeTextSignatureV1(item.id, currentBlock.phase);
|
|
384
395
|
stream.push({
|
|
385
396
|
type: "text_end",
|
|
386
397
|
contentIndex: blockIndex(),
|
|
387
398
|
content: currentBlock.text,
|
|
399
|
+
phase: currentBlock.phase ?? "final_answer",
|
|
388
400
|
partial: output,
|
|
389
401
|
});
|
|
390
402
|
currentBlock = null;
|
package/src/types.ts
CHANGED
|
@@ -258,10 +258,12 @@ export type StreamFunction<TApi extends Api> = (
|
|
|
258
258
|
options: OptionsForApi<TApi>,
|
|
259
259
|
) => AssistantMessageEventStream;
|
|
260
260
|
|
|
261
|
+
export type AssistantMessagePhase = "commentary" | "final_answer";
|
|
262
|
+
|
|
261
263
|
export interface TextSignatureV1 {
|
|
262
264
|
v: 1;
|
|
263
265
|
id: string;
|
|
264
|
-
phase?:
|
|
266
|
+
phase?: AssistantMessagePhase;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
/**
|
|
@@ -288,6 +290,8 @@ export interface WebCitation {
|
|
|
288
290
|
export interface TextContent {
|
|
289
291
|
type: "text";
|
|
290
292
|
text: string;
|
|
293
|
+
/** Semantic role supplied by Responses. Absence has final-answer semantics. */
|
|
294
|
+
phase?: AssistantMessagePhase;
|
|
291
295
|
textSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)
|
|
292
296
|
/** Structured citations for this span, when the provider ran a server-side search. */
|
|
293
297
|
citations?: WebCitation[];
|
|
@@ -468,9 +472,15 @@ export interface Context {
|
|
|
468
472
|
|
|
469
473
|
export type AssistantMessageEvent =
|
|
470
474
|
| { type: "start"; contentIndex?: undefined; partial: AssistantMessage }
|
|
471
|
-
| { type: "text_start"; contentIndex: number; partial: AssistantMessage }
|
|
475
|
+
| { type: "text_start"; contentIndex: number; phase?: AssistantMessagePhase; partial: AssistantMessage }
|
|
472
476
|
| { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
473
|
-
| {
|
|
477
|
+
| {
|
|
478
|
+
type: "text_end";
|
|
479
|
+
contentIndex: number;
|
|
480
|
+
content: string;
|
|
481
|
+
phase?: AssistantMessagePhase;
|
|
482
|
+
partial: AssistantMessage;
|
|
483
|
+
}
|
|
474
484
|
| { type: "thinking_start"; contentIndex: number; partial: AssistantMessage }
|
|
475
485
|
| { type: "thinking_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
476
486
|
| { type: "thinking_end"; contentIndex: number; content: string; partial: AssistantMessage }
|