@llumiverse/drivers 1.0.0 → 1.1.0-dev.20260427.054520Z

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.
Files changed (38) hide show
  1. package/lib/cjs/bedrock/index.js +90 -10
  2. package/lib/cjs/bedrock/index.js.map +1 -1
  3. package/lib/cjs/openai/index.js +2 -0
  4. package/lib/cjs/openai/index.js.map +1 -1
  5. package/lib/cjs/vertexai/index.js +31 -22
  6. package/lib/cjs/vertexai/index.js.map +1 -1
  7. package/lib/cjs/vertexai/models/claude.js +99 -26
  8. package/lib/cjs/vertexai/models/claude.js.map +1 -1
  9. package/lib/cjs/vertexai/models/gemini.js +35 -335
  10. package/lib/cjs/vertexai/models/gemini.js.map +1 -1
  11. package/lib/esm/bedrock/index.js +90 -10
  12. package/lib/esm/bedrock/index.js.map +1 -1
  13. package/lib/esm/openai/index.js +2 -0
  14. package/lib/esm/openai/index.js.map +1 -1
  15. package/lib/esm/vertexai/index.js +31 -22
  16. package/lib/esm/vertexai/index.js.map +1 -1
  17. package/lib/esm/vertexai/models/claude.js +99 -28
  18. package/lib/esm/vertexai/models/claude.js.map +1 -1
  19. package/lib/esm/vertexai/models/gemini.js +36 -336
  20. package/lib/esm/vertexai/models/gemini.js.map +1 -1
  21. package/lib/types/bedrock/index.d.ts +5 -2
  22. package/lib/types/bedrock/index.d.ts.map +1 -1
  23. package/lib/types/openai/index.d.ts.map +1 -1
  24. package/lib/types/vertexai/index.d.ts +4 -1
  25. package/lib/types/vertexai/index.d.ts.map +1 -1
  26. package/lib/types/vertexai/models/claude.d.ts +16 -0
  27. package/lib/types/vertexai/models/claude.d.ts.map +1 -1
  28. package/lib/types/vertexai/models/gemini.d.ts +4 -8
  29. package/lib/types/vertexai/models/gemini.d.ts.map +1 -1
  30. package/package.json +8 -8
  31. package/src/bedrock/index.ts +104 -12
  32. package/src/bedrock/streaming-tool-use.test.ts +250 -0
  33. package/src/openai/index.ts +2 -0
  34. package/src/vertexai/index.ts +32 -22
  35. package/src/vertexai/models/claude-streaming-spacing.test.ts +174 -0
  36. package/src/vertexai/models/claude.ts +120 -29
  37. package/src/vertexai/models/gemini-conversation-mutation.test.ts +174 -0
  38. package/src/vertexai/models/gemini.ts +48 -391
@@ -23,6 +23,8 @@ export declare class VertexAIDriver extends AbstractDriver<VertexAIDriverOptions
23
23
  anthropicClient: AnthropicVertex | undefined;
24
24
  fetchClient: FetchClient | undefined;
25
25
  googleGenAI: GoogleGenAI | undefined;
26
+ googleGenAIRegion: string | undefined;
27
+ googleGenAIFlex: boolean | undefined;
26
28
  llamaClient: FetchClient & {
27
29
  region?: string;
28
30
  } | undefined;
@@ -32,7 +34,8 @@ export declare class VertexAIDriver extends AbstractDriver<VertexAIDriverOptions
32
34
  private authClientPromise;
33
35
  constructor(options: VertexAIDriverOptions);
34
36
  private getAuthClient;
35
- getGoogleGenAIClient(region?: string): GoogleGenAI;
37
+ getGoogleGenAIClient(region?: string, flex?: boolean): GoogleGenAI;
38
+ private buildGoogleGenAIClient;
36
39
  getFetchClient(): FetchClient;
37
40
  getLLamaClient(region?: string): FetchClient;
38
41
  getAnthropicClient(region?: string): Promise<AnthropicVertex>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vertexai/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vertexai/index.ts"],"names":[],"mappings":"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"}
@@ -77,6 +77,22 @@ export declare class ClaudeModelDefinition implements ModelDefinition<ClaudeProm
77
77
  * we need to merge them before sending to the API.
78
78
  */
79
79
  export declare function mergeConsecutiveUserMessages(messages: MessageParam[]): MessageParam[];
80
+ /**
81
+ * Update the conversation messages
82
+ * @param prompt
83
+ * @param response
84
+ * @returns
85
+ */
86
+ export declare function updateConversation(conversation: ClaudePrompt | undefined | null, prompt: ClaudePrompt): ClaudePrompt;
87
+ /**
88
+ * Sanitize messages by removing empty text blocks.
89
+ * Claude API rejects messages with empty text content blocks ("text content blocks must be non-empty").
90
+ * This handles cases where streaming was interrupted and left empty text blocks.
91
+ *
92
+ * - Filters out empty text blocks from each message's content
93
+ * - Removes messages entirely if they have no content after filtering
94
+ */
95
+ export declare function sanitizeMessages(messages: MessageParam[]): MessageParam[];
80
96
  /**
81
97
  * Fix orphaned tool_use blocks in the conversation.
82
98
  * @exported for testing
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../../src/vertexai/models/claude.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAmE,YAAY,EAAE,cAAc,EAAwB,MAAM,sCAAsC,CAAC;AAGzL,OAAO,EACH,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,gBAAgB,EAK5D,eAAe,EAAE,sBAAsB,EAE3B,aAAa,EAGzB,OAAO,EAGV,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIpD,CAAA;AAED,eAAO,MAAM,2BAA2B,UAGvC,CAAC;AAEF,UAAU,YAAY;IAClB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAWD,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,GAAG,SAAS,CAc3E;AA8FD,qBAAa,qBAAsB,YAAW,eAAe,CAAC,YAAY,CAAC;IAEvE,KAAK,EAAE,OAAO,CAAA;gBAEF,OAAO,EAAE,MAAM;IAUrB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAuGlH,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4DnH,2BAA2B,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAiIzJ;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,qBAAqB,CACjB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,GAChC,eAAe;IA8DlB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,sBAAsB;CAuCjC;AAYD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA6CrF;AA6DD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA2E3E;AAgED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAUjF;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA8CtF"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../../src/vertexai/models/claude.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAmE,YAAY,EAAE,cAAc,EAAwB,MAAM,sCAAsC,CAAC;AAGzL,OAAO,EACH,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,gBAAgB,EAK5D,eAAe,EAAE,sBAAsB,EAE3B,aAAa,EAGzB,OAAO,EAGV,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIpD,CAAA;AAED,eAAO,MAAM,2BAA2B,UAGvC,CAAC;AAEF,UAAU,YAAY;IAClB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AA+BD,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,GAAG,SAAS,CAc3E;AA8FD,qBAAa,qBAAsB,YAAW,eAAe,CAAC,YAAY,CAAC;IAEvE,KAAK,EAAE,OAAO,CAAA;gBAEF,OAAO,EAAE,MAAM;IAUrB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAuGlH,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAwDnH,2BAA2B,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IA+HzJ;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,qBAAqB,CACjB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,GAChC,eAAe;IA8DlB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,sBAAsB;CAuCjC;AAYD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA6CrF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAgBpH;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA+BzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA2E3E;AAuID;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAUjF;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA8CtF"}
@@ -1,15 +1,11 @@
1
- import { Content, GenerateContentResponseUsageMetadata } from "@google/genai";
2
- import { AIModel, Completion, CompletionChunkObject, ExecutionOptions, ExecutionTokenUsage, LlumiverseError, LlumiverseErrorContext, PromptSegment } from "@llumiverse/core";
3
- import { GenerateContentPrompt, VertexAIDriver } from "../index.js";
4
- import { ModelDefinition } from "../models.js";
1
+ import { type Content, type GenerateContentResponseUsageMetadata } from "@google/genai";
2
+ import { type AIModel, type Completion, type CompletionChunkObject, type ExecutionOptions, type ExecutionTokenUsage, LlumiverseError, type LlumiverseErrorContext, type PromptSegment } from "@llumiverse/core";
3
+ import type { GenerateContentPrompt, VertexAIDriver } from "../index.js";
4
+ import type { ModelDefinition } from "../models.js";
5
5
  export declare function mergeConsecutiveRole(contents: Content[] | undefined): Content[];
6
6
  export declare class GeminiModelDefinition implements ModelDefinition<GenerateContentPrompt> {
7
7
  model: AIModel;
8
8
  constructor(modelId: string);
9
- preValidationProcessing(result: Completion, options: ExecutionOptions): {
10
- result: Completion;
11
- options: ExecutionOptions;
12
- };
13
9
  createPrompt(_driver: VertexAIDriver, segments: PromptSegment[], options: ExecutionOptions): Promise<GenerateContentPrompt>;
14
10
  usageMetadataToTokenUsage(usageMetadata: GenerateContentResponseUsageMetadata | undefined): ExecutionTokenUsage;
15
11
  requestTextCompletion(driver: VertexAIDriver, prompt: GenerateContentPrompt, options: ExecutionOptions): Promise<Completion>;
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../../src/vertexai/models/gemini.ts"],"names":[],"mappings":"AACA,OAAO,EACH,OAAO,EACP,oCAAoC,EAMvC,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAoB,gBAAgB,EAC9E,mBAAmB,EAKK,eAAe,EAAE,sBAAsB,EAC/D,aAAa,EAOhB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AA4e/C,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CA2B/E;AAgED,qBAAa,qBAAsB,YAAW,eAAe,CAAC,qBAAqB,CAAC;IAEhF,KAAK,EAAE,OAAO,CAAA;gBAEF,OAAO,EAAE,MAAM;IAU3B,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,GAAG;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAA;KAAE;IAyBnH,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA+HjI,yBAAyB,CAAC,aAAa,EAAE,oCAAoC,GAAG,SAAS,GAAG,mBAAmB;IA0BzG,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8G5H,2BAA2B,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAiFlK;;;;;;;;;;;;;;;;;;;OAmBG;IACH,qBAAqB,CACjB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,GAChC,eAAe;IAwClB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAkB3B;AAGD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAsB/E"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../../src/vertexai/models/gemini.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,oCAAoC,EAM5C,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,qBAAqB,EAAyB,KAAK,gBAAgB,EACvG,KAAK,mBAAmB,EAKP,eAAe,EAAE,KAAK,sBAAsB,EAC7D,KAAK,aAAa,EAOrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AA2KpD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CA2B/E;AAuED,qBAAa,qBAAsB,YAAW,eAAe,CAAC,qBAAqB,CAAC;IAEhF,KAAK,EAAE,OAAO,CAAA;gBAEF,OAAO,EAAE,MAAM;IAUrB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA+HjI,yBAAyB,CAAC,aAAa,EAAE,oCAAoC,GAAG,SAAS,GAAG,mBAAmB;IA+BzG,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4G5H,2BAA2B,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAkFlK;;;;;;;;;;;;;;;;;;;OAmBG;IACH,qBAAqB,CACjB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,GAChC,eAAe;IAwClB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAkB3B;AAGD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAsB/E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llumiverse/drivers",
3
- "version": "1.0.0",
3
+ "version": "1.1.0-dev.20260427.054520Z",
4
4
  "type": "module",
5
5
  "description": "LLM driver implementations. Currently supported are: openai, huggingface, bedrock, replicate.",
6
6
  "files": [
@@ -44,11 +44,11 @@
44
44
  "dotenv": "^16.6.1",
45
45
  "rimraf": "^6.1.2",
46
46
  "ts-dual-module": "^0.6.3",
47
- "typescript": "^5.9.3",
47
+ "typescript": "^6.0.2",
48
48
  "vitest": "^4.0.18"
49
49
  },
50
50
  "dependencies": {
51
- "@anthropic-ai/sdk": "^0.78.0",
51
+ "@anthropic-ai/sdk": "^0.85.0",
52
52
  "@anthropic-ai/vertex-sdk": "^0.14.4",
53
53
  "@aws-sdk/client-bedrock": "^3.985.0",
54
54
  "@aws-sdk/client-bedrock-runtime": "^3.985.0",
@@ -63,18 +63,18 @@
63
63
  "@azure/identity": "^4.13.0",
64
64
  "@azure/openai": "2.0.0",
65
65
  "@google-cloud/aiplatform": "^6.5.0",
66
- "@google/genai": "^1.46.0",
66
+ "@google/genai": "^1.49.0",
67
67
  "@huggingface/inference": "4.13.11",
68
68
  "@vertesia/api-fetch-client": "^0.82.4",
69
69
  "eventsource": "^4.1.0",
70
- "google-auth-library": "^10.5.0",
70
+ "google-auth-library": "^10.6.2",
71
71
  "groq-sdk": "^0.37.0",
72
72
  "mnemonist": "^0.40.3",
73
73
  "node-web-stream-adapters": "^0.2.1",
74
- "openai": "^6.22.0",
74
+ "openai": "^6.33.0",
75
75
  "replicate": "^1.4.0",
76
- "@llumiverse/common": "1.0.0",
77
- "@llumiverse/core": "1.0.0"
76
+ "@llumiverse/common": "1.1.0-dev.20260427.054520Z",
77
+ "@llumiverse/core": "1.1.0-dev.20260427.054520Z"
78
78
  },
79
79
  "ts_dual_module": {
80
80
  "outDir": "lib"
@@ -86,9 +86,10 @@ export interface BedrockDriverOptions extends DriverOptions {
86
86
  training_role_arn?: string;
87
87
 
88
88
  /**
89
- * The credentials to use to access AWS
89
+ * The credentials to use to access AWS (IAM access key + secret)
90
90
  */
91
91
  credentials?: AwsCredentialIdentity | Provider<AwsCredentialIdentity>;
92
+
92
93
  }
93
94
 
94
95
  //Used to get a max_token value when not specified in the model options. Claude requires it to be set.
@@ -144,6 +145,9 @@ function isClaudeVersionGTE(modelString: string, targetMajor: number, targetMino
144
145
 
145
146
  export type BedrockPrompt = NovaMessagesPrompt | ConverseRequest | TwelvelabsPegasusRequest;
146
147
 
148
+ type BedrockSystemBlock = NonNullable<ConverseRequest['system']>[number];
149
+ type BedrockToolEntry = NonNullable<NonNullable<ConverseRequest['toolConfig']>['tools']>[number];
150
+
147
151
  export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockPrompt> {
148
152
 
149
153
  static PROVIDER = "bedrock";
@@ -372,9 +376,16 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
372
376
  const completionResult: CompletionChunkObject = {
373
377
  result: reasoning + resultText ? [{ type: "text", value: reasoning + resultText }] : [],
374
378
  token_usage: {
375
- prompt: result.usage?.inputTokens,
379
+ // Bedrock's inputTokens already excludes cache-read tokens,
380
+ // so prompt_new is inputTokens directly (no subtraction needed).
381
+ // prompt is the total including cached + cache_write for consistency
382
+ // with the Vertex Claude driver.
383
+ prompt_new: result.usage?.inputTokens,
384
+ prompt: result.usage ? (result.usage.inputTokens ?? 0) + (result.usage.cacheReadInputTokens ?? 0) + (result.usage.cacheWriteInputTokens ?? 0) : undefined,
376
385
  result: result.usage?.outputTokens,
377
386
  total: result.usage?.totalTokens,
387
+ prompt_cached: result.usage?.cacheReadInputTokens ?? undefined,
388
+ prompt_cache_write: result.usage?.cacheWriteInputTokens ?? undefined,
378
389
  },
379
390
  finish_reason: converseFinishReason(result.stopReason),
380
391
  };
@@ -382,20 +393,29 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
382
393
  return completionResult;
383
394
  };
384
395
 
385
- getExtractedStream(result: ConverseStreamOutput, _prompt?: BedrockPrompt, options?: ExecutionOptions): CompletionChunkObject {
396
+ getExtractedStream(result: ConverseStreamOutput, _prompt?: BedrockPrompt, options?: ExecutionOptions, streamingToolBlocks?: Map<number, { id: string; name: string }>): CompletionChunkObject {
386
397
  let output: string = "";
387
398
  let reasoning: string = "";
388
399
  let stop_reason = "";
389
400
  let token_usage: ExecutionTokenUsage | undefined;
401
+ let tool_use: ToolUse[] | undefined;
390
402
 
391
403
  // Check if we should include thoughts (always true for reasoning-only models like DeepSeek R1)
392
404
  const isReasoningModel = options?.model?.includes('deepseek') && options?.model?.includes('r1');
393
405
  const shouldIncludeThoughts = isReasoningModel || (options && (options.model_options as BedrockClaudeOptions)?.include_thoughts);
394
406
 
395
- // Handle content block start events (for reasoning blocks)
407
+ // Handle content block start events (for reasoning blocks and tool use)
396
408
  if (result.contentBlockStart) {
397
- // Handle redacted content at block start
398
- if (result.contentBlockStart.start && 'reasoningContent' in result.contentBlockStart.start && shouldIncludeThoughts) {
409
+ if (result.contentBlockStart.start && 'toolUse' in result.contentBlockStart.start && result.contentBlockStart.start.toolUse) {
410
+ // Register new tool call block and emit an initial chunk so the accumulator can track it by id
411
+ const toolUseStart = result.contentBlockStart.start.toolUse;
412
+ const blockIndex = result.contentBlockStart.contentBlockIndex ?? -1;
413
+ const id = toolUseStart.toolUseId ?? '';
414
+ const name = toolUseStart.name ?? '';
415
+ streamingToolBlocks?.set(blockIndex, { id, name });
416
+ tool_use = [{ id, tool_name: name, tool_input: '' as any }];
417
+ } else if (result.contentBlockStart.start && 'reasoningContent' in result.contentBlockStart.start && shouldIncludeThoughts) {
418
+ // Handle redacted content at block start
399
419
  const reasoningStart = result.contentBlockStart.start as any;
400
420
  if (reasoningStart.reasoningContent?.redactedContent) {
401
421
  const redactedData = new TextDecoder().decode(reasoningStart.reasoningContent.redactedContent);
@@ -404,10 +424,17 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
404
424
  }
405
425
  }
406
426
 
407
- // Handle content block deltas (text and reasoning)
427
+ // Handle content block deltas (text, reasoning, and tool use)
408
428
  if (result.contentBlockDelta) {
409
429
  const delta = result.contentBlockDelta.delta;
410
- if (delta?.text) {
430
+ if (delta?.toolUse) {
431
+ // Emit tool input chunk; the accumulator in DefaultCompletionStream concatenates these strings
432
+ const blockIndex = result.contentBlockDelta.contentBlockIndex ?? -1;
433
+ const toolBlock = streamingToolBlocks?.get(blockIndex);
434
+ if (toolBlock && delta.toolUse.input !== undefined) {
435
+ tool_use = [{ id: toolBlock.id, tool_name: '', tool_input: delta.toolUse.input as any }];
436
+ }
437
+ } else if (delta?.text) {
411
438
  output = delta.text;
412
439
  } else if (delta?.reasoningContent && shouldIncludeThoughts) {
413
440
  if (delta.reasoningContent.text) {
@@ -432,7 +459,9 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
432
459
 
433
460
  // Handle content block stop events
434
461
  if (result.contentBlockStop) {
435
- // Content block ended - could be end of reasoning or text block
462
+ // Clean up tool block tracking entry
463
+ const blockIndex = result.contentBlockStop.contentBlockIndex ?? -1;
464
+ streamingToolBlocks?.delete(blockIndex);
436
465
  // Add minimal spacing for reasoning blocks if not already present
437
466
  if (reasoning && !reasoning.endsWith('\n\n') && shouldIncludeThoughts) {
438
467
  reasoning += '\n\n';
@@ -445,9 +474,12 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
445
474
 
446
475
  if (result.metadata) {
447
476
  token_usage = {
448
- prompt: result.metadata.usage?.inputTokens,
477
+ prompt_new: result.metadata.usage?.inputTokens,
478
+ prompt: result.metadata.usage ? (result.metadata.usage.inputTokens ?? 0) + (result.metadata.usage.cacheReadInputTokens ?? 0) + (result.metadata.usage.cacheWriteInputTokens ?? 0) : undefined,
449
479
  result: result.metadata.usage?.outputTokens,
450
480
  total: result.metadata.usage?.totalTokens,
481
+ prompt_cached: result.metadata.usage?.cacheReadInputTokens ?? undefined,
482
+ prompt_cache_write: result.metadata.usage?.cacheWriteInputTokens ?? undefined,
451
483
  }
452
484
  }
453
485
 
@@ -455,6 +487,7 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
455
487
  result: reasoning + output ? [{ type: "text", value: reasoning + output }] : [],
456
488
  token_usage: token_usage,
457
489
  finish_reason: converseFinishReason(stop_reason),
490
+ tool_use,
458
491
  };
459
492
 
460
493
  return completionResult;
@@ -824,8 +857,9 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
824
857
  throw new Error("[Bedrock] Stream not found in response");
825
858
  }
826
859
 
860
+ const streamingToolBlocks = new Map<number, { id: string; name: string }>();
827
861
  return transformAsyncIterator(stream, (streamSegment: ConverseStreamOutput) => {
828
- return this.getExtractedStream(streamSegment, conversePrompt, options);
862
+ return this.getExtractedStream(streamSegment, conversePrompt, options, streamingToolBlocks);
829
863
  });
830
864
 
831
865
  }).catch((err) => {
@@ -1009,6 +1043,47 @@ export class BedrockDriver extends AbstractDriver<BedrockDriverOptions, BedrockP
1009
1043
  request.messages = convertToolBlocksToText(request.messages);
1010
1044
  }
1011
1045
 
1046
+ // Prompt caching: use three breakpoints so stable system blocks, tool definitions,
1047
+ // and the conversation history prefix can all be reused across Claude turns.
1048
+ if (options.model.includes('claude')) {
1049
+ // Always strip stale markers from prior turns
1050
+ if (request.messages) {
1051
+ request.messages = stripClaudeCachePoints(request.messages);
1052
+ }
1053
+ request.system = stripClaudeCachePointsFromSystem(request.system);
1054
+ if (request.toolConfig?.tools) {
1055
+ request.toolConfig = {
1056
+ ...request.toolConfig,
1057
+ tools: stripClaudeCachePointsFromTools(request.toolConfig.tools),
1058
+ };
1059
+ }
1060
+
1061
+ const claudeOptions = model_options as unknown as BedrockClaudeOptions;
1062
+ const cacheEnabled = claudeOptions?.cache_enabled === true;
1063
+ if (cacheEnabled) {
1064
+ const cacheTtl = claudeOptions?.cache_ttl;
1065
+ const cachePointBlock = { type: 'default' as const, ...(cacheTtl && { ttl: cacheTtl }) };
1066
+
1067
+ if (request.system && request.system.length > 0) {
1068
+ request.system = [...request.system, { cachePoint: cachePointBlock } satisfies BedrockSystemBlock];
1069
+ }
1070
+
1071
+ if (request.toolConfig?.tools && request.toolConfig.tools.length > 0) {
1072
+ request.toolConfig.tools = [
1073
+ ...request.toolConfig.tools,
1074
+ { cachePoint: cachePointBlock } satisfies BedrockToolEntry,
1075
+ ];
1076
+ }
1077
+
1078
+ if (request.messages && request.messages.length >= 4) {
1079
+ const pivotMsg = request.messages[request.messages.length - 2];
1080
+ if (pivotMsg.content && Array.isArray(pivotMsg.content) && pivotMsg.content.length > 0) {
1081
+ pivotMsg.content = [...pivotMsg.content, { cachePoint: cachePointBlock }];
1082
+ }
1083
+ }
1084
+ }
1085
+ }
1086
+
1012
1087
  return request;
1013
1088
  }
1014
1089
 
@@ -1556,6 +1631,23 @@ function updateConversation(conversation: ConverseRequest, prompt: ConverseReque
1556
1631
  };
1557
1632
  }
1558
1633
 
1634
+ function stripClaudeCachePoints(messages: Message[]): Message[] {
1635
+ return messages.map(message => ({
1636
+ ...message,
1637
+ content: message.content?.filter(block => !('cachePoint' in block)),
1638
+ }));
1639
+ }
1640
+
1641
+ function stripClaudeCachePointsFromSystem(system?: ConverseRequest['system']): ConverseRequest['system'] | undefined {
1642
+ return (system?.filter(block => !('cachePoint' in (block as object))) ?? undefined) as ConverseRequest['system'] | undefined;
1643
+ }
1644
+
1645
+ function stripClaudeCachePointsFromTools(
1646
+ tools?: NonNullable<NonNullable<ConverseRequest['toolConfig']>['tools']>
1647
+ ): NonNullable<NonNullable<ConverseRequest['toolConfig']>['tools']> | undefined {
1648
+ return (tools?.filter(tool => !('cachePoint' in (tool as object))) ?? undefined) as NonNullable<NonNullable<ConverseRequest['toolConfig']>['tools']> | undefined;
1649
+ }
1650
+
1559
1651
  /**
1560
1652
  * Fix orphaned toolUse blocks in the conversation.
1561
1653
  *
@@ -1671,4 +1763,4 @@ function formatAmazonModalities(modalities: ModelModality[]): string[] {
1671
1763
  }
1672
1764
  }
1673
1765
  return standardizedModalities;
1674
- }
1766
+ }
@@ -0,0 +1,250 @@
1
+ import {
2
+ AIModel,
3
+ Completion,
4
+ CompletionChunkObject,
5
+ DriverOptions,
6
+ EmbeddingsOptions,
7
+ EmbeddingsResult,
8
+ ExecutionOptions,
9
+ ModelSearchPayload,
10
+ PromptRole,
11
+ PromptSegment,
12
+ } from '@llumiverse/common';
13
+ import { AbstractDriver } from '@llumiverse/core';
14
+ import { beforeEach, describe, expect, it } from 'vitest';
15
+ import { BedrockDriver } from './index.js';
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Unit tests: getExtractedStream tool use handling
19
+ // ---------------------------------------------------------------------------
20
+
21
+ describe('BedrockDriver getExtractedStream — tool use', () => {
22
+ let driver: BedrockDriver;
23
+ let toolBlocks: Map<number, { id: string; name: string }>;
24
+
25
+ beforeEach(() => {
26
+ driver = new BedrockDriver({ region: 'us-east-1' });
27
+ toolBlocks = new Map();
28
+ });
29
+
30
+ it('emits an initial tool_use chunk on contentBlockStart', () => {
31
+ const chunk = driver['getExtractedStream'](
32
+ {
33
+ contentBlockStart: {
34
+ contentBlockIndex: 1,
35
+ start: { toolUse: { toolUseId: 'tool-abc', name: 'my_tool' } },
36
+ },
37
+ },
38
+ undefined,
39
+ undefined,
40
+ toolBlocks
41
+ );
42
+
43
+ expect(chunk.tool_use).toHaveLength(1);
44
+ expect(chunk.tool_use![0]).toMatchObject({ id: 'tool-abc', tool_name: 'my_tool', tool_input: '' });
45
+ expect(toolBlocks.get(1)).toEqual({ id: 'tool-abc', name: 'my_tool' });
46
+ });
47
+
48
+ it('emits a delta tool_use chunk on contentBlockDelta', () => {
49
+ toolBlocks.set(1, { id: 'tool-abc', name: 'my_tool' });
50
+
51
+ const chunk = driver['getExtractedStream'](
52
+ {
53
+ contentBlockDelta: {
54
+ contentBlockIndex: 1,
55
+ delta: { toolUse: { input: '{"key":' } },
56
+ },
57
+ },
58
+ undefined,
59
+ undefined,
60
+ toolBlocks
61
+ );
62
+
63
+ expect(chunk.tool_use).toHaveLength(1);
64
+ expect(chunk.tool_use![0]).toMatchObject({ id: 'tool-abc', tool_name: '', tool_input: '{"key":' });
65
+ });
66
+
67
+ it('removes the block from the map on contentBlockStop', () => {
68
+ toolBlocks.set(1, { id: 'tool-abc', name: 'my_tool' });
69
+
70
+ driver['getExtractedStream'](
71
+ { contentBlockStop: { contentBlockIndex: 1 } },
72
+ undefined,
73
+ undefined,
74
+ toolBlocks
75
+ );
76
+
77
+ expect(toolBlocks.has(1)).toBe(false);
78
+ });
79
+
80
+ it('tracks two interleaved tool calls by independent contentBlockIndex', () => {
81
+ driver['getExtractedStream'](
82
+ { contentBlockStart: { contentBlockIndex: 1, start: { toolUse: { toolUseId: 'id-1', name: 'tool_a' } } } },
83
+ undefined, undefined, toolBlocks
84
+ );
85
+ driver['getExtractedStream'](
86
+ { contentBlockStart: { contentBlockIndex: 3, start: { toolUse: { toolUseId: 'id-2', name: 'tool_b' } } } },
87
+ undefined, undefined, toolBlocks
88
+ );
89
+
90
+ expect(toolBlocks.get(1)).toEqual({ id: 'id-1', name: 'tool_a' });
91
+ expect(toolBlocks.get(3)).toEqual({ id: 'id-2', name: 'tool_b' });
92
+
93
+ const chunk = driver['getExtractedStream'](
94
+ { contentBlockDelta: { contentBlockIndex: 3, delta: { toolUse: { input: '"val"' } } } },
95
+ undefined, undefined, toolBlocks
96
+ );
97
+ expect(chunk.tool_use![0].id).toBe('id-2');
98
+ });
99
+
100
+ it('still extracts text deltas when no tool use is present', () => {
101
+ const chunk = driver['getExtractedStream'](
102
+ { contentBlockDelta: { contentBlockIndex: 0, delta: { text: 'hello' } } },
103
+ undefined,
104
+ undefined,
105
+ toolBlocks
106
+ );
107
+
108
+ expect(chunk.result).toEqual([{ type: 'text', value: 'hello' }]);
109
+ expect(chunk.tool_use).toBeUndefined();
110
+ });
111
+
112
+ it('emits finish_reason "tool_use" from messageStop', () => {
113
+ const chunk = driver['getExtractedStream'](
114
+ { messageStop: { stopReason: 'tool_use' } },
115
+ undefined,
116
+ undefined,
117
+ toolBlocks
118
+ );
119
+
120
+ expect(chunk.finish_reason).toBe('tool_use');
121
+ });
122
+ });
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Integration tests: full accumulation via driver.stream()
126
+ // ---------------------------------------------------------------------------
127
+
128
+ class FakeDriver extends AbstractDriver<DriverOptions, string> {
129
+ provider = 'fake';
130
+ chunks: CompletionChunkObject[] = [];
131
+
132
+ async requestTextCompletion(_prompt: string, _options: ExecutionOptions): Promise<Completion> {
133
+ throw new Error('not implemented');
134
+ }
135
+
136
+ async requestTextCompletionStream(_prompt: string, _options: ExecutionOptions): Promise<AsyncIterable<CompletionChunkObject>> {
137
+ const chunks = this.chunks;
138
+ return (async function* () { for (const c of chunks) yield c; })();
139
+ }
140
+
141
+ async listModels(_params?: ModelSearchPayload): Promise<AIModel[]> { return []; }
142
+ async validateConnection(): Promise<boolean> { return true; }
143
+ async generateEmbeddings(_options: EmbeddingsOptions): Promise<EmbeddingsResult> {
144
+ throw new Error('not implemented');
145
+ }
146
+ }
147
+
148
+ const FAKE_SEGMENTS: PromptSegment[] = [{ role: PromptRole.user, content: 'test' }];
149
+
150
+ describe('driver.stream() — Bedrock tool use accumulation', () => {
151
+ it('assembles and JSON-parses tool_input from streamed chunks', async () => {
152
+ const driver = new FakeDriver({});
153
+ const options: ExecutionOptions = { model: 'test-model' };
154
+
155
+ // Simulate what the fixed getExtractedStream emits for one tool call
156
+ driver.chunks = [
157
+ { result: [], tool_use: [{ id: 'tool-1', tool_name: 'do_thing', tool_input: '' as any }] },
158
+ { result: [], tool_use: [{ id: 'tool-1', tool_name: '', tool_input: '{"param"' as any }] },
159
+ { result: [], tool_use: [{ id: 'tool-1', tool_name: '', tool_input: ':"hello"}' as any }] },
160
+ { result: [], finish_reason: 'tool_use' },
161
+ ];
162
+
163
+ const stream = await driver.stream(FAKE_SEGMENTS, options);
164
+ for await (const _ of stream) { /* drain */ }
165
+
166
+ expect(stream.completion!.finish_reason).toBe('tool_use');
167
+ expect(stream.completion!.tool_use).toHaveLength(1);
168
+ expect(stream.completion!.tool_use![0]).toMatchObject({
169
+ id: 'tool-1',
170
+ tool_name: 'do_thing',
171
+ tool_input: { param: 'hello' },
172
+ });
173
+ });
174
+
175
+ it('handles two simultaneous tool calls', async () => {
176
+ const driver = new FakeDriver({});
177
+ const options: ExecutionOptions = { model: 'test-model' };
178
+
179
+ driver.chunks = [
180
+ { result: [], tool_use: [{ id: 'id-a', tool_name: 'tool_a', tool_input: '' as any }] },
181
+ { result: [], tool_use: [{ id: 'id-b', tool_name: 'tool_b', tool_input: '' as any }] },
182
+ { result: [], tool_use: [{ id: 'id-a', tool_name: '', tool_input: '{"x":1}' as any }] },
183
+ { result: [], tool_use: [{ id: 'id-b', tool_name: '', tool_input: '{"y":2}' as any }] },
184
+ { result: [], finish_reason: 'tool_use' },
185
+ ];
186
+
187
+ const stream = await driver.stream(FAKE_SEGMENTS, options);
188
+ for await (const _ of stream) { /* drain */ }
189
+
190
+ const toolUse = stream.completion!.tool_use!;
191
+ expect(toolUse).toHaveLength(2);
192
+ expect(toolUse.find(t => t.id === 'id-a')!.tool_input).toEqual({ x: 1 });
193
+ expect(toolUse.find(t => t.id === 'id-b')!.tool_input).toEqual({ y: 2 });
194
+ });
195
+
196
+ it('drops truncated tool calls when finish_reason is length', async () => {
197
+ const driver = new FakeDriver({});
198
+ const options: ExecutionOptions = { model: 'test-model' };
199
+
200
+ driver.chunks = [
201
+ { result: [], tool_use: [{ id: 'trunc', tool_name: 'tool_c', tool_input: '' as any }] },
202
+ { result: [], tool_use: [{ id: 'trunc', tool_name: '', tool_input: '{"incomplete' as any }] },
203
+ { result: [], finish_reason: 'length' },
204
+ ];
205
+
206
+ const stream = await driver.stream(FAKE_SEGMENTS, options);
207
+ for await (const _ of stream) { /* drain */ }
208
+
209
+ expect(stream.completion!.tool_use).toBeUndefined();
210
+ });
211
+ });
212
+
213
+ describe('BedrockDriver buildStreamingConversation', () => {
214
+ it('writes streamed text and tool use blocks back into the assistant message', () => {
215
+ const driver = new BedrockDriver({ region: 'us-east-1' });
216
+ const prompt = {
217
+ modelId: 'anthropic.claude-sonnet',
218
+ messages: [
219
+ { role: 'user', content: [{ text: 'What is the weather in Paris?' }] },
220
+ ],
221
+ };
222
+
223
+ const conversation = driver.buildStreamingConversation(
224
+ prompt as any,
225
+ [{ type: 'text', value: 'Let me check.' }] as any,
226
+ [{
227
+ id: 'tool-1',
228
+ tool_name: 'get_weather',
229
+ tool_input: { location: 'Paris' },
230
+ }],
231
+ { model: 'anthropic.claude-sonnet' } as ExecutionOptions
232
+ ) as any;
233
+
234
+ expect(conversation.messages).toHaveLength(2);
235
+ expect(conversation.messages[0]).toEqual(prompt.messages[0]);
236
+ expect(conversation.messages[1]).toEqual({
237
+ role: 'assistant',
238
+ content: [
239
+ { text: 'Let me check.' },
240
+ {
241
+ toolUse: {
242
+ toolUseId: 'tool-1',
243
+ name: 'get_weather',
244
+ input: { location: 'Paris' },
245
+ },
246
+ },
247
+ ],
248
+ });
249
+ });
250
+ });
@@ -793,6 +793,8 @@ function mapUsage(usage?: OpenAI.Responses.ResponseUsage | null): ExecutionToken
793
793
  prompt: usage.input_tokens,
794
794
  result: usage.output_tokens,
795
795
  total: usage.total_tokens,
796
+ prompt_cached: usage.input_tokens_details?.cached_tokens ?? undefined,
797
+ prompt_new: usage.input_tokens - (usage.input_tokens_details?.cached_tokens ?? 0),
796
798
  };
797
799
  }
798
800