free-short-video 7.0.3 → 7.0.4

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
@@ -1,26 +1,25 @@
1
1
  ---
2
2
 
3
- # What's New in v7.0.3
3
+ # What's New in v7.0.4
4
4
 
5
5
  ## What's New
6
6
 
7
7
  ### Features & Improvements
8
8
 
9
- * **Survive Agnes queue saturation.** When Agnes rejects submissions with `503 video_queue_full` (the free Video 2.5 Flash queue can stay full for 10+ minutes), the app now retries on a dedicated track — every 30–60s for up to 900s (`AGNES_VIDEO_QUEUE_RETRY_SECONDS`) — without consuming the normal retry budget, so you no longer get a premature "generation failed" while you were simply never queued. Nothing is consumed by a rejected submit. The progress panel shows it live: `Agnes 视频队列已满(HTTP 503 · video_queue_full),正在排队重试(第 N 次 / 已等 X 分钟)。建议错峰重试或稍后再试。`
10
- * **Honest error messages.** Upstream failures now surface the real reason instead of a generic `HTTP 503: server error` or a raw dict dump: the submitted error's `code` and `message` are kept end-to-end (e.g. `ComfyUI internal error: inference not finished after 15 minutes (code=500)`), so "queue full" and "queued but stuck" are no longer indistinguishable. `error_logs/` stores readable text plus the upstream code.
11
- * **Queue notices in your language (all 22 locales).** Queue-full progress and failure notices are now rendered by the frontend from structured i18n keys, so Japanese, Arabic, Russian, etc. users see their own language instead of a Chinese fallback. The raw upstream error (HTTP status, `video_queue_full`) is quoted untranslated, as it comes from the API.
12
- * **Config panels start collapsed.** All four configuration cards (provider management, model selection, workspace, privacy) now start collapsed instead of expanded; your manual expand/collapse preference is still remembered per browser.
13
- * **Know when a model is not adapted.** Video models that appear in the upstream model list but have no capability entry in your installed version are now marked with `⚠` plus an explicit notice, instead of being silently submitted with the old v2.0 pixel protocol. The app version is shown in the page footer and returned by `GET /api/models`.
9
+ * **Upstream API error trends.** Errors that carry an HTTP status code are now aggregated per task — by status code, model type (chat / image / video) and API method — and returned by the task detail API. The frontend reports them anonymously as `api_error` events, and task-failure / creation-failure events now carry the extracted status code as well, so recurring upstream failures (for example `503 video_queue_full`) can be tracked as a trend over time instead of being seen as unrelated single errors. The aggregation lives in memory for the lifetime of the task and is cleared with it; `error_logs/` remains the offline source of truth.
10
+ * **Honest "not ready yet" state while polling.** A transient `404` while waiting for a submitted video is now an expected intermediate state rather than an error. It no longer consumes the consecutive-failure budget and no longer gets flushed into `error_logs`, so the failure panel and the logs stop reporting failures for tasks that were in fact still queued upstream.
11
+
12
+ ### Refactoring & Optimizations
13
+
14
+ * Regression coverage for the upstream error-handling path: consecutive `404`s followed by a successful completion, and a permanent `404` that correctly falls back to the overall polling timeout.
14
15
 
15
16
  ### Bug Fixes
16
17
 
17
- * **Switching the text model provider back to Agnes now works.** Previously the switch only changed the UI: the backend kept the previously selected third-party provider and kept calling its endpoint (an empty value was dropped by the form layer and treated as "no change"). The selection is now sent explicitly, and it persists when you click **Save**.
18
- * **Frontend no longer breaks after an update.** The app entry page is now served with `Cache-Control: no-cache`, so browsers can no longer keep a stale entry page that references deleted build assets — the "task list (or the whole app) silently stopped working after pulling a new version" failure mode is gone. After upgrading, do one hard refresh (`Cmd/Ctrl+Shift+R`); afterwards updates apply on normal reload.
19
- * Fixed the queue-full messaging to name Agnes, quote the raw upstream error, and include an actionable suggestion (retry off-peak), instead of the vague "upstream video queue is full".
18
+ * Fixed video tasks failing with a spurious error when the upstream gateway had not made the task record visible yet — a state observed for 420s and even 1100s+ under peak load. The overall polling timeout stays as the final backstop, and it keeps the `video_id` so the task can be resumed rather than reported as a server-confirmed failure.
20
19
 
21
20
  ---
22
21
 
23
- No action is required after upgrading. If your tasks sit at 0% for a long time, that is the Agnes free queue — the app now retries and tells you so; off-peak retries or switching to Video 2.0 (separate queue) remain the practical workarounds.
22
+ No action is required after upgrading. If a task still fails, the failure panel now shows the real upstream reason with its HTTP status code, and repeated upstream errors of the same kind become visible as a trend.
24
23
 
25
24
  ---
26
25
 
@@ -461,6 +461,12 @@ class AgnesVideoAPI:
461
461
  last_status = ""
462
462
  poll_count = 0
463
463
  consecutive_failures = 0
464
+ # U9(v7.0.4):提交返回的是网关路由键,任务记录入库可见有延迟——
465
+ # 轮询侧 404 "task not found" 是「任务还没准备好」的中间态而非错误
466
+ # (实测上游高峰期可见性延迟可从历史 ~20s 劣化到 10min+),单独计数、
467
+ # 只做周期性 info 日志,不进 error_logs、不消耗连续失败配额;
468
+ # 最终兜底是循环顶部的 max_poll_duration 整体超时。
469
+ not_found_count = 0
464
470
  start_time = asyncio.get_event_loop().time()
465
471
  # 2.5 系列查询需带 model_name(text 模式可省略,但带上更通用)
466
472
  model_param = f"&model_name={self.model}" if is_v25_video_model(self.model) else ""
@@ -511,6 +517,18 @@ class AgnesVideoAPI:
511
517
  logger.warning("[KeyRotation] HTTP 429 on poll, 换 Key 立即重试")
512
518
  continue
513
519
  break
520
+ # U9:404(task not found)= 记录尚未在网关任务库可见,中间态
521
+ if resp.status_code == 404:
522
+ not_found_count += 1
523
+ poll_count += 1 # 计入自适应间隔与周期日志的推进
524
+ if not_found_count == 1 or not_found_count % 10 == 0:
525
+ logger.info(
526
+ f"[AgnesVideo] Video {video_id[:16]}... not yet visible "
527
+ f"(404 task not found), poll #{not_found_count}, "
528
+ f"elapsed {elapsed:.0f}s"
529
+ )
530
+ await asyncio.sleep(_adaptive_poll_interval(interval, poll_count))
531
+ continue
514
532
  resp.raise_for_status()
515
533
  result = resp.json()
516
534
  status = result.get("status", "")
@@ -33,6 +33,63 @@ _WORKSPACE_ROOT: Optional[Path] = None
33
33
  # 用 contextvar 而非逐层传参,将改动面收敛到 API 模块内部(PRD FR9)。
34
34
  _TASK_ID_CTX: ContextVar[str] = ContextVar("error_collector_task_id", default="")
35
35
 
36
+ # ── GA 埋点:上游接口报错聚合(内存态,按 task_id 分组)──
37
+ # 结构:{task_id: {"{status_code}|{model_type}|{api_method}": {
38
+ # "status_code": int, "model_type": str, "api_method": str,
39
+ # "count": int, "first_ts": iso, "last_ts": iso}}}
40
+ #
41
+ # 设计说明:故意**不写入 task_state.json**——流水线持有自己的 TaskManager
42
+ # 缓存实例,每次 update_state 都会拿缓存快照整份覆盖落盘,error_collector
43
+ # 若单独写盘会被下一次流水线落盘冲掉。改为内存聚合 +
44
+ # GET /api/tasks/{id}(web/routes/task_routes.py)读取时合并下发,
45
+ # 前端轮询到新增量即上报 GA `api_error` 事件(503 趋势分析用)。
46
+ # 进程重启后内存聚合清零属预期:前端埋点本身只覆盖"页面开着"的窗口,
47
+ # 完整离线趋势仍以 error_logs/ 落盘文件为准。
48
+ _TASK_ERROR_AGG: dict = {}
49
+ _MAX_AGG_ENTRIES_PER_TASK = 32 # 按维度聚合后条目数上限,防异常状态码刷爆
50
+
51
+
52
+ def _bump_upstream_error(task_id: str, status_code: int, model_type: str, api_method: str) -> None:
53
+ """累加一次带 HTTP 状态码的上游报错(内存聚合,失败静默不影响主流程)。"""
54
+ try:
55
+ now = datetime.now().isoformat(timespec="seconds")
56
+ per_task = _TASK_ERROR_AGG.setdefault(task_id, {})
57
+ key = f"{status_code}|{model_type}|{api_method}"
58
+ entry = per_task.get(key)
59
+ if entry is None:
60
+ if len(per_task) >= _MAX_AGG_ENTRIES_PER_TASK:
61
+ logger.warning(
62
+ f"[ErrorCollector] Upstream error agg cap "
63
+ f"({_MAX_AGG_ENTRIES_PER_TASK}) reached for task {task_id}, dropping"
64
+ )
65
+ return
66
+ per_task[key] = {
67
+ "status_code": status_code,
68
+ "model_type": model_type,
69
+ "api_method": api_method,
70
+ "count": 1,
71
+ "first_ts": now,
72
+ "last_ts": now,
73
+ }
74
+ else:
75
+ entry["count"] += 1
76
+ entry["last_ts"] = now
77
+ except Exception as e:
78
+ logger.debug(f"[ErrorCollector] Failed to bump upstream error agg: {e}")
79
+
80
+
81
+ def get_task_upstream_errors(task_id: str) -> list:
82
+ """返回某任务聚合后的上游报错列表(按首次发生时间排序,供任务详情端点合并下发)。"""
83
+ per_task = _TASK_ERROR_AGG.get(task_id or "")
84
+ if not per_task:
85
+ return []
86
+ return sorted(per_task.values(), key=lambda e: e.get("first_ts", ""))
87
+
88
+
89
+ def clear_task_upstream_errors(task_id: str) -> None:
90
+ """任务删除时清理对应聚合(防内存缓慢增长)。"""
91
+ _TASK_ERROR_AGG.pop(task_id or "", None)
92
+
36
93
 
37
94
  def set_workspace_root(path: str) -> None:
38
95
  """设置错误日志存储的根目录(通常为激活的工作空间路径)。
@@ -198,6 +255,12 @@ def collect_error(
198
255
  with open(filepath, "w", encoding="utf-8") as f:
199
256
  json.dump(error_data, f, ensure_ascii=False, indent=2)
200
257
 
258
+ # GA 埋点:带 HTTP 状态码的报错同步累加进任务级内存聚合
259
+ if status_code and error_data["task_id"]:
260
+ _bump_upstream_error(
261
+ error_data["task_id"], status_code, model_type, api_method
262
+ )
263
+
201
264
  # 优化路线图 1.5b:按数量轮转,防止 error_logs 无限膨胀
202
265
  _rotate_error_logs(log_dir)
203
266
 
package/core/config.py CHANGED
@@ -18,7 +18,7 @@ CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
18
18
  # ═══════════════════════════════════════════════════
19
19
  # 应用版本号(v6.1 新增:发版时同步更新,见 docs/dev/release_process.md)
20
20
  # ═══════════════════════════════════════════════════
21
- APP_VERSION = "7.0.3"
21
+ APP_VERSION = "7.0.4"
22
22
 
23
23
  # 未配置 API Key 时的统一报错文案(含免费获取与在线体验兜底,全站路由共用)
24
24
  #
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-short-video",
3
- "version": "7.0.3",
3
+ "version": "7.0.4",
4
4
  "description": "Free AI short-video generator — run the full free-short-video service locally with one command. Text-to-video, image generation, Edge-TTS narration, subtitles, and compositing, all free. Official site: https://video.lichuanyang.top",
5
5
  "bin": {
6
6
  "free-short-video": "bin/cli.js",