deepfish-ai 1.0.26 → 1.0.28
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/package.json +1 -1
- package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/Logger.js +9 -3
- package/src/AgentRobot/BaseAgentRobot/index.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/AliBailian.js +997 -0
- package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +100 -5
- package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +8 -0
- package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +8 -8
- package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +59 -10
- package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +6 -4
- package/src/AgentRobot/BaseAgentRobot/utils/analyzeReturn.test.js +104 -0
- package/src/AgentRobot/BaseAgentRobot/utils/copilot.js +117 -0
- package/src/AgentRobot/BaseAgentRobot/utils/normal.js +288 -0
- package/src/cli/ConfigManager.js +25 -1
- package/src/cli/DefaultConfig.js +22 -6
- package/src/cli/SkillConfigManager.js +3 -2
- package/src/cli/ai-auth.js +231 -0
- package/src/cli/ai-config.js +21 -3
- package/src/cli/index.js +4 -2
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/embedding.js +0 -459
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
const fs = require('fs-extra')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
// 阿里百炼官方推荐 Base URL (OpenAI 兼容模式)
|
|
5
|
+
const REQUIRED_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
|
|
6
|
+
|
|
7
|
+
// 模型常量
|
|
8
|
+
const MODEL_TEXT_TO_IMAGE = 'wanx2.1-t2i-turbo'
|
|
9
|
+
const MODEL_TEXT_TO_VIDEO = 'wanx2.1-t2v-turbo'
|
|
10
|
+
const MODEL_IMAGE_RECOGNITION = 'qwen-vl-max'
|
|
11
|
+
const MODEL_TEXT_TO_SPEECH = 'qwen-tts'
|
|
12
|
+
const MODEL_SPEECH_TO_TEXT = 'qwen-audio-asr'
|
|
13
|
+
|
|
14
|
+
// 音频默认参数
|
|
15
|
+
const DEFAULT_TTS_VOICE = 'Cherry'
|
|
16
|
+
const DEFAULT_TTS_FORMAT = 'mp3'
|
|
17
|
+
|
|
18
|
+
function ok(data = null) {
|
|
19
|
+
return { success: true, data }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function fail(error, data = null) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
error: error?.message || String(error),
|
|
26
|
+
data,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getAiConfig(ctx) {
|
|
31
|
+
const aiConfig = ctx?.agentRobot?.aiConfig || {}
|
|
32
|
+
const baseUrl = String(aiConfig.baseUrl || '').trim()
|
|
33
|
+
const apiKey = String(aiConfig.apiKey || '').trim()
|
|
34
|
+
|
|
35
|
+
if (!baseUrl) {
|
|
36
|
+
throw new Error('aiConfig.baseUrl is empty, please configure AI baseUrl first')
|
|
37
|
+
}
|
|
38
|
+
if (!apiKey) {
|
|
39
|
+
throw new Error('aiConfig.apiKey is empty, please configure AI apiKey first')
|
|
40
|
+
}
|
|
41
|
+
return { baseUrl, apiKey }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function checkSkillAvailability(ctx) {
|
|
45
|
+
const { baseUrl } = getAiConfig(ctx)
|
|
46
|
+
// 检查是否包含 dashscope.aliyuncs.com
|
|
47
|
+
const normalized = String(baseUrl || '').toLowerCase()
|
|
48
|
+
const canUse = normalized.includes('dashscope.aliyuncs.com')
|
|
49
|
+
return {
|
|
50
|
+
canUse,
|
|
51
|
+
baseUrl,
|
|
52
|
+
requiredBaseUrl: REQUIRED_BASE_URL,
|
|
53
|
+
reason: canUse
|
|
54
|
+
? 'AliBailian skill is available.'
|
|
55
|
+
: `AliBailian skill is unavailable. aiConfig.baseUrl must contain 'dashscope.aliyuncs.com'.`,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function assertSkillAvailability(ctx) {
|
|
60
|
+
const checkResult = checkSkillAvailability(ctx)
|
|
61
|
+
if (!checkResult.canUse) {
|
|
62
|
+
throw new Error(checkResult.reason)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getRootBaseUrl(baseUrl) {
|
|
67
|
+
const normalized = String(baseUrl || '').replace(/\/+$/, '')
|
|
68
|
+
const lower = normalized.toLowerCase()
|
|
69
|
+
// coding 子域用于 OpenAI 兼容调用,AIGC 服务接口需走 dashscope 主域
|
|
70
|
+
if (lower.includes('coding.dashscope.aliyuncs.com')) {
|
|
71
|
+
return 'https://dashscope.aliyuncs.com'
|
|
72
|
+
}
|
|
73
|
+
if (lower.endsWith('/compatible-mode/v1')) {
|
|
74
|
+
return normalized.replace(/\/compatible-mode\/v1$/i, '')
|
|
75
|
+
}
|
|
76
|
+
if (lower.endsWith('/v1')) {
|
|
77
|
+
return normalized.replace(/\/v1$/i, '')
|
|
78
|
+
}
|
|
79
|
+
return normalized
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function joinUrl(baseUrl, endpoint) {
|
|
83
|
+
return `${String(baseUrl || '').replace(/\/+$/, '')}/${String(endpoint || '').replace(/^\/+/, '')}`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function sleep(ms) {
|
|
87
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildImageDataUrl(imagePath) {
|
|
91
|
+
const fullPath = path.resolve(process.cwd(), imagePath)
|
|
92
|
+
if (!fs.existsSync(fullPath)) {
|
|
93
|
+
throw new Error(`Image file does not exist: ${fullPath}`)
|
|
94
|
+
}
|
|
95
|
+
const ext = path.extname(fullPath).toLowerCase()
|
|
96
|
+
const mimeTypeMap = {
|
|
97
|
+
'.png': 'image/png',
|
|
98
|
+
'.jpg': 'image/jpeg',
|
|
99
|
+
'.jpeg': 'image/jpeg',
|
|
100
|
+
'.webp': 'image/webp',
|
|
101
|
+
'.gif': 'image/gif',
|
|
102
|
+
'.bmp': 'image/bmp',
|
|
103
|
+
}
|
|
104
|
+
const mimeType = mimeTypeMap[ext] || 'application/octet-stream'
|
|
105
|
+
const base64 = fs.readFileSync(fullPath).toString('base64')
|
|
106
|
+
return {
|
|
107
|
+
dataUrl: `data:${mimeType};base64,${base64}`,
|
|
108
|
+
fullPath,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getExtFromUrl(url) {
|
|
113
|
+
try {
|
|
114
|
+
const u = new URL(url)
|
|
115
|
+
const ext = path.extname(u.pathname || '').toLowerCase()
|
|
116
|
+
if (['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp'].includes(ext)) {
|
|
117
|
+
return ext
|
|
118
|
+
}
|
|
119
|
+
return '.png'
|
|
120
|
+
} catch {
|
|
121
|
+
return '.png'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getMediaExtFromUrl(url, fallbackExt = '.bin') {
|
|
126
|
+
try {
|
|
127
|
+
const u = new URL(url)
|
|
128
|
+
const ext = path.extname(u.pathname || '').toLowerCase()
|
|
129
|
+
return ext || fallbackExt
|
|
130
|
+
} catch {
|
|
131
|
+
return fallbackExt
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function collectImageUrls(payload) {
|
|
136
|
+
const urls = new Set()
|
|
137
|
+
|
|
138
|
+
function visit(node) {
|
|
139
|
+
if (!node) return
|
|
140
|
+
if (typeof node === 'string') {
|
|
141
|
+
if (/^https?:\/\//i.test(node) && /\.(png|jpe?g|webp|gif|bmp)(\?|$)/i.test(node)) {
|
|
142
|
+
urls.add(node)
|
|
143
|
+
}
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if (Array.isArray(node)) {
|
|
147
|
+
node.forEach((item) => visit(item))
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
if (typeof node === 'object') {
|
|
151
|
+
const directCandidates = [
|
|
152
|
+
node.url,
|
|
153
|
+
node.image_url,
|
|
154
|
+
node.imageUrl,
|
|
155
|
+
node.result_url,
|
|
156
|
+
node.resultUrl,
|
|
157
|
+
]
|
|
158
|
+
directCandidates.forEach((item) => visit(item))
|
|
159
|
+
Object.values(node).forEach((value) => visit(value))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
visit(payload)
|
|
164
|
+
return Array.from(urls)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function collectMediaUrls(payload, type = 'video') {
|
|
168
|
+
const urls = new Set()
|
|
169
|
+
const regexByType = {
|
|
170
|
+
video: /\.(mp4|mov|m4v|webm|avi|mkv)(\?|$)/i,
|
|
171
|
+
audio: /\.(mp3|wav|pcm|opus|aac|flac|m4a)(\?|$)/i,
|
|
172
|
+
}
|
|
173
|
+
const matcher = regexByType[type] || regexByType.video
|
|
174
|
+
|
|
175
|
+
function visit(node) {
|
|
176
|
+
if (!node) return
|
|
177
|
+
if (typeof node === 'string') {
|
|
178
|
+
if (/^https?:\/\//i.test(node) && matcher.test(node)) {
|
|
179
|
+
urls.add(node)
|
|
180
|
+
}
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(node)) {
|
|
184
|
+
node.forEach((item) => visit(item))
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
if (typeof node === 'object') {
|
|
188
|
+
Object.values(node).forEach((value) => visit(value))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
visit(payload)
|
|
193
|
+
return Array.from(urls)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function downloadImagesToCwd(ctx, imageUrls = []) {
|
|
197
|
+
const cwd = process.cwd()
|
|
198
|
+
const downloadedFiles = []
|
|
199
|
+
|
|
200
|
+
for (let i = 0; i < imageUrls.length; i += 1) {
|
|
201
|
+
const url = imageUrls[i]
|
|
202
|
+
const ext = getExtFromUrl(url)
|
|
203
|
+
const fileName = `bailian_image_${Date.now()}_${i + 1}${ext}`
|
|
204
|
+
const outputPath = path.join(cwd, fileName)
|
|
205
|
+
const response = await ctx.axios.get(url, {
|
|
206
|
+
responseType: 'arraybuffer',
|
|
207
|
+
timeout: 180000,
|
|
208
|
+
})
|
|
209
|
+
fs.writeFileSync(outputPath, response.data)
|
|
210
|
+
downloadedFiles.push(outputPath)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return downloadedFiles
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function downloadMediaToCwd(ctx, mediaUrls = [], prefix = 'bailian_media', fallbackExt = '.bin') {
|
|
217
|
+
const cwd = process.cwd()
|
|
218
|
+
const downloadedFiles = []
|
|
219
|
+
|
|
220
|
+
for (let i = 0; i < mediaUrls.length; i += 1) {
|
|
221
|
+
const url = mediaUrls[i]
|
|
222
|
+
const ext = getMediaExtFromUrl(url, fallbackExt)
|
|
223
|
+
const fileName = `${prefix}_${Date.now()}_${i + 1}${ext}`
|
|
224
|
+
const outputPath = path.join(cwd, fileName)
|
|
225
|
+
const response = await ctx.axios.get(url, {
|
|
226
|
+
responseType: 'arraybuffer',
|
|
227
|
+
timeout: 240000,
|
|
228
|
+
})
|
|
229
|
+
fs.writeFileSync(outputPath, response.data)
|
|
230
|
+
downloadedFiles.push(outputPath)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return downloadedFiles
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function postJson(ctx, url, body, timeout = 180000, headers = {}) {
|
|
237
|
+
const { apiKey } = getAiConfig(ctx)
|
|
238
|
+
const defaultHeaders = {
|
|
239
|
+
Authorization: `Bearer ${apiKey}`,
|
|
240
|
+
'Content-Type': 'application/json',
|
|
241
|
+
'X-DashScope-Async': 'enable', // 默认开启异步任务(适用于生图/生视频)
|
|
242
|
+
...headers,
|
|
243
|
+
}
|
|
244
|
+
const response = await ctx.axios.post(url, body, {
|
|
245
|
+
headers: defaultHeaders,
|
|
246
|
+
timeout,
|
|
247
|
+
})
|
|
248
|
+
return response.data
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function getAudioExt(format = DEFAULT_TTS_FORMAT) {
|
|
252
|
+
const fmt = String(format || '').toLowerCase()
|
|
253
|
+
if (['mp3', 'wav', 'pcm', 'opus', 'aac', 'flac'].includes(fmt)) {
|
|
254
|
+
return fmt === 'pcm' ? 'pcm' : fmt
|
|
255
|
+
}
|
|
256
|
+
return DEFAULT_TTS_FORMAT
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function saveAudioToCwd(buffer, format = DEFAULT_TTS_FORMAT, outputFileName = '') {
|
|
260
|
+
const cwd = process.cwd()
|
|
261
|
+
const ext = getAudioExt(format)
|
|
262
|
+
const fileName = outputFileName
|
|
263
|
+
? outputFileName
|
|
264
|
+
: `bailian_audio_${Date.now()}.${ext}`
|
|
265
|
+
const outputPath = path.join(cwd, fileName)
|
|
266
|
+
fs.writeFileSync(outputPath, buffer)
|
|
267
|
+
return outputPath
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function saveTextToCwd(text, prefix = 'bailian_text', ext = '.txt') {
|
|
271
|
+
const outputPath = path.join(process.cwd(), `${prefix}_${Date.now()}${ext}`)
|
|
272
|
+
fs.writeFileSync(outputPath, String(text || ''), 'utf8')
|
|
273
|
+
return outputPath
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function saveJsonToCwd(data, prefix = 'bailian_result') {
|
|
277
|
+
const outputPath = path.join(process.cwd(), `${prefix}_${Date.now()}.json`)
|
|
278
|
+
fs.writeJsonSync(outputPath, data, { spaces: 2 })
|
|
279
|
+
return outputPath
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function getJson(ctx, url, timeout = 120000) {
|
|
283
|
+
const { apiKey } = getAiConfig(ctx)
|
|
284
|
+
const headers = {
|
|
285
|
+
Authorization: `Bearer ${apiKey}`,
|
|
286
|
+
}
|
|
287
|
+
const response = await ctx.axios.get(url, { headers, timeout })
|
|
288
|
+
return response.data
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function parseTaskId(data) {
|
|
292
|
+
return (
|
|
293
|
+
data?.output?.task_id ||
|
|
294
|
+
data?.output?.taskId ||
|
|
295
|
+
data?.task_id ||
|
|
296
|
+
data?.taskId ||
|
|
297
|
+
data?.request_id ||
|
|
298
|
+
''
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function parseTaskStatus(data) {
|
|
303
|
+
return (
|
|
304
|
+
data?.output?.task_status ||
|
|
305
|
+
data?.output?.taskStatus ||
|
|
306
|
+
data?.task_status ||
|
|
307
|
+
data?.taskStatus ||
|
|
308
|
+
''
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function pollTaskResult(ctx, taskId, intervalMs = 3000, maxPoll = 40) {
|
|
313
|
+
const { baseUrl } = getAiConfig(ctx)
|
|
314
|
+
const rootBaseUrl = getRootBaseUrl(baseUrl)
|
|
315
|
+
const taskUrl = joinUrl(rootBaseUrl, `api/v1/tasks/${taskId}`)
|
|
316
|
+
|
|
317
|
+
let lastData = null
|
|
318
|
+
for (let i = 0; i < maxPoll; i += 1) {
|
|
319
|
+
const data = await getJson(ctx, taskUrl)
|
|
320
|
+
lastData = data
|
|
321
|
+
const status = String(parseTaskStatus(data)).toUpperCase()
|
|
322
|
+
if (!status) {
|
|
323
|
+
await sleep(intervalMs)
|
|
324
|
+
continue
|
|
325
|
+
}
|
|
326
|
+
if (['SUCCEEDED', 'SUCCESS', 'COMPLETED'].includes(status)) {
|
|
327
|
+
return ok({
|
|
328
|
+
taskId,
|
|
329
|
+
status,
|
|
330
|
+
task: data,
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
if (['FAILED', 'CANCELED', 'CANCELLED', 'ERROR'].includes(status)) {
|
|
334
|
+
return fail('Task failed', {
|
|
335
|
+
taskId,
|
|
336
|
+
status,
|
|
337
|
+
task: data,
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
await sleep(intervalMs)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return fail('Task polling timeout', {
|
|
344
|
+
taskId,
|
|
345
|
+
task: lastData,
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* 阿里百炼文生图
|
|
351
|
+
* 接口: POST /api/v1/services/aigc/text2image/image-synthesis
|
|
352
|
+
*/
|
|
353
|
+
async function aliBailianTextToImage(
|
|
354
|
+
prompt,
|
|
355
|
+
model = MODEL_TEXT_TO_IMAGE,
|
|
356
|
+
size = '1024*1024',
|
|
357
|
+
n = 1,
|
|
358
|
+
autoPoll = true,
|
|
359
|
+
autoDownload = true,
|
|
360
|
+
) {
|
|
361
|
+
try {
|
|
362
|
+
assertSkillAvailability(this)
|
|
363
|
+
if (!prompt) {
|
|
364
|
+
return fail('prompt is required')
|
|
365
|
+
}
|
|
366
|
+
const { baseUrl } = getAiConfig(this)
|
|
367
|
+
const rootBaseUrl = getRootBaseUrl(baseUrl)
|
|
368
|
+
const url = joinUrl(rootBaseUrl, 'api/v1/services/aigc/text2image/image-synthesis')
|
|
369
|
+
const body = {
|
|
370
|
+
model,
|
|
371
|
+
input: { prompt },
|
|
372
|
+
parameters: {
|
|
373
|
+
size,
|
|
374
|
+
n: Number(n) > 0 ? Number(n) : 1,
|
|
375
|
+
},
|
|
376
|
+
}
|
|
377
|
+
const data = await postJson(this, url, body)
|
|
378
|
+
const taskId = parseTaskId(data)
|
|
379
|
+
|
|
380
|
+
if (!autoPoll || !taskId) {
|
|
381
|
+
const imageUrls = collectImageUrls(data)
|
|
382
|
+
let downloadedFiles = []
|
|
383
|
+
const savedResultFile = await saveJsonToCwd(data, 'bailian_image_result')
|
|
384
|
+
if (autoDownload && imageUrls.length > 0) {
|
|
385
|
+
downloadedFiles = await downloadImagesToCwd(this, imageUrls)
|
|
386
|
+
}
|
|
387
|
+
return ok({
|
|
388
|
+
taskId,
|
|
389
|
+
status: parseTaskStatus(data) || 'PENDING',
|
|
390
|
+
imageUrls,
|
|
391
|
+
downloadedFiles,
|
|
392
|
+
savedResultFile,
|
|
393
|
+
response: data,
|
|
394
|
+
})
|
|
395
|
+
}
|
|
396
|
+
const pollResult = await pollTaskResult(this, taskId)
|
|
397
|
+
if (!pollResult.success) {
|
|
398
|
+
return pollResult
|
|
399
|
+
}
|
|
400
|
+
const imageUrls = collectImageUrls(pollResult.data?.task)
|
|
401
|
+
let downloadedFiles = []
|
|
402
|
+
const savedResultFile = await saveJsonToCwd(
|
|
403
|
+
pollResult.data?.task || {},
|
|
404
|
+
'bailian_image_result',
|
|
405
|
+
)
|
|
406
|
+
if (autoDownload && imageUrls.length > 0) {
|
|
407
|
+
downloadedFiles = await downloadImagesToCwd(this, imageUrls)
|
|
408
|
+
}
|
|
409
|
+
return ok({
|
|
410
|
+
...pollResult.data,
|
|
411
|
+
imageUrls,
|
|
412
|
+
downloadedFiles,
|
|
413
|
+
savedResultFile,
|
|
414
|
+
})
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return fail(error, { prompt, model, size, n, autoPoll, autoDownload })
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 阿里百炼文生视频
|
|
422
|
+
* 接口: POST /api/v1/services/aigc/video-generation/video-synthesis
|
|
423
|
+
*/
|
|
424
|
+
async function aliBailianTextToVideo(
|
|
425
|
+
prompt,
|
|
426
|
+
model = MODEL_TEXT_TO_VIDEO,
|
|
427
|
+
size = '1280*720',
|
|
428
|
+
duration = 5,
|
|
429
|
+
autoPoll = true,
|
|
430
|
+
autoDownload = true,
|
|
431
|
+
) {
|
|
432
|
+
try {
|
|
433
|
+
assertSkillAvailability(this)
|
|
434
|
+
if (!prompt) {
|
|
435
|
+
return fail('prompt is required')
|
|
436
|
+
}
|
|
437
|
+
const { baseUrl } = getAiConfig(this)
|
|
438
|
+
const rootBaseUrl = getRootBaseUrl(baseUrl)
|
|
439
|
+
const url = joinUrl(rootBaseUrl, 'api/v1/services/aigc/video-generation/video-synthesis')
|
|
440
|
+
const body = {
|
|
441
|
+
model,
|
|
442
|
+
input: { prompt },
|
|
443
|
+
parameters: {
|
|
444
|
+
size,
|
|
445
|
+
duration: Number(duration) > 0 ? Number(duration) : 5,
|
|
446
|
+
},
|
|
447
|
+
}
|
|
448
|
+
const data = await postJson(this, url, body, 240000)
|
|
449
|
+
const taskId = parseTaskId(data)
|
|
450
|
+
|
|
451
|
+
if (!autoPoll || !taskId) {
|
|
452
|
+
const videoUrls = collectMediaUrls(data, 'video')
|
|
453
|
+
let downloadedFiles = []
|
|
454
|
+
const savedResultFile = await saveJsonToCwd(data, 'bailian_video_result')
|
|
455
|
+
if (autoDownload && videoUrls.length > 0) {
|
|
456
|
+
downloadedFiles = await downloadMediaToCwd(this, videoUrls, 'bailian_video', '.mp4')
|
|
457
|
+
}
|
|
458
|
+
return ok({
|
|
459
|
+
taskId,
|
|
460
|
+
status: parseTaskStatus(data) || 'PENDING',
|
|
461
|
+
videoUrls,
|
|
462
|
+
downloadedFiles,
|
|
463
|
+
savedResultFile,
|
|
464
|
+
response: data,
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
const pollResult = await pollTaskResult(this, taskId, 5000, 60)
|
|
468
|
+
if (!pollResult.success) {
|
|
469
|
+
return pollResult
|
|
470
|
+
}
|
|
471
|
+
const videoUrls = collectMediaUrls(pollResult.data?.task, 'video')
|
|
472
|
+
let downloadedFiles = []
|
|
473
|
+
const savedResultFile = await saveJsonToCwd(
|
|
474
|
+
pollResult.data?.task || {},
|
|
475
|
+
'bailian_video_result',
|
|
476
|
+
)
|
|
477
|
+
if (autoDownload && videoUrls.length > 0) {
|
|
478
|
+
downloadedFiles = await downloadMediaToCwd(this, videoUrls, 'bailian_video', '.mp4')
|
|
479
|
+
}
|
|
480
|
+
return ok({
|
|
481
|
+
...pollResult.data,
|
|
482
|
+
videoUrls,
|
|
483
|
+
downloadedFiles,
|
|
484
|
+
savedResultFile,
|
|
485
|
+
})
|
|
486
|
+
} catch (error) {
|
|
487
|
+
return fail(error, { prompt, model, size, duration, autoPoll, autoDownload })
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* 阿里百炼图片识别 (多模态对话)
|
|
493
|
+
* 接口: POST /compatible-mode/v1/chat/completions
|
|
494
|
+
*/
|
|
495
|
+
async function aliBailianImageRecognition(
|
|
496
|
+
imagePath,
|
|
497
|
+
question = '请详细描述这张图片内容。',
|
|
498
|
+
model = MODEL_IMAGE_RECOGNITION,
|
|
499
|
+
autoSave = true,
|
|
500
|
+
outputFileName = '',
|
|
501
|
+
) {
|
|
502
|
+
try {
|
|
503
|
+
assertSkillAvailability(this)
|
|
504
|
+
if (!imagePath) {
|
|
505
|
+
return fail('imagePath is required')
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const { dataUrl, fullPath } = buildImageDataUrl(imagePath)
|
|
509
|
+
const { baseUrl } = getAiConfig(this)
|
|
510
|
+
// 直接使用 baseUrl,因为 baseUrl 通常是 compatible-mode/v1
|
|
511
|
+
const chatUrl = joinUrl(baseUrl, 'chat/completions')
|
|
512
|
+
|
|
513
|
+
const body = {
|
|
514
|
+
model,
|
|
515
|
+
messages: [
|
|
516
|
+
{
|
|
517
|
+
role: 'user',
|
|
518
|
+
content: [
|
|
519
|
+
{
|
|
520
|
+
type: 'text',
|
|
521
|
+
text: question,
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
type: 'image_url',
|
|
525
|
+
image_url: {
|
|
526
|
+
url: dataUrl,
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
stream: false,
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// 多模态对话通常不需要 X-DashScope-Async 头,直接同步返回
|
|
536
|
+
const { apiKey } = getAiConfig(this)
|
|
537
|
+
const headers = {
|
|
538
|
+
Authorization: `Bearer ${apiKey}`,
|
|
539
|
+
'Content-Type': 'application/json',
|
|
540
|
+
}
|
|
541
|
+
const response = await this.axios.post(chatUrl, body, { headers, timeout: 180000 })
|
|
542
|
+
const data = response.data
|
|
543
|
+
const content = data?.choices?.[0]?.message?.content || ''
|
|
544
|
+
let savedTextFile = ''
|
|
545
|
+
let savedResultFile = ''
|
|
546
|
+
if (autoSave) {
|
|
547
|
+
savedTextFile = outputFileName
|
|
548
|
+
? path.join(process.cwd(), outputFileName)
|
|
549
|
+
: await saveTextToCwd(content, 'bailian_image_recognition', '.txt')
|
|
550
|
+
if (outputFileName) {
|
|
551
|
+
fs.writeFileSync(savedTextFile, String(content || ''), 'utf8')
|
|
552
|
+
}
|
|
553
|
+
savedResultFile = await saveJsonToCwd(data, 'bailian_image_recognition_result')
|
|
554
|
+
}
|
|
555
|
+
return ok({
|
|
556
|
+
imagePath: fullPath,
|
|
557
|
+
question,
|
|
558
|
+
model,
|
|
559
|
+
content,
|
|
560
|
+
savedTextFile,
|
|
561
|
+
savedResultFile,
|
|
562
|
+
response: data,
|
|
563
|
+
})
|
|
564
|
+
} catch (error) {
|
|
565
|
+
return fail(error, { imagePath, question, model, autoSave, outputFileName })
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* 阿里百炼文字转语音 (OpenAI 兼容音频接口)
|
|
571
|
+
* 接口: POST /audio/speech
|
|
572
|
+
*/
|
|
573
|
+
async function aliBailianTextToSpeech(
|
|
574
|
+
text,
|
|
575
|
+
model = MODEL_TEXT_TO_SPEECH,
|
|
576
|
+
voice = DEFAULT_TTS_VOICE,
|
|
577
|
+
format = DEFAULT_TTS_FORMAT,
|
|
578
|
+
autoDownload = true,
|
|
579
|
+
outputFileName = '',
|
|
580
|
+
) {
|
|
581
|
+
try {
|
|
582
|
+
assertSkillAvailability(this)
|
|
583
|
+
if (!text) {
|
|
584
|
+
return fail('text is required')
|
|
585
|
+
}
|
|
586
|
+
const { baseUrl, apiKey } = getAiConfig(this)
|
|
587
|
+
const url = joinUrl(baseUrl, 'audio/speech')
|
|
588
|
+
const response = await this.axios.post(
|
|
589
|
+
url,
|
|
590
|
+
{
|
|
591
|
+
model,
|
|
592
|
+
input: text,
|
|
593
|
+
voice,
|
|
594
|
+
format,
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
headers: {
|
|
598
|
+
Authorization: `Bearer ${apiKey}`,
|
|
599
|
+
'Content-Type': 'application/json',
|
|
600
|
+
},
|
|
601
|
+
responseType: 'arraybuffer',
|
|
602
|
+
timeout: 180000,
|
|
603
|
+
},
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
const audioBuffer = Buffer.from(response.data)
|
|
607
|
+
let downloadedFile = ''
|
|
608
|
+
if (autoDownload) {
|
|
609
|
+
downloadedFile = await saveAudioToCwd(audioBuffer, format, outputFileName)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return ok({
|
|
613
|
+
model,
|
|
614
|
+
voice,
|
|
615
|
+
format: getAudioExt(format),
|
|
616
|
+
audioSize: audioBuffer.length,
|
|
617
|
+
downloadedFile,
|
|
618
|
+
})
|
|
619
|
+
} catch (error) {
|
|
620
|
+
return fail(error, {
|
|
621
|
+
text,
|
|
622
|
+
model,
|
|
623
|
+
voice,
|
|
624
|
+
format,
|
|
625
|
+
autoDownload,
|
|
626
|
+
outputFileName,
|
|
627
|
+
})
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* 阿里百炼语音转文字 (OpenAI 兼容音频接口)
|
|
633
|
+
* 接口: POST /audio/transcriptions
|
|
634
|
+
*/
|
|
635
|
+
async function aliBailianSpeechToText(
|
|
636
|
+
audioPath,
|
|
637
|
+
model = MODEL_SPEECH_TO_TEXT,
|
|
638
|
+
language = 'zh',
|
|
639
|
+
prompt = '',
|
|
640
|
+
autoSave = true,
|
|
641
|
+
outputFileName = '',
|
|
642
|
+
) {
|
|
643
|
+
try {
|
|
644
|
+
assertSkillAvailability(this)
|
|
645
|
+
if (!audioPath) {
|
|
646
|
+
return fail('audioPath is required')
|
|
647
|
+
}
|
|
648
|
+
const fullPath = path.resolve(process.cwd(), audioPath)
|
|
649
|
+
if (!fs.existsSync(fullPath)) {
|
|
650
|
+
return fail(`Audio file does not exist: ${fullPath}`)
|
|
651
|
+
}
|
|
652
|
+
if (typeof FormData === 'undefined' || typeof Blob === 'undefined') {
|
|
653
|
+
return fail('Current Node.js runtime does not support FormData/Blob for audio upload')
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const { baseUrl, apiKey } = getAiConfig(this)
|
|
657
|
+
const url = joinUrl(baseUrl, 'audio/transcriptions')
|
|
658
|
+
const fileBuffer = fs.readFileSync(fullPath)
|
|
659
|
+
const form = new FormData()
|
|
660
|
+
form.append('file', new Blob([fileBuffer]), path.basename(fullPath))
|
|
661
|
+
form.append('model', model)
|
|
662
|
+
if (language) {
|
|
663
|
+
form.append('language', language)
|
|
664
|
+
}
|
|
665
|
+
if (prompt) {
|
|
666
|
+
form.append('prompt', prompt)
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const extraHeaders =
|
|
670
|
+
typeof form.getHeaders === 'function' ? form.getHeaders() : {}
|
|
671
|
+
const response = await this.axios.post(url, form, {
|
|
672
|
+
headers: {
|
|
673
|
+
Authorization: `Bearer ${apiKey}`,
|
|
674
|
+
...extraHeaders,
|
|
675
|
+
},
|
|
676
|
+
timeout: 180000,
|
|
677
|
+
})
|
|
678
|
+
const data = response.data || {}
|
|
679
|
+
let savedTextFile = ''
|
|
680
|
+
let savedResultFile = ''
|
|
681
|
+
if (autoSave) {
|
|
682
|
+
savedTextFile = outputFileName
|
|
683
|
+
? path.join(process.cwd(), outputFileName)
|
|
684
|
+
: await saveTextToCwd(data.text || '', 'bailian_stt', '.txt')
|
|
685
|
+
if (outputFileName) {
|
|
686
|
+
fs.writeFileSync(savedTextFile, String(data.text || ''), 'utf8')
|
|
687
|
+
}
|
|
688
|
+
savedResultFile = await saveJsonToCwd(data, 'bailian_stt_result')
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return ok({
|
|
692
|
+
audioPath: fullPath,
|
|
693
|
+
model,
|
|
694
|
+
language,
|
|
695
|
+
text: data.text || '',
|
|
696
|
+
savedTextFile,
|
|
697
|
+
savedResultFile,
|
|
698
|
+
response: data,
|
|
699
|
+
})
|
|
700
|
+
} catch (error) {
|
|
701
|
+
return fail(error, { audioPath, model, language, prompt, autoSave, outputFileName })
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
async function aliBailianGetTaskResult(taskId, autoDownload = true) {
|
|
706
|
+
try {
|
|
707
|
+
assertSkillAvailability(this)
|
|
708
|
+
if (!taskId) {
|
|
709
|
+
return fail('taskId is required')
|
|
710
|
+
}
|
|
711
|
+
const result = await pollTaskResult(this, taskId, 0, 1)
|
|
712
|
+
if (!result.success) {
|
|
713
|
+
return result
|
|
714
|
+
}
|
|
715
|
+
const taskPayload = result.data?.task || {}
|
|
716
|
+
const imageUrls = collectImageUrls(taskPayload)
|
|
717
|
+
const videoUrls = collectMediaUrls(taskPayload, 'video')
|
|
718
|
+
const savedResultFile = await saveJsonToCwd(taskPayload, 'bailian_task_result')
|
|
719
|
+
let downloadedImageFiles = []
|
|
720
|
+
let downloadedVideoFiles = []
|
|
721
|
+
if (autoDownload && imageUrls.length > 0) {
|
|
722
|
+
downloadedImageFiles = await downloadImagesToCwd(this, imageUrls)
|
|
723
|
+
}
|
|
724
|
+
if (autoDownload && videoUrls.length > 0) {
|
|
725
|
+
downloadedVideoFiles = await downloadMediaToCwd(this, videoUrls, 'bailian_video', '.mp4')
|
|
726
|
+
}
|
|
727
|
+
return ok({
|
|
728
|
+
...result.data,
|
|
729
|
+
imageUrls,
|
|
730
|
+
videoUrls,
|
|
731
|
+
downloadedImageFiles,
|
|
732
|
+
downloadedVideoFiles,
|
|
733
|
+
savedResultFile,
|
|
734
|
+
})
|
|
735
|
+
} catch (error) {
|
|
736
|
+
return fail(error, { taskId, autoDownload })
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function aliBailianReadme() {
|
|
741
|
+
try {
|
|
742
|
+
const checkResult = checkSkillAvailability(this)
|
|
743
|
+
const statusText = checkResult.canUse ? 'Available' : 'Unavailable'
|
|
744
|
+
const usageNote = checkResult.canUse
|
|
745
|
+
? 'You can call aliBailianTextToImage, aliBailianTextToVideo, aliBailianImageRecognition and aliBailianGetTaskResult directly.'
|
|
746
|
+
: `This skill is disabled because aiConfig.baseUrl must contain 'dashscope.aliyuncs.com'. Please update your AI config first.`
|
|
747
|
+
|
|
748
|
+
return `# AliBailian Skill Guide\n\n- Status: ${statusText}\n- Current baseUrl: ${checkResult.baseUrl || '(empty)'}\n- Required baseUrl: ${checkResult.requiredBaseUrl}\n\n${usageNote}`
|
|
749
|
+
} catch (error) {
|
|
750
|
+
return `# AliBailian Skill Guide\n\n- Status: Unavailable\n- Reason: ${error?.message || String(error)}`
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const descriptions = [
|
|
755
|
+
{
|
|
756
|
+
type: 'function',
|
|
757
|
+
function: {
|
|
758
|
+
name: 'aliBailianReadme',
|
|
759
|
+
description:
|
|
760
|
+
'阿里百炼说明与可用性检测:先判断当前aiConfig.baseUrl是否配置正确,再返回工具说明。调用本工具前建议先调用此函数确认可用性。',
|
|
761
|
+
parameters: {
|
|
762
|
+
type: 'object',
|
|
763
|
+
properties: {},
|
|
764
|
+
required: [],
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
type: 'function',
|
|
770
|
+
function: {
|
|
771
|
+
name: 'aliBailianTextToSpeech',
|
|
772
|
+
description:
|
|
773
|
+
'阿里百炼文字转语音:将文本合成为语音。默认自动下载到当前工作目录。',
|
|
774
|
+
parameters: {
|
|
775
|
+
type: 'object',
|
|
776
|
+
properties: {
|
|
777
|
+
text: {
|
|
778
|
+
type: 'string',
|
|
779
|
+
description: '要合成语音的文本内容。',
|
|
780
|
+
},
|
|
781
|
+
model: {
|
|
782
|
+
type: 'string',
|
|
783
|
+
description: `文字转语音模型名,默认 ${MODEL_TEXT_TO_SPEECH}。`,
|
|
784
|
+
},
|
|
785
|
+
voice: {
|
|
786
|
+
type: 'string',
|
|
787
|
+
description: `音色名称,默认 ${DEFAULT_TTS_VOICE}。`,
|
|
788
|
+
},
|
|
789
|
+
format: {
|
|
790
|
+
type: 'string',
|
|
791
|
+
description: `音频格式(mp3/wav/opus 等),默认 ${DEFAULT_TTS_FORMAT}。`,
|
|
792
|
+
},
|
|
793
|
+
autoDownload: {
|
|
794
|
+
type: 'boolean',
|
|
795
|
+
description: '是否自动下载到当前工作目录,默认 true。',
|
|
796
|
+
},
|
|
797
|
+
outputFileName: {
|
|
798
|
+
type: 'string',
|
|
799
|
+
description: '可选,自定义输出文件名(如 my_tts.mp3)。',
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
required: ['text'],
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
type: 'function',
|
|
808
|
+
function: {
|
|
809
|
+
name: 'aliBailianSpeechToText',
|
|
810
|
+
description:
|
|
811
|
+
'阿里百炼语音转文字:上传本地音频文件并返回识别文本。',
|
|
812
|
+
parameters: {
|
|
813
|
+
type: 'object',
|
|
814
|
+
properties: {
|
|
815
|
+
audioPath: {
|
|
816
|
+
type: 'string',
|
|
817
|
+
description: '本地音频路径(相对路径或绝对路径)。',
|
|
818
|
+
},
|
|
819
|
+
model: {
|
|
820
|
+
type: 'string',
|
|
821
|
+
description: `语音转文字模型名,默认 ${MODEL_SPEECH_TO_TEXT}。`,
|
|
822
|
+
},
|
|
823
|
+
language: {
|
|
824
|
+
type: 'string',
|
|
825
|
+
description: '识别语言,默认 zh。',
|
|
826
|
+
},
|
|
827
|
+
prompt: {
|
|
828
|
+
type: 'string',
|
|
829
|
+
description: '可选,识别提示词。',
|
|
830
|
+
},
|
|
831
|
+
autoSave: {
|
|
832
|
+
type: 'boolean',
|
|
833
|
+
description: '是否自动保存识别文本和原始结果到当前工作目录,默认 true。',
|
|
834
|
+
},
|
|
835
|
+
outputFileName: {
|
|
836
|
+
type: 'string',
|
|
837
|
+
description: '可选,自定义转写文本文件名。',
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
required: ['audioPath'],
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
type: 'function',
|
|
846
|
+
function: {
|
|
847
|
+
name: 'aliBailianTextToImage',
|
|
848
|
+
description:
|
|
849
|
+
'阿里百炼文生图:根据文本提示词生成图片。自动读取 this.agentRobot.aiConfig 中的 baseUrl 和 apiKey 进行调用。',
|
|
850
|
+
parameters: {
|
|
851
|
+
type: 'object',
|
|
852
|
+
properties: {
|
|
853
|
+
prompt: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: '图片提示词。',
|
|
856
|
+
},
|
|
857
|
+
model: {
|
|
858
|
+
type: 'string',
|
|
859
|
+
description: `文生图模型名,默认 ${MODEL_TEXT_TO_IMAGE}。`,
|
|
860
|
+
},
|
|
861
|
+
size: {
|
|
862
|
+
type: 'string',
|
|
863
|
+
description: '图片尺寸,默认 1024*1024。',
|
|
864
|
+
},
|
|
865
|
+
n: {
|
|
866
|
+
type: 'number',
|
|
867
|
+
description: '生成图片数量,默认 1。',
|
|
868
|
+
},
|
|
869
|
+
autoPoll: {
|
|
870
|
+
type: 'boolean',
|
|
871
|
+
description: '是否自动轮询任务结果,默认 true。',
|
|
872
|
+
},
|
|
873
|
+
autoDownload: {
|
|
874
|
+
type: 'boolean',
|
|
875
|
+
description: '是否自动下载生成图片到当前工作目录,默认 true。',
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
required: ['prompt'],
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
type: 'function',
|
|
884
|
+
function: {
|
|
885
|
+
name: 'aliBailianTextToVideo',
|
|
886
|
+
description:
|
|
887
|
+
'阿里百炼文生视频:根据文本提示词生成视频。自动读取 this.agentRobot.aiConfig 中的 baseUrl 和 apiKey 进行调用。',
|
|
888
|
+
parameters: {
|
|
889
|
+
type: 'object',
|
|
890
|
+
properties: {
|
|
891
|
+
prompt: {
|
|
892
|
+
type: 'string',
|
|
893
|
+
description: '视频提示词。',
|
|
894
|
+
},
|
|
895
|
+
model: {
|
|
896
|
+
type: 'string',
|
|
897
|
+
description: `文生视频模型名,默认 ${MODEL_TEXT_TO_VIDEO}。`,
|
|
898
|
+
},
|
|
899
|
+
size: {
|
|
900
|
+
type: 'string',
|
|
901
|
+
description: '视频尺寸,默认 1280*720。',
|
|
902
|
+
},
|
|
903
|
+
duration: {
|
|
904
|
+
type: 'number',
|
|
905
|
+
description: '视频时长(秒),默认 5。',
|
|
906
|
+
},
|
|
907
|
+
autoPoll: {
|
|
908
|
+
type: 'boolean',
|
|
909
|
+
description: '是否自动轮询任务结果,默认 true。',
|
|
910
|
+
},
|
|
911
|
+
autoDownload: {
|
|
912
|
+
type: 'boolean',
|
|
913
|
+
description: '是否自动下载生成视频到当前工作目录,默认 true。',
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
required: ['prompt'],
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
type: 'function',
|
|
922
|
+
function: {
|
|
923
|
+
name: 'aliBailianImageRecognition',
|
|
924
|
+
description:
|
|
925
|
+
'阿里百炼图片识别:输入本地图片路径和问题,返回识别与理解结果。自动读取 this.agentRobot.aiConfig 中的 baseUrl 和 apiKey 进行调用。',
|
|
926
|
+
parameters: {
|
|
927
|
+
type: 'object',
|
|
928
|
+
properties: {
|
|
929
|
+
imagePath: {
|
|
930
|
+
type: 'string',
|
|
931
|
+
description: '本地图片路径(相对路径或绝对路径)。',
|
|
932
|
+
},
|
|
933
|
+
question: {
|
|
934
|
+
type: 'string',
|
|
935
|
+
description: '关于图片的问题,默认“请详细描述这张图片内容。”。',
|
|
936
|
+
},
|
|
937
|
+
model: {
|
|
938
|
+
type: 'string',
|
|
939
|
+
description: `多模态模型名,默认 ${MODEL_IMAGE_RECOGNITION}。`,
|
|
940
|
+
},
|
|
941
|
+
autoSave: {
|
|
942
|
+
type: 'boolean',
|
|
943
|
+
description: '是否自动保存识别文本和原始结果到当前工作目录,默认 true。',
|
|
944
|
+
},
|
|
945
|
+
outputFileName: {
|
|
946
|
+
type: 'string',
|
|
947
|
+
description: '可选,自定义识别文本文件名。',
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
required: ['imagePath'],
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
type: 'function',
|
|
956
|
+
function: {
|
|
957
|
+
name: 'aliBailianGetTaskResult',
|
|
958
|
+
description: '阿里百炼任务查询:根据 taskId 查询一次任务当前状态与结果。',
|
|
959
|
+
parameters: {
|
|
960
|
+
type: 'object',
|
|
961
|
+
properties: {
|
|
962
|
+
taskId: {
|
|
963
|
+
type: 'string',
|
|
964
|
+
description: '阿里百炼异步任务ID。',
|
|
965
|
+
},
|
|
966
|
+
autoDownload: {
|
|
967
|
+
type: 'boolean',
|
|
968
|
+
description: '是否自动下载任务中的图片/视频并保存结果JSON到当前工作目录,默认 true。',
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
required: ['taskId'],
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
]
|
|
976
|
+
|
|
977
|
+
const functions = {
|
|
978
|
+
aliBailianReadme,
|
|
979
|
+
aliBailianTextToSpeech,
|
|
980
|
+
aliBailianSpeechToText,
|
|
981
|
+
aliBailianTextToImage,
|
|
982
|
+
aliBailianTextToVideo,
|
|
983
|
+
aliBailianImageRecognition,
|
|
984
|
+
aliBailianGetTaskResult,
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const AliBailianTool = {
|
|
988
|
+
name: 'AliBailianTool',
|
|
989
|
+
description:
|
|
990
|
+
'提供阿里百炼多模态能力,包括文生图、文生视频、图片识别与任务查询,统一使用当前AI配置的baseUrl/apiKey。',
|
|
991
|
+
platform: 'all',
|
|
992
|
+
descriptions,
|
|
993
|
+
functions,
|
|
994
|
+
isSystem: true,
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
module.exports = AliBailianTool
|