@llumiverse/drivers 1.0.0-dev.20260202.145450Z → 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.
Files changed (240) hide show
  1. package/lib/cjs/adobe/firefly.js +120 -0
  2. package/lib/cjs/adobe/firefly.js.map +1 -0
  3. package/lib/cjs/azure/azure_foundry.js +432 -0
  4. package/lib/cjs/azure/azure_foundry.js.map +1 -0
  5. package/lib/cjs/bedrock/converse.js +359 -0
  6. package/lib/cjs/bedrock/converse.js.map +1 -0
  7. package/lib/cjs/bedrock/index.js +1441 -0
  8. package/lib/cjs/bedrock/index.js.map +1 -0
  9. package/lib/cjs/bedrock/nova-image-payload.js +207 -0
  10. package/lib/cjs/bedrock/nova-image-payload.js.map +1 -0
  11. package/lib/cjs/bedrock/payloads.js +3 -0
  12. package/lib/cjs/bedrock/payloads.js.map +1 -0
  13. package/lib/cjs/bedrock/s3.js +107 -0
  14. package/lib/cjs/bedrock/s3.js.map +1 -0
  15. package/lib/cjs/bedrock/twelvelabs.js +87 -0
  16. package/lib/cjs/bedrock/twelvelabs.js.map +1 -0
  17. package/lib/cjs/groq/index.js +326 -0
  18. package/lib/cjs/groq/index.js.map +1 -0
  19. package/lib/cjs/huggingface_ie.js +201 -0
  20. package/lib/cjs/huggingface_ie.js.map +1 -0
  21. package/lib/cjs/index.js +31 -0
  22. package/lib/cjs/index.js.map +1 -0
  23. package/lib/cjs/mistral/index.js +176 -0
  24. package/lib/cjs/mistral/index.js.map +1 -0
  25. package/lib/cjs/mistral/types.js +83 -0
  26. package/lib/cjs/mistral/types.js.map +1 -0
  27. package/lib/cjs/openai/azure_openai.js +72 -0
  28. package/lib/cjs/openai/azure_openai.js.map +1 -0
  29. package/lib/cjs/openai/index.js +1100 -0
  30. package/lib/cjs/openai/index.js.map +1 -0
  31. package/lib/cjs/openai/openai.js +21 -0
  32. package/lib/cjs/openai/openai.js.map +1 -0
  33. package/lib/cjs/openai/openai_compatible.js +63 -0
  34. package/lib/cjs/openai/openai_compatible.js.map +1 -0
  35. package/lib/cjs/openai/openai_format.js +131 -0
  36. package/lib/cjs/openai/openai_format.js.map +1 -0
  37. package/lib/cjs/package.json +3 -0
  38. package/lib/cjs/replicate.js +275 -0
  39. package/lib/cjs/replicate.js.map +1 -0
  40. package/lib/cjs/test-driver/TestErrorCompletionStream.js +20 -0
  41. package/lib/cjs/test-driver/TestErrorCompletionStream.js.map +1 -0
  42. package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js +24 -0
  43. package/lib/cjs/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
  44. package/lib/cjs/test-driver/index.js +109 -0
  45. package/lib/cjs/test-driver/index.js.map +1 -0
  46. package/lib/cjs/test-driver/utils.js +30 -0
  47. package/lib/cjs/test-driver/utils.js.map +1 -0
  48. package/lib/cjs/togetherai/index.js +126 -0
  49. package/lib/cjs/togetherai/index.js.map +1 -0
  50. package/lib/cjs/togetherai/interfaces.js +3 -0
  51. package/lib/cjs/togetherai/interfaces.js.map +1 -0
  52. package/lib/cjs/vertexai/debug.js +12 -0
  53. package/lib/cjs/vertexai/debug.js.map +1 -0
  54. package/lib/cjs/vertexai/embeddings/embeddings-image.js +27 -0
  55. package/lib/cjs/vertexai/embeddings/embeddings-image.js.map +1 -0
  56. package/lib/cjs/vertexai/embeddings/embeddings-text.js +23 -0
  57. package/lib/cjs/vertexai/embeddings/embeddings-text.js.map +1 -0
  58. package/lib/cjs/vertexai/index.js +635 -0
  59. package/lib/cjs/vertexai/index.js.map +1 -0
  60. package/lib/cjs/vertexai/models/claude.js +842 -0
  61. package/lib/cjs/vertexai/models/claude.js.map +1 -0
  62. package/lib/cjs/vertexai/models/gemini.js +1110 -0
  63. package/lib/cjs/vertexai/models/gemini.js.map +1 -0
  64. package/lib/cjs/vertexai/models/imagen.js +303 -0
  65. package/lib/cjs/vertexai/models/imagen.js.map +1 -0
  66. package/lib/cjs/vertexai/models/llama.js +183 -0
  67. package/lib/cjs/vertexai/models/llama.js.map +1 -0
  68. package/lib/cjs/vertexai/models.js +35 -0
  69. package/lib/cjs/vertexai/models.js.map +1 -0
  70. package/lib/cjs/watsonx/index.js +161 -0
  71. package/lib/cjs/watsonx/index.js.map +1 -0
  72. package/lib/cjs/watsonx/interfaces.js +3 -0
  73. package/lib/cjs/watsonx/interfaces.js.map +1 -0
  74. package/lib/cjs/xai/index.js +65 -0
  75. package/lib/cjs/xai/index.js.map +1 -0
  76. package/lib/esm/adobe/firefly.js +116 -0
  77. package/lib/esm/adobe/firefly.js.map +1 -0
  78. package/lib/esm/azure/azure_foundry.js +426 -0
  79. package/lib/esm/azure/azure_foundry.js.map +1 -0
  80. package/lib/esm/bedrock/converse.js +352 -0
  81. package/lib/esm/bedrock/converse.js.map +1 -0
  82. package/lib/esm/bedrock/index.js +1434 -0
  83. package/lib/esm/bedrock/index.js.map +1 -0
  84. package/lib/esm/bedrock/nova-image-payload.js +203 -0
  85. package/lib/esm/bedrock/nova-image-payload.js.map +1 -0
  86. package/lib/esm/bedrock/payloads.js +2 -0
  87. package/lib/esm/bedrock/payloads.js.map +1 -0
  88. package/lib/esm/bedrock/s3.js +99 -0
  89. package/lib/esm/bedrock/s3.js.map +1 -0
  90. package/lib/esm/bedrock/twelvelabs.js +84 -0
  91. package/lib/esm/bedrock/twelvelabs.js.map +1 -0
  92. package/lib/esm/groq/index.js +319 -0
  93. package/lib/esm/groq/index.js.map +1 -0
  94. package/lib/esm/huggingface_ie.js +197 -0
  95. package/lib/esm/huggingface_ie.js.map +1 -0
  96. package/lib/esm/index.js +15 -0
  97. package/lib/esm/index.js.map +1 -0
  98. package/lib/esm/mistral/index.js +172 -0
  99. package/lib/esm/mistral/index.js.map +1 -0
  100. package/lib/esm/mistral/types.js +80 -0
  101. package/lib/esm/mistral/types.js.map +1 -0
  102. package/lib/esm/openai/azure_openai.js +68 -0
  103. package/lib/esm/openai/azure_openai.js.map +1 -0
  104. package/lib/esm/openai/index.js +1093 -0
  105. package/lib/esm/openai/index.js.map +1 -0
  106. package/lib/esm/openai/openai.js +14 -0
  107. package/lib/esm/openai/openai.js.map +1 -0
  108. package/lib/esm/openai/openai_compatible.js +56 -0
  109. package/lib/esm/openai/openai_compatible.js.map +1 -0
  110. package/lib/esm/openai/openai_format.js +127 -0
  111. package/lib/esm/openai/openai_format.js.map +1 -0
  112. package/lib/esm/replicate.js +268 -0
  113. package/lib/esm/replicate.js.map +1 -0
  114. package/lib/esm/test-driver/TestErrorCompletionStream.js +16 -0
  115. package/lib/esm/test-driver/TestErrorCompletionStream.js.map +1 -0
  116. package/lib/esm/test-driver/TestValidationErrorCompletionStream.js +20 -0
  117. package/lib/esm/test-driver/TestValidationErrorCompletionStream.js.map +1 -0
  118. package/lib/esm/test-driver/index.js +91 -0
  119. package/lib/esm/test-driver/index.js.map +1 -0
  120. package/lib/esm/test-driver/utils.js +25 -0
  121. package/lib/esm/test-driver/utils.js.map +1 -0
  122. package/lib/esm/togetherai/index.js +122 -0
  123. package/lib/esm/togetherai/index.js.map +1 -0
  124. package/lib/esm/togetherai/interfaces.js +2 -0
  125. package/lib/esm/togetherai/interfaces.js.map +1 -0
  126. package/lib/esm/vertexai/debug.js +6 -0
  127. package/lib/esm/vertexai/debug.js.map +1 -0
  128. package/lib/esm/vertexai/embeddings/embeddings-image.js +24 -0
  129. package/lib/esm/vertexai/embeddings/embeddings-image.js.map +1 -0
  130. package/lib/esm/vertexai/embeddings/embeddings-text.js +20 -0
  131. package/lib/esm/vertexai/embeddings/embeddings-text.js.map +1 -0
  132. package/lib/esm/vertexai/index.js +630 -0
  133. package/lib/esm/vertexai/index.js.map +1 -0
  134. package/lib/esm/vertexai/models/claude.js +833 -0
  135. package/lib/esm/vertexai/models/claude.js.map +1 -0
  136. package/lib/esm/vertexai/models/gemini.js +1104 -0
  137. package/lib/esm/vertexai/models/gemini.js.map +1 -0
  138. package/lib/esm/vertexai/models/imagen.js +299 -0
  139. package/lib/esm/vertexai/models/imagen.js.map +1 -0
  140. package/lib/esm/vertexai/models/llama.js +179 -0
  141. package/lib/esm/vertexai/models/llama.js.map +1 -0
  142. package/lib/esm/vertexai/models.js +32 -0
  143. package/lib/esm/vertexai/models.js.map +1 -0
  144. package/lib/esm/watsonx/index.js +157 -0
  145. package/lib/esm/watsonx/index.js.map +1 -0
  146. package/lib/esm/watsonx/interfaces.js +2 -0
  147. package/lib/esm/watsonx/interfaces.js.map +1 -0
  148. package/lib/esm/xai/index.js +58 -0
  149. package/lib/esm/xai/index.js.map +1 -0
  150. package/lib/types/adobe/firefly.d.ts +30 -0
  151. package/lib/types/adobe/firefly.d.ts.map +1 -0
  152. package/lib/types/azure/azure_foundry.d.ts +52 -0
  153. package/lib/types/azure/azure_foundry.d.ts.map +1 -0
  154. package/lib/types/bedrock/converse.d.ts +8 -0
  155. package/lib/types/bedrock/converse.d.ts.map +1 -0
  156. package/lib/types/bedrock/index.d.ts +135 -0
  157. package/lib/types/bedrock/index.d.ts.map +1 -0
  158. package/lib/types/bedrock/nova-image-payload.d.ts +74 -0
  159. package/lib/types/bedrock/nova-image-payload.d.ts.map +1 -0
  160. package/lib/types/bedrock/payloads.d.ts +12 -0
  161. package/lib/types/bedrock/payloads.d.ts.map +1 -0
  162. package/lib/types/bedrock/s3.d.ts +23 -0
  163. package/lib/types/bedrock/s3.d.ts.map +1 -0
  164. package/lib/types/bedrock/twelvelabs.d.ts +50 -0
  165. package/lib/types/bedrock/twelvelabs.d.ts.map +1 -0
  166. package/lib/types/groq/index.d.ts +27 -0
  167. package/lib/types/groq/index.d.ts.map +1 -0
  168. package/lib/types/huggingface_ie.d.ts +35 -0
  169. package/lib/types/huggingface_ie.d.ts.map +1 -0
  170. package/lib/types/index.d.ts +15 -0
  171. package/lib/types/index.d.ts.map +1 -0
  172. package/lib/types/mistral/index.d.ts +25 -0
  173. package/lib/types/mistral/index.d.ts.map +1 -0
  174. package/lib/types/mistral/types.d.ts +127 -0
  175. package/lib/types/mistral/types.d.ts.map +1 -0
  176. package/lib/types/openai/azure_openai.d.ts +25 -0
  177. package/lib/types/openai/azure_openai.d.ts.map +1 -0
  178. package/lib/types/openai/index.d.ts +126 -0
  179. package/lib/types/openai/index.d.ts.map +1 -0
  180. package/lib/types/openai/openai.d.ts +15 -0
  181. package/lib/types/openai/openai.d.ts.map +1 -0
  182. package/lib/types/openai/openai_compatible.d.ts +31 -0
  183. package/lib/types/openai/openai_compatible.d.ts.map +1 -0
  184. package/lib/types/openai/openai_format.d.ts +21 -0
  185. package/lib/types/openai/openai_format.d.ts.map +1 -0
  186. package/lib/types/replicate.d.ts +48 -0
  187. package/lib/types/replicate.d.ts.map +1 -0
  188. package/lib/types/test-driver/TestErrorCompletionStream.d.ts +9 -0
  189. package/lib/types/test-driver/TestErrorCompletionStream.d.ts.map +1 -0
  190. package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts +9 -0
  191. package/lib/types/test-driver/TestValidationErrorCompletionStream.d.ts.map +1 -0
  192. package/lib/types/test-driver/index.d.ts +24 -0
  193. package/lib/types/test-driver/index.d.ts.map +1 -0
  194. package/lib/types/test-driver/utils.d.ts +5 -0
  195. package/lib/types/test-driver/utils.d.ts.map +1 -0
  196. package/lib/types/togetherai/index.d.ts +23 -0
  197. package/lib/types/togetherai/index.d.ts.map +1 -0
  198. package/lib/types/togetherai/interfaces.d.ts +96 -0
  199. package/lib/types/togetherai/interfaces.d.ts.map +1 -0
  200. package/lib/types/vertexai/debug.d.ts +2 -0
  201. package/lib/types/vertexai/debug.d.ts.map +1 -0
  202. package/lib/types/vertexai/embeddings/embeddings-image.d.ts +11 -0
  203. package/lib/types/vertexai/embeddings/embeddings-image.d.ts.map +1 -0
  204. package/lib/types/vertexai/embeddings/embeddings-text.d.ts +10 -0
  205. package/lib/types/vertexai/embeddings/embeddings-text.d.ts.map +1 -0
  206. package/lib/types/vertexai/index.d.ts +79 -0
  207. package/lib/types/vertexai/index.d.ts.map +1 -0
  208. package/lib/types/vertexai/models/claude.d.ts +103 -0
  209. package/lib/types/vertexai/models/claude.d.ts.map +1 -0
  210. package/lib/types/vertexai/models/gemini.d.ts +78 -0
  211. package/lib/types/vertexai/models/gemini.d.ts.map +1 -0
  212. package/lib/types/vertexai/models/imagen.d.ts +75 -0
  213. package/lib/types/vertexai/models/imagen.d.ts.map +1 -0
  214. package/lib/types/vertexai/models/llama.d.ts +20 -0
  215. package/lib/types/vertexai/models/llama.d.ts.map +1 -0
  216. package/lib/types/vertexai/models.d.ts +20 -0
  217. package/lib/types/vertexai/models.d.ts.map +1 -0
  218. package/lib/types/watsonx/index.d.ts +27 -0
  219. package/lib/types/watsonx/index.d.ts.map +1 -0
  220. package/lib/types/watsonx/interfaces.d.ts +65 -0
  221. package/lib/types/watsonx/interfaces.d.ts.map +1 -0
  222. package/lib/types/xai/index.d.ts +18 -0
  223. package/lib/types/xai/index.d.ts.map +1 -0
  224. package/package.json +18 -18
  225. package/src/bedrock/converse.ts +85 -10
  226. package/src/bedrock/error-handling.test.ts +352 -0
  227. package/src/bedrock/index.ts +293 -16
  228. package/src/groq/index.ts +9 -4
  229. package/src/mistral/index.ts +25 -22
  230. package/src/mistral/types.ts +0 -5
  231. package/src/openai/error-handling.test.ts +567 -0
  232. package/src/openai/index.ts +513 -33
  233. package/src/openai/openai_compatible.ts +7 -0
  234. package/src/openai/openai_format.ts +1 -1
  235. package/src/vertexai/index.ts +61 -13
  236. package/src/vertexai/models/claude-error-handling.test.ts +432 -0
  237. package/src/vertexai/models/claude.ts +287 -10
  238. package/src/vertexai/models/gemini-error-handling.test.ts +353 -0
  239. package/src/vertexai/models/gemini.ts +329 -52
  240. package/src/vertexai/models.ts +7 -2
@@ -0,0 +1,842 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClaudeModelDefinition = exports.NON_GLOBAL_ANTHROPIC_MODELS = exports.ANTHROPIC_REGIONS = void 0;
4
+ exports.collectTools = collectTools;
5
+ exports.mergeConsecutiveUserMessages = mergeConsecutiveUserMessages;
6
+ exports.fixOrphanedToolUse = fixOrphanedToolUse;
7
+ exports.claudeMessagesContainToolBlocks = claudeMessagesContainToolBlocks;
8
+ exports.convertClaudeToolBlocksToText = convertClaudeToolBlocksToText;
9
+ const error_1 = require("@anthropic-ai/sdk/error");
10
+ const core_1 = require("@llumiverse/core");
11
+ const async_1 = require("@llumiverse/core/async");
12
+ exports.ANTHROPIC_REGIONS = {
13
+ us: "us-east5",
14
+ europe: "europe-west1",
15
+ global: "global",
16
+ };
17
+ exports.NON_GLOBAL_ANTHROPIC_MODELS = [
18
+ "claude-3-5",
19
+ "claude-3",
20
+ ];
21
+ function claudeFinishReason(reason) {
22
+ if (!reason)
23
+ return undefined;
24
+ switch (reason) {
25
+ case 'end_turn': return "stop";
26
+ case 'max_tokens': return "length";
27
+ default: return reason; //stop_sequence
28
+ }
29
+ }
30
+ function collectTools(content) {
31
+ const out = [];
32
+ for (const block of content) {
33
+ if (block.type === "tool_use") {
34
+ out.push({
35
+ id: block.id,
36
+ tool_name: block.name,
37
+ tool_input: block.input,
38
+ });
39
+ }
40
+ }
41
+ return out.length > 0 ? out : undefined;
42
+ }
43
+ function collectAllTextContent(content, includeThoughts = false) {
44
+ const textParts = [];
45
+ // First pass: collect thinking blocks
46
+ if (includeThoughts) {
47
+ for (const block of content) {
48
+ if (block.type === 'thinking' && block.thinking) {
49
+ textParts.push(block.thinking);
50
+ }
51
+ else if (block.type === 'redacted_thinking' && block.data) {
52
+ textParts.push(`[Redacted thinking: ${block.data}]`);
53
+ }
54
+ }
55
+ if (textParts.length > 0) {
56
+ textParts.push(''); // Create a new line after thinking blocks
57
+ }
58
+ }
59
+ // Second pass: collect text blocks
60
+ for (const block of content) {
61
+ if (block.type === 'text' && block.text) {
62
+ textParts.push(block.text);
63
+ }
64
+ }
65
+ return textParts.join('\n');
66
+ }
67
+ //Used to get a max_token value when not specified in the model options. Claude requires it to be set.
68
+ function maxToken(option) {
69
+ const modelOptions = option.model_options;
70
+ if (modelOptions && typeof modelOptions.max_tokens === "number") {
71
+ return modelOptions.max_tokens;
72
+ }
73
+ else {
74
+ let maxSupportedTokens = (0, core_1.getMaxTokensLimitVertexAi)(option.model);
75
+ // Fallback to the default max tokens limit for the model
76
+ if (option.model.includes('claude-3-7-sonnet') && (modelOptions?.thinking_budget_tokens ?? 0) < 48000) {
77
+ maxSupportedTokens = 64000; // Claude 3.7 can go up to 128k with a beta header, but when no max tokens is specified, we default to 64k.
78
+ }
79
+ return maxSupportedTokens;
80
+ }
81
+ }
82
+ async function collectFileBlocks(segment, restrictedTypes = false) {
83
+ const contentBlocks = [];
84
+ for (const file of segment.files || []) {
85
+ if (file.mime_type?.startsWith("image/")) {
86
+ const allowedTypes = ["image/png", "image/jpeg", "image/gif", "image/webp"];
87
+ if (!allowedTypes.includes(file.mime_type)) {
88
+ throw new Error(`Unsupported image type: ${file.mime_type}`);
89
+ }
90
+ const mimeType = String(file.mime_type);
91
+ contentBlocks.push({
92
+ type: 'image',
93
+ source: {
94
+ type: 'base64',
95
+ data: await (0, core_1.readStreamAsBase64)(await file.getStream()),
96
+ media_type: mimeType
97
+ }
98
+ });
99
+ }
100
+ else if (!restrictedTypes) {
101
+ if (file.mime_type === "application/pdf") {
102
+ contentBlocks.push({
103
+ title: file.name,
104
+ type: 'document',
105
+ source: {
106
+ type: 'base64',
107
+ data: await (0, core_1.readStreamAsBase64)(await file.getStream()),
108
+ media_type: 'application/pdf'
109
+ }
110
+ });
111
+ }
112
+ else if (file.mime_type?.startsWith("text/")) {
113
+ contentBlocks.push({
114
+ title: file.name,
115
+ type: 'document',
116
+ source: {
117
+ type: 'text',
118
+ data: await (0, core_1.readStreamAsString)(await file.getStream()),
119
+ media_type: 'text/plain'
120
+ }
121
+ });
122
+ }
123
+ }
124
+ }
125
+ return contentBlocks;
126
+ }
127
+ class ClaudeModelDefinition {
128
+ model;
129
+ constructor(modelId) {
130
+ this.model = {
131
+ id: modelId,
132
+ name: modelId,
133
+ provider: 'vertexai',
134
+ type: core_1.ModelType.Text,
135
+ can_stream: true,
136
+ };
137
+ }
138
+ async createPrompt(_driver, segments, options) {
139
+ // Convert the prompt to the format expected by the Claude API
140
+ let system = segments
141
+ .filter(segment => segment.role === core_1.PromptRole.system)
142
+ .map(segment => ({
143
+ text: segment.content,
144
+ type: 'text'
145
+ }));
146
+ if (options.result_schema) {
147
+ let schemaText = '';
148
+ if (options.tools && options.tools.length > 0) {
149
+ schemaText = "When not calling tools, the answer must be a JSON object using the following JSON Schema:\n" + JSON.stringify(options.result_schema);
150
+ }
151
+ else {
152
+ schemaText = "The answer must be a JSON object using the following JSON Schema:\n" + JSON.stringify(options.result_schema);
153
+ }
154
+ const schemaSegments = {
155
+ text: schemaText,
156
+ type: 'text'
157
+ };
158
+ system.push(schemaSegments);
159
+ }
160
+ let messages = [];
161
+ const safetyMessages = [];
162
+ for (const segment of segments) {
163
+ if (segment.role === core_1.PromptRole.system) {
164
+ continue;
165
+ }
166
+ if (segment.role === core_1.PromptRole.tool) {
167
+ if (!segment.tool_use_id) {
168
+ throw new Error("Tool prompt segment must have a tool use ID");
169
+ }
170
+ // Build content blocks for tool results (restricted types)
171
+ const contentBlocks = [];
172
+ if (segment.content) {
173
+ contentBlocks.push({
174
+ type: 'text',
175
+ text: segment.content
176
+ });
177
+ }
178
+ // Collect file blocks with type safety
179
+ const fileBlocks = await collectFileBlocks(segment, true);
180
+ contentBlocks.push(...fileBlocks);
181
+ messages.push({
182
+ role: 'user',
183
+ content: [{
184
+ type: 'tool_result',
185
+ tool_use_id: segment.tool_use_id,
186
+ content: contentBlocks,
187
+ }]
188
+ });
189
+ }
190
+ else {
191
+ // Build content blocks for regular messages (all types allowed)
192
+ const contentBlocks = [];
193
+ if (segment.content) {
194
+ contentBlocks.push({
195
+ type: 'text',
196
+ text: segment.content
197
+ });
198
+ }
199
+ // Collect file blocks without restrictions
200
+ const fileBlocks = await collectFileBlocks(segment, false);
201
+ contentBlocks.push(...fileBlocks);
202
+ if (contentBlocks.length === 0) {
203
+ continue; // skip empty segments
204
+ }
205
+ const messageParam = {
206
+ role: segment.role === core_1.PromptRole.assistant ? 'assistant' : 'user',
207
+ content: contentBlocks
208
+ };
209
+ if (segment.role === core_1.PromptRole.safety) {
210
+ safetyMessages.push(messageParam);
211
+ }
212
+ else {
213
+ messages.push(messageParam);
214
+ }
215
+ }
216
+ }
217
+ messages = messages.concat(safetyMessages);
218
+ if (system && system.length === 0) {
219
+ system = undefined; // If system is empty, set to undefined
220
+ }
221
+ return {
222
+ messages: messages,
223
+ system: system
224
+ };
225
+ }
226
+ async requestTextCompletion(driver, prompt, options) {
227
+ const splits = options.model.split("/");
228
+ let region = undefined;
229
+ if (splits[0] === "locations" && splits.length >= 2) {
230
+ region = splits[1];
231
+ }
232
+ const modelName = splits[splits.length - 1];
233
+ options = { ...options, model: modelName };
234
+ const client = await driver.getAnthropicClient(region);
235
+ options.model_options = options.model_options;
236
+ if (options.model_options?._option_id !== "vertexai-claude") {
237
+ driver.logger.warn({ options: options.model_options }, "Invalid model options");
238
+ }
239
+ let conversation = updateConversation(options.conversation, prompt);
240
+ const { payload, requestOptions } = getClaudePayload(options, conversation);
241
+ // disable streaming, the create function is overloaded so payload type matters.
242
+ const nonStreamingPayload = { ...payload, stream: false };
243
+ const result = await client.messages.create(nonStreamingPayload, requestOptions);
244
+ // Use the new function to collect text content, including thinking if enabled
245
+ const includeThoughts = options.model_options?.include_thoughts ?? false;
246
+ const text = collectAllTextContent(result.content, includeThoughts);
247
+ const tool_use = collectTools(result.content);
248
+ conversation = updateConversation(conversation, createPromptFromResponse(result));
249
+ // Increment turn counter and apply stripping (same pattern as other drivers)
250
+ conversation = (0, core_1.incrementConversationTurn)(conversation);
251
+ const currentTurn = (0, core_1.getConversationMeta)(conversation).turnNumber;
252
+ const stripOptions = {
253
+ keepForTurns: options.stripImagesAfterTurns ?? Infinity,
254
+ currentTurn,
255
+ textMaxTokens: options.stripTextMaxTokens,
256
+ };
257
+ let processedConversation = (0, core_1.stripBase64ImagesFromConversation)(conversation, stripOptions);
258
+ processedConversation = (0, core_1.truncateLargeTextInConversation)(processedConversation, stripOptions);
259
+ processedConversation = (0, core_1.stripHeartbeatsFromConversation)(processedConversation, {
260
+ keepForTurns: options.stripHeartbeatsAfterTurns ?? 1,
261
+ currentTurn,
262
+ });
263
+ return {
264
+ result: text ? [{ type: "text", value: text }] : [{ type: "text", value: '' }],
265
+ tool_use,
266
+ token_usage: {
267
+ prompt: result.usage.input_tokens,
268
+ result: result.usage.output_tokens,
269
+ total: result.usage.input_tokens + result.usage.output_tokens
270
+ },
271
+ // make sure we set finish_reason to the correct value (claude is normally setting this by itself)
272
+ finish_reason: tool_use ? "tool_use" : claudeFinishReason(result?.stop_reason ?? ''),
273
+ conversation: processedConversation
274
+ };
275
+ }
276
+ async requestTextCompletionStream(driver, prompt, options) {
277
+ const splits = options.model.split("/");
278
+ let region = undefined;
279
+ if (splits[0] === "locations" && splits.length >= 2) {
280
+ region = splits[1];
281
+ }
282
+ const modelName = splits[splits.length - 1];
283
+ options = { ...options, model: modelName };
284
+ const client = await driver.getAnthropicClient(region);
285
+ const model_options = options.model_options;
286
+ if (model_options?._option_id !== "vertexai-claude") {
287
+ driver.logger.warn({ options: options.model_options }, "Invalid model options");
288
+ }
289
+ // Include conversation history (same as non-streaming)
290
+ const conversation = updateConversation(options.conversation, prompt);
291
+ const { payload, requestOptions } = getClaudePayload(options, conversation);
292
+ const streamingPayload = { ...payload, stream: true };
293
+ const response_stream = await client.messages.stream(streamingPayload, requestOptions);
294
+ // Track current tool use being built from streaming
295
+ let currentToolUse = null;
296
+ const stream = (0, async_1.asyncMap)(response_stream, async (streamEvent) => {
297
+ switch (streamEvent.type) {
298
+ case "message_start":
299
+ return {
300
+ result: [{ type: "text", value: '' }],
301
+ token_usage: {
302
+ prompt: streamEvent.message.usage.input_tokens,
303
+ result: streamEvent.message.usage.output_tokens
304
+ }
305
+ };
306
+ case "message_delta":
307
+ return {
308
+ result: [{ type: "text", value: '' }],
309
+ token_usage: {
310
+ result: streamEvent.usage.output_tokens
311
+ },
312
+ finish_reason: claudeFinishReason(streamEvent.delta.stop_reason ?? undefined),
313
+ };
314
+ case "content_block_start":
315
+ // Handle tool_use blocks
316
+ if (streamEvent.content_block.type === "tool_use") {
317
+ currentToolUse = {
318
+ id: streamEvent.content_block.id,
319
+ name: streamEvent.content_block.name,
320
+ inputJson: ''
321
+ };
322
+ return {
323
+ result: [],
324
+ tool_use: [{
325
+ id: streamEvent.content_block.id,
326
+ tool_name: streamEvent.content_block.name,
327
+ tool_input: '' // Will be accumulated via input_json_delta
328
+ }]
329
+ };
330
+ }
331
+ // Handle redacted thinking blocks
332
+ if (streamEvent.content_block.type === "redacted_thinking" && model_options?.include_thoughts) {
333
+ return {
334
+ result: [{ type: "text", value: `[Redacted thinking: ${streamEvent.content_block.data}]` }]
335
+ };
336
+ }
337
+ break;
338
+ case "content_block_delta":
339
+ // Handle different delta types
340
+ switch (streamEvent.delta.type) {
341
+ case "text_delta":
342
+ return {
343
+ result: streamEvent.delta.text ? [{ type: "text", value: streamEvent.delta.text }] : []
344
+ };
345
+ case "input_json_delta":
346
+ // Accumulate tool input JSON
347
+ if (currentToolUse && streamEvent.delta.partial_json) {
348
+ return {
349
+ result: [],
350
+ tool_use: [{
351
+ id: currentToolUse.id,
352
+ tool_name: '', // Name already sent in content_block_start
353
+ tool_input: streamEvent.delta.partial_json
354
+ }]
355
+ };
356
+ }
357
+ break;
358
+ case "thinking_delta":
359
+ if (model_options?.include_thoughts) {
360
+ return {
361
+ result: streamEvent.delta.thinking ? [{ type: "text", value: streamEvent.delta.thinking }] : [],
362
+ };
363
+ }
364
+ break;
365
+ case "signature_delta":
366
+ // Signature deltas, signify the end of the thoughts.
367
+ if (model_options?.include_thoughts) {
368
+ return {
369
+ result: [{ type: "text", value: '\n\n' }], // Double newline for more spacing
370
+ };
371
+ }
372
+ break;
373
+ }
374
+ break;
375
+ case "content_block_stop":
376
+ // Reset current tool use tracking when block ends
377
+ if (currentToolUse) {
378
+ currentToolUse = null;
379
+ }
380
+ // Handle the end of content blocks, for redacted thinking blocks
381
+ if (model_options?.include_thoughts) {
382
+ return {
383
+ result: [{ type: "text", value: '\n\n' }] // Add double newline for spacing
384
+ };
385
+ }
386
+ break;
387
+ }
388
+ // Default case for all other event types
389
+ return {
390
+ result: []
391
+ };
392
+ });
393
+ return stream;
394
+ }
395
+ /**
396
+ * Format Anthropic API errors into LlumiverseError with proper status codes and retryability.
397
+ *
398
+ * Anthropic API errors have a specific structure:
399
+ * - APIError.status: HTTP status code (400, 401, 403, 404, 409, 422, 429, 500+)
400
+ * - APIError.error: Nested error object with type and message
401
+ * - APIError.requestID: Request ID for support (can be null)
402
+ *
403
+ * Common error types:
404
+ * - BadRequestError (400): Invalid request parameters
405
+ * - AuthenticationError (401): Authentication required
406
+ * - PermissionDeniedError (403): Insufficient permissions
407
+ * - NotFoundError (404): Resource not found
408
+ * - ConflictError (409): Resource conflict
409
+ * - UnprocessableEntityError (422): Validation error
410
+ * - RateLimitError (429): Rate limit exceeded
411
+ * - InternalServerError (500+): Server-side errors
412
+ * - APIConnectionError: Connection issues (no status code)
413
+ * - APIConnectionTimeoutError: Request timeout (no status code)
414
+ *
415
+ * @see https://docs.anthropic.com/en/api/errors
416
+ */
417
+ formatLlumiverseError(_driver, error, context) {
418
+ // Check if it's an Anthropic API error
419
+ const isAnthropicError = this.isAnthropicApiError(error);
420
+ if (!isAnthropicError) {
421
+ // Not an Anthropic API error, use default handling
422
+ throw error;
423
+ }
424
+ const apiError = error;
425
+ const httpStatusCode = apiError.status;
426
+ // Extract error message and nested error details
427
+ let message = apiError.message || String(error);
428
+ // Extract error type from nested error object if available
429
+ let errorType;
430
+ if (apiError.error && typeof apiError.error === 'object') {
431
+ const nestedError = apiError.error;
432
+ if (nestedError.error && typeof nestedError.error === 'object') {
433
+ errorType = nestedError.error.type;
434
+ // Use the nested error message if it's more specific
435
+ if (nestedError.error.message) {
436
+ message = nestedError.error.message;
437
+ }
438
+ }
439
+ }
440
+ // Build user-facing message with status code
441
+ let userMessage = message;
442
+ // Include status code in message (for end-user visibility)
443
+ if (httpStatusCode) {
444
+ userMessage = `[${httpStatusCode}] ${userMessage}`;
445
+ }
446
+ // Include error type if available
447
+ if (errorType && errorType !== 'error') {
448
+ userMessage = `${errorType}: ${userMessage}`;
449
+ }
450
+ // Add request ID if available (useful for Anthropic support)
451
+ if (apiError.requestID) {
452
+ userMessage += ` (Request ID: ${apiError.requestID})`;
453
+ }
454
+ // Determine retryability based on Anthropic error types
455
+ const retryable = this.isClaudeErrorRetryable(error, httpStatusCode, errorType);
456
+ // Use the error constructor name as the error name
457
+ const errorName = error.constructor?.name || 'AnthropicError';
458
+ return new core_1.LlumiverseError(`[${context.provider}] ${userMessage}`, retryable, context, error, httpStatusCode, errorName);
459
+ }
460
+ /**
461
+ * Type guard to check if error is an Anthropic API error.
462
+ */
463
+ isAnthropicApiError(error) {
464
+ return (error !== null &&
465
+ typeof error === 'object' &&
466
+ error instanceof error_1.APIError);
467
+ }
468
+ /**
469
+ * Determine if an Anthropic API error is retryable.
470
+ *
471
+ * Retryable errors:
472
+ * - RateLimitError (429): Rate limit exceeded, retry with backoff
473
+ * - InternalServerError (500+): Server-side errors
474
+ * - APIConnectionTimeoutError: Request timeout
475
+ * - 408 (Request Timeout): Request timeout
476
+ * - 529 (Overloaded): Service overloaded
477
+ *
478
+ * Non-retryable errors:
479
+ * - BadRequestError (400): Invalid request parameters
480
+ * - AuthenticationError (401): Authentication failure
481
+ * - PermissionDeniedError (403): Insufficient permissions
482
+ * - NotFoundError (404): Resource not found
483
+ * - ConflictError (409): Resource conflict
484
+ * - UnprocessableEntityError (422): Validation error
485
+ * - Other 4xx client errors
486
+ * - invalid_request_error: Invalid request structure
487
+ *
488
+ * @param error - The error object
489
+ * @param httpStatusCode - The HTTP status code if available
490
+ * @param errorType - The nested error type if available
491
+ * @returns True if retryable, false if not retryable, undefined if unknown
492
+ */
493
+ isClaudeErrorRetryable(error, httpStatusCode, errorType) {
494
+ // Check specific Anthropic error types by class
495
+ if (error instanceof error_1.RateLimitError)
496
+ return true;
497
+ if (error instanceof error_1.InternalServerError)
498
+ return true;
499
+ if (error instanceof error_1.APIConnectionTimeoutError)
500
+ return true;
501
+ // Non-retryable by error type
502
+ if (error instanceof error_1.BadRequestError)
503
+ return false;
504
+ if (error instanceof error_1.AuthenticationError)
505
+ return false;
506
+ if (error instanceof error_1.PermissionDeniedError)
507
+ return false;
508
+ if (error instanceof error_1.NotFoundError)
509
+ return false;
510
+ if (error instanceof error_1.ConflictError)
511
+ return false;
512
+ if (error instanceof error_1.UnprocessableEntityError)
513
+ return false;
514
+ // Check nested error type
515
+ if (errorType === 'invalid_request_error')
516
+ return false;
517
+ // Use HTTP status code
518
+ if (httpStatusCode !== undefined) {
519
+ if (httpStatusCode === 429)
520
+ return true; // Rate limit
521
+ if (httpStatusCode === 408)
522
+ return true; // Request timeout
523
+ if (httpStatusCode === 529)
524
+ return true; // Overloaded
525
+ if (httpStatusCode >= 500 && httpStatusCode < 600)
526
+ return true; // Server errors
527
+ if (httpStatusCode >= 400 && httpStatusCode < 500)
528
+ return false; // Client errors
529
+ }
530
+ // Connection errors without status codes
531
+ if (error instanceof error_1.APIConnectionError && !(error instanceof error_1.APIConnectionTimeoutError)) {
532
+ // Generic connection errors might be retryable (network issues)
533
+ return true;
534
+ }
535
+ // Unknown error type - let consumer decide retry strategy
536
+ return undefined;
537
+ }
538
+ }
539
+ exports.ClaudeModelDefinition = ClaudeModelDefinition;
540
+ function createPromptFromResponse(response) {
541
+ return {
542
+ messages: [{
543
+ role: response.role,
544
+ content: response.content,
545
+ }],
546
+ system: undefined
547
+ };
548
+ }
549
+ /**
550
+ * Merge consecutive user messages in the conversation.
551
+ * This is required because Anthropic's API expects all tool_result blocks
552
+ * from a single assistant turn to be in one user message.
553
+ * When multiple tool results are added as separate user messages,
554
+ * we need to merge them before sending to the API.
555
+ */
556
+ function mergeConsecutiveUserMessages(messages) {
557
+ if (messages.length === 0)
558
+ return [];
559
+ // Check if any merging is needed
560
+ const needsMerging = messages.some((msg, i) => i < messages.length - 1 &&
561
+ msg.role === 'user' &&
562
+ messages[i + 1].role === 'user');
563
+ if (!needsMerging) {
564
+ return messages;
565
+ }
566
+ const result = [];
567
+ let i = 0;
568
+ while (i < messages.length) {
569
+ const current = messages[i];
570
+ if (current.role === 'user') {
571
+ // Collect all consecutive user messages
572
+ const mergedContent = [];
573
+ while (i < messages.length && messages[i].role === 'user') {
574
+ const userMsg = messages[i];
575
+ if (Array.isArray(userMsg.content)) {
576
+ mergedContent.push(...userMsg.content);
577
+ }
578
+ else if (typeof userMsg.content === 'string') {
579
+ mergedContent.push({ type: 'text', text: userMsg.content });
580
+ }
581
+ i++;
582
+ }
583
+ result.push({
584
+ role: 'user',
585
+ content: mergedContent
586
+ });
587
+ }
588
+ else {
589
+ result.push(current);
590
+ i++;
591
+ }
592
+ }
593
+ return result;
594
+ }
595
+ /**
596
+ * Update the conversation messages
597
+ * @param prompt
598
+ * @param response
599
+ * @returns
600
+ */
601
+ function updateConversation(conversation, prompt) {
602
+ const baseSystemMessages = conversation?.system || [];
603
+ const baseMessages = conversation?.messages || [];
604
+ const system = baseSystemMessages.concat(prompt.system || []);
605
+ // Merge consecutive user messages to ensure tool_result blocks are properly grouped
606
+ const mergedMessages = mergeConsecutiveUserMessages(baseMessages.concat(prompt.messages || []));
607
+ return {
608
+ messages: mergedMessages,
609
+ system: system.length > 0 ? system : undefined // If system is empty, set to undefined
610
+ };
611
+ }
612
+ /**
613
+ * Sanitize messages by removing empty text blocks.
614
+ * Claude API rejects messages with empty text content blocks ("text content blocks must be non-empty").
615
+ * This handles cases where streaming was interrupted and left empty text blocks.
616
+ *
617
+ * - Filters out empty text blocks from each message's content
618
+ * - Removes messages entirely if they have no content after filtering
619
+ */
620
+ function sanitizeMessages(messages) {
621
+ const result = [];
622
+ for (const message of messages) {
623
+ if (typeof message.content === 'string') {
624
+ // String content - keep only if non-empty
625
+ if (message.content.trim()) {
626
+ result.push(message);
627
+ }
628
+ continue;
629
+ }
630
+ // Array content - filter out empty text blocks
631
+ const filteredContent = message.content.filter(block => {
632
+ if (block.type === 'text') {
633
+ return block.text && block.text.trim().length > 0;
634
+ }
635
+ // Keep all non-text blocks (tool_use, tool_result, image, etc.)
636
+ return true;
637
+ });
638
+ // Only include message if it has content after filtering
639
+ if (filteredContent.length > 0) {
640
+ result.push({
641
+ ...message,
642
+ content: filteredContent
643
+ });
644
+ }
645
+ }
646
+ return result;
647
+ }
648
+ /**
649
+ * Fix orphaned tool_use blocks in the conversation.
650
+ * @exported for testing
651
+ *
652
+ * When an agent is stopped mid-tool-execution, the assistant message contains tool_use blocks
653
+ * but no corresponding tool_result was added. The Anthropic API requires that every tool_use
654
+ * must be followed by a tool_result in the next user message.
655
+ *
656
+ * This function detects such cases and injects synthetic tool_result blocks indicating
657
+ * the tools were interrupted, allowing the conversation to continue.
658
+ */
659
+ function fixOrphanedToolUse(messages) {
660
+ if (messages.length < 2)
661
+ return messages;
662
+ const result = [];
663
+ for (let i = 0; i < messages.length; i++) {
664
+ const current = messages[i];
665
+ result.push(current);
666
+ // Check if this is an assistant message with tool_use blocks
667
+ if (current.role === 'assistant' && Array.isArray(current.content)) {
668
+ const toolUseBlocks = current.content.filter((block) => block.type === 'tool_use');
669
+ if (toolUseBlocks.length > 0) {
670
+ // Check if the next message is a user message with matching tool_results
671
+ const nextMessage = messages[i + 1];
672
+ if (nextMessage && nextMessage.role === 'user' && Array.isArray(nextMessage.content)) {
673
+ // Get tool_result IDs from the next message
674
+ const toolResultIds = new Set(nextMessage.content
675
+ .filter((block) => block.type === 'tool_result')
676
+ .map(block => block.tool_use_id));
677
+ // Find orphaned tool_use blocks (no matching tool_result)
678
+ const orphanedToolUse = toolUseBlocks.filter(block => !toolResultIds.has(block.id));
679
+ if (orphanedToolUse.length > 0) {
680
+ // Inject synthetic tool_results for orphaned tool_use
681
+ const syntheticResults = orphanedToolUse.map(block => ({
682
+ type: 'tool_result',
683
+ tool_use_id: block.id,
684
+ content: `[Tool interrupted: The user stopped the operation before "${block.name}" could execute.]`
685
+ }));
686
+ // Prepend synthetic results to the next user message
687
+ const updatedNextMessage = {
688
+ ...nextMessage,
689
+ content: [...syntheticResults, ...nextMessage.content]
690
+ };
691
+ // Replace the next message in our iteration
692
+ messages[i + 1] = updatedNextMessage;
693
+ }
694
+ }
695
+ else if (nextMessage && nextMessage.role === 'user') {
696
+ // Next message is a user message but not array content (plain text)
697
+ // We need to convert it and add tool_results
698
+ const syntheticResults = toolUseBlocks.map(block => ({
699
+ type: 'tool_result',
700
+ tool_use_id: block.id,
701
+ content: `[Tool interrupted: The user stopped the operation before "${block.name}" could execute.]`
702
+ }));
703
+ const textContent = typeof nextMessage.content === 'string'
704
+ ? { type: 'text', text: nextMessage.content }
705
+ : { type: 'text', text: '' };
706
+ const updatedNextMessage = {
707
+ role: 'user',
708
+ content: [...syntheticResults, textContent]
709
+ };
710
+ messages[i + 1] = updatedNextMessage;
711
+ }
712
+ // Note: If there's no nextMessage, we leave the conversation as-is.
713
+ // The tool_use blocks are expected to be there - the next turn will provide tool_results.
714
+ }
715
+ }
716
+ }
717
+ return result;
718
+ }
719
+ function getClaudePayload(options, prompt) {
720
+ const modelName = options.model; // Model name is already extracted in the calling methods
721
+ const model_options = options.model_options;
722
+ // Add beta header for Claude 3.7 models to enable 128k output tokens
723
+ let requestOptions = undefined;
724
+ if (modelName.includes('claude-3-7-sonnet') &&
725
+ ((model_options?.max_tokens ?? 0) > 64000 || (model_options?.thinking_budget_tokens ?? 0) > 64000)) {
726
+ requestOptions = {
727
+ headers: {
728
+ 'anthropic-beta': 'output-128k-2025-02-19'
729
+ }
730
+ };
731
+ }
732
+ // Fix orphaned tool_use blocks (can occur when agent is stopped mid-tool-execution)
733
+ const fixedMessages = fixOrphanedToolUse(prompt.messages);
734
+ // Sanitize messages to remove empty text blocks (can occur from interrupted streaming)
735
+ let sanitizedMessages = sanitizeMessages(fixedMessages);
736
+ // Validate tools have input_schema.type set to 'object' as required by the Anthropic SDK
737
+ if (options.tools) {
738
+ for (const tool of options.tools) {
739
+ if (tool.input_schema.type !== 'object') {
740
+ throw new Error(`Tool "${tool.name}" has invalid input_schema.type: expected "object", got "${tool.input_schema.type}"`);
741
+ }
742
+ }
743
+ }
744
+ // When no tools are provided but conversation contains tool_use/tool_result blocks
745
+ // (e.g. checkpoint summary calls), convert tool blocks to text to avoid API errors
746
+ const hasTools = options.tools && options.tools.length > 0;
747
+ if (!hasTools && claudeMessagesContainToolBlocks(sanitizedMessages)) {
748
+ sanitizedMessages = convertClaudeToolBlocksToText(sanitizedMessages);
749
+ }
750
+ const payload = {
751
+ messages: sanitizedMessages,
752
+ system: prompt.system,
753
+ tools: hasTools ? options.tools : undefined,
754
+ temperature: model_options?.temperature,
755
+ model: modelName,
756
+ max_tokens: maxToken(options),
757
+ top_p: model_options?.temperature != null ? undefined : model_options?.top_p,
758
+ top_k: model_options?.top_k,
759
+ stop_sequences: model_options?.stop_sequence,
760
+ thinking: model_options?.thinking_mode ?
761
+ {
762
+ budget_tokens: model_options?.thinking_budget_tokens ?? 1024,
763
+ type: "enabled"
764
+ } : {
765
+ type: "disabled"
766
+ }
767
+ };
768
+ return { payload, requestOptions };
769
+ }
770
+ /**
771
+ * Checks whether any Claude message contains tool_use or tool_result content blocks.
772
+ */
773
+ function claudeMessagesContainToolBlocks(messages) {
774
+ for (const msg of messages) {
775
+ if (!Array.isArray(msg.content))
776
+ continue;
777
+ for (const block of msg.content) {
778
+ if (typeof block === 'object' && block !== null && 'type' in block) {
779
+ if (block.type === 'tool_use' || block.type === 'tool_result')
780
+ return true;
781
+ }
782
+ }
783
+ }
784
+ return false;
785
+ }
786
+ /**
787
+ * Converts tool_use and tool_result blocks to text in Claude messages.
788
+ * Preserves tool call information while removing structured blocks that
789
+ * require tools to be defined in the API request.
790
+ */
791
+ function convertClaudeToolBlocksToText(messages) {
792
+ return messages.map(msg => {
793
+ if (!Array.isArray(msg.content))
794
+ return msg;
795
+ let hasToolBlocks = false;
796
+ for (const block of msg.content) {
797
+ if (typeof block === 'object' && block !== null && 'type' in block &&
798
+ (block.type === 'tool_use' || block.type === 'tool_result')) {
799
+ hasToolBlocks = true;
800
+ break;
801
+ }
802
+ }
803
+ if (!hasToolBlocks)
804
+ return msg;
805
+ const newContent = [];
806
+ for (const block of msg.content) {
807
+ if (typeof block === 'string') {
808
+ newContent.push(block);
809
+ continue;
810
+ }
811
+ if (block.type === 'tool_use') {
812
+ const inputStr = block.input ? JSON.stringify(block.input) : '';
813
+ const truncated = inputStr.length > 500 ? inputStr.substring(0, 500) + '...' : inputStr;
814
+ newContent.push({
815
+ type: 'text',
816
+ text: `[Tool call: ${block.name}(${truncated})]`,
817
+ });
818
+ }
819
+ else if (block.type === 'tool_result') {
820
+ let resultStr = 'No content';
821
+ if (typeof block.content === 'string') {
822
+ resultStr = block.content.length > 500 ? block.content.substring(0, 500) + '...' : block.content;
823
+ }
824
+ else if (Array.isArray(block.content)) {
825
+ const texts = block.content
826
+ .filter((c) => c.type === 'text')
827
+ .map(c => c.text.length > 500 ? c.text.substring(0, 500) + '...' : c.text);
828
+ resultStr = texts.join('\n') || 'No text content';
829
+ }
830
+ newContent.push({
831
+ type: 'text',
832
+ text: `[Tool result: ${resultStr}]`,
833
+ });
834
+ }
835
+ else {
836
+ newContent.push(block);
837
+ }
838
+ }
839
+ return { ...msg, content: newContent };
840
+ });
841
+ }
842
+ //# sourceMappingURL=claude.js.map