@llumiverse/drivers 0.19.0 → 0.20.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/lib/cjs/bedrock/converse.js +181 -123
- package/lib/cjs/bedrock/converse.js.map +1 -1
- package/lib/cjs/bedrock/index.js +152 -70
- package/lib/cjs/bedrock/index.js.map +1 -1
- package/lib/cjs/vertexai/index.js +1 -0
- package/lib/cjs/vertexai/index.js.map +1 -1
- package/lib/cjs/vertexai/models/claude.js +227 -118
- package/lib/cjs/vertexai/models/claude.js.map +1 -1
- package/lib/cjs/vertexai/models/gemini.js +110 -70
- package/lib/cjs/vertexai/models/gemini.js.map +1 -1
- package/lib/cjs/vertexai/models/imagen.js +2 -2
- package/lib/cjs/vertexai/models/imagen.js.map +1 -1
- package/lib/cjs/watsonx/index.js +10 -10
- package/lib/cjs/watsonx/index.js.map +1 -1
- package/lib/esm/bedrock/converse.js +180 -122
- package/lib/esm/bedrock/converse.js.map +1 -1
- package/lib/esm/bedrock/index.js +153 -71
- package/lib/esm/bedrock/index.js.map +1 -1
- package/lib/esm/vertexai/index.js +1 -0
- package/lib/esm/vertexai/index.js.map +1 -1
- package/lib/esm/vertexai/models/claude.js +228 -119
- package/lib/esm/vertexai/models/claude.js.map +1 -1
- package/lib/esm/vertexai/models/gemini.js +109 -70
- package/lib/esm/vertexai/models/gemini.js.map +1 -1
- package/lib/esm/vertexai/models/imagen.js +2 -2
- package/lib/esm/vertexai/models/imagen.js.map +1 -1
- package/lib/esm/watsonx/index.js +10 -10
- package/lib/esm/watsonx/index.js.map +1 -1
- package/lib/types/bedrock/converse.d.ts +2 -2
- package/lib/types/bedrock/converse.d.ts.map +1 -1
- package/lib/types/bedrock/index.d.ts +5 -5
- package/lib/types/bedrock/index.d.ts.map +1 -1
- package/lib/types/vertexai/index.d.ts +2 -3
- package/lib/types/vertexai/index.d.ts.map +1 -1
- package/lib/types/vertexai/models/claude.d.ts +5 -7
- package/lib/types/vertexai/models/claude.d.ts.map +1 -1
- package/lib/types/vertexai/models/gemini.d.ts +4 -2
- package/lib/types/vertexai/models/gemini.d.ts.map +1 -1
- package/lib/types/vertexai/models.d.ts +2 -2
- package/lib/types/vertexai/models.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/bedrock/converse.ts +194 -129
- package/src/bedrock/index.ts +177 -82
- package/src/vertexai/index.ts +3 -3
- package/src/vertexai/models/claude.ts +268 -138
- package/src/vertexai/models/gemini.ts +120 -77
- package/src/vertexai/models/imagen.ts +3 -3
- package/src/vertexai/models.ts +2 -2
- package/src/watsonx/index.ts +12 -12
|
@@ -44,12 +44,10 @@ const geminiSafetySettings: SafetySetting[] = [
|
|
|
44
44
|
];
|
|
45
45
|
|
|
46
46
|
function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentPrompt): GenerateContentParameters {
|
|
47
|
-
//1.0 Ultra does not support JSON output, 1.0 Pro does.
|
|
48
|
-
const useStructuredOutput = supportsStructuredOutput(options);
|
|
49
|
-
|
|
50
47
|
const model_options = options.model_options as any;
|
|
51
48
|
const tools = getToolDefinitions(options.tools);
|
|
52
|
-
|
|
49
|
+
|
|
50
|
+
const useStructuredOutput = supportsStructuredOutput(options) && !tools;
|
|
53
51
|
|
|
54
52
|
return {
|
|
55
53
|
model: options.model,
|
|
@@ -65,7 +63,7 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
|
|
|
65
63
|
} : undefined,
|
|
66
64
|
candidateCount: 1,
|
|
67
65
|
//JSON/Structured output
|
|
68
|
-
responseMimeType: useStructuredOutput ? "application/json" :
|
|
66
|
+
responseMimeType: useStructuredOutput ? "application/json" : undefined,
|
|
69
67
|
responseSchema: useStructuredOutput ? parseJSONtoSchema(options.result_schema, true) : undefined,
|
|
70
68
|
//Model options
|
|
71
69
|
temperature: model_options?.temperature,
|
|
@@ -76,10 +74,10 @@ function getGeminiPayload(options: ExecutionOptions, prompt: GenerateContentProm
|
|
|
76
74
|
presencePenalty: model_options?.presence_penalty,
|
|
77
75
|
frequencyPenalty: model_options?.frequency_penalty,
|
|
78
76
|
seed: model_options?.seed,
|
|
79
|
-
thinkingConfig: model_options?.include_thoughts || model_options?.
|
|
77
|
+
thinkingConfig: model_options?.include_thoughts || model_options?.thinking_budget_tokens ?
|
|
80
78
|
{
|
|
81
79
|
includeThoughts: model_options?.include_thoughts,
|
|
82
|
-
thinkingBudget: model_options?.
|
|
80
|
+
thinkingBudget: model_options?.thinking_budget_tokens,
|
|
83
81
|
} : undefined,
|
|
84
82
|
}
|
|
85
83
|
};
|
|
@@ -133,8 +131,8 @@ function convertType(type?: string | string[]): Type | undefined {
|
|
|
133
131
|
*/
|
|
134
132
|
function convertSchema(jsSchema?: JSONSchema, depth: number = 0, requiredAll = false): Schema {
|
|
135
133
|
// Prevent circular references
|
|
136
|
-
if (depth >
|
|
137
|
-
throw new Error("Maximum schema depth exceeded. Possible circular reference detected.");
|
|
134
|
+
if (depth > 20) {
|
|
135
|
+
throw new Error("Maximum schema depth (20) exceeded. Possible circular reference detected.");
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
if (!jsSchema) return {};
|
|
@@ -438,6 +436,35 @@ function collectToolUseParts(content: Content): ToolUse[] | undefined {
|
|
|
438
436
|
return out.length > 0 ? out : undefined;
|
|
439
437
|
}
|
|
440
438
|
|
|
439
|
+
export function mergeConsecutiveRole(contents: Content[] | undefined): Content[] {
|
|
440
|
+
if (!contents || contents.length === 0) return [];
|
|
441
|
+
|
|
442
|
+
const needsMerging = contents.some((content, i) =>
|
|
443
|
+
i < contents.length - 1 && content.role === contents[i + 1].role
|
|
444
|
+
);
|
|
445
|
+
// If no merging needed, return original array
|
|
446
|
+
if (!needsMerging) {
|
|
447
|
+
return contents;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const result: Content[] = [];
|
|
451
|
+
let currentContent = { ...contents[0], parts: [...(contents[0].parts || [])] };
|
|
452
|
+
|
|
453
|
+
for (let i = 1; i < contents.length; i++) {
|
|
454
|
+
if (currentContent.role === contents[i].role) {
|
|
455
|
+
// Same role - concatenate parts (without merging individual parts)
|
|
456
|
+
currentContent.parts = (currentContent.parts || []).concat(...(contents[i].parts || []));
|
|
457
|
+
} else {
|
|
458
|
+
// Different role - push current and start new
|
|
459
|
+
result.push(currentContent);
|
|
460
|
+
currentContent = { ...contents[i], parts: [...(contents[i].parts || [])] };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
result.push(currentContent);
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
|
|
441
468
|
export class GeminiModelDefinition implements ModelDefinition<GenerateContentPrompt> {
|
|
442
469
|
|
|
443
470
|
model: AIModel
|
|
@@ -448,13 +475,13 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
448
475
|
name: modelId,
|
|
449
476
|
provider: 'vertexai',
|
|
450
477
|
type: ModelType.Text,
|
|
451
|
-
can_stream: true
|
|
478
|
+
can_stream: true
|
|
452
479
|
} satisfies AIModel;
|
|
453
480
|
}
|
|
454
481
|
|
|
455
482
|
preValidationProcessing(result: Completion, options: ExecutionOptions): { result: Completion, options: ExecutionOptions } {
|
|
456
|
-
//
|
|
457
|
-
if (!options.result_schema || !result.result) {
|
|
483
|
+
// Guard clause, if no result_schema, error, or tool use, skip processing
|
|
484
|
+
if (!options.result_schema || !result.result || result.tool_use || result.error) {
|
|
458
485
|
return { result, options };
|
|
459
486
|
}
|
|
460
487
|
try {
|
|
@@ -463,106 +490,122 @@ export class GeminiModelDefinition implements ModelDefinition<GenerateContentPro
|
|
|
463
490
|
return { result, options };
|
|
464
491
|
} catch (error) {
|
|
465
492
|
// Log error during processing but don't fail the completion
|
|
466
|
-
console.warn('Error during Gemini JSON pre-validation', error);
|
|
493
|
+
console.warn('Error during Gemini JSON pre-validation: ', error);
|
|
467
494
|
// Return original result if cleanup fails
|
|
468
495
|
return { result, options };
|
|
469
496
|
}
|
|
470
497
|
}
|
|
471
498
|
|
|
472
|
-
async createPrompt(_driver: VertexAIDriver, segments: PromptSegment[], options:
|
|
499
|
+
async createPrompt(_driver: VertexAIDriver, segments: PromptSegment[], options: ExecutionOptions): Promise<GenerateContentPrompt> {
|
|
473
500
|
const splits = options.model.split("/");
|
|
474
501
|
const modelName = splits[splits.length - 1];
|
|
475
502
|
options = { ...options, model: modelName };
|
|
476
503
|
|
|
477
504
|
const schema = options.result_schema;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
let lastUserContent: Content | undefined = undefined;
|
|
483
|
-
const toolParts = [];
|
|
505
|
+
let contents: Content[] = [];
|
|
506
|
+
let system: Content | undefined = { role: "user", parts: [] }; // Single content block for system messages
|
|
507
|
+
|
|
508
|
+
const safety: Content[] = [];
|
|
484
509
|
|
|
485
510
|
for (const msg of segments) {
|
|
511
|
+
// Role specific handling
|
|
512
|
+
if (msg.role === PromptRole.system) {
|
|
513
|
+
// Text only for system messages
|
|
514
|
+
if (msg.files && msg.files.length > 0) {
|
|
515
|
+
throw new Error("Gemini does not support files/images etc. in system messages. Only text content is allowed.");
|
|
516
|
+
}
|
|
486
517
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
518
|
+
if (msg.content) {
|
|
519
|
+
system.parts?.push({
|
|
520
|
+
text: msg.content
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
} else if (msg.role === PromptRole.tool) {
|
|
524
|
+
if (!msg.tool_use_id) {
|
|
525
|
+
throw new Error("Tool response missing tool_use_id");
|
|
526
|
+
}
|
|
527
|
+
contents.push({
|
|
528
|
+
role: 'user',
|
|
529
|
+
parts: [
|
|
530
|
+
{
|
|
531
|
+
functionResponse: {
|
|
532
|
+
name: msg.tool_use_id,
|
|
533
|
+
response: formatFunctionResponse(msg.content || ''),
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
]
|
|
537
|
+
});
|
|
538
|
+
} else { // PromptRole.user, PromptRole.assistant, PromptRole.safety
|
|
539
|
+
const parts: Part[] = [];
|
|
540
|
+
// Text content handling
|
|
541
|
+
if (msg.content) {
|
|
542
|
+
parts.push({
|
|
543
|
+
text: msg.content,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// File content handling
|
|
493
548
|
if (msg.files) {
|
|
494
549
|
for (const f of msg.files) {
|
|
495
550
|
const stream = await f.getStream();
|
|
496
551
|
const data = await readStreamAsBase64(stream);
|
|
497
|
-
|
|
552
|
+
parts.push({
|
|
498
553
|
inlineData: {
|
|
499
554
|
data,
|
|
500
|
-
mimeType: f.mime_type
|
|
555
|
+
mimeType: f.mime_type
|
|
501
556
|
}
|
|
502
557
|
});
|
|
503
558
|
}
|
|
504
559
|
}
|
|
505
560
|
|
|
506
|
-
if (
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (lastUserContent && lastUserContent.role === role) {
|
|
519
|
-
lastUserContent?.parts?.push({ text: msg.content });
|
|
520
|
-
fileParts?.forEach(p => lastUserContent?.parts?.push(p));
|
|
521
|
-
} else {
|
|
522
|
-
const content: Content = {
|
|
523
|
-
role,
|
|
524
|
-
parts: [{ text: msg.content }],
|
|
525
|
-
}
|
|
526
|
-
fileParts?.forEach(p => content?.parts?.push(p));
|
|
527
|
-
|
|
528
|
-
if (role === 'user') {
|
|
529
|
-
lastUserContent = content;
|
|
561
|
+
if (parts.length > 0) {
|
|
562
|
+
if (msg.role === PromptRole.safety) {
|
|
563
|
+
safety.push({
|
|
564
|
+
role: 'user',
|
|
565
|
+
parts,
|
|
566
|
+
});
|
|
567
|
+
} else {
|
|
568
|
+
contents.push({
|
|
569
|
+
role: msg.role === PromptRole.assistant ? 'model' : 'user',
|
|
570
|
+
parts,
|
|
571
|
+
});
|
|
530
572
|
}
|
|
531
|
-
contents.push(content);
|
|
532
573
|
}
|
|
533
574
|
}
|
|
534
575
|
}
|
|
535
576
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
safety.push("Fill all appropriate fields in the JSON output.");
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (safety.length > 0) {
|
|
546
|
-
const content = safety.join('\n');
|
|
547
|
-
if (lastUserContent) {
|
|
548
|
-
lastUserContent?.parts?.push({ text: content });
|
|
577
|
+
// Adding JSON Schema to system message
|
|
578
|
+
if (schema) {
|
|
579
|
+
if (supportsStructuredOutput(options) && !options.tools) {
|
|
580
|
+
// Gemini structured output is unnecessarily sparse. Adding encouragement to fill the fields.
|
|
581
|
+
// Putting JSON in prompt is not recommended by Google, when using structured output.
|
|
582
|
+
system.parts?.push({ text: "Fill all appropriate fields in the JSON output." });
|
|
549
583
|
} else {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
parts
|
|
553
|
-
|
|
584
|
+
// Fallback to putting the schema in the system instructions, if not using structured output.
|
|
585
|
+
if (options.tools) {
|
|
586
|
+
system.parts?.push({
|
|
587
|
+
text: "When not calling tools, the output must be a JSON object using the following JSON Schema:\n" + JSON.stringify(schema)
|
|
588
|
+
});
|
|
589
|
+
} else {
|
|
590
|
+
system.parts?.push({ text: "The output must be a JSON object using the following JSON Schema:\n" + JSON.stringify(schema) });
|
|
591
|
+
}
|
|
554
592
|
}
|
|
555
593
|
}
|
|
594
|
+
|
|
595
|
+
// If no system messages, set system to undefined.
|
|
596
|
+
if (!system.parts || system.parts.length === 0) {
|
|
597
|
+
system = undefined;
|
|
598
|
+
}
|
|
556
599
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
parts: toolParts,
|
|
561
|
-
});
|
|
600
|
+
// Add safety messages to the end of contents. They are in effect user messages that come at the end.
|
|
601
|
+
if (safety.length > 0) {
|
|
602
|
+
contents = contents.concat(safety);
|
|
562
603
|
}
|
|
563
604
|
|
|
564
|
-
//
|
|
565
|
-
|
|
605
|
+
// Merge consecutive messages with the same role. Note: this may not be necessary, works without it, keeping to match previous behavior.
|
|
606
|
+
contents = mergeConsecutiveRole(contents);
|
|
607
|
+
|
|
608
|
+
return { contents, system };
|
|
566
609
|
}
|
|
567
610
|
|
|
568
611
|
async requestTextCompletion(driver: VertexAIDriver, prompt: GenerateContentPrompt, options: ExecutionOptions): Promise<Completion> {
|
|
@@ -341,13 +341,13 @@ export class ImagenModelDefinition {
|
|
|
341
341
|
if (options.model_options?._option_id !== "vertexai-imagen") {
|
|
342
342
|
driver.logger.warn("Invalid model options", {options: options.model_options });
|
|
343
343
|
}
|
|
344
|
-
options.model_options = options.model_options as ImagenOptions;
|
|
344
|
+
options.model_options = options.model_options as ImagenOptions | undefined;
|
|
345
345
|
|
|
346
346
|
if (options.output_modality !== Modalities.image) {
|
|
347
347
|
throw new Error(`Image generation requires image output_modality`);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
const taskType: string = options.model_options
|
|
350
|
+
const taskType: string = options.model_options?.edit_mode ?? ImagenTaskType.TEXT_IMAGE;
|
|
351
351
|
|
|
352
352
|
driver.logger.info("Task type: " + taskType);
|
|
353
353
|
|
|
@@ -362,7 +362,7 @@ export class ImagenModelDefinition {
|
|
|
362
362
|
}
|
|
363
363
|
const instances = [instanceValue];
|
|
364
364
|
|
|
365
|
-
let parameter: any = getImagenParameters(taskType, options.model_options);
|
|
365
|
+
let parameter: any = getImagenParameters(taskType, options.model_options ?? {_option_id: "vertexai-imagen"});
|
|
366
366
|
parameter.negativePrompt = prompt.negativePrompt ?? undefined;
|
|
367
367
|
|
|
368
368
|
const numberOfImages = options.model_options?.number_of_images ?? 1;
|
package/src/vertexai/models.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIModel, Completion,
|
|
1
|
+
import { AIModel, Completion, PromptSegment, ExecutionOptions, CompletionChunk } from "@llumiverse/core";
|
|
2
2
|
import { VertexAIDriver , trimModelName} from "./index.js";
|
|
3
3
|
import { GeminiModelDefinition } from "./models/gemini.js";
|
|
4
4
|
import { ClaudeModelDefinition } from "./models/claude.js";
|
|
@@ -7,7 +7,7 @@ import { LLamaModelDefinition } from "./models/llama.js";
|
|
|
7
7
|
export interface ModelDefinition<PromptT = any> {
|
|
8
8
|
model: AIModel;
|
|
9
9
|
versions?: string[]; // the versions of the model that are available. ex: ['001', '002']
|
|
10
|
-
createPrompt: (driver: VertexAIDriver, segments: PromptSegment[], options:
|
|
10
|
+
createPrompt: (driver: VertexAIDriver, segments: PromptSegment[], options: ExecutionOptions) => Promise<PromptT>;
|
|
11
11
|
requestTextCompletion: (driver: VertexAIDriver, prompt: PromptT, options: ExecutionOptions) => Promise<Completion>;
|
|
12
12
|
requestTextCompletionStream: (driver: VertexAIDriver, prompt: PromptT, options: ExecutionOptions) => Promise<AsyncIterable<CompletionChunk>>;
|
|
13
13
|
preValidationProcessing?(result: Completion, options: ExecutionOptions): { result: Completion, options: ExecutionOptions };
|
package/src/watsonx/index.ts
CHANGED
|
@@ -33,17 +33,17 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
|
|
|
33
33
|
if (options.model_options?._option_id !== "text-fallback") {
|
|
34
34
|
this.logger.warn("Invalid model options", {options: options.model_options });
|
|
35
35
|
}
|
|
36
|
-
options.model_options = options.model_options as TextFallbackOptions;
|
|
36
|
+
options.model_options = options.model_options as TextFallbackOptions | undefined;
|
|
37
37
|
|
|
38
38
|
const payload: WatsonxTextGenerationPayload = {
|
|
39
39
|
model_id: options.model,
|
|
40
40
|
input: prompt + "\n",
|
|
41
41
|
parameters: {
|
|
42
|
-
max_new_tokens: options.model_options
|
|
43
|
-
temperature: options.model_options
|
|
44
|
-
top_k: options.model_options
|
|
45
|
-
top_p: options.model_options
|
|
46
|
-
stop_sequences: options.model_options
|
|
42
|
+
max_new_tokens: options.model_options?.max_tokens,
|
|
43
|
+
temperature: options.model_options?.temperature,
|
|
44
|
+
top_k: options.model_options?.top_k,
|
|
45
|
+
top_p: options.model_options?.top_p,
|
|
46
|
+
stop_sequences: options.model_options?.stop_sequence,
|
|
47
47
|
},
|
|
48
48
|
project_id: this.projectId,
|
|
49
49
|
}
|
|
@@ -68,16 +68,16 @@ export class WatsonxDriver extends AbstractDriver<WatsonxDriverOptions, string>
|
|
|
68
68
|
if (options.model_options?._option_id !== "text-fallback") {
|
|
69
69
|
this.logger.warn("Invalid model options", {options: options.model_options });
|
|
70
70
|
}
|
|
71
|
-
options.model_options = options.model_options as TextFallbackOptions;
|
|
71
|
+
options.model_options = options.model_options as TextFallbackOptions | undefined;
|
|
72
72
|
const payload: WatsonxTextGenerationPayload = {
|
|
73
73
|
model_id: options.model,
|
|
74
74
|
input: prompt + "\n",
|
|
75
75
|
parameters: {
|
|
76
|
-
max_new_tokens: options.model_options
|
|
77
|
-
temperature: options.model_options
|
|
78
|
-
top_k: options.model_options
|
|
79
|
-
top_p: options.model_options
|
|
80
|
-
stop_sequences: options.model_options
|
|
76
|
+
max_new_tokens: options.model_options?.max_tokens,
|
|
77
|
+
temperature: options.model_options?.temperature,
|
|
78
|
+
top_k: options.model_options?.top_k,
|
|
79
|
+
top_p: options.model_options?.top_p,
|
|
80
|
+
stop_sequences: options.model_options?.stop_sequence,
|
|
81
81
|
},
|
|
82
82
|
project_id: this.projectId,
|
|
83
83
|
}
|