@llumiverse/drivers 0.23.0 → 0.24.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/README.md +141 -218
- package/lib/cjs/azure/azure_foundry.js +46 -2
- package/lib/cjs/azure/azure_foundry.js.map +1 -1
- package/lib/cjs/bedrock/index.js +140 -15
- package/lib/cjs/bedrock/index.js.map +1 -1
- package/lib/cjs/groq/index.js +115 -85
- package/lib/cjs/groq/index.js.map +1 -1
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/openai/index.js +310 -114
- package/lib/cjs/openai/index.js.map +1 -1
- package/lib/cjs/openai/openai_compatible.js +62 -0
- package/lib/cjs/openai/openai_compatible.js.map +1 -0
- package/lib/cjs/openai/openai_format.js +32 -39
- package/lib/cjs/openai/openai_format.js.map +1 -1
- package/lib/cjs/vertexai/index.js +147 -0
- package/lib/cjs/vertexai/index.js.map +1 -1
- package/lib/cjs/vertexai/models/claude.js +88 -2
- package/lib/cjs/vertexai/models/claude.js.map +1 -1
- package/lib/cjs/vertexai/models/gemini.js +59 -20
- package/lib/cjs/vertexai/models/gemini.js.map +1 -1
- package/lib/cjs/xai/index.js +10 -16
- package/lib/cjs/xai/index.js.map +1 -1
- package/lib/esm/azure/azure_foundry.js +46 -2
- package/lib/esm/azure/azure_foundry.js.map +1 -1
- package/lib/esm/bedrock/index.js +141 -16
- package/lib/esm/bedrock/index.js.map +1 -1
- package/lib/esm/groq/index.js +115 -85
- package/lib/esm/groq/index.js.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/openai/index.js +311 -115
- package/lib/esm/openai/index.js.map +1 -1
- package/lib/esm/openai/openai_compatible.js +55 -0
- package/lib/esm/openai/openai_compatible.js.map +1 -0
- package/lib/esm/openai/openai_format.js +32 -39
- package/lib/esm/openai/openai_format.js.map +1 -1
- package/lib/esm/vertexai/index.js +148 -1
- package/lib/esm/vertexai/index.js.map +1 -1
- package/lib/esm/vertexai/models/claude.js +87 -2
- package/lib/esm/vertexai/models/claude.js.map +1 -1
- package/lib/esm/vertexai/models/gemini.js +60 -21
- package/lib/esm/vertexai/models/gemini.js.map +1 -1
- package/lib/esm/xai/index.js +10 -16
- package/lib/esm/xai/index.js.map +1 -1
- package/lib/types/azure/azure_foundry.d.ts +7 -5
- package/lib/types/azure/azure_foundry.d.ts.map +1 -1
- package/lib/types/bedrock/index.d.ts +5 -0
- package/lib/types/bedrock/index.d.ts.map +1 -1
- package/lib/types/groq/index.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/openai/index.d.ts +13 -7
- package/lib/types/openai/index.d.ts.map +1 -1
- package/lib/types/openai/openai_compatible.d.ts +26 -0
- package/lib/types/openai/openai_compatible.d.ts.map +1 -0
- package/lib/types/openai/openai_format.d.ts +4 -2
- package/lib/types/openai/openai_format.d.ts.map +1 -1
- package/lib/types/vertexai/index.d.ts +11 -0
- package/lib/types/vertexai/index.d.ts.map +1 -1
- package/lib/types/vertexai/models/claude.d.ts +8 -0
- package/lib/types/vertexai/models/claude.d.ts.map +1 -1
- package/lib/types/vertexai/models/gemini.d.ts +1 -1
- package/lib/types/vertexai/models/gemini.d.ts.map +1 -1
- package/lib/types/xai/index.d.ts +2 -3
- package/lib/types/xai/index.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/azure/azure_foundry.ts +56 -7
- package/src/bedrock/index.ts +188 -24
- package/src/groq/index.ts +120 -94
- package/src/index.ts +1 -0
- package/src/openai/index.ts +363 -136
- package/src/openai/openai_compatible.ts +74 -0
- package/src/openai/openai_format.ts +44 -54
- package/src/vertexai/index.ts +186 -0
- package/src/vertexai/models/claude.ts +97 -2
- package/src/vertexai/models/gemini.ts +78 -27
- package/src/xai/index.ts +10 -17
|
@@ -5,12 +5,20 @@ import {
|
|
|
5
5
|
} from "@google/genai";
|
|
6
6
|
import {
|
|
7
7
|
AIModel, Completion, CompletionChunkObject, CompletionResult, ExecutionOptions,
|
|
8
|
-
ExecutionTokenUsage,
|
|
9
|
-
|
|
8
|
+
ExecutionTokenUsage,
|
|
9
|
+
getConversationMeta,
|
|
10
|
+
getMaxTokensLimitVertexAi,
|
|
11
|
+
incrementConversationTurn,
|
|
12
|
+
JSONObject, JSONSchema, ModelType, PromptOptions, PromptRole,
|
|
13
|
+
PromptSegment, readStreamAsBase64, StatelessExecutionOptions,
|
|
14
|
+
stripBase64ImagesFromConversation,
|
|
15
|
+
ToolDefinition, ToolUse,
|
|
16
|
+
truncateLargeTextInConversation,
|
|
17
|
+
unwrapConversationArray,
|
|
10
18
|
VertexAIGeminiOptions
|
|
11
19
|
} from "@llumiverse/core";
|
|
12
20
|
import { asyncMap } from "@llumiverse/core/async";
|
|
13
|
-
import {
|
|
21
|
+
import { GenerateContentPrompt, VertexAIDriver } from "../index.js";
|
|
14
22
|
import { ModelDefinition } from "../models.js";
|
|
15
23
|
|
|
16
24
|
function supportsStructuredOutput(options: PromptOptions): boolean {
|
|
@@ -467,11 +475,17 @@ function collectToolUseParts(content: Content): ToolUse[] | undefined {
|
|
|
467
475
|
const parts = content.parts ?? [];
|
|
468
476
|
for (const part of parts) {
|
|
469
477
|
if (part.functionCall) {
|
|
470
|
-
|
|
478
|
+
const toolUse: ToolUse = {
|
|
471
479
|
id: part.functionCall.name ?? '',
|
|
472
480
|
tool_name: part.functionCall.name ?? '',
|
|
473
481
|
tool_input: part.functionCall.args as JSONObject,
|
|
474
|
-
}
|
|
482
|
+
};
|
|
483
|
+
// Capture thought_signature for Gemini thinking models (2.5+/3.0+)
|
|
484
|
+
// This must be passed back with the function response
|
|
485
|
+
if (part.thoughtSignature) {
|
|
486
|
+
toolUse.thought_signature = part.thoughtSignature;
|
|
487
|
+
}
|
|
488
|
+
out.push(toolUse);
|
|
475
489
|
}
|
|
476
490
|
}
|
|
477
491
|
return out.length > 0 ? out : undefined;
|
|
@@ -545,7 +559,7 @@ function geminiThinkingConfig(option: StatelessExecutionOptions): ThinkingConfig
|
|
|
545
559
|
const model_options = option.model_options as VertexAIGeminiOptions | undefined;
|
|
546
560
|
const include_thoughts = model_options?.include_thoughts ?? false;
|
|
547
561
|
if (model_options?.thinking_budget_tokens) {
|
|
548
|
-
return {includeThoughts: include_thoughts, thinkingBudget: model_options.thinking_budget_tokens};
|
|
562
|
+
return { includeThoughts: include_thoughts, thinkingBudget: model_options.thinking_budget_tokens };
|
|
549
563
|
}
|
|
550
564
|
|
|
551
565
|
// Set minimum thinking level by default.
|
|
@@ -623,16 +637,18 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
623
637
|
if (!msg.tool_use_id) {
|
|
624
638
|
throw new Error("Tool response missing tool_use_id");
|
|
625
639
|
}
|
|
640
|
+
// Build functionResponse part with optional thought_signature for Gemini thinking models
|
|
641
|
+
const functionResponsePart: Part = {
|
|
642
|
+
functionResponse: {
|
|
643
|
+
name: msg.tool_use_id,
|
|
644
|
+
response: formatFunctionResponse(msg.content || ''),
|
|
645
|
+
},
|
|
646
|
+
// Include thought_signature if provided (required for Gemini 2.5+/3.0+ thinking models)
|
|
647
|
+
thoughtSignature: msg.thought_signature,
|
|
648
|
+
};
|
|
626
649
|
contents.push({
|
|
627
650
|
role: 'user',
|
|
628
|
-
parts: [
|
|
629
|
-
{
|
|
630
|
-
functionResponse: {
|
|
631
|
-
name: msg.tool_use_id,
|
|
632
|
-
response: formatFunctionResponse(msg.content || ''),
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
]
|
|
651
|
+
parts: [functionResponsePart]
|
|
636
652
|
});
|
|
637
653
|
} else { // PromptRole.user, PromptRole.assistant, PromptRole.safety
|
|
638
654
|
const parts: Part[] = [];
|
|
@@ -646,14 +662,27 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
646
662
|
// File content handling
|
|
647
663
|
if (msg.files) {
|
|
648
664
|
for (const f of msg.files) {
|
|
649
|
-
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
665
|
+
let fileUrl = await f.getURL();
|
|
666
|
+
const isGsUrl = fileUrl.startsWith('gs://') || fileUrl.startsWith('https://storage.googleapis.com/');
|
|
667
|
+
|
|
668
|
+
if (isGsUrl) {
|
|
669
|
+
parts.push({
|
|
670
|
+
fileData: {
|
|
671
|
+
fileUri: fileUrl,
|
|
672
|
+
mimeType: f.mime_type
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
} else {
|
|
676
|
+
// Inline data handling
|
|
677
|
+
const stream = await f.getStream();
|
|
678
|
+
const data = await readStreamAsBase64(stream);
|
|
679
|
+
parts.push({
|
|
680
|
+
inlineData: {
|
|
681
|
+
data,
|
|
682
|
+
mimeType: f.mime_type
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
}
|
|
657
686
|
}
|
|
658
687
|
}
|
|
659
688
|
|
|
@@ -742,7 +771,7 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
742
771
|
const modelName = splits[splits.length - 1];
|
|
743
772
|
options = { ...options, model: modelName };
|
|
744
773
|
|
|
745
|
-
let conversation = updateConversation(options.conversation
|
|
774
|
+
let conversation = updateConversation(options.conversation, prompt.contents);
|
|
746
775
|
prompt.contents = conversation;
|
|
747
776
|
|
|
748
777
|
// TODO: Remove hack, use global endpoint manually if needed.
|
|
@@ -792,12 +821,27 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
792
821
|
finish_reason = "tool_use";
|
|
793
822
|
}
|
|
794
823
|
|
|
824
|
+
// Increment turn counter for deferred stripping
|
|
825
|
+
conversation = incrementConversationTurn(conversation) as Content[];
|
|
826
|
+
|
|
827
|
+
// Strip large base64 image data based on options.stripImagesAfterTurns
|
|
828
|
+
const currentTurn = getConversationMeta(conversation).turnNumber;
|
|
829
|
+
const stripOptions = {
|
|
830
|
+
keepForTurns: options.stripImagesAfterTurns ?? Infinity,
|
|
831
|
+
currentTurn,
|
|
832
|
+
textMaxTokens: options.stripTextMaxTokens
|
|
833
|
+
};
|
|
834
|
+
let processedConversation = stripBase64ImagesFromConversation(conversation, stripOptions);
|
|
835
|
+
|
|
836
|
+
// Truncate large text content if configured
|
|
837
|
+
processedConversation = truncateLargeTextInConversation(processedConversation, stripOptions);
|
|
838
|
+
|
|
795
839
|
return {
|
|
796
840
|
result: result && result.length > 0 ? result : [{ type: "text" as const, value: '' }],
|
|
797
841
|
token_usage: token_usage,
|
|
798
842
|
finish_reason: finish_reason,
|
|
799
843
|
original_response: options.include_original_response ? response : undefined,
|
|
800
|
-
conversation,
|
|
844
|
+
conversation: processedConversation,
|
|
801
845
|
tool_use
|
|
802
846
|
} satisfies Completion;
|
|
803
847
|
}
|
|
@@ -811,6 +855,10 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
811
855
|
const modelName = splits[splits.length - 1];
|
|
812
856
|
options = { ...options, model: modelName };
|
|
813
857
|
|
|
858
|
+
// Include conversation history in prompt contents (same as non-streaming)
|
|
859
|
+
const conversation = updateConversation(options.conversation, prompt.contents);
|
|
860
|
+
prompt.contents = conversation;
|
|
861
|
+
|
|
814
862
|
if (options.model.includes("gemini-2.5-flash-image")) {
|
|
815
863
|
region = "global"; // Gemini Flash Image only available in global region, this is for nano-banana model
|
|
816
864
|
}
|
|
@@ -897,16 +945,19 @@ function getToolFunction(tool: ToolDefinition): FunctionDeclaration {
|
|
|
897
945
|
};
|
|
898
946
|
}
|
|
899
947
|
|
|
900
|
-
|
|
901
948
|
/**
|
|
902
949
|
* Update the conversation messages
|
|
903
950
|
* @param prompt
|
|
904
951
|
* @param response
|
|
905
952
|
* @returns
|
|
906
953
|
*/
|
|
907
|
-
function updateConversation(conversation:
|
|
908
|
-
|
|
954
|
+
function updateConversation(conversation: unknown, prompt: Content[]): Content[] {
|
|
955
|
+
// Unwrap array if wrapped, otherwise treat as array
|
|
956
|
+
const unwrapped = unwrapConversationArray<Content>(conversation);
|
|
957
|
+
const convArray = unwrapped ?? (conversation as Content[] || []);
|
|
958
|
+
return convArray.concat(prompt);
|
|
909
959
|
}
|
|
960
|
+
|
|
910
961
|
/**
|
|
911
962
|
*
|
|
912
963
|
* Gemini supports JSON output in the response. so we test if the response is a valid JSON object. otherwise we treat the response as a string.
|
package/src/xai/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIModel,
|
|
1
|
+
import { AIModel, DriverOptions, PromptOptions, PromptSegment, Providers } from "@llumiverse/core";
|
|
2
2
|
import { formatOpenAILikeMultimodalPrompt, OpenAIPromptFormatterOptions } from "../openai/openai_format.js";
|
|
3
3
|
import { FetchClient } from "@vertesia/api-fetch-client";
|
|
4
4
|
import OpenAI from "openai";
|
|
@@ -15,7 +15,7 @@ export interface xAiDriverOptions extends DriverOptions {
|
|
|
15
15
|
export class xAIDriver extends BaseOpenAIDriver {
|
|
16
16
|
|
|
17
17
|
service: OpenAI;
|
|
18
|
-
provider
|
|
18
|
+
readonly provider = Providers.xai;
|
|
19
19
|
xai_service: FetchClient;
|
|
20
20
|
DEFAULT_ENDPOINT = "https://api.x.ai/v1";
|
|
21
21
|
|
|
@@ -31,7 +31,6 @@ export class xAIDriver extends BaseOpenAIDriver {
|
|
|
31
31
|
baseURL: opts.endpoint ?? this.DEFAULT_ENDPOINT,
|
|
32
32
|
});
|
|
33
33
|
this.xai_service = new FetchClient(opts.endpoint ?? this.DEFAULT_ENDPOINT).withAuthCallback(async () => `Bearer ${opts.apiKey}`);
|
|
34
|
-
this.provider = "xai";
|
|
35
34
|
//this.formatPrompt = this._formatPrompt; //TODO: fix xai prompt formatting
|
|
36
35
|
}
|
|
37
36
|
|
|
@@ -49,17 +48,9 @@ export class xAIDriver extends BaseOpenAIDriver {
|
|
|
49
48
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
finish_reason: result.choices[0].finish_reason,
|
|
56
|
-
token_usage: {
|
|
57
|
-
prompt: result.usage?.prompt_tokens,
|
|
58
|
-
result: result.usage?.completion_tokens,
|
|
59
|
-
total: result.usage?.total_tokens,
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
51
|
+
// Note: We intentionally do NOT override extractDataFromResponse here.
|
|
52
|
+
// The base class implementation properly handles tool_calls extraction.
|
|
53
|
+
// xAI's API is OpenAI-compatible and returns tool_calls in the same format.
|
|
63
54
|
|
|
64
55
|
async listModels(): Promise<AIModel[]> {
|
|
65
56
|
const [lm, em] = await Promise.all([
|
|
@@ -76,10 +67,12 @@ export class xAIDriver extends BaseOpenAIDriver {
|
|
|
76
67
|
return {
|
|
77
68
|
id: model.id,
|
|
78
69
|
provider: this.provider,
|
|
79
|
-
name: model.
|
|
80
|
-
description: model.
|
|
70
|
+
name: model.id,
|
|
71
|
+
description: `${model.id} by ${model.owned_by}`,
|
|
81
72
|
is_multimodal: model.input_modalities.length > 1,
|
|
82
|
-
|
|
73
|
+
input_modalities: model.input_modalities,
|
|
74
|
+
output_modalities: model.output_modalities,
|
|
75
|
+
tags: [...model.input_modalities.map(m => `i:${m}`), ...model.output_modalities.map(m => `o:${m}`)],
|
|
83
76
|
} satisfies AIModel;
|
|
84
77
|
});
|
|
85
78
|
|