@tacoreai/web-sdk 1.21.0 → 1.22.1-beta.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
CHANGED
|
@@ -17,52 +17,63 @@ npm install @tacoreai/web-sdk
|
|
|
17
17
|
### AppsClient (Data & AI)
|
|
18
18
|
示例略(保持原有章节)
|
|
19
19
|
|
|
20
|
+
### AppsClient (Events)
|
|
21
|
+
|
|
22
|
+
应用自定义埋点应使用事件日志接口,不要把高频事件写入通用数据模型。
|
|
23
|
+
应用代码不要直接请求 `/plat` 平台接口,也不要读取平台登录 token;写入和读取都应通过 `AppsClient.invoke(...)` 走 `/apps/events/*`。事件日志不新增 WebSDK 专用方法,apiName 直接和后端 path 对齐。
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
const tracked = await client.invoke('events/track', {
|
|
27
|
+
eventName: 'signup_click',
|
|
28
|
+
properties: { plan: 'pro' },
|
|
29
|
+
visitorId: localStorage.getItem('visitor_id'),
|
|
30
|
+
sessionId: sessionStorage.getItem('session_id'),
|
|
31
|
+
path: window.location.pathname,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 以下读取接口仅适用于已登录且具备 app owner/admin 角色的应用管理界面。
|
|
35
|
+
const event = await client.invoke('events/read', {
|
|
36
|
+
eventId: tracked?.data?.eventId,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const page = await client.invoke('events/query', {
|
|
40
|
+
day: '2026-05-14',
|
|
41
|
+
eventName: 'signup_click',
|
|
42
|
+
limit: 50,
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
可用接口包括 `events/track`、`events/batch-track`、`events/read`、`events/batch-read`、`events/query`。写入接口由后端校验应用部署域名或自定义域名来源;所有读取接口都要求 Apps 登录态且当前用户为应用 `owner/admin`。平台通用查看/搜索由平台工作视图承接。
|
|
47
|
+
|
|
20
48
|
---
|
|
21
49
|
|
|
22
|
-
## 文件存储 API(
|
|
50
|
+
## 文件存储 API(R2 / GCS)
|
|
23
51
|
|
|
24
52
|
先确保已通过 AppsAuthManager 登录,并使用 AppsClient 初始化时提供 appId。以下接口均作用于 /apps 路由,需携带 Authorization。
|
|
25
53
|
|
|
26
|
-
1)
|
|
54
|
+
1) 上传文件到 R2(公开访问 / 前端展示)
|
|
27
55
|
|
|
28
56
|
import { AppsClient } from '@tacoreai/web-sdk'
|
|
29
57
|
const client = new AppsClient('YOUR_APP_ID')
|
|
30
58
|
|
|
31
59
|
const input = document.querySelector('#fileInput')
|
|
32
60
|
const file = input.files[0]
|
|
33
|
-
const result = await client.uploadFile(file
|
|
34
|
-
// result
|
|
35
|
-
|
|
36
|
-
2) 获取临时下载 URL
|
|
61
|
+
const result = await client.uploadFile(file)
|
|
62
|
+
// result => { success: true, url, key }
|
|
37
63
|
|
|
38
|
-
|
|
39
|
-
// data.url 为可直接访问的签名地址(有效期30分钟)
|
|
64
|
+
2) 下载 R2 文件
|
|
40
65
|
|
|
41
|
-
|
|
66
|
+
const response = await client.downloadFromR2('<key>')
|
|
67
|
+
const blob = await response.blob()
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
3) 删除 R2 文件
|
|
44
70
|
|
|
45
|
-
|
|
71
|
+
await client.deleteFromR2('<key>')
|
|
46
72
|
|
|
47
|
-
|
|
48
|
-
// 例如:保存为本地文件
|
|
49
|
-
const a = document.createElement('a')
|
|
50
|
-
a.href = URL.createObjectURL(blob)
|
|
51
|
-
a.download = 'download.bin'
|
|
52
|
-
a.click()
|
|
53
|
-
URL.revokeObjectURL(a.href)
|
|
73
|
+
4) 上传文件到 GCS(Gemini / 多模态分析输入)
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
## [新增] 文件上传与分析
|
|
58
|
-
|
|
59
|
-
此接口用于上传文件(如 PDF、图片)以供 AI 进行深度分析或作为图片编辑的输入。
|
|
60
|
-
|
|
61
|
-
### `uploadFileForAnalysis(file)`
|
|
62
|
-
- **功能**: 将文件上传至 Google 的专用存储,并返回一个可供 AI 模型引用的 `fileUri`。
|
|
63
|
-
- **参数**:
|
|
64
|
-
- `file` (File): 从文件输入框获取的 File 对象。
|
|
65
|
-
- **返回值**: `Promise<{fileUri: string, mimeType: string, ...}>`
|
|
75
|
+
const gcsResult = await client.uploadToGCS(file)
|
|
76
|
+
// gcsResult.fileUri => gs://... ,供 Gemini 等模型使用
|
|
66
77
|
|
|
67
78
|
---
|
|
68
79
|
|
|
@@ -111,12 +122,12 @@ const imageUri = result.data.uri;
|
|
|
111
122
|
- **返回**: `Promise<Object>` (后端响应对象,包含 `data.uri` 或 `data.images`)
|
|
112
123
|
|
|
113
124
|
### 核心流程
|
|
114
|
-
1. **上传图片**:
|
|
125
|
+
1. **上传图片**: Google / Gemini 链路下,所有源图片(包括底图、叠加图、掩膜图)都应先通过 `uploadToGCS(file)` 获取 `gs://` 形式的 `fileUri`。
|
|
115
126
|
2. **调用编辑**: 使用获取到的 `fileUri` 和 `mimeType`,通过 `editImage` 方法并采用**简化的 `prompt + inputs` 模式**进行调用。
|
|
116
127
|
|
|
117
128
|
### 前置要求
|
|
118
129
|
- **必须先登录** (AppsAuthManager)
|
|
119
|
-
- `
|
|
130
|
+
- `uploadToGCS`
|
|
120
131
|
|
|
121
132
|
### 1. 简化调用 (prompt + inputs 模式,推荐)
|
|
122
133
|
SDK 会自动将 `prompt` 和 `inputs` 数组组合成标准的多模态 `messages`。
|
|
@@ -125,13 +136,13 @@ import { AppsClient } from '@tacoreai/web-sdk'
|
|
|
125
136
|
const client = new AppsClient('YOUR_APP_ID')
|
|
126
137
|
|
|
127
138
|
// 1. 上传源文件
|
|
128
|
-
const baseFile = await client.
|
|
139
|
+
const baseFile = await client.uploadToGCS(file);
|
|
129
140
|
|
|
130
141
|
// 2. 调用编辑接口
|
|
131
142
|
const result = await client.editImage({
|
|
132
143
|
prompt: '仅移除图中叠加的文字,保持其他区域不变,输出高清。',
|
|
133
144
|
inputs: [
|
|
134
|
-
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType:
|
|
145
|
+
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType: file.type }
|
|
135
146
|
]
|
|
136
147
|
});
|
|
137
148
|
// 获取图片 URI (兼容不同后端返回结构)
|
|
@@ -142,41 +153,41 @@ const editedBase64 = result.data.uri || (result.data.images && result.data.image
|
|
|
142
153
|
|
|
143
154
|
- **场景 A:单图编辑(去字/去水印/局部替换)**
|
|
144
155
|
|
|
145
|
-
const base = await client.
|
|
156
|
+
const base = await client.uploadToGCS(file);
|
|
146
157
|
const result = await client.editImage({
|
|
147
158
|
prompt: '移除叠加文字,其他区域完全保持不变。',
|
|
148
159
|
inputs: [
|
|
149
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
160
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: file.type }
|
|
150
161
|
]
|
|
151
162
|
});
|
|
152
163
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
153
164
|
|
|
154
165
|
- **场景 B:双图融合(将服装穿到模特身上)**
|
|
155
166
|
|
|
156
|
-
const garment = await client.
|
|
157
|
-
const modelPhoto = await client.
|
|
167
|
+
const garment = await client.uploadToGCS(garmentFile);
|
|
168
|
+
const modelPhoto = await client.uploadToGCS(modelFile);
|
|
158
169
|
|
|
159
170
|
const result = await client.editImage({
|
|
160
171
|
prompt: '将服装自然替换到模特身上,保持姿态与光照;去除服装图背景;保持背景不变;不改变面部和发型。',
|
|
161
172
|
inputs: [
|
|
162
173
|
// 建议顺序:底图(模特)放前,叠加/融合图(服装)放后
|
|
163
|
-
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType:
|
|
164
|
-
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType:
|
|
174
|
+
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType: modelFile.type },
|
|
175
|
+
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType: garmentFile.type }
|
|
165
176
|
]
|
|
166
177
|
});
|
|
167
178
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
168
179
|
|
|
169
180
|
- **场景 C:掩膜修复/扩展(Inpainting / Outpainting)**
|
|
170
181
|
|
|
171
|
-
const base = await client.
|
|
172
|
-
const mask = await client.
|
|
182
|
+
const base = await client.uploadToGCS(baseFile);
|
|
183
|
+
const mask = await client.uploadToGCS(maskFile); // 掩膜语义按后端约定
|
|
173
184
|
|
|
174
185
|
const result = await client.editImage({
|
|
175
186
|
prompt: '在掩膜区域进行修复,移除水印并保持纹理一致;不要改变未掩膜区域。',
|
|
176
187
|
inputs: [
|
|
177
188
|
// 建议顺序:底图 -> 掩膜
|
|
178
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
179
|
-
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType:
|
|
189
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: baseFile.type },
|
|
190
|
+
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType: maskFile.type }
|
|
180
191
|
]
|
|
181
192
|
});
|
|
182
193
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
@@ -184,7 +195,7 @@ const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
|
184
195
|
### 3. 高级模式 (messages 透传)
|
|
185
196
|
用于完全控制请求结构,不推荐常规使用。
|
|
186
197
|
|
|
187
|
-
const base = await client.
|
|
198
|
+
const base = await client.uploadToGCS(file)
|
|
188
199
|
|
|
189
200
|
const result = await client.editImage({
|
|
190
201
|
model: 'gemini-2.5-flash-image-preview',
|
|
@@ -193,7 +204,7 @@ const result = await client.editImage({
|
|
|
193
204
|
role: 'user',
|
|
194
205
|
content: [
|
|
195
206
|
{ type: 'text', text: '移除图中叠加的文字...' },
|
|
196
|
-
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType:
|
|
207
|
+
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType: file.type } }
|
|
197
208
|
]
|
|
198
209
|
}
|
|
199
210
|
]
|
|
@@ -202,30 +213,46 @@ const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
|
202
213
|
|
|
203
214
|
---
|
|
204
215
|
|
|
205
|
-
## [新增]
|
|
216
|
+
## [新增] 视频生成(Seedance 2.0)
|
|
206
217
|
|
|
207
|
-
通过 AppsClient
|
|
218
|
+
通过 AppsClient 调用统一视频任务接口。当前云模式默认视频模型为 `agent:tacore-3.3`,后端会映射到火山引擎 `doubao-seedance-2-0-260128`。计费将自动关联到应用所属的组织。
|
|
208
219
|
|
|
209
220
|
### 定价
|
|
210
|
-
-
|
|
211
|
-
-
|
|
221
|
+
- `agent:tacore-3.3`
|
|
222
|
+
- 输入价格:4
|
|
223
|
+
- 输出价格:10
|
|
212
224
|
|
|
213
225
|
### `createVideoJob(options)`
|
|
214
226
|
- **功能**: 创建一个异步的视频生成任务。
|
|
215
227
|
- **参数**:
|
|
216
228
|
- `options` (object):
|
|
217
|
-
- `prompt` (string,
|
|
218
|
-
- `
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
- `prompt` (string, 推荐): 视频描述提示词。
|
|
230
|
+
- `content` (array, 推荐): 多模态输入数组,支持:
|
|
231
|
+
- `{ type: 'text', text: '...' }`
|
|
232
|
+
- `{ type: 'image_url', image_url: { url: 'https://...' }, role: 'reference_image' }`
|
|
233
|
+
- `{ type: 'video_url', video_url: { url: 'https://...' }, role: 'reference_video' }`
|
|
234
|
+
- `{ type: 'audio_url', audio_url: { url: 'https://...' }, role: 'reference_audio' }`
|
|
235
|
+
- `model` (string, 可选): 默认 `agent:tacore-3.3`。
|
|
236
|
+
- `generate_audio` (boolean, 可选): 是否生成音频。
|
|
237
|
+
- `ratio` (string, 可选): 画幅比例,例如 `16:9`。
|
|
238
|
+
- `duration` (number, 可选): 视频时长(秒)。
|
|
239
|
+
- `watermark` (boolean, 可选): 是否保留 provider 水印。
|
|
240
|
+
- `resolution` (string, 可选): 分辨率。
|
|
241
|
+
- `seed` (number, 可选): 随机种子。
|
|
242
|
+
- `return_last_frame` (boolean, 可选): 是否返回尾帧。
|
|
243
|
+
- `providerOptions` (object, 可选): 透传给 provider 的扩展字段。
|
|
221
244
|
- **返回值**: `Promise<object>` - 包含任务初始状态的文档。
|
|
222
245
|
|
|
246
|
+
### 参考素材上传
|
|
247
|
+
- 图片、视频、音频参考素材必须先调用 `uploadToR2`,然后把返回的公网 `url` 填到 `content` 中。
|
|
248
|
+
- 不要直接把本地 `File`/`Blob`/base64 传给 `createVideoJob`。
|
|
249
|
+
|
|
223
250
|
### `remixVideoJob(jobId, options)`
|
|
224
|
-
- **功能**: 基于一个已完成的视频,创建一个新的 Remix
|
|
251
|
+
- **功能**: 基于一个已完成的视频,创建一个新的 Remix 任务。后端会自动把原视频作为 `reference_video` 注入。
|
|
225
252
|
- **参数**:
|
|
226
253
|
- `jobId` (string, 必填): 原始视频任务的 `wyID`。
|
|
227
254
|
- `options` (object):
|
|
228
|
-
- `prompt`
|
|
255
|
+
- 兼容 `createVideoJob(options)` 的全部字段,至少需要 `prompt` 或 `content`。
|
|
229
256
|
- **返回值**: `Promise<object>` - 包含新的 Remix 任务初始状态的文档。
|
|
230
257
|
|
|
231
258
|
### `getVideoJob(jobId)`
|
|
@@ -248,10 +275,29 @@ const client = new AppsClient('YOUR_APP_ID');
|
|
|
248
275
|
|
|
249
276
|
async function generateAndPollVideo() {
|
|
250
277
|
try {
|
|
278
|
+
const image = await client.uploadToR2(fileInput.files[0]);
|
|
279
|
+
const audio = await client.uploadToR2(audioInput.files[0]);
|
|
280
|
+
|
|
251
281
|
// 1. 提交任务
|
|
252
282
|
const jobResult = await client.createVideoJob({
|
|
253
|
-
|
|
254
|
-
|
|
283
|
+
model: 'agent:tacore-3.3',
|
|
284
|
+
content: [
|
|
285
|
+
{ type: 'text', text: '第一视角果茶广告短片' },
|
|
286
|
+
{
|
|
287
|
+
type: 'image_url',
|
|
288
|
+
image_url: { url: image.url },
|
|
289
|
+
role: 'reference_image',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: 'audio_url',
|
|
293
|
+
audio_url: { url: audio.url },
|
|
294
|
+
role: 'reference_audio',
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
generate_audio: true,
|
|
298
|
+
ratio: '16:9',
|
|
299
|
+
duration: 11,
|
|
300
|
+
watermark: false,
|
|
255
301
|
});
|
|
256
302
|
const jobId = jobResult.data.wyID;
|
|
257
303
|
console.log('视频任务已创建, ID:', jobId);
|
|
@@ -401,4 +447,4 @@ Body:
|
|
|
401
447
|
• 图像编辑:editImage({ messages }) 或 editImage({ prompt, inputs, ... })
|
|
402
448
|
• 单个爬虫:scrapeUrl({ url, projectId? })
|
|
403
449
|
• 批量爬虫:scrapeMultipleUrls({ urls, projectId?, maxConcurrent? })
|
|
404
|
-
- 错误处理:注意处理 401 未登录、402 余额不足、404 组织不存在、500/503 外部服务异常等。
|
|
450
|
+
- 错误处理:注意处理 401 未登录、402 余额不足、404 组织不存在、500/503 外部服务异常等。
|
|
@@ -31,12 +31,25 @@ const getRequestSource = (isPlatformProxyRequest, isSchedulerRequest) => {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const createRequestScopedAppsClient = (env, options = {}) => {
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
accessToken = null,
|
|
36
|
+
previewAppServerApiKey = null,
|
|
37
|
+
enableAppServerServiceAuth = false,
|
|
38
|
+
} = options;
|
|
35
39
|
const config = {
|
|
36
40
|
...env,
|
|
37
41
|
accessToken: accessToken || null,
|
|
42
|
+
enableAppServerServiceAuth,
|
|
38
43
|
};
|
|
39
44
|
|
|
45
|
+
if (enableAppServerServiceAuth) {
|
|
46
|
+
config.appServerOwnerAppId = env.appId;
|
|
47
|
+
config.tacoreAppServerServiceToken =
|
|
48
|
+
env.tacoreAppServerServiceToken ||
|
|
49
|
+
process.env.TACORE_APP_SERVER_SERVICE_TOKEN ||
|
|
50
|
+
null;
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
if (previewAppServerApiKey) {
|
|
41
54
|
config.previewAppServerApiKey = previewAppServerApiKey;
|
|
42
55
|
}
|
|
@@ -274,6 +287,7 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
274
287
|
);
|
|
275
288
|
const requestSource = getRequestSource(isPlatformProxyRequest, isSchedulerRequest);
|
|
276
289
|
let previewAppServerApiKey = null;
|
|
290
|
+
let isExternalApiKeyRequest = false;
|
|
277
291
|
|
|
278
292
|
console.log(`[AppServer] invoke api="${apiName}" source="${requestSource}"`);
|
|
279
293
|
|
|
@@ -318,6 +332,8 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
318
332
|
});
|
|
319
333
|
}
|
|
320
334
|
|
|
335
|
+
isExternalApiKeyRequest = true;
|
|
336
|
+
|
|
321
337
|
if (isPreviewMode() && !requestAccessToken) {
|
|
322
338
|
previewAppServerApiKey = requestApiKey;
|
|
323
339
|
}
|
|
@@ -330,17 +346,21 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
330
346
|
hasRequestApiKey: Boolean(requestApiKey),
|
|
331
347
|
hasPlatformInteropHeader: Boolean(platformInteropHeader),
|
|
332
348
|
hasSchedulerInteropHeader: Boolean(schedulerInteropHeader),
|
|
349
|
+
isExternalApiKeyRequest,
|
|
333
350
|
willForwardPreviewAppServerApiKey: Boolean(previewAppServerApiKey),
|
|
334
351
|
});
|
|
335
352
|
|
|
336
353
|
const appsClient = createRequestScopedAppsClient(env, {
|
|
337
354
|
accessToken: requestAccessToken || null,
|
|
338
355
|
previewAppServerApiKey,
|
|
356
|
+
enableAppServerServiceAuth: Boolean((isSchedulerRequest || isExternalApiKeyRequest) && !requestAccessToken),
|
|
339
357
|
});
|
|
340
358
|
console.log(`[AppServer Debug] scoped AppsClient api="${apiName}"`, {
|
|
341
359
|
hasAccessToken: Boolean(appsClient.getAccessToken()),
|
|
342
360
|
hasPreviewAppServerApiKey: Boolean(appsClient.config.previewAppServerApiKey),
|
|
343
361
|
hasInteropKey: Boolean(appsClient.config.tacoreServerInteropAppServerApiKey),
|
|
362
|
+
serviceAuthEnabled: appsClient.config.enableAppServerServiceAuth === true,
|
|
363
|
+
hasServiceToken: Boolean(appsClient.config.tacoreAppServerServiceToken),
|
|
344
364
|
});
|
|
345
365
|
const data = await appsClient.invokeAppServerAPI(apiName, payload, {
|
|
346
366
|
appsClient,
|
|
@@ -33,4 +33,23 @@ export const appsClientToolsMethods = {
|
|
|
33
33
|
const result = await this._post("/apps/sensitive/detect", { content });
|
|
34
34
|
return result.data;
|
|
35
35
|
},
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// ==================== 外部工具 ====================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 执行 Google 搜索
|
|
41
|
+
* @param {object} options
|
|
42
|
+
* @param {string} options.query - 搜索关键词
|
|
43
|
+
* @param {number} [options.num=10] - 返回结果数量 (1-10)
|
|
44
|
+
* @returns {Promise<{success: boolean, data: Array<{title: string, link: string, snippet: string}>}>}
|
|
45
|
+
*/
|
|
46
|
+
async googleSearch(options = {}) {
|
|
47
|
+
if (!options.query) {
|
|
48
|
+
throw new Error("query is required for googleSearch.");
|
|
49
|
+
}
|
|
50
|
+
return this._post("/apps/tools/google-search", {
|
|
51
|
+
appId: this.appId,
|
|
52
|
+
...options,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -46,19 +46,21 @@ export const appsClientVoiceVideoMethods = {
|
|
|
46
46
|
return response; // 直接返回 Response 对象
|
|
47
47
|
},
|
|
48
48
|
|
|
49
|
-
// ====================
|
|
49
|
+
// ==================== 新增:视频生成 ====================
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
* @param {object} options - { prompt
|
|
52
|
+
* 创建一个视频生成任务(默认走豆包 Seedance 2.0)
|
|
53
|
+
* @param {object} options - { prompt?, content?, model?, generate_audio?, ratio?, duration?, watermark?, resolution?, seed?, return_last_frame?, providerOptions? }
|
|
54
54
|
* @returns {Promise<object>} 视频任务文档
|
|
55
55
|
*/
|
|
56
56
|
async createVideoJob(options = {}) {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const hasPrompt = typeof options.prompt === "string" && options.prompt.trim();
|
|
58
|
+
const hasContent = Array.isArray(options.content) && options.content.length > 0;
|
|
59
|
+
if (!hasPrompt && !hasContent) {
|
|
60
|
+
throw new Error("prompt or content is required for video generation.");
|
|
59
61
|
}
|
|
60
|
-
|
|
61
|
-
return this._post("/apps/
|
|
62
|
+
|
|
63
|
+
return this._post("/apps/videos/tasks", {
|
|
62
64
|
appId: this.appId,
|
|
63
65
|
...options,
|
|
64
66
|
});
|
|
@@ -67,19 +69,23 @@ export const appsClientVoiceVideoMethods = {
|
|
|
67
69
|
/**
|
|
68
70
|
* 基于现有视频创建一个 Remix 任务
|
|
69
71
|
* @param {string} jobId - 原始视频任务的 wyID
|
|
70
|
-
* @param {object} options -
|
|
72
|
+
* @param {object} options - 兼容 createVideoJob 的请求体,后端会自动注入 reference_video
|
|
71
73
|
* @returns {Promise<object>} 新的 Remix 任务初始状态的文档
|
|
72
74
|
*/
|
|
73
75
|
async remixVideoJob(jobId, options = {}) {
|
|
74
76
|
if (!jobId) {
|
|
75
77
|
throw new Error("jobId of the original video is required for remixing.");
|
|
76
78
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
|
|
80
|
+
const hasPrompt = typeof options.prompt === "string" && options.prompt.trim();
|
|
81
|
+
const hasContent = Array.isArray(options.content) && options.content.length > 0;
|
|
82
|
+
if (!hasPrompt && !hasContent) {
|
|
83
|
+
throw new Error("prompt or content is required for remixing.");
|
|
79
84
|
}
|
|
80
|
-
|
|
85
|
+
|
|
86
|
+
return this._post(`/apps/videos/tasks/${jobId}/remix`, {
|
|
81
87
|
appId: this.appId,
|
|
82
|
-
|
|
88
|
+
...options,
|
|
83
89
|
});
|
|
84
90
|
},
|
|
85
91
|
|
|
@@ -92,7 +98,7 @@ export const appsClientVoiceVideoMethods = {
|
|
|
92
98
|
if (!jobId) {
|
|
93
99
|
throw new Error("jobId is required.");
|
|
94
100
|
}
|
|
95
|
-
return this._get(`/apps/
|
|
101
|
+
return this._get(`/apps/videos/tasks/${jobId}`, { appId: this.appId });
|
|
96
102
|
},
|
|
97
103
|
|
|
98
104
|
/**
|
|
@@ -101,6 +107,6 @@ export const appsClientVoiceVideoMethods = {
|
|
|
101
107
|
* @returns {Promise<object>} 任务列表和分页信息
|
|
102
108
|
*/
|
|
103
109
|
async listVideoJobs(options = {}) {
|
|
104
|
-
return this._get("/apps/
|
|
110
|
+
return this._get("/apps/videos/tasks", { appId: this.appId, ...options });
|
|
105
111
|
},
|
|
106
112
|
};
|
|
@@ -2,6 +2,8 @@ import { getAppsApiBaseUrl, isBrowser, isBackend, isSSR, getGlobalOptions } from
|
|
|
2
2
|
|
|
3
3
|
const ACCESS_TOKEN_STORAGE_KEY = "tacoreai_apps_access_token";
|
|
4
4
|
const PREVIEW_APPSERVER_API_KEY_HEADER = "x-tacore-preview-app-server-api-key";
|
|
5
|
+
const APP_SERVER_SERVICE_TOKEN_HEADER = "x-tacore-app-server-service-token";
|
|
6
|
+
const APP_SERVER_OWNER_APP_ID_HEADER = "x-tacore-app-server-owner-app-id";
|
|
5
7
|
const INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR =
|
|
6
8
|
"tacoreServerInteropAppServerApiKey is required for backend environment. Provide it in config or via TACORE_SERVER_INTEROP_APP_SERVER_API_KEY env var.";
|
|
7
9
|
|
|
@@ -85,6 +87,14 @@ export class BaseAppsClient {
|
|
|
85
87
|
if (isBackend && !isSSR && !this.config.tacoreServerInteropAppServerApiKey) {
|
|
86
88
|
this.config.tacoreServerInteropAppServerApiKey = process.env.TACORE_SERVER_INTEROP_APP_SERVER_API_KEY;
|
|
87
89
|
}
|
|
90
|
+
if (
|
|
91
|
+
isBackend &&
|
|
92
|
+
!isSSR &&
|
|
93
|
+
this.config.enableAppServerServiceAuth === true &&
|
|
94
|
+
!this.config.tacoreAppServerServiceToken
|
|
95
|
+
) {
|
|
96
|
+
this.config.tacoreAppServerServiceToken = process.env.TACORE_APP_SERVER_SERVICE_TOKEN;
|
|
97
|
+
}
|
|
88
98
|
|
|
89
99
|
if (shouldRequireInteropAppServerApiKey() && !this.config.tacoreServerInteropAppServerApiKey) {
|
|
90
100
|
throw new Error(INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR);
|
|
@@ -186,8 +196,17 @@ export class BaseAppsClient {
|
|
|
186
196
|
headers["Authorization"] = `Bearer ${token}`;
|
|
187
197
|
}
|
|
188
198
|
|
|
189
|
-
if (
|
|
199
|
+
if (
|
|
200
|
+
isBackend &&
|
|
201
|
+
!isSSR &&
|
|
202
|
+
!token &&
|
|
203
|
+
this.config.enableAppServerServiceAuth === true &&
|
|
204
|
+
this.config.tacoreServerInteropAppServerApiKey &&
|
|
205
|
+
this.config.tacoreAppServerServiceToken
|
|
206
|
+
) {
|
|
190
207
|
headers["x-tacore-server-interop-app-server-api-key"] = this.config.tacoreServerInteropAppServerApiKey;
|
|
208
|
+
headers[APP_SERVER_SERVICE_TOKEN_HEADER] = this.config.tacoreAppServerServiceToken;
|
|
209
|
+
headers[APP_SERVER_OWNER_APP_ID_HEADER] = this.config.appServerOwnerAppId || this.config.appId || process.env.APP_ID || this.appId;
|
|
191
210
|
} else if (
|
|
192
211
|
isBackendPreviewMode() &&
|
|
193
212
|
!token &&
|
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.22.1-beta.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -13,6 +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\" && npm publish"
|
|
16
|
+
"release": "npm version minor && git commit -am \"web-sdk update version\" && git push && npm publish",
|
|
17
|
+
"release:beta": "npm version prerelease --preid beta --no-git-tag-version && git commit -am \"web-sdk update beta version\" && npm publish --tag beta"
|
|
17
18
|
}
|
|
18
19
|
}
|