Skip to content

Commit 56a4390

Browse files
uzunenescursoragent
andcommitted
feat: add set_provider_headers for BYOK switching, include 12_veo in run_all
- Client.set_provider_headers(): update BYOK keys (fal/replicate/runway) for subsequent requests - _common.set_provider_headers(): wrapper for examples - run_all_capabilities: add 12_veo_from_catalog.py (SDK→API→Fal flow) Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 17df640 commit 56a4390

4 files changed

Lines changed: 135 additions & 0 deletions

File tree

examples/12_veo_from_catalog.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
Step 12: Video with Veo from catalog.
3+
4+
Flow:
5+
1. get_models(provider="fal", model_type="video") from API (database).
6+
2. Filter models whose id/name contains "veo".
7+
3. Pick the newest by first_seen_at.
8+
4. Call videos.generate with that model; write result to sample_outputs.
9+
10+
Requires: VISGATE_API_KEY. Optional: VISGATE_FAL_API_KEY for BYOK.
11+
cd examples && python 12_veo_from_catalog.py
12+
"""
13+
from __future__ import annotations
14+
15+
from pathlib import Path
16+
17+
import httpx
18+
19+
from _common import create_client
20+
21+
OUTPUT_DIR = Path(__file__).resolve().parent / "sample_outputs"
22+
OUTPUT_VIDEO = OUTPUT_DIR / "istanbul.mp4"
23+
PROMPT = "Cinematic drone over Istanbul at golden hour, Bosphorus and minarets, 4k"
24+
DURATION = 6.0
25+
FALLBACK_MODEL = "fal-ai/veo3"
26+
27+
28+
def pick_newest_veo(models_response):
29+
"""Filter models containing 'veo' in id/name, sort by first_seen_at (newest first), return first."""
30+
veo = [
31+
m
32+
for m in models_response.models
33+
if m
34+
and ("veo" in (m.id or "").lower() or "veo" in (m.name or "").lower())
35+
]
36+
if not veo:
37+
return None
38+
# Newest first (first_seen_at desc; None last)
39+
veo.sort(key=lambda m: m.first_seen_at or "", reverse=True)
40+
return veo[0].id
41+
42+
43+
def download_url(url: str, path: Path, timeout: float = 120.0) -> None:
44+
with httpx.Client(timeout=timeout, follow_redirects=True) as client:
45+
r = client.get(url)
46+
r.raise_for_status()
47+
path.parent.mkdir(parents=True, exist_ok=True)
48+
path.write_bytes(r.content)
49+
50+
51+
def main() -> None:
52+
client = create_client()
53+
54+
# 1. Get video models from API (database), newest first
55+
resp = client.models.list(
56+
provider="fal",
57+
model_type="video",
58+
sort="newest",
59+
limit=100,
60+
)
61+
model_id = pick_newest_veo(resp)
62+
if not model_id:
63+
model_id = FALLBACK_MODEL
64+
print(f" No Veo in catalog; using fallback {model_id}")
65+
else:
66+
print(f" Using newest Veo from catalog: {model_id}")
67+
68+
# 2. Generate video via Visgate API
69+
print(f" Generating video (prompt={PROMPT[:50]}...) ...")
70+
# Note: Veo expects duration as "4s"/"6s"/"8s". Visgate API should convert
71+
# duration_seconds to that format. If validation fails, try another provider.
72+
result = client.videos.generate(
73+
model=model_id,
74+
prompt=PROMPT,
75+
duration_seconds=6.0,
76+
)
77+
if not result.video_url:
78+
print(" No video_url in response.")
79+
return
80+
print(f" Got video_url")
81+
82+
# 3. Write to sample_outputs
83+
download_url(result.video_url, OUTPUT_VIDEO)
84+
print(f" Written {OUTPUT_VIDEO}")
85+
client.close()
86+
87+
88+
if __name__ == "__main__":
89+
main()

examples/_common.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ def create_client() -> Client:
3636
)
3737

3838

39+
def set_provider_headers(
40+
client: Client,
41+
*,
42+
fal_key: Optional[str] = None,
43+
replicate_key: Optional[str] = None,
44+
runway_key: Optional[str] = None,
45+
) -> None:
46+
"""Update client BYOK headers. Omit args for managed mode (no BYOK)."""
47+
client.set_provider_headers(
48+
fal_key=fal_key,
49+
replicate_key=replicate_key,
50+
runway_key=runway_key,
51+
)
52+
53+
3954
def provider_key(provider: str) -> Optional[str]:
4055
"""Resolve a BYOK provider key from environment."""
4156
env_map = {

examples/run_all_capabilities.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"09_provider_keys.py", # provider keys list + validate
2626
"10_api_keys.py", # GET /api-keys
2727
"11_billing_readonly.py", # GET billing/stats, info, pricing
28+
"12_veo_from_catalog.py", # Veo from catalog (SDK→API→Fal)
2829
]
2930

3031

src/visgate_sdk/client.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ def __init__(
158158
self.api_key = _resolve_api_key(api_key)
159159
self.base_url = base_url.rstrip("/")
160160
self.max_retries = max_retries
161+
self._fal_key = fal_key
162+
self._replicate_key = replicate_key
163+
self._runway_key = runway_key
161164

162165
self._client = httpx.Client(
163166
base_url=self.base_url,
@@ -203,6 +206,33 @@ def generate(
203206
"""
204207
return self._generate(prompt=prompt, model=model, params=params)
205208

209+
def set_provider_headers(
210+
self,
211+
*,
212+
fal_key: Optional[str] = None,
213+
replicate_key: Optional[str] = None,
214+
runway_key: Optional[str] = None,
215+
) -> None:
216+
"""Update BYOK provider keys for subsequent requests.
217+
218+
Omitted keys are set to None (managed mode). Pass a key to use BYOK for that provider.
219+
"""
220+
self._fal_key = fal_key or None
221+
self._replicate_key = replicate_key or None
222+
self._runway_key = runway_key or None
223+
# Rebuild the underlying httpx client with new headers
224+
self._client.close()
225+
self._client = httpx.Client(
226+
base_url=self.base_url,
227+
timeout=httpx.Timeout(DEFAULT_TIMEOUT),
228+
headers=_build_headers(
229+
self.api_key,
230+
fal_key=self._fal_key,
231+
replicate_key=self._replicate_key,
232+
runway_key=self._runway_key,
233+
),
234+
)
235+
206236
def health(self) -> Dict[str, Any]:
207237
"""Check API health status.
208238

0 commit comments

Comments
 (0)