dippoo Claude Opus 4.5 commited on
Commit
7b5f7c4
·
1 Parent(s): 4b21f3a

Add video model selector with cloud/pod backend support

Browse files

- Added video model dropdown for img2video mode (WAN I2V, Kling, Veo, Seedance, Sora)
- Added backend selector for video generation (Cloud API vs RunPod)
- Added cloud video generation endpoint using WaveSpeed API
- Added more text-to-image models (FLUX Pro, WAN 2.6, Dreamina, Qwen)
- Added more video models (Kling O1/O3/Pro, Veo 3/3.1, Sora 2, Seedance)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

src/content_engine/api/routes_video.py CHANGED
@@ -1,8 +1,9 @@
1
- """Video generation routes — WAN 2.2 img2video on RunPod pod."""
2
 
3
  from __future__ import annotations
4
 
5
  import asyncio
 
6
  import logging
7
  import os
8
  import time
@@ -20,6 +21,14 @@ router = APIRouter(prefix="/api/video", tags=["video"])
20
  # Video jobs tracking
21
  _video_jobs: dict[str, dict] = {}
22
 
 
 
 
 
 
 
 
 
23
  # Pod state is shared from routes_pod
24
  def _get_pod_state():
25
  from content_engine.api.routes_pod import _pod_state
@@ -127,6 +136,168 @@ async def generate_video(
127
  raise HTTPException(500, f"Generation failed: {e}")
128
 
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  async def _poll_video_job(job_id: str, prompt_id: str):
131
  """Poll ComfyUI for video job completion."""
132
  import httpx
 
1
+ """Video generation routes — WAN 2.2 img2video on RunPod pod or WaveSpeed cloud."""
2
 
3
  from __future__ import annotations
4
 
5
  import asyncio
6
+ import base64
7
  import logging
8
  import os
9
  import time
 
21
  # Video jobs tracking
22
  _video_jobs: dict[str, dict] = {}
23
 
24
+ # WaveSpeed provider (initialized from main.py)
25
+ _wavespeed_provider = None
26
+
27
+ def init_wavespeed(provider):
28
+ """Initialize WaveSpeed provider for cloud video generation."""
29
+ global _wavespeed_provider
30
+ _wavespeed_provider = provider
31
+
32
  # Pod state is shared from routes_pod
33
  def _get_pod_state():
34
  from content_engine.api.routes_pod import _pod_state
 
136
  raise HTTPException(500, f"Generation failed: {e}")
137
 
138
 
139
+ @router.post("/generate/cloud")
140
+ async def generate_video_cloud(
141
+ image: UploadFile = File(...),
142
+ prompt: str = Form("smooth motion, high quality video"),
143
+ negative_prompt: str = Form(""),
144
+ model: str = Form("wan-2.6-i2v"),
145
+ num_frames: int = Form(81),
146
+ fps: int = Form(24),
147
+ seed: int = Form(-1),
148
+ ):
149
+ """Generate a video using WaveSpeed cloud API (Kling, WAN I2V, Veo, etc)."""
150
+ import random
151
+ import httpx
152
+
153
+ if not _wavespeed_provider:
154
+ raise HTTPException(500, "WaveSpeed API not configured")
155
+
156
+ job_id = str(uuid.uuid4())[:8]
157
+ seed = seed if seed >= 0 else random.randint(0, 2**32 - 1)
158
+
159
+ # Read the image
160
+ image_bytes = await image.read()
161
+ image_b64 = base64.b64encode(image_bytes).decode("utf-8")
162
+
163
+ # Create job entry
164
+ _video_jobs[job_id] = {
165
+ "status": "running",
166
+ "seed": seed,
167
+ "started_at": time.time(),
168
+ "num_frames": num_frames,
169
+ "fps": fps,
170
+ "model": model,
171
+ "backend": "cloud",
172
+ }
173
+
174
+ logger.info("Cloud video generation started: %s (model=%s)", job_id, model)
175
+
176
+ # Start background task for cloud video generation
177
+ asyncio.create_task(_generate_cloud_video(job_id, image_bytes, prompt, negative_prompt, model, seed))
178
+
179
+ return {
180
+ "job_id": job_id,
181
+ "status": "running",
182
+ "seed": seed,
183
+ "model": model,
184
+ "estimated_time": "~30-120 seconds",
185
+ }
186
+
187
+
188
+ async def _generate_cloud_video(
189
+ job_id: str,
190
+ image_bytes: bytes,
191
+ prompt: str,
192
+ negative_prompt: str,
193
+ model: str,
194
+ seed: int,
195
+ ):
196
+ """Background task to generate video via WaveSpeed cloud API."""
197
+ import httpx
198
+ import aiohttp
199
+
200
+ try:
201
+ # Upload image to temporary hosting (WaveSpeed needs URL)
202
+ image_url = await _wavespeed_provider._upload_temp_image(image_bytes)
203
+
204
+ # Resolve model to WaveSpeed model ID
205
+ from content_engine.services.cloud_providers.wavespeed_provider import VIDEO_MODEL_MAP
206
+ wavespeed_model = VIDEO_MODEL_MAP.get(model, VIDEO_MODEL_MAP.get("default", "alibaba/wan-2.6-i2v-720p"))
207
+
208
+ # Call WaveSpeed video API
209
+ api_key = _wavespeed_provider._api_key
210
+ endpoint = f"https://api.wavespeed.ai/api/v3/{wavespeed_model}"
211
+
212
+ payload = {
213
+ "image": image_url,
214
+ "prompt": prompt,
215
+ "enable_sync_mode": True,
216
+ }
217
+ if negative_prompt:
218
+ payload["negative_prompt"] = negative_prompt
219
+
220
+ async with httpx.AsyncClient(timeout=300) as client:
221
+ resp = await client.post(
222
+ endpoint,
223
+ json=payload,
224
+ headers={
225
+ "Authorization": f"Bearer {api_key}",
226
+ "Content-Type": "application/json",
227
+ },
228
+ )
229
+
230
+ if resp.status_code != 200:
231
+ error_text = resp.text[:500]
232
+ logger.error("WaveSpeed video API error: %s", error_text)
233
+ _video_jobs[job_id]["status"] = "failed"
234
+ _video_jobs[job_id]["error"] = f"API error: {error_text[:200]}"
235
+ return
236
+
237
+ result = resp.json()
238
+ data = result.get("data", result)
239
+
240
+ # Check for failed status
241
+ if data.get("status") == "failed":
242
+ error_msg = data.get("error", "Unknown error")
243
+ _video_jobs[job_id]["status"] = "failed"
244
+ _video_jobs[job_id]["error"] = error_msg
245
+ return
246
+
247
+ # Extract video URL
248
+ video_url = None
249
+ outputs = data.get("outputs", [])
250
+ if outputs:
251
+ video_url = outputs[0]
252
+ elif "output" in data:
253
+ out = data["output"]
254
+ if isinstance(out, list) and out:
255
+ video_url = out[0]
256
+ elif isinstance(out, str):
257
+ video_url = out
258
+
259
+ if not video_url:
260
+ _video_jobs[job_id]["status"] = "failed"
261
+ _video_jobs[job_id]["error"] = "No video URL in response"
262
+ return
263
+
264
+ # Download the video
265
+ logger.info("Downloading cloud video: %s", video_url[:80])
266
+ video_resp = await client.get(video_url)
267
+
268
+ if video_resp.status_code != 200:
269
+ _video_jobs[job_id]["status"] = "failed"
270
+ _video_jobs[job_id]["error"] = "Failed to download video"
271
+ return
272
+
273
+ # Save to local output directory
274
+ from content_engine.config import settings
275
+ output_dir = settings.paths.output_dir / "videos"
276
+ output_dir.mkdir(parents=True, exist_ok=True)
277
+
278
+ # Determine extension from URL or default to mp4
279
+ ext = ".mp4"
280
+ if video_url.endswith(".webm"):
281
+ ext = ".webm"
282
+ elif video_url.endswith(".webp"):
283
+ ext = ".webp"
284
+
285
+ local_path = output_dir / f"video_{job_id}{ext}"
286
+ local_path.write_bytes(video_resp.content)
287
+
288
+ _video_jobs[job_id]["status"] = "completed"
289
+ _video_jobs[job_id]["output_path"] = str(local_path)
290
+ _video_jobs[job_id]["completed_at"] = time.time()
291
+ _video_jobs[job_id]["filename"] = local_path.name
292
+
293
+ logger.info("Cloud video saved: %s", local_path)
294
+
295
+ except Exception as e:
296
+ logger.error("Cloud video generation failed: %s", e)
297
+ _video_jobs[job_id]["status"] = "failed"
298
+ _video_jobs[job_id]["error"] = str(e)
299
+
300
+
301
  async def _poll_video_job(job_id: str, prompt_id: str):
302
  """Poll ComfyUI for video job completion."""
303
  import httpx
src/content_engine/api/ui.html CHANGED
@@ -838,11 +838,28 @@ select { cursor: pointer; }
838
  <div id="cloud-model-select" style="display:none">
839
  <label>Cloud Model (Text-to-Image)</label>
840
  <select id="gen-cloud-model">
841
- <option value="seedream-4.5">SeeDream v4.5 (Best Quality)</option>
842
- <option value="nano-banana-pro">NanoBanana Pro</option>
843
- <option value="nano-banana">NanoBanana</option>
844
- <option value="seedream-4">SeeDream v4</option>
845
- <option value="seedream-3.1">SeeDream v3.1</option>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  </select>
847
  </div>
848
 
@@ -891,7 +908,39 @@ select { cursor: pointer; }
891
  <img id="video-preview-img" style="max-width:100%; border-radius:6px">
892
  </div>
893
  <div class="section-title" style="margin-top:12px">Video Settings</div>
894
- <div style="display:grid; grid-template-columns:1fr 1fr; gap:8px">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
  <div>
896
  <label>Duration</label>
897
  <select id="video-duration">
@@ -908,8 +957,8 @@ select { cursor: pointer; }
908
  </select>
909
  </div>
910
  </div>
911
- <div style="font-size:11px;color:var(--text-secondary);margin-top:8px">
912
- Uses WAN 2.2 I2V on RunPod. Longer videos take more time (~2 sec per frame).
913
  </div>
914
  </div>
915
 
@@ -1244,7 +1293,11 @@ select { cursor: pointer; }
1244
  <span style="color:var(--text-secondary)">Checking...</span>
1245
  </div>
1246
  </div>
1247
- <div id="pod-controls" style="display:flex; gap:8px; align-items:center">
 
 
 
 
1248
  <select id="pod-gpu-select" style="padding:8px 12px; border-radius:6px; background:var(--bg-primary); border:1px solid var(--border); color:var(--text-primary)">
1249
  <option value="NVIDIA GeForce RTX 4090">RTX 4090 - $0.44/hr (24GB)</option>
1250
  <option value="NVIDIA RTX A6000">RTX A6000 - $0.76/hr (48GB)</option>
@@ -1388,6 +1441,7 @@ const API = ''; // same origin
1388
  let currentPage = 'generate';
1389
  let selectedRating = 'sfw';
1390
  let selectedBackend = 'pod';
 
1391
  let selectedMode = 'txt2img';
1392
  let templatesData = [];
1393
  let charactersData = [];
@@ -1633,11 +1687,10 @@ function selectMode(chip, mode) {
1633
  selectedMode = mode;
1634
  document.getElementById('img2img-section').style.display = mode === 'img2img' ? '' : 'none';
1635
  document.getElementById('img2video-section').style.display = mode === 'img2video' ? '' : 'none';
1636
- // For video mode, force pod backend
1637
- if (mode === 'img2video') {
1638
- document.querySelectorAll('#backend-chips .chip').forEach(c => c.classList.remove('selected'));
1639
- document.querySelector('#backend-chips .chip:nth-child(2)').classList.add('selected');
1640
- selectedBackend = 'pod';
1641
  }
1642
  // Update generate button text
1643
  const btn = document.getElementById('generate-btn');
@@ -1753,6 +1806,24 @@ function selectBackend(chip, backend) {
1753
  updateCloudModelVisibility();
1754
  }
1755
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1756
  function updateCloudModelVisibility() {
1757
  const isCloud = selectedBackend === 'cloud';
1758
  const isPod = selectedBackend === 'pod';
@@ -1844,12 +1915,20 @@ async function doGenerate() {
1844
  formData.append('num_frames', document.getElementById('video-duration').value || '81');
1845
  formData.append('fps', document.getElementById('video-fps').value || '24');
1846
  formData.append('seed', document.getElementById('gen-seed').value || '-1');
 
 
 
 
 
 
1847
 
1848
- const res = await fetch(API + '/api/video/generate', { method: 'POST', body: formData });
 
1849
  const data = await res.json();
1850
  if (!res.ok) throw new Error(data.detail || 'Video generation failed');
1851
 
1852
- toast('Video generating on RunPod... This takes 1-3 minutes', 'info');
 
1853
  await pollForVideo(data.job_id);
1854
  return;
1855
  }
@@ -2734,6 +2813,7 @@ function updatePodUI(pod) {
2734
 
2735
  async function startPod() {
2736
  const gpuType = document.getElementById('pod-gpu-select').value;
 
2737
  const btn = document.getElementById('pod-start-btn');
2738
  btn.disabled = true;
2739
  btn.textContent = 'Starting...';
@@ -2742,7 +2822,7 @@ async function startPod() {
2742
  const res = await fetch(API + '/api/pod/start', {
2743
  method: 'POST',
2744
  headers: {'Content-Type': 'application/json'},
2745
- body: JSON.stringify({gpu_type: gpuType})
2746
  });
2747
  const data = await res.json();
2748
 
@@ -2750,7 +2830,8 @@ async function startPod() {
2750
  throw new Error(data.detail || 'Failed to start pod');
2751
  }
2752
 
2753
- toast('Starting GPU pod... This takes 2-3 minutes', 'info');
 
2754
  loadPodStatus();
2755
  } catch(e) {
2756
  toast('Failed to start pod: ' + e.message, 'error');
 
838
  <div id="cloud-model-select" style="display:none">
839
  <label>Cloud Model (Text-to-Image)</label>
840
  <select id="gen-cloud-model">
841
+ <optgroup label="SeeDream (ByteDance)">
842
+ <option value="seedream-4.5">SeeDream v4.5 (Best Quality)</option>
843
+ <option value="seedream-4">SeeDream v4</option>
844
+ <option value="seedream-3.1">SeeDream v3.1</option>
845
+ </optgroup>
846
+ <optgroup label="NanoBanana (Google)">
847
+ <option value="nano-banana-pro">NanoBanana Pro</option>
848
+ <option value="nano-banana">NanoBanana</option>
849
+ </optgroup>
850
+ <optgroup label="FLUX (Black Forest Labs)">
851
+ <option value="flux-pro">FLUX Pro (Highest Quality)</option>
852
+ <option value="flux-dev">FLUX Dev</option>
853
+ <option value="flux-schnell">FLUX Schnell (Fast)</option>
854
+ </optgroup>
855
+ <optgroup label="WAN (Alibaba)">
856
+ <option value="wan-2.6">WAN 2.6 (Latest)</option>
857
+ <option value="wan-2.2">WAN 2.2</option>
858
+ </optgroup>
859
+ <optgroup label="Other">
860
+ <option value="dreamina-3.1">Dreamina v3.1</option>
861
+ <option value="qwen-image">Qwen Image</option>
862
+ </optgroup>
863
  </select>
864
  </div>
865
 
 
908
  <img id="video-preview-img" style="max-width:100%; border-radius:6px">
909
  </div>
910
  <div class="section-title" style="margin-top:12px">Video Settings</div>
911
+ <div style="margin-bottom:8px">
912
+ <label>Backend</label>
913
+ <div id="video-backend-chips" class="chips" style="margin-top:4px">
914
+ <div class="chip selected" onclick="selectVideoBackend(this, 'cloud')">Cloud API (WaveSpeed)</div>
915
+ <div class="chip" onclick="selectVideoBackend(this, 'pod')">RunPod (WAN 2.2)</div>
916
+ </div>
917
+ </div>
918
+ <div id="video-cloud-model-select">
919
+ <label>Video Model</label>
920
+ <select id="video-cloud-model">
921
+ <optgroup label="WAN I2V (Alibaba)">
922
+ <option value="wan-2.6-i2v" selected>WAN 2.6 I2V 720p (Best)</option>
923
+ <option value="wan-2.2-i2v">WAN 2.2 I2V 480p</option>
924
+ </optgroup>
925
+ <optgroup label="Kling (Kuaishou)">
926
+ <option value="kling-o3-pro">Kling O3 Pro (Highest Quality)</option>
927
+ <option value="kling-o3">Kling O3</option>
928
+ <option value="kling-o1">Kling O1</option>
929
+ </optgroup>
930
+ <optgroup label="Seedance (ByteDance)">
931
+ <option value="seedance-1.5-pro">Seedance 1.5 Pro</option>
932
+ <option value="seedance-1.5">Seedance 1.5</option>
933
+ </optgroup>
934
+ <optgroup label="Veo (Google)">
935
+ <option value="veo-3.1">Veo 3.1 (Latest)</option>
936
+ <option value="veo-3">Veo 3</option>
937
+ </optgroup>
938
+ <optgroup label="Sora (OpenAI)">
939
+ <option value="sora-2">Sora 2</option>
940
+ </optgroup>
941
+ </select>
942
+ </div>
943
+ <div style="display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px">
944
  <div>
945
  <label>Duration</label>
946
  <select id="video-duration">
 
957
  </select>
958
  </div>
959
  </div>
960
+ <div id="video-note" style="font-size:11px;color:var(--text-secondary);margin-top:8px">
961
+ Cloud API: Fast generation via WaveSpeed. RunPod: Uses WAN 2.2 I2V (~2 sec per frame).
962
  </div>
963
  </div>
964
 
 
1293
  <span style="color:var(--text-secondary)">Checking...</span>
1294
  </div>
1295
  </div>
1296
+ <div id="pod-controls" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap">
1297
+ <select id="pod-model-type" style="padding:8px 12px; border-radius:6px; background:var(--bg-primary); border:1px solid var(--border); color:var(--text-primary)">
1298
+ <option value="flux">FLUX.2 (Realistic)</option>
1299
+ <option value="wan">WAN 2.2 (General/Anime)</option>
1300
+ </select>
1301
  <select id="pod-gpu-select" style="padding:8px 12px; border-radius:6px; background:var(--bg-primary); border:1px solid var(--border); color:var(--text-primary)">
1302
  <option value="NVIDIA GeForce RTX 4090">RTX 4090 - $0.44/hr (24GB)</option>
1303
  <option value="NVIDIA RTX A6000">RTX A6000 - $0.76/hr (48GB)</option>
 
1441
  let currentPage = 'generate';
1442
  let selectedRating = 'sfw';
1443
  let selectedBackend = 'pod';
1444
+ let selectedVideoBackend = 'cloud';
1445
  let selectedMode = 'txt2img';
1446
  let templatesData = [];
1447
  let charactersData = [];
 
1687
  selectedMode = mode;
1688
  document.getElementById('img2img-section').style.display = mode === 'img2img' ? '' : 'none';
1689
  document.getElementById('img2video-section').style.display = mode === 'img2video' ? '' : 'none';
1690
+ // Hide regular backend chips for video mode (video has its own backend selector)
1691
+ const backendSection = document.getElementById('backend-chips').parentElement;
1692
+ if (backendSection) {
1693
+ backendSection.style.display = mode === 'img2video' ? 'none' : '';
 
1694
  }
1695
  // Update generate button text
1696
  const btn = document.getElementById('generate-btn');
 
1806
  updateCloudModelVisibility();
1807
  }
1808
 
1809
+ function selectVideoBackend(chip, backend) {
1810
+ chip.parentElement.querySelectorAll('.chip').forEach(c => c.classList.remove('selected'));
1811
+ chip.classList.add('selected');
1812
+ selectedVideoBackend = backend;
1813
+ // Show/hide video model dropdown based on backend
1814
+ const videoModelSelect = document.getElementById('video-cloud-model-select');
1815
+ if (videoModelSelect) {
1816
+ videoModelSelect.style.display = backend === 'cloud' ? '' : 'none';
1817
+ }
1818
+ // Update note
1819
+ const videoNote = document.getElementById('video-note');
1820
+ if (videoNote) {
1821
+ videoNote.textContent = backend === 'cloud'
1822
+ ? 'Cloud API: Fast generation via WaveSpeed. Pay per video.'
1823
+ : 'RunPod: Uses WAN 2.2 I2V on your pod (~2 sec per frame).';
1824
+ }
1825
+ }
1826
+
1827
  function updateCloudModelVisibility() {
1828
  const isCloud = selectedBackend === 'cloud';
1829
  const isPod = selectedBackend === 'pod';
 
1915
  formData.append('num_frames', document.getElementById('video-duration').value || '81');
1916
  formData.append('fps', document.getElementById('video-fps').value || '24');
1917
  formData.append('seed', document.getElementById('gen-seed').value || '-1');
1918
+ formData.append('backend', selectedVideoBackend);
1919
+
1920
+ // Add video model for cloud backend
1921
+ if (selectedVideoBackend === 'cloud') {
1922
+ formData.append('model', document.getElementById('video-cloud-model').value);
1923
+ }
1924
 
1925
+ const endpoint = selectedVideoBackend === 'cloud' ? '/api/video/generate/cloud' : '/api/video/generate';
1926
+ const res = await fetch(API + endpoint, { method: 'POST', body: formData });
1927
  const data = await res.json();
1928
  if (!res.ok) throw new Error(data.detail || 'Video generation failed');
1929
 
1930
+ const backendLabel = selectedVideoBackend === 'cloud' ? 'Cloud API' : 'RunPod';
1931
+ toast(`Video generating via ${backendLabel}...`, 'info');
1932
  await pollForVideo(data.job_id);
1933
  return;
1934
  }
 
2813
 
2814
  async function startPod() {
2815
  const gpuType = document.getElementById('pod-gpu-select').value;
2816
+ const modelType = document.getElementById('pod-model-type').value;
2817
  const btn = document.getElementById('pod-start-btn');
2818
  btn.disabled = true;
2819
  btn.textContent = 'Starting...';
 
2822
  const res = await fetch(API + '/api/pod/start', {
2823
  method: 'POST',
2824
  headers: {'Content-Type': 'application/json'},
2825
+ body: JSON.stringify({gpu_type: gpuType, model_type: modelType})
2826
  });
2827
  const data = await res.json();
2828
 
 
2830
  throw new Error(data.detail || 'Failed to start pod');
2831
  }
2832
 
2833
+ const modelName = modelType === 'wan' ? 'WAN 2.2' : 'FLUX.2';
2834
+ toast(`Starting ${modelName} pod... This takes 3-5 minutes`, 'info');
2835
  loadPodStatus();
2836
  } catch(e) {
2837
  toast('Failed to start pod: ' + e.message, 'error');
src/content_engine/main.py CHANGED
@@ -138,6 +138,10 @@ async def lifespan(app: FastAPI):
138
  routes_catalog.init_routes(catalog)
139
  routes_system.init_routes(comfyui_client, catalog, template_engine, character_profiles)
140
 
 
 
 
 
141
  # Initialize LoRA trainer (local)
142
  from content_engine.services.lora_trainer import LoRATrainer
143
  lora_trainer = LoRATrainer()
 
138
  routes_catalog.init_routes(catalog)
139
  routes_system.init_routes(comfyui_client, catalog, template_engine, character_profiles)
140
 
141
+ # Initialize video routes with WaveSpeed provider for cloud video generation
142
+ if wavespeed_provider:
143
+ routes_video.init_wavespeed(wavespeed_provider)
144
+
145
  # Initialize LoRA trainer (local)
146
  from content_engine.services.lora_trainer import LoRATrainer
147
  lora_trainer = LoRATrainer()
src/content_engine/services/cloud_providers/wavespeed_provider.py CHANGED
@@ -35,16 +35,49 @@ logger = logging.getLogger(__name__)
35
 
36
  # Map friendly names to WaveSpeed model IDs (text-to-image)
37
  MODEL_MAP = {
38
- # NanoBanana
39
- "nano-banana": "google-nano-banana-text-to-image",
40
- "nano-banana-pro": "google-nano-banana-pro-text-to-image",
41
- # SeeDream
42
- "seedream-3": "bytedance-seedream-v3",
43
- "seedream-3.1": "bytedance-seedream-v3.1",
44
- "seedream-4": "bytedance-seedream-v4",
45
- "seedream-4.5": "bytedance-seedream-v4.5",
 
 
 
 
 
 
 
 
 
 
 
 
46
  # Default
47
- "default": "bytedance-seedream-v4.5",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  # Map friendly names to WaveSpeed edit model API paths
 
35
 
36
  # Map friendly names to WaveSpeed model IDs (text-to-image)
37
  MODEL_MAP = {
38
+ # NanoBanana (Google)
39
+ "nano-banana": "google/nano-banana",
40
+ "nano-banana-pro": "google/nano-banana-pro",
41
+ # SeeDream (ByteDance)
42
+ "seedream-3": "bytedance/seedream-v3",
43
+ "seedream-3.1": "bytedance/seedream-v3.1",
44
+ "seedream-4": "bytedance/seedream-v4",
45
+ "seedream-4.5": "bytedance/seedream-v4.5",
46
+ # WAN (Alibaba)
47
+ "wan-2.2": "alibaba/wan-2.2",
48
+ "wan-2.6": "alibaba/wan-2.6",
49
+ # FLUX (Black Forest Labs)
50
+ "flux-dev": "black-forest-labs/flux-dev",
51
+ "flux-schnell": "black-forest-labs/flux-schnell",
52
+ "flux-pro": "black-forest-labs/flux-pro",
53
+ # Dreamina (ByteDance)
54
+ "dreamina-3": "bytedance/dreamina-v3",
55
+ "dreamina-3.1": "bytedance/dreamina-v3.1",
56
+ # Qwen (WaveSpeed optimized)
57
+ "qwen-image": "wavespeedai/qwen-image",
58
  # Default
59
+ "default": "bytedance/seedream-v4.5",
60
+ }
61
+
62
+ # Image-to-Video models
63
+ VIDEO_MODEL_MAP = {
64
+ # Kling (Kuaishou)
65
+ "kling-o1": "kuaishou/kling-o1",
66
+ "kling-o3": "kuaishou/kling-o3",
67
+ "kling-o3-pro": "kuaishou/kling-o3-pro",
68
+ # Veo (Google)
69
+ "veo-3": "google/veo-3",
70
+ "veo-3.1": "google/veo-3.1",
71
+ # WAN I2V (Alibaba)
72
+ "wan-2.2-i2v": "alibaba/wan-2.2-i2v-480p",
73
+ "wan-2.6-i2v": "alibaba/wan-2.6-i2v-720p",
74
+ # Seedance (ByteDance)
75
+ "seedance-1.5": "bytedance/seedance-1.5",
76
+ "seedance-1.5-pro": "bytedance/seedance-1.5-pro",
77
+ # Sora (OpenAI)
78
+ "sora-2": "openai/sora-2",
79
+ # Default
80
+ "default": "alibaba/wan-2.6-i2v-720p",
81
  }
82
 
83
  # Map friendly names to WaveSpeed edit model API paths