@jerome-benoit/sap-ai-provider 4.0.0-rc.1 → 4.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -29737,22 +29737,22 @@ var require_dist = __commonJS({
29737
29737
  // src/index.ts
29738
29738
  var index_exports = {};
29739
29739
  __export(index_exports, {
29740
- OrchestrationClient: () => import_orchestration5.OrchestrationClient,
29741
- OrchestrationResponse: () => import_orchestration4.OrchestrationResponse,
29742
- OrchestrationStreamChunkResponse: () => import_orchestration4.OrchestrationStreamChunkResponse,
29743
- OrchestrationStreamResponse: () => import_orchestration4.OrchestrationStreamResponse,
29740
+ OrchestrationClient: () => import_orchestration4.OrchestrationClient,
29741
+ OrchestrationResponse: () => import_orchestration3.OrchestrationResponse,
29742
+ OrchestrationStreamChunkResponse: () => import_orchestration3.OrchestrationStreamChunkResponse,
29743
+ OrchestrationStreamResponse: () => import_orchestration3.OrchestrationStreamResponse,
29744
29744
  buildAzureContentSafetyFilter: () => import_orchestration2.buildAzureContentSafetyFilter,
29745
29745
  buildDocumentGroundingConfig: () => import_orchestration2.buildDocumentGroundingConfig,
29746
29746
  buildDpiMaskingProvider: () => import_orchestration2.buildDpiMaskingProvider,
29747
29747
  buildLlamaGuard38BFilter: () => import_orchestration2.buildLlamaGuard38BFilter,
29748
29748
  buildTranslationConfig: () => import_orchestration2.buildTranslationConfig,
29749
29749
  createSAPAIProvider: () => createSAPAIProvider,
29750
- isConfigReference: () => import_orchestration3.isConfigReference,
29750
+ isConfigReference: () => import_orchestration2.isConfigReference,
29751
29751
  sapai: () => sapai
29752
29752
  });
29753
29753
  module.exports = __toCommonJS(index_exports);
29754
29754
 
29755
- // src/sap-ai-chat-language-model.ts
29755
+ // src/sap-ai-language-model.ts
29756
29756
  var import_orchestration = require("@sap-ai-sdk/orchestration");
29757
29757
  var import_zod_to_json_schema = require("zod-to-json-schema");
29758
29758
 
@@ -29764,25 +29764,78 @@ function convertToSAPMessages(prompt, options = {}) {
29764
29764
  const includeReasoning = options.includeReasoning ?? false;
29765
29765
  for (const message of prompt) {
29766
29766
  switch (message.role) {
29767
+ case "assistant": {
29768
+ let text = "";
29769
+ const toolCalls = [];
29770
+ for (const part of message.content) {
29771
+ switch (part.type) {
29772
+ case "reasoning": {
29773
+ if (includeReasoning && part.text) {
29774
+ text += `<reasoning>${part.text}</reasoning>`;
29775
+ }
29776
+ break;
29777
+ }
29778
+ case "text": {
29779
+ text += part.text;
29780
+ break;
29781
+ }
29782
+ case "tool-call": {
29783
+ let argumentsJson;
29784
+ if (typeof part.input === "string") {
29785
+ try {
29786
+ JSON.parse(part.input);
29787
+ argumentsJson = part.input;
29788
+ } catch {
29789
+ argumentsJson = JSON.stringify(part.input);
29790
+ }
29791
+ } else {
29792
+ argumentsJson = JSON.stringify(part.input);
29793
+ }
29794
+ toolCalls.push({
29795
+ function: {
29796
+ arguments: argumentsJson,
29797
+ name: part.toolName
29798
+ },
29799
+ id: part.toolCallId,
29800
+ type: "function"
29801
+ });
29802
+ break;
29803
+ }
29804
+ }
29805
+ }
29806
+ const assistantMessage = {
29807
+ content: text || "",
29808
+ role: "assistant",
29809
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
29810
+ };
29811
+ messages.push(assistantMessage);
29812
+ break;
29813
+ }
29767
29814
  case "system": {
29768
29815
  const systemMessage = {
29769
- role: "system",
29770
- content: message.content
29816
+ content: message.content,
29817
+ role: "system"
29771
29818
  };
29772
29819
  messages.push(systemMessage);
29773
29820
  break;
29774
29821
  }
29822
+ case "tool": {
29823
+ for (const part of message.content) {
29824
+ if (part.type === "tool-result") {
29825
+ const toolMessage = {
29826
+ content: JSON.stringify(part.output),
29827
+ role: "tool",
29828
+ tool_call_id: part.toolCallId
29829
+ };
29830
+ messages.push(toolMessage);
29831
+ }
29832
+ }
29833
+ break;
29834
+ }
29775
29835
  case "user": {
29776
29836
  const contentParts = [];
29777
29837
  for (const part of message.content) {
29778
29838
  switch (part.type) {
29779
- case "text": {
29780
- contentParts.push({
29781
- type: "text",
29782
- text: part.text
29783
- });
29784
- break;
29785
- }
29786
29839
  case "file": {
29787
29840
  if (!part.mediaType.startsWith("image/")) {
29788
29841
  throw new import_provider.UnsupportedFunctionalityError({
@@ -29824,10 +29877,17 @@ function convertToSAPMessages(prompt, options = {}) {
29824
29877
  }
29825
29878
  }
29826
29879
  contentParts.push({
29827
- type: "image_url",
29828
29880
  image_url: {
29829
29881
  url: imageUrl
29830
- }
29882
+ },
29883
+ type: "image_url"
29884
+ });
29885
+ break;
29886
+ }
29887
+ case "text": {
29888
+ contentParts.push({
29889
+ text: part.text,
29890
+ type: "text"
29831
29891
  });
29832
29892
  break;
29833
29893
  }
@@ -29839,75 +29899,15 @@ function convertToSAPMessages(prompt, options = {}) {
29839
29899
  }
29840
29900
  }
29841
29901
  const userMessage = contentParts.length === 1 && contentParts[0].type === "text" ? {
29842
- role: "user",
29843
- content: contentParts[0].text ?? ""
29902
+ content: contentParts[0].text ?? "",
29903
+ role: "user"
29844
29904
  } : {
29845
- role: "user",
29846
- content: contentParts
29905
+ content: contentParts,
29906
+ role: "user"
29847
29907
  };
29848
29908
  messages.push(userMessage);
29849
29909
  break;
29850
29910
  }
29851
- case "assistant": {
29852
- let text = "";
29853
- const toolCalls = [];
29854
- for (const part of message.content) {
29855
- switch (part.type) {
29856
- case "text": {
29857
- text += part.text;
29858
- break;
29859
- }
29860
- case "reasoning": {
29861
- if (includeReasoning && part.text) {
29862
- text += `<reasoning>${part.text}</reasoning>`;
29863
- }
29864
- break;
29865
- }
29866
- case "tool-call": {
29867
- let argumentsJson;
29868
- if (typeof part.input === "string") {
29869
- try {
29870
- JSON.parse(part.input);
29871
- argumentsJson = part.input;
29872
- } catch {
29873
- argumentsJson = JSON.stringify(part.input);
29874
- }
29875
- } else {
29876
- argumentsJson = JSON.stringify(part.input);
29877
- }
29878
- toolCalls.push({
29879
- id: part.toolCallId,
29880
- type: "function",
29881
- function: {
29882
- name: part.toolName,
29883
- arguments: argumentsJson
29884
- }
29885
- });
29886
- break;
29887
- }
29888
- }
29889
- }
29890
- const assistantMessage = {
29891
- role: "assistant",
29892
- content: text || "",
29893
- tool_calls: toolCalls.length > 0 ? toolCalls : void 0
29894
- };
29895
- messages.push(assistantMessage);
29896
- break;
29897
- }
29898
- case "tool": {
29899
- for (const part of message.content) {
29900
- if (part.type === "tool-result") {
29901
- const toolMessage = {
29902
- role: "tool",
29903
- tool_call_id: part.toolCallId,
29904
- content: JSON.stringify(part.output)
29905
- };
29906
- messages.push(toolMessage);
29907
- }
29908
- }
29909
- break;
29910
- }
29911
29911
  default: {
29912
29912
  const _exhaustiveCheck = message;
29913
29913
  throw new Error(
@@ -29922,16 +29922,6 @@ function convertToSAPMessages(prompt, options = {}) {
29922
29922
  // src/sap-ai-error.ts
29923
29923
  var import_provider2 = require("@ai-sdk/provider");
29924
29924
  var import_util = __toESM(require_dist(), 1);
29925
- function getStatusCodeFromSAPError(code) {
29926
- if (!code) return 500;
29927
- if (code >= 100 && code < 600) {
29928
- return code;
29929
- }
29930
- return 500;
29931
- }
29932
- function isRetryable(statusCode) {
29933
- return statusCode === 429 || statusCode >= 500 && statusCode < 600;
29934
- }
29935
29925
  function convertSAPErrorToAPICallError(errorResponse, context) {
29936
29926
  const error = errorResponse.error;
29937
29927
  let message;
@@ -29953,9 +29943,9 @@ function convertSAPErrorToAPICallError(errorResponse, context) {
29953
29943
  const statusCode = getStatusCodeFromSAPError(code);
29954
29944
  const responseBody = JSON.stringify({
29955
29945
  error: {
29956
- message,
29957
29946
  code,
29958
29947
  location,
29948
+ message,
29959
29949
  request_id: requestId
29960
29950
  }
29961
29951
  });
@@ -29978,53 +29968,14 @@ Error location: ${location}`;
29978
29968
  Request ID: ${requestId}`;
29979
29969
  }
29980
29970
  return new import_provider2.APICallError({
29971
+ isRetryable: isRetryable(statusCode),
29981
29972
  message: enhancedMessage,
29982
- url: context?.url ?? "",
29983
29973
  requestBodyValues: context?.requestBody,
29984
- statusCode,
29985
- responseHeaders: context?.responseHeaders,
29986
29974
  responseBody,
29987
- isRetryable: isRetryable(statusCode)
29988
- });
29989
- }
29990
- function isOrchestrationErrorResponse(error) {
29991
- if (error === null || typeof error !== "object" || !("error" in error)) {
29992
- return false;
29993
- }
29994
- const errorEnvelope = error;
29995
- const innerError = errorEnvelope.error;
29996
- if (innerError === void 0) return false;
29997
- if (Array.isArray(innerError)) {
29998
- return innerError.every(
29999
- (entry) => entry !== null && typeof entry === "object" && "message" in entry && typeof entry.message === "string"
30000
- );
30001
- }
30002
- return typeof innerError === "object" && innerError !== null && "message" in innerError && typeof innerError.message === "string";
30003
- }
30004
- function normalizeHeaders(headers) {
30005
- if (!headers || typeof headers !== "object") return void 0;
30006
- const record = headers;
30007
- const entries = Object.entries(record).flatMap(([key, value]) => {
30008
- if (typeof value === "string") return [[key, value]];
30009
- if (Array.isArray(value)) {
30010
- const strings = value.filter((item) => typeof item === "string").join("; ");
30011
- return strings.length > 0 ? [[key, strings]] : [];
30012
- }
30013
- if (typeof value === "number" || typeof value === "boolean") {
30014
- return [[key, String(value)]];
30015
- }
30016
- return [];
29975
+ responseHeaders: context?.responseHeaders,
29976
+ statusCode,
29977
+ url: context?.url ?? ""
30017
29978
  });
30018
- if (entries.length === 0) return void 0;
30019
- return Object.fromEntries(entries);
30020
- }
30021
- function getAxiosResponseHeaders(error) {
30022
- if (!(error instanceof Error)) return void 0;
30023
- const rootCause = (0, import_util.isErrorWithCause)(error) ? error.rootCause : error;
30024
- if (typeof rootCause !== "object") return void 0;
30025
- const maybeAxios = rootCause;
30026
- if (maybeAxios.isAxiosError !== true) return void 0;
30027
- return normalizeHeaders(maybeAxios.response?.headers);
30028
29979
  }
30029
29980
  function convertToAISDKError(error, context) {
30030
29981
  if (error instanceof import_provider2.APICallError || error instanceof import_provider2.LoadAPIKeyError) {
@@ -30049,30 +30000,79 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-serv
30049
30000
  }
30050
30001
  if (errorMsg.includes("econnrefused") || errorMsg.includes("enotfound") || errorMsg.includes("network") || errorMsg.includes("timeout")) {
30051
30002
  return new import_provider2.APICallError({
30003
+ cause: error,
30004
+ isRetryable: true,
30052
30005
  message: `Network error connecting to SAP AI Core: ${error.message}`,
30053
- url: context?.url ?? "",
30054
30006
  requestBodyValues: context?.requestBody,
30055
- statusCode: 503,
30056
- isRetryable: true,
30057
30007
  responseHeaders,
30058
- cause: error
30008
+ statusCode: 503,
30009
+ url: context?.url ?? ""
30059
30010
  });
30060
30011
  }
30061
30012
  }
30062
30013
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error occurred";
30063
30014
  const fullMessage = context?.operation ? `SAP AI Core ${context.operation} failed: ${message}` : `SAP AI Core error: ${message}`;
30064
30015
  return new import_provider2.APICallError({
30016
+ cause: error,
30017
+ isRetryable: false,
30065
30018
  message: fullMessage,
30066
- url: context?.url ?? "",
30067
30019
  requestBodyValues: context?.requestBody,
30068
- statusCode: 500,
30069
- isRetryable: false,
30070
30020
  responseHeaders,
30071
- cause: error
30021
+ statusCode: 500,
30022
+ url: context?.url ?? ""
30023
+ });
30024
+ }
30025
+ function getAxiosResponseHeaders(error) {
30026
+ if (!(error instanceof Error)) return void 0;
30027
+ const rootCause = (0, import_util.isErrorWithCause)(error) ? error.rootCause : error;
30028
+ if (typeof rootCause !== "object") return void 0;
30029
+ const maybeAxios = rootCause;
30030
+ if (maybeAxios.isAxiosError !== true) return void 0;
30031
+ return normalizeHeaders(maybeAxios.response?.headers);
30032
+ }
30033
+ function getStatusCodeFromSAPError(code) {
30034
+ if (!code) return 500;
30035
+ if (code >= 100 && code < 600) {
30036
+ return code;
30037
+ }
30038
+ return 500;
30039
+ }
30040
+ function isOrchestrationErrorResponse(error) {
30041
+ if (error === null || typeof error !== "object" || !("error" in error)) {
30042
+ return false;
30043
+ }
30044
+ const errorEnvelope = error;
30045
+ const innerError = errorEnvelope.error;
30046
+ if (innerError === void 0) return false;
30047
+ if (Array.isArray(innerError)) {
30048
+ return innerError.every(
30049
+ (entry) => entry !== null && typeof entry === "object" && "message" in entry && typeof entry.message === "string"
30050
+ );
30051
+ }
30052
+ return typeof innerError === "object" && innerError !== null && "message" in innerError && typeof innerError.message === "string";
30053
+ }
30054
+ function isRetryable(statusCode) {
30055
+ return statusCode === 429 || statusCode >= 500 && statusCode < 600;
30056
+ }
30057
+ function normalizeHeaders(headers) {
30058
+ if (!headers || typeof headers !== "object") return void 0;
30059
+ const record = headers;
30060
+ const entries = Object.entries(record).flatMap(([key, value]) => {
30061
+ if (typeof value === "string") return [[key, value]];
30062
+ if (Array.isArray(value)) {
30063
+ const strings = value.filter((item) => typeof item === "string").join("; ");
30064
+ return strings.length > 0 ? [[key, strings]] : [];
30065
+ }
30066
+ if (typeof value === "number" || typeof value === "boolean") {
30067
+ return [[key, String(value)]];
30068
+ }
30069
+ return [];
30072
30070
  });
30071
+ if (entries.length === 0) return void 0;
30072
+ return Object.fromEntries(entries);
30073
30073
  }
30074
30074
 
30075
- // src/sap-ai-chat-language-model.ts
30075
+ // src/sap-ai-language-model.ts
30076
30076
  var StreamIdGenerator = class {
30077
30077
  /**
30078
30078
  * Generates a unique ID for a text block.
@@ -30083,139 +30083,27 @@ var StreamIdGenerator = class {
30083
30083
  return crypto.randomUUID();
30084
30084
  }
30085
30085
  };
30086
- function validateModelParameters(params, warnings) {
30087
- if (params.temperature !== void 0 && (params.temperature < 0 || params.temperature > 2)) {
30088
- warnings.push({
30089
- type: "other",
30090
- message: `temperature=${String(params.temperature)} is outside typical range [0, 2]. The API may reject this value.`
30091
- });
30092
- }
30093
- if (params.topP !== void 0 && (params.topP < 0 || params.topP > 1)) {
30094
- warnings.push({
30095
- type: "other",
30096
- message: `topP=${String(params.topP)} is outside valid range [0, 1]. The API may reject this value.`
30097
- });
30098
- }
30099
- if (params.frequencyPenalty !== void 0 && (params.frequencyPenalty < -2 || params.frequencyPenalty > 2)) {
30100
- warnings.push({
30101
- type: "other",
30102
- message: `frequencyPenalty=${String(params.frequencyPenalty)} is outside typical range [-2, 2]. The API may reject this value.`
30103
- });
30104
- }
30105
- if (params.presencePenalty !== void 0 && (params.presencePenalty < -2 || params.presencePenalty > 2)) {
30106
- warnings.push({
30107
- type: "other",
30108
- message: `presencePenalty=${String(params.presencePenalty)} is outside typical range [-2, 2]. The API may reject this value.`
30109
- });
30110
- }
30111
- if (params.maxTokens !== void 0 && params.maxTokens <= 0) {
30112
- warnings.push({
30113
- type: "other",
30114
- message: `maxTokens=${String(params.maxTokens)} must be positive. The API will likely reject this value.`
30115
- });
30116
- }
30117
- if (params.n !== void 0 && params.n <= 0) {
30118
- warnings.push({
30119
- type: "other",
30120
- message: `n=${String(params.n)} must be positive. The API will likely reject this value.`
30121
- });
30122
- }
30123
- }
30124
- function createAISDKRequestBodySummary(options) {
30125
- return {
30126
- promptMessages: options.prompt.length,
30127
- hasImageParts: options.prompt.some(
30128
- (message) => message.role === "user" && message.content.some(
30129
- (part) => part.type === "file" && part.mediaType.startsWith("image/")
30130
- )
30131
- ),
30132
- tools: options.tools?.length ?? 0,
30133
- temperature: options.temperature,
30134
- topP: options.topP,
30135
- topK: options.topK,
30136
- maxOutputTokens: options.maxOutputTokens,
30137
- stopSequences: options.stopSequences?.length,
30138
- seed: options.seed,
30139
- toolChoiceType: options.toolChoice?.type,
30140
- responseFormatType: options.responseFormat?.type
30141
- };
30142
- }
30143
- function hasCallableParse(obj) {
30144
- return typeof obj.parse === "function";
30145
- }
30146
- function isZodSchema(obj) {
30147
- if (obj === null || typeof obj !== "object") {
30148
- return false;
30149
- }
30150
- const record = obj;
30151
- return "_def" in record && "parse" in record && hasCallableParse(record);
30152
- }
30153
- function buildSAPToolParameters(schema) {
30154
- const schemaType = schema.type;
30155
- if (schemaType !== void 0 && schemaType !== "object") {
30156
- return {
30157
- type: "object",
30158
- properties: {},
30159
- required: []
30160
- };
30161
- }
30162
- const properties = schema.properties && typeof schema.properties === "object" ? schema.properties : {};
30163
- const required = Array.isArray(schema.required) && schema.required.every((item) => typeof item === "string") ? schema.required : [];
30164
- const additionalFields = Object.fromEntries(
30165
- Object.entries(schema).filter(
30166
- ([key]) => key !== "type" && key !== "properties" && key !== "required"
30167
- )
30168
- );
30169
- return {
30170
- type: "object",
30171
- properties,
30172
- required,
30173
- ...additionalFields
30174
- };
30175
- }
30176
- var SAPAIChatLanguageModel = class {
30177
- specificationVersion = "v3";
30086
+ var SAPAILanguageModel = class {
30178
30087
  modelId;
30179
- config;
30180
- settings;
30181
- /**
30182
- * Creates a new SAP AI Chat Language Model instance.
30183
- *
30184
- * @param modelId - The model identifier
30185
- * @param settings - Model-specific configuration settings
30186
- * @param config - Internal configuration (deployment config, destination, etc.)
30187
- *
30188
- * @internal This constructor is not meant to be called directly.
30189
- * Use the provider function instead.
30190
- */
30191
- constructor(modelId, settings, config) {
30192
- this.settings = settings;
30193
- this.config = config;
30194
- this.modelId = modelId;
30195
- }
30196
- /**
30197
- * Checks if a URL is supported for file/image uploads.
30198
- *
30199
- * @param url - The URL to check
30200
- * @returns True if the URL protocol is HTTPS or data with valid image format
30201
- */
30202
- supportsUrl(url) {
30203
- if (url.protocol === "https:") return true;
30204
- if (url.protocol === "data:") {
30205
- return /^data:image\//i.test(url.href);
30206
- }
30207
- return false;
30208
- }
30088
+ specificationVersion = "v3";
30209
30089
  /**
30210
- * Returns supported URL patterns for different content types.
30090
+ * Model capabilities.
30211
30091
  *
30212
- * @returns Record of content types to regex patterns
30092
+ * These defaults assume “modern” model behavior to avoid maintaining a
30093
+ * per-model capability matrix. If a deployment doesn't support a feature,
30094
+ * SAP AI Core will fail the request at runtime.
30213
30095
  */
30214
- get supportedUrls() {
30215
- return {
30216
- "image/*": [/^https:\/\/.+$/i, /^data:image\/.*$/]
30217
- };
30218
- }
30096
+ supportsImageUrls = true;
30097
+ /** Multiple completions via the `n` parameter (provider-specific support). */
30098
+ supportsMultipleCompletions = true;
30099
+ /** Parallel tool calls. */
30100
+ supportsParallelToolCalls = true;
30101
+ /** Streaming responses. */
30102
+ supportsStreaming = true;
30103
+ /** Structured JSON outputs (json_schema response format). */
30104
+ supportsStructuredOutputs = true;
30105
+ /** Tool/function calling. */
30106
+ supportsToolCalls = true;
30219
30107
  /**
30220
30108
  * Generates text completion using SAP AI Core's Orchestration API.
30221
30109
  *
@@ -30261,206 +30149,31 @@ var SAPAIChatLanguageModel = class {
30261
30149
  return this.config.provider;
30262
30150
  }
30263
30151
  /**
30264
- * Model capabilities.
30265
- *
30266
- * These defaults assume “modern” model behavior to avoid maintaining a
30267
- * per-model capability matrix. If a deployment doesn't support a feature,
30268
- * SAP AI Core will fail the request at runtime.
30269
- */
30270
- supportsImageUrls = true;
30271
- /** Structured JSON outputs (json_schema response format). */
30272
- supportsStructuredOutputs = true;
30273
- /** Tool/function calling. */
30274
- supportsToolCalls = true;
30275
- /** Streaming responses. */
30276
- supportsStreaming = true;
30277
- /** Multiple completions via the `n` parameter (provider-specific support). */
30278
- supportsMultipleCompletions = true;
30279
- /** Parallel tool calls. */
30280
- supportsParallelToolCalls = true;
30281
- /**
30282
- * Builds orchestration module config for SAP AI SDK.
30152
+ * Returns supported URL patterns for different content types.
30283
30153
  *
30284
- * @param options - Call options from the AI SDK
30285
- * @returns Object containing orchestration config, messages, and warnings
30286
- * @internal
30154
+ * @returns Record of content types to regex patterns
30287
30155
  */
30288
- buildOrchestrationConfig(options) {
30289
- const providerOptions = options.providerOptions?.sap ?? {};
30290
- const warnings = [];
30291
- const messages = convertToSAPMessages(options.prompt, {
30292
- includeReasoning: providerOptions.includeReasoning ?? this.settings.includeReasoning ?? false
30293
- });
30294
- let tools;
30295
- const settingsTools = providerOptions.tools ?? this.settings.tools;
30296
- const optionsTools = options.tools;
30297
- const shouldUseSettingsTools = settingsTools && settingsTools.length > 0 && (!optionsTools || optionsTools.length === 0);
30298
- const shouldUseOptionsTools = !!(optionsTools && optionsTools.length > 0);
30299
- if (settingsTools && settingsTools.length > 0 && optionsTools && optionsTools.length > 0) {
30300
- warnings.push({
30301
- type: "other",
30302
- message: "Both settings.tools and call options.tools were provided; preferring call options.tools."
30303
- });
30304
- }
30305
- if (shouldUseSettingsTools) {
30306
- tools = settingsTools;
30307
- } else {
30308
- const availableTools = shouldUseOptionsTools ? optionsTools : void 0;
30309
- tools = availableTools?.map((tool) => {
30310
- if (tool.type === "function") {
30311
- const inputSchema = tool.inputSchema;
30312
- const toolWithParams = tool;
30313
- let parameters;
30314
- if (toolWithParams.parameters && isZodSchema(toolWithParams.parameters)) {
30315
- try {
30316
- const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(
30317
- toolWithParams.parameters,
30318
- {
30319
- $refStrategy: "none"
30320
- }
30321
- );
30322
- const schemaRecord = jsonSchema;
30323
- delete schemaRecord.$schema;
30324
- parameters = buildSAPToolParameters(schemaRecord);
30325
- } catch (error) {
30326
- warnings.push({
30327
- type: "unsupported",
30328
- feature: `tool schema conversion for ${tool.name}`,
30329
- details: `Failed to convert tool Zod schema: ${error instanceof Error ? error.message : String(error)}. Falling back to empty object schema.`
30330
- });
30331
- parameters = buildSAPToolParameters({});
30332
- }
30333
- } else if (inputSchema && Object.keys(inputSchema).length > 0) {
30334
- const hasProperties = inputSchema.properties && typeof inputSchema.properties === "object" && Object.keys(inputSchema.properties).length > 0;
30335
- if (hasProperties) {
30336
- parameters = buildSAPToolParameters(inputSchema);
30337
- } else {
30338
- parameters = buildSAPToolParameters({});
30339
- }
30340
- } else {
30341
- parameters = buildSAPToolParameters({});
30342
- }
30343
- return {
30344
- type: "function",
30345
- function: {
30346
- name: tool.name,
30347
- description: tool.description,
30348
- parameters
30349
- }
30350
- };
30351
- } else {
30352
- warnings.push({
30353
- type: "unsupported",
30354
- feature: `tool type for ${tool.name}`,
30355
- details: "Only 'function' tool type is supported."
30356
- });
30357
- return null;
30358
- }
30359
- }).filter((t) => t !== null);
30360
- }
30361
- const supportsN = !this.modelId.startsWith("amazon--") && !this.modelId.startsWith("anthropic--");
30362
- const modelParams = {};
30363
- const maxTokens = options.maxOutputTokens ?? providerOptions.modelParams?.maxTokens ?? this.settings.modelParams?.maxTokens;
30364
- if (maxTokens !== void 0) modelParams.max_tokens = maxTokens;
30365
- const temperature = options.temperature ?? providerOptions.modelParams?.temperature ?? this.settings.modelParams?.temperature;
30366
- if (temperature !== void 0) modelParams.temperature = temperature;
30367
- const topP = options.topP ?? providerOptions.modelParams?.topP ?? this.settings.modelParams?.topP;
30368
- if (topP !== void 0) modelParams.top_p = topP;
30369
- if (options.topK !== void 0) modelParams.top_k = options.topK;
30370
- const frequencyPenalty = options.frequencyPenalty ?? providerOptions.modelParams?.frequencyPenalty ?? this.settings.modelParams?.frequencyPenalty;
30371
- if (frequencyPenalty !== void 0) {
30372
- modelParams.frequency_penalty = frequencyPenalty;
30373
- }
30374
- const presencePenalty = options.presencePenalty ?? providerOptions.modelParams?.presencePenalty ?? this.settings.modelParams?.presencePenalty;
30375
- if (presencePenalty !== void 0) {
30376
- modelParams.presence_penalty = presencePenalty;
30377
- }
30378
- if (supportsN) {
30379
- const nValue = providerOptions.modelParams?.n ?? this.settings.modelParams?.n;
30380
- if (nValue !== void 0) {
30381
- modelParams.n = nValue;
30382
- }
30383
- }
30384
- const parallelToolCalls = providerOptions.modelParams?.parallel_tool_calls ?? this.settings.modelParams?.parallel_tool_calls;
30385
- if (parallelToolCalls !== void 0) {
30386
- modelParams.parallel_tool_calls = parallelToolCalls;
30387
- }
30388
- if (options.stopSequences && options.stopSequences.length > 0) {
30389
- modelParams.stop = options.stopSequences;
30390
- }
30391
- if (options.seed !== void 0) {
30392
- modelParams.seed = options.seed;
30393
- }
30394
- validateModelParameters(
30395
- {
30396
- temperature,
30397
- topP,
30398
- frequencyPenalty,
30399
- presencePenalty,
30400
- maxTokens,
30401
- n: modelParams.n
30402
- },
30403
- warnings
30404
- );
30405
- if (options.toolChoice && options.toolChoice.type !== "auto") {
30406
- warnings.push({
30407
- type: "unsupported",
30408
- feature: "toolChoice",
30409
- details: `SAP AI SDK does not support toolChoice '${options.toolChoice.type}'. Using default 'auto' behavior.`
30410
- });
30411
- }
30412
- if (options.responseFormat?.type === "json") {
30413
- warnings.push({
30414
- type: "other",
30415
- message: "responseFormat JSON mode is forwarded to the underlying model; support and schema adherence depend on the model/deployment."
30416
- });
30417
- }
30418
- const responseFormat = options.responseFormat?.type === "json" ? options.responseFormat.schema ? {
30419
- type: "json_schema",
30420
- json_schema: {
30421
- name: options.responseFormat.name ?? "response",
30422
- description: options.responseFormat.description,
30423
- schema: options.responseFormat.schema,
30424
- strict: null
30425
- }
30426
- } : { type: "json_object" } : void 0;
30427
- const orchestrationConfig = {
30428
- promptTemplating: {
30429
- model: {
30430
- name: this.modelId,
30431
- version: providerOptions.modelVersion ?? this.settings.modelVersion ?? "latest",
30432
- params: modelParams
30433
- },
30434
- prompt: {
30435
- template: [],
30436
- tools: tools && tools.length > 0 ? tools : void 0,
30437
- ...responseFormat ? { response_format: responseFormat } : {}
30438
- }
30439
- },
30440
- ...(() => {
30441
- const masking = providerOptions.masking ?? this.settings.masking;
30442
- return masking && Object.keys(masking).length > 0 ? { masking } : {};
30443
- })(),
30444
- ...(() => {
30445
- const filtering = providerOptions.filtering ?? this.settings.filtering;
30446
- return filtering && Object.keys(filtering).length > 0 ? { filtering } : {};
30447
- })()
30156
+ get supportedUrls() {
30157
+ return {
30158
+ "image/*": [/^https:\/\/.+$/i, /^data:image\/.*$/]
30448
30159
  };
30449
- return { orchestrationConfig, messages, warnings };
30450
30160
  }
30161
+ config;
30162
+ settings;
30451
30163
  /**
30452
- * Creates an OrchestrationClient instance.
30164
+ * Creates a new SAP AI Chat Language Model instance.
30453
30165
  *
30454
- * @param config - Orchestration module configuration
30455
- * @returns OrchestrationClient instance
30456
- * @internal
30166
+ * @param modelId - The model identifier
30167
+ * @param settings - Model-specific configuration settings
30168
+ * @param config - Internal configuration (deployment config, destination, etc.)
30169
+ *
30170
+ * @internal This constructor is not meant to be called directly.
30171
+ * Use the provider function instead.
30457
30172
  */
30458
- createClient(config) {
30459
- return new import_orchestration.OrchestrationClient(
30460
- config,
30461
- this.config.deploymentConfig,
30462
- this.config.destination
30463
- );
30173
+ constructor(modelId, settings, config) {
30174
+ this.settings = settings;
30175
+ this.config = config;
30176
+ this.modelId = modelId;
30464
30177
  }
30465
30178
  /**
30466
30179
  * Generates a single completion (non-streaming).
@@ -30498,7 +30211,7 @@ var SAPAIChatLanguageModel = class {
30498
30211
  */
30499
30212
  async doGenerate(options) {
30500
30213
  try {
30501
- const { orchestrationConfig, messages, warnings } = this.buildOrchestrationConfig(options);
30214
+ const { messages, orchestrationConfig, warnings } = this.buildOrchestrationConfig(options);
30502
30215
  const client = this.createClient(orchestrationConfig);
30503
30216
  const promptTemplating = orchestrationConfig.promptTemplating;
30504
30217
  const requestBody = {
@@ -30515,6 +30228,14 @@ var SAPAIChatLanguageModel = class {
30515
30228
  ...(() => {
30516
30229
  const filtering = orchestrationConfig.filtering;
30517
30230
  return filtering && Object.keys(filtering).length > 0 ? { filtering } : {};
30231
+ })(),
30232
+ ...(() => {
30233
+ const grounding = orchestrationConfig.grounding;
30234
+ return grounding && Object.keys(grounding).length > 0 ? { grounding } : {};
30235
+ })(),
30236
+ ...(() => {
30237
+ const translation = orchestrationConfig.translation;
30238
+ return translation && Object.keys(translation).length > 0 ? { translation } : {};
30518
30239
  })()
30519
30240
  };
30520
30241
  const response = await (async () => {
@@ -30565,18 +30286,18 @@ var SAPAIChatLanguageModel = class {
30565
30286
  const textContent = response.getContent();
30566
30287
  if (textContent) {
30567
30288
  content.push({
30568
- type: "text",
30569
- text: textContent
30289
+ text: textContent,
30290
+ type: "text"
30570
30291
  });
30571
30292
  }
30572
30293
  const toolCalls = response.getToolCalls();
30573
30294
  if (toolCalls) {
30574
30295
  for (const toolCall of toolCalls) {
30575
30296
  content.push({
30576
- type: "tool-call",
30297
+ input: toolCall.function.arguments,
30577
30298
  toolCallId: toolCall.id,
30578
30299
  toolName: toolCall.function.name,
30579
- input: toolCall.function.arguments
30300
+ type: "tool-call"
30580
30301
  });
30581
30302
  }
30582
30303
  }
@@ -30585,26 +30306,13 @@ var SAPAIChatLanguageModel = class {
30585
30306
  const finishReason = mapFinishReason(finishReasonRaw);
30586
30307
  const rawResponseBody = {
30587
30308
  content: textContent,
30588
- toolCalls,
30309
+ finishReason: finishReasonRaw,
30589
30310
  tokenUsage,
30590
- finishReason: finishReasonRaw
30311
+ toolCalls
30591
30312
  };
30592
30313
  return {
30593
30314
  content,
30594
30315
  finishReason,
30595
- usage: {
30596
- inputTokens: {
30597
- total: tokenUsage.prompt_tokens,
30598
- noCache: tokenUsage.prompt_tokens,
30599
- cacheRead: void 0,
30600
- cacheWrite: void 0
30601
- },
30602
- outputTokens: {
30603
- total: tokenUsage.completion_tokens,
30604
- text: tokenUsage.completion_tokens,
30605
- reasoning: void 0
30606
- }
30607
- },
30608
30316
  providerMetadata: {
30609
30317
  "sap-ai": {
30610
30318
  finishReason: finishReasonRaw ?? "unknown",
@@ -30616,18 +30324,31 @@ var SAPAIChatLanguageModel = class {
30616
30324
  body: requestBody
30617
30325
  },
30618
30326
  response: {
30619
- timestamp: /* @__PURE__ */ new Date(),
30620
- modelId: this.modelId,
30327
+ body: rawResponseBody,
30621
30328
  headers: responseHeaders,
30622
- body: rawResponseBody
30329
+ modelId: this.modelId,
30330
+ timestamp: /* @__PURE__ */ new Date()
30331
+ },
30332
+ usage: {
30333
+ inputTokens: {
30334
+ cacheRead: void 0,
30335
+ cacheWrite: void 0,
30336
+ noCache: tokenUsage.prompt_tokens,
30337
+ total: tokenUsage.prompt_tokens
30338
+ },
30339
+ outputTokens: {
30340
+ reasoning: void 0,
30341
+ text: tokenUsage.completion_tokens,
30342
+ total: tokenUsage.completion_tokens
30343
+ }
30623
30344
  },
30624
30345
  warnings
30625
30346
  };
30626
30347
  } catch (error) {
30627
30348
  throw convertToAISDKError(error, {
30628
30349
  operation: "doGenerate",
30629
- url: "sap-ai:orchestration",
30630
- requestBody: createAISDKRequestBodySummary(options)
30350
+ requestBody: createAISDKRequestBodySummary(options),
30351
+ url: "sap-ai:orchestration"
30631
30352
  });
30632
30353
  }
30633
30354
  }
@@ -30656,6 +30377,8 @@ var SAPAIChatLanguageModel = class {
30656
30377
  * - Usage format: `{ inputTokens: { total, ... }, outputTokens: { total, ... } }`
30657
30378
  * - Warnings only in `stream-start` event
30658
30379
  *
30380
+ * @see {@link https://sdk.vercel.ai/docs/ai-sdk-core/streaming Vercel AI SDK Streaming}
30381
+ *
30659
30382
  * @param options - Streaming options including prompt, tools, and settings
30660
30383
  * @returns Promise resolving to stream and request metadata
30661
30384
  *
@@ -30681,7 +30404,7 @@ var SAPAIChatLanguageModel = class {
30681
30404
  */
30682
30405
  async doStream(options) {
30683
30406
  try {
30684
- const { orchestrationConfig, messages, warnings } = this.buildOrchestrationConfig(options);
30407
+ const { messages, orchestrationConfig, warnings } = this.buildOrchestrationConfig(options);
30685
30408
  const client = this.createClient(orchestrationConfig);
30686
30409
  const promptTemplating = orchestrationConfig.promptTemplating;
30687
30410
  const requestBody = {
@@ -30708,25 +30431,25 @@ var SAPAIChatLanguageModel = class {
30708
30431
  const idGenerator = new StreamIdGenerator();
30709
30432
  let textBlockId = null;
30710
30433
  const streamState = {
30434
+ activeText: false,
30711
30435
  finishReason: {
30712
- unified: "other",
30713
- raw: void 0
30436
+ raw: void 0,
30437
+ unified: "other"
30714
30438
  },
30439
+ isFirstChunk: true,
30715
30440
  usage: {
30716
30441
  inputTokens: {
30717
- total: void 0,
30718
- noCache: void 0,
30719
30442
  cacheRead: void 0,
30720
- cacheWrite: void 0
30443
+ cacheWrite: void 0,
30444
+ noCache: void 0,
30445
+ total: void 0
30721
30446
  },
30722
30447
  outputTokens: {
30723
- total: void 0,
30448
+ reasoning: void 0,
30724
30449
  text: void 0,
30725
- reasoning: void 0
30450
+ total: void 0
30726
30451
  }
30727
- },
30728
- isFirstChunk: true,
30729
- activeText: false
30452
+ }
30730
30453
  };
30731
30454
  const toolCallsInProgress = /* @__PURE__ */ new Map();
30732
30455
  const sdkStream = streamResponse.stream;
@@ -30734,6 +30457,11 @@ var SAPAIChatLanguageModel = class {
30734
30457
  const warningsSnapshot = [...warnings];
30735
30458
  const warningsOut = [...warningsSnapshot];
30736
30459
  const transformedStream = new ReadableStream({
30460
+ cancel(reason) {
30461
+ if (reason) {
30462
+ console.debug("SAP AI stream cancelled:", reason);
30463
+ }
30464
+ },
30737
30465
  async start(controller) {
30738
30466
  controller.enqueue({
30739
30467
  type: "stream-start",
@@ -30744,30 +30472,30 @@ var SAPAIChatLanguageModel = class {
30744
30472
  if (streamState.isFirstChunk) {
30745
30473
  streamState.isFirstChunk = false;
30746
30474
  controller.enqueue({
30747
- type: "response-metadata",
30748
30475
  modelId,
30749
- timestamp: /* @__PURE__ */ new Date()
30476
+ timestamp: /* @__PURE__ */ new Date(),
30477
+ type: "response-metadata"
30750
30478
  });
30751
30479
  }
30752
30480
  const deltaToolCalls = chunk.getDeltaToolCalls();
30753
30481
  if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
30754
30482
  streamState.finishReason = {
30755
- unified: "tool-calls",
30756
- raw: void 0
30483
+ raw: void 0,
30484
+ unified: "tool-calls"
30757
30485
  };
30758
30486
  }
30759
30487
  const deltaContent = chunk.getDeltaContent();
30760
30488
  if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason.unified !== "tool-calls") {
30761
30489
  if (!streamState.activeText) {
30762
30490
  textBlockId = idGenerator.generateTextBlockId();
30763
- controller.enqueue({ type: "text-start", id: textBlockId });
30491
+ controller.enqueue({ id: textBlockId, type: "text-start" });
30764
30492
  streamState.activeText = true;
30765
30493
  }
30766
30494
  if (textBlockId) {
30767
30495
  controller.enqueue({
30768
- type: "text-delta",
30496
+ delta: deltaContent,
30769
30497
  id: textBlockId,
30770
- delta: deltaContent
30498
+ type: "text-delta"
30771
30499
  });
30772
30500
  }
30773
30501
  }
@@ -30779,11 +30507,11 @@ var SAPAIChatLanguageModel = class {
30779
30507
  }
30780
30508
  if (!toolCallsInProgress.has(index)) {
30781
30509
  toolCallsInProgress.set(index, {
30782
- id: toolCallChunk.id ?? `tool_${String(index)}`,
30783
- toolName: toolCallChunk.function?.name,
30784
30510
  arguments: "",
30511
+ didEmitCall: false,
30785
30512
  didEmitInputStart: false,
30786
- didEmitCall: false
30513
+ id: toolCallChunk.id ?? `tool_${String(index)}`,
30514
+ toolName: toolCallChunk.function?.name
30787
30515
  });
30788
30516
  }
30789
30517
  const tc = toolCallsInProgress.get(index);
@@ -30798,9 +30526,9 @@ var SAPAIChatLanguageModel = class {
30798
30526
  if (!tc.didEmitInputStart && tc.toolName) {
30799
30527
  tc.didEmitInputStart = true;
30800
30528
  controller.enqueue({
30801
- type: "tool-input-start",
30802
30529
  id: tc.id,
30803
- toolName: tc.toolName
30530
+ toolName: tc.toolName,
30531
+ type: "tool-input-start"
30804
30532
  });
30805
30533
  }
30806
30534
  const argumentsDelta = toolCallChunk.function?.arguments;
@@ -30808,9 +30536,9 @@ var SAPAIChatLanguageModel = class {
30808
30536
  tc.arguments += argumentsDelta;
30809
30537
  if (tc.didEmitInputStart) {
30810
30538
  controller.enqueue({
30811
- type: "tool-input-delta",
30539
+ delta: argumentsDelta,
30812
30540
  id: tc.id,
30813
- delta: argumentsDelta
30541
+ type: "tool-input-delta"
30814
30542
  });
30815
30543
  }
30816
30544
  }
@@ -30828,28 +30556,28 @@ var SAPAIChatLanguageModel = class {
30828
30556
  if (!tc.didEmitInputStart) {
30829
30557
  tc.didEmitInputStart = true;
30830
30558
  controller.enqueue({
30831
- type: "tool-input-start",
30832
30559
  id: tc.id,
30833
- toolName: tc.toolName ?? ""
30560
+ toolName: tc.toolName ?? "",
30561
+ type: "tool-input-start"
30834
30562
  });
30835
30563
  }
30836
30564
  if (!tc.toolName) {
30837
30565
  warningsOut.push({
30838
- type: "other",
30839
- message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name."
30566
+ message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name.",
30567
+ type: "other"
30840
30568
  });
30841
30569
  }
30842
30570
  tc.didEmitCall = true;
30843
- controller.enqueue({ type: "tool-input-end", id: tc.id });
30571
+ controller.enqueue({ id: tc.id, type: "tool-input-end" });
30844
30572
  controller.enqueue({
30845
- type: "tool-call",
30573
+ input: tc.arguments,
30846
30574
  toolCallId: tc.id,
30847
30575
  toolName: tc.toolName ?? "",
30848
- input: tc.arguments
30576
+ type: "tool-call"
30849
30577
  });
30850
30578
  }
30851
30579
  if (streamState.activeText && textBlockId) {
30852
- controller.enqueue({ type: "text-end", id: textBlockId });
30580
+ controller.enqueue({ id: textBlockId, type: "text-end" });
30853
30581
  streamState.activeText = false;
30854
30582
  }
30855
30583
  }
@@ -30864,109 +30592,399 @@ var SAPAIChatLanguageModel = class {
30864
30592
  if (!tc.didEmitInputStart) {
30865
30593
  tc.didEmitInputStart = true;
30866
30594
  controller.enqueue({
30867
- type: "tool-input-start",
30868
30595
  id: tc.id,
30869
- toolName: tc.toolName ?? ""
30596
+ toolName: tc.toolName ?? "",
30597
+ type: "tool-input-start"
30870
30598
  });
30871
30599
  }
30872
30600
  if (!tc.toolName) {
30873
30601
  warningsOut.push({
30874
- type: "other",
30875
- message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name."
30602
+ message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name.",
30603
+ type: "other"
30876
30604
  });
30877
30605
  }
30878
30606
  didEmitAnyToolCalls = true;
30879
30607
  tc.didEmitCall = true;
30880
- controller.enqueue({ type: "tool-input-end", id: tc.id });
30608
+ controller.enqueue({ id: tc.id, type: "tool-input-end" });
30881
30609
  controller.enqueue({
30882
- type: "tool-call",
30610
+ input: tc.arguments,
30883
30611
  toolCallId: tc.id,
30884
30612
  toolName: tc.toolName ?? "",
30885
- input: tc.arguments
30613
+ type: "tool-call"
30614
+ });
30615
+ }
30616
+ if (streamState.activeText && textBlockId) {
30617
+ controller.enqueue({ id: textBlockId, type: "text-end" });
30618
+ }
30619
+ const finalFinishReason = streamResponse.getFinishReason();
30620
+ if (finalFinishReason) {
30621
+ streamState.finishReason = mapFinishReason(finalFinishReason);
30622
+ } else if (didEmitAnyToolCalls) {
30623
+ streamState.finishReason = {
30624
+ raw: void 0,
30625
+ unified: "tool-calls"
30626
+ };
30627
+ }
30628
+ const finalUsage = streamResponse.getTokenUsage();
30629
+ if (finalUsage) {
30630
+ streamState.usage.inputTokens.total = finalUsage.prompt_tokens;
30631
+ streamState.usage.inputTokens.noCache = finalUsage.prompt_tokens;
30632
+ streamState.usage.outputTokens.total = finalUsage.completion_tokens;
30633
+ streamState.usage.outputTokens.text = finalUsage.completion_tokens;
30634
+ }
30635
+ controller.enqueue({
30636
+ finishReason: streamState.finishReason,
30637
+ type: "finish",
30638
+ usage: streamState.usage
30639
+ });
30640
+ controller.close();
30641
+ } catch (error) {
30642
+ const aiError = convertToAISDKError(error, {
30643
+ operation: "doStream",
30644
+ requestBody: createAISDKRequestBodySummary(options),
30645
+ url: "sap-ai:orchestration"
30646
+ });
30647
+ controller.enqueue({
30648
+ error: aiError instanceof Error ? aiError : new Error(String(aiError)),
30649
+ type: "error"
30650
+ });
30651
+ controller.close();
30652
+ }
30653
+ }
30654
+ });
30655
+ return {
30656
+ request: {
30657
+ body: requestBody
30658
+ },
30659
+ stream: transformedStream
30660
+ };
30661
+ } catch (error) {
30662
+ throw convertToAISDKError(error, {
30663
+ operation: "doStream",
30664
+ requestBody: createAISDKRequestBodySummary(options),
30665
+ url: "sap-ai:orchestration"
30666
+ });
30667
+ }
30668
+ }
30669
+ /**
30670
+ * Checks if a URL is supported for file/image uploads.
30671
+ *
30672
+ * @param url - The URL to check
30673
+ * @returns True if the URL protocol is HTTPS or data with valid image format
30674
+ */
30675
+ supportsUrl(url) {
30676
+ if (url.protocol === "https:") return true;
30677
+ if (url.protocol === "data:") {
30678
+ return /^data:image\//i.test(url.href);
30679
+ }
30680
+ return false;
30681
+ }
30682
+ /**
30683
+ * Builds orchestration module config for SAP AI SDK.
30684
+ *
30685
+ * @param options - Call options from the AI SDK
30686
+ * @returns Object containing orchestration config, messages, and warnings
30687
+ * @internal
30688
+ */
30689
+ buildOrchestrationConfig(options) {
30690
+ const providerOptions = options.providerOptions?.sap ?? {};
30691
+ const warnings = [];
30692
+ const messages = convertToSAPMessages(options.prompt, {
30693
+ includeReasoning: providerOptions.includeReasoning ?? this.settings.includeReasoning ?? false
30694
+ });
30695
+ let tools;
30696
+ const settingsTools = providerOptions.tools ?? this.settings.tools;
30697
+ const optionsTools = options.tools;
30698
+ const shouldUseSettingsTools = settingsTools && settingsTools.length > 0 && (!optionsTools || optionsTools.length === 0);
30699
+ const shouldUseOptionsTools = !!(optionsTools && optionsTools.length > 0);
30700
+ if (settingsTools && settingsTools.length > 0 && optionsTools && optionsTools.length > 0) {
30701
+ warnings.push({
30702
+ message: "Both settings.tools and call options.tools were provided; preferring call options.tools.",
30703
+ type: "other"
30704
+ });
30705
+ }
30706
+ if (shouldUseSettingsTools) {
30707
+ tools = settingsTools;
30708
+ } else {
30709
+ const availableTools = shouldUseOptionsTools ? optionsTools : void 0;
30710
+ tools = availableTools?.map((tool) => {
30711
+ if (tool.type === "function") {
30712
+ const inputSchema = tool.inputSchema;
30713
+ const toolWithParams = tool;
30714
+ let parameters;
30715
+ if (toolWithParams.parameters && isZodSchema(toolWithParams.parameters)) {
30716
+ try {
30717
+ const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(
30718
+ toolWithParams.parameters,
30719
+ {
30720
+ $refStrategy: "none"
30721
+ }
30722
+ );
30723
+ const schemaRecord = jsonSchema;
30724
+ delete schemaRecord.$schema;
30725
+ parameters = buildSAPToolParameters(schemaRecord);
30726
+ } catch (error) {
30727
+ warnings.push({
30728
+ details: `Failed to convert tool Zod schema: ${error instanceof Error ? error.message : String(error)}. Falling back to empty object schema.`,
30729
+ feature: `tool schema conversion for ${tool.name}`,
30730
+ type: "unsupported"
30886
30731
  });
30732
+ parameters = buildSAPToolParameters({});
30887
30733
  }
30888
- if (streamState.activeText && textBlockId) {
30889
- controller.enqueue({ type: "text-end", id: textBlockId });
30890
- }
30891
- const finalFinishReason = streamResponse.getFinishReason();
30892
- if (finalFinishReason) {
30893
- streamState.finishReason = mapFinishReason(finalFinishReason);
30894
- } else if (didEmitAnyToolCalls) {
30895
- streamState.finishReason = {
30896
- unified: "tool-calls",
30897
- raw: void 0
30898
- };
30899
- }
30900
- const finalUsage = streamResponse.getTokenUsage();
30901
- if (finalUsage) {
30902
- streamState.usage.inputTokens.total = finalUsage.prompt_tokens;
30903
- streamState.usage.inputTokens.noCache = finalUsage.prompt_tokens;
30904
- streamState.usage.outputTokens.total = finalUsage.completion_tokens;
30905
- streamState.usage.outputTokens.text = finalUsage.completion_tokens;
30734
+ } else if (inputSchema && Object.keys(inputSchema).length > 0) {
30735
+ const hasProperties = inputSchema.properties && typeof inputSchema.properties === "object" && Object.keys(inputSchema.properties).length > 0;
30736
+ if (hasProperties) {
30737
+ parameters = buildSAPToolParameters(inputSchema);
30738
+ } else {
30739
+ parameters = buildSAPToolParameters({});
30906
30740
  }
30907
- controller.enqueue({
30908
- type: "finish",
30909
- finishReason: streamState.finishReason,
30910
- usage: streamState.usage
30911
- });
30912
- controller.close();
30913
- } catch (error) {
30914
- const aiError = convertToAISDKError(error, {
30915
- operation: "doStream",
30916
- url: "sap-ai:orchestration",
30917
- requestBody: createAISDKRequestBodySummary(options)
30918
- });
30919
- controller.enqueue({
30920
- type: "error",
30921
- error: aiError instanceof Error ? aiError : new Error(String(aiError))
30922
- });
30923
- controller.close();
30924
- }
30925
- },
30926
- cancel(reason) {
30927
- if (reason) {
30928
- console.debug("SAP AI stream cancelled:", reason);
30741
+ } else {
30742
+ parameters = buildSAPToolParameters({});
30929
30743
  }
30744
+ return {
30745
+ function: {
30746
+ description: tool.description,
30747
+ name: tool.name,
30748
+ parameters
30749
+ },
30750
+ type: "function"
30751
+ };
30752
+ } else {
30753
+ warnings.push({
30754
+ details: "Only 'function' tool type is supported.",
30755
+ feature: `tool type for ${tool.name}`,
30756
+ type: "unsupported"
30757
+ });
30758
+ return null;
30930
30759
  }
30760
+ }).filter((t) => t !== null);
30761
+ }
30762
+ const supportsN = !this.modelId.startsWith("amazon--") && !this.modelId.startsWith("anthropic--");
30763
+ const modelParams = {};
30764
+ const maxTokens = options.maxOutputTokens ?? providerOptions.modelParams?.maxTokens ?? this.settings.modelParams?.maxTokens;
30765
+ if (maxTokens !== void 0) modelParams.max_tokens = maxTokens;
30766
+ const temperature = options.temperature ?? providerOptions.modelParams?.temperature ?? this.settings.modelParams?.temperature;
30767
+ if (temperature !== void 0) modelParams.temperature = temperature;
30768
+ const topP = options.topP ?? providerOptions.modelParams?.topP ?? this.settings.modelParams?.topP;
30769
+ if (topP !== void 0) modelParams.top_p = topP;
30770
+ if (options.topK !== void 0) modelParams.top_k = options.topK;
30771
+ const frequencyPenalty = options.frequencyPenalty ?? providerOptions.modelParams?.frequencyPenalty ?? this.settings.modelParams?.frequencyPenalty;
30772
+ if (frequencyPenalty !== void 0) {
30773
+ modelParams.frequency_penalty = frequencyPenalty;
30774
+ }
30775
+ const presencePenalty = options.presencePenalty ?? providerOptions.modelParams?.presencePenalty ?? this.settings.modelParams?.presencePenalty;
30776
+ if (presencePenalty !== void 0) {
30777
+ modelParams.presence_penalty = presencePenalty;
30778
+ }
30779
+ if (supportsN) {
30780
+ const nValue = providerOptions.modelParams?.n ?? this.settings.modelParams?.n;
30781
+ if (nValue !== void 0) {
30782
+ modelParams.n = nValue;
30783
+ }
30784
+ }
30785
+ const parallelToolCalls = providerOptions.modelParams?.parallel_tool_calls ?? this.settings.modelParams?.parallel_tool_calls;
30786
+ if (parallelToolCalls !== void 0) {
30787
+ modelParams.parallel_tool_calls = parallelToolCalls;
30788
+ }
30789
+ if (options.stopSequences && options.stopSequences.length > 0) {
30790
+ modelParams.stop = options.stopSequences;
30791
+ }
30792
+ if (options.seed !== void 0) {
30793
+ modelParams.seed = options.seed;
30794
+ }
30795
+ validateModelParameters(
30796
+ {
30797
+ frequencyPenalty,
30798
+ maxTokens,
30799
+ n: modelParams.n,
30800
+ presencePenalty,
30801
+ temperature,
30802
+ topP
30803
+ },
30804
+ warnings
30805
+ );
30806
+ if (options.toolChoice && options.toolChoice.type !== "auto") {
30807
+ warnings.push({
30808
+ details: `SAP AI SDK does not support toolChoice '${options.toolChoice.type}'. Using default 'auto' behavior.`,
30809
+ feature: "toolChoice",
30810
+ type: "unsupported"
30931
30811
  });
30932
- return {
30933
- stream: transformedStream,
30934
- request: {
30935
- body: requestBody
30936
- }
30937
- };
30938
- } catch (error) {
30939
- throw convertToAISDKError(error, {
30940
- operation: "doStream",
30941
- url: "sap-ai:orchestration",
30942
- requestBody: createAISDKRequestBodySummary(options)
30812
+ }
30813
+ if (options.responseFormat?.type === "json") {
30814
+ warnings.push({
30815
+ message: "responseFormat JSON mode is forwarded to the underlying model; support and schema adherence depend on the model/deployment.",
30816
+ type: "other"
30943
30817
  });
30944
30818
  }
30819
+ const responseFormat = options.responseFormat?.type === "json" ? options.responseFormat.schema ? {
30820
+ json_schema: {
30821
+ description: options.responseFormat.description,
30822
+ name: options.responseFormat.name ?? "response",
30823
+ schema: options.responseFormat.schema,
30824
+ strict: null
30825
+ },
30826
+ type: "json_schema"
30827
+ } : { type: "json_object" } : void 0;
30828
+ const orchestrationConfig = {
30829
+ promptTemplating: {
30830
+ model: {
30831
+ name: this.modelId,
30832
+ params: modelParams,
30833
+ version: providerOptions.modelVersion ?? this.settings.modelVersion ?? "latest"
30834
+ },
30835
+ prompt: {
30836
+ template: [],
30837
+ tools: tools && tools.length > 0 ? tools : void 0,
30838
+ ...responseFormat ? { response_format: responseFormat } : {}
30839
+ }
30840
+ },
30841
+ ...(() => {
30842
+ const masking = providerOptions.masking ?? this.settings.masking;
30843
+ return masking && Object.keys(masking).length > 0 ? { masking } : {};
30844
+ })(),
30845
+ ...(() => {
30846
+ const filtering = providerOptions.filtering ?? this.settings.filtering;
30847
+ return filtering && Object.keys(filtering).length > 0 ? { filtering } : {};
30848
+ })(),
30849
+ ...(() => {
30850
+ const grounding = providerOptions.grounding ?? this.settings.grounding;
30851
+ return grounding && Object.keys(grounding).length > 0 ? { grounding } : {};
30852
+ })(),
30853
+ ...(() => {
30854
+ const translation = providerOptions.translation ?? this.settings.translation;
30855
+ return translation && Object.keys(translation).length > 0 ? { translation } : {};
30856
+ })()
30857
+ };
30858
+ return { messages, orchestrationConfig, warnings };
30859
+ }
30860
+ /**
30861
+ * Creates an OrchestrationClient instance.
30862
+ *
30863
+ * @param config - Orchestration module configuration
30864
+ * @returns OrchestrationClient instance
30865
+ * @internal
30866
+ */
30867
+ createClient(config) {
30868
+ return new import_orchestration.OrchestrationClient(
30869
+ config,
30870
+ this.config.deploymentConfig,
30871
+ this.config.destination
30872
+ );
30945
30873
  }
30946
30874
  };
30875
+ function buildSAPToolParameters(schema) {
30876
+ const schemaType = schema.type;
30877
+ if (schemaType !== void 0 && schemaType !== "object") {
30878
+ return {
30879
+ properties: {},
30880
+ required: [],
30881
+ type: "object"
30882
+ };
30883
+ }
30884
+ const properties = schema.properties && typeof schema.properties === "object" ? schema.properties : {};
30885
+ const required = Array.isArray(schema.required) && schema.required.every((item) => typeof item === "string") ? schema.required : [];
30886
+ const additionalFields = Object.fromEntries(
30887
+ Object.entries(schema).filter(
30888
+ ([key]) => key !== "type" && key !== "properties" && key !== "required"
30889
+ )
30890
+ );
30891
+ return {
30892
+ properties,
30893
+ required,
30894
+ type: "object",
30895
+ ...additionalFields
30896
+ };
30897
+ }
30898
+ function createAISDKRequestBodySummary(options) {
30899
+ return {
30900
+ hasImageParts: options.prompt.some(
30901
+ (message) => message.role === "user" && message.content.some(
30902
+ (part) => part.type === "file" && part.mediaType.startsWith("image/")
30903
+ )
30904
+ ),
30905
+ maxOutputTokens: options.maxOutputTokens,
30906
+ promptMessages: options.prompt.length,
30907
+ responseFormatType: options.responseFormat?.type,
30908
+ seed: options.seed,
30909
+ stopSequences: options.stopSequences?.length,
30910
+ temperature: options.temperature,
30911
+ toolChoiceType: options.toolChoice?.type,
30912
+ tools: options.tools?.length ?? 0,
30913
+ topK: options.topK,
30914
+ topP: options.topP
30915
+ };
30916
+ }
30917
+ function hasCallableParse(obj) {
30918
+ return typeof obj.parse === "function";
30919
+ }
30920
+ function isZodSchema(obj) {
30921
+ if (obj === null || typeof obj !== "object") {
30922
+ return false;
30923
+ }
30924
+ const record = obj;
30925
+ return "_def" in record && "parse" in record && hasCallableParse(record);
30926
+ }
30947
30927
  function mapFinishReason(reason) {
30948
30928
  const raw = reason;
30949
- if (!reason) return { unified: "other", raw };
30929
+ if (!reason) return { raw, unified: "other" };
30950
30930
  switch (reason.toLowerCase()) {
30951
- case "stop":
30931
+ case "content_filter":
30932
+ return { raw, unified: "content-filter" };
30952
30933
  case "end_turn":
30953
- case "stop_sequence":
30954
30934
  case "eos":
30955
- return { unified: "stop", raw };
30935
+ case "stop":
30936
+ case "stop_sequence":
30937
+ return { raw, unified: "stop" };
30938
+ case "error":
30939
+ return { raw, unified: "error" };
30940
+ case "function_call":
30941
+ case "tool_call":
30942
+ case "tool_calls":
30943
+ return { raw, unified: "tool-calls" };
30956
30944
  case "length":
30957
30945
  case "max_tokens":
30958
30946
  case "max_tokens_reached":
30959
- return { unified: "length", raw };
30960
- case "tool_calls":
30961
- case "tool_call":
30962
- case "function_call":
30963
- return { unified: "tool-calls", raw };
30964
- case "content_filter":
30965
- return { unified: "content-filter", raw };
30966
- case "error":
30967
- return { unified: "error", raw };
30947
+ return { raw, unified: "length" };
30968
30948
  default:
30969
- return { unified: "other", raw };
30949
+ return { raw, unified: "other" };
30950
+ }
30951
+ }
30952
+ function validateModelParameters(params, warnings) {
30953
+ if (params.temperature !== void 0 && (params.temperature < 0 || params.temperature > 2)) {
30954
+ warnings.push({
30955
+ message: `temperature=${String(params.temperature)} is outside typical range [0, 2]. The API may reject this value.`,
30956
+ type: "other"
30957
+ });
30958
+ }
30959
+ if (params.topP !== void 0 && (params.topP < 0 || params.topP > 1)) {
30960
+ warnings.push({
30961
+ message: `topP=${String(params.topP)} is outside valid range [0, 1]. The API may reject this value.`,
30962
+ type: "other"
30963
+ });
30964
+ }
30965
+ if (params.frequencyPenalty !== void 0 && (params.frequencyPenalty < -2 || params.frequencyPenalty > 2)) {
30966
+ warnings.push({
30967
+ message: `frequencyPenalty=${String(params.frequencyPenalty)} is outside typical range [-2, 2]. The API may reject this value.`,
30968
+ type: "other"
30969
+ });
30970
+ }
30971
+ if (params.presencePenalty !== void 0 && (params.presencePenalty < -2 || params.presencePenalty > 2)) {
30972
+ warnings.push({
30973
+ message: `presencePenalty=${String(params.presencePenalty)} is outside typical range [-2, 2]. The API may reject this value.`,
30974
+ type: "other"
30975
+ });
30976
+ }
30977
+ if (params.maxTokens !== void 0 && params.maxTokens <= 0) {
30978
+ warnings.push({
30979
+ message: `maxTokens=${String(params.maxTokens)} must be positive. The API will likely reject this value.`,
30980
+ type: "other"
30981
+ });
30982
+ }
30983
+ if (params.n !== void 0 && params.n <= 0) {
30984
+ warnings.push({
30985
+ message: `n=${String(params.n)} must be positive. The API will likely reject this value.`,
30986
+ type: "other"
30987
+ });
30970
30988
  }
30971
30989
  }
30972
30990
 
@@ -30984,19 +31002,19 @@ function createSAPAIProvider(options = {}) {
30984
31002
  const mergedSettings = {
30985
31003
  ...options.defaultSettings,
30986
31004
  ...settings,
31005
+ filtering: settings.filtering ?? options.defaultSettings?.filtering,
31006
+ // Complex objects: override, do not merge
31007
+ masking: settings.masking ?? options.defaultSettings?.masking,
30987
31008
  modelParams: {
30988
31009
  ...options.defaultSettings?.modelParams ?? {},
30989
31010
  ...settings.modelParams ?? {}
30990
31011
  },
30991
- // Complex objects: override, do not merge
30992
- masking: settings.masking ?? options.defaultSettings?.masking,
30993
- filtering: settings.filtering ?? options.defaultSettings?.filtering,
30994
31012
  tools: settings.tools ?? options.defaultSettings?.tools
30995
31013
  };
30996
- return new SAPAIChatLanguageModel(modelId, mergedSettings, {
30997
- provider: "sap-ai",
31014
+ return new SAPAILanguageModel(modelId, mergedSettings, {
30998
31015
  deploymentConfig,
30999
- destination: options.destination
31016
+ destination: options.destination,
31017
+ provider: "sap-ai"
31000
31018
  });
31001
31019
  };
31002
31020
  const provider = function(modelId, settings) {
@@ -31012,17 +31030,12 @@ function createSAPAIProvider(options = {}) {
31012
31030
  }
31013
31031
  var sapai = createSAPAIProvider();
31014
31032
 
31015
- // src/sap-ai-chat-settings.ts
31033
+ // src/sap-ai-settings.ts
31016
31034
  var import_orchestration2 = require("@sap-ai-sdk/orchestration");
31017
-
31018
- // src/types/completion-request.ts
31019
31035
  var import_orchestration3 = require("@sap-ai-sdk/orchestration");
31020
31036
 
31021
- // src/types/completion-response.ts
31022
- var import_orchestration4 = require("@sap-ai-sdk/orchestration");
31023
-
31024
31037
  // src/index.ts
31025
- var import_orchestration5 = require("@sap-ai-sdk/orchestration");
31038
+ var import_orchestration4 = require("@sap-ai-sdk/orchestration");
31026
31039
  // Annotate the CommonJS export names for ESM import in node:
31027
31040
  0 && (module.exports = {
31028
31041
  OrchestrationClient,