@jerome-benoit/sap-ai-provider 4.1.0 → 4.1.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
@@ -29765,6 +29765,17 @@ var import_orchestration = require("@sap-ai-sdk/orchestration");
29765
29765
  // src/sap-ai-error.ts
29766
29766
  var import_provider = require("@ai-sdk/provider");
29767
29767
  var import_util = __toESM(require_dist(), 1);
29768
+ var HTTP_STATUS = {
29769
+ BAD_REQUEST: 400,
29770
+ CONFLICT: 409,
29771
+ FORBIDDEN: 403,
29772
+ INTERNAL_ERROR: 500,
29773
+ NOT_FOUND: 404,
29774
+ RATE_LIMIT: 429,
29775
+ REQUEST_TIMEOUT: 408,
29776
+ SERVICE_UNAVAILABLE: 503,
29777
+ UNAUTHORIZED: 401
29778
+ };
29768
29779
  function convertSAPErrorToAPICallError(errorResponse, context) {
29769
29780
  const error = errorResponse.error;
29770
29781
  let message;
@@ -29793,7 +29804,7 @@ function convertSAPErrorToAPICallError(errorResponse, context) {
29793
29804
  }
29794
29805
  });
29795
29806
  let enhancedMessage = message;
29796
- if (statusCode === 401 || statusCode === 403) {
29807
+ if (statusCode === HTTP_STATUS.UNAUTHORIZED || statusCode === HTTP_STATUS.FORBIDDEN) {
29797
29808
  enhancedMessage += "\n\nAuthentication failed. Verify your AICORE_SERVICE_KEY environment variable is set correctly.\nSee: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-service-key";
29798
29809
  if (requestId) {
29799
29810
  enhancedMessage += `
@@ -29803,7 +29814,7 @@ Request ID: ${requestId}`;
29803
29814
  message: enhancedMessage
29804
29815
  });
29805
29816
  }
29806
- if (statusCode === 404) {
29817
+ if (statusCode === HTTP_STATUS.NOT_FOUND) {
29807
29818
  enhancedMessage += "\n\nResource not found. The model or deployment may not exist in your SAP AI Core instance.\nSee: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-deployment-for-orchestration";
29808
29819
  if (requestId) {
29809
29820
  enhancedMessage += `
@@ -29816,9 +29827,9 @@ Request ID: ${requestId}`;
29816
29827
  modelType: "languageModel"
29817
29828
  });
29818
29829
  }
29819
- if (statusCode === 429) {
29830
+ if (statusCode === HTTP_STATUS.RATE_LIMIT) {
29820
29831
  enhancedMessage += "\n\nRate limit exceeded. Please try again later or contact your SAP administrator.\nSee: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/rate-limits";
29821
- } else if (statusCode >= 500) {
29832
+ } else if (statusCode >= HTTP_STATUS.INTERNAL_ERROR) {
29822
29833
  enhancedMessage += "\n\nSAP AI Core service error. This is typically a temporary issue. The request will be retried automatically.\nSee: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/troubleshooting";
29823
29834
  } else if (location) {
29824
29835
  enhancedMessage += `
@@ -29878,7 +29889,7 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-serv
29878
29889
  message: `Network error connecting to SAP AI Core: ${originalMsg}`,
29879
29890
  requestBodyValues: context?.requestBody,
29880
29891
  responseHeaders,
29881
- statusCode: 503,
29892
+ statusCode: HTTP_STATUS.SERVICE_UNAVAILABLE,
29882
29893
  url: context?.url ?? ""
29883
29894
  });
29884
29895
  }
@@ -29891,7 +29902,7 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-serv
29891
29902
  Check your destination configuration or provide a valid destinationName.`,
29892
29903
  requestBodyValues: context?.requestBody,
29893
29904
  responseHeaders,
29894
- statusCode: 500,
29905
+ statusCode: HTTP_STATUS.BAD_REQUEST,
29895
29906
  url: context?.url ?? ""
29896
29907
  });
29897
29908
  }
@@ -29915,7 +29926,7 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-depl
29915
29926
  The model's response was blocked by content safety filters. Try a different prompt.`,
29916
29927
  requestBodyValues: context?.requestBody,
29917
29928
  responseHeaders,
29918
- statusCode: 400,
29929
+ statusCode: HTTP_STATUS.BAD_REQUEST,
29919
29930
  url: context?.url ?? ""
29920
29931
  });
29921
29932
  }
@@ -29932,14 +29943,91 @@ The model's response was blocked by content safety filters. Try a different prom
29932
29943
  url: context?.url ?? ""
29933
29944
  });
29934
29945
  }
29935
- if (errorMsg.includes("iterate over") || errorMsg.includes("consumed stream") || errorMsg.includes("parse message into json") || errorMsg.includes("no body")) {
29946
+ if (errorMsg.includes("consumed stream")) {
29947
+ return new import_provider.APICallError({
29948
+ cause: error,
29949
+ isRetryable: false,
29950
+ message: `SAP AI Core stream consumption error: ${originalMsg}`,
29951
+ requestBodyValues: context?.requestBody,
29952
+ responseHeaders,
29953
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29954
+ url: context?.url ?? ""
29955
+ });
29956
+ }
29957
+ if (errorMsg.includes("iterating over") || errorMsg.includes("parse message into json") || errorMsg.includes("received from") || errorMsg.includes("no body") || errorMsg.includes("invalid sse payload")) {
29936
29958
  return new import_provider.APICallError({
29937
29959
  cause: error,
29938
29960
  isRetryable: true,
29939
29961
  message: `SAP AI Core streaming error: ${originalMsg}`,
29940
29962
  requestBodyValues: context?.requestBody,
29941
29963
  responseHeaders,
29942
- statusCode: 500,
29964
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29965
+ url: context?.url ?? ""
29966
+ });
29967
+ }
29968
+ if (errorMsg.includes("prompt template or messages must be defined") || errorMsg.includes("filtering parameters cannot be empty") || errorMsg.includes("templating yaml string must be non-empty") || errorMsg.includes("could not access response data") || errorMsg.includes("could not parse json") || errorMsg.includes("error parsing yaml") || errorMsg.includes("yaml does not conform") || errorMsg.includes("validation errors")) {
29969
+ return new import_provider.APICallError({
29970
+ cause: error,
29971
+ isRetryable: false,
29972
+ message: `SAP AI Core configuration error: ${originalMsg}`,
29973
+ requestBodyValues: context?.requestBody,
29974
+ responseHeaders,
29975
+ statusCode: HTTP_STATUS.BAD_REQUEST,
29976
+ url: context?.url ?? ""
29977
+ });
29978
+ }
29979
+ if (errorMsg.includes("buffer is not available as globals")) {
29980
+ return new import_provider.APICallError({
29981
+ cause: error,
29982
+ isRetryable: false,
29983
+ message: `SAP AI Core environment error: ${originalMsg}`,
29984
+ requestBodyValues: context?.requestBody,
29985
+ responseHeaders,
29986
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29987
+ url: context?.url ?? ""
29988
+ });
29989
+ }
29990
+ if (errorMsg.includes("response stream is undefined")) {
29991
+ return new import_provider.APICallError({
29992
+ cause: error,
29993
+ isRetryable: false,
29994
+ message: `SAP AI Core response stream error: ${originalMsg}`,
29995
+ requestBodyValues: context?.requestBody,
29996
+ responseHeaders,
29997
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29998
+ url: context?.url ?? ""
29999
+ });
30000
+ }
30001
+ if (errorMsg.includes("response is required to process") || errorMsg.includes("stream is still open") || errorMsg.includes("data is not available yet")) {
30002
+ return new import_provider.APICallError({
30003
+ cause: error,
30004
+ isRetryable: true,
30005
+ message: `SAP AI Core response processing error: ${originalMsg}`,
30006
+ requestBodyValues: context?.requestBody,
30007
+ responseHeaders,
30008
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
30009
+ url: context?.url ?? ""
30010
+ });
30011
+ }
30012
+ if (errorMsg.includes("failed to fetch the list of deployments")) {
30013
+ return new import_provider.APICallError({
30014
+ cause: error,
30015
+ isRetryable: true,
30016
+ message: `SAP AI Core deployment retrieval error: ${originalMsg}`,
30017
+ requestBodyValues: context?.requestBody,
30018
+ responseHeaders,
30019
+ statusCode: HTTP_STATUS.SERVICE_UNAVAILABLE,
30020
+ url: context?.url ?? ""
30021
+ });
30022
+ }
30023
+ if (errorMsg.includes("received non-uint8array")) {
30024
+ return new import_provider.APICallError({
30025
+ cause: error,
30026
+ isRetryable: false,
30027
+ message: `SAP AI Core stream buffer error: ${originalMsg}`,
30028
+ requestBodyValues: context?.requestBody,
30029
+ responseHeaders,
30030
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29943
30031
  url: context?.url ?? ""
29944
30032
  });
29945
30033
  }
@@ -29952,7 +30040,7 @@ The model's response was blocked by content safety filters. Try a different prom
29952
30040
  message: fullMessage,
29953
30041
  requestBodyValues: context?.requestBody,
29954
30042
  responseHeaders,
29955
- statusCode: 500,
30043
+ statusCode: HTTP_STATUS.INTERNAL_ERROR,
29956
30044
  url: context?.url ?? ""
29957
30045
  });
29958
30046
  }
@@ -29985,11 +30073,11 @@ function getAxiosResponseHeaders(error) {
29985
30073
  return normalizeHeaders(maybeAxios.response?.headers);
29986
30074
  }
29987
30075
  function getStatusCodeFromSAPError(code) {
29988
- if (!code) return 500;
30076
+ if (!code) return HTTP_STATUS.INTERNAL_ERROR;
29989
30077
  if (code >= 100 && code < 600) {
29990
30078
  return code;
29991
30079
  }
29992
- return 500;
30080
+ return HTTP_STATUS.INTERNAL_ERROR;
29993
30081
  }
29994
30082
  function isOrchestrationErrorResponse(error) {
29995
30083
  if (error === null || typeof error !== "object" || !("error" in error)) {
@@ -29999,14 +30087,34 @@ function isOrchestrationErrorResponse(error) {
29999
30087
  const innerError = errorEnvelope.error;
30000
30088
  if (innerError === void 0) return false;
30001
30089
  if (Array.isArray(innerError)) {
30002
- return innerError.every(
30003
- (entry) => entry !== null && typeof entry === "object" && "message" in entry && typeof entry.message === "string"
30004
- );
30090
+ return innerError.every((entry) => {
30091
+ if (entry === null || typeof entry !== "object" || !("message" in entry)) {
30092
+ return false;
30093
+ }
30094
+ const errorEntry = entry;
30095
+ if (typeof errorEntry.message !== "string") {
30096
+ return false;
30097
+ }
30098
+ if ("code" in entry && typeof errorEntry.code !== "number") {
30099
+ return false;
30100
+ }
30101
+ return true;
30102
+ });
30103
+ }
30104
+ if (typeof innerError !== "object" || innerError === null || !("message" in innerError)) {
30105
+ return false;
30106
+ }
30107
+ const errorObj = innerError;
30108
+ if (typeof errorObj.message !== "string") {
30109
+ return false;
30110
+ }
30111
+ if ("code" in innerError && typeof errorObj.code !== "number") {
30112
+ return false;
30005
30113
  }
30006
- return typeof innerError === "object" && innerError !== null && "message" in innerError && typeof innerError.message === "string";
30114
+ return true;
30007
30115
  }
30008
30116
  function isRetryable(statusCode) {
30009
- return statusCode === 408 || statusCode === 409 || statusCode === 429 || statusCode >= 500 && statusCode < 600;
30117
+ return statusCode === HTTP_STATUS.REQUEST_TIMEOUT || statusCode === HTTP_STATUS.CONFLICT || statusCode === HTTP_STATUS.RATE_LIMIT || statusCode >= HTTP_STATUS.INTERNAL_ERROR && statusCode < 600;
30010
30118
  }
30011
30119
  function normalizeHeaders(headers) {
30012
30120
  if (!headers || typeof headers !== "object") return void 0;
@@ -30084,9 +30192,46 @@ var modelParamsSchema = import_zod.z.object({
30084
30192
  */
30085
30193
  topP: import_zod.z.number().min(0).max(1).optional()
30086
30194
  }).catchall(import_zod.z.unknown());
30195
+ var embeddingModelParamsSchema = import_zod.z.object({
30196
+ /**
30197
+ * The number of dimensions for output embeddings.
30198
+ * Must be a positive integer. Support varies by model.
30199
+ * - text-embedding-3-small: supports 512, 1536
30200
+ * - text-embedding-3-large: supports 256, 1024, 3072
30201
+ */
30202
+ dimensions: import_zod.z.number().int().positive().optional(),
30203
+ /**
30204
+ * Encoding format for embeddings.
30205
+ * - 'float': Array of floats (default)
30206
+ * - 'base64': Base64-encoded binary
30207
+ * - 'binary': Raw binary format
30208
+ */
30209
+ encoding_format: import_zod.z.enum(["base64", "binary", "float"]).optional(),
30210
+ /**
30211
+ * Whether to normalize the embedding vectors.
30212
+ * When true, vectors are normalized to unit length.
30213
+ */
30214
+ normalize: import_zod.z.boolean().optional()
30215
+ }).catchall(import_zod.z.unknown());
30216
+ function validateEmbeddingModelParamsSettings(modelParams) {
30217
+ return embeddingModelParamsSchema.parse(modelParams);
30218
+ }
30087
30219
  function validateModelParamsSettings(modelParams) {
30088
30220
  return modelParamsSchema.parse(modelParams);
30089
30221
  }
30222
+ function validateModelParamsWithWarnings(params, warnings) {
30223
+ const result = modelParamsSchema.safeParse(params);
30224
+ if (!result.success) {
30225
+ for (const issue of result.error.issues) {
30226
+ const path = issue.path.join(".");
30227
+ const value = path ? params[path] : void 0;
30228
+ warnings.push({
30229
+ message: `${path}=${String(value)} is invalid: ${issue.message}. The API may reject this value.`,
30230
+ type: "other"
30231
+ });
30232
+ }
30233
+ }
30234
+ }
30090
30235
  var sapAILanguageModelProviderOptions = (0, import_provider_utils.lazySchema)(
30091
30236
  () => (0, import_provider_utils.zodSchema)(
30092
30237
  import_zod.z.object({
@@ -30112,8 +30257,14 @@ var sapAIEmbeddingProviderOptions = (0, import_provider_utils.lazySchema)(
30112
30257
  /**
30113
30258
  * Additional model parameters for this call.
30114
30259
  * Passed directly to the embedding API.
30260
+ *
30261
+ * Known parameters:
30262
+ * - `dimensions`: Output embedding dimensions (model-dependent)
30263
+ * - `encoding_format`: 'float' | 'base64' | 'binary'
30264
+ * - `normalize`: Whether to normalize vectors
30265
+ * @see {@link embeddingModelParamsSchema} for validation rules
30115
30266
  */
30116
- modelParams: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
30267
+ modelParams: embeddingModelParamsSchema.optional(),
30117
30268
  /**
30118
30269
  * Embedding task type for this specific call.
30119
30270
  * Overrides the constructor `type` setting.
@@ -30161,6 +30312,9 @@ var SAPAIEmbeddingModel = class {
30161
30312
  * @param config - Internal configuration from the provider
30162
30313
  */
30163
30314
  constructor(modelId, settings = {}, config) {
30315
+ if (settings.modelParams) {
30316
+ validateEmbeddingModelParamsSettings(settings.modelParams);
30317
+ }
30164
30318
  this.modelId = modelId;
30165
30319
  this.settings = settings;
30166
30320
  this.config = config;
@@ -30298,7 +30452,7 @@ function convertToSAPMessages(prompt, options = {}) {
30298
30452
  switch (part.type) {
30299
30453
  case "reasoning": {
30300
30454
  if (includeReasoning && part.text) {
30301
- text += `<reasoning>${part.text}</reasoning>`;
30455
+ text += `<think>${part.text}</think>`;
30302
30456
  }
30303
30457
  break;
30304
30458
  }
@@ -31142,7 +31296,7 @@ var SAPAILanguageModel = class {
31142
31296
  if (options.seed !== void 0) {
31143
31297
  modelParams.seed = options.seed;
31144
31298
  }
31145
- validateAISDKParameters(
31299
+ validateModelParamsWithWarnings(
31146
31300
  {
31147
31301
  frequencyPenalty: options.frequencyPenalty,
31148
31302
  maxTokens: options.maxOutputTokens,
@@ -31291,38 +31445,6 @@ function mapFinishReason(reason) {
31291
31445
  return { raw, unified: "other" };
31292
31446
  }
31293
31447
  }
31294
- function validateAISDKParameters(params, warnings) {
31295
- if (params.temperature !== void 0 && (params.temperature < 0 || params.temperature > 2)) {
31296
- warnings.push({
31297
- message: `temperature=${String(params.temperature)} is outside typical range [0, 2]. The API may reject this value.`,
31298
- type: "other"
31299
- });
31300
- }
31301
- if (params.topP !== void 0 && (params.topP < 0 || params.topP > 1)) {
31302
- warnings.push({
31303
- message: `topP=${String(params.topP)} is outside valid range [0, 1]. The API may reject this value.`,
31304
- type: "other"
31305
- });
31306
- }
31307
- if (params.frequencyPenalty !== void 0 && (params.frequencyPenalty < -2 || params.frequencyPenalty > 2)) {
31308
- warnings.push({
31309
- message: `frequencyPenalty=${String(params.frequencyPenalty)} is outside typical range [-2, 2]. The API may reject this value.`,
31310
- type: "other"
31311
- });
31312
- }
31313
- if (params.presencePenalty !== void 0 && (params.presencePenalty < -2 || params.presencePenalty > 2)) {
31314
- warnings.push({
31315
- message: `presencePenalty=${String(params.presencePenalty)} is outside typical range [-2, 2]. The API may reject this value.`,
31316
- type: "other"
31317
- });
31318
- }
31319
- if (params.maxTokens !== void 0 && params.maxTokens <= 0) {
31320
- warnings.push({
31321
- message: `maxTokens=${String(params.maxTokens)} must be positive. The API will likely reject this value.`,
31322
- type: "other"
31323
- });
31324
- }
31325
- }
31326
31448
 
31327
31449
  // src/sap-ai-provider.ts
31328
31450
  function createSAPAIProvider(options = {}) {