@pippit-dev/cli 1.0.0 → 1.0.2

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.
@@ -5,7 +5,6 @@ import (
5
5
  "fmt"
6
6
 
7
7
  "github.com/Pippit-dev/pippit-cli/internal/common"
8
- "github.com/Pippit-dev/pippit-cli/internal/config"
9
8
  )
10
9
 
11
10
  // SubmitRunOptions is the stable command-facing request shape for short drama run submission.
@@ -22,21 +21,9 @@ type SubmitRunResult struct {
22
21
  WebThreadLink string `json:"web_thread_link"`
23
22
  }
24
23
 
25
- type submitRunResponse struct {
26
- Ret string `json:"ret"`
27
- Errmsg string `json:"errmsg"`
28
- Data struct {
29
- WebThreadLink string `json:"web_thread_link"`
30
- Run struct {
31
- ThreadID string `json:"thread_id"`
32
- RunID string `json:"run_id"`
33
- } `json:"run"`
34
- } `json:"data"`
35
- }
36
-
37
24
  func SubmitRun(ctx context.Context, opts *SubmitRunOptions, runner *common.Runner) (*SubmitRunResult, error) {
38
25
  if runner == nil || runner.Client == nil {
39
- return nil, fmt.Errorf("submit_run runner client is required")
26
+ return nil, fmt.Errorf("submit_run 运行器客户端缺失")
40
27
  }
41
28
 
42
29
  body := map[string]any{
@@ -50,21 +37,21 @@ func SubmitRun(ctx context.Context, opts *SubmitRunOptions, runner *common.Runne
50
37
  }
51
38
  body["agent_name"] = "pippit_nest_novel_agent"
52
39
 
53
- var resp submitRunResponse
54
- if err := runner.Client.SendRequest(ctx, submitRunPath(runner), body, &resp); err != nil {
55
- return nil, fmt.Errorf("submit_run request failed: %w", err)
40
+ var resp common.SubmitRunResponse
41
+ if err := runner.Client.SendRequest(ctx, common.SubmitRunPath(runner), body, &resp); err != nil {
42
+ return nil, fmt.Errorf("提交 short_drama 请求失败: %w", err)
56
43
  }
57
44
  if resp.Ret != "0" {
58
45
  if resp.Errmsg == "" {
59
- resp.Errmsg = "unknown error"
46
+ resp.Errmsg = "未知错误"
60
47
  }
61
- return nil, fmt.Errorf("submit_run failed: ret=%s errmsg=%s", resp.Ret, resp.Errmsg)
48
+ return nil, fmt.Errorf("short_drama 请求返回失败: ret=%s errmsg=%s", resp.Ret, resp.Errmsg)
62
49
  }
63
50
  if resp.Data.Run.ThreadID == "" {
64
- return nil, fmt.Errorf("submit_run response missing data.run.thread_id")
51
+ return nil, fmt.Errorf("short_drama 响应缺少 data.run.thread_id")
65
52
  }
66
53
  if resp.Data.Run.RunID == "" {
67
- return nil, fmt.Errorf("submit_run response missing data.run.run_id")
54
+ return nil, fmt.Errorf("short_drama 响应缺少 data.run.run_id")
68
55
  }
69
56
  return &SubmitRunResult{
70
57
  ThreadID: resp.Data.Run.ThreadID,
@@ -72,10 +59,3 @@ func SubmitRun(ctx context.Context, opts *SubmitRunOptions, runner *common.Runne
72
59
  WebThreadLink: resp.Data.WebThreadLink,
73
60
  }, nil
74
61
  }
75
-
76
- func submitRunPath(runner *common.Runner) string {
77
- if runner != nil && runner.Config != nil && runner.Config.Paths != nil && runner.Config.Paths.SubmitRun != "" {
78
- return runner.Config.Paths.SubmitRun
79
- }
80
- return config.SubmitRunPath
81
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-tool-cli": "scripts/run.js"
@@ -29,7 +29,7 @@ metadata:
29
29
  4. **获取会话文件** - 根据 `thread_id` 拉取会话文件列表,得到 `file_path`、`download_url`。这和查询会话进展同等重要。
30
30
  5. **下载重要资产** - 使用文件列表中的 `download_url` 下载资源,并按 `file_path` 写入用户本地目标文件路径。
31
31
 
32
- 重要资产包括但不限于:剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物。只要 `+list-thread-file` 返回了这些资产的 `download_url`,就要及时调用下载工具落盘,不要只展示文件元信息。
32
+ 重要资产包括但不限于:剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物。只要 `list-thread-file` 返回了这些资产的 `download_url`,就要及时调用下载工具落盘,不要只展示文件元信息。
33
33
 
34
34
  ## 短剧主流程顺序
35
35
 
@@ -94,7 +94,7 @@ pippit-tool-cli short-drama +submit-run --message "参考这个大纲写第一
94
94
 
95
95
  ```bash
96
96
  # 查询会话可读进展
97
- pippit-tool-cli short-drama +get-thread --thread-id THREAD_ID --run-id RUN_ID
97
+ pippit-tool-cli get-thread --thread-id THREAD_ID --run-id RUN_ID
98
98
  ```
99
99
 
100
100
  > `thread_id` 和 `run_id` 由 `+submit-run` 返回。`run_id` 可省略,省略时返回当前 `thread_id` 下的所有 Run;传入时只看指定 Run。
@@ -121,10 +121,10 @@ pippit-tool-cli short-drama +upload-file --path /path/to/outline.txt
121
121
 
122
122
  ```bash
123
123
  # 获取会话文件列表
124
- pippit-tool-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1 --page-size 200
124
+ pippit-tool-cli list-thread-file --thread-id THREAD_ID --page-num 1 --page-size 200
125
125
  ```
126
126
 
127
- `+list-thread-file` 返回的每个文件对象包含:
127
+ `list-thread-file` 返回的每个文件对象包含:
128
128
 
129
129
  ```json
130
130
  {
@@ -134,16 +134,16 @@ pippit-tool-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1
134
134
  }
135
135
  ```
136
136
 
137
- `+list-thread-file` 只负责获取会话文件列表,不负责下载文件,也不需要判断本地文件是否已存在。
137
+ `list-thread-file` 只负责获取会话文件列表,不负责下载文件,也不需要判断本地文件是否已存在。
138
138
 
139
139
  ### 5. 下载文件资源
140
140
 
141
141
  ```bash
142
142
  # 下载文件资源到指定文件路径
143
- pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PATH --updated-at UPDATED_AT
143
+ pippit-tool-cli download-result --url DOWNLOAD_URL --output-path FILE_PATH --updated-at UPDATED_AT
144
144
  ```
145
145
 
146
- `FILE_PATH` 必须直接使用 `+list-thread-file` 返回的完整 `file_path`,包含文件名,不要取父目录。`UPDATED_AT` 使用同一文件对象返回的 `updated_at`;如果没有 `updated_at`,可省略 `--updated-at`。`+download-result` 负责把会话产生的文件通过 URL 下载到该目标文件路径;如果目标文件已存在且本地修改时间不早于 `updated_at`,跳过下载;如果本地文件早于 `updated_at`,覆盖更新。
146
+ `FILE_PATH` 必须直接使用 `list-thread-file` 返回的完整 `file_path`,包含文件名,不要取父目录。`UPDATED_AT` 使用同一文件对象返回的 `updated_at`;如果没有 `updated_at`,可省略 `--updated-at`。`download-result` 负责把会话产生的文件通过 URL 下载到该目标文件路径;如果目标文件已存在且本地修改时间不早于 `updated_at`,跳过下载;如果本地文件早于 `updated_at`,覆盖更新。
147
147
 
148
148
  ## 典型工作流
149
149
 
@@ -154,8 +154,8 @@ pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FI
154
154
  → 拿到 thread_id、run_id 和 web_thread_link
155
155
  2. 立即将 web_thread_link 展示给用户
156
156
  3. 并行发起,二者同等重要:
157
- a. pippit-tool-cli short-drama +get-thread --thread-id THREAD_ID --run-id RUN_ID
158
- b. pippit-tool-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num PAGE_NUM --page-size 200
157
+ a. pippit-tool-cli get-thread --thread-id THREAD_ID --run-id RUN_ID
158
+ b. pippit-tool-cli list-thread-file --thread-id THREAD_ID --page-num PAGE_NUM --page-size 200
159
159
  4. 检查 `get-thread` 返回的 readable_text:
160
160
  - 如果任务仍在进行中:展示可读进展,继续查询
161
161
  - 如果后端 Agent 提出问题:从 readable_text 中提取问题并展示,等待用户回复
@@ -163,9 +163,9 @@ pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FI
163
163
  - 对每个文件取 file_path、download_url、updated_at
164
164
  - 将 file_path 作为本地目标文件路径,包含文件名
165
165
  - 有 download_url 的重要资产:加入本轮下载队列
166
- - 不判断 file_path 在本地是否已存在,是否跳过由 +download-result 内部处理
166
+ - 不判断 file_path 在本地是否已存在,是否跳过由 download-result 内部处理
167
167
  - 如果本轮 total 达到 200:下一轮将 PAGE_NUM 加 1,继续查询新一页文件
168
- 6. 对重要资产,立即调用 +download-result 并行下载资源:
168
+ 6. 对重要资产,立即调用 download-result 并行下载资源:
169
169
  - 使用第 5 步获取的 download_url 作为 --url
170
170
  - 使用第 5 步获取的完整 file_path 作为 --output-path
171
171
  - 如果第 5 步返回 updated_at,作为 --updated-at 传入
@@ -199,23 +199,23 @@ pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FI
199
199
  ## 轮询策略
200
200
 
201
201
  - **间隔**:每 10 秒查询一次。
202
- - **进展查询**:每轮调用 `+get-thread` 查看 `readable_text`。优先带上本轮 `run_id` 聚焦当前任务;需要查看整个会话时可省略 `--run-id`。
203
- - **并行查询**:每次 `+submit-run` 返回 `thread_id` 后,同时发起 `+get-thread` 和 `+list-thread-file`;二者同等重要,不能只查询会话进展而忽略会话文件。
204
- - **文件分页**:`+list-thread-file` 使用 `--page-size 200`。如果本轮返回的 `total` 达到 200,下一轮使用 `--page-num` 加 1 查询新一页结果;如果未达到 200,保持当前页继续轮询新增产物。
205
- - **重要资产识别**:每轮都检查 `+list-thread-file` 返回的文件。剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物都是重要资产。
206
- - **文件下载**:解析 `+list-thread-file` 的结果后,对带 `download_url` 的重要资产立即调用 `+download-result` 下载资源;不要在 `list-thread-file` 阶段检查文件是否已存在,存在性检查由下载工具内部处理。
202
+ - **进展查询**:每轮调用 `get-thread` 查看 `readable_text`。优先带上本轮 `run_id` 聚焦当前任务;需要查看整个会话时可省略 `--run-id`。
203
+ - **并行查询**:每次 `+submit-run` 返回 `thread_id` 后,同时发起 `get-thread` 和 `list-thread-file`;二者同等重要,不能只查询会话进展而忽略会话文件。
204
+ - **文件分页**:`list-thread-file` 使用 `--page-size 200`。如果本轮返回的 `total` 达到 200,下一轮使用 `--page-num` 加 1 查询新一页结果;如果未达到 200,保持当前页继续轮询新增产物。
205
+ - **重要资产识别**:每轮都检查 `list-thread-file` 返回的文件。剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物都是重要资产。
206
+ - **文件下载**:解析 `list-thread-file` 的结果后,对带 `download_url` 的重要资产立即调用 `download-result` 下载资源;不要在 `list-thread-file` 阶段检查文件是否已存在,存在性检查由下载工具内部处理。
207
207
  - **下载完成标准**:不要把文件元信息展示当成下载完成;必须拿到本地 `file_path`,或明确记录该文件在重试后仍下载失败。
208
208
  - **用户确认**:如果消息中出现需要用户确认、补充设定或回答问题的内容,先判断是否包含表单、问卷、选项或按钮;包含时按“短剧主流程顺序”和“表单与问卷选项处理原则”清洗选项,再展示给用户并等待回复。
209
209
  - **超时**:如果长时间无结果,告知用户任务仍在生成中,可稍后通过 `web_thread_link` 查看。
210
- - **错误处理**:`+get-thread`、`+list-thread-file` 或 `+download-result` 任一调用失败时,记录失败原因和参数,在后续轮询中主动重试;重试期间继续处理其他成功返回的消息和文件。连续多轮失败后再向用户说明仍未完成的查询或下载项。
210
+ - **错误处理**:`get-thread`、`list-thread-file` 或 `download-result` 任一调用失败时,记录失败原因和参数,在后续轮询中主动重试;重试期间继续处理其他成功返回的消息和文件。连续多轮失败后再向用户说明仍未完成的查询或下载项。
211
211
 
212
212
  ## 完成标准
213
213
 
214
- 一次短剧任务不能只以 `+get-thread` 返回的 `readable_text` 作为结束条件。完成前必须同时检查:
214
+ 一次短剧任务不能只以 `get-thread` 返回的 `readable_text` 作为结束条件。完成前必须同时检查:
215
215
 
216
- 1. 已处理 `+get-thread` 返回的最新 `readable_text`、用户确认问题和最终消息。
217
- 2. 已用 `--page-size 200` 调用 `+list-thread-file` 获取会话文件列表;如果本轮 `total` 达到 200,已在后续轮询中递增 `page-num` 查询新一页。
218
- 3. 对所有带 `download_url` 的重要资产,已调用 `+download-result` 下载到本地 `file_path`。
216
+ 1. 已处理 `get-thread` 返回的最新 `readable_text`、用户确认问题和最终消息。
217
+ 2. 已用 `--page-size 200` 调用 `list-thread-file` 获取会话文件列表;如果本轮 `total` 达到 200,已在后续轮询中递增 `page-num` 查询新一页。
218
+ 3. 对所有带 `download_url` 的重要资产,已调用 `download-result` 下载到本地 `file_path`。
219
219
  4. 已按短剧主流程顺序检查服务端表单、问卷和选项,没有把跳过必要阶段的选项直接呈现给用户;如果跳过 `剧本标准化`,已明确这是可选阶段。
220
220
  5. 对查询失败或下载失败的资产,已在后续轮询中主动重试,并在最终回复中列出仍失败的文件或命令。
221
221
 
@@ -231,7 +231,7 @@ pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FI
231
231
  }
232
232
  ```
233
233
 
234
- **+get-thread** 返回:
234
+ **get-thread** 返回:
235
235
 
236
236
  ```text
237
237
  Thread: thread_...
@@ -242,7 +242,7 @@ Thread: thread_...
242
242
  [assistant] ...
243
243
  ```
244
244
 
245
- **+upload-file** 返回:
245
+ **short-drama +upload-file** 返回:
246
246
 
247
247
  ```json
248
248
  {
@@ -252,7 +252,7 @@ Thread: thread_...
252
252
 
253
253
  `+upload-file` 通过 `multipart/form-data` 上传文件,表单文件字段名为 `file`。本地文件必须存在、不能是目录,后缀必须是 `.doc`、`.docx` 或 `.txt`;不支持的后缀会直接报错。返回的 `asset_id` 来自服务端 `pippit_asset_id`,如果没有该字段才回退到 `asset_id`。
254
254
 
255
- **+list-thread-file** 返回:
255
+ **list-thread-file** 返回:
256
256
 
257
257
  ```json
258
258
  {
@@ -270,7 +270,7 @@ Thread: thread_...
270
270
 
271
271
  当 `total` 达到 200 时,`message` 会用 `<system-remind>` 提示下一轮将 `page-num` 加 1 查询新一页。
272
272
 
273
- **+download-result** 返回:
273
+ **download-result** 返回:
274
274
 
275
275
  ```json
276
276
  {
@@ -281,16 +281,16 @@ Thread: thread_...
281
281
 
282
282
  ## 会话文件与资源下载
283
283
 
284
- 先用 `+list-thread-file` 获取会话文件列表,再用 `+download-result` 并行下载重要资产。获取文件元信息不是最终目标,重要资产落盘才是核心目标。文件是否已存在由下载工具内部检查,`+list-thread-file` 阶段不要做本地存在性判断。
284
+ 先用 `list-thread-file` 获取会话文件列表,再用 `download-result` 并行下载重要资产。获取文件元信息不是最终目标,重要资产落盘才是核心目标。文件是否已存在由下载工具内部检查,`list-thread-file` 阶段不要做本地存在性判断。
285
285
 
286
286
  ### 获取会话文件
287
287
 
288
- `+list-thread-file` 的 `files` 中逐个读取文件元信息:`file_path`、`file_name`、`download_url`、`updated_at`。重点识别剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物等重要资产。
288
+ `list-thread-file` 的 `files` 中逐个读取文件元信息:`file_path`、`file_name`、`download_url`、`updated_at`。重点识别剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物等重要资产。
289
289
 
290
290
  ```
291
291
  1. 有download_url的重要资产
292
292
  → 记录该file_path、URL和updated_at
293
- → 使用 +download-result 将URL资源下载到该file_path;有updated_at时传入--updated-at
293
+ → 使用 download-result 将URL资源下载到该file_path;有updated_at时传入--updated-at
294
294
  2. 本轮total达到200
295
295
  → 下一轮page-num加1,继续查询新一页结果
296
296
  3. 本轮total未达到200
@@ -304,7 +304,7 @@ Thread: thread_...
304
304
 
305
305
  对带 `download_url` 的重要资产调用下载工具,可并行。重要资产必须主动下载,不要等用户再次要求,也不要在调用下载工具前先检查本地文件是否存在。
306
306
 
307
- 1. 调用 `pippit-tool-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PATH --updated-at UPDATED_AT`;如果文件对象没有 `updated_at`,省略 `--updated-at`。
307
+ 1. 调用 `pippit-tool-cli download-result --url DOWNLOAD_URL --output-path FILE_PATH --updated-at UPDATED_AT`;如果文件对象没有 `updated_at`,省略 `--updated-at`。
308
308
  2. 下载完成后,向用户展示本地文件路径;如果某个文件下载失败,记录失败项并在后续轮询中重试,不阻塞已成功落盘的文件展示。
309
309
 
310
310
  ## 向用户展示内容
@@ -325,7 +325,7 @@ Thread: thread_...
325
325
 
326
326
  1. **上传**:如果用户给了本地 `.doc` / `.docx` / `.txt` 参考文件,先调用 `+upload-file`。
327
327
  2. **提交任务**:首次创作时把用户原始短剧需求和唯一剧本 `asset_id` 通过 `+submit-run --asset-ids` 发给后端;同一 `thread_id` 后续续写或修改不再追加新的剧本文件。
328
- 3. **传话、取文件、下载资源**:根据 `+get-thread` 返回的 `readable_text` 展示进展、问题和结果;遇到表单、问卷、选项或按钮时,只做流程合理性清洗,不替用户决定创作内容;根据 `+list-thread-file` 获取文件列表;再根据 `download_url` 调用 `+download-result` 把缺失资源下载到用户本地。
328
+ 3. **传话、取文件、下载资源**:根据 `get-thread` 返回的 `readable_text` 展示进展、问题和结果;遇到表单、问卷、选项或按钮时,只做流程合理性清洗,不替用户决定创作内容;根据 `list-thread-file` 获取文件列表;再根据 `download_url` 调用 `download-result` 把缺失资源下载到用户本地。
329
329
 
330
330
  **不要做的事:**
331
331
 
@@ -340,10 +340,10 @@ Thread: thread_...
340
340
 
341
341
  - `--message` 是用户的原始短剧需求,不能为空。
342
342
  - 查询进展时优先使用 `+submit-run` 返回的 `thread_id` 和 `run_id`;如果需要查看整个会话,可以省略 `--run-id`。
343
- - `+get-thread` 当前固定走服务端 v2 响应,输出字段是 `readable_text`;不要解析旧版 `messages` 数组。
343
+ - `get-thread` 当前固定走服务端 v2 响应,输出字段是 `readable_text`;不要解析旧版 `messages` 数组。
344
344
  - `+upload-file` 当前用于短剧场景文件上传链路,只支持本地 `.doc` / `.docx` / `.txt` 文件;`--path` 不能为空,路径必须指向真实文件,不能是目录。
345
345
  - `+upload-file` 上传成功后只返回 `asset_id`;把该值原样作为 `+submit-run --asset-ids` 的参数。
346
346
  - 单次创作会话中(相同 `thread_id`),`+submit-run` 只支持绑定一个剧本文件。不要在同一 `thread_id` 下重复上传并追加第二个剧本 `asset_id`;用户给多个剧本时,先让用户选择一个,或分别开启新的创作会话。
347
- - `+list-thread-file` 只需要 `thread_id`;分页参数使用 `--page-num 1 --page-size 200` 起步,`total` 达到 200 时下一轮递增 `page-num`。
348
- - `+list-thread-file` 和 `+download-result` 是两个不同的 CLI 指令:前者获取会话文件元信息,后者下载 URL 资源并写入到本地目标文件路径。
349
- - `+download-result` 接收 `--url`、`--output-path`、`--updated-at`、`--workers`;`--output-path` 必须是包含文件名的目标文件路径。
347
+ - `list-thread-file` 只需要 `thread_id`;分页参数使用 `--page-num 1 --page-size 200` 起步,`total` 达到 200 时下一轮递增 `page-num`。
348
+ - `list-thread-file` 和 `download-result` 是两个不同的 CLI 指令:前者获取会话文件元信息,后者下载 URL 资源并写入到本地目标文件路径。
349
+ - `download-result` 接收 `--url`、`--output-path`、`--updated-at`、`--workers`;`--output-path` 必须是包含文件名的目标文件路径。
@@ -11,6 +11,8 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
11
11
 
12
12
  sys.path.insert(0, os.path.dirname(__file__))
13
13
 
14
+ HTTP_TIMEOUT_SECONDS = 30 * 60
15
+
14
16
 
15
17
  def download_file(url, filepath):
16
18
  """下载单个文件"""
@@ -18,7 +20,7 @@ def download_file(url, filepath):
18
20
  req = urllib.request.Request(url, headers={"User-Agent": "XYQ-Nest-Skill/1.0"})
19
21
  tmp_path = filepath + ".tmp"
20
22
  try:
21
- with urllib.request.urlopen(req, timeout=600) as resp:
23
+ with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_SECONDS) as resp:
22
24
  with open(tmp_path, "wb") as f:
23
25
  shutil.copyfileobj(resp, f, length=1024 * 1024)
24
26
  os.replace(tmp_path, filepath)
@@ -11,7 +11,7 @@ import urllib.request
11
11
  import urllib.error
12
12
 
13
13
  sys.path.insert(0, os.path.dirname(__file__))
14
- from xyq_common import XYQ_BASE, ACCESS_KEY, UPLOAD_FILE_PATH, parse_response
14
+ from xyq_common import XYQ_BASE, ACCESS_KEY, UPLOAD_FILE_PATH, HTTP_TIMEOUT_SECONDS, parse_response
15
15
 
16
16
  # 允许的 MIME 类型前缀
17
17
  ALLOWED_PREFIXES = ("image/", "video/")
@@ -70,7 +70,7 @@ def upload_file(file_path: str) -> dict:
70
70
  },
71
71
  )
72
72
  try:
73
- with urllib.request.urlopen(req, timeout=120) as resp:
73
+ with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_SECONDS) as resp:
74
74
  result = json.loads(resp.read().decode("utf-8"))
75
75
  return parse_response(result)
76
76
  except urllib.error.HTTPError as e:
@@ -13,6 +13,7 @@ ACCESS_KEY = os.environ.get("XYQ_ACCESS_KEY", "")
13
13
  SUBMIT_RUN_PATH = "/api/biz/v1/skill/submit_run"
14
14
  GET_THREAD_PATH = "/api/biz/v1/skill/get_thread"
15
15
  UPLOAD_FILE_PATH = "/api/biz/v1/skill/upload_file"
16
+ HTTP_TIMEOUT_SECONDS = 30 * 60
16
17
 
17
18
  if not ACCESS_KEY:
18
19
  print("错误:请设置 XYQ_ACCESS_KEY 环境变量", file=sys.stderr)
@@ -22,7 +23,7 @@ if not ACCESS_KEY:
22
23
  def _headers():
23
24
  return {
24
25
  "Authorization": f"Bearer {ACCESS_KEY}",
25
- "Content-Type": "application/json"
26
+ "Content-Type": "application/json",
26
27
  }
27
28
 
28
29
 
@@ -37,7 +38,7 @@ def api_post(path: str, body: dict) -> dict:
37
38
  headers=_headers(),
38
39
  )
39
40
  try:
40
- with urllib.request.urlopen(req, timeout=30) as resp:
41
+ with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_SECONDS) as resp:
41
42
  return json.loads(resp.read().decode("utf-8"))
42
43
  except urllib.error.HTTPError as e:
43
44
  err_body = e.read().decode("utf-8") if e.fp else ""
@@ -53,7 +54,7 @@ def api_get(path: str) -> dict:
53
54
  url = f"{XYQ_BASE.rstrip('/')}{path}"
54
55
  req = urllib.request.Request(url, method="GET", headers=_headers())
55
56
  try:
56
- with urllib.request.urlopen(req, timeout=30) as resp:
57
+ with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_SECONDS) as resp:
57
58
  return json.loads(resp.read().decode("utf-8"))
58
59
  except urllib.error.HTTPError as e:
59
60
  err_body = e.read().decode("utf-8") if e.fp else ""