@jerome-benoit/sap-ai-provider 3.0.0 → 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/README.md +168 -113
- package/dist/index.cjs +713 -595
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +324 -223
- package/dist/index.d.ts +324 -223
- package/dist/index.js +713 -594
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/index.cjs
CHANGED
|
@@ -24766,7 +24766,7 @@ var require_form_data = __commonJS({
|
|
|
24766
24766
|
var parseUrl = require("url").parse;
|
|
24767
24767
|
var fs = require("fs");
|
|
24768
24768
|
var Stream = require("stream").Stream;
|
|
24769
|
-
var
|
|
24769
|
+
var crypto2 = require("crypto");
|
|
24770
24770
|
var mime = require_mime_types();
|
|
24771
24771
|
var asynckit = require_asynckit();
|
|
24772
24772
|
var setToStringTag = require_es_set_tostringtag();
|
|
@@ -24972,7 +24972,7 @@ var require_form_data = __commonJS({
|
|
|
24972
24972
|
return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]);
|
|
24973
24973
|
};
|
|
24974
24974
|
FormData2.prototype._generateBoundary = function() {
|
|
24975
|
-
this._boundary = "--------------------------" +
|
|
24975
|
+
this._boundary = "--------------------------" + crypto2.randomBytes(12).toString("hex");
|
|
24976
24976
|
};
|
|
24977
24977
|
FormData2.prototype.getLengthSync = function() {
|
|
24978
24978
|
var knownLength = this._overheadLength + this._valueLength;
|
|
@@ -26202,7 +26202,7 @@ var require_axios = __commonJS({
|
|
|
26202
26202
|
"node_modules/axios/dist/node/axios.cjs"(exports2, module2) {
|
|
26203
26203
|
"use strict";
|
|
26204
26204
|
var FormData$1 = require_form_data();
|
|
26205
|
-
var
|
|
26205
|
+
var crypto2 = require("crypto");
|
|
26206
26206
|
var url = require("url");
|
|
26207
26207
|
var proxyFromEnv = require_proxy_from_env();
|
|
26208
26208
|
var http = require("http");
|
|
@@ -26217,7 +26217,7 @@ var require_axios = __commonJS({
|
|
|
26217
26217
|
return e && typeof e === "object" && "default" in e ? e : { "default": e };
|
|
26218
26218
|
}
|
|
26219
26219
|
var FormData__default = /* @__PURE__ */ _interopDefaultLegacy(FormData$1);
|
|
26220
|
-
var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(
|
|
26220
|
+
var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto2);
|
|
26221
26221
|
var url__default = /* @__PURE__ */ _interopDefaultLegacy(url);
|
|
26222
26222
|
var proxyFromEnv__default = /* @__PURE__ */ _interopDefaultLegacy(proxyFromEnv);
|
|
26223
26223
|
var http__default = /* @__PURE__ */ _interopDefaultLegacy(http);
|
|
@@ -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: () =>
|
|
29741
|
-
OrchestrationResponse: () =>
|
|
29742
|
-
OrchestrationStreamChunkResponse: () =>
|
|
29743
|
-
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: () =>
|
|
29750
|
+
isConfigReference: () => import_orchestration2.isConfigReference,
|
|
29751
29751
|
sapai: () => sapai
|
|
29752
29752
|
});
|
|
29753
29753
|
module.exports = __toCommonJS(index_exports);
|
|
29754
29754
|
|
|
29755
|
-
// src/sap-ai-
|
|
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
|
-
|
|
29770
|
-
|
|
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,73 +29899,15 @@ function convertToSAPMessages(prompt, options = {}) {
|
|
|
29839
29899
|
}
|
|
29840
29900
|
}
|
|
29841
29901
|
const userMessage = contentParts.length === 1 && contentParts[0].type === "text" ? {
|
|
29842
|
-
|
|
29843
|
-
|
|
29902
|
+
content: contentParts[0].text ?? "",
|
|
29903
|
+
role: "user"
|
|
29844
29904
|
} : {
|
|
29845
|
-
|
|
29846
|
-
|
|
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
|
-
const toolMessage = {
|
|
29901
|
-
role: "tool",
|
|
29902
|
-
tool_call_id: part.toolCallId,
|
|
29903
|
-
content: JSON.stringify(part.output)
|
|
29904
|
-
};
|
|
29905
|
-
messages.push(toolMessage);
|
|
29906
|
-
}
|
|
29907
|
-
break;
|
|
29908
|
-
}
|
|
29909
29911
|
default: {
|
|
29910
29912
|
const _exhaustiveCheck = message;
|
|
29911
29913
|
throw new Error(
|
|
@@ -29920,16 +29922,6 @@ function convertToSAPMessages(prompt, options = {}) {
|
|
|
29920
29922
|
// src/sap-ai-error.ts
|
|
29921
29923
|
var import_provider2 = require("@ai-sdk/provider");
|
|
29922
29924
|
var import_util = __toESM(require_dist(), 1);
|
|
29923
|
-
function getStatusCodeFromSAPError(code) {
|
|
29924
|
-
if (!code) return 500;
|
|
29925
|
-
if (code >= 100 && code < 600) {
|
|
29926
|
-
return code;
|
|
29927
|
-
}
|
|
29928
|
-
return 500;
|
|
29929
|
-
}
|
|
29930
|
-
function isRetryable(statusCode) {
|
|
29931
|
-
return statusCode === 429 || statusCode >= 500 && statusCode < 600;
|
|
29932
|
-
}
|
|
29933
29925
|
function convertSAPErrorToAPICallError(errorResponse, context) {
|
|
29934
29926
|
const error = errorResponse.error;
|
|
29935
29927
|
let message;
|
|
@@ -29951,9 +29943,9 @@ function convertSAPErrorToAPICallError(errorResponse, context) {
|
|
|
29951
29943
|
const statusCode = getStatusCodeFromSAPError(code);
|
|
29952
29944
|
const responseBody = JSON.stringify({
|
|
29953
29945
|
error: {
|
|
29954
|
-
message,
|
|
29955
29946
|
code,
|
|
29956
29947
|
location,
|
|
29948
|
+
message,
|
|
29957
29949
|
request_id: requestId
|
|
29958
29950
|
}
|
|
29959
29951
|
});
|
|
@@ -29976,53 +29968,14 @@ Error location: ${location}`;
|
|
|
29976
29968
|
Request ID: ${requestId}`;
|
|
29977
29969
|
}
|
|
29978
29970
|
return new import_provider2.APICallError({
|
|
29971
|
+
isRetryable: isRetryable(statusCode),
|
|
29979
29972
|
message: enhancedMessage,
|
|
29980
|
-
url: context?.url ?? "",
|
|
29981
29973
|
requestBodyValues: context?.requestBody,
|
|
29982
|
-
statusCode,
|
|
29983
|
-
responseHeaders: context?.responseHeaders,
|
|
29984
29974
|
responseBody,
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
function isOrchestrationErrorResponse(error) {
|
|
29989
|
-
if (error === null || typeof error !== "object" || !("error" in error)) {
|
|
29990
|
-
return false;
|
|
29991
|
-
}
|
|
29992
|
-
const errorEnvelope = error;
|
|
29993
|
-
const innerError = errorEnvelope.error;
|
|
29994
|
-
if (innerError === void 0) return false;
|
|
29995
|
-
if (Array.isArray(innerError)) {
|
|
29996
|
-
return innerError.every(
|
|
29997
|
-
(entry) => entry !== null && typeof entry === "object" && "message" in entry && typeof entry.message === "string"
|
|
29998
|
-
);
|
|
29999
|
-
}
|
|
30000
|
-
return typeof innerError === "object" && innerError !== null && "message" in innerError && typeof innerError.message === "string";
|
|
30001
|
-
}
|
|
30002
|
-
function normalizeHeaders(headers) {
|
|
30003
|
-
if (!headers || typeof headers !== "object") return void 0;
|
|
30004
|
-
const record = headers;
|
|
30005
|
-
const entries = Object.entries(record).flatMap(([key, value]) => {
|
|
30006
|
-
if (typeof value === "string") return [[key, value]];
|
|
30007
|
-
if (Array.isArray(value)) {
|
|
30008
|
-
const strings = value.filter((item) => typeof item === "string").join("; ");
|
|
30009
|
-
return strings.length > 0 ? [[key, strings]] : [];
|
|
30010
|
-
}
|
|
30011
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
30012
|
-
return [[key, String(value)]];
|
|
30013
|
-
}
|
|
30014
|
-
return [];
|
|
29975
|
+
responseHeaders: context?.responseHeaders,
|
|
29976
|
+
statusCode,
|
|
29977
|
+
url: context?.url ?? ""
|
|
30015
29978
|
});
|
|
30016
|
-
if (entries.length === 0) return void 0;
|
|
30017
|
-
return Object.fromEntries(entries);
|
|
30018
|
-
}
|
|
30019
|
-
function getAxiosResponseHeaders(error) {
|
|
30020
|
-
if (!(error instanceof Error)) return void 0;
|
|
30021
|
-
const rootCause = (0, import_util.isErrorWithCause)(error) ? error.rootCause : error;
|
|
30022
|
-
if (typeof rootCause !== "object") return void 0;
|
|
30023
|
-
const maybeAxios = rootCause;
|
|
30024
|
-
if (maybeAxios.isAxiosError !== true) return void 0;
|
|
30025
|
-
return normalizeHeaders(maybeAxios.response?.headers);
|
|
30026
29979
|
}
|
|
30027
29980
|
function convertToAISDKError(error, context) {
|
|
30028
29981
|
if (error instanceof import_provider2.APICallError || error instanceof import_provider2.LoadAPIKeyError) {
|
|
@@ -30047,167 +30000,92 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-serv
|
|
|
30047
30000
|
}
|
|
30048
30001
|
if (errorMsg.includes("econnrefused") || errorMsg.includes("enotfound") || errorMsg.includes("network") || errorMsg.includes("timeout")) {
|
|
30049
30002
|
return new import_provider2.APICallError({
|
|
30003
|
+
cause: error,
|
|
30004
|
+
isRetryable: true,
|
|
30050
30005
|
message: `Network error connecting to SAP AI Core: ${error.message}`,
|
|
30051
|
-
url: context?.url ?? "",
|
|
30052
30006
|
requestBodyValues: context?.requestBody,
|
|
30053
|
-
statusCode: 503,
|
|
30054
|
-
isRetryable: true,
|
|
30055
30007
|
responseHeaders,
|
|
30056
|
-
|
|
30008
|
+
statusCode: 503,
|
|
30009
|
+
url: context?.url ?? ""
|
|
30057
30010
|
});
|
|
30058
30011
|
}
|
|
30059
30012
|
}
|
|
30060
30013
|
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error occurred";
|
|
30061
30014
|
const fullMessage = context?.operation ? `SAP AI Core ${context.operation} failed: ${message}` : `SAP AI Core error: ${message}`;
|
|
30062
30015
|
return new import_provider2.APICallError({
|
|
30016
|
+
cause: error,
|
|
30017
|
+
isRetryable: false,
|
|
30063
30018
|
message: fullMessage,
|
|
30064
|
-
url: context?.url ?? "",
|
|
30065
30019
|
requestBodyValues: context?.requestBody,
|
|
30066
|
-
statusCode: 500,
|
|
30067
|
-
isRetryable: false,
|
|
30068
30020
|
responseHeaders,
|
|
30069
|
-
|
|
30021
|
+
statusCode: 500,
|
|
30022
|
+
url: context?.url ?? ""
|
|
30070
30023
|
});
|
|
30071
30024
|
}
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30075
|
-
if (
|
|
30076
|
-
|
|
30077
|
-
|
|
30078
|
-
|
|
30079
|
-
});
|
|
30080
|
-
}
|
|
30081
|
-
if (params.topP !== void 0 && (params.topP < 0 || params.topP > 1)) {
|
|
30082
|
-
warnings.push({
|
|
30083
|
-
type: "other",
|
|
30084
|
-
message: `topP=${String(params.topP)} is outside valid range [0, 1]. The API may reject this value.`
|
|
30085
|
-
});
|
|
30086
|
-
}
|
|
30087
|
-
if (params.frequencyPenalty !== void 0 && (params.frequencyPenalty < -2 || params.frequencyPenalty > 2)) {
|
|
30088
|
-
warnings.push({
|
|
30089
|
-
type: "other",
|
|
30090
|
-
message: `frequencyPenalty=${String(params.frequencyPenalty)} is outside typical range [-2, 2]. The API may reject this value.`
|
|
30091
|
-
});
|
|
30092
|
-
}
|
|
30093
|
-
if (params.presencePenalty !== void 0 && (params.presencePenalty < -2 || params.presencePenalty > 2)) {
|
|
30094
|
-
warnings.push({
|
|
30095
|
-
type: "other",
|
|
30096
|
-
message: `presencePenalty=${String(params.presencePenalty)} is outside typical range [-2, 2]. The API may reject this value.`
|
|
30097
|
-
});
|
|
30098
|
-
}
|
|
30099
|
-
if (params.maxTokens !== void 0 && params.maxTokens <= 0) {
|
|
30100
|
-
warnings.push({
|
|
30101
|
-
type: "other",
|
|
30102
|
-
message: `maxTokens=${String(params.maxTokens)} must be positive. The API will likely reject this value.`
|
|
30103
|
-
});
|
|
30104
|
-
}
|
|
30105
|
-
if (params.n !== void 0 && params.n <= 0) {
|
|
30106
|
-
warnings.push({
|
|
30107
|
-
type: "other",
|
|
30108
|
-
message: `n=${String(params.n)} must be positive. The API will likely reject this value.`
|
|
30109
|
-
});
|
|
30110
|
-
}
|
|
30111
|
-
}
|
|
30112
|
-
function createAISDKRequestBodySummary(options) {
|
|
30113
|
-
return {
|
|
30114
|
-
promptMessages: options.prompt.length,
|
|
30115
|
-
hasImageParts: options.prompt.some(
|
|
30116
|
-
(message) => message.role === "user" && message.content.some(
|
|
30117
|
-
(part) => part.type === "file" && part.mediaType.startsWith("image/")
|
|
30118
|
-
)
|
|
30119
|
-
),
|
|
30120
|
-
tools: options.tools?.length ?? 0,
|
|
30121
|
-
temperature: options.temperature,
|
|
30122
|
-
topP: options.topP,
|
|
30123
|
-
topK: options.topK,
|
|
30124
|
-
maxOutputTokens: options.maxOutputTokens,
|
|
30125
|
-
stopSequences: options.stopSequences?.length,
|
|
30126
|
-
seed: options.seed,
|
|
30127
|
-
toolChoiceType: options.toolChoice?.type,
|
|
30128
|
-
responseFormatType: options.responseFormat?.type
|
|
30129
|
-
};
|
|
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);
|
|
30130
30032
|
}
|
|
30131
|
-
function
|
|
30132
|
-
|
|
30033
|
+
function getStatusCodeFromSAPError(code) {
|
|
30034
|
+
if (!code) return 500;
|
|
30035
|
+
if (code >= 100 && code < 600) {
|
|
30036
|
+
return code;
|
|
30037
|
+
}
|
|
30038
|
+
return 500;
|
|
30133
30039
|
}
|
|
30134
|
-
function
|
|
30135
|
-
if (
|
|
30040
|
+
function isOrchestrationErrorResponse(error) {
|
|
30041
|
+
if (error === null || typeof error !== "object" || !("error" in error)) {
|
|
30136
30042
|
return false;
|
|
30137
30043
|
}
|
|
30138
|
-
const
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
30142
|
-
|
|
30143
|
-
|
|
30144
|
-
|
|
30145
|
-
type: "object",
|
|
30146
|
-
properties: {},
|
|
30147
|
-
required: []
|
|
30148
|
-
};
|
|
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
|
+
);
|
|
30149
30051
|
}
|
|
30150
|
-
|
|
30151
|
-
const required = Array.isArray(schema.required) && schema.required.every((item) => typeof item === "string") ? schema.required : [];
|
|
30152
|
-
const additionalFields = Object.fromEntries(
|
|
30153
|
-
Object.entries(schema).filter(
|
|
30154
|
-
([key]) => key !== "type" && key !== "properties" && key !== "required"
|
|
30155
|
-
)
|
|
30156
|
-
);
|
|
30157
|
-
return {
|
|
30158
|
-
type: "object",
|
|
30159
|
-
properties,
|
|
30160
|
-
required,
|
|
30161
|
-
...additionalFields
|
|
30162
|
-
};
|
|
30052
|
+
return typeof innerError === "object" && innerError !== null && "message" in innerError && typeof innerError.message === "string";
|
|
30163
30053
|
}
|
|
30164
|
-
|
|
30165
|
-
|
|
30166
|
-
|
|
30167
|
-
|
|
30168
|
-
|
|
30169
|
-
|
|
30170
|
-
|
|
30171
|
-
|
|
30172
|
-
|
|
30173
|
-
|
|
30174
|
-
|
|
30175
|
-
|
|
30176
|
-
|
|
30177
|
-
|
|
30178
|
-
|
|
30179
|
-
|
|
30180
|
-
|
|
30181
|
-
|
|
30182
|
-
|
|
30183
|
-
|
|
30184
|
-
|
|
30185
|
-
|
|
30186
|
-
|
|
30187
|
-
* @param url - The URL to check
|
|
30188
|
-
* @returns True if the URL protocol is HTTPS or data (content-type rules are enforced via supportedUrls)
|
|
30189
|
-
*/
|
|
30190
|
-
supportsUrl(url) {
|
|
30191
|
-
return url.protocol === "https:" || url.protocol === "data:";
|
|
30192
|
-
}
|
|
30193
|
-
/**
|
|
30194
|
-
* Returns supported URL patterns for different content types.
|
|
30195
|
-
*
|
|
30196
|
-
* @returns Record of content types to regex patterns
|
|
30197
|
-
*/
|
|
30198
|
-
get supportedUrls() {
|
|
30199
|
-
return {
|
|
30200
|
-
"image/*": [/^https:\/\/.+$/i, /^data:image\/.*$/]
|
|
30201
|
-
};
|
|
30202
|
-
}
|
|
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 [];
|
|
30070
|
+
});
|
|
30071
|
+
if (entries.length === 0) return void 0;
|
|
30072
|
+
return Object.fromEntries(entries);
|
|
30073
|
+
}
|
|
30074
|
+
|
|
30075
|
+
// src/sap-ai-language-model.ts
|
|
30076
|
+
var StreamIdGenerator = class {
|
|
30203
30077
|
/**
|
|
30204
|
-
*
|
|
30078
|
+
* Generates a unique ID for a text block.
|
|
30205
30079
|
*
|
|
30206
|
-
* @returns
|
|
30080
|
+
* @returns RFC 4122-compliant UUID string
|
|
30207
30081
|
*/
|
|
30208
|
-
|
|
30209
|
-
return
|
|
30082
|
+
generateTextBlockId() {
|
|
30083
|
+
return crypto.randomUUID();
|
|
30210
30084
|
}
|
|
30085
|
+
};
|
|
30086
|
+
var SAPAILanguageModel = class {
|
|
30087
|
+
modelId;
|
|
30088
|
+
specificationVersion = "v3";
|
|
30211
30089
|
/**
|
|
30212
30090
|
* Model capabilities.
|
|
30213
30091
|
*
|
|
@@ -30216,203 +30094,91 @@ var SAPAIChatLanguageModel = class {
|
|
|
30216
30094
|
* SAP AI Core will fail the request at runtime.
|
|
30217
30095
|
*/
|
|
30218
30096
|
supportsImageUrls = true;
|
|
30219
|
-
/** Structured JSON outputs (json_schema response format). */
|
|
30220
|
-
supportsStructuredOutputs = true;
|
|
30221
|
-
/** Tool/function calling. */
|
|
30222
|
-
supportsToolCalls = true;
|
|
30223
|
-
/** Streaming responses. */
|
|
30224
|
-
supportsStreaming = true;
|
|
30225
30097
|
/** Multiple completions via the `n` parameter (provider-specific support). */
|
|
30226
30098
|
supportsMultipleCompletions = true;
|
|
30227
30099
|
/** Parallel tool calls. */
|
|
30228
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;
|
|
30229
30107
|
/**
|
|
30230
|
-
*
|
|
30108
|
+
* Generates text completion using SAP AI Core's Orchestration API.
|
|
30231
30109
|
*
|
|
30232
|
-
*
|
|
30233
|
-
*
|
|
30234
|
-
*
|
|
30110
|
+
* This method implements the `LanguageModelV3.doGenerate` interface,
|
|
30111
|
+
* providing synchronous (non-streaming) text generation with support for:
|
|
30112
|
+
* - Multi-turn conversations with system/user/assistant messages
|
|
30113
|
+
* - Tool calling (function calling) with structured outputs
|
|
30114
|
+
* - Multi-modal inputs (text + images)
|
|
30115
|
+
* - Data masking via SAP DPI
|
|
30116
|
+
* - Content filtering via Azure Content Safety or Llama Guard
|
|
30117
|
+
*
|
|
30118
|
+
* **Return Structure:**
|
|
30119
|
+
* - Finish reason: `{ unified: string, raw?: string }`
|
|
30120
|
+
* - Usage: Nested structure with token breakdown `{ inputTokens: { total, ... }, outputTokens: { total, ... } }`
|
|
30121
|
+
* - Warnings: Array of warnings with `type` and optional `feature` field
|
|
30122
|
+
*
|
|
30123
|
+
* @param options - Generation options including prompt, tools, temperature, etc.
|
|
30124
|
+
* @returns Promise resolving to generation result with content, usage, and metadata
|
|
30125
|
+
*
|
|
30126
|
+
* @throws {InvalidPromptError} If prompt format is invalid
|
|
30127
|
+
* @throws {InvalidArgumentError} If arguments are malformed
|
|
30128
|
+
* @throws {APICallError} If the SAP AI Core API call fails
|
|
30129
|
+
*
|
|
30130
|
+
* @example
|
|
30131
|
+
* ```typescript
|
|
30132
|
+
* const result = await model.doGenerate({
|
|
30133
|
+
* prompt: [
|
|
30134
|
+
* { role: 'user', content: [{ type: 'text', text: 'Hello!' }] }
|
|
30135
|
+
* ],
|
|
30136
|
+
* temperature: 0.7,
|
|
30137
|
+
* maxTokens: 100
|
|
30138
|
+
* });
|
|
30139
|
+
*
|
|
30140
|
+
* console.log(result.content); // Array of V3 content parts
|
|
30141
|
+
* console.log(result.finishReason.unified); // "stop", "length", "tool-calls", etc.
|
|
30142
|
+
* console.log(result.usage.inputTokens.total); // Total input tokens
|
|
30143
|
+
* ```
|
|
30144
|
+
*
|
|
30145
|
+
* @since 1.0.0
|
|
30146
|
+
* @since 4.0.0 Updated to LanguageModelV3 interface
|
|
30235
30147
|
*/
|
|
30236
|
-
|
|
30237
|
-
|
|
30238
|
-
|
|
30239
|
-
|
|
30240
|
-
|
|
30241
|
-
|
|
30242
|
-
|
|
30243
|
-
|
|
30244
|
-
|
|
30245
|
-
|
|
30246
|
-
|
|
30247
|
-
if (settingsTools && settingsTools.length > 0 && optionsTools && optionsTools.length > 0) {
|
|
30248
|
-
warnings.push({
|
|
30249
|
-
type: "other",
|
|
30250
|
-
message: "Both settings.tools and call options.tools were provided; preferring call options.tools."
|
|
30251
|
-
});
|
|
30252
|
-
}
|
|
30253
|
-
if (shouldUseSettingsTools) {
|
|
30254
|
-
tools = settingsTools;
|
|
30255
|
-
} else {
|
|
30256
|
-
const availableTools = shouldUseOptionsTools ? optionsTools : void 0;
|
|
30257
|
-
tools = availableTools?.map((tool) => {
|
|
30258
|
-
if (tool.type === "function") {
|
|
30259
|
-
const inputSchema = tool.inputSchema;
|
|
30260
|
-
const toolWithParams = tool;
|
|
30261
|
-
let parameters;
|
|
30262
|
-
if (toolWithParams.parameters && isZodSchema(toolWithParams.parameters)) {
|
|
30263
|
-
try {
|
|
30264
|
-
const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(
|
|
30265
|
-
toolWithParams.parameters,
|
|
30266
|
-
{
|
|
30267
|
-
$refStrategy: "none"
|
|
30268
|
-
}
|
|
30269
|
-
);
|
|
30270
|
-
const schemaRecord = jsonSchema;
|
|
30271
|
-
delete schemaRecord.$schema;
|
|
30272
|
-
parameters = buildSAPToolParameters(schemaRecord);
|
|
30273
|
-
} catch {
|
|
30274
|
-
warnings.push({
|
|
30275
|
-
type: "unsupported-tool",
|
|
30276
|
-
tool,
|
|
30277
|
-
details: "Failed to convert tool Zod schema to JSON Schema. Falling back to empty object schema."
|
|
30278
|
-
});
|
|
30279
|
-
parameters = buildSAPToolParameters({});
|
|
30280
|
-
}
|
|
30281
|
-
} else if (inputSchema && Object.keys(inputSchema).length > 0) {
|
|
30282
|
-
const hasProperties = inputSchema.properties && typeof inputSchema.properties === "object" && Object.keys(inputSchema.properties).length > 0;
|
|
30283
|
-
if (hasProperties) {
|
|
30284
|
-
parameters = buildSAPToolParameters(inputSchema);
|
|
30285
|
-
} else {
|
|
30286
|
-
parameters = buildSAPToolParameters({});
|
|
30287
|
-
}
|
|
30288
|
-
} else {
|
|
30289
|
-
parameters = buildSAPToolParameters({});
|
|
30290
|
-
}
|
|
30291
|
-
return {
|
|
30292
|
-
type: "function",
|
|
30293
|
-
function: {
|
|
30294
|
-
name: tool.name,
|
|
30295
|
-
description: tool.description,
|
|
30296
|
-
parameters
|
|
30297
|
-
}
|
|
30298
|
-
};
|
|
30299
|
-
} else {
|
|
30300
|
-
warnings.push({
|
|
30301
|
-
type: "unsupported-tool",
|
|
30302
|
-
tool
|
|
30303
|
-
});
|
|
30304
|
-
return null;
|
|
30305
|
-
}
|
|
30306
|
-
}).filter((t) => t !== null);
|
|
30307
|
-
}
|
|
30308
|
-
const supportsN = !this.modelId.startsWith("amazon--") && !this.modelId.startsWith("anthropic--");
|
|
30309
|
-
const modelParams = {};
|
|
30310
|
-
const maxTokens = options.maxOutputTokens ?? providerOptions.modelParams?.maxTokens ?? this.settings.modelParams?.maxTokens;
|
|
30311
|
-
if (maxTokens !== void 0) modelParams.max_tokens = maxTokens;
|
|
30312
|
-
const temperature = options.temperature ?? providerOptions.modelParams?.temperature ?? this.settings.modelParams?.temperature;
|
|
30313
|
-
if (temperature !== void 0) modelParams.temperature = temperature;
|
|
30314
|
-
const topP = options.topP ?? providerOptions.modelParams?.topP ?? this.settings.modelParams?.topP;
|
|
30315
|
-
if (topP !== void 0) modelParams.top_p = topP;
|
|
30316
|
-
if (options.topK !== void 0) modelParams.top_k = options.topK;
|
|
30317
|
-
const frequencyPenalty = options.frequencyPenalty ?? providerOptions.modelParams?.frequencyPenalty ?? this.settings.modelParams?.frequencyPenalty;
|
|
30318
|
-
if (frequencyPenalty !== void 0) {
|
|
30319
|
-
modelParams.frequency_penalty = frequencyPenalty;
|
|
30320
|
-
}
|
|
30321
|
-
const presencePenalty = options.presencePenalty ?? providerOptions.modelParams?.presencePenalty ?? this.settings.modelParams?.presencePenalty;
|
|
30322
|
-
if (presencePenalty !== void 0) {
|
|
30323
|
-
modelParams.presence_penalty = presencePenalty;
|
|
30324
|
-
}
|
|
30325
|
-
if (supportsN) {
|
|
30326
|
-
const nValue = providerOptions.modelParams?.n ?? this.settings.modelParams?.n;
|
|
30327
|
-
if (nValue !== void 0) {
|
|
30328
|
-
modelParams.n = nValue;
|
|
30329
|
-
}
|
|
30330
|
-
}
|
|
30331
|
-
const parallelToolCalls = providerOptions.modelParams?.parallel_tool_calls ?? this.settings.modelParams?.parallel_tool_calls;
|
|
30332
|
-
if (parallelToolCalls !== void 0) {
|
|
30333
|
-
modelParams.parallel_tool_calls = parallelToolCalls;
|
|
30334
|
-
}
|
|
30335
|
-
if (options.stopSequences && options.stopSequences.length > 0) {
|
|
30336
|
-
modelParams.stop = options.stopSequences;
|
|
30337
|
-
}
|
|
30338
|
-
if (options.seed !== void 0) {
|
|
30339
|
-
modelParams.seed = options.seed;
|
|
30340
|
-
}
|
|
30341
|
-
validateModelParameters(
|
|
30342
|
-
{
|
|
30343
|
-
temperature,
|
|
30344
|
-
topP,
|
|
30345
|
-
frequencyPenalty,
|
|
30346
|
-
presencePenalty,
|
|
30347
|
-
maxTokens,
|
|
30348
|
-
n: modelParams.n
|
|
30349
|
-
},
|
|
30350
|
-
warnings
|
|
30351
|
-
);
|
|
30352
|
-
if (options.toolChoice && options.toolChoice.type !== "auto") {
|
|
30353
|
-
warnings.push({
|
|
30354
|
-
type: "unsupported-setting",
|
|
30355
|
-
setting: "toolChoice",
|
|
30356
|
-
details: `SAP AI SDK does not support toolChoice '${options.toolChoice.type}'. Using default 'auto' behavior.`
|
|
30357
|
-
});
|
|
30358
|
-
}
|
|
30359
|
-
if (options.responseFormat?.type === "json") {
|
|
30360
|
-
warnings.push({
|
|
30361
|
-
type: "other",
|
|
30362
|
-
message: "responseFormat JSON mode is forwarded to the underlying model; support and schema adherence depend on the model/deployment."
|
|
30363
|
-
});
|
|
30364
|
-
}
|
|
30365
|
-
const responseFormat = options.responseFormat?.type === "json" ? options.responseFormat.schema ? {
|
|
30366
|
-
type: "json_schema",
|
|
30367
|
-
json_schema: {
|
|
30368
|
-
name: options.responseFormat.name ?? "response",
|
|
30369
|
-
description: options.responseFormat.description,
|
|
30370
|
-
schema: options.responseFormat.schema,
|
|
30371
|
-
strict: null
|
|
30372
|
-
}
|
|
30373
|
-
} : { type: "json_object" } : void 0;
|
|
30374
|
-
const orchestrationConfig = {
|
|
30375
|
-
promptTemplating: {
|
|
30376
|
-
model: {
|
|
30377
|
-
name: this.modelId,
|
|
30378
|
-
version: providerOptions.modelVersion ?? this.settings.modelVersion ?? "latest",
|
|
30379
|
-
params: modelParams
|
|
30380
|
-
},
|
|
30381
|
-
prompt: {
|
|
30382
|
-
template: [],
|
|
30383
|
-
tools: tools && tools.length > 0 ? tools : void 0,
|
|
30384
|
-
...responseFormat ? { response_format: responseFormat } : {}
|
|
30385
|
-
}
|
|
30386
|
-
},
|
|
30387
|
-
...(() => {
|
|
30388
|
-
const masking = providerOptions.masking ?? this.settings.masking;
|
|
30389
|
-
return masking && Object.keys(masking).length > 0 ? { masking } : {};
|
|
30390
|
-
})(),
|
|
30391
|
-
...(() => {
|
|
30392
|
-
const filtering = providerOptions.filtering ?? this.settings.filtering;
|
|
30393
|
-
return filtering && Object.keys(filtering).length > 0 ? { filtering } : {};
|
|
30394
|
-
})()
|
|
30148
|
+
get provider() {
|
|
30149
|
+
return this.config.provider;
|
|
30150
|
+
}
|
|
30151
|
+
/**
|
|
30152
|
+
* Returns supported URL patterns for different content types.
|
|
30153
|
+
*
|
|
30154
|
+
* @returns Record of content types to regex patterns
|
|
30155
|
+
*/
|
|
30156
|
+
get supportedUrls() {
|
|
30157
|
+
return {
|
|
30158
|
+
"image/*": [/^https:\/\/.+$/i, /^data:image\/.*$/]
|
|
30395
30159
|
};
|
|
30396
|
-
return { orchestrationConfig, messages, warnings };
|
|
30397
30160
|
}
|
|
30161
|
+
config;
|
|
30162
|
+
settings;
|
|
30398
30163
|
/**
|
|
30399
|
-
* Creates
|
|
30164
|
+
* Creates a new SAP AI Chat Language Model instance.
|
|
30400
30165
|
*
|
|
30401
|
-
* @param
|
|
30402
|
-
* @
|
|
30403
|
-
* @
|
|
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.
|
|
30404
30172
|
*/
|
|
30405
|
-
|
|
30406
|
-
|
|
30407
|
-
|
|
30408
|
-
|
|
30409
|
-
this.config.destination
|
|
30410
|
-
);
|
|
30173
|
+
constructor(modelId, settings, config) {
|
|
30174
|
+
this.settings = settings;
|
|
30175
|
+
this.config = config;
|
|
30176
|
+
this.modelId = modelId;
|
|
30411
30177
|
}
|
|
30412
30178
|
/**
|
|
30413
30179
|
* Generates a single completion (non-streaming).
|
|
30414
30180
|
*
|
|
30415
|
-
* This method implements the `
|
|
30181
|
+
* This method implements the `LanguageModelV3.doGenerate` interface,
|
|
30416
30182
|
* sending a request to SAP AI Core and returning the complete response.
|
|
30417
30183
|
*
|
|
30418
30184
|
* **Features:**
|
|
@@ -30420,6 +30186,13 @@ var SAPAIChatLanguageModel = class {
|
|
|
30420
30186
|
* - Multi-modal input (text + images)
|
|
30421
30187
|
* - Data masking (if configured)
|
|
30422
30188
|
* - Content filtering (if configured)
|
|
30189
|
+
* - Abort signal support (via Promise.race)
|
|
30190
|
+
*
|
|
30191
|
+
* **Note on Abort Signal:**
|
|
30192
|
+
* The abort signal implementation uses Promise.race to reject the promise when
|
|
30193
|
+
* the signal is aborted. However, this does not cancel the underlying HTTP request
|
|
30194
|
+
* to SAP AI Core - the request continues executing on the server. This is a
|
|
30195
|
+
* limitation of the SAP AI SDK's chatCompletion API.
|
|
30423
30196
|
*
|
|
30424
30197
|
* @param options - Generation options including prompt, tools, and settings
|
|
30425
30198
|
* @returns Promise resolving to the generation result with content, usage, and metadata
|
|
@@ -30438,7 +30211,7 @@ var SAPAIChatLanguageModel = class {
|
|
|
30438
30211
|
*/
|
|
30439
30212
|
async doGenerate(options) {
|
|
30440
30213
|
try {
|
|
30441
|
-
const {
|
|
30214
|
+
const { messages, orchestrationConfig, warnings } = this.buildOrchestrationConfig(options);
|
|
30442
30215
|
const client = this.createClient(orchestrationConfig);
|
|
30443
30216
|
const promptTemplating = orchestrationConfig.promptTemplating;
|
|
30444
30217
|
const requestBody = {
|
|
@@ -30455,6 +30228,14 @@ var SAPAIChatLanguageModel = class {
|
|
|
30455
30228
|
...(() => {
|
|
30456
30229
|
const filtering = orchestrationConfig.filtering;
|
|
30457
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 } : {};
|
|
30458
30239
|
})()
|
|
30459
30240
|
};
|
|
30460
30241
|
const response = await (async () => {
|
|
@@ -30492,7 +30273,7 @@ var SAPAIChatLanguageModel = class {
|
|
|
30492
30273
|
Object.entries(responseHeadersRaw).flatMap(([key, value]) => {
|
|
30493
30274
|
if (typeof value === "string") return [[key, value]];
|
|
30494
30275
|
if (Array.isArray(value)) {
|
|
30495
|
-
const strings = value.filter((item) => typeof item === "string").join("
|
|
30276
|
+
const strings = value.filter((item) => typeof item === "string").join("; ");
|
|
30496
30277
|
return strings.length > 0 ? [[key, strings]] : [];
|
|
30497
30278
|
}
|
|
30498
30279
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -30505,18 +30286,18 @@ var SAPAIChatLanguageModel = class {
|
|
|
30505
30286
|
const textContent = response.getContent();
|
|
30506
30287
|
if (textContent) {
|
|
30507
30288
|
content.push({
|
|
30508
|
-
|
|
30509
|
-
|
|
30289
|
+
text: textContent,
|
|
30290
|
+
type: "text"
|
|
30510
30291
|
});
|
|
30511
30292
|
}
|
|
30512
30293
|
const toolCalls = response.getToolCalls();
|
|
30513
30294
|
if (toolCalls) {
|
|
30514
30295
|
for (const toolCall of toolCalls) {
|
|
30515
30296
|
content.push({
|
|
30516
|
-
|
|
30297
|
+
input: toolCall.function.arguments,
|
|
30517
30298
|
toolCallId: toolCall.id,
|
|
30518
30299
|
toolName: toolCall.function.name,
|
|
30519
|
-
|
|
30300
|
+
type: "tool-call"
|
|
30520
30301
|
});
|
|
30521
30302
|
}
|
|
30522
30303
|
}
|
|
@@ -30525,18 +30306,13 @@ var SAPAIChatLanguageModel = class {
|
|
|
30525
30306
|
const finishReason = mapFinishReason(finishReasonRaw);
|
|
30526
30307
|
const rawResponseBody = {
|
|
30527
30308
|
content: textContent,
|
|
30528
|
-
|
|
30309
|
+
finishReason: finishReasonRaw,
|
|
30529
30310
|
tokenUsage,
|
|
30530
|
-
|
|
30311
|
+
toolCalls
|
|
30531
30312
|
};
|
|
30532
30313
|
return {
|
|
30533
30314
|
content,
|
|
30534
30315
|
finishReason,
|
|
30535
|
-
usage: {
|
|
30536
|
-
inputTokens: tokenUsage.prompt_tokens,
|
|
30537
|
-
outputTokens: tokenUsage.completion_tokens,
|
|
30538
|
-
totalTokens: tokenUsage.total_tokens
|
|
30539
|
-
},
|
|
30540
30316
|
providerMetadata: {
|
|
30541
30317
|
"sap-ai": {
|
|
30542
30318
|
finishReason: finishReasonRaw ?? "unknown",
|
|
@@ -30548,37 +30324,61 @@ var SAPAIChatLanguageModel = class {
|
|
|
30548
30324
|
body: requestBody
|
|
30549
30325
|
},
|
|
30550
30326
|
response: {
|
|
30551
|
-
|
|
30552
|
-
modelId: this.modelId,
|
|
30327
|
+
body: rawResponseBody,
|
|
30553
30328
|
headers: responseHeaders,
|
|
30554
|
-
|
|
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
|
+
}
|
|
30555
30344
|
},
|
|
30556
30345
|
warnings
|
|
30557
30346
|
};
|
|
30558
30347
|
} catch (error) {
|
|
30559
30348
|
throw convertToAISDKError(error, {
|
|
30560
30349
|
operation: "doGenerate",
|
|
30561
|
-
|
|
30562
|
-
|
|
30350
|
+
requestBody: createAISDKRequestBodySummary(options),
|
|
30351
|
+
url: "sap-ai:orchestration"
|
|
30563
30352
|
});
|
|
30564
30353
|
}
|
|
30565
30354
|
}
|
|
30566
30355
|
/**
|
|
30567
30356
|
* Generates a streaming completion.
|
|
30568
30357
|
*
|
|
30569
|
-
* This method implements the `
|
|
30358
|
+
* This method implements the `LanguageModelV3.doStream` interface,
|
|
30570
30359
|
* sending a streaming request to SAP AI Core and returning a stream of response parts.
|
|
30571
30360
|
*
|
|
30572
30361
|
* **Stream Events:**
|
|
30573
|
-
* - `stream-start` - Stream initialization
|
|
30362
|
+
* - `stream-start` - Stream initialization with warnings
|
|
30574
30363
|
* - `response-metadata` - Response metadata (model, timestamp)
|
|
30575
|
-
* - `text-start` - Text
|
|
30576
|
-
* - `text-delta` - Incremental text chunks
|
|
30577
|
-
* - `text-end` - Text
|
|
30578
|
-
* - `tool-
|
|
30364
|
+
* - `text-start` - Text block begins (with unique ID)
|
|
30365
|
+
* - `text-delta` - Incremental text chunks (with block ID)
|
|
30366
|
+
* - `text-end` - Text block completes (with accumulated text)
|
|
30367
|
+
* - `tool-input-start` - Tool input begins
|
|
30368
|
+
* - `tool-input-delta` - Tool input chunk
|
|
30369
|
+
* - `tool-input-end` - Tool input completes
|
|
30370
|
+
* - `tool-call` - Complete tool call
|
|
30579
30371
|
* - `finish` - Stream completes with usage and finish reason
|
|
30580
30372
|
* - `error` - Error occurred
|
|
30581
30373
|
*
|
|
30374
|
+
* **Stream Structure:**
|
|
30375
|
+
* - Text blocks have explicit lifecycle with unique IDs
|
|
30376
|
+
* - Finish reason format: `{ unified: string, raw?: string }`
|
|
30377
|
+
* - Usage format: `{ inputTokens: { total, ... }, outputTokens: { total, ... } }`
|
|
30378
|
+
* - Warnings only in `stream-start` event
|
|
30379
|
+
*
|
|
30380
|
+
* @see {@link https://sdk.vercel.ai/docs/ai-sdk-core/streaming Vercel AI SDK Streaming}
|
|
30381
|
+
*
|
|
30582
30382
|
* @param options - Streaming options including prompt, tools, and settings
|
|
30583
30383
|
* @returns Promise resolving to stream and request metadata
|
|
30584
30384
|
*
|
|
@@ -30594,12 +30394,17 @@ var SAPAIChatLanguageModel = class {
|
|
|
30594
30394
|
* if (part.type === 'text-delta') {
|
|
30595
30395
|
* process.stdout.write(part.delta);
|
|
30596
30396
|
* }
|
|
30397
|
+
* if (part.type === 'text-end') {
|
|
30398
|
+
* console.log('Block complete:', part.id, part.text);
|
|
30399
|
+
* }
|
|
30597
30400
|
* }
|
|
30598
30401
|
* ```
|
|
30402
|
+
*
|
|
30403
|
+
* @since 4.0.0
|
|
30599
30404
|
*/
|
|
30600
30405
|
async doStream(options) {
|
|
30601
30406
|
try {
|
|
30602
|
-
const {
|
|
30407
|
+
const { messages, orchestrationConfig, warnings } = this.buildOrchestrationConfig(options);
|
|
30603
30408
|
const client = this.createClient(orchestrationConfig);
|
|
30604
30409
|
const promptTemplating = orchestrationConfig.promptTemplating;
|
|
30605
30410
|
const requestBody = {
|
|
@@ -30623,15 +30428,28 @@ var SAPAIChatLanguageModel = class {
|
|
|
30623
30428
|
options.abortSignal,
|
|
30624
30429
|
{ promptTemplating: { include_usage: true } }
|
|
30625
30430
|
);
|
|
30431
|
+
const idGenerator = new StreamIdGenerator();
|
|
30432
|
+
let textBlockId = null;
|
|
30626
30433
|
const streamState = {
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
|
|
30630
|
-
|
|
30631
|
-
totalTokens: void 0
|
|
30434
|
+
activeText: false,
|
|
30435
|
+
finishReason: {
|
|
30436
|
+
raw: void 0,
|
|
30437
|
+
unified: "other"
|
|
30632
30438
|
},
|
|
30633
30439
|
isFirstChunk: true,
|
|
30634
|
-
|
|
30440
|
+
usage: {
|
|
30441
|
+
inputTokens: {
|
|
30442
|
+
cacheRead: void 0,
|
|
30443
|
+
cacheWrite: void 0,
|
|
30444
|
+
noCache: void 0,
|
|
30445
|
+
total: void 0
|
|
30446
|
+
},
|
|
30447
|
+
outputTokens: {
|
|
30448
|
+
reasoning: void 0,
|
|
30449
|
+
text: void 0,
|
|
30450
|
+
total: void 0
|
|
30451
|
+
}
|
|
30452
|
+
}
|
|
30635
30453
|
};
|
|
30636
30454
|
const toolCallsInProgress = /* @__PURE__ */ new Map();
|
|
30637
30455
|
const sdkStream = streamResponse.stream;
|
|
@@ -30639,6 +30457,11 @@ var SAPAIChatLanguageModel = class {
|
|
|
30639
30457
|
const warningsSnapshot = [...warnings];
|
|
30640
30458
|
const warningsOut = [...warningsSnapshot];
|
|
30641
30459
|
const transformedStream = new ReadableStream({
|
|
30460
|
+
cancel(reason) {
|
|
30461
|
+
if (reason) {
|
|
30462
|
+
console.debug("SAP AI stream cancelled:", reason);
|
|
30463
|
+
}
|
|
30464
|
+
},
|
|
30642
30465
|
async start(controller) {
|
|
30643
30466
|
controller.enqueue({
|
|
30644
30467
|
type: "stream-start",
|
|
@@ -30649,26 +30472,32 @@ var SAPAIChatLanguageModel = class {
|
|
|
30649
30472
|
if (streamState.isFirstChunk) {
|
|
30650
30473
|
streamState.isFirstChunk = false;
|
|
30651
30474
|
controller.enqueue({
|
|
30652
|
-
type: "response-metadata",
|
|
30653
30475
|
modelId,
|
|
30654
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
30476
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
30477
|
+
type: "response-metadata"
|
|
30655
30478
|
});
|
|
30656
30479
|
}
|
|
30657
30480
|
const deltaToolCalls = chunk.getDeltaToolCalls();
|
|
30658
30481
|
if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
|
|
30659
|
-
streamState.finishReason =
|
|
30482
|
+
streamState.finishReason = {
|
|
30483
|
+
raw: void 0,
|
|
30484
|
+
unified: "tool-calls"
|
|
30485
|
+
};
|
|
30660
30486
|
}
|
|
30661
30487
|
const deltaContent = chunk.getDeltaContent();
|
|
30662
|
-
if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason !== "tool-calls") {
|
|
30488
|
+
if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason.unified !== "tool-calls") {
|
|
30663
30489
|
if (!streamState.activeText) {
|
|
30664
|
-
|
|
30490
|
+
textBlockId = idGenerator.generateTextBlockId();
|
|
30491
|
+
controller.enqueue({ id: textBlockId, type: "text-start" });
|
|
30665
30492
|
streamState.activeText = true;
|
|
30666
30493
|
}
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
|
|
30671
|
-
|
|
30494
|
+
if (textBlockId) {
|
|
30495
|
+
controller.enqueue({
|
|
30496
|
+
delta: deltaContent,
|
|
30497
|
+
id: textBlockId,
|
|
30498
|
+
type: "text-delta"
|
|
30499
|
+
});
|
|
30500
|
+
}
|
|
30672
30501
|
}
|
|
30673
30502
|
if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
|
|
30674
30503
|
for (const toolCallChunk of deltaToolCalls) {
|
|
@@ -30678,11 +30507,11 @@ var SAPAIChatLanguageModel = class {
|
|
|
30678
30507
|
}
|
|
30679
30508
|
if (!toolCallsInProgress.has(index)) {
|
|
30680
30509
|
toolCallsInProgress.set(index, {
|
|
30681
|
-
id: toolCallChunk.id ?? `tool_${String(index)}`,
|
|
30682
|
-
toolName: toolCallChunk.function?.name,
|
|
30683
30510
|
arguments: "",
|
|
30511
|
+
didEmitCall: false,
|
|
30684
30512
|
didEmitInputStart: false,
|
|
30685
|
-
|
|
30513
|
+
id: toolCallChunk.id ?? `tool_${String(index)}`,
|
|
30514
|
+
toolName: toolCallChunk.function?.name
|
|
30686
30515
|
});
|
|
30687
30516
|
}
|
|
30688
30517
|
const tc = toolCallsInProgress.get(index);
|
|
@@ -30697,9 +30526,9 @@ var SAPAIChatLanguageModel = class {
|
|
|
30697
30526
|
if (!tc.didEmitInputStart && tc.toolName) {
|
|
30698
30527
|
tc.didEmitInputStart = true;
|
|
30699
30528
|
controller.enqueue({
|
|
30700
|
-
type: "tool-input-start",
|
|
30701
30529
|
id: tc.id,
|
|
30702
|
-
toolName: tc.toolName
|
|
30530
|
+
toolName: tc.toolName,
|
|
30531
|
+
type: "tool-input-start"
|
|
30703
30532
|
});
|
|
30704
30533
|
}
|
|
30705
30534
|
const argumentsDelta = toolCallChunk.function?.arguments;
|
|
@@ -30707,9 +30536,9 @@ var SAPAIChatLanguageModel = class {
|
|
|
30707
30536
|
tc.arguments += argumentsDelta;
|
|
30708
30537
|
if (tc.didEmitInputStart) {
|
|
30709
30538
|
controller.enqueue({
|
|
30710
|
-
|
|
30539
|
+
delta: argumentsDelta,
|
|
30711
30540
|
id: tc.id,
|
|
30712
|
-
|
|
30541
|
+
type: "tool-input-delta"
|
|
30713
30542
|
});
|
|
30714
30543
|
}
|
|
30715
30544
|
}
|
|
@@ -30718,7 +30547,7 @@ var SAPAIChatLanguageModel = class {
|
|
|
30718
30547
|
const chunkFinishReason = chunk.getFinishReason();
|
|
30719
30548
|
if (chunkFinishReason) {
|
|
30720
30549
|
streamState.finishReason = mapFinishReason(chunkFinishReason);
|
|
30721
|
-
if (streamState.finishReason === "tool-calls") {
|
|
30550
|
+
if (streamState.finishReason.unified === "tool-calls") {
|
|
30722
30551
|
const toolCalls2 = Array.from(toolCallsInProgress.values());
|
|
30723
30552
|
for (const tc of toolCalls2) {
|
|
30724
30553
|
if (tc.didEmitCall) {
|
|
@@ -30727,28 +30556,28 @@ var SAPAIChatLanguageModel = class {
|
|
|
30727
30556
|
if (!tc.didEmitInputStart) {
|
|
30728
30557
|
tc.didEmitInputStart = true;
|
|
30729
30558
|
controller.enqueue({
|
|
30730
|
-
type: "tool-input-start",
|
|
30731
30559
|
id: tc.id,
|
|
30732
|
-
toolName: tc.toolName ?? ""
|
|
30560
|
+
toolName: tc.toolName ?? "",
|
|
30561
|
+
type: "tool-input-start"
|
|
30733
30562
|
});
|
|
30734
30563
|
}
|
|
30735
30564
|
if (!tc.toolName) {
|
|
30736
30565
|
warningsOut.push({
|
|
30737
|
-
|
|
30738
|
-
|
|
30566
|
+
message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name.",
|
|
30567
|
+
type: "other"
|
|
30739
30568
|
});
|
|
30740
30569
|
}
|
|
30741
30570
|
tc.didEmitCall = true;
|
|
30742
|
-
controller.enqueue({ type: "tool-input-end"
|
|
30571
|
+
controller.enqueue({ id: tc.id, type: "tool-input-end" });
|
|
30743
30572
|
controller.enqueue({
|
|
30744
|
-
|
|
30573
|
+
input: tc.arguments,
|
|
30745
30574
|
toolCallId: tc.id,
|
|
30746
30575
|
toolName: tc.toolName ?? "",
|
|
30747
|
-
|
|
30576
|
+
type: "tool-call"
|
|
30748
30577
|
});
|
|
30749
30578
|
}
|
|
30750
|
-
if (streamState.activeText) {
|
|
30751
|
-
controller.enqueue({ type: "text-end"
|
|
30579
|
+
if (streamState.activeText && textBlockId) {
|
|
30580
|
+
controller.enqueue({ id: textBlockId, type: "text-end" });
|
|
30752
30581
|
streamState.activeText = false;
|
|
30753
30582
|
}
|
|
30754
30583
|
}
|
|
@@ -30763,105 +30592,399 @@ var SAPAIChatLanguageModel = class {
|
|
|
30763
30592
|
if (!tc.didEmitInputStart) {
|
|
30764
30593
|
tc.didEmitInputStart = true;
|
|
30765
30594
|
controller.enqueue({
|
|
30766
|
-
type: "tool-input-start",
|
|
30767
30595
|
id: tc.id,
|
|
30768
|
-
toolName: tc.toolName ?? ""
|
|
30596
|
+
toolName: tc.toolName ?? "",
|
|
30597
|
+
type: "tool-input-start"
|
|
30769
30598
|
});
|
|
30770
30599
|
}
|
|
30771
30600
|
if (!tc.toolName) {
|
|
30772
30601
|
warningsOut.push({
|
|
30773
|
-
|
|
30774
|
-
|
|
30602
|
+
message: "Received tool-call delta without a tool name. Emitting tool-call with an empty tool name.",
|
|
30603
|
+
type: "other"
|
|
30775
30604
|
});
|
|
30776
30605
|
}
|
|
30777
30606
|
didEmitAnyToolCalls = true;
|
|
30778
30607
|
tc.didEmitCall = true;
|
|
30779
|
-
controller.enqueue({ type: "tool-input-end"
|
|
30608
|
+
controller.enqueue({ id: tc.id, type: "tool-input-end" });
|
|
30780
30609
|
controller.enqueue({
|
|
30781
|
-
|
|
30610
|
+
input: tc.arguments,
|
|
30782
30611
|
toolCallId: tc.id,
|
|
30783
30612
|
toolName: tc.toolName ?? "",
|
|
30784
|
-
|
|
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"
|
|
30785
30731
|
});
|
|
30732
|
+
parameters = buildSAPToolParameters({});
|
|
30786
30733
|
}
|
|
30787
|
-
|
|
30788
|
-
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
|
|
30793
|
-
} else if (didEmitAnyToolCalls) {
|
|
30794
|
-
streamState.finishReason = "tool-calls";
|
|
30795
|
-
}
|
|
30796
|
-
const finalUsage = streamResponse.getTokenUsage();
|
|
30797
|
-
if (finalUsage) {
|
|
30798
|
-
streamState.usage.inputTokens = finalUsage.prompt_tokens;
|
|
30799
|
-
streamState.usage.outputTokens = finalUsage.completion_tokens;
|
|
30800
|
-
streamState.usage.totalTokens = finalUsage.total_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({});
|
|
30801
30740
|
}
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
finishReason: streamState.finishReason,
|
|
30805
|
-
usage: streamState.usage
|
|
30806
|
-
});
|
|
30807
|
-
controller.close();
|
|
30808
|
-
} catch (error) {
|
|
30809
|
-
const aiError = convertToAISDKError(error, {
|
|
30810
|
-
operation: "doStream",
|
|
30811
|
-
url: "sap-ai:orchestration",
|
|
30812
|
-
requestBody: createAISDKRequestBodySummary(options)
|
|
30813
|
-
});
|
|
30814
|
-
controller.enqueue({
|
|
30815
|
-
type: "error",
|
|
30816
|
-
error: aiError instanceof Error ? aiError : new Error(String(aiError))
|
|
30817
|
-
});
|
|
30818
|
-
controller.close();
|
|
30819
|
-
}
|
|
30820
|
-
},
|
|
30821
|
-
cancel(reason) {
|
|
30822
|
-
if (reason) {
|
|
30823
|
-
console.debug("SAP AI stream cancelled:", reason);
|
|
30741
|
+
} else {
|
|
30742
|
+
parameters = buildSAPToolParameters({});
|
|
30824
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;
|
|
30825
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"
|
|
30826
30811
|
});
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
|
|
30832
|
-
warnings: warningsOut
|
|
30833
|
-
};
|
|
30834
|
-
} catch (error) {
|
|
30835
|
-
throw convertToAISDKError(error, {
|
|
30836
|
-
operation: "doStream",
|
|
30837
|
-
url: "sap-ai:orchestration",
|
|
30838
|
-
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"
|
|
30839
30817
|
});
|
|
30840
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
|
+
);
|
|
30841
30873
|
}
|
|
30842
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
|
+
}
|
|
30843
30927
|
function mapFinishReason(reason) {
|
|
30844
|
-
|
|
30928
|
+
const raw = reason;
|
|
30929
|
+
if (!reason) return { raw, unified: "other" };
|
|
30845
30930
|
switch (reason.toLowerCase()) {
|
|
30846
|
-
case "
|
|
30931
|
+
case "content_filter":
|
|
30932
|
+
return { raw, unified: "content-filter" };
|
|
30847
30933
|
case "end_turn":
|
|
30848
|
-
case "stop_sequence":
|
|
30849
30934
|
case "eos":
|
|
30850
|
-
|
|
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" };
|
|
30851
30944
|
case "length":
|
|
30852
30945
|
case "max_tokens":
|
|
30853
30946
|
case "max_tokens_reached":
|
|
30854
|
-
return "length";
|
|
30855
|
-
case "tool_calls":
|
|
30856
|
-
case "tool_call":
|
|
30857
|
-
case "function_call":
|
|
30858
|
-
return "tool-calls";
|
|
30859
|
-
case "content_filter":
|
|
30860
|
-
return "content-filter";
|
|
30861
|
-
case "error":
|
|
30862
|
-
return "error";
|
|
30947
|
+
return { raw, unified: "length" };
|
|
30863
30948
|
default:
|
|
30864
|
-
return "other";
|
|
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
|
+
});
|
|
30865
30988
|
}
|
|
30866
30989
|
}
|
|
30867
30990
|
|
|
@@ -30879,19 +31002,19 @@ function createSAPAIProvider(options = {}) {
|
|
|
30879
31002
|
const mergedSettings = {
|
|
30880
31003
|
...options.defaultSettings,
|
|
30881
31004
|
...settings,
|
|
31005
|
+
filtering: settings.filtering ?? options.defaultSettings?.filtering,
|
|
31006
|
+
// Complex objects: override, do not merge
|
|
31007
|
+
masking: settings.masking ?? options.defaultSettings?.masking,
|
|
30882
31008
|
modelParams: {
|
|
30883
31009
|
...options.defaultSettings?.modelParams ?? {},
|
|
30884
31010
|
...settings.modelParams ?? {}
|
|
30885
31011
|
},
|
|
30886
|
-
// Complex objects: override, do not merge
|
|
30887
|
-
masking: settings.masking ?? options.defaultSettings?.masking,
|
|
30888
|
-
filtering: settings.filtering ?? options.defaultSettings?.filtering,
|
|
30889
31012
|
tools: settings.tools ?? options.defaultSettings?.tools
|
|
30890
31013
|
};
|
|
30891
|
-
return new
|
|
30892
|
-
provider: "sap-ai",
|
|
31014
|
+
return new SAPAILanguageModel(modelId, mergedSettings, {
|
|
30893
31015
|
deploymentConfig,
|
|
30894
|
-
destination: options.destination
|
|
31016
|
+
destination: options.destination,
|
|
31017
|
+
provider: "sap-ai"
|
|
30895
31018
|
});
|
|
30896
31019
|
};
|
|
30897
31020
|
const provider = function(modelId, settings) {
|
|
@@ -30907,17 +31030,12 @@ function createSAPAIProvider(options = {}) {
|
|
|
30907
31030
|
}
|
|
30908
31031
|
var sapai = createSAPAIProvider();
|
|
30909
31032
|
|
|
30910
|
-
// src/sap-ai-
|
|
31033
|
+
// src/sap-ai-settings.ts
|
|
30911
31034
|
var import_orchestration2 = require("@sap-ai-sdk/orchestration");
|
|
30912
|
-
|
|
30913
|
-
// src/types/completion-request.ts
|
|
30914
31035
|
var import_orchestration3 = require("@sap-ai-sdk/orchestration");
|
|
30915
31036
|
|
|
30916
|
-
// src/types/completion-response.ts
|
|
30917
|
-
var import_orchestration4 = require("@sap-ai-sdk/orchestration");
|
|
30918
|
-
|
|
30919
31037
|
// src/index.ts
|
|
30920
|
-
var
|
|
31038
|
+
var import_orchestration4 = require("@sap-ai-sdk/orchestration");
|
|
30921
31039
|
// Annotate the CommonJS export names for ESM import in node:
|
|
30922
31040
|
0 && (module.exports = {
|
|
30923
31041
|
OrchestrationClient,
|