@tacoreai/web-sdk 1.22.1-beta.2 → 1.24.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/database/core/apps/AppsClient.AI.js +102 -1
- package/database/core/apps/AppsClient.AppServer.js +36 -10
- package/database/core/apps/AppsClient.Data.js +18 -0
- package/database/core/apps/AppsClient.js +4 -0
- package/database/core/apps/AppsPublicClient.js +31 -1
- package/database/core/apps/BaseAppsClient.js +43 -2
- package/package.json +2 -2
|
@@ -1,6 +1,107 @@
|
|
|
1
1
|
export const appsClientAiMethods = {
|
|
2
2
|
// ==================== AI 能力 ====================
|
|
3
3
|
|
|
4
|
+
async streamRuntime(options = {}) {
|
|
5
|
+
try {
|
|
6
|
+
const messages = Array.isArray(options.messages) ? options.messages : [];
|
|
7
|
+
if (!messages.length && !options.systemPrompt) {
|
|
8
|
+
throw new Error("messages or systemPrompt is required for AI runtime.");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!options.onChunk || typeof options.onChunk !== "function") {
|
|
12
|
+
throw new Error("onChunk callback is required for streaming.");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const requestData = {
|
|
16
|
+
...options,
|
|
17
|
+
appId: this._getDataSourceAppId(),
|
|
18
|
+
projectId: options.projectId || this.config.projectId,
|
|
19
|
+
model: options.model || "agent:tacore-1.2",
|
|
20
|
+
messages,
|
|
21
|
+
stream: true,
|
|
22
|
+
};
|
|
23
|
+
delete requestData.onChunk;
|
|
24
|
+
delete requestData.onComplete;
|
|
25
|
+
delete requestData.onError;
|
|
26
|
+
delete requestData.signal;
|
|
27
|
+
|
|
28
|
+
const url = `${this.config.apiBaseUrl}/apps/ai/runtime/run`;
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: this._getRequestHeaders(),
|
|
32
|
+
body: JSON.stringify(requestData),
|
|
33
|
+
signal: options.signal,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errorText = await response.text().catch(() => "");
|
|
38
|
+
throw new Error(errorText || `HTTP ${response.status}: ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const reader = response.body?.getReader();
|
|
42
|
+
if (!reader) {
|
|
43
|
+
throw new Error("Response body is not readable");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const decoder = new TextDecoder();
|
|
47
|
+
let buffer = "";
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
while (true) {
|
|
51
|
+
const { done, value } = await reader.read();
|
|
52
|
+
if (done) break;
|
|
53
|
+
|
|
54
|
+
buffer += decoder.decode(value, { stream: true });
|
|
55
|
+
while (true) {
|
|
56
|
+
const lineEnd = buffer.indexOf("\n");
|
|
57
|
+
if (lineEnd === -1) break;
|
|
58
|
+
|
|
59
|
+
const line = buffer.slice(0, lineEnd).trim();
|
|
60
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
61
|
+
if (!line || line.startsWith(":")) continue;
|
|
62
|
+
|
|
63
|
+
if (line.startsWith("data: ")) {
|
|
64
|
+
const data = line.slice(6);
|
|
65
|
+
if (data === "[DONE]") {
|
|
66
|
+
options.onComplete?.();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(data);
|
|
72
|
+
const choice = parsed?.choices?.[0];
|
|
73
|
+
if (parsed?.error || choice?.error) {
|
|
74
|
+
const streamError = new Error(parsed?.error?.message || choice?.error?.message || parsed?.message || "AI runtime error");
|
|
75
|
+
options.onError?.(streamError);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (choice?.delta?.hasOwnProperty("content")) {
|
|
79
|
+
options.onChunk(choice.delta.content || "", parsed);
|
|
80
|
+
}
|
|
81
|
+
if (choice?.finish_reason === "stop") {
|
|
82
|
+
options.onComplete?.();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
} catch (parseError) {
|
|
86
|
+
console.warn("Failed to parse runtime SSE data:", parseError);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
options.onComplete?.();
|
|
93
|
+
} finally {
|
|
94
|
+
reader.cancel();
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error.name === "AbortError") {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
options.onError?.(error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
4
105
|
async streamCompletion(options = {}) {
|
|
5
106
|
try {
|
|
6
107
|
if (!options.messages || !Array.isArray(options.messages)) {
|
|
@@ -12,7 +113,7 @@ export const appsClientAiMethods = {
|
|
|
12
113
|
}
|
|
13
114
|
|
|
14
115
|
const requestData = {
|
|
15
|
-
appId: this.
|
|
116
|
+
appId: this._getDataSourceAppId(),
|
|
16
117
|
model: options.model || "agent:tacore-1.2",
|
|
17
118
|
messages: options.messages,
|
|
18
119
|
temperature: options.temperature,
|
|
@@ -21,6 +21,29 @@ const isBrowserPreviewRuntime = () => {
|
|
|
21
21
|
return pathname.startsWith("/app-run/");
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const normalizeAppId = value => String(value || "").trim();
|
|
25
|
+
|
|
26
|
+
const getAppServerOwnerAppId = function() {
|
|
27
|
+
const explicitOwnerAppId = normalizeAppId(this.config?.appServerOwnerAppId);
|
|
28
|
+
if (explicitOwnerAppId) {
|
|
29
|
+
return explicitOwnerAppId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const shellAppId = normalizeAppId(this.config?.appId);
|
|
33
|
+
if (shellAppId) {
|
|
34
|
+
return shellAppId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof this._getChannelAppId === "function") {
|
|
38
|
+
const channelAppId = normalizeAppId(this._getChannelAppId());
|
|
39
|
+
if (channelAppId) {
|
|
40
|
+
return channelAppId;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return normalizeAppId(this.appId);
|
|
45
|
+
};
|
|
46
|
+
|
|
24
47
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
25
48
|
|
|
26
49
|
const isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
|
|
@@ -100,22 +123,23 @@ const clearPreviewSessionToken = (appId) => {
|
|
|
100
123
|
|
|
101
124
|
const getPreviewSessionToken = async function(options = {}) {
|
|
102
125
|
const { forceRefresh = false } = options;
|
|
126
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
103
127
|
if (forceRefresh) {
|
|
104
|
-
clearPreviewSessionToken(
|
|
128
|
+
clearPreviewSessionToken(appServerOwnerAppId);
|
|
105
129
|
}
|
|
106
130
|
|
|
107
|
-
const cachedToken = previewSessionTokenCache.get(
|
|
131
|
+
const cachedToken = previewSessionTokenCache.get(appServerOwnerAppId);
|
|
108
132
|
if (cachedToken) {
|
|
109
133
|
return cachedToken;
|
|
110
134
|
}
|
|
111
135
|
|
|
112
|
-
const pendingRequest = previewSessionPromiseCache.get(
|
|
136
|
+
const pendingRequest = previewSessionPromiseCache.get(appServerOwnerAppId);
|
|
113
137
|
if (pendingRequest) {
|
|
114
138
|
return pendingRequest;
|
|
115
139
|
}
|
|
116
140
|
|
|
117
141
|
const requestPromise = (async () => {
|
|
118
|
-
const endpoint = `/appserver-preview/session?appId=${encodeURIComponent(
|
|
142
|
+
const endpoint = `/appserver-preview/session?appId=${encodeURIComponent(appServerOwnerAppId)}`;
|
|
119
143
|
const response = await fetch(endpoint, {
|
|
120
144
|
method: "GET",
|
|
121
145
|
headers: {
|
|
@@ -138,15 +162,15 @@ const getPreviewSessionToken = async function(options = {}) {
|
|
|
138
162
|
throw new Error("[AppServer-PreviewSession] Missing preview token in session response.");
|
|
139
163
|
}
|
|
140
164
|
|
|
141
|
-
previewSessionTokenCache.set(
|
|
165
|
+
previewSessionTokenCache.set(appServerOwnerAppId, previewToken);
|
|
142
166
|
return previewToken;
|
|
143
167
|
})();
|
|
144
168
|
|
|
145
|
-
previewSessionPromiseCache.set(
|
|
169
|
+
previewSessionPromiseCache.set(appServerOwnerAppId, requestPromise);
|
|
146
170
|
try {
|
|
147
171
|
return await requestPromise;
|
|
148
172
|
} finally {
|
|
149
|
-
previewSessionPromiseCache.delete(
|
|
173
|
+
previewSessionPromiseCache.delete(appServerOwnerAppId);
|
|
150
174
|
}
|
|
151
175
|
};
|
|
152
176
|
|
|
@@ -367,7 +391,8 @@ const buildRemoteRequestInit = function(
|
|
|
367
391
|
};
|
|
368
392
|
|
|
369
393
|
const invokeByPlatformProxy = async function(appServerAPIName, payload, options = {}) {
|
|
370
|
-
const
|
|
394
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
395
|
+
const endpoint = `/apps/invokeAppServerAPI?appId=${encodeURIComponent(appServerOwnerAppId)}&apiName=${encodeURIComponent(appServerAPIName)}`;
|
|
371
396
|
const response = await fetch(
|
|
372
397
|
`${this.config.apiBaseUrl}${endpoint}`,
|
|
373
398
|
buildRemoteRequestInit.call(this, payload, options, {}, {
|
|
@@ -378,7 +403,8 @@ const invokeByPlatformProxy = async function(appServerAPIName, payload, options
|
|
|
378
403
|
};
|
|
379
404
|
|
|
380
405
|
const invokeByPreviewProxyOnce = async function(appServerAPIName, payload, options = {}, previewToken) {
|
|
381
|
-
const
|
|
406
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
407
|
+
const endpoint = `/appserver-preview/invokeAppServerAPI?appId=${encodeURIComponent(appServerOwnerAppId)}&apiName=${encodeURIComponent(appServerAPIName)}`;
|
|
382
408
|
const response = await fetch(
|
|
383
409
|
endpoint,
|
|
384
410
|
buildRemoteRequestInit.call(this, payload, options, {
|
|
@@ -398,7 +424,7 @@ const invokeByPreviewProxy = async function(appServerAPIName, payload, options =
|
|
|
398
424
|
throw error;
|
|
399
425
|
}
|
|
400
426
|
|
|
401
|
-
clearPreviewSessionToken(this
|
|
427
|
+
clearPreviewSessionToken(getAppServerOwnerAppId.call(this));
|
|
402
428
|
previewToken = await getPreviewSessionToken.call(this, { forceRefresh: true });
|
|
403
429
|
return await invokeByPreviewProxyOnce.call(this, appServerAPIName, payload, options, previewToken);
|
|
404
430
|
}
|
|
@@ -136,6 +136,24 @@ export const appsClientDataMethods = {
|
|
|
136
136
|
}
|
|
137
137
|
},
|
|
138
138
|
|
|
139
|
+
async runTransactionAction(action = {}) {
|
|
140
|
+
try {
|
|
141
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) {
|
|
142
|
+
throw new Error("action must be a plain object");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const requestData = {
|
|
146
|
+
appId: this.appId,
|
|
147
|
+
...action,
|
|
148
|
+
};
|
|
149
|
+
const result = await this._post("/apps/actions/run", requestData);
|
|
150
|
+
return result.data || result;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`执行AI应用事务动作失败 (action: ${action?.name || action?.actionName || "unknown"}):`, error);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
139
157
|
// ==================== 便捷方法 ====================
|
|
140
158
|
|
|
141
159
|
async hasData(modelName) {
|
|
@@ -86,6 +86,9 @@ class AppsClient extends BaseAppsClient {
|
|
|
86
86
|
"deleteData": ({ modelName, wyID, options }) =>
|
|
87
87
|
this.deleteData(modelName, wyID, options),
|
|
88
88
|
|
|
89
|
+
"actions/run": params => this.runTransactionAction(params),
|
|
90
|
+
"runTransactionAction": params => this.runTransactionAction(params),
|
|
91
|
+
|
|
89
92
|
// === 电商模块 ===
|
|
90
93
|
"createProduct": params => this.createProduct(params),
|
|
91
94
|
"updateProduct": ({ productId, data }) => this.updateProduct(productId, data),
|
|
@@ -178,6 +181,7 @@ class AppsClient extends BaseAppsClient {
|
|
|
178
181
|
|
|
179
182
|
// === AI ===
|
|
180
183
|
"streamCompletion": options => this.streamCompletion(options),
|
|
184
|
+
"ai/runtime/run": options => this.streamRuntime(options),
|
|
181
185
|
|
|
182
186
|
// === Knowledge Base ===
|
|
183
187
|
"createKnowledgeBaseDataStore": params => this.createKnowledgeBaseDataStore(params),
|
|
@@ -22,6 +22,7 @@ export class AppsPublicClient extends BaseAppsClient {
|
|
|
22
22
|
get adapters() {
|
|
23
23
|
return {
|
|
24
24
|
'submitPublicData': ({ modelName, data }) => this.submitPublicData(modelName, data),
|
|
25
|
+
'createPublicData': ({ modelName, data }) => this.createPublicData(modelName, data),
|
|
25
26
|
'readConfiguredPublicData': (params = {}) => {
|
|
26
27
|
const { modelName, query, ...rest } = params;
|
|
27
28
|
|
|
@@ -56,7 +57,7 @@ export class AppsPublicClient extends BaseAppsClient {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const result = await this._post("/apps/public/data/submit", {
|
|
59
|
-
appId: this.
|
|
60
|
+
appId: this._getDataSourceAppId(),
|
|
60
61
|
modelName: modelName,
|
|
61
62
|
data: jsonData,
|
|
62
63
|
});
|
|
@@ -71,6 +72,35 @@ export class AppsPublicClient extends BaseAppsClient {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* 按模型策略公开写入数据。
|
|
77
|
+
* 后端只校验 `publicWrite` 策略和来源应用,不写入任何业务语义。
|
|
78
|
+
*/
|
|
79
|
+
async createPublicData(modelName, jsonData) {
|
|
80
|
+
try {
|
|
81
|
+
if (!modelName) {
|
|
82
|
+
throw new Error("modelName is required");
|
|
83
|
+
}
|
|
84
|
+
if (!jsonData || typeof jsonData !== "object") {
|
|
85
|
+
throw new Error("jsonData must be a valid object");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const result = await this._post("/apps/data/public/create", {
|
|
89
|
+
appId: this._getDataSourceAppId(),
|
|
90
|
+
modelName,
|
|
91
|
+
data: jsonData,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
console.log(
|
|
95
|
+
`✅ [AppsPublicClient] Public data created successfully for model: ${modelName} in app: ${this._getDataSourceAppId()}`
|
|
96
|
+
);
|
|
97
|
+
return result;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`公开写入数据失败 (model: ${modelName}):`, error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
74
104
|
/**
|
|
75
105
|
* 读取由 AppsClient.writeData 设为公共的数据 (通过 publicConfig)
|
|
76
106
|
* @param {string} modelName - 模型名称
|
|
@@ -5,6 +5,9 @@ const PREVIEW_APPSERVER_API_KEY_HEADER = "x-tacore-preview-app-server-api-key";
|
|
|
5
5
|
const FORWARDED_APP_SERVER_API_KEY_HEADER = "x-tacore-forwarded-app-server-api-key";
|
|
6
6
|
const APP_SERVER_SERVICE_TOKEN_HEADER = "x-tacore-app-server-service-token";
|
|
7
7
|
const APP_SERVER_OWNER_APP_ID_HEADER = "x-tacore-app-server-owner-app-id";
|
|
8
|
+
const CHANNEL_APP_ID_HEADER = "x-tacore-channel-app-id";
|
|
9
|
+
const INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR =
|
|
10
|
+
"tacoreServerInteropAppServerApiKey is required for backend environment. Provide it in config or via TACORE_SERVER_INTEROP_APP_SERVER_API_KEY env var.";
|
|
8
11
|
const APP_SERVER_SERVICE_CREDENTIAL_REQUIRED_ERROR =
|
|
9
12
|
"AppServer service credential is required for backend service auth. Provide TACORE_SERVER_INTEROP_APP_SERVER_API_KEY + TACORE_APP_SERVER_SERVICE_TOKEN, or forward a validated appServerApiKey.";
|
|
10
13
|
|
|
@@ -63,6 +66,8 @@ const hasAppServerServiceCredential = (config = {}) => {
|
|
|
63
66
|
const shouldRequireAppServerServiceCredential = (config = {}) =>
|
|
64
67
|
isBackend && !isSSR && config.enableAppServerServiceAuth === true;
|
|
65
68
|
|
|
69
|
+
const normalizeAppId = value => String(value || "").trim();
|
|
70
|
+
|
|
66
71
|
/**
|
|
67
72
|
* Apps SDK 基类
|
|
68
73
|
* 封装通用的配置管理、Header构建、HTTP请求逻辑
|
|
@@ -168,6 +173,37 @@ export class BaseAppsClient {
|
|
|
168
173
|
// 默认不进行额外的业务错误检查
|
|
169
174
|
}
|
|
170
175
|
|
|
176
|
+
/**
|
|
177
|
+
* 获取本次 Apps 请求的数据上下文应用。
|
|
178
|
+
* dataSourceId 是数据、认证、权限、计费等运行时上下文锚点;当前壳应用仅作为渠道来源。
|
|
179
|
+
*/
|
|
180
|
+
_getDataSourceAppId() {
|
|
181
|
+
return normalizeAppId(this.config.dataSourceId) || normalizeAppId(this.appId);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 获取渠道应用 ID。渠道身份只用于来源校验、归因和审计,不改变业务数据归属。
|
|
186
|
+
*/
|
|
187
|
+
_getChannelAppId() {
|
|
188
|
+
const dataSourceAppId = this._getDataSourceAppId();
|
|
189
|
+
const explicitChannelAppId = normalizeAppId(this.config.channelAppId);
|
|
190
|
+
if (explicitChannelAppId && explicitChannelAppId !== dataSourceAppId) {
|
|
191
|
+
return explicitChannelAppId;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const currentShellAppId = normalizeAppId(this.config.appId);
|
|
195
|
+
if (currentShellAppId && currentShellAppId !== dataSourceAppId) {
|
|
196
|
+
return currentShellAppId;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const constructorAppId = normalizeAppId(this.appId);
|
|
200
|
+
if (constructorAppId && constructorAppId !== dataSourceAppId) {
|
|
201
|
+
return constructorAppId;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
|
|
171
207
|
/**
|
|
172
208
|
* 构建请求头
|
|
173
209
|
* @param {Object} additionalHeaders - 额外的 Header
|
|
@@ -194,8 +230,13 @@ export class BaseAppsClient {
|
|
|
194
230
|
if (this.config.projectId) {
|
|
195
231
|
headers["x-project-id"] = this.config.projectId;
|
|
196
232
|
}
|
|
197
|
-
|
|
198
|
-
|
|
233
|
+
const dataSourceAppId = this._getDataSourceAppId();
|
|
234
|
+
const channelAppId = this._getChannelAppId();
|
|
235
|
+
if (dataSourceAppId) {
|
|
236
|
+
headers["x-app-id"] = dataSourceAppId;
|
|
237
|
+
}
|
|
238
|
+
if (channelAppId) {
|
|
239
|
+
headers[CHANNEL_APP_ID_HEADER] = channelAppId;
|
|
199
240
|
}
|
|
200
241
|
|
|
201
242
|
const token = this._getAccessToken();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacoreai/web-sdk",
|
|
3
3
|
"description": "This file is for app server package, not the real npm package",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.24.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"./app-server-runtime": "./database/core/apps/AppServerRuntime.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"release": "npm version minor && git commit -am \"web-sdk update version\" &&
|
|
16
|
+
"release": "npm version minor && git commit -am \"web-sdk update version\" && npm publish && git push",
|
|
17
17
|
"release:beta": "npm version prerelease --preid beta --no-git-tag-version && git commit -am \"web-sdk update beta version\" && npm publish --tag beta"
|
|
18
18
|
}
|
|
19
19
|
}
|