fotohub 1.0.0__tar.gz

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.
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ .venv/
13
+ venv/
14
+ env/
15
+ .env
16
+ *.so
17
+ .coverage
18
+ htmlcov/
fotohub-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FOTOhub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fotohub-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,521 @@
1
+ Metadata-Version: 2.4
2
+ Name: fotohub
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for the FOTOhub AI Platform
5
+ Project-URL: Homepage, https://fotohub.app
6
+ Project-URL: Documentation, https://docs.fotohub.app/sdk/python
7
+ Project-URL: Repository, https://github.com/fotohubapp/sdk-python
8
+ Project-URL: Issues, https://github.com/fotohubapp/sdk-python/issues
9
+ Author-email: FOTOhub <dev@fotohub.app>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai,fotohub,image-generation,llm,music-generation,video-generation
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: httpx>=0.24
26
+ Requires-Dist: pydantic>=2.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.5; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
30
+ Requires-Dist: pytest-httpx>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest>=7.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.1; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ <p align="center">
36
+ <img src="https://static.fotohub.app/brand/fotohub-logo-dark.png" alt="FOTOhub" width="280" />
37
+ </p>
38
+
39
+ <p align="center">
40
+ <strong>Official Python SDK for the FOTOhub AI Platform</strong>
41
+ </p>
42
+
43
+ <p align="center">
44
+ <a href="https://pypi.org/project/fotohub/"><img src="https://img.shields.io/pypi/v/fotohub?color=blue&label=PyPI" alt="PyPI Version" /></a>
45
+ <a href="https://pypi.org/project/fotohub/"><img src="https://img.shields.io/pypi/pyversions/fotohub" alt="Python Versions" /></a>
46
+ <a href="https://github.com/fotohubapp/sdk-python/blob/main/LICENSE"><img src="https://img.shields.io/github/license/fotohubapp/sdk-python" alt="License" /></a>
47
+ <a href="https://pypi.org/project/fotohub/"><img src="https://img.shields.io/pypi/dm/fotohub?color=green" alt="Downloads" /></a>
48
+ <a href="https://docs.fotohub.app/sdk/python"><img src="https://img.shields.io/badge/docs-fotohub.app-blue" alt="Documentation" /></a>
49
+ </p>
50
+
51
+ <p align="center">
52
+ Generate images, videos, music, and chat with LLMs — all through a single, unified Python client.<br/>
53
+ Supports 80+ AI models from 10+ providers with built-in credit management.
54
+ </p>
55
+
56
+ ---
57
+
58
+ ## Features
59
+
60
+ - **Image Generation** — 25+ models including SeedDream 5.0, Flux, SDXL, Midjourney-style, and more
61
+ - **Video Generation** — Async job-based video creation with polling and image-to-video
62
+ - **Music Generation** — AI-powered music and audio creation
63
+ - **Chat / LLM** — OpenAI-compatible chat completions with streaming support
64
+ - **Gabriel AI** — Intent orchestration engine for natural-language workflows
65
+ - **Storage** — S3 bucket provisioning with presigned upload/download URLs
66
+ - **Translation** — Multi-language translation (no auth required)
67
+ - **Fully Typed** — Complete type annotations with Pydantic v2 models
68
+ - **Sync + Async** — Both synchronous and asynchronous clients included
69
+ - **Automatic Retries** — Exponential backoff with configurable retry logic
70
+ - **Streaming** — Real-time SSE streaming for chat completions
71
+
72
+ ## Installation
73
+
74
+ ```bash
75
+ pip install fotohub
76
+ ```
77
+
78
+ Requires Python 3.9 or higher.
79
+
80
+ ## Quick Start
81
+
82
+ ```python
83
+ from fotohub import FotoHub
84
+
85
+ client = FotoHub(api_key="fh_live_...")
86
+
87
+ # Generate an image
88
+ result = client.generate_image(prompt="A mountain landscape at golden hour")
89
+ print(result.images[0].url)
90
+ ```
91
+
92
+ ## Authentication
93
+
94
+ Get your API key from [fotohub.app/settings/api](https://fotohub.app/settings/api).
95
+
96
+ ```python
97
+ from fotohub import FotoHub
98
+
99
+ # Option 1: Pass directly
100
+ client = FotoHub(api_key="fh_live_...")
101
+
102
+ # Option 2: Environment variable
103
+ # export FOTOHUB_API_KEY=fh_live_...
104
+ client = FotoHub()
105
+ ```
106
+
107
+ The SDK authenticates via both `Authorization: Bearer` and `x-api-key` headers.
108
+
109
+ ## Usage Examples
110
+
111
+ ### Image Generation
112
+
113
+ ```python
114
+ from fotohub import FotoHub
115
+
116
+ client = FotoHub(api_key="fh_live_...")
117
+
118
+ # Basic generation (default: SeedDream 5.0 Lite)
119
+ result = client.generate_image(
120
+ prompt="A serene Japanese garden with cherry blossoms",
121
+ )
122
+ print(result.images[0].url)
123
+
124
+ # Advanced options
125
+ result = client.generate_image(
126
+ prompt="Cyberpunk cityscape at night, neon reflections on wet streets",
127
+ model="flux-1-schnell",
128
+ width=1280,
129
+ height=720,
130
+ num_images=2,
131
+ negative_prompt="blurry, low quality",
132
+ seed=42,
133
+ guidance_scale=7.5,
134
+ steps=30,
135
+ )
136
+
137
+ for image in result.images:
138
+ print(f"{image.url} — {image.width}x{image.height}, {image.credits_used} credits")
139
+ ```
140
+
141
+ ### Video Generation
142
+
143
+ Video generation is asynchronous — submit a job and poll for the result.
144
+
145
+ ```python
146
+ from fotohub import FotoHub
147
+
148
+ client = FotoHub(api_key="fh_live_...")
149
+
150
+ # Start a video generation job
151
+ job = client.generate_video(
152
+ prompt="A drone shot flying over a tropical beach at sunrise",
153
+ duration=5.0,
154
+ aspect_ratio="16:9",
155
+ )
156
+ print(f"Job started: {job.job_id}")
157
+
158
+ # Poll until complete (blocks up to 10 minutes)
159
+ result = client.poll_video(job.job_id, poll_interval=5.0, max_wait=600)
160
+
161
+ if result.status == "completed":
162
+ print(f"Video: {result.video_url}")
163
+ else:
164
+ print(f"Failed: {result.error}")
165
+ ```
166
+
167
+ **Image-to-Video:**
168
+
169
+ ```python
170
+ job = client.generate_video(
171
+ prompt="Camera slowly zooms in, subtle parallax motion",
172
+ image_url="https://example.com/photo.jpg",
173
+ )
174
+ ```
175
+
176
+ ### Music Generation
177
+
178
+ ```python
179
+ from fotohub import FotoHub
180
+
181
+ client = FotoHub(api_key="fh_live_...")
182
+
183
+ result = client.generate_music(
184
+ prompt="Upbeat electronic track with heavy bass and synth arpeggios",
185
+ duration=30.0,
186
+ )
187
+ print(f"Audio: {result.audio.url}")
188
+ print(f"Duration: {result.audio.duration}s")
189
+ print(f"Credits used: {result.credits_used}")
190
+ ```
191
+
192
+ ### Chat Completions (OpenAI-Compatible)
193
+
194
+ ```python
195
+ from fotohub import FotoHub
196
+
197
+ client = FotoHub(api_key="fh_live_...")
198
+
199
+ response = client.chat(
200
+ messages=[
201
+ {"role": "system", "content": "You are a helpful assistant."},
202
+ {"role": "user", "content": "Explain quantum computing in simple terms."},
203
+ ],
204
+ model="gpt-4o",
205
+ temperature=0.7,
206
+ max_tokens=1000,
207
+ )
208
+ print(response.choices[0].message.content)
209
+ ```
210
+
211
+ ### Streaming Chat
212
+
213
+ ```python
214
+ from fotohub import FotoHub
215
+
216
+ client = FotoHub(api_key="fh_live_...")
217
+
218
+ stream = client.chat(
219
+ messages=[{"role": "user", "content": "Write a short poem about the sea."}],
220
+ stream=True,
221
+ )
222
+
223
+ for chunk in stream:
224
+ if chunk.delta_content:
225
+ print(chunk.delta_content, end="", flush=True)
226
+ print()
227
+
228
+ # Or collect the entire response at once:
229
+ stream = client.chat(messages=[...], stream=True)
230
+ full_text = stream.collect()
231
+ ```
232
+
233
+ ### Gabriel AI (Intent Orchestration)
234
+
235
+ ```python
236
+ from fotohub import FotoHub
237
+
238
+ client = FotoHub() # No API key needed
239
+
240
+ result = client.gabriel(
241
+ message="I want to create a logo for my coffee shop",
242
+ context={"user_tier": "pro"},
243
+ )
244
+ print(f"Intent: {result.intent}")
245
+ print(f"Response: {result.response}")
246
+ print(f"Suggested actions: {result.actions}")
247
+ ```
248
+
249
+ ### Storage & S3 Buckets
250
+
251
+ ```python
252
+ from fotohub import FotoHub
253
+
254
+ client = FotoHub(api_key="fh_live_...")
255
+
256
+ # List buckets
257
+ buckets = client.list_buckets()
258
+ for bucket in buckets.buckets:
259
+ print(f"{bucket.name} ({bucket.region}) — {bucket.object_count} objects")
260
+
261
+ # Provision a dedicated S3 bucket
262
+ provision = client.provision_s3_bucket(name="my-media-bucket", region="eu-central-1")
263
+ print(f"Bucket ID: {provision.bucket_id}")
264
+ print(f"Endpoint: {provision.endpoint}")
265
+
266
+ # Presigned upload
267
+ upload = client.presign_upload(
268
+ bucket_id="bucket-123",
269
+ key="images/photo.jpg",
270
+ content_type="image/jpeg",
271
+ expires_in=3600,
272
+ )
273
+ # Use upload.url with PUT request to upload your file
274
+
275
+ # Presigned download
276
+ download = client.presign_download(bucket_id="bucket-123", key="images/photo.jpg")
277
+ print(f"Download: {download.url}")
278
+ ```
279
+
280
+ ### Translation (No Auth Required)
281
+
282
+ ```python
283
+ from fotohub import FotoHub
284
+
285
+ client = FotoHub() # No API key needed
286
+
287
+ result = client.translate(text="Hello, how are you?", target_language="pl")
288
+ print(result.translated_text) # "Cześć, jak się masz?"
289
+ print(result.source_language) # "en"
290
+ ```
291
+
292
+ ### Usage Analytics
293
+
294
+ ```python
295
+ from fotohub import FotoHub
296
+
297
+ client = FotoHub(api_key="fh_live_...")
298
+
299
+ usage = client.get_usage(start_date="2026-07-01", end_date="2026-07-18", category="image")
300
+ print(f"Total credits: {usage.total_credits_used}")
301
+ for record in usage.records:
302
+ print(f" {record.date}: {record.credits_used} credits ({record.request_count} requests)")
303
+ ```
304
+
305
+ ## Async Client
306
+
307
+ Every method is available as an async variant via `AsyncFotoHub`:
308
+
309
+ ```python
310
+ import asyncio
311
+ from fotohub import AsyncFotoHub
312
+
313
+ async def main():
314
+ async with AsyncFotoHub(api_key="fh_live_...") as client:
315
+ # Generate images concurrently
316
+ import asyncio as aio
317
+ results = await aio.gather(
318
+ client.generate_image(prompt="A sunset over mountains"),
319
+ client.generate_image(prompt="A forest in morning mist"),
320
+ client.generate_image(prompt="An ocean wave at golden hour"),
321
+ )
322
+ for r in results:
323
+ print(r.images[0].url)
324
+
325
+ # Async streaming chat
326
+ stream = await client.chat(
327
+ messages=[{"role": "user", "content": "Hello!"}],
328
+ stream=True,
329
+ )
330
+ async for chunk in stream:
331
+ if chunk.delta_content:
332
+ print(chunk.delta_content, end="")
333
+ print()
334
+
335
+ asyncio.run(main())
336
+ ```
337
+
338
+ ## Error Handling
339
+
340
+ The SDK raises typed exceptions for all error conditions:
341
+
342
+ ```python
343
+ from fotohub import (
344
+ FotoHub,
345
+ AuthError,
346
+ InsufficientCreditsError,
347
+ RateLimitError,
348
+ ValidationError,
349
+ ServerError,
350
+ TimeoutError,
351
+ VideoJobTimeoutError,
352
+ )
353
+
354
+ client = FotoHub(api_key="fh_live_...")
355
+
356
+ try:
357
+ result = client.generate_image(prompt="test")
358
+ except AuthError as e:
359
+ # Invalid or missing API key (HTTP 401/403)
360
+ print(f"Authentication failed: {e}")
361
+ except InsufficientCreditsError as e:
362
+ # Not enough credits (HTTP 402)
363
+ print(f"Need credits! Required: {e.credits_required}, Available: {e.credits_available}")
364
+ except RateLimitError as e:
365
+ # Too many requests (HTTP 429)
366
+ print(f"Rate limited. Retry after {e.retry_after}s")
367
+ except ValidationError as e:
368
+ # Invalid parameters (HTTP 400/422)
369
+ print(f"Invalid request: {e.errors}")
370
+ except ServerError as e:
371
+ # Server error (HTTP 5xx)
372
+ print(f"Server error: {e}")
373
+ except TimeoutError as e:
374
+ # Request timed out
375
+ print(f"Timed out: {e}")
376
+ except VideoJobTimeoutError as e:
377
+ # Video polling exceeded max_wait
378
+ print(f"Video job {e.job_id} timed out")
379
+ ```
380
+
381
+ ### Exception Hierarchy
382
+
383
+ | Exception | HTTP Status | Description |
384
+ |-----------|-------------|-------------|
385
+ | `FotoHubError` | Any | Base exception for all SDK errors |
386
+ | `AuthError` | 401, 403 | Invalid or missing API key |
387
+ | `InsufficientCreditsError` | 402 | Account lacks sufficient credits |
388
+ | `RateLimitError` | 429 | Rate limit exceeded |
389
+ | `ValidationError` | 400, 422 | Invalid request parameters |
390
+ | `ServerError` | 5xx | Server-side error |
391
+ | `TimeoutError` | — | Request timed out or connection failed |
392
+ | `VideoJobTimeoutError` | — | Video polling exceeded `max_wait` |
393
+
394
+ All exceptions include `status_code` and `response_body` attributes for debugging.
395
+
396
+ ## Configuration
397
+
398
+ ```python
399
+ from fotohub import FotoHub
400
+
401
+ client = FotoHub(
402
+ api_key="fh_live_...",
403
+ base_url="https://apis.fotohub.app", # Custom API endpoint
404
+ timeout=120.0, # Request timeout (seconds)
405
+ max_retries=3, # Max retry attempts
406
+ )
407
+ ```
408
+
409
+ ### Environment Variables
410
+
411
+ | Variable | Description | Default |
412
+ |----------|-------------|---------|
413
+ | `FOTOHUB_API_KEY` | API key for authentication | — |
414
+ | `FOTOHUB_BASE_URL` | Override API base URL | `https://apis.fotohub.app` |
415
+
416
+ ### Retry Behavior
417
+
418
+ The SDK automatically retries on transient failures:
419
+
420
+ - **HTTP 429** — Rate limit (respects `Retry-After` header)
421
+ - **HTTP 500, 502, 503, 504** — Server errors
422
+ - **Connection timeouts** — Network failures
423
+
424
+ Backoff schedule: `0.5s → 1s → 2s → 4s → ...` (capped at 30s).
425
+
426
+ ## Context Managers
427
+
428
+ Both clients support context managers for automatic resource cleanup:
429
+
430
+ ```python
431
+ # Sync
432
+ with FotoHub(api_key="fh_live_...") as client:
433
+ result = client.generate_image(prompt="test")
434
+
435
+ # Async
436
+ async with AsyncFotoHub(api_key="fh_live_...") as client:
437
+ result = await client.generate_image(prompt="test")
438
+ ```
439
+
440
+ ## API Reference
441
+
442
+ ### `FotoHub` / `AsyncFotoHub`
443
+
444
+ | Method | Description |
445
+ |--------|-------------|
446
+ | `generate_image(prompt, *, model, width, height, num_images, ...)` | Generate images from text |
447
+ | `generate_video(prompt, *, model, duration, image_url, aspect_ratio, ...)` | Start video generation job |
448
+ | `poll_video(job_id, *, poll_interval, max_wait)` | Poll video job until completion |
449
+ | `generate_music(prompt, *, model, duration, ...)` | Generate music from text |
450
+ | `chat(messages, *, model, stream, temperature, max_tokens, ...)` | Chat completion (streaming supported) |
451
+ | `translate(text, *, target_language, source_language)` | Translate text between languages |
452
+ | `gabriel(message, *, context)` | Intent orchestration via Gabriel AI |
453
+ | `get_usage(*, start_date, end_date, category)` | Retrieve usage analytics |
454
+ | `list_buckets()` | List storage buckets |
455
+ | `create_bucket(name, *, region)` | Create a storage bucket |
456
+ | `provision_s3_bucket(*, name, region)` | Provision a dedicated S3 bucket |
457
+ | `presign_upload(bucket_id, *, key, content_type, expires_in)` | Get presigned upload URL |
458
+ | `presign_download(bucket_id, *, key, expires_in)` | Get presigned download URL |
459
+
460
+ ## Type Safety
461
+
462
+ The SDK ships with a `py.typed` marker and full type annotations. All response models are Pydantic v2 `BaseModel` subclasses:
463
+
464
+ ```python
465
+ from fotohub import FotoHub, ImageGenerationResponse, ImageResult
466
+
467
+ client = FotoHub(api_key="fh_live_...")
468
+
469
+ result: ImageGenerationResponse = client.generate_image(prompt="test")
470
+ image: ImageResult = result.images[0]
471
+
472
+ # All fields are fully typed
473
+ print(image.url) # str
474
+ print(image.width) # int
475
+ print(image.credits_used) # float
476
+ print(image.generation_time_ms) # Optional[int]
477
+ ```
478
+
479
+ ## Requirements
480
+
481
+ | Dependency | Version |
482
+ |-----------|---------|
483
+ | Python | >= 3.9 |
484
+ | httpx | >= 0.24 |
485
+ | pydantic | >= 2.0 |
486
+
487
+ ## Contributing
488
+
489
+ We welcome contributions! To get started:
490
+
491
+ ```bash
492
+ # Clone the repository
493
+ git clone https://github.com/fotohubapp/sdk-python.git
494
+ cd sdk-python
495
+
496
+ # Install development dependencies
497
+ pip install -e ".[dev]"
498
+
499
+ # Run tests
500
+ pytest
501
+
502
+ # Run linting
503
+ ruff check .
504
+
505
+ # Run type checking
506
+ mypy fotohub/
507
+ ```
508
+
509
+ Please ensure all tests pass and type checks are clean before submitting a pull request.
510
+
511
+ ## Links
512
+
513
+ - [Documentation](https://docs.fotohub.app/sdk/python)
514
+ - [API Reference](https://docs.fotohub.app/api)
515
+ - [FOTOhub Platform](https://fotohub.app)
516
+ - [Changelog](https://github.com/fotohubapp/sdk-python/releases)
517
+ - [Issue Tracker](https://github.com/fotohubapp/sdk-python/issues)
518
+
519
+ ## License
520
+
521
+ MIT License. See [LICENSE](LICENSE) for details.