@jerome-benoit/sap-ai-provider 4.5.0 → 4.6.1

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 (33) hide show
  1. package/dist/{chunk-SD6CRCHX.js → chunk-DQXZH6CW.js} +50 -43
  2. package/dist/chunk-DQXZH6CW.js.map +1 -0
  3. package/dist/{chunk-SC6SVJGO.js → chunk-HJZCRF7J.js} +11 -4
  4. package/dist/chunk-HJZCRF7J.js.map +1 -0
  5. package/dist/{chunk-T2KXS7WW.js → chunk-I5ZTK7M5.js} +64 -64
  6. package/dist/{chunk-T2KXS7WW.js.map → chunk-I5ZTK7M5.js.map} +1 -1
  7. package/dist/{chunk-NRLDO6VY.js → chunk-IAXILSPQ.js} +6 -4
  8. package/dist/chunk-IAXILSPQ.js.map +1 -0
  9. package/dist/{chunk-3VLXFYCM.js → chunk-U2HDUNO7.js} +55 -19
  10. package/dist/chunk-U2HDUNO7.js.map +1 -0
  11. package/dist/{foundation-models-embedding-model-strategy-FO5RWBZ2.js → foundation-models-embedding-model-strategy-C4SXGZ66.js} +5 -10
  12. package/dist/foundation-models-embedding-model-strategy-C4SXGZ66.js.map +1 -0
  13. package/dist/{foundation-models-language-model-strategy-COZPNAJ3.js → foundation-models-language-model-strategy-3D4V6PSZ.js} +6 -6
  14. package/dist/foundation-models-language-model-strategy-3D4V6PSZ.js.map +1 -0
  15. package/dist/index.cjs +186 -138
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +6 -6
  20. package/dist/index.js.map +1 -1
  21. package/dist/{orchestration-embedding-model-strategy-GEONA32Q.js → orchestration-embedding-model-strategy-7FY6NJD7.js} +5 -10
  22. package/dist/orchestration-embedding-model-strategy-7FY6NJD7.js.map +1 -0
  23. package/dist/{orchestration-language-model-strategy-PZBP7G5O.js → orchestration-language-model-strategy-FI5HLHWP.js} +9 -5
  24. package/dist/orchestration-language-model-strategy-FI5HLHWP.js.map +1 -0
  25. package/package.json +14 -14
  26. package/dist/chunk-3VLXFYCM.js.map +0 -1
  27. package/dist/chunk-NRLDO6VY.js.map +0 -1
  28. package/dist/chunk-SC6SVJGO.js.map +0 -1
  29. package/dist/chunk-SD6CRCHX.js.map +0 -1
  30. package/dist/foundation-models-embedding-model-strategy-FO5RWBZ2.js.map +0 -1
  31. package/dist/foundation-models-language-model-strategy-COZPNAJ3.js.map +0 -1
  32. package/dist/orchestration-embedding-model-strategy-GEONA32Q.js.map +0 -1
  33. package/dist/orchestration-language-model-strategy-PZBP7G5O.js.map +0 -1
@@ -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":[]}
@@ -2,11 +2,12 @@ import {createRequire as __createRequire} from 'module';var require=__createRequ
2
2
  import {
3
3
  buildEmbeddingResult,
4
4
  prepareEmbeddingCall
5
- } from "./chunk-3VLXFYCM.js";
5
+ } from "./chunk-U2HDUNO7.js";
6
6
  import {
7
7
  VERSION,
8
- convertToAISDKError
9
- } from "./chunk-T2KXS7WW.js";
8
+ convertToAISDKError,
9
+ deepMerge
10
+ } from "./chunk-I5ZTK7M5.js";
10
11
 
11
12
  // src/base-embedding-model-strategy.ts
12
13
  var BaseEmbeddingModelStrategy = class {
@@ -46,9 +47,15 @@ var BaseEmbeddingModelStrategy = class {
46
47
  });
47
48
  }
48
49
  }
50
+ mergeModelParams(settings, embeddingOptions) {
51
+ return deepMerge(
52
+ settings.modelParams ?? {},
53
+ embeddingOptions?.modelParams ?? {}
54
+ );
55
+ }
49
56
  };
50
57
 
51
58
  export {
52
59
  BaseEmbeddingModelStrategy
53
60
  };
54
- //# sourceMappingURL=chunk-SC6SVJGO.js.map
61
+ //# sourceMappingURL=chunk-HJZCRF7J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base-embedding-model-strategy.ts"],"sourcesContent":["/** Base class for embedding model strategies using the Template Method pattern. */\nimport type {\n EmbeddingModelV3CallOptions,\n EmbeddingModelV3Embedding,\n EmbeddingModelV3Result,\n} from \"@ai-sdk/provider\";\n\nimport type { SAPAIEmbeddingSettings } from \"./sap-ai-settings.js\";\nimport type { EmbeddingModelAPIStrategy, EmbeddingModelStrategyConfig } from \"./sap-ai-strategy.js\";\n\nimport { deepMerge } from \"./deep-merge.js\";\nimport { convertToAISDKError } from \"./sap-ai-error.js\";\nimport {\n buildEmbeddingResult,\n type EmbeddingProviderOptions,\n type EmbeddingType,\n prepareEmbeddingCall,\n} from \"./strategy-utils.js\";\nimport { VERSION } from \"./version.js\";\n\n/**\n * Abstract base class for embedding model strategies using the Template Method pattern.\n * @template TClient - The SDK client type (e.g., AzureOpenAiEmbeddingClient, OrchestrationEmbeddingClient).\n * @template TResponse - The API response type from the SDK client.\n * @internal\n */\nexport abstract class BaseEmbeddingModelStrategy<\n TClient,\n TResponse,\n> implements EmbeddingModelAPIStrategy {\n /**\n * Template method implementing the shared embedding algorithm.\n * @param config - Strategy configuration.\n * @param settings - Embedding model settings.\n * @param options - AI SDK call options.\n * @param maxEmbeddingsPerCall - Maximum embeddings per call.\n * @returns Complete embedding result for AI SDK.\n * @internal\n */\n async doEmbed(\n config: EmbeddingModelStrategyConfig,\n settings: SAPAIEmbeddingSettings,\n options: EmbeddingModelV3CallOptions,\n maxEmbeddingsPerCall: number,\n ): Promise<EmbeddingModelV3Result> {\n const { abortSignal, values } = options;\n\n const { embeddingOptions, providerName } = await prepareEmbeddingCall(\n { maxEmbeddingsPerCall, modelId: config.modelId, provider: config.provider },\n options,\n );\n\n const embeddingType =\n embeddingOptions?.type ?? (settings.type as EmbeddingType | undefined) ?? \"text\";\n\n try {\n const client = this.createClient(config, settings, embeddingOptions);\n\n const response = await this.executeCall(client, values, embeddingType, abortSignal);\n\n const embeddings = this.extractEmbeddings(response);\n const totalTokens = this.extractTokenCount(response);\n\n return buildEmbeddingResult({\n embeddings,\n modelId: config.modelId,\n providerName,\n totalTokens,\n version: VERSION,\n });\n } catch (error) {\n throw convertToAISDKError(error, {\n operation: \"doEmbed\",\n requestBody: { values: values.length },\n url: this.getUrl(),\n });\n }\n }\n\n /**\n * Creates the appropriate SDK client for this API.\n * @param config - Strategy configuration.\n * @param settings - Embedding model settings.\n * @param embeddingOptions - Parsed provider options from the call.\n * @returns SDK client instance.\n * @internal\n */\n protected abstract createClient(\n config: EmbeddingModelStrategyConfig,\n settings: SAPAIEmbeddingSettings,\n embeddingOptions: EmbeddingProviderOptions | undefined,\n ): TClient;\n\n /**\n * Executes the embedding API call.\n * @param client - SDK client instance.\n * @param values - Input strings to embed.\n * @param embeddingType - Type of embedding (text, query, document).\n * @param abortSignal - Optional abort signal.\n * @returns SDK response containing embeddings.\n * @internal\n */\n protected abstract executeCall(\n client: TClient,\n values: string[],\n embeddingType: EmbeddingType,\n abortSignal: AbortSignal | undefined,\n ): Promise<TResponse>;\n\n /**\n * Extracts embeddings from the SDK response.\n * @param response - SDK response containing embedding data.\n * @returns Array of normalized embedding vectors.\n * @internal\n */\n protected abstract extractEmbeddings(response: TResponse): EmbeddingModelV3Embedding[];\n\n /**\n * Extracts total token count from the SDK response.\n * @param response - SDK response containing usage data.\n * @returns Total token count used for the embedding request.\n * @internal\n */\n protected abstract extractTokenCount(response: TResponse): number;\n\n /**\n * Returns the URL identifier for this API (used in error messages).\n * @returns URL string identifier.\n * @internal\n */\n protected abstract getUrl(): string;\n\n protected mergeModelParams(\n settings: SAPAIEmbeddingSettings,\n embeddingOptions: EmbeddingProviderOptions | undefined,\n ): Record<string, unknown> {\n return deepMerge(\n (settings.modelParams as Record<string, unknown> | undefined) ?? {},\n embeddingOptions?.modelParams ?? {},\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AA0BO,IAAe,6BAAf,MAGgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUrC,MAAM,QACJ,QACA,UACA,SACA,sBACiC;AACjC,UAAM,EAAE,aAAa,OAAO,IAAI;AAEhC,UAAM,EAAE,kBAAkB,aAAa,IAAI,MAAM;AAAA,MAC/C,EAAE,sBAAsB,SAAS,OAAO,SAAS,UAAU,OAAO,SAAS;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,gBACJ,kBAAkB,QAAS,SAAS,QAAsC;AAE5E,QAAI;AACF,YAAM,SAAS,KAAK,aAAa,QAAQ,UAAU,gBAAgB;AAEnE,YAAM,WAAW,MAAM,KAAK,YAAY,QAAQ,QAAQ,eAAe,WAAW;AAElF,YAAM,aAAa,KAAK,kBAAkB,QAAQ;AAClD,YAAM,cAAc,KAAK,kBAAkB,QAAQ;AAEnD,aAAO,qBAAqB;AAAA,QAC1B;AAAA,QACA,SAAS,OAAO;AAAA,QAChB;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,oBAAoB,OAAO;AAAA,QAC/B,WAAW;AAAA,QACX,aAAa,EAAE,QAAQ,OAAO,OAAO;AAAA,QACrC,KAAK,KAAK,OAAO;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAuDU,iBACR,UACA,kBACyB;AACzB,WAAO;AAAA,MACJ,SAAS,eAAuD,CAAC;AAAA,MAClE,kBAAkB,eAAe,CAAC;AAAA,IACpC;AAAA,EACF;AACF;","names":[]}
@@ -30000,6 +30000,67 @@ var sapAIEmbeddingProviderOptions = lazySchema(
30000
30000
  )
30001
30001
  );
30002
30002
 
30003
+ // src/deep-merge.ts
30004
+ var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
30005
+ var MAX_DEPTH = 100;
30006
+ function deepMerge(...sources) {
30007
+ let result = {};
30008
+ for (const source of sources) {
30009
+ if (source == null) continue;
30010
+ result = mergeTwo(result, source, /* @__PURE__ */ new Set(), 0);
30011
+ }
30012
+ return result;
30013
+ }
30014
+ function cloneDeep(obj, ancestors, depth) {
30015
+ if (depth > MAX_DEPTH) {
30016
+ throw new Error("Maximum merge depth exceeded");
30017
+ }
30018
+ if (ancestors.has(obj)) {
30019
+ throw new Error("Circular reference detected during deep merge");
30020
+ }
30021
+ ancestors.add(obj);
30022
+ const result = {};
30023
+ for (const key of Object.keys(obj)) {
30024
+ if (!isSafeKey(key)) continue;
30025
+ const value = obj[key];
30026
+ result[key] = isPlainObject(value) ? cloneDeep(value, ancestors, depth + 1) : value;
30027
+ }
30028
+ ancestors.delete(obj);
30029
+ return result;
30030
+ }
30031
+ function isPlainObject(value) {
30032
+ if (value === null || typeof value !== "object") return false;
30033
+ const proto = Object.getPrototypeOf(value);
30034
+ return proto === Object.prototype || proto === null;
30035
+ }
30036
+ function isSafeKey(key) {
30037
+ return !DANGEROUS_KEYS.has(key);
30038
+ }
30039
+ function mergeTwo(target, source, ancestors, depth) {
30040
+ if (depth > MAX_DEPTH) {
30041
+ throw new Error("Maximum merge depth exceeded");
30042
+ }
30043
+ if (ancestors.has(source)) {
30044
+ throw new Error("Circular reference detected during deep merge");
30045
+ }
30046
+ ancestors.add(source);
30047
+ for (const key of Object.keys(source)) {
30048
+ if (!isSafeKey(key)) continue;
30049
+ const sourceValue = source[key];
30050
+ const targetValue = target[key];
30051
+ if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
30052
+ const cloned = cloneDeep(targetValue, /* @__PURE__ */ new Set(), depth + 1);
30053
+ target[key] = mergeTwo(cloned, sourceValue, ancestors, depth + 1);
30054
+ } else if (isPlainObject(sourceValue)) {
30055
+ target[key] = cloneDeep(sourceValue, /* @__PURE__ */ new Set(), depth + 1);
30056
+ } else {
30057
+ target[key] = sourceValue;
30058
+ }
30059
+ }
30060
+ ancestors.delete(source);
30061
+ return target;
30062
+ }
30063
+
30003
30064
  // src/sap-ai-error.ts
30004
30065
  var import_util = __toESM(require_dist(), 1);
30005
30066
  import { APICallError, LoadAPIKeyError, NoSuchModelError } from "@ai-sdk/provider";
@@ -30488,69 +30549,8 @@ function tryExtractSAPErrorFromMessage(message) {
30488
30549
  }
30489
30550
  }
30490
30551
 
30491
- // src/deep-merge.ts
30492
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
30493
- var MAX_DEPTH = 100;
30494
- function deepMerge(...sources) {
30495
- let result = {};
30496
- for (const source of sources) {
30497
- if (source == null) continue;
30498
- result = mergeTwo(result, source, /* @__PURE__ */ new Set(), 0);
30499
- }
30500
- return result;
30501
- }
30502
- function cloneDeep(obj, ancestors, depth) {
30503
- if (depth > MAX_DEPTH) {
30504
- throw new Error("Maximum merge depth exceeded");
30505
- }
30506
- if (ancestors.has(obj)) {
30507
- throw new Error("Circular reference detected during deep merge");
30508
- }
30509
- ancestors.add(obj);
30510
- const result = {};
30511
- for (const key of Object.keys(obj)) {
30512
- if (!isSafeKey(key)) continue;
30513
- const value = obj[key];
30514
- result[key] = isPlainObject(value) ? cloneDeep(value, ancestors, depth + 1) : value;
30515
- }
30516
- ancestors.delete(obj);
30517
- return result;
30518
- }
30519
- function isPlainObject(value) {
30520
- if (value === null || typeof value !== "object") return false;
30521
- const proto = Object.getPrototypeOf(value);
30522
- return proto === Object.prototype || proto === null;
30523
- }
30524
- function isSafeKey(key) {
30525
- return !DANGEROUS_KEYS.has(key);
30526
- }
30527
- function mergeTwo(target, source, ancestors, depth) {
30528
- if (depth > MAX_DEPTH) {
30529
- throw new Error("Maximum merge depth exceeded");
30530
- }
30531
- if (ancestors.has(source)) {
30532
- throw new Error("Circular reference detected during deep merge");
30533
- }
30534
- ancestors.add(source);
30535
- for (const key of Object.keys(source)) {
30536
- if (!isSafeKey(key)) continue;
30537
- const sourceValue = source[key];
30538
- const targetValue = target[key];
30539
- if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
30540
- const cloned = cloneDeep(targetValue, /* @__PURE__ */ new Set(), depth + 1);
30541
- target[key] = mergeTwo(cloned, sourceValue, ancestors, depth + 1);
30542
- } else if (isPlainObject(sourceValue)) {
30543
- target[key] = cloneDeep(sourceValue, /* @__PURE__ */ new Set(), depth + 1);
30544
- } else {
30545
- target[key] = sourceValue;
30546
- }
30547
- }
30548
- ancestors.delete(source);
30549
- return target;
30550
- }
30551
-
30552
30552
  // src/version.ts
30553
- var VERSION = true ? "4.5.0" : "0.0.0-test";
30553
+ var VERSION = true ? "4.6.1" : "0.0.0-test";
30554
30554
 
30555
30555
  export {
30556
30556
  __toESM,
@@ -30562,12 +30562,12 @@ export {
30562
30562
  orchestrationConfigRefSchema,
30563
30563
  sapAILanguageModelProviderOptions,
30564
30564
  sapAIEmbeddingProviderOptions,
30565
+ deepMerge,
30565
30566
  require_dist,
30566
30567
  ApiSwitchError,
30567
30568
  UnsupportedFeatureError,
30568
30569
  convertToAISDKError,
30569
30570
  normalizeHeaders,
30570
- deepMerge,
30571
30571
  VERSION
30572
30572
  };
30573
30573
  /*! Bundled license information:
@@ -30603,4 +30603,4 @@ mime-types/index.js:
30603
30603
  axios/dist/node/axios.cjs:
30604
30604
  (*! Axios v1.13.6 Copyright (c) 2026 Matt Zabriskie and contributors *)
30605
30605
  */
30606
- //# sourceMappingURL=chunk-T2KXS7WW.js.map
30606
+ //# sourceMappingURL=chunk-I5ZTK7M5.js.map