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