@pippit-dev/cli 1.0.14 → 1.0.15
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
|
@@ -273,6 +273,22 @@ pippit-tool-cli generate-video \
|
|
|
273
273
|
|
|
274
274
|
命令输出 `thread_id`、`run_id` 和 `web_thread_link`。提交生视频 HTTP 请求时,参考图、参考视频和参考音频会使用上传接口返回的 `pippit_asset_id`,并分别写入 `video_part_tool_param.images`、`video_part_tool_param.videos` 和 `video_part_tool_param.audios`。图片最多 9 张,支持 `.jpg`、`.jpeg`、`.png`、`.gif`、`.bmp`、`.webp`、`.svg`;视频最多 3 个,支持 `.mp4`、`.avi`、`.mov`、`.wmv`、`.flv`、`.webm`、`.mkv`、`.m4v`;音频最多 3 个,仅支持 `.mp3`、`.wav`。普通用户支持模型 `Seedance_2.0_mini_lite`;`seedance2.0_vision`、`seedance2.0_fast_vision` 和 `Seedance_2.0_mini` 为 VIP 专属模型。CLI 会在提交前校验 prompt、素材数量和文件后缀;模型、比例、分辨率等语义校验由服务端处理。
|
|
275
275
|
|
|
276
|
+
首尾帧生视频时,按首帧、尾帧的顺序传入两次 `--image`,并设置 `--generate-type 1`:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pippit-tool-cli generate-video \
|
|
280
|
+
--prompt "让镜头从首帧平滑过渡到尾帧" \
|
|
281
|
+
--image "~/images/first.jpg" \
|
|
282
|
+
--image "~/images/last.jpg" \
|
|
283
|
+
--duration 5 \
|
|
284
|
+
--ratio "16:9" \
|
|
285
|
+
--model "Seedance_2.0_mini" \
|
|
286
|
+
--resolution "720p" \
|
|
287
|
+
--generate-type 1
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`--generate-type` 可选,填写后原样写入 `video_part_tool_param.generate_type`;值 `1` 表示首尾帧生成。CLI 保持图片上传和请求中的输入顺序,不在本地校验该参数的枚举值,具体能力与约束由服务端决定。
|
|
291
|
+
|
|
276
292
|
查询并下载生图/生视频结果:
|
|
277
293
|
|
|
278
294
|
```bash
|
package/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
7daa51a0f44e35afb9ce5ffc6b95bb2aaf138a1563a94279b3f5dc5c742677e3 pippit-tool-cli-1.0.15-darwin-amd64.tar.gz
|
|
2
|
+
0a9f4cfa203617987c150695eaf41d4186bff9331356d2d846a8a5cd2bc6fe0c pippit-tool-cli-1.0.15-darwin-arm64.tar.gz
|
|
3
|
+
3fcc0fe05f9539a978a1e94e99140f685953ade1ccdfecb16e6e41866792fdfa pippit-tool-cli-1.0.15-linux-amd64.tar.gz
|
|
4
|
+
138c7389f51798ccd04f56a11966a69143a68212dfa04115d98b3e2d1b7af993 pippit-tool-cli-1.0.15-linux-arm64.tar.gz
|
|
5
|
+
f922c90c29fe6ee9e8fe6d911153a242933cbd8d71bb0b8011c963772d929a68 pippit-tool-cli-1.0.15-windows-amd64.zip
|
|
6
|
+
676afe65912cd4c1324f0165395c5493fdb0dc8d4966528e5f1b4c782a2ae1a5 pippit-tool-cli-1.0.15-windows-arm64.zip
|
|
@@ -13,6 +13,7 @@ import (
|
|
|
13
13
|
func NewCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Command {
|
|
14
14
|
opts := &internalgen.Options{}
|
|
15
15
|
var durationSec int
|
|
16
|
+
var generateType int64
|
|
16
17
|
|
|
17
18
|
cmd := &cobra.Command{
|
|
18
19
|
Use: "generate-video",
|
|
@@ -22,6 +23,9 @@ func NewCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Command
|
|
|
22
23
|
if cmd.Flags().Changed("duration") {
|
|
23
24
|
opts.DurationSec = &durationSec
|
|
24
25
|
}
|
|
26
|
+
if cmd.Flags().Changed("generate-type") {
|
|
27
|
+
opts.GenerateType = &generateType
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
result, err := internalgen.Run(cmd.Context(), opts, runner)
|
|
27
31
|
if err != nil {
|
|
@@ -46,5 +50,6 @@ func NewCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Command
|
|
|
46
50
|
flags.StringVar(&opts.Ratio, "ratio", "", "video ratio, such as 9:16, 16:9, 3:4, 4:3")
|
|
47
51
|
flags.StringVar(&opts.Model, "model", "", "video model; normal users: Seedance_2.0_mini_lite; VIP-only: seedance2.0_vision, seedance2.0_fast_vision, Seedance_2.0_mini")
|
|
48
52
|
flags.StringVar(&opts.Resolution, "resolution", "", "video resolution, such as 720p, 1080p")
|
|
53
|
+
flags.Int64Var(&generateType, "generate-type", 0, "generation type passed to the service; set 1 for first-and-last-frame generation and provide two --image values in first-frame, last-frame order")
|
|
49
54
|
return cmd
|
|
50
55
|
}
|
|
@@ -72,6 +72,9 @@ func TestGenerateVideo(t *testing.T) {
|
|
|
72
72
|
if param["ratio"] != "9:16" || param["model"] != "seedance2.0_vision" || param["resolution"] != "720p" {
|
|
73
73
|
t.Fatalf("param = %#v, want ratio/model/resolution", param)
|
|
74
74
|
}
|
|
75
|
+
if param["generate_type"] != float64(1) {
|
|
76
|
+
t.Fatalf("generate_type = %v, want 1", param["generate_type"])
|
|
77
|
+
}
|
|
75
78
|
assertAssetRefs(t, param["images"], []string{"image_asset_1", "image_asset_2"})
|
|
76
79
|
assertAssetRefs(t, param["videos"], []string{"video_asset_1", "video_asset_2"})
|
|
77
80
|
assertAssetRefs(t, param["audios"], []string{"audio_asset_1"})
|
|
@@ -108,6 +111,7 @@ func TestGenerateVideo(t *testing.T) {
|
|
|
108
111
|
"--ratio", "9:16",
|
|
109
112
|
"--model", "seedance2.0_vision",
|
|
110
113
|
"--resolution", "720p",
|
|
114
|
+
"--generate-type", "1",
|
|
111
115
|
})
|
|
112
116
|
|
|
113
117
|
if err := root.Execute(); err != nil {
|
|
@@ -124,6 +128,21 @@ func TestGenerateVideoSkipsSemanticValidation(t *testing.T) {
|
|
|
124
128
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
125
129
|
switch r.URL.Path {
|
|
126
130
|
case "/api/biz/v1/skill/submit_run":
|
|
131
|
+
data, err := io.ReadAll(r.Body)
|
|
132
|
+
if err != nil {
|
|
133
|
+
t.Fatalf("read body: %v", err)
|
|
134
|
+
}
|
|
135
|
+
var body map[string]any
|
|
136
|
+
if err := sonic.Unmarshal(data, &body); err != nil {
|
|
137
|
+
t.Fatalf("decode body: %v", err)
|
|
138
|
+
}
|
|
139
|
+
param, ok := body["video_part_tool_param"].(map[string]any)
|
|
140
|
+
if !ok {
|
|
141
|
+
t.Fatalf("video_part_tool_param = %#v, want object", body["video_part_tool_param"])
|
|
142
|
+
}
|
|
143
|
+
if param["generate_type"] != float64(99) {
|
|
144
|
+
t.Fatalf("generate_type = %v, want 99", param["generate_type"])
|
|
145
|
+
}
|
|
127
146
|
_, _ = w.Write([]byte(`{"ret":"0","errmsg":"","data":{"run":{"thread_id":"thread_123","run_id":"run_456"}}}`))
|
|
128
147
|
default:
|
|
129
148
|
t.Fatalf("unexpected path %s", r.URL.Path)
|
|
@@ -140,6 +159,7 @@ func TestGenerateVideoSkipsSemanticValidation(t *testing.T) {
|
|
|
140
159
|
"--ratio", "1:1",
|
|
141
160
|
"--model", "bad_model",
|
|
142
161
|
"--resolution", "bad_resolution",
|
|
162
|
+
"--generate-type", "99",
|
|
143
163
|
})
|
|
144
164
|
|
|
145
165
|
if err := root.Execute(); err != nil {
|
|
@@ -28,14 +28,15 @@ var (
|
|
|
28
28
|
|
|
29
29
|
// Options is the stable command-facing request shape for generate-video.
|
|
30
30
|
type Options struct {
|
|
31
|
-
Prompt
|
|
32
|
-
ImagePaths
|
|
33
|
-
VideoPaths
|
|
34
|
-
AudioPaths
|
|
35
|
-
DurationSec
|
|
36
|
-
Ratio
|
|
37
|
-
Model
|
|
38
|
-
Resolution
|
|
31
|
+
Prompt string
|
|
32
|
+
ImagePaths []string
|
|
33
|
+
VideoPaths []string
|
|
34
|
+
AudioPaths []string
|
|
35
|
+
DurationSec *int
|
|
36
|
+
Ratio string
|
|
37
|
+
Model string
|
|
38
|
+
Resolution string
|
|
39
|
+
GenerateType *int64
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
type mediaAsset struct {
|
|
@@ -43,14 +44,15 @@ type mediaAsset struct {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
type videoPartToolParam struct {
|
|
46
|
-
Images
|
|
47
|
-
Prompt
|
|
48
|
-
DurationSec
|
|
49
|
-
Ratio
|
|
50
|
-
Videos
|
|
51
|
-
Audios
|
|
52
|
-
Model
|
|
53
|
-
Resolution
|
|
47
|
+
Images []mediaAsset `json:"images,omitempty"`
|
|
48
|
+
Prompt string `json:"prompt"`
|
|
49
|
+
DurationSec *int `json:"duration_sec,omitempty"`
|
|
50
|
+
Ratio string `json:"ratio,omitempty"`
|
|
51
|
+
Videos []mediaAsset `json:"videos,omitempty"`
|
|
52
|
+
Audios []mediaAsset `json:"audios,omitempty"`
|
|
53
|
+
Model string `json:"model,omitempty"`
|
|
54
|
+
Resolution string `json:"resolution,omitempty"`
|
|
55
|
+
GenerateType *int64 `json:"generate_type,omitempty"`
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
// Result is the JSON envelope printed by `pippit-tool-cli generate-video`.
|
|
@@ -171,14 +173,15 @@ func uploadMediaList(ctx context.Context, paths []string, runner *common.Runner)
|
|
|
171
173
|
|
|
172
174
|
func buildSubmitRunBody(opts *Options, imageAssetIDs []string, videoAssetIDs []string, audioAssetIDs []string) map[string]any {
|
|
173
175
|
param := videoPartToolParam{
|
|
174
|
-
Images:
|
|
175
|
-
Prompt:
|
|
176
|
-
DurationSec:
|
|
177
|
-
Ratio:
|
|
178
|
-
Videos:
|
|
179
|
-
Audios:
|
|
180
|
-
Model:
|
|
181
|
-
Resolution:
|
|
176
|
+
Images: assetRefs(imageAssetIDs),
|
|
177
|
+
Prompt: strings.TrimSpace(opts.Prompt),
|
|
178
|
+
DurationSec: opts.DurationSec,
|
|
179
|
+
Ratio: strings.TrimSpace(opts.Ratio),
|
|
180
|
+
Videos: assetRefs(videoAssetIDs),
|
|
181
|
+
Audios: assetRefs(audioAssetIDs),
|
|
182
|
+
Model: strings.TrimSpace(opts.Model),
|
|
183
|
+
Resolution: strings.TrimSpace(opts.Resolution),
|
|
184
|
+
GenerateType: opts.GenerateType,
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
return map[string]any{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: xyq-skill
|
|
3
|
-
description: 通过小云雀的 AI 能力进行综合创作,支持生成和编辑图片/视频,并在用户明确要求图片或视频模型直出、指定图片或视频模型或直接调用 CLI 时使用 pippit-tool-cli generate-image / generate-video
|
|
3
|
+
description: 通过小云雀的 AI 能力进行综合创作,支持生成和编辑图片/视频,并在用户明确要求图片或视频模型直出、指定图片或视频模型或直接调用 CLI 时使用 pippit-tool-cli generate-image / generate-video。覆盖文生图、文生视频、图生视频、首尾帧生视频、视频编辑、风格转换、视频续写、视频复刻、TVC、宣传片、音乐 MV、产品广告、分镜和教育短视频等场景。当用户提到小云雀、xyq、上传参考图/视频/mp3或wav音频、查看生成进度时也应触发。短剧生成、续写、改写、人物设定和分集创作应使用 xyq-short-drama-skill,不在本技能中执行。
|
|
4
4
|
user-invocable: true
|
|
5
5
|
metadata:
|
|
6
6
|
{
|
|
@@ -71,6 +71,7 @@ pippit-tool-cli query-result \
|
|
|
71
71
|
|
|
72
72
|
- 用户明确说“视频模型直出”、“直接调模型”或“直接调用 CLI”。
|
|
73
73
|
- 用户指定了具体视频模型(如 Seedance),并希望单次直接生成视频。
|
|
74
|
+
- 用户明确要求“首尾帧生视频”、指定首帧和尾帧,或要求从第一张图过渡到第二张图。
|
|
74
75
|
- 上游流程已明确将任务标记为 direct-model / 模型直出。
|
|
75
76
|
|
|
76
77
|
执行原则:
|
|
@@ -78,13 +79,20 @@ pippit-tool-cli query-result \
|
|
|
78
79
|
1. 执行前用 `command -v pippit-tool-cli` 确认 CLI 可用;不可用时报告阻塞,不要悄悄降级到会话 API。
|
|
79
80
|
2. 真实提交会消耗 credits;如果用户本轮尚未明确确认生成,按“用户确认与反问”规则征得明确确认后再运行。
|
|
80
81
|
3. 保留用户原始 prompt,不要自行扩写、润色、翻译或增加风格词。
|
|
81
|
-
4. 只添加用户已经给出的 `--model`、`--duration`、`--ratio`、`--resolution`、`--image`、`--video`、`--audio` 参数;未给参数交给 CLI 默认值。
|
|
82
|
-
5.
|
|
83
|
-
6.
|
|
82
|
+
4. 只添加用户已经给出的 `--model`、`--duration`、`--ratio`、`--resolution`、`--image`、`--video`、`--audio`、`--generate-type` 参数;未给参数交给 CLI 默认值。
|
|
83
|
+
5. 首尾帧请求固定传 `--generate-type 1`,并按首帧、尾帧顺序传入两次 `--image`,不得重排。用户未明确两张图片的角色或缺少任一张时,先询问用户;不要在 skill 侧维护额外的 `generate_type` 枚举 allowlist,其他值原样交给服务端处理。
|
|
84
|
+
6. `generate-video` 返回后,保存 `thread_id`、`run_id`,并立即向用户展示 `web_thread_link`。
|
|
85
|
+
7. 每隔 10 秒调用 `query-result`,直到 `completed=true`。出现 `error_message` 时停止并报告;成功时展示并下载 `videos[].output_path`。
|
|
84
86
|
|
|
85
87
|
```bash
|
|
86
88
|
pippit-tool-cli generate-video --prompt "用户原始描述"
|
|
87
89
|
|
|
90
|
+
pippit-tool-cli generate-video \
|
|
91
|
+
--prompt "用户原始描述" \
|
|
92
|
+
--image FIRST_FRAME_PATH \
|
|
93
|
+
--image LAST_FRAME_PATH \
|
|
94
|
+
--generate-type 1
|
|
95
|
+
|
|
88
96
|
pippit-tool-cli query-result \
|
|
89
97
|
--thread-id THREAD_ID \
|
|
90
98
|
--run-id RUN_ID \
|
|
@@ -95,7 +103,7 @@ pippit-tool-cli query-result \
|
|
|
95
103
|
|
|
96
104
|
### 路由 C:小云雀后端 Agent 编排
|
|
97
105
|
|
|
98
|
-
需要意图确认、脚本/分镜拆解、MV、TVC、局部编辑、复杂参考素材编排,或者用户未明确要求模型直出时,继续使用本技能内置的 `submit_run.py` / `get_thread.py`
|
|
106
|
+
需要意图确认、脚本/分镜拆解、MV、TVC、局部编辑、复杂参考素材编排,或者用户未明确要求模型直出时,继续使用本技能内置的 `submit_run.py` / `get_thread.py` 会话工作流;明确的首尾帧请求除外,必须走路由 B。
|
|
99
107
|
|
|
100
108
|
### 路由 D:短剧工作流
|
|
101
109
|
|
|
@@ -215,7 +223,18 @@ python3 {baseDir}/scripts/download_results.py --urls URL1 URL2 URL3 --output-dir
|
|
|
215
223
|
6. completed=true 后展示并下载 images[].output_path;出现 error_message 时停止并报告
|
|
216
224
|
```
|
|
217
225
|
|
|
218
|
-
### 场景 3
|
|
226
|
+
### 场景 3:用户明确要求视频模型直出(含首尾帧)
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
1. command -v pippit-tool-cli → 确认 CLI 可用
|
|
230
|
+
2. 普通视频模型直出:pippit-tool-cli generate-video --prompt "用户原始描述" [仅添加用户已给出的其他参数]
|
|
231
|
+
3. 首尾帧直出:确认两张图片的首帧/尾帧角色,按顺序执行 generate-video --image FIRST_FRAME_PATH --image LAST_FRAME_PATH --generate-type 1
|
|
232
|
+
4. 拿到 thread_id、run_id 和 web_thread_link,立即展示 web_thread_link
|
|
233
|
+
5. 每隔 10 秒调用 query-result --thread-id THREAD_ID --run-id RUN_ID --download-dir OUTPUT_DIR
|
|
234
|
+
6. completed=true 后展示并下载 videos[].output_path;出现 error_message 时停止并报告
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 场景 4:用户提供图片/视频/音频要求编辑修改或作为参考(如"参考这个视频做一个新的"、"用这首歌做MV")
|
|
219
238
|
|
|
220
239
|
```
|
|
221
240
|
1. upload_file.py /path/to/video.mp4 → 拿到 asset_id1
|
|
@@ -226,7 +245,7 @@ python3 {baseDir}/scripts/download_results.py --urls URL1 URL2 URL3 --output-dir
|
|
|
226
245
|
|
|
227
246
|
用户给了文件路径 + 编辑指令 = 先上传文件,再把编辑指令和 所有asset_id 一起发送。
|
|
228
247
|
|
|
229
|
-
### 场景
|
|
248
|
+
### 场景 5:用户提供参考图/视频/音频要求生成新内容
|
|
230
249
|
|
|
231
250
|
```
|
|
232
251
|
1. upload_file.py /path/to/ref1.png → 拿到 asset_id1
|
|
@@ -237,7 +256,7 @@ python3 {baseDir}/scripts/download_results.py --urls URL1 URL2 URL3 --output-dir
|
|
|
237
256
|
6. 后续同场景 1 的步骤 2-6
|
|
238
257
|
```
|
|
239
258
|
|
|
240
|
-
### 场景
|
|
259
|
+
### 场景 6:在已有会话中追加新需求
|
|
241
260
|
|
|
242
261
|
```
|
|
243
262
|
1. submit_run.py --message "新的描述" --thread-id THREAD_ID → 拿到 thread_id、run_id、web_thread_link
|
|
@@ -316,7 +335,7 @@ python3 {baseDir}/scripts/download_results.py --urls URL1 URL2 URL3 --output-dir
|
|
|
316
335
|
|
|
317
336
|
你(用户侧 Agent)的职责是**搬运工**,不是创作者。会话 API 路由由后端 Agent 负责理解需求、拆解分镜、编排工作流、选模型、写 prompt;图片/视频模型直出路由把用户原始参数传给 CLI。你要做的是:
|
|
318
337
|
|
|
319
|
-
1. **准备素材**:会话 API 路由用 `upload_file.py` 把本地文件转为 asset_id;图片/视频模型直出路由把本地路径直接交给 CLI 的 `--image` / `--video` / `--audio`
|
|
338
|
+
1. **准备素材**:会话 API 路由用 `upload_file.py` 把本地文件转为 asset_id;图片/视频模型直出路由把本地路径直接交给 CLI 的 `--image` / `--video` / `--audio` 参数;首尾帧任务固定传 `--generate-type 1` 并保持首帧、尾帧顺序
|
|
320
339
|
2. **提交任务**:先按“执行路由”判断;图片模型直出调用 `pippit-tool-cli generate-image`,视频模型直出调用 `pippit-tool-cli generate-video`,其余任务把用户的原始描述 + asset_id 原封不动发给 `submit_run.py`
|
|
321
340
|
3. **传话**:根据 `get_thread.py` 返回的消息列表,展示过程中的意图询问、创作信息等
|
|
322
341
|
4. **取件**:会话 API 路由用 `get_thread.py` 轮询,图片/视频模型直出路由用 `query-result` 轮询 → 检查结果 → 下载产物 → 结果展示给用户
|