@jerome-benoit/sap-ai-provider-v2 4.4.18 → 4.6.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 (31) hide show
  1. package/README.md +16 -9
  2. package/dist/{chunk-BBWFNLML.js → chunk-BV5Y6RMC.js} +3 -3
  3. package/dist/{chunk-O6ASHXPO.js → chunk-DBAIFR3B.js} +445 -321
  4. package/dist/{chunk-O6ASHXPO.js.map → chunk-DBAIFR3B.js.map} +1 -1
  5. package/dist/{chunk-SD6CRCHX.js → chunk-DQXZH6CW.js} +50 -43
  6. package/dist/chunk-DQXZH6CW.js.map +1 -0
  7. package/dist/{chunk-4CAVT2YC.js → chunk-KS5QNMYZ.js} +193 -146
  8. package/dist/chunk-KS5QNMYZ.js.map +1 -0
  9. package/dist/{chunk-3M6NFVCS.js → chunk-WYDTQDMJ.js} +11 -5
  10. package/dist/chunk-WYDTQDMJ.js.map +1 -0
  11. package/dist/{foundation-models-embedding-model-strategy-XJV5REFR.js → foundation-models-embedding-model-strategy-XLD3PGCK.js} +4 -4
  12. package/dist/{foundation-models-language-model-strategy-X6VNJZFG.js → foundation-models-language-model-strategy-SNBZBWX4.js} +12 -7
  13. package/dist/foundation-models-language-model-strategy-SNBZBWX4.js.map +1 -0
  14. package/dist/index.cjs +714 -509
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +19 -2
  17. package/dist/index.d.ts +19 -2
  18. package/dist/index.js +6 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/{orchestration-embedding-model-strategy-3GIX5HAR.js → orchestration-embedding-model-strategy-6BKD5NW7.js} +4 -4
  21. package/dist/{orchestration-language-model-strategy-GYBTFP2F.js → orchestration-language-model-strategy-OYSEPGQG.js} +23 -6
  22. package/dist/orchestration-language-model-strategy-OYSEPGQG.js.map +1 -0
  23. package/package.json +20 -20
  24. package/dist/chunk-3M6NFVCS.js.map +0 -1
  25. package/dist/chunk-4CAVT2YC.js.map +0 -1
  26. package/dist/chunk-SD6CRCHX.js.map +0 -1
  27. package/dist/foundation-models-language-model-strategy-X6VNJZFG.js.map +0 -1
  28. package/dist/orchestration-language-model-strategy-GYBTFP2F.js.map +0 -1
  29. /package/dist/{chunk-BBWFNLML.js.map → chunk-BV5Y6RMC.js.map} +0 -0
  30. /package/dist/{foundation-models-embedding-model-strategy-XJV5REFR.js.map → foundation-models-embedding-model-strategy-XLD3PGCK.js.map} +0 -0
  31. /package/dist/{orchestration-embedding-model-strategy-3GIX5HAR.js.map → orchestration-embedding-model-strategy-6BKD5NW7.js.map} +0 -0
@@ -97,51 +97,34 @@ function convertToSAPMessages(prompt, options = {}) {
97
97
  for (const part of message.content) {
98
98
  switch (part.type) {
99
99
  case "file": {
100
- if (!part.mediaType.startsWith("image/")) {
101
- throw new UnsupportedFunctionalityError({
102
- functionality: "Only image files are supported"
100
+ const fileDataUrl = buildDataUrl(part);
101
+ if (part.mediaType.startsWith("image/")) {
102
+ const supportedFormats = [
103
+ "image/png",
104
+ "image/jpeg",
105
+ "image/jpg",
106
+ "image/gif",
107
+ "image/webp"
108
+ ];
109
+ if (!supportedFormats.includes(part.mediaType.toLowerCase())) {
110
+ console.warn(
111
+ `Image format ${part.mediaType} may not be supported by all models. Recommended formats: PNG, JPEG, GIF, WebP`
112
+ );
113
+ }
114
+ contentParts.push({
115
+ image_url: {
116
+ url: fileDataUrl
117
+ },
118
+ type: "image_url"
103
119
  });
104
- }
105
- const supportedFormats = [
106
- "image/png",
107
- "image/jpeg",
108
- "image/jpg",
109
- "image/gif",
110
- "image/webp"
111
- ];
112
- if (!supportedFormats.includes(part.mediaType.toLowerCase())) {
113
- console.warn(
114
- `Image format ${part.mediaType} may not be supported by all models. Recommended formats: PNG, JPEG, GIF, WebP`
115
- );
116
- }
117
- let imageUrl;
118
- if (part.data instanceof URL) {
119
- imageUrl = part.data.toString();
120
- } else if (typeof part.data === "string") {
121
- imageUrl = `data:${part.mediaType};base64,${part.data}`;
122
- } else if (part.data instanceof Uint8Array) {
123
- const base64Data = Buffer.from(part.data).toString("base64");
124
- imageUrl = `data:${part.mediaType};base64,${base64Data}`;
125
- } else if (Buffer.isBuffer(part.data)) {
126
- const base64Data = Buffer.from(part.data).toString("base64");
127
- imageUrl = `data:${part.mediaType};base64,${base64Data}`;
128
120
  } else {
129
- const maybeBufferLike = part.data;
130
- if (maybeBufferLike !== null && typeof maybeBufferLike === "object" && "toString" in maybeBufferLike) {
131
- const base64Data = maybeBufferLike.toString("base64");
132
- imageUrl = `data:${part.mediaType};base64,${base64Data}`;
133
- } else {
134
- throw new UnsupportedFunctionalityError({
135
- functionality: "Unsupported file data type for image. Expected URL, base64 string, or Uint8Array."
136
- });
137
- }
121
+ contentParts.push({
122
+ file: {
123
+ file_data: fileDataUrl
124
+ },
125
+ type: "file"
126
+ });
138
127
  }
139
- contentParts.push({
140
- image_url: {
141
- url: imageUrl
142
- },
143
- type: "image_url"
144
- });
145
128
  break;
146
129
  }
147
130
  case "text": {
@@ -188,10 +171,34 @@ function unescapeOrchestrationPlaceholders(text) {
188
171
  if (!text) return text;
189
172
  return text.replaceAll(JINJA2_DELIMITERS_ESCAPED_PATTERN, "{$1");
190
173
  }
174
+ function buildDataUrl(part) {
175
+ if (part.data instanceof URL) {
176
+ return part.data.toString();
177
+ }
178
+ if (typeof part.data === "string") {
179
+ return `data:${part.mediaType};base64,${part.data}`;
180
+ }
181
+ if (part.data instanceof Uint8Array) {
182
+ const base64Data = Buffer.from(part.data).toString("base64");
183
+ return `data:${part.mediaType};base64,${base64Data}`;
184
+ }
185
+ if (Buffer.isBuffer(part.data)) {
186
+ const base64Data = Buffer.from(part.data).toString("base64");
187
+ return `data:${part.mediaType};base64,${base64Data}`;
188
+ }
189
+ const maybeBufferLike = part.data;
190
+ if (maybeBufferLike !== null && typeof maybeBufferLike === "object" && "toString" in maybeBufferLike) {
191
+ const base64Data = maybeBufferLike.toString("base64");
192
+ return `data:${part.mediaType};base64,${base64Data}`;
193
+ }
194
+ throw new UnsupportedFunctionalityError({
195
+ functionality: "Unsupported file data type. Expected URL, base64 string, or Uint8Array."
196
+ });
197
+ }
191
198
 
192
199
  export {
193
200
  convertToSAPMessages,
194
201
  escapeOrchestrationPlaceholders,
195
202
  unescapeOrchestrationPlaceholders
196
203
  };
197
- //# sourceMappingURL=chunk-SD6CRCHX.js.map
204
+ //# sourceMappingURL=chunk-DQXZH6CW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/convert-to-sap-messages.ts"],"sourcesContent":["import type {\n AssistantChatMessage,\n ChatMessage,\n SystemChatMessage,\n ToolChatMessage,\n UserChatMessage,\n} from \"@sap-ai-sdk/orchestration\";\n\nimport {\n InvalidPromptError,\n LanguageModelV3Prompt,\n UnsupportedFunctionalityError,\n} from \"@ai-sdk/provider\";\nimport { Buffer } from \"node:buffer\";\n\n/**\n * Options for converting Vercel AI SDK prompts to SAP AI SDK messages.\n * @see {@link convertToSAPMessages}\n */\nexport interface ConvertToSAPMessagesOptions {\n /**\n * Whether to escape Jinja2 template delimiters (`{{`, `{%`, `{#`) in message content.\n * This prevents SAP orchestration from interpreting user content as template syntax.\n * @default true\n */\n readonly escapeTemplatePlaceholders?: boolean;\n /**\n * Whether to include assistant reasoning parts (wrapped in `<think>` tags).\n * @default false\n */\n readonly includeReasoning?: boolean;\n}\n\n/**\n * @internal\n */\nconst ZERO_WIDTH_SPACE = \"\\u200B\";\n\n/**\n * Safely serializes a value to JSON string, handling edge cases that would cause JSON.stringify to throw.\n *\n * Handles:\n * - Circular references (objects that reference themselves)\n * - BigInt values (converted to string representation)\n * - Undefined values and symbols (handled by JSON.stringify's default behavior)\n * @param value - The value to serialize.\n * @returns JSON string representation, or a fallback string representation if serialization fails.\n * @internal\n */\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value, (_key, val) =>\n typeof val === \"bigint\" ? val.toString() : (val as unknown),\n );\n } catch {\n return String(value);\n }\n}\n\n/**\n * @internal\n */\nconst JINJA2_DELIMITERS_PATTERN = /\\{(?=[{%#])/g;\n\n/**\n * @internal\n */\nconst JINJA2_DELIMITERS_ESCAPED_PATTERN = new RegExp(`\\\\{${ZERO_WIDTH_SPACE}([{%#])`, \"g\");\n\n/**\n * @internal\n */\ninterface UserContentItem {\n readonly file?: {\n readonly file_data: string;\n };\n readonly image_url?: {\n readonly url: string;\n };\n readonly text?: string;\n readonly type: \"file\" | \"image_url\" | \"text\";\n}\n\n/**\n * Converts Vercel AI SDK prompt to SAP AI SDK ChatMessage array.\n *\n * Handles all Vercel AI SDK message types:\n * - `system` → `SystemChatMessage`\n * - `user` (text/images) → `UserChatMessage`\n * - `assistant` (text/tool-calls) → `AssistantChatMessage`\n * - `tool` (tool results) → `ToolChatMessage`\n * @param prompt - The Vercel AI SDK LanguageModelV3Prompt to convert.\n * @param options - Conversion options.\n * @param options.escapeTemplatePlaceholders - Whether to escape Jinja2 template delimiters (default: true).\n * @param options.includeReasoning - Whether to include assistant reasoning parts (default: false).\n * @returns SAP AI SDK ChatMessage array ready for orchestration requests.\n * @throws {UnsupportedFunctionalityError} When encountering unsupported content types or file formats.\n * @throws {InvalidPromptError} When encountering unsupported message roles.\n */\nexport function convertToSAPMessages(\n prompt: LanguageModelV3Prompt,\n options: ConvertToSAPMessagesOptions = {},\n): ChatMessage[] {\n const messages: ChatMessage[] = [];\n const includeReasoning = options.includeReasoning ?? false;\n const escapeTemplatePlaceholders = options.escapeTemplatePlaceholders ?? true;\n\n const maybeEscape = (text: string): string =>\n escapeTemplatePlaceholders ? escapeOrchestrationPlaceholders(text) : text;\n\n for (const message of prompt) {\n switch (message.role) {\n case \"assistant\": {\n let text = \"\";\n const toolCalls: {\n function: { arguments: string; name: string };\n id: string;\n type: \"function\";\n }[] = [];\n\n for (const part of message.content) {\n switch (part.type) {\n case \"reasoning\": {\n if (includeReasoning && part.text) {\n text += `<think>${maybeEscape(part.text)}</think>`;\n }\n break;\n }\n case \"text\": {\n text += maybeEscape(part.text);\n break;\n }\n case \"tool-call\": {\n // Normalize tool call input to JSON string (Vercel AI SDK provides strings or objects)\n let argumentsJson: string;\n if (typeof part.input === \"string\") {\n argumentsJson = part.input;\n } else {\n argumentsJson = JSON.stringify(part.input);\n }\n\n // Escape tool call arguments if needed (they may contain placeholder syntax)\n toolCalls.push({\n function: {\n arguments: maybeEscape(argumentsJson),\n name: part.toolName,\n },\n id: part.toolCallId,\n type: \"function\",\n });\n break;\n }\n }\n }\n\n if (text || toolCalls.length > 0) {\n const assistantMessage: AssistantChatMessage = {\n content: text,\n role: \"assistant\",\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n };\n messages.push(assistantMessage);\n }\n break;\n }\n\n case \"system\": {\n const systemMessage: SystemChatMessage = {\n content: maybeEscape(message.content),\n role: \"system\",\n };\n messages.push(systemMessage);\n break;\n }\n\n case \"tool\": {\n for (const part of message.content) {\n if (part.type === \"tool-result\") {\n const serializedOutput = safeJsonStringify(part.output);\n const toolMessage: ToolChatMessage = {\n content: maybeEscape(serializedOutput),\n role: \"tool\",\n tool_call_id: part.toolCallId,\n };\n messages.push(toolMessage);\n }\n }\n break;\n }\n\n case \"user\": {\n const contentParts: UserContentItem[] = [];\n\n for (const part of message.content) {\n switch (part.type) {\n case \"file\": {\n const fileDataUrl = buildDataUrl(part);\n\n if (part.mediaType.startsWith(\"image/\")) {\n const supportedFormats = [\n \"image/png\",\n \"image/jpeg\",\n \"image/jpg\",\n \"image/gif\",\n \"image/webp\",\n ];\n if (!supportedFormats.includes(part.mediaType.toLowerCase())) {\n console.warn(\n `Image format ${part.mediaType} may not be supported by all models. ` +\n `Recommended formats: PNG, JPEG, GIF, WebP`,\n );\n }\n\n contentParts.push({\n image_url: {\n url: fileDataUrl,\n },\n type: \"image_url\",\n });\n } else {\n contentParts.push({\n file: {\n file_data: fileDataUrl,\n },\n type: \"file\",\n });\n }\n break;\n }\n case \"text\": {\n contentParts.push({\n text: maybeEscape(part.text),\n type: \"text\",\n });\n break;\n }\n default: {\n throw new UnsupportedFunctionalityError({\n functionality: `Content type ${(part as { type: string }).type}`,\n });\n }\n }\n }\n\n const firstPart = contentParts[0];\n const userMessage: UserChatMessage =\n contentParts.length === 1 && firstPart?.type === \"text\"\n ? {\n content: firstPart.text ?? \"\",\n role: \"user\",\n }\n : {\n content: contentParts as UserChatMessage[\"content\"],\n role: \"user\",\n };\n\n messages.push(userMessage);\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = message;\n throw new InvalidPromptError({\n message: `Unsupported role: ${(_exhaustiveCheck as { role: string }).role}`,\n prompt: JSON.stringify(message),\n });\n }\n }\n }\n\n return messages;\n}\n\n/**\n * Escapes Jinja2 template delimiters by inserting zero-width spaces.\n *\n * Converts `{{`, `{%`, `{#` to `{\\u200B{`, `{\\u200B%`, `{\\u200B#` respectively.\n * This prevents SAP orchestration from interpreting user content as template syntax.\n * @param text - The text to escape.\n * @returns The escaped text with zero-width spaces inserted.\n * @see {@link unescapeOrchestrationPlaceholders} for the reverse operation.\n */\nexport function escapeOrchestrationPlaceholders(text: string): string {\n if (!text) return text;\n return text.replaceAll(JINJA2_DELIMITERS_PATTERN, `{${ZERO_WIDTH_SPACE}`);\n}\n\n/**\n * Reverses escaping by removing zero-width spaces from template delimiters.\n *\n * Useful for processing model responses that may contain escaped delimiters.\n * @param text - The text to unescape.\n * @returns The unescaped text with zero-width spaces removed.\n * @see {@link escapeOrchestrationPlaceholders} for the escaping operation.\n */\nexport function unescapeOrchestrationPlaceholders(text: string): string {\n if (!text) return text;\n return text.replaceAll(JINJA2_DELIMITERS_ESCAPED_PATTERN, \"{$1\");\n}\n\n/**\n * Builds a data URL from a file part's data and media type.\n *\n * Supports URL, base64 string, Uint8Array, Buffer, and buffer-like objects.\n * @internal\n * @param part - The file part containing data and mediaType.\n * @param part.data - The file data as URL, base64 string, or Uint8Array.\n * @param part.mediaType - The MIME type of the file.\n * @returns The data URL string.\n * @throws {UnsupportedFunctionalityError} If the data type is not supported.\n */\nfunction buildDataUrl(part: { data: string | Uint8Array | URL; mediaType: string }): string {\n if (part.data instanceof URL) {\n return part.data.toString();\n }\n\n if (typeof part.data === \"string\") {\n return `data:${part.mediaType};base64,${part.data}`;\n }\n\n if (part.data instanceof Uint8Array) {\n const base64Data = Buffer.from(part.data).toString(\"base64\");\n return `data:${part.mediaType};base64,${base64Data}`;\n }\n\n if (Buffer.isBuffer(part.data)) {\n const base64Data = Buffer.from(part.data).toString(\"base64\");\n return `data:${part.mediaType};base64,${base64Data}`;\n }\n\n const maybeBufferLike = part.data as unknown;\n\n if (\n maybeBufferLike !== null &&\n typeof maybeBufferLike === \"object\" &&\n \"toString\" in (maybeBufferLike as Record<string, unknown>)\n ) {\n const base64Data = (\n maybeBufferLike as {\n toString: (encoding?: string) => string;\n }\n ).toString(\"base64\");\n return `data:${part.mediaType};base64,${base64Data}`;\n }\n\n throw new UnsupportedFunctionalityError({\n functionality: \"Unsupported file data type. Expected URL, base64 string, or Uint8Array.\",\n });\n}\n"],"mappings":"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","names":[]}
@@ -4,11 +4,11 @@ import {
4
4
  getProviderName,
5
5
  sapAIEmbeddingProviderOptions,
6
6
  validateModelParamsWithWarnings
7
- } from "./chunk-O6ASHXPO.js";
7
+ } from "./chunk-DBAIFR3B.js";
8
8
 
9
9
  // src/strategy-utils.ts
10
10
  import { TooManyEmbeddingValuesForCallError } from "@ai-sdk/provider";
11
- import { parseProviderOptions } from "@ai-sdk/provider-utils";
11
+ import { convertAsyncIteratorToReadableStream, parseProviderOptions } from "@ai-sdk/provider-utils";
12
12
  import { z } from "zod";
13
13
  var StreamIdGenerator = class {
14
14
  /**
@@ -64,6 +64,19 @@ function buildGenerateResult(config) {
64
64
  tokenUsage,
65
65
  toolCalls
66
66
  };
67
+ const citations = response.getCitations?.();
68
+ if (citations?.length) {
69
+ for (const citation of citations) {
70
+ content.push({
71
+ id: String(citation.ref_id ?? citation.url),
72
+ sourceType: "url",
73
+ title: citation.title,
74
+ type: "source",
75
+ url: citation.url
76
+ });
77
+ }
78
+ }
79
+ const intermediateFailures = response.getIntermediateFailures?.();
67
80
  return {
68
81
  content,
69
82
  finishReason,
@@ -71,6 +84,7 @@ function buildGenerateResult(config) {
71
84
  [providerName]: {
72
85
  finishReason: finishReasonRaw ?? "unknown",
73
86
  finishReasonMapped: finishReason,
87
+ ...intermediateFailures?.length ? { intermediateFailures } : {},
74
88
  ...typeof responseHeaders?.["x-request-id"] === "string" ? { requestId: responseHeaders["x-request-id"] } : {},
75
89
  version
76
90
  }
@@ -81,19 +95,20 @@ function buildGenerateResult(config) {
81
95
  response: {
82
96
  body: rawResponseBody,
83
97
  headers: responseHeaders,
98
+ id: response.responseId,
84
99
  modelId,
85
100
  timestamp: /* @__PURE__ */ new Date()
86
101
  },
87
102
  usage: {
88
103
  inputTokens: {
89
- cacheRead: void 0,
104
+ cacheRead: tokenUsage?.prompt_tokens_details?.cached_tokens,
90
105
  cacheWrite: void 0,
91
- noCache: tokenUsage?.prompt_tokens,
106
+ noCache: tokenUsage?.prompt_tokens_details?.cached_tokens != null ? (tokenUsage.prompt_tokens ?? 0) - tokenUsage.prompt_tokens_details.cached_tokens : tokenUsage?.prompt_tokens,
92
107
  total: tokenUsage?.prompt_tokens
93
108
  },
94
109
  outputTokens: {
95
- reasoning: void 0,
96
- text: tokenUsage?.completion_tokens,
110
+ reasoning: tokenUsage?.completion_tokens_details?.reasoning_tokens,
111
+ text: tokenUsage?.completion_tokens_details?.reasoning_tokens != null ? (tokenUsage.completion_tokens ?? 0) - tokenUsage.completion_tokens_details.reasoning_tokens : tokenUsage?.completion_tokens,
97
112
  total: tokenUsage?.completion_tokens
98
113
  }
99
114
  },
@@ -269,9 +284,12 @@ function createStreamTransformer(config) {
269
284
  modelId,
270
285
  options,
271
286
  providerName,
287
+ responseHeaders,
272
288
  responseId,
273
289
  sdkStream,
290
+ streamResponseGetCitations,
274
291
  streamResponseGetFinishReason,
292
+ streamResponseGetIntermediateFailures,
275
293
  streamResponseGetTokenUsage,
276
294
  url,
277
295
  version,
@@ -280,131 +298,11 @@ function createStreamTransformer(config) {
280
298
  let textBlockId = null;
281
299
  const streamState = createInitialStreamState();
282
300
  const toolCallsInProgress = /* @__PURE__ */ new Map();
283
- return new ReadableStream({
284
- cancel() {
285
- },
286
- async start(controller) {
287
- controller.enqueue({
288
- type: "stream-start",
289
- warnings: [...warnings]
290
- });
291
- try {
292
- for await (const chunk of sdkStream) {
293
- if (includeRawChunks) {
294
- controller.enqueue({
295
- rawValue: chunk._data ?? chunk,
296
- type: "raw"
297
- });
298
- }
299
- if (streamState.isFirstChunk) {
300
- streamState.isFirstChunk = false;
301
- controller.enqueue({
302
- id: responseId,
303
- modelId,
304
- timestamp: /* @__PURE__ */ new Date(),
305
- type: "response-metadata"
306
- });
307
- }
308
- const deltaToolCalls = chunk.getDeltaToolCalls();
309
- if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
310
- streamState.finishReason = {
311
- raw: void 0,
312
- unified: "tool-calls"
313
- };
314
- }
315
- const deltaContent = chunk.getDeltaContent();
316
- if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason.unified !== "tool-calls") {
317
- if (!streamState.activeText) {
318
- textBlockId = idGenerator.generateTextBlockId();
319
- controller.enqueue({ id: textBlockId, type: "text-start" });
320
- streamState.activeText = true;
321
- }
322
- if (textBlockId) {
323
- controller.enqueue({
324
- delta: deltaContent,
325
- id: textBlockId,
326
- type: "text-delta"
327
- });
328
- }
329
- }
330
- if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
331
- for (const toolCallChunk of deltaToolCalls) {
332
- const index = toolCallChunk.index;
333
- if (typeof index !== "number" || !Number.isFinite(index)) {
334
- continue;
335
- }
336
- if (!toolCallsInProgress.has(index)) {
337
- toolCallsInProgress.set(index, {
338
- arguments: "",
339
- didEmitCall: false,
340
- didEmitInputStart: false,
341
- id: toolCallChunk.id ?? `tool_${String(index)}`,
342
- toolName: toolCallChunk.function?.name
343
- });
344
- }
345
- const tc = toolCallsInProgress.get(index);
346
- if (!tc) continue;
347
- if (toolCallChunk.id) {
348
- tc.id = toolCallChunk.id;
349
- }
350
- const nextToolName = toolCallChunk.function?.name;
351
- if (typeof nextToolName === "string" && nextToolName.length > 0) {
352
- tc.toolName = nextToolName;
353
- }
354
- if (!tc.didEmitInputStart && tc.toolName) {
355
- tc.didEmitInputStart = true;
356
- controller.enqueue({
357
- id: tc.id,
358
- toolName: tc.toolName,
359
- type: "tool-input-start"
360
- });
361
- }
362
- const argumentsDelta = toolCallChunk.function?.arguments;
363
- if (typeof argumentsDelta === "string" && argumentsDelta.length > 0) {
364
- tc.arguments += argumentsDelta;
365
- if (tc.didEmitInputStart) {
366
- controller.enqueue({
367
- delta: argumentsDelta,
368
- id: tc.id,
369
- type: "tool-input-delta"
370
- });
371
- }
372
- }
373
- }
374
- }
375
- const chunkFinishReason = chunk.getFinishReason();
376
- if (chunkFinishReason) {
377
- streamState.finishReason = mapFinishReason(chunkFinishReason);
378
- if (streamState.finishReason.unified === "tool-calls") {
379
- const toolCalls2 = Array.from(toolCallsInProgress.values());
380
- for (const tc of toolCalls2) {
381
- if (tc.didEmitCall) {
382
- continue;
383
- }
384
- if (!tc.didEmitInputStart) {
385
- tc.didEmitInputStart = true;
386
- controller.enqueue({
387
- id: tc.id,
388
- toolName: tc.toolName ?? "",
389
- type: "tool-input-start"
390
- });
391
- }
392
- tc.didEmitCall = true;
393
- controller.enqueue({ id: tc.id, type: "tool-input-end" });
394
- controller.enqueue({
395
- input: tc.arguments,
396
- toolCallId: tc.id,
397
- toolName: tc.toolName ?? "",
398
- type: "tool-call"
399
- });
400
- }
401
- if (streamState.activeText && textBlockId) {
402
- controller.enqueue({ id: textBlockId, type: "text-end" });
403
- streamState.activeText = false;
404
- }
405
- }
406
- }
407
- }
301
+ return convertAsyncIteratorToReadableStream(
302
+ safeIterate(sdkStream)[Symbol.asyncIterator]()
303
+ ).pipeThrough(
304
+ new TransformStream({
305
+ flush(controller) {
408
306
  const toolCalls = Array.from(toolCallsInProgress.values());
409
307
  let didEmitAnyToolCalls = false;
410
308
  for (const tc of toolCalls) {
@@ -444,15 +342,34 @@ function createStreamTransformer(config) {
444
342
  const finalUsage = streamResponseGetTokenUsage();
445
343
  if (finalUsage) {
446
344
  streamState.usage.inputTokens.total = finalUsage.prompt_tokens;
447
- streamState.usage.inputTokens.noCache = finalUsage.prompt_tokens;
345
+ streamState.usage.inputTokens.cacheRead = finalUsage.prompt_tokens_details?.cached_tokens;
346
+ streamState.usage.inputTokens.noCache = finalUsage.prompt_tokens_details?.cached_tokens != null ? (finalUsage.prompt_tokens ?? 0) - finalUsage.prompt_tokens_details.cached_tokens : finalUsage.prompt_tokens;
448
347
  streamState.usage.outputTokens.total = finalUsage.completion_tokens;
449
- streamState.usage.outputTokens.text = finalUsage.completion_tokens;
348
+ streamState.usage.outputTokens.reasoning = finalUsage.completion_tokens_details?.reasoning_tokens;
349
+ streamState.usage.outputTokens.text = finalUsage.completion_tokens_details?.reasoning_tokens != null ? (finalUsage.completion_tokens ?? 0) - finalUsage.completion_tokens_details.reasoning_tokens : finalUsage.completion_tokens;
350
+ }
351
+ const streamCitations = streamResponseGetCitations?.();
352
+ if (streamCitations?.length) {
353
+ for (const citation of streamCitations) {
354
+ controller.enqueue({
355
+ id: String(citation.ref_id ?? citation.url),
356
+ sourceType: "url",
357
+ title: citation.title,
358
+ type: "source",
359
+ url: citation.url
360
+ });
361
+ }
450
362
  }
363
+ const streamIntermediateFailures = streamResponseGetIntermediateFailures?.();
451
364
  controller.enqueue({
452
365
  finishReason: streamState.finishReason,
453
366
  providerMetadata: {
454
367
  [providerName]: {
455
368
  finishReason: streamState.finishReason.raw,
369
+ ...streamIntermediateFailures?.length ? {
370
+ intermediateFailures: streamIntermediateFailures
371
+ } : {},
372
+ ...typeof responseHeaders?.["x-request-id"] === "string" ? { requestId: responseHeaders["x-request-id"] } : {},
456
373
  responseId,
457
374
  version
458
375
  }
@@ -460,21 +377,144 @@ function createStreamTransformer(config) {
460
377
  type: "finish",
461
378
  usage: streamState.usage
462
379
  });
463
- controller.close();
464
- } catch (error) {
465
- const aiError = convertToAISDKError(error, {
466
- operation: "doStream",
467
- requestBody: createAISDKRequestBodySummary(options),
468
- url
469
- });
380
+ },
381
+ start(controller) {
470
382
  controller.enqueue({
471
- error: aiError instanceof Error ? aiError : new Error(String(aiError)),
472
- type: "error"
383
+ type: "stream-start",
384
+ warnings: [...warnings]
473
385
  });
474
- controller.close();
386
+ },
387
+ transform(chunk, controller) {
388
+ if (chunk instanceof Error) {
389
+ const aiError = convertToAISDKError(chunk, {
390
+ operation: "doStream",
391
+ requestBody: createAISDKRequestBodySummary(options),
392
+ url
393
+ });
394
+ controller.enqueue({
395
+ error: aiError instanceof Error ? aiError : new Error(String(aiError)),
396
+ type: "error"
397
+ });
398
+ controller.terminate();
399
+ return;
400
+ }
401
+ if (includeRawChunks) {
402
+ controller.enqueue({
403
+ rawValue: chunk._data ?? chunk,
404
+ type: "raw"
405
+ });
406
+ }
407
+ if (streamState.isFirstChunk) {
408
+ streamState.isFirstChunk = false;
409
+ controller.enqueue({
410
+ id: responseId,
411
+ modelId,
412
+ timestamp: /* @__PURE__ */ new Date(),
413
+ type: "response-metadata"
414
+ });
415
+ }
416
+ const deltaToolCalls = chunk.getDeltaToolCalls();
417
+ if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
418
+ streamState.finishReason = {
419
+ raw: void 0,
420
+ unified: "tool-calls"
421
+ };
422
+ }
423
+ const deltaContent = chunk.getDeltaContent();
424
+ if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason.unified !== "tool-calls") {
425
+ if (!streamState.activeText) {
426
+ textBlockId = idGenerator.generateTextBlockId();
427
+ controller.enqueue({ id: textBlockId, type: "text-start" });
428
+ streamState.activeText = true;
429
+ }
430
+ if (textBlockId) {
431
+ controller.enqueue({
432
+ delta: deltaContent,
433
+ id: textBlockId,
434
+ type: "text-delta"
435
+ });
436
+ }
437
+ }
438
+ if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
439
+ for (const toolCallChunk of deltaToolCalls) {
440
+ const index = toolCallChunk.index;
441
+ if (typeof index !== "number" || !Number.isFinite(index)) {
442
+ continue;
443
+ }
444
+ if (!toolCallsInProgress.has(index)) {
445
+ toolCallsInProgress.set(index, {
446
+ arguments: "",
447
+ didEmitCall: false,
448
+ didEmitInputStart: false,
449
+ id: toolCallChunk.id ?? `tool_${String(index)}`,
450
+ toolName: toolCallChunk.function?.name
451
+ });
452
+ }
453
+ const tc = toolCallsInProgress.get(index);
454
+ if (!tc) continue;
455
+ if (toolCallChunk.id) {
456
+ tc.id = toolCallChunk.id;
457
+ }
458
+ const nextToolName = toolCallChunk.function?.name;
459
+ if (typeof nextToolName === "string" && nextToolName.length > 0) {
460
+ tc.toolName = nextToolName;
461
+ }
462
+ if (!tc.didEmitInputStart && tc.toolName) {
463
+ tc.didEmitInputStart = true;
464
+ controller.enqueue({
465
+ id: tc.id,
466
+ toolName: tc.toolName,
467
+ type: "tool-input-start"
468
+ });
469
+ }
470
+ const argumentsDelta = toolCallChunk.function?.arguments;
471
+ if (typeof argumentsDelta === "string" && argumentsDelta.length > 0) {
472
+ tc.arguments += argumentsDelta;
473
+ if (tc.didEmitInputStart) {
474
+ controller.enqueue({
475
+ delta: argumentsDelta,
476
+ id: tc.id,
477
+ type: "tool-input-delta"
478
+ });
479
+ }
480
+ }
481
+ }
482
+ }
483
+ const chunkFinishReason = chunk.getFinishReason();
484
+ if (chunkFinishReason) {
485
+ streamState.finishReason = mapFinishReason(chunkFinishReason);
486
+ if (streamState.finishReason.unified === "tool-calls") {
487
+ const toolCalls = Array.from(toolCallsInProgress.values());
488
+ for (const tc of toolCalls) {
489
+ if (tc.didEmitCall) {
490
+ continue;
491
+ }
492
+ if (!tc.didEmitInputStart) {
493
+ tc.didEmitInputStart = true;
494
+ controller.enqueue({
495
+ id: tc.id,
496
+ toolName: tc.toolName ?? "",
497
+ type: "tool-input-start"
498
+ });
499
+ }
500
+ tc.didEmitCall = true;
501
+ controller.enqueue({ id: tc.id, type: "tool-input-end" });
502
+ controller.enqueue({
503
+ input: tc.arguments,
504
+ toolCallId: tc.id,
505
+ toolName: tc.toolName ?? "",
506
+ type: "tool-call"
507
+ });
508
+ }
509
+ if (streamState.activeText && textBlockId) {
510
+ controller.enqueue({ id: textBlockId, type: "text-end" });
511
+ streamState.activeText = false;
512
+ }
513
+ }
514
+ }
475
515
  }
476
- }
477
- });
516
+ })
517
+ );
478
518
  }
479
519
  function extractResponseContent(response) {
480
520
  const content = [];
@@ -617,6 +657,13 @@ async function prepareEmbeddingCall(config, options) {
617
657
  }
618
658
  return { embeddingOptions: sapOptions, providerName };
619
659
  }
660
+ async function* safeIterate(iterable) {
661
+ try {
662
+ yield* iterable;
663
+ } catch (error) {
664
+ yield error instanceof Error ? error : new Error(String(error));
665
+ }
666
+ }
620
667
 
621
668
  export {
622
669
  StreamIdGenerator,
@@ -633,4 +680,4 @@ export {
633
680
  normalizeEmbedding,
634
681
  prepareEmbeddingCall
635
682
  };
636
- //# sourceMappingURL=chunk-4CAVT2YC.js.map
683
+ //# sourceMappingURL=chunk-KS5QNMYZ.js.map