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