@llumiverse/drivers 1.0.0-dev.20260224.234313Z → 1.0.0-dev.20260327.110720Z
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 +86 -12
- package/lib/cjs/bedrock/converse.js.map +1 -1
- package/lib/cjs/bedrock/index.js +208 -1
- package/lib/cjs/bedrock/index.js.map +1 -1
- package/lib/cjs/groq/index.js +7 -4
- package/lib/cjs/groq/index.js.map +1 -1
- package/lib/cjs/openai/index.js +457 -26
- package/lib/cjs/openai/index.js.map +1 -1
- package/lib/cjs/openai/openai_compatible.js +1 -0
- package/lib/cjs/openai/openai_compatible.js.map +1 -1
- package/lib/cjs/vertexai/index.js +42 -0
- package/lib/cjs/vertexai/index.js.map +1 -1
- package/lib/cjs/vertexai/models/claude.js +230 -2
- package/lib/cjs/vertexai/models/claude.js.map +1 -1
- package/lib/cjs/vertexai/models/gemini.js +261 -41
- package/lib/cjs/vertexai/models/gemini.js.map +1 -1
- package/lib/cjs/vertexai/models.js +1 -1
- package/lib/cjs/vertexai/models.js.map +1 -1
- package/lib/esm/bedrock/converse.js +80 -6
- package/lib/esm/bedrock/converse.js.map +1 -1
- package/lib/esm/bedrock/index.js +207 -2
- package/lib/esm/bedrock/index.js.map +1 -1
- package/lib/esm/groq/index.js +7 -4
- package/lib/esm/groq/index.js.map +1 -1
- package/lib/esm/openai/index.js +456 -27
- package/lib/esm/openai/index.js.map +1 -1
- package/lib/esm/openai/openai_compatible.js +1 -0
- package/lib/esm/openai/openai_compatible.js.map +1 -1
- package/lib/esm/vertexai/index.js +43 -1
- package/lib/esm/vertexai/index.js.map +1 -1
- package/lib/esm/vertexai/models/claude.js +229 -3
- package/lib/esm/vertexai/models/claude.js.map +1 -1
- package/lib/esm/vertexai/models/gemini.js +262 -43
- package/lib/esm/vertexai/models/gemini.js.map +1 -1
- package/lib/esm/vertexai/models.js +1 -1
- package/lib/esm/vertexai/models.js.map +1 -1
- package/lib/types/bedrock/converse.d.ts +1 -2
- package/lib/types/bedrock/converse.d.ts.map +1 -1
- package/lib/types/bedrock/index.d.ts +54 -2
- package/lib/types/bedrock/index.d.ts.map +1 -1
- package/lib/types/openai/index.d.ts +96 -1
- package/lib/types/openai/index.d.ts.map +1 -1
- package/lib/types/openai/openai_compatible.d.ts +5 -0
- package/lib/types/openai/openai_compatible.d.ts.map +1 -1
- package/lib/types/openai/openai_format.d.ts +1 -1
- package/lib/types/vertexai/index.d.ts +11 -1
- package/lib/types/vertexai/index.d.ts.map +1 -1
- package/lib/types/vertexai/models/claude.d.ts +64 -1
- package/lib/types/vertexai/models/claude.d.ts.map +1 -1
- package/lib/types/vertexai/models/gemini.d.ts +61 -1
- package/lib/types/vertexai/models/gemini.d.ts.map +1 -1
- package/lib/types/vertexai/models.d.ts +6 -1
- package/lib/types/vertexai/models.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/bedrock/converse.ts +85 -10
- package/src/bedrock/error-handling.test.ts +352 -0
- package/src/bedrock/index.ts +228 -3
- package/src/groq/index.ts +9 -4
- package/src/openai/error-handling.test.ts +567 -0
- package/src/openai/index.ts +505 -29
- package/src/openai/openai_compatible.ts +7 -0
- package/src/openai/openai_format.ts +1 -1
- package/src/vertexai/index.ts +56 -5
- package/src/vertexai/models/claude-error-handling.test.ts +432 -0
- package/src/vertexai/models/claude.ts +273 -7
- package/src/vertexai/models/gemini-error-handling.test.ts +353 -0
- package/src/vertexai/models/gemini.ts +304 -48
- package/src/vertexai/models.ts +7 -2
package/src/bedrock/index.ts
CHANGED
|
@@ -20,12 +20,14 @@ import {
|
|
|
20
20
|
getMaxTokensLimitBedrock,
|
|
21
21
|
getModelCapabilities,
|
|
22
22
|
incrementConversationTurn,
|
|
23
|
+
LlumiverseError, LlumiverseErrorContext,
|
|
23
24
|
modelModalitiesToArray,
|
|
24
25
|
ModelOptions,
|
|
25
26
|
NovaCanvasOptions,
|
|
26
27
|
PromptSegment,
|
|
27
28
|
StatelessExecutionOptions,
|
|
28
29
|
stripBinaryFromConversation,
|
|
30
|
+
stripHeartbeatsFromConversation,
|
|
29
31
|
TextFallbackOptions, ToolDefinition, ToolUse, TrainingJob, TrainingJobStatus, TrainingOptions,
|
|
30
32
|
truncateLargeTextInConversation
|
|
31
33
|
} from "@llumiverse/core";
|
|
@@ -84,9 +86,10 @@ export interface BedrockDriverOptions extends DriverOptions {
|
|
|
84
86
|
training_role_arn?: string;
|
|
85
87
|
|
|
86
88
|
/**
|
|
87
|
-
* The credentials to use to access AWS
|
|
89
|
+
* The credentials to use to access AWS (IAM access key + secret)
|
|
88
90
|
*/
|
|
89
91
|
credentials?: AwsCredentialIdentity | Provider<AwsCredentialIdentity>;
|
|
92
|
+
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
//Used to get a max_token value when not specified in the model options. Claude requires it to be set.
|
|
@@ -190,6 +193,143 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
|
|
|
190
193
|
return await formatConversePrompt(segments, opts);
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Format AWS Bedrock errors into LlumiverseError with proper status codes and retryability.
|
|
198
|
+
*
|
|
199
|
+
* AWS SDK errors provide:
|
|
200
|
+
* - error.name: The exception type (e.g., "ThrottlingException")
|
|
201
|
+
* - error.$metadata.httpStatusCode: The HTTP status code
|
|
202
|
+
* - error.$metadata.requestId: The AWS request ID for tracking
|
|
203
|
+
* - error.$fault: "client" or "server" indicating error category
|
|
204
|
+
*
|
|
205
|
+
* @param error - The AWS SDK error
|
|
206
|
+
* @param context - Context about where the error occurred
|
|
207
|
+
* @returns A standardized LlumiverseError
|
|
208
|
+
*/
|
|
209
|
+
public formatLlumiverseError(
|
|
210
|
+
error: unknown,
|
|
211
|
+
context: LlumiverseErrorContext
|
|
212
|
+
): LlumiverseError {
|
|
213
|
+
// Check if it's an AWS SDK error with $metadata
|
|
214
|
+
const awsError = error as any;
|
|
215
|
+
const hasMetadata = awsError?.$metadata !== undefined;
|
|
216
|
+
|
|
217
|
+
if (!hasMetadata) {
|
|
218
|
+
// Not an AWS SDK error, use default handling
|
|
219
|
+
return super.formatLlumiverseError(error, context);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Extract AWS-specific fields
|
|
223
|
+
const errorName = awsError.name || 'UnknownError';
|
|
224
|
+
const httpStatusCode = awsError.$metadata?.httpStatusCode;
|
|
225
|
+
const requestId = awsError.$metadata?.requestId;
|
|
226
|
+
const fault = awsError.$fault; // "client" or "server"
|
|
227
|
+
|
|
228
|
+
// Extract error message - handle both Error instances and plain objects
|
|
229
|
+
let message: string;
|
|
230
|
+
if (error instanceof Error) {
|
|
231
|
+
message = error.message;
|
|
232
|
+
} else if (typeof awsError.message === 'string') {
|
|
233
|
+
message = awsError.message;
|
|
234
|
+
} else {
|
|
235
|
+
message = String(error);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Build user-facing message with error name and status code
|
|
239
|
+
let userMessage = message;
|
|
240
|
+
|
|
241
|
+
// Include status code in message if available (for end-user visibility)
|
|
242
|
+
if (httpStatusCode) {
|
|
243
|
+
userMessage = `[${httpStatusCode}] ${userMessage}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Prefix with error name if it's meaningful (not just "Error")
|
|
247
|
+
if (errorName && errorName !== 'Error' && errorName !== 'UnknownError') {
|
|
248
|
+
userMessage = `${errorName}: ${userMessage}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Add request ID if available (useful for AWS support)
|
|
252
|
+
if (requestId) {
|
|
253
|
+
userMessage += ` (Request ID: ${requestId})`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Determine retryability based on AWS error types
|
|
257
|
+
const retryable = this.isBedrockErrorRetryable(errorName, httpStatusCode, fault);
|
|
258
|
+
|
|
259
|
+
return new LlumiverseError(
|
|
260
|
+
`[${this.provider}] ${userMessage}`,
|
|
261
|
+
retryable,
|
|
262
|
+
context,
|
|
263
|
+
error,
|
|
264
|
+
httpStatusCode, // Only set code if we have numeric status code
|
|
265
|
+
errorName // Preserve AWS error name
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Determine if a Bedrock error is retryable based on error type and status.
|
|
271
|
+
*
|
|
272
|
+
* Retryable errors:
|
|
273
|
+
* - ThrottlingException: Rate limit exceeded, retry with backoff
|
|
274
|
+
* - ServiceUnavailableException: Service temporarily down
|
|
275
|
+
* - InternalServerException: Server-side error
|
|
276
|
+
* - ServiceQuotaExceededException: Quota exhausted, may recover
|
|
277
|
+
* - 5xx status codes: Server errors
|
|
278
|
+
* - 429, 408 status codes: Rate limit, timeout
|
|
279
|
+
*
|
|
280
|
+
* Non-retryable errors:
|
|
281
|
+
* - ValidationException: Invalid request parameters
|
|
282
|
+
* - AccessDeniedException: Authentication/authorization failure
|
|
283
|
+
* - ResourceNotFoundException: Resource doesn't exist
|
|
284
|
+
* - ConflictException: Resource state conflict
|
|
285
|
+
* - ResourceInUseException: Resource locked by another operation
|
|
286
|
+
* - 4xx status codes (except 429, 408): Client errors
|
|
287
|
+
*
|
|
288
|
+
* @param errorName - The AWS error name (e.g., "ThrottlingException")
|
|
289
|
+
* @param httpStatusCode - The HTTP status code if available
|
|
290
|
+
* @param fault - The fault type ("client" or "server")
|
|
291
|
+
* @returns True if retryable, false if not retryable, undefined if unknown
|
|
292
|
+
*/
|
|
293
|
+
private isBedrockErrorRetryable(
|
|
294
|
+
errorName: string,
|
|
295
|
+
httpStatusCode: number | undefined,
|
|
296
|
+
fault: string | undefined
|
|
297
|
+
): boolean | undefined {
|
|
298
|
+
// Check specific AWS error types first
|
|
299
|
+
switch (errorName) {
|
|
300
|
+
// Retryable errors
|
|
301
|
+
case 'ThrottlingException':
|
|
302
|
+
case 'ServiceUnavailableException':
|
|
303
|
+
case 'InternalServerException':
|
|
304
|
+
case 'ServiceQuotaExceededException':
|
|
305
|
+
return true;
|
|
306
|
+
|
|
307
|
+
// Non-retryable errors
|
|
308
|
+
case 'ValidationException':
|
|
309
|
+
case 'AccessDeniedException':
|
|
310
|
+
case 'ResourceNotFoundException':
|
|
311
|
+
case 'ConflictException':
|
|
312
|
+
case 'ResourceInUseException':
|
|
313
|
+
case 'TooManyTagsException':
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// If we have HTTP status code, use it
|
|
318
|
+
if (httpStatusCode !== undefined) {
|
|
319
|
+
if (httpStatusCode === 429 || httpStatusCode === 408) return true; // Rate limit, timeout
|
|
320
|
+
if (httpStatusCode === 529) return true; // Overloaded
|
|
321
|
+
if (httpStatusCode >= 500 && httpStatusCode < 600) return true; // Server errors
|
|
322
|
+
if (httpStatusCode >= 400 && httpStatusCode < 500) return false; // Client errors
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Fall back to fault type
|
|
326
|
+
if (fault === 'server') return true;
|
|
327
|
+
if (fault === 'client') return false;
|
|
328
|
+
|
|
329
|
+
// Unknown error type - let consumer decide retry strategy
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
|
|
193
333
|
getExtractedExecution(result: ConverseResponse, _prompt?: BedrockPrompt, options?: ExecutionOptions): CompletionChunkObject {
|
|
194
334
|
let resultText = "";
|
|
195
335
|
let reasoning = "";
|
|
@@ -483,6 +623,10 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
|
|
|
483
623
|
};
|
|
484
624
|
let processedConversation = stripBinaryFromConversation(conversation, stripOptions);
|
|
485
625
|
processedConversation = truncateLargeTextInConversation(processedConversation, stripOptions);
|
|
626
|
+
processedConversation = stripHeartbeatsFromConversation(processedConversation, {
|
|
627
|
+
keepForTurns: options.stripHeartbeatsAfterTurns ?? 1,
|
|
628
|
+
currentTurn,
|
|
629
|
+
});
|
|
486
630
|
|
|
487
631
|
return processedConversation as ConverseRequest;
|
|
488
632
|
}
|
|
@@ -551,6 +695,12 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
|
|
|
551
695
|
// Truncate large text content if configured
|
|
552
696
|
processedConversation = truncateLargeTextInConversation(processedConversation, stripOptions);
|
|
553
697
|
|
|
698
|
+
// Strip old heartbeat status messages
|
|
699
|
+
processedConversation = stripHeartbeatsFromConversation(processedConversation, {
|
|
700
|
+
keepForTurns: options.stripHeartbeatsAfterTurns ?? 1,
|
|
701
|
+
currentTurn,
|
|
702
|
+
});
|
|
703
|
+
|
|
554
704
|
const completion = {
|
|
555
705
|
...this.getExtractedExecution(res, conversePrompt, options),
|
|
556
706
|
original_response: options.include_original_response ? res : undefined,
|
|
@@ -852,6 +1002,12 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
|
|
|
852
1002
|
request.toolConfig = {
|
|
853
1003
|
tools: tool_defs,
|
|
854
1004
|
}
|
|
1005
|
+
} else if (request.messages && messagesContainToolBlocks(request.messages)) {
|
|
1006
|
+
// Bedrock requires toolConfig when conversation contains toolUse/toolResult blocks.
|
|
1007
|
+
// When no tools are provided (e.g. checkpoint summary calls), convert tool blocks
|
|
1008
|
+
// to text representations so the conversation data is preserved while satisfying
|
|
1009
|
+
// Bedrock's API requirements without making tools callable.
|
|
1010
|
+
request.messages = convertToolBlocksToText(request.messages);
|
|
855
1011
|
}
|
|
856
1012
|
|
|
857
1013
|
return request;
|
|
@@ -1195,7 +1351,7 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
|
|
|
1195
1351
|
const executor = this.getExecutor();
|
|
1196
1352
|
|
|
1197
1353
|
// Prepare the request payload for TwelveLabs Marengo
|
|
1198
|
-
|
|
1354
|
+
const invokeBody: TwelvelabsMarengoRequest = {
|
|
1199
1355
|
inputType: "text"
|
|
1200
1356
|
};
|
|
1201
1357
|
|
|
@@ -1285,6 +1441,75 @@ function getToolDefinition(tool: ToolDefinition): Tool.ToolSpecMember {
|
|
|
1285
1441
|
}
|
|
1286
1442
|
}
|
|
1287
1443
|
|
|
1444
|
+
/**
|
|
1445
|
+
* Checks whether any message contains toolUse or toolResult content blocks.
|
|
1446
|
+
*/
|
|
1447
|
+
export function messagesContainToolBlocks(messages: Message[]): boolean {
|
|
1448
|
+
for (const msg of messages) {
|
|
1449
|
+
if (!msg.content) continue;
|
|
1450
|
+
for (const block of msg.content) {
|
|
1451
|
+
if ((block as ContentBlock.ToolUseMember).toolUse ||
|
|
1452
|
+
(block as ContentBlock.ToolResultMember).toolResult) {
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Converts toolUse and toolResult content blocks to text representations.
|
|
1462
|
+
* This preserves the tool call information in the conversation while removing
|
|
1463
|
+
* the structured tool blocks that require Bedrock's toolConfig to be set.
|
|
1464
|
+
*
|
|
1465
|
+
* Used when no tools are provided (e.g. checkpoint summary calls) but the
|
|
1466
|
+
* conversation history contains tool interactions from prior turns.
|
|
1467
|
+
*/
|
|
1468
|
+
export function convertToolBlocksToText(messages: Message[]): Message[] {
|
|
1469
|
+
return messages.map(msg => {
|
|
1470
|
+
if (!msg.content) return msg;
|
|
1471
|
+
let hasToolBlocks = false;
|
|
1472
|
+
for (const block of msg.content) {
|
|
1473
|
+
if ((block as ContentBlock.ToolUseMember).toolUse ||
|
|
1474
|
+
(block as ContentBlock.ToolResultMember).toolResult) {
|
|
1475
|
+
hasToolBlocks = true;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (!hasToolBlocks) return msg;
|
|
1480
|
+
|
|
1481
|
+
const newContent: ContentBlock[] = [];
|
|
1482
|
+
for (const block of msg.content) {
|
|
1483
|
+
const toolUse = (block as ContentBlock.ToolUseMember).toolUse;
|
|
1484
|
+
const toolResult = (block as ContentBlock.ToolResultMember).toolResult;
|
|
1485
|
+
if (toolUse) {
|
|
1486
|
+
const inputStr = toolUse.input ? JSON.stringify(toolUse.input) : '';
|
|
1487
|
+
const truncatedInput = inputStr.length > 500 ? inputStr.substring(0, 500) + '...' : inputStr;
|
|
1488
|
+
newContent.push({
|
|
1489
|
+
text: `[Tool call: ${toolUse.name}(${truncatedInput})]`,
|
|
1490
|
+
} as ContentBlock.TextMember);
|
|
1491
|
+
} else if (toolResult) {
|
|
1492
|
+
const resultTexts: string[] = [];
|
|
1493
|
+
if (toolResult.content) {
|
|
1494
|
+
for (const c of toolResult.content) {
|
|
1495
|
+
if ((c as any).text) {
|
|
1496
|
+
const text = (c as any).text as string;
|
|
1497
|
+
resultTexts.push(text.length > 500 ? text.substring(0, 500) + '...' : text);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
const resultStr = resultTexts.length > 0 ? resultTexts.join('\n') : 'No text content';
|
|
1502
|
+
newContent.push({
|
|
1503
|
+
text: `[Tool result: ${resultStr}]`,
|
|
1504
|
+
} as ContentBlock.TextMember);
|
|
1505
|
+
} else {
|
|
1506
|
+
newContent.push(block);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
return { ...msg, content: newContent };
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1288
1513
|
/**
|
|
1289
1514
|
* Recursively removes undefined values from an object.
|
|
1290
1515
|
* AWS Bedrock's additionalModelRequestFields must be valid JSON, and undefined is not valid JSON.
|
|
@@ -1447,4 +1672,4 @@ function formatAmazonModalities(modalities: ModelModality[]): string[] {
|
|
|
1447
1672
|
}
|
|
1448
1673
|
}
|
|
1449
1674
|
return standardizedModalities;
|
|
1450
|
-
}
|
|
1675
|
+
}
|
package/src/groq/index.ts
CHANGED
|
@@ -299,12 +299,17 @@ function convertResponseItemsToGroqMessages(items: ResponseInputItem[]): ChatCom
|
|
|
299
299
|
} else if (part.type === 'input_image') {
|
|
300
300
|
const imgPart = part as OpenAI.Responses.ResponseInputImage;
|
|
301
301
|
if (imgPart.image_url) {
|
|
302
|
+
const image_url: { url: string; detail?: 'auto' | 'low' | 'high' } = {
|
|
303
|
+
url: imgPart.image_url
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
if (imgPart.detail) {
|
|
307
|
+
image_url.detail = imgPart.detail as 'auto' | 'low' | 'high';
|
|
308
|
+
}
|
|
309
|
+
|
|
302
310
|
parts.push({
|
|
303
311
|
type: 'image_url',
|
|
304
|
-
image_url
|
|
305
|
-
url: imgPart.image_url,
|
|
306
|
-
...(imgPart.detail && { detail: imgPart.detail })
|
|
307
|
-
}
|
|
312
|
+
image_url
|
|
308
313
|
});
|
|
309
314
|
}
|
|
310
315
|
}
|