|
| 1 | +# API プレビュー機能 実装計画 |
| 2 | + |
| 3 | +## 1. 目的 |
| 4 | + |
| 5 | +`api/` ディレクトリ内の FastAPI アプリケーションにおいて、`demo_gradio.py` と同様の動画生成中のプレビュー機能を実装する。これにより、API クライアントは生成プロセスの途中経過をリアルタイムで確認できるようになる。 |
| 6 | + |
| 7 | +## 2. 現状分析と課題 |
| 8 | + |
| 9 | +* **`demo_gradio.py` のプレビュー:** |
| 10 | + * `worker` 内の `sample_hunyuan` コールバックで `vae_decode_fake` を使用し、プレビュー画像を生成。 |
| 11 | + * `AsyncStream` を介して Gradio UI にプレビュー画像を送信。 |
| 12 | +* **`api/worker.py` の現状:** |
| 13 | + * `sample_hunyuan` のコールバックは進捗テキスト更新とキャンセルチェックのみ。プレビュー生成・送信は未実装。 |
| 14 | + * `worker` からクライアントへのリアルタイムデータ送信手段が直接はない。 |
| 15 | +* **API での実現課題:** |
| 16 | + * `api/worker.py` のコールバックでプレビュー画像を生成する必要がある。 |
| 17 | + * 生成したプレビュー画像を API クライアントにリアルタイムで送信する仕組みが必要。 |
| 18 | + * `vae_decode_fake` 関数の利用可否確認 (→ 確認済み、利用可能)。 |
| 19 | + |
| 20 | +## 3. 計画 |
| 21 | + |
| 22 | +### 3.1. 情報収集 (完了) |
| 23 | + |
| 24 | +* `diffusers_helper/hunyuan.py` を確認し、`vae_decode_fake` 関数が存在することを確認した。 |
| 25 | + |
| 26 | +### 3.2. API 設計 |
| 27 | + |
| 28 | +* 既存の Server-Sent Events (SSE) エンドポイント `/stream/status/{job_id}` (`api.py` 内) を拡張し、プレビュー画像データも送信するようにする。 |
| 29 | +* SSE イベントのデータ構造に、オプションとして Base64 エンコードされたプレビュー画像 (`preview_image_base64`) を追加する。 |
| 30 | + |
| 31 | + ```json |
| 32 | + { |
| 33 | + "job_id": "...", |
| 34 | + "status": "processing", |
| 35 | + "progress": 25.5, |
| 36 | + "progress_step": 5, |
| 37 | + "progress_total": 20, |
| 38 | + "progress_info": "Sampling...", |
| 39 | + "preview_image_base64": "data:image/jpeg;base64,..." // Optional |
| 40 | + } |
| 41 | + ``` |
| 42 | + |
| 43 | +### 3.3. 実装方針 |
| 44 | + |
| 45 | +* **`api/worker.py` の修正:** |
| 46 | + * `callback` 関数内で、`sample_hunyuan` から渡される中間潜在変数 (`d['denoised']`) を取得する。 |
| 47 | + * `vae_decode_fake` を使用してプレビュー画像を生成する。 |
| 48 | + * 生成した画像を JPEG 形式にエンコードし、Base64 文字列に変換する (`data:image/jpeg;base64,...` 形式)。 |
| 49 | + * 変換した Base64 文字列を `queue_manager` の新しい関数 (例: `update_current_preview`) を呼び出してメモリ上のストアに一時保存する。 |
| 50 | +* **`api/queue_manager.py` の修正:** |
| 51 | + * プレビュー情報 (Base64 文字列) を一時的に保持するためのグローバルな辞書 (例: `current_previews = {}`) を追加する。 |
| 52 | + * `worker.py` からプレビュー情報を受け取り、`current_previews` を更新する関数 (例: `update_current_preview(job_id, preview_base64)`) を追加する。 |
| 53 | + * SSE ハンドラからプレビュー情報を取得する関数 (例: `get_current_preview(job_id)`) を追加する。 |
| 54 | + * ジョブ完了時または失敗時に `current_previews` から該当ジョブのエントリを削除する処理を追加する (例: `clear_current_preview(job_id)`)。 |
| 55 | + * **注意:** このプレビュー情報は揮発性であり、JSON キューファイル (`job_queue.json`) には保存しない。 |
| 56 | +* **`api/api.py` の修正:** |
| 57 | + * `/stream/status/{job_id}` の SSE `event_generator` 関数を修正する。 |
| 58 | + * ジョブが `processing` 状態の場合、`queue_manager` の新しい関数 (例: `get_current_preview`) を呼び出して最新のプレビュー画像 Base64 文字列を取得する。 |
| 59 | + * 取得した Base64 文字列を SSE イベントデータの `preview_image_base64` フィールドに含めてクライアントに送信する。 |
| 60 | + |
| 61 | +### 3.4. 処理フロー (Mermaid図) |
| 62 | + |
| 63 | +```mermaid |
| 64 | +sequenceDiagram |
| 65 | + participant Client |
| 66 | + participant FastAPI (api.py) |
| 67 | + participant QueueManager (queue_manager.py) |
| 68 | + participant Worker (worker.py) |
| 69 | + participant Models (models.py / diffusers_helper) |
| 70 | + |
| 71 | + Client->>FastAPI: POST /generate (画像, プロンプト) |
| 72 | + FastAPI->>QueueManager: add_to_queue() |
| 73 | + QueueManager-->>FastAPI: job_id |
| 74 | + FastAPI-->>Client: {job_id: ...} |
| 75 | + |
| 76 | + Client->>FastAPI: GET /stream/status/{job_id} (SSE接続) |
| 77 | + FastAPI->>FastAPI: event_generator() 開始 |
| 78 | + |
| 79 | + loop Worker Thread |
| 80 | + Worker->>QueueManager: get_next_job() |
| 81 | + QueueManager-->>Worker: job (or None) |
| 82 | + opt job is not None |
| 83 | + Worker->>QueueManager: update_job_status(job_id, "processing") # ファイル更新 |
| 84 | + Worker->>Models: モデルロード/準備 ... |
| 85 | + Worker->>Models: sample_hunyuan(..., callback=callback_func) |
| 86 | + loop Sampling Steps |
| 87 | + Models->>Worker: callback_func(d) 呼び出し |
| 88 | + Worker->>Models: vae_decode_fake(d['denoised']) # プレビュー生成 |
| 89 | + Models-->>Worker: preview_image_tensor |
| 90 | + Worker->>Worker: 画像をJPEG Base64に変換 |
| 91 | + Worker->>QueueManager: update_current_preview(job_id, preview_base64) # メモリ更新 |
| 92 | + Worker->>QueueManager: update_job_progress(...) # ファイル更新 (進捗のみ) |
| 93 | + end |
| 94 | + Models-->>Worker: generated_latents |
| 95 | + Worker->>Models: vae_decode() # 最終デコード |
| 96 | + Models-->>Worker: final_pixels |
| 97 | + Worker->>Worker: save_bcthw_as_mp4() |
| 98 | + Worker->>QueueManager: update_job_status(job_id, "completed") # ファイル更新 |
| 99 | + Worker->>QueueManager: clear_current_preview(job_id) # メモリクリア |
| 100 | + end |
| 101 | + end |
| 102 | + |
| 103 | + loop SSE Connection (event_generator) |
| 104 | + FastAPI->>QueueManager: get_job_by_id(job_id) (ファイルから進捗取得) |
| 105 | + alt job is processing |
| 106 | + FastAPI->>QueueManager: get_current_preview(job_id) (メモリからプレビュー取得) |
| 107 | + QueueManager-->>FastAPI: preview_base64 (or None) |
| 108 | + end |
| 109 | + FastAPI->>Client: event: progress, data: {..., preview_image_base64: ...} # 進捗とプレビュー送信 |
| 110 | + alt job is terminal |
| 111 | + FastAPI->>Client: event: status, data: {...} # 最終ステータス送信 |
| 112 | + break |
| 113 | + end |
| 114 | + FastAPI->>FastAPI: asyncio.sleep(1) |
| 115 | + end |
| 116 | +``` |
| 117 | + |
| 118 | +## 4. 懸念点 |
| 119 | + |
| 120 | +* **データ受け渡し:** `worker` スレッドと SSE ハンドラ (FastAPI の非同期コンテキスト) 間でのプレビューデータ受け渡し (`queue_manager` のメモリ上の辞書) が、スレッドセーフティやパフォーマンスの観点から問題ないか。高頻度更新時の競合やメモリ使用量に注意が必要。 |
| 121 | +* **パフォーマンス:** プレビュー画像の生成 (`vae_decode_fake`)、JPEG エンコード、Base64 エンコードが `worker` のコールバック内で実行されるため、全体の生成時間に影響を与える可能性がある。 |
| 122 | + |
| 123 | +## 5. 次のステップ |
| 124 | + |
| 125 | +* この計画に基づき、`code` モードに切り替えて実装を開始する。 |
0 commit comments