runapi-gpt-image-2 0.1.0__py3-none-any.whl
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.
- runapi/gpt_image_2/__init__.py +24 -0
- runapi/gpt_image_2/client.py +29 -0
- runapi/gpt_image_2/py.typed +0 -0
- runapi/gpt_image_2/resources/__init__.py +4 -0
- runapi/gpt_image_2/resources/edit_image.py +77 -0
- runapi/gpt_image_2/resources/text_to_image.py +73 -0
- runapi/gpt_image_2/types.py +36 -0
- runapi_gpt_image_2-0.1.0.dist-info/METADATA +87 -0
- runapi_gpt_image_2-0.1.0.dist-info/RECORD +10 -0
- runapi_gpt_image_2-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""GPT Image 2 client for RunAPI."""
|
|
2
|
+
|
|
3
|
+
from runapi.core import (
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
InsufficientCreditsError,
|
|
6
|
+
NotFoundError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
TaskFailedError,
|
|
9
|
+
TaskTimeoutError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .client import GptImage2Client
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"GptImage2Client",
|
|
17
|
+
"AuthenticationError",
|
|
18
|
+
"RateLimitError",
|
|
19
|
+
"InsufficientCreditsError",
|
|
20
|
+
"NotFoundError",
|
|
21
|
+
"ValidationError",
|
|
22
|
+
"TaskFailedError",
|
|
23
|
+
"TaskTimeoutError",
|
|
24
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""GPT Image 2 client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from runapi.core import ClientOptions, HttpClient, resolve_api_key
|
|
8
|
+
|
|
9
|
+
from .resources.edit_image import EditImage
|
|
10
|
+
from .resources.text_to_image import TextToImage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GptImage2Client:
|
|
14
|
+
"""GPT Image 2 text-to-image and edit-image client.
|
|
15
|
+
|
|
16
|
+
Example::
|
|
17
|
+
|
|
18
|
+
client = GptImage2Client(api_key="sk-...")
|
|
19
|
+
result = client.text_to_image.run(
|
|
20
|
+
model="gpt-image-2", prompt="A futuristic cityscape at night"
|
|
21
|
+
)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, api_key: Optional[str] = None, **options: Any) -> None:
|
|
25
|
+
resolved_api_key = resolve_api_key(api_key)
|
|
26
|
+
client_options = ClientOptions(api_key=resolved_api_key, **options)
|
|
27
|
+
http = client_options.http_client or HttpClient(client_options)
|
|
28
|
+
self.text_to_image = TextToImage(http)
|
|
29
|
+
self.edit_image = EditImage(http)
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""GPT Image 2 edit-image resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import (
|
|
10
|
+
ASPECT_RATIOS,
|
|
11
|
+
EDIT_MODELS,
|
|
12
|
+
RESOLUTIONS,
|
|
13
|
+
CompletedEditImageResponse,
|
|
14
|
+
EditImageResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EditImage(Resource):
|
|
19
|
+
"""Edit images from a prompt and source images with GPT Image 2."""
|
|
20
|
+
|
|
21
|
+
ENDPOINT = "/api/v1/gpt_image_2/edit_image"
|
|
22
|
+
|
|
23
|
+
RESPONSE_CLASS = EditImageResponse
|
|
24
|
+
COMPLETED_RESPONSE_CLASS = CompletedEditImageResponse
|
|
25
|
+
|
|
26
|
+
def run(self, **params: Any) -> Any:
|
|
27
|
+
"""Create an edit-image task and poll until it completes.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
**params: Edit-image parameters (model, prompt, ...).
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The completed edit-image response.
|
|
34
|
+
"""
|
|
35
|
+
task = self.create(**params)
|
|
36
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
37
|
+
|
|
38
|
+
def create(self, **params: Any) -> Any:
|
|
39
|
+
"""Create an edit-image task and return immediately with an id.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
**params: Edit-image parameters (model, prompt, ...).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The task creation result with an id.
|
|
46
|
+
"""
|
|
47
|
+
compacted = self._compact_params(params)
|
|
48
|
+
self._validate_params(compacted)
|
|
49
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
50
|
+
|
|
51
|
+
def get(self, id: str) -> Any:
|
|
52
|
+
"""Fetch the current status of an edit-image task.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
id: Task id.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The current edit-image status.
|
|
59
|
+
"""
|
|
60
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
61
|
+
|
|
62
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
63
|
+
if not params.get("model"):
|
|
64
|
+
raise ValidationError("model is required")
|
|
65
|
+
if not params.get("prompt"):
|
|
66
|
+
raise ValidationError("prompt is required")
|
|
67
|
+
|
|
68
|
+
model = params.get("model")
|
|
69
|
+
if model not in EDIT_MODELS:
|
|
70
|
+
raise ValidationError(f"Invalid model: {model}. Must be: {', '.join(EDIT_MODELS)}")
|
|
71
|
+
|
|
72
|
+
urls = params.get("source_image_urls")
|
|
73
|
+
if urls is None or (hasattr(urls, "__len__") and len(urls) == 0):
|
|
74
|
+
raise ValidationError("source_image_urls is required for edit image requests")
|
|
75
|
+
|
|
76
|
+
self._validate_optional(params, "aspect_ratio", ASPECT_RATIOS)
|
|
77
|
+
self._validate_optional(params, "output_resolution", RESOLUTIONS)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""GPT Image 2 text-to-image resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import (
|
|
10
|
+
ASPECT_RATIOS,
|
|
11
|
+
GENERATION_MODELS,
|
|
12
|
+
RESOLUTIONS,
|
|
13
|
+
CompletedTextToImageResponse,
|
|
14
|
+
TextToImageResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TextToImage(Resource):
|
|
19
|
+
"""Generate images from text prompts with GPT Image 2."""
|
|
20
|
+
|
|
21
|
+
ENDPOINT = "/api/v1/gpt_image_2/text_to_image"
|
|
22
|
+
|
|
23
|
+
RESPONSE_CLASS = TextToImageResponse
|
|
24
|
+
COMPLETED_RESPONSE_CLASS = CompletedTextToImageResponse
|
|
25
|
+
|
|
26
|
+
def run(self, **params: Any) -> Any:
|
|
27
|
+
"""Create a text-to-image task and poll until it completes.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
**params: Text-to-image parameters (model, prompt, ...).
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The completed text-to-image response.
|
|
34
|
+
"""
|
|
35
|
+
task = self.create(**params)
|
|
36
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
37
|
+
|
|
38
|
+
def create(self, **params: Any) -> Any:
|
|
39
|
+
"""Create a text-to-image task and return immediately with an id.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
**params: Text-to-image parameters (model, prompt, ...).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The task creation result with an id.
|
|
46
|
+
"""
|
|
47
|
+
compacted = self._compact_params(params)
|
|
48
|
+
self._validate_params(compacted)
|
|
49
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
50
|
+
|
|
51
|
+
def get(self, id: str) -> Any:
|
|
52
|
+
"""Fetch the current status of a text-to-image task.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
id: Task id.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The current text-to-image status.
|
|
59
|
+
"""
|
|
60
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
61
|
+
|
|
62
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
63
|
+
if not params.get("model"):
|
|
64
|
+
raise ValidationError("model is required")
|
|
65
|
+
if not params.get("prompt"):
|
|
66
|
+
raise ValidationError("prompt is required")
|
|
67
|
+
|
|
68
|
+
model = params.get("model")
|
|
69
|
+
if model not in GENERATION_MODELS:
|
|
70
|
+
raise ValidationError(f"Invalid model: {model}. Must be: {', '.join(GENERATION_MODELS)}")
|
|
71
|
+
|
|
72
|
+
self._validate_optional(params, "aspect_ratio", ASPECT_RATIOS)
|
|
73
|
+
self._validate_optional(params, "output_resolution", RESOLUTIONS)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""GPT Image 2 model lists, enums, and response models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from runapi.core import BaseModel, TaskResponse, optional, required
|
|
6
|
+
|
|
7
|
+
MODELS = ["gpt-image-2"]
|
|
8
|
+
GENERATION_MODELS = MODELS
|
|
9
|
+
EDIT_MODELS = MODELS
|
|
10
|
+
|
|
11
|
+
ASPECT_RATIOS = ["auto", "1:1", "9:16", "16:9", "4:3", "3:4"]
|
|
12
|
+
RESOLUTIONS = ["1k", "2k", "4k"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Image(BaseModel):
|
|
16
|
+
url = optional(str)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TextToImageResponse(TaskResponse):
|
|
20
|
+
"""Task status/result for GPT Image 2 text-to-image."""
|
|
21
|
+
id = required(str)
|
|
22
|
+
status = optional(str, enum=lambda: TaskResponse.Status.ALL)
|
|
23
|
+
images = optional([lambda: Image])
|
|
24
|
+
error = optional(str)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
EditImageResponse = TextToImageResponse
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CompletedTextToImageResponse(TextToImageResponse):
|
|
31
|
+
"""Narrowed response from ``run()`` once polling observes completion."""
|
|
32
|
+
|
|
33
|
+
images = required([lambda: Image])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
CompletedEditImageResponse = CompletedTextToImageResponse
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runapi-gpt-image-2
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GPT Image 2 text-to-image and edit-image client for RunAPI
|
|
5
|
+
Project-URL: Homepage, https://runapi.ai/models/gpt-image-2
|
|
6
|
+
Project-URL: Documentation, https://runapi.ai/docs#sdk-gpt-image-2
|
|
7
|
+
Author-email: RunAPI <contact@runapi.ai>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: ai,edit-image,gpt-image-2,runapi,sdk,text-to-image
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Requires-Dist: runapi-core
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# GPT Image 2 Python SDK for RunAPI
|
|
15
|
+
|
|
16
|
+
The GPT Image 2 Python SDK is the language-specific package for GPT Image 2 on
|
|
17
|
+
RunAPI. Use it for text-to-image and edit-image flows when your application needs
|
|
18
|
+
JSON request bodies, task status lookup, and consistent RunAPI errors in Python.
|
|
19
|
+
|
|
20
|
+
For model details, use https://runapi.ai/models/gpt-image-2; for API reference,
|
|
21
|
+
use https://runapi.ai/docs#gpt-image-2; for SDK docs, use
|
|
22
|
+
https://runapi.ai/docs#sdk-gpt-image-2.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install runapi-gpt-image-2
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from runapi.gpt_image_2 import GptImage2Client
|
|
34
|
+
|
|
35
|
+
client = GptImage2Client() # reads RUNAPI_API_KEY, or pass api_key="sk-..."
|
|
36
|
+
|
|
37
|
+
task = client.text_to_image.create(
|
|
38
|
+
model="gpt-image-2",
|
|
39
|
+
prompt="A futuristic cityscape at night, cinematic",
|
|
40
|
+
aspect_ratio="16:9",
|
|
41
|
+
output_resolution="2k",
|
|
42
|
+
)
|
|
43
|
+
status = client.text_to_image.get(task.id)
|
|
44
|
+
|
|
45
|
+
edit = client.edit_image.create(
|
|
46
|
+
model="gpt-image-2",
|
|
47
|
+
prompt="Transform into oil painting style",
|
|
48
|
+
source_image_urls=["https://example.com/source.jpg"],
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use `create` to submit a task and return quickly, `get` to fetch the latest task
|
|
53
|
+
state, and `run` to create and poll until completion:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
result = client.text_to_image.run(
|
|
57
|
+
model="gpt-image-2",
|
|
58
|
+
prompt="A serene mountain lake at dawn",
|
|
59
|
+
)
|
|
60
|
+
print(result.images[0].url)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
In web request handlers, prefer `create` plus webhook or later `get` polling so a
|
|
64
|
+
worker is not held open.
|
|
65
|
+
|
|
66
|
+
RunAPI-generated file URLs are temporary. Download and store generated images in
|
|
67
|
+
your own durable storage within 7 days; do not treat returned URLs as long-term
|
|
68
|
+
assets.
|
|
69
|
+
|
|
70
|
+
## Language notes
|
|
71
|
+
|
|
72
|
+
Pass parameters as keyword arguments and catch the `runapi.gpt_image_2` error
|
|
73
|
+
classes when building image jobs or scripts. The available resources are
|
|
74
|
+
`text_to_image` and `edit_image`. Keep `RUNAPI_API_KEY` in the environment or
|
|
75
|
+
your secret manager; never commit API keys or callback secrets.
|
|
76
|
+
|
|
77
|
+
## Links
|
|
78
|
+
|
|
79
|
+
- Model page: https://runapi.ai/models/gpt-image-2
|
|
80
|
+
- SDK docs: https://runapi.ai/docs#sdk-gpt-image-2
|
|
81
|
+
- Product docs: https://runapi.ai/docs#gpt-image-2
|
|
82
|
+
- Pricing and rate limits: https://runapi.ai/models/gpt-image-2
|
|
83
|
+
- Full catalog: https://runapi.ai/models
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
Licensed under the Apache License, Version 2.0.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
runapi/gpt_image_2/__init__.py,sha256=xU6VDYR1FBLH0EvUpTiR5o_63oWxFTHE9bfM20OMaho,471
|
|
2
|
+
runapi/gpt_image_2/client.py,sha256=E4K4o_PTacNeC6X0jF4vP-p_7ye-WY-HRT4r6SwPubg,910
|
|
3
|
+
runapi/gpt_image_2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
runapi/gpt_image_2/types.py,sha256=5_nUjQdKlzdP8sxGg8NbgNngaLCjOo_6ZYC7S5DzM_U,918
|
|
5
|
+
runapi/gpt_image_2/resources/__init__.py,sha256=cWTZRb2Hewznm9agkhjkcKjEPbJO2YaQOW1fuixLjtw,113
|
|
6
|
+
runapi/gpt_image_2/resources/edit_image.py,sha256=Fg4mTc8JSFe2wBHkhWSvTrj8ipEyHaRpjhYpWdFhp_E,2390
|
|
7
|
+
runapi/gpt_image_2/resources/text_to_image.py,sha256=F4Q4HtbZT3RZ1cSIZLPAphJZooMUzPLOHlMUMTB5lq0,2219
|
|
8
|
+
runapi_gpt_image_2-0.1.0.dist-info/METADATA,sha256=_YfFWGpd6VQsNA26nZ0rG4e2sNHmw88GABVzsABW9BY,2742
|
|
9
|
+
runapi_gpt_image_2-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
10
|
+
runapi_gpt_image_2-0.1.0.dist-info/RECORD,,
|