skillfree 0.1.4 → 0.1.6

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/SKILL.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 功能
4
4
 
5
- SkillFree 是一个统一的 AI 能力调用层。通过一个 API Key,调用对话、图像生成、语音合成、视频生成、音乐生成、文档 OCR、向量 Embedding 等 56 个模型。
5
+ SkillFree 是一个统一的 AI 能力调用层。通过一个 API Key,调用对话、图像生成、语音合成、视频生成、音乐生成、文档 OCR、向量 Embedding 等 53 个模型。
6
6
 
7
7
  ## 使用方式
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "🦞 一个 API,满足所有龙虾技能需求",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -75,8 +75,30 @@ async function pilot(flags) {
75
75
 
76
76
  // ── IMAGE ─────────────────────────────────────────────────────────────────────
77
77
  if (type === 'image') {
78
+ const imageModel = model || 'gemini-3.1-flash-image-preview'
79
+
80
+ // qwen-image-edit-plus:图片编辑,走 /v1/images/edits,multipart/form-data
81
+ if (imageModel.startsWith('qwen-image')) {
82
+ if (!flags.file) throw new Error('qwen-image-edit 需要 --file 指定原始图片路径')
83
+ const imgBuf = fs.readFileSync(path.resolve(flags.file))
84
+ const form = new FormData()
85
+ form.append('model', imageModel)
86
+ form.append('prompt', prompt)
87
+ form.append('size', flags.size || '1024x1024')
88
+ form.append('image', new Blob([imgBuf], { type: 'image/png' }), path.basename(flags.file))
89
+ const res = await request('/images/edits', { method: 'POST', body: form })
90
+ const data = await res.json()
91
+ if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
92
+ const url = data.data?.[0]?.url
93
+ if (!url) throw new Error('未返回图像URL: ' + JSON.stringify(data).slice(0, 200))
94
+ if (output) await downloadAndSave(url, output)
95
+ else console.log('图像 URL:', url)
96
+ return
97
+ }
98
+
99
+ // doubao-seedream-5.0-lite / 其他标准图像模型
78
100
  const res = await post('/images/generations', {
79
- model: model || 'gemini-3.1-flash-image-preview',
101
+ model: imageModel,
80
102
  prompt,
81
103
  n: 1,
82
104
  size: flags.size || '1024x1024',
@@ -85,12 +107,8 @@ async function pilot(flags) {
85
107
  const url = res.data?.[0]?.url || res.data?.[0]?.b64_json
86
108
  if (!url) throw new Error('未返回图像数据: ' + JSON.stringify(res).slice(0, 200))
87
109
  if (output) {
88
- if (url.startsWith('http')) {
89
- await downloadAndSave(url, output)
90
- } else {
91
- fs.writeFileSync(output, Buffer.from(url, 'base64'))
92
- console.log(`✅ 已保存到 ${output}`)
93
- }
110
+ if (url.startsWith('http')) await downloadAndSave(url, output)
111
+ else { fs.writeFileSync(output, Buffer.from(url, 'base64')); console.log(`✅ 已保存到 ${output}`) }
94
112
  } else {
95
113
  console.log('图像 URL:', url)
96
114
  }
@@ -216,8 +234,26 @@ async function pilot(flags) {
216
234
  throw new Error('Suno 生成超时(150s),请手动查询 task_id: ' + taskId)
217
235
 
218
236
  } else {
219
- // music-2.5(MiniMax)暂不支持(需要 lyrics 参数,建议直接调 /v1/responses
220
- throw new Error(`music-2.5 需要通过 /v1/responses 直接调用,请参考文档`)
237
+ // music-2.5(MiniMax),走 /v1/responses,需要 lyrics
238
+ const res = await request('/responses', {
239
+ method: 'POST',
240
+ body: JSON.stringify({
241
+ model: 'music-2.5',
242
+ input: prompt,
243
+ lyrics: flags.lyrics || `[verse]\n${prompt}`,
244
+ audio_setting: { sample_rate: 44100, bitrate: 256000, format: 'mp3' },
245
+ output_format: 'url',
246
+ stream: false,
247
+ }),
248
+ })
249
+ const data = await res.json()
250
+ if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
251
+ const audioUrl = data.output?.[0]?.content?.[0]?.audio
252
+ if (!audioUrl) throw new Error('未返回音频: ' + JSON.stringify(data).slice(0, 200))
253
+ const duration = ((data.extra_info?.music_duration || 0) / 1000).toFixed(1)
254
+ console.log(`✅ music-2.5 生成成功!时长约 ${duration} 秒`)
255
+ console.log('音频 URL:', audioUrl)
256
+ if (output) await downloadAndSave(audioUrl, output)
221
257
  }
222
258
  }
223
259
 
@@ -265,7 +301,38 @@ async function pilot(flags) {
265
301
  return
266
302
  }
267
303
 
268
- throw new Error(`不支持的类型: ${type},可选: chat | image | tts | stt | music | ocr | video`)
304
+ // ── EMBEDDING ─────────────────────────────────────────────────────────────────
305
+ if (type === 'embedding') {
306
+ // doubao-embedding-vision:走 /v1/responses(多模态),input 为数组
307
+ // 简单文本 embedding 也可用
308
+ const inputData = flags.file
309
+ ? [{ type: 'image_url', image_url: { url: 'data:image/png;base64,' + fs.readFileSync(path.resolve(flags.file)).toString('base64') } }]
310
+ : [{ type: 'text', text: prompt }]
311
+ const res = await request('/embeddings', {
312
+ method: 'POST',
313
+ body: JSON.stringify({
314
+ model: model || 'doubao-embedding-vision-251215',
315
+ input: inputData,
316
+ encoding_format: 'float',
317
+ dimensions: 1024,
318
+ sparse_embedding: { type: 'disabled' },
319
+ }),
320
+ })
321
+ const data = await res.json()
322
+ if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
323
+ const embedding = data.data?.[0]?.embedding
324
+ if (!embedding) throw new Error('未返回向量: ' + JSON.stringify(data).slice(0, 200))
325
+ console.log(`✅ Embedding 成功!维度: ${embedding.length}`)
326
+ if (output) {
327
+ fs.writeFileSync(output, JSON.stringify(embedding))
328
+ console.log(`已保存到 ${output}`)
329
+ } else {
330
+ console.log('前5维:', embedding.slice(0, 5))
331
+ }
332
+ return
333
+ }
334
+
335
+ throw new Error(`不支持的类型: ${type},可选: chat | image | tts | stt | music | ocr | video | embedding`)
269
336
  }
270
337
 
271
338
  module.exports = { pilot }