Skip to content

Commit 7978721

Browse files
uzunenescursoragent
andcommitted
chore: post-cleanup — CHANGELOG 0.2.3, CI lint examples, PyPI tag check, docs/examples/ruff
- CHANGELOG: add [0.2.3] with run_with_sifreler, API.md, examples README gallery, ruff fixes - CI: ruff check src/ tests/ examples/ - publish-pypi: verify release tag matches pyproject.toml version - docs/API.md: latest API, run_with_sifreler, low-cost run, skip_gcs_upload, 404/MODEL_NOT_FOUND - README: remove dead script refs, add run_with_sifreler - CONTRIBUTING: lint scope + code style - examples: run_with_sifreler.py, Tee E702 fix in all scripts, 12_veo F541 fix - examples/README: sample results, provider counts, image gallery, low-cost run - Remove obsolete examples: _common, capture_all_outputs, create_placeholder_video, fetch_sample_outputs, generate_istanbul_video_veo, make_video_and_gif Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8b5df5d commit 7978721

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1364
-1419
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run: pip install -e ".[dev]"
3030

3131
- name: Lint
32-
run: ruff check src/ tests/
32+
run: ruff check src/ tests/ examples/
3333

3434
- name: Test
3535
run: pytest tests/ -v

.github/workflows/publish-pypi.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ jobs:
2020
steps:
2121
- uses: actions/checkout@v4
2222

23+
- name: Verify version matches release tag
24+
run: |
25+
TAG="${{ github.event.release.tag_name }}"
26+
VER=$(grep -m1 '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
27+
if [ "v${VER}" != "${TAG}" ]; then
28+
echo "::error::pyproject.toml version (${VER}) does not match release tag (${TAG}). Bump version to match before publishing."
29+
exit 1
30+
fi
31+
2332
- name: Set up Python
2433
uses: actions/setup-python@v5
2534
with:

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ htmlcov/
1717
# OS
1818
.DS_Store
1919

20-
# Env
20+
# Env / secrets
2121
.env
2222
.env.*
23+
sifreler
24+
*sa-key*.json

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## [0.2.3] - 2026-02-13
4+
5+
### Added
6+
7+
- `examples/run_with_sifreler.py` — run examples with env loaded from `sifreler` or `VISGATE_ENV_FILE`.
8+
- docs/API.md: low-cost run, `skip_gcs_upload`, 404/MODEL_NOT_FOUND, run_with_sifreler.
9+
- examples/README.md: sample results (live API output), provider counts table, image gallery, low-cost run command.
10+
11+
### Changed
12+
13+
- README.md: removed references to deleted scripts (`capture_all_outputs`, `fetch_sample_outputs`, `make_video_and_gif`); added `run_with_sifreler`.
14+
- CONTRIBUTING.md: lint scope includes `examples/`; code style (no semicolons, no f-strings without placeholders).
15+
- All example scripts: `Tee` class write/flush split to multiple lines (ruff E702).
16+
- examples/12_veo_from_catalog.py: fixed f-string without placeholders (ruff F541).
17+
318
## [0.2.2] - 2026-02-11
419

520
### Added

CONTRIBUTING.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pip install -e ".[dev]"
1616

1717
```bash
1818
# Lint
19-
ruff check src/ tests/
19+
ruff check src/ tests/ examples/
2020

2121
# Unit tests
2222
pytest tests/ -v
@@ -32,16 +32,18 @@ python examples/01_live_api_smoke.py
3232

3333
1. Fork the repo and create a feature branch.
3434
2. Make your changes. Add tests for new functionality.
35-
3. Ensure `ruff check src/ tests/` and `pytest tests/ -v` pass.
35+
3. Ensure `ruff check src/ tests/ examples/` and `pytest tests/ -v` pass.
3636
4. Open a PR with a clear description of what changed and why.
3737

3838
## Code Style
3939

4040
- Python 3.9+ compatible.
4141
- Use `from __future__ import annotations` in all modules.
42-
- Run `ruff check` before committing.
42+
- Run `ruff check src/ tests/ examples/` before committing.
4343
- Keep the public API surface minimal.
4444
- Every public export must be in `__init__.py` `__all__`.
45+
- No multiple statements on one line (no semicolons); no f-strings without placeholders.
46+
- User-facing strings in English only.
4547

4648
## Releasing
4749

README.md

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# visgate-sdk
22

3-
Python SDK for the [Visgate API](https://visgateai.com) -- one client for image and video generation across Fal, Replicate, and Runway.
3+
Python SDK for the [Visgate API](https://visgateai.com) one client for image and video generation across **Fal**, **Replicate**, and **Runway**.
44

55
[![CI](https://github.com/visgate-ai/visgate-python/actions/workflows/ci.yml/badge.svg)](https://github.com/visgate-ai/visgate-python/actions/workflows/ci.yml)
66
[![PyPI](https://img.shields.io/pypi/v/visgate-sdk)](https://pypi.org/project/visgate-sdk/)
77
[![Python](https://img.shields.io/pypi/pyversions/visgate-sdk)](https://pypi.org/project/visgate-sdk/)
88
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
99

10+
## Overview
11+
12+
Visgate is a visual AI gateway: one API, one SDK, three providers. Generate images and videos with Fal, Replicate, or Runway — managed keys or bring your own. Built-in cache, usage tracking, and typed exceptions.
13+
1014
## Install
1115

1216
```bash
@@ -26,7 +30,7 @@ print(result.image_url)
2630
client.close()
2731
```
2832

29-
Or with a context manager:
33+
With a context manager:
3034

3135
```python
3236
with Client() as client:
@@ -38,28 +42,29 @@ with Client() as client:
3842

3943
- **One client, three providers.** Fal, Replicate, and Runway behind a single API.
4044
- **Managed and BYOK modes.** Use Visgate-managed keys or bring your own.
41-
- **Sync and async.** `Client` for synchronous code, `AsyncClient` for async.
42-
- **Automatic retries.** Transient errors (429, 5xx) are retried with exponential backoff.
43-
- **Typed exceptions.** Catch `AuthenticationError`, `RateLimitError`, `ProviderError`, etc.
44-
- **Full type hints.** PEP 561 compliant with `py.typed` marker.
45+
- **Sync and async.** `Client` for sync, `AsyncClient` for async.
46+
- **Async 202 + poll.** `videos.generate(wait=False)` / `images.generate(wait=False)` for long-running jobs; poll with `client.requests.get()`.
47+
- **Automatic retries.** Transient errors (429, 5xx) with exponential backoff.
48+
- **Typed exceptions.** `AuthenticationError`, `RateLimitError`, `ProviderError`, etc.
49+
- **Full type hints.** PEP 561 compliant.
4550

4651
## Authentication
4752

48-
Set your API key as an environment variable (recommended):
53+
Set your API key (required for most endpoints):
4954

5055
```bash
5156
export VISGATE_API_KEY="vg-..."
5257
```
5358

54-
Or pass it directly:
59+
Or pass directly:
5560

5661
```python
5762
client = Client(api_key="vg-...")
5863
```
5964

6065
## BYOK (Bring Your Own Key)
6166

62-
Use your own provider keys while still routing through Visgate:
67+
Use your own provider keys while routing through Visgate:
6368

6469
```python
6570
client = Client(
@@ -95,7 +100,7 @@ result = client.videos.generate(
95100
prompt="Cinematic drone over Istanbul at golden hour, Bosphorus and minarets",
96101
duration_seconds=5.0,
97102
)
98-
print(result.video_url) # Convert to GIF for previews: examples/make_video_and_gif.py --mp4 path
103+
print(result.video_url)
99104
```
100105

101106
### Model Discovery
@@ -196,38 +201,35 @@ All API traffic uses HTTPS. Provider API keys (BYOK) are encrypted at rest; othe
196201

197202
## Examples
198203

199-
A **step-by-step guide** with purpose, command, and proof output for each example is in [examples/README.md](examples/README.md) (textbook-style with all results).
204+
A step-by-step guide for each example is in [examples/README.md](examples/README.md).
200205

201206
| Script | Description | Auth |
202207
|--------|-------------|:---:|
203-
| `00_smoke_sdk.py` | Version and client (no network) | No |
204-
| `01_live_api_smoke.py` | Health check and model listing | No |
208+
| `00_smoke_sdk.py` | SDK version and client (no network) | No |
209+
| `01_live_api_smoke.py` | Health and model listing | No |
205210
| `00_auth_identity.py` | Auth identity verification | Yes |
206211
| `01_models_catalog.py` | Model discovery and search | Yes |
207212
| `02_generate_unified.py` | Unified generation (managed + BYOK) | Yes |
208-
| `03_images_all_providers.py` | Image generation across providers | Yes |
213+
| `03_images_all_providers.py` | Image generation (Fal, Replicate, Runway) | Yes |
209214
| `04_videos_all_providers.py` | Video generation | Yes |
210-
| `05_usage_history_verify.py` | Usage, logs, and dashboard | Yes |
215+
| `05_usage_history_verify.py` | Usage, logs, dashboard | Yes |
211216
| `06_provider_balances.py` | Provider status and balances | Yes |
212-
| `07_cache_demo.py` | Cache: two identical image requests | Yes |
217+
| `07_cache_demo.py` | Exact cache (identical requests) | Yes |
213218
| `08_semantic_cache_demo.py` | Semantic cache (similar prompts) | Yes |
214219
| `09_provider_keys.py` | Provider keys list and validate | Yes |
215-
| `10_api_keys.py` | List API keys (GET /api-keys) | Yes |
220+
| `10_api_keys.py` | List API keys | Yes |
216221
| `11_billing_readonly.py` | Billing stats, info, pricing | Yes |
222+
| `12_veo_from_catalog.py` | Veo from catalog → generate → save | Yes |
223+
| `13_async_generation.py` | Async 202 + poll (video/image) | Yes |
217224

218225
```bash
219-
# Run all examples in order
226+
# Run all examples (requires VISGATE_API_KEY; generation steps incur cost)
220227
VISGATE_API_KEY=vg-... python examples/run_all_capabilities.py
221228

222-
# Capture all text outputs to sample_outputs/out_*.txt
223-
cd examples && python capture_all_outputs.py
224-
```
229+
# Run with a credentials file (e.g. sifreler in repo root)
230+
python examples/run_with_sifreler.py
225231

226-
Sample outputs use an **Istanbul theme** (Bosphorus, minarets, golden hour). They are under [`examples/sample_outputs/`](examples/sample_outputs/) (images, `istanbul.mp4`, `istanbul.gif`, and text proofs). Regenerate:
227-
228-
```bash
229-
cd examples && python fetch_sample_outputs.py # images + video
230-
cd examples && python make_video_and_gif.py # video + GIF
232+
# Low-cost run (no generation): see examples/README.md
231233
```
232234

233235
## Contributing

docs/API.md

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,34 @@
11
# API reference
22

3-
Short reference. Full OpenAPI spec: your API base URL + `/docs`.
3+
Short reference for the **visgate-python** SDK. Full OpenAPI spec: **https://visgateai.com/api/v1/docs** (or your base URL + `/docs`).
44

55
## Testing against the live API
66

7-
Use the **visgate-python** SDK and examples to hit the live API. Sample outputs (images and video) use an **Istanbul theme** for a consistent look.
7+
Use the SDK and the numbered examples to hit the live API. Sample outputs use an **Istanbul theme** (Bosphorus, Galata Tower, golden hour).
88

99
```bash
1010
pip install visgate-sdk
1111
export VISGATE_API_KEY=vg-...
1212

13-
# Health and models (minimal check)
13+
# Health and models (no auth)
1414
python examples/01_live_api_smoke.py
1515

16-
# Exact cache: identical requests; second = cache hit
17-
python examples/07_cache_demo.py
16+
# Auth and identity
17+
python examples/00_auth_identity.py
1818

19-
# Semantic cache: similar prompts; second may = cache hit (Vertex AI + Firestore)
20-
python examples/08_semantic_cache_demo.py
21-
22-
# Run all capability examples (images, video, usage, providers, etc.)
19+
# Run all examples (requires API key; generation steps incur cost)
2320
python examples/run_all_capabilities.py
2421
```
2522

26-
**Regenerate sample outputs (Istanbul-themed images + video + GIF):**
23+
**Run with a credentials file (e.g. `sifreler` in repo root):**
2724

2825
```bash
29-
cd examples
30-
python fetch_sample_outputs.py # images + video (waits for video when async)
31-
python make_video_and_gif.py # video + GIF (or use --mp4 path to convert only)
32-
python create_placeholder_video.py # placeholder istanbul.mp4 + .gif without API
26+
python examples/run_with_sifreler.py
27+
python examples/run_with_sifreler.py 04_videos_all_providers.py # single example
3328
```
3429

30+
**Low-cost run (no image/video generation):** `00_smoke_sdk`, `01_live_api_smoke`, `00_auth_identity`, `01_models_catalog`, `05_usage_history_verify`, `06_provider_balances`, `09_provider_keys`, `10_api_keys`, `11_billing_readonly`. See [examples/README.md](examples/README.md) for the full table and sample outputs.
31+
3532
Base URL defaults to `https://visgateai.com/api/v1`. Override with `VISGATE_BASE_URL` for staging or local.
3633

3734
## Installation
@@ -207,6 +204,7 @@ result = client.videos.generate(
207204
prompt="a cat walking",
208205
image_url=None, # Optional, image URL to animate
209206
duration_seconds=5.0, # Optional, default 5.0
207+
skip_gcs_upload=False, # Set True to return provider URL directly (faster, avoids proxy timeouts)
210208
params=None, # Optional, additional model-specific parameters
211209
)
212210
```
@@ -224,16 +222,64 @@ result = await client.videos.generate(
224222
### VideoResult Properties
225223

226224
- `id` (str): Request ID
227-
- `video_url` (str | None): Generated video URL
225+
- `video_url` (str | None): Generated video URL (may be ``None`` if generation failed or still processing)
228226
- `model` (str): Model identifier used
229-
- `provider` (str): Provider name
227+
- `provider` (str): Provider name (e.g. ``"fal"``, ``"replicate"``, ``"runway"``)
230228
- `cost` (float): Cost in USD
231229
- `cache_hit` (bool): Whether the result was served from cache
232-
- `provider_cost_avoided_micro` (int | None): When `cache_hit` is true, provider cost avoided in micro-USD (1e-6 USD). Omitted on cache miss.
230+
- `provider_cost_avoided_micro` (int | None): When `cache_hit` is true, provider cost avoided in micro-USD. Omitted on cache miss.
233231
- `latency_ms` (int | None): Request latency in milliseconds
234232
- `created_at` (datetime): Creation timestamp
235233

236-
**Note:** Video generation may be asynchronous. Check `video_url` to see if the video is ready. You can convert the downloaded MP4 to a GIF for previews (e.g. `examples/make_video_and_gif.py` or `--mp4 path/to/video.mp4`).
234+
**Note:** Video generation can take a minute or more. Use `skip_gcs_upload=True` to get the provider URL directly and avoid proxy timeouts. For non-blocking flow, use `wait=False` and poll via the Requests resource (see below).
235+
236+
### Async 202 + poll (wait=False)
237+
238+
For long-running video or image generation, use `wait=False` to get a 202 response and a `GenerationRequest`; then poll until complete:
239+
240+
```python
241+
req = client.videos.generate(
242+
model="fal-ai/veo3",
243+
prompt="Cinematic drone over Istanbul",
244+
duration_seconds=5.0,
245+
wait=False,
246+
)
247+
# req has .request_id and .status ("accepted", "processing", "completed", "failed")
248+
result = req.wait() # blocks until completed, returns VideoResult
249+
print(result.video_url)
250+
251+
# Or poll manually
252+
status = client.requests.get(req.request_id, wait=True)
253+
# status.result is VideoResult when status.status == "completed"
254+
```
255+
256+
## Requests Resource
257+
258+
Poll async generation status (202 flow for videos/images).
259+
260+
### Sync
261+
262+
```python
263+
req = client.requests.get(request_id) # current status
264+
req = client.requests.get(request_id, wait=True) # block until completed
265+
# req: RequestStatusResult with .request_id, .status, .result (VideoResult/ImageResult when completed)
266+
```
267+
268+
### Async
269+
270+
```python
271+
req = await client.requests.get(request_id)
272+
req = await client.requests.get(request_id, wait=True)
273+
```
274+
275+
### RequestStatusResult
276+
277+
- `request_id` (str): Request ID
278+
- `status` (str): `"accepted"` | `"processing"` | `"completed"` | `"failed"`
279+
- `result` (VideoResult | ImageResult | None): Output when status is `"completed"`
280+
- `error` (dict | None): Error details when status is `"failed"`
281+
282+
Example: `examples/13_async_generation.py`.
237283

238284
## Usage Resource
239285

@@ -311,10 +357,10 @@ except VisgateError as e:
311357
| Exception | HTTP Status | When |
312358
|-----------|-------------|------|
313359
| `AuthenticationError` | 401 | Invalid or missing API key |
314-
| `RateLimitError` | 429 | Too many requests (includes `retry_after` attribute) |
315-
| `ProviderError` | 502+ | Upstream provider (e.g., fal.ai) failed |
316360
| `ValidationError` | 422 | Bad request parameters |
317-
| `VisgateError` | Other | Other API errors |
361+
| `RateLimitError` | 429 | Too many requests (includes `retry_after` attribute) |
362+
| `ProviderError` | 502 | Upstream provider (Fal, Replicate, Runway) failed |
363+
| `VisgateError` | 404, 500, etc. | e.g. 404 + ``MODEL_NOT_FOUND`` for unsupported model; other API errors |
318364

319365
### Exception Properties
320366

@@ -431,10 +477,10 @@ API traffic is over HTTPS. Provider keys (BYOK) are encrypted at rest; account a
431477

432478
## Sample outputs (Istanbul theme)
433479

434-
All example scripts use Istanbul-themed prompts (Bosphorus, minarets, golden hour, Galata Tower, etc.). Generated assets are under `examples/sample_outputs/`: images (e.g. `generate_unified.jpg`, `sunset_istanbul.jpg`, `galata_tower.jpg`, `bosphorus_night.jpg`), cache demos, and video `istanbul.mp4` plus `istanbul.gif` (video converted to GIF for previews).
480+
Example scripts use Istanbul-themed prompts (Bosphorus, Galata Tower, golden hour). Generated assets are in `examples/sample_outputs/`: images (`generate_unified.jpg`, `sunset_istanbul.jpg`, `galata_tower.jpg`, `bosphorus_night.jpg`), cache demos (`cache_demo.jpg`, `semantic_exact.jpg`, `semantic_similar.jpg`), and video `istanbul.mp4` with preview `istanbul.gif`. See [examples/README.md](examples/README.md) for the output gallery and provider counts.
435481

436-
## Additional Resources
482+
## Additional resources
437483

438484
- SDK README: [README.md](../README.md)
439-
- Examples (step-by-step): [examples/README.md](../examples/README.md)
440-
- API OpenAPI Spec: https://visgateai.com/api/v1/docs
485+
- Examples (step-by-step and sample results): [examples/README.md](../examples/README.md)
486+
- OpenAPI spec: **https://visgateai.com/api/v1/docs**

0 commit comments

Comments
 (0)