lollms-client 1.1.3__py3-none-any.whl → 1.3.1__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.
Potentially problematic release.
This version of lollms-client might be problematic. Click here for more details.
- lollms_client/__init__.py +1 -1
- lollms_client/llm_bindings/lollms/__init__.py +2 -2
- lollms_client/llm_bindings/ollama/__init__.py +80 -23
- lollms_client/llm_bindings/openai/__init__.py +3 -3
- lollms_client/lollms_core.py +286 -132
- lollms_client/lollms_discussion.py +419 -147
- lollms_client/lollms_tti_binding.py +32 -82
- lollms_client/tti_bindings/diffusers/__init__.py +372 -315
- lollms_client/tti_bindings/openai/__init__.py +124 -0
- {lollms_client-1.1.3.dist-info → lollms_client-1.3.1.dist-info}/METADATA +1 -1
- {lollms_client-1.1.3.dist-info → lollms_client-1.3.1.dist-info}/RECORD +14 -14
- lollms_client/tti_bindings/dalle/__init__.py +0 -454
- {lollms_client-1.1.3.dist-info → lollms_client-1.3.1.dist-info}/WHEEL +0 -0
- {lollms_client-1.1.3.dist-info → lollms_client-1.3.1.dist-info}/licenses/LICENSE +0 -0
- {lollms_client-1.1.3.dist-info → lollms_client-1.3.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional, List, Dict, Any, Union
|
|
3
|
+
import base64
|
|
4
|
+
import requests
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from ascii_colors import trace_exception
|
|
7
|
+
from openai import OpenAI
|
|
8
|
+
from lollms_client.lollms_tti_binding import LollmsTTIBinding
|
|
9
|
+
|
|
10
|
+
BindingName = "OpenAITTIBinding"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenAITTIBinding(LollmsTTIBinding):
|
|
14
|
+
"""
|
|
15
|
+
OpenAI Text-to-Image (TTI) binding for LoLLMS.
|
|
16
|
+
|
|
17
|
+
This binding provides access to OpenAI's image generation models
|
|
18
|
+
(`gpt-image-1`, `dall-e-2`, `dall-e-3`).
|
|
19
|
+
|
|
20
|
+
Parameters can be set globally at initialization or per-request during
|
|
21
|
+
generation. Runtime parameters override initialization ones.
|
|
22
|
+
|
|
23
|
+
----------------------------
|
|
24
|
+
Global parameters (init):
|
|
25
|
+
----------------------------
|
|
26
|
+
- model (str): Model name. ["gpt-image-1", "dall-e-2", "dall-e-3"]. Default: "gpt-image-1".
|
|
27
|
+
- api_key (str): OpenAI API key. If empty, uses OPENAI_API_KEY from environment.
|
|
28
|
+
- size (str): Default image size. ["256x256", "512x512", "1024x1024", "2048x2048"]. Default: "1024x1024".
|
|
29
|
+
- n (int): Default number of images per request (max 10). Default: 1.
|
|
30
|
+
- quality (str): Image quality. ["standard", "hd"]. Only supported by "dall-e-3". Default: "standard".
|
|
31
|
+
|
|
32
|
+
----------------------------
|
|
33
|
+
Runtime parameters (kwargs in generate or edit):
|
|
34
|
+
----------------------------
|
|
35
|
+
- prompt (str): Required. The text prompt for image generation or editing.
|
|
36
|
+
- size (str): Output size. Overrides global. Default: global "size".
|
|
37
|
+
- n (int): Number of images to generate. Overrides global. Default: global "n".
|
|
38
|
+
- quality (str): Image quality. Only for dall-e-3. Overrides global. Default: global "quality".
|
|
39
|
+
- image (Union[str, Path]): For edit. Input image (base64, URL, or file path).
|
|
40
|
+
- mask (Union[str, Path]): For edit. Optional mask image.
|
|
41
|
+
|
|
42
|
+
Methods:
|
|
43
|
+
--------
|
|
44
|
+
- generate_image(prompt: str, **kwargs) -> List[bytes]:
|
|
45
|
+
Generates images from a prompt.
|
|
46
|
+
|
|
47
|
+
- edit_image(prompt: str, image: Union[str, Path], mask: Optional[Union[str, Path]] = None, **kwargs) -> List[bytes]:
|
|
48
|
+
Edits an existing image using a prompt and optional mask.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
model: str = "gpt-image-1",
|
|
54
|
+
api_key: Optional[str] = None,
|
|
55
|
+
size: str = "1024x1024",
|
|
56
|
+
n: int = 1,
|
|
57
|
+
quality: str = "standard",
|
|
58
|
+
**kwargs,
|
|
59
|
+
):
|
|
60
|
+
self.client = OpenAI(api_key=api_key)
|
|
61
|
+
self.global_params = {
|
|
62
|
+
"model": model,
|
|
63
|
+
"size": size,
|
|
64
|
+
"n": n,
|
|
65
|
+
"quality": quality,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def _resolve_param(self, name: str, kwargs: Dict[str, Any], default: Any) -> Any:
|
|
69
|
+
"""Resolve a parameter from runtime kwargs, global config, or default."""
|
|
70
|
+
return kwargs.get(name, self.global_params.get(name, default))
|
|
71
|
+
|
|
72
|
+
def _load_image(self, image: Union[str, Path]) -> Any:
|
|
73
|
+
"""Helper to load an image from path, URL, or base64 string."""
|
|
74
|
+
if isinstance(image, Path) or Path(str(image)).exists():
|
|
75
|
+
with open(image, "rb") as f:
|
|
76
|
+
return f.read()
|
|
77
|
+
if isinstance(image, str) and image.startswith("http"):
|
|
78
|
+
return image
|
|
79
|
+
if isinstance(image, str) and image.strip().startswith(("iVBOR", "/9j/")): # base64
|
|
80
|
+
return base64.b64decode(image)
|
|
81
|
+
return image
|
|
82
|
+
|
|
83
|
+
def generate_image(self, prompt: str, **kwargs) -> List[bytes]:
|
|
84
|
+
model = self._resolve_param("model", kwargs, "gpt-image-1")
|
|
85
|
+
size = self._resolve_param("size", kwargs, "1024x1024")
|
|
86
|
+
n = self._resolve_param("n", kwargs, 1)
|
|
87
|
+
quality = self._resolve_param("quality", kwargs, "standard")
|
|
88
|
+
|
|
89
|
+
response = self.client.images.generate(
|
|
90
|
+
model=model,
|
|
91
|
+
prompt=prompt,
|
|
92
|
+
size=size,
|
|
93
|
+
n=n,
|
|
94
|
+
**({"quality": quality} if model == "dall-e-3" else {}),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return [base64.b64decode(img.b64_json) for img in response.data]
|
|
98
|
+
|
|
99
|
+
def edit_image(
|
|
100
|
+
self,
|
|
101
|
+
prompt: str,
|
|
102
|
+
image: Union[str, Path],
|
|
103
|
+
mask: Optional[Union[str, Path]] = None,
|
|
104
|
+
**kwargs,
|
|
105
|
+
) -> List[bytes]:
|
|
106
|
+
model = self._resolve_param("model", kwargs, "gpt-image-1")
|
|
107
|
+
size = self._resolve_param("size", kwargs, "1024x1024")
|
|
108
|
+
n = self._resolve_param("n", kwargs, 1)
|
|
109
|
+
quality = self._resolve_param("quality", kwargs, "standard")
|
|
110
|
+
|
|
111
|
+
image_data = self._load_image(image)
|
|
112
|
+
mask_data = self._load_image(mask) if mask else None
|
|
113
|
+
|
|
114
|
+
response = self.client.images.edit(
|
|
115
|
+
model=model,
|
|
116
|
+
prompt=prompt,
|
|
117
|
+
image=image_data,
|
|
118
|
+
mask=mask_data,
|
|
119
|
+
size=size,
|
|
120
|
+
n=n,
|
|
121
|
+
**({"quality": quality} if model == "dall-e-3" else {}),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return [base64.b64decode(img.b64_json) for img in response.data]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
lollms_client/__init__.py,sha256=
|
|
1
|
+
lollms_client/__init__.py,sha256=SMot7i85VNJMIL7Zf7trWYlvTgWYlRmwZpF8qchlIyI,1146
|
|
2
2
|
lollms_client/lollms_config.py,sha256=goEseDwDxYJf3WkYJ4IrLXwg3Tfw73CXV2Avg45M_hE,21876
|
|
3
|
-
lollms_client/lollms_core.py,sha256=
|
|
4
|
-
lollms_client/lollms_discussion.py,sha256=
|
|
3
|
+
lollms_client/lollms_core.py,sha256=QIsKQfSWDSD2gzVrlVZmis3VdEf4_95d4ynYGB4DIQI,171085
|
|
4
|
+
lollms_client/lollms_discussion.py,sha256=4vOnXJp4nLDtL2gRmnkTB4-mjYyIHsgp35pRSJPeT9U,117527
|
|
5
5
|
lollms_client/lollms_js_analyzer.py,sha256=01zUvuO2F_lnUe_0NLxe1MF5aHE1hO8RZi48mNPv-aw,8361
|
|
6
6
|
lollms_client/lollms_llm_binding.py,sha256=5-Vknm0YILPd6ZiwZynsXMfns__Yd_1tDDc2fciRiiA,25020
|
|
7
7
|
lollms_client/lollms_mcp_binding.py,sha256=psb27A23VFWDfZsR2WUbQXQxiZDW5yfOak6ZtbMfszI,10222
|
|
@@ -9,7 +9,7 @@ lollms_client/lollms_mcp_security.py,sha256=FhVTDhSBjksGEZnopVnjFmEF5dv7D8bBTqoa
|
|
|
9
9
|
lollms_client/lollms_personality.py,sha256=O-9nqZhazcITOkxjT24ENTxTmIoZLgqIsQ9WtWs0Id0,8719
|
|
10
10
|
lollms_client/lollms_python_analyzer.py,sha256=7gf1fdYgXCOkPUkBAPNmr6S-66hMH4_KonOMsADASxc,10246
|
|
11
11
|
lollms_client/lollms_stt_binding.py,sha256=jAUhLouEhh2hmm1bK76ianfw_6B59EHfY3FmLv6DU-g,5111
|
|
12
|
-
lollms_client/lollms_tti_binding.py,sha256=
|
|
12
|
+
lollms_client/lollms_tti_binding.py,sha256=B38nzBCSPV9jVRZa-x8W7l9nJEW0RyS1MMJoueb8kt0,8519
|
|
13
13
|
lollms_client/lollms_ttm_binding.py,sha256=FjVVSNXOZXK1qvcKEfxdiX6l2b4XdGOSNnZ0utAsbDg,4167
|
|
14
14
|
lollms_client/lollms_tts_binding.py,sha256=5cJYECj8PYLJAyB6SEH7_fhHYK3Om-Y3arkygCnZ24o,4342
|
|
15
15
|
lollms_client/lollms_ttv_binding.py,sha256=KkTaHLBhEEdt4sSVBlbwr5i_g_TlhcrwrT-7DjOsjWQ,4131
|
|
@@ -25,12 +25,12 @@ lollms_client/llm_bindings/groq/__init__.py,sha256=EGrMh9vuCoM4pskDw8ydfsAWYgEb4
|
|
|
25
25
|
lollms_client/llm_bindings/hugging_face_inference_api/__init__.py,sha256=SFcj5XQTDmN9eR4of82IgQa9iRYZaGlF6rMlF5S5wWg,13938
|
|
26
26
|
lollms_client/llm_bindings/litellm/__init__.py,sha256=lRH4VfZMUG5JCCj6a7hk2PTfSyDowAu-ujLOM-XPl-8,12756
|
|
27
27
|
lollms_client/llm_bindings/llamacpp/__init__.py,sha256=4CbNYpfquVEgfsxuLsxQta_dZRSpbSBL-VWhyDMdBAc,59379
|
|
28
|
-
lollms_client/llm_bindings/lollms/__init__.py,sha256=
|
|
28
|
+
lollms_client/llm_bindings/lollms/__init__.py,sha256=7DgTGHtrFjhRnjx0YYlNTip2p5TSV-_4GN00ekEUd3g,24855
|
|
29
29
|
lollms_client/llm_bindings/lollms_webui/__init__.py,sha256=iuDfhZZoLC-PDEPLHrcjk5-962S5c7OeCI7PMdJxI_A,17753
|
|
30
30
|
lollms_client/llm_bindings/mistral/__init__.py,sha256=cddz9xIj8NRFLKHe2JMxzstpUrNIu5s9juci3mhiHfo,14133
|
|
31
|
-
lollms_client/llm_bindings/ollama/__init__.py,sha256=
|
|
31
|
+
lollms_client/llm_bindings/ollama/__init__.py,sha256=a6cgzXPuo8ZLhIZHJFy8QF0n5ZTk0X4OC1JSyXG1enk,46013
|
|
32
32
|
lollms_client/llm_bindings/open_router/__init__.py,sha256=cAFWtCWJx0WjIe1w2JReCf6WlAZjrXYA4jZ8l3zqxMs,14915
|
|
33
|
-
lollms_client/llm_bindings/openai/__init__.py,sha256=
|
|
33
|
+
lollms_client/llm_bindings/openai/__init__.py,sha256=ElLbtHLwR61Uj3W6G4g6QIhxtCqUGOCQBYwhQyN60us,26142
|
|
34
34
|
lollms_client/llm_bindings/openllm/__init__.py,sha256=RC9dVeopslS-zXTsSJ7VC4iVsKgZCBwfmccmr_LCHA0,29971
|
|
35
35
|
lollms_client/llm_bindings/pythonllamacpp/__init__.py,sha256=ZTuVa5ngu9GPVImjs_g8ArV7Bx7a1Rze518Tz8AFJ3U,31807
|
|
36
36
|
lollms_client/llm_bindings/tensor_rt/__init__.py,sha256=xiT-JAyNI_jo6CE0nle9Xoc7U8-UHAfEHrnCwmDTiOE,32023
|
|
@@ -48,10 +48,10 @@ lollms_client/stt_bindings/lollms/__init__.py,sha256=9Vmn1sQQZKLGLe7nZnc-0LnNeSY
|
|
|
48
48
|
lollms_client/stt_bindings/whisper/__init__.py,sha256=1Ej67GdRKBy1bba14jMaYDYHiZkxJASkWm5eF07ztDQ,15363
|
|
49
49
|
lollms_client/stt_bindings/whispercpp/__init__.py,sha256=xSAQRjAhljak3vWCpkP0Vmdb6WmwTzPjXyaIB85KLGU,21439
|
|
50
50
|
lollms_client/tti_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
-
lollms_client/tti_bindings/
|
|
52
|
-
lollms_client/tti_bindings/diffusers/__init__.py,sha256=YI9-VoqdQafoQgkSS0e5GhPNd30CxfI9m3AzdhNWhbs,37021
|
|
51
|
+
lollms_client/tti_bindings/diffusers/__init__.py,sha256=e1qrhiAQI_J-C_PKGIz2EEmtonw-uKAa9bw2N4qUP68,40092
|
|
53
52
|
lollms_client/tti_bindings/gemini/__init__.py,sha256=f9fPuqnrBZ1Z-obcoP6EVvbEXNbNCSg21cd5efLCk8U,16707
|
|
54
53
|
lollms_client/tti_bindings/lollms/__init__.py,sha256=5Tnsn4b17djvieQkcjtIDBm3qf0pg5ZWWov-4_2wmo0,8762
|
|
54
|
+
lollms_client/tti_bindings/openai/__init__.py,sha256=YWJolJSQfIzTJvrLQVe8rQewP7rddf6z87g4rnp-lTs,4932
|
|
55
55
|
lollms_client/ttm_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
lollms_client/ttm_bindings/audiocraft/__init__.py,sha256=a0k6wTrHth6GaVOiNnVboeFY3oKVvCQPbQlqO38XEyc,14328
|
|
57
57
|
lollms_client/ttm_bindings/bark/__init__.py,sha256=Pr3ou2a-7hNYDqbkxrAbghZpO5HvGUhz7e-7VGXIHHA,18976
|
|
@@ -63,8 +63,8 @@ lollms_client/tts_bindings/piper_tts/__init__.py,sha256=0IEWG4zH3_sOkSb9WbZzkeV5
|
|
|
63
63
|
lollms_client/tts_bindings/xtts/__init__.py,sha256=FgcdUH06X6ZR806WQe5ixaYx0QoxtAcOgYo87a2qxYc,18266
|
|
64
64
|
lollms_client/ttv_bindings/__init__.py,sha256=UZ8o2izQOJLQgtZ1D1cXoNST7rzqW22rL2Vufc7ddRc,3141
|
|
65
65
|
lollms_client/ttv_bindings/lollms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
|
-
lollms_client-1.1.
|
|
67
|
-
lollms_client-1.1.
|
|
68
|
-
lollms_client-1.1.
|
|
69
|
-
lollms_client-1.1.
|
|
70
|
-
lollms_client-1.1.
|
|
66
|
+
lollms_client-1.3.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
67
|
+
lollms_client-1.3.1.dist-info/METADATA,sha256=vxRJoe8JCZ1v_mnehbmuOTYEzDLjlWVYKk2hL9chuS8,58549
|
|
68
|
+
lollms_client-1.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
69
|
+
lollms_client-1.3.1.dist-info/top_level.txt,sha256=Bk_kz-ri6Arwsk7YG-T5VsRorV66uVhcHGvb_g2WqgE,14
|
|
70
|
+
lollms_client-1.3.1.dist-info/RECORD,,
|
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
# lollms_client/tti_bindings/dalle/__init__.py
|
|
2
|
-
import requests
|
|
3
|
-
import base64
|
|
4
|
-
from lollms_client.lollms_tti_binding import LollmsTTIBinding
|
|
5
|
-
from typing import Optional, List, Dict, Any, Union
|
|
6
|
-
from ascii_colors import trace_exception, ASCIIColors
|
|
7
|
-
import json # For json.JSONDecodeError in error handling, and general JSON operations
|
|
8
|
-
import os # Added for environment variable access
|
|
9
|
-
|
|
10
|
-
# Defines the binding name for the manager
|
|
11
|
-
BindingName = "DalleTTIBinding_Impl"
|
|
12
|
-
|
|
13
|
-
# DALL-E specific constants
|
|
14
|
-
DALLE_API_HOST = "https://api.openai.com/v1"
|
|
15
|
-
OPENAI_API_KEY_ENV_VAR = "OPENAI_API_KEY" # Environment variable name
|
|
16
|
-
|
|
17
|
-
# Supported models and their properties
|
|
18
|
-
DALLE_MODELS = {
|
|
19
|
-
"dall-e-2": {
|
|
20
|
-
"sizes": ["256x256", "512x512", "1024x1024"],
|
|
21
|
-
"default_size": "1024x1024",
|
|
22
|
-
"supports_quality": False,
|
|
23
|
-
"supports_style": False,
|
|
24
|
-
"max_prompt_length": 1000 # Characters
|
|
25
|
-
},
|
|
26
|
-
"dall-e-3": {
|
|
27
|
-
"sizes": ["1024x1024", "1792x1024", "1024x1792"],
|
|
28
|
-
"default_size": "1024x1024",
|
|
29
|
-
"qualities": ["standard", "hd"],
|
|
30
|
-
"default_quality": "standard",
|
|
31
|
-
"styles": ["vivid", "natural"],
|
|
32
|
-
"default_style": "vivid",
|
|
33
|
-
"supports_quality": True,
|
|
34
|
-
"supports_style": True,
|
|
35
|
-
"max_prompt_length": 4000 # Characters
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
class DalleTTIBinding_Impl(LollmsTTIBinding):
|
|
39
|
-
"""
|
|
40
|
-
Concrete implementation of LollmsTTIBinding for OpenAI's DALL-E API.
|
|
41
|
-
"""
|
|
42
|
-
def __init__(self, **kwargs):
|
|
43
|
-
"""
|
|
44
|
-
Initialize the DALL-E TTI binding.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
api_key (Optional[str]): OpenAI API key. If None or empty, attempts to read
|
|
48
|
-
from the OPENAI_API_KEY environment variable.
|
|
49
|
-
model_name (str): Name of the DALL-E model to use (e.g., "dall-e-3", "dall-e-2").
|
|
50
|
-
default_size (Optional[str]): Default image size (e.g., "1024x1024").
|
|
51
|
-
If None, uses model's default.
|
|
52
|
-
default_quality (Optional[str]): Default image quality for DALL-E 3 ("standard", "hd").
|
|
53
|
-
If None, uses model's default if applicable.
|
|
54
|
-
default_style (Optional[str]): Default image style for DALL-E 3 ("vivid", "natural").
|
|
55
|
-
If None, uses model's default if applicable.
|
|
56
|
-
host_address (str): The API host address. Defaults to OpenAI's public API.
|
|
57
|
-
verify_ssl_certificate (bool): Whether to verify SSL certificates.
|
|
58
|
-
**kwargs: Catches other potential parameters like 'service_key' or 'client_id'.
|
|
59
|
-
"""
|
|
60
|
-
super().__init__(binding_name="dalle")
|
|
61
|
-
|
|
62
|
-
# Extract parameters from kwargs, providing defaults
|
|
63
|
-
self.api_key = kwargs.get("service_key")
|
|
64
|
-
self.model_name = kwargs.get("model_name")
|
|
65
|
-
self.default_size = kwargs.get("default_size")
|
|
66
|
-
self.default_quality = kwargs.get("default_quality")
|
|
67
|
-
self.default_style = kwargs.get("default_style")
|
|
68
|
-
self.host_address = kwargs.get("host_address", DALLE_API_HOST) # Provide default
|
|
69
|
-
self.verify_ssl_certificate = kwargs.get("verify_ssl_certificate", True) # Provide default
|
|
70
|
-
|
|
71
|
-
# Resolve API key from kwargs or environment variable
|
|
72
|
-
resolved_api_key = self.api_key
|
|
73
|
-
if not resolved_api_key:
|
|
74
|
-
ASCIIColors.info(f"API key not provided directly, checking environment variable '{OPENAI_API_KEY_ENV_VAR}'...")
|
|
75
|
-
resolved_api_key = os.environ.get(OPENAI_API_KEY_ENV_VAR)
|
|
76
|
-
|
|
77
|
-
if not resolved_api_key:
|
|
78
|
-
raise ValueError(f"OpenAI API key is required. Provide it directly or set the '{OPENAI_API_KEY_ENV_VAR}' environment variable.")
|
|
79
|
-
|
|
80
|
-
self.api_key = resolved_api_key
|
|
81
|
-
|
|
82
|
-
# Model name validation
|
|
83
|
-
if not self.model_name:
|
|
84
|
-
ASCIIColors.warning("Model name is required.")
|
|
85
|
-
if self.model_name not in DALLE_MODELS:
|
|
86
|
-
ASCIIColors.warning(f"Unsupported DALL-E model: {self.model_name}. Supported models: {list(DALLE_MODELS.keys())}")
|
|
87
|
-
self.model_name = list(DALLE_MODELS.keys())[1]
|
|
88
|
-
ASCIIColors.warning(f"Defaulting to {self.model_name}")
|
|
89
|
-
|
|
90
|
-
model_props = DALLE_MODELS[self.model_name]
|
|
91
|
-
|
|
92
|
-
# Size
|
|
93
|
-
self.current_size = self.default_size or model_props["default_size"]
|
|
94
|
-
if self.current_size not in model_props["sizes"]:
|
|
95
|
-
raise ValueError(f"Unsupported size '{self.current_size}' for model '{self.model_name}'. Supported sizes: {model_props['sizes']}")
|
|
96
|
-
|
|
97
|
-
# Quality
|
|
98
|
-
if model_props["supports_quality"]:
|
|
99
|
-
self.current_quality = self.default_quality or model_props["default_quality"]
|
|
100
|
-
if self.current_quality not in model_props["qualities"]:
|
|
101
|
-
raise ValueError(f"Unsupported quality '{self.current_quality}' for model '{self.model_name}'. Supported qualities: {model_props['qualities']}")
|
|
102
|
-
else:
|
|
103
|
-
self.current_quality = None # Explicitly None if not supported
|
|
104
|
-
|
|
105
|
-
# Style
|
|
106
|
-
if model_props["supports_style"]:
|
|
107
|
-
self.current_style = self.default_style or model_props["default_style"]
|
|
108
|
-
if self.current_style not in model_props["styles"]:
|
|
109
|
-
raise ValueError(f"Unsupported style '{self.current_style}' for model '{self.model_name}'. Supported styles: {model_props['styles']}")
|
|
110
|
-
else:
|
|
111
|
-
self.current_style = None # Explicitly None if not supported
|
|
112
|
-
|
|
113
|
-
# Client ID
|
|
114
|
-
self.client_id = kwargs.get("service_key", kwargs.get("client_id", "dalle_client_user"))
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def _get_model_properties(self, model_name: Optional[str] = None) -> Dict[str, Any]:
|
|
118
|
-
"""Helper to get properties for a given model name, or the instance's current model."""
|
|
119
|
-
return DALLE_MODELS.get(model_name or self.model_name, {})
|
|
120
|
-
|
|
121
|
-
def generate_image(self,
|
|
122
|
-
prompt: str,
|
|
123
|
-
negative_prompt: Optional[str] = "",
|
|
124
|
-
width: int = 1024, # Default width
|
|
125
|
-
height: int = 1024, # Default height
|
|
126
|
-
**kwargs) -> bytes:
|
|
127
|
-
"""
|
|
128
|
-
Generates image data using the DALL-E API.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
prompt (str): The positive text prompt.
|
|
132
|
-
negative_prompt (Optional[str]): The negative prompt. For DALL-E 3, this is
|
|
133
|
-
appended to the main prompt. For DALL-E 2, it's ignored.
|
|
134
|
-
width (int): Image width.
|
|
135
|
-
height (int): Image height.
|
|
136
|
-
**kwargs: Additional parameters:
|
|
137
|
-
- model (str): Override the instance's default model for this call.
|
|
138
|
-
- quality (str): Override quality ("standard", "hd" for DALL-E 3).
|
|
139
|
-
- style (str): Override style ("vivid", "natural" for DALL-E 3).
|
|
140
|
-
- n (int): Number of images to generate (OpenAI supports >1, but this binding returns one). Default 1.
|
|
141
|
-
- user (str): A unique identifier for your end-user (OpenAI abuse monitoring).
|
|
142
|
-
Returns:
|
|
143
|
-
bytes: The generated image data (PNG format from DALL-E).
|
|
144
|
-
|
|
145
|
-
Raises:
|
|
146
|
-
Exception: If the request fails or image generation fails on the server.
|
|
147
|
-
"""
|
|
148
|
-
model_override = kwargs.get("model")
|
|
149
|
-
active_model_name = model_override if model_override else self.model_name
|
|
150
|
-
|
|
151
|
-
model_props = self._get_model_properties(active_model_name)
|
|
152
|
-
if not model_props:
|
|
153
|
-
raise ValueError(f"Model {active_model_name} properties not found. Supported: {list(DALLE_MODELS.keys())}")
|
|
154
|
-
|
|
155
|
-
# Format size string and validate against the active model for this generation
|
|
156
|
-
size_str = f"{width}x{height}"
|
|
157
|
-
if size_str not in model_props["sizes"]:
|
|
158
|
-
ASCIIColors.warning(f"Unsupported size '{size_str}' for model '{active_model_name}'. Supported sizes: {model_props['sizes']}. Adjust width/height for this model.")
|
|
159
|
-
size_str = model_props["sizes"][0]
|
|
160
|
-
|
|
161
|
-
# Handle prompt and negative prompt based on the active model
|
|
162
|
-
final_prompt = prompt
|
|
163
|
-
if active_model_name == "dall-e-3" and negative_prompt:
|
|
164
|
-
final_prompt = f"{prompt}. Avoid: {negative_prompt}."
|
|
165
|
-
ASCIIColors.info(f"DALL-E 3: Appended negative prompt. Final prompt: '{final_prompt[:100]}...'")
|
|
166
|
-
elif active_model_name == "dall-e-2" and negative_prompt:
|
|
167
|
-
ASCIIColors.warning("DALL-E 2 does not support negative_prompt. It will be ignored.")
|
|
168
|
-
|
|
169
|
-
# Truncate prompt if too long for the active model
|
|
170
|
-
max_len = model_props.get("max_prompt_length", 4000)
|
|
171
|
-
if len(final_prompt) > max_len:
|
|
172
|
-
ASCIIColors.warning(f"Prompt for {active_model_name} is too long ({len(final_prompt)} chars). Truncating to {max_len} characters.")
|
|
173
|
-
final_prompt = final_prompt[:max_len]
|
|
174
|
-
|
|
175
|
-
endpoint = f"{self.host_address}/images/generations"
|
|
176
|
-
headers = {
|
|
177
|
-
"Authorization": f"Bearer {self.api_key}",
|
|
178
|
-
"Content-Type": "application/json"
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
payload = {
|
|
182
|
-
"model": active_model_name,
|
|
183
|
-
"prompt": final_prompt,
|
|
184
|
-
"n": kwargs.get("n", 1), # This binding expects to return one image
|
|
185
|
-
"size": size_str,
|
|
186
|
-
"response_format": "b64_json" # Request base64 encoded image
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
# Add model-specific parameters (quality, style)
|
|
190
|
-
# Use kwargs if provided, otherwise instance defaults, but only if the active model supports them.
|
|
191
|
-
if model_props["supports_quality"]:
|
|
192
|
-
payload["quality"] = kwargs.get("quality", self.current_quality)
|
|
193
|
-
if model_props["supports_style"]:
|
|
194
|
-
payload["style"] = kwargs.get("style", self.current_style)
|
|
195
|
-
|
|
196
|
-
if "user" in kwargs: # Pass user param if provided for moderation
|
|
197
|
-
payload["user"] = kwargs["user"]
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
response = requests.post(endpoint, json=payload, headers=headers, verify=self.verify_ssl_certificate)
|
|
201
|
-
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
|
202
|
-
|
|
203
|
-
response_json = response.json()
|
|
204
|
-
|
|
205
|
-
if not response_json.get("data") or not response_json["data"][0].get("b64_json"):
|
|
206
|
-
raise Exception("Server did not return image data in expected b64_json format.")
|
|
207
|
-
|
|
208
|
-
img_base64 = response_json["data"][0]["b64_json"]
|
|
209
|
-
img_bytes = base64.b64decode(img_base64)
|
|
210
|
-
return img_bytes
|
|
211
|
-
|
|
212
|
-
except requests.exceptions.HTTPError as e:
|
|
213
|
-
error_detail = "Unknown server error"
|
|
214
|
-
if e.response is not None:
|
|
215
|
-
try:
|
|
216
|
-
err_json = e.response.json()
|
|
217
|
-
if "error" in err_json and isinstance(err_json["error"], dict) and "message" in err_json["error"]:
|
|
218
|
-
error_detail = err_json["error"]["message"]
|
|
219
|
-
elif "detail" in err_json: # Fallback for other error structures
|
|
220
|
-
error_detail = err_json["detail"]
|
|
221
|
-
else: # If no specific error message, use raw text (limited)
|
|
222
|
-
error_detail = e.response.text[:500]
|
|
223
|
-
except (json.JSONDecodeError, requests.exceptions.JSONDecodeError): # If response is not JSON
|
|
224
|
-
error_detail = e.response.text[:500]
|
|
225
|
-
trace_exception(e)
|
|
226
|
-
raise Exception(f"HTTP request failed: {e.response.status_code} {e.response.reason} - Detail: {error_detail}") from e
|
|
227
|
-
else: # HTTPError without a response object (less common)
|
|
228
|
-
trace_exception(e)
|
|
229
|
-
raise Exception(f"HTTP request failed without a response body: {e}") from e
|
|
230
|
-
except requests.exceptions.RequestException as e: # Catches other network errors (DNS, ConnectionError, etc.)
|
|
231
|
-
trace_exception(e)
|
|
232
|
-
raise Exception(f"Request failed due to network issue: {e}") from e
|
|
233
|
-
except Exception as e: # Catches other errors (e.g., base64 decoding, unexpected issues)
|
|
234
|
-
trace_exception(e)
|
|
235
|
-
raise Exception(f"Image generation process failed: {e}") from e
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
def list_services(self, **kwargs) -> List[Dict[str, str]]:
|
|
239
|
-
"""
|
|
240
|
-
Lists available DALL-E models supported by this binding.
|
|
241
|
-
`client_id` from kwargs is ignored as DALL-E auth is via API key.
|
|
242
|
-
"""
|
|
243
|
-
services = []
|
|
244
|
-
for model_name, props in DALLE_MODELS.items():
|
|
245
|
-
caption = f"OpenAI {model_name.upper()}"
|
|
246
|
-
help_text = f"Size options: {', '.join(props['sizes'])}. "
|
|
247
|
-
if props["supports_quality"]:
|
|
248
|
-
help_text += f"Qualities: {', '.join(props['qualities'])}. "
|
|
249
|
-
if props["supports_style"]:
|
|
250
|
-
help_text += f"Styles: {', '.join(props['styles'])}. "
|
|
251
|
-
services.append({
|
|
252
|
-
"name": model_name,
|
|
253
|
-
"caption": caption,
|
|
254
|
-
"help": help_text.strip()
|
|
255
|
-
})
|
|
256
|
-
return services
|
|
257
|
-
|
|
258
|
-
def get_settings(self, **kwargs) -> List[Dict[str, Any]]:
|
|
259
|
-
"""
|
|
260
|
-
Retrieves the current configurable default settings for the DALL-E binding.
|
|
261
|
-
`client_id` from kwargs is ignored. Returns settings in a ConfigTemplate-like format.
|
|
262
|
-
"""
|
|
263
|
-
model_props = self._get_model_properties(self.model_name) # Settings relative to current default model
|
|
264
|
-
|
|
265
|
-
settings = [
|
|
266
|
-
{
|
|
267
|
-
"name": "model_name",
|
|
268
|
-
"type": "str",
|
|
269
|
-
"value": self.model_name,
|
|
270
|
-
"description": "Default DALL-E model for generation.",
|
|
271
|
-
"options": list(DALLE_MODELS.keys()),
|
|
272
|
-
"category": "Model Configuration"
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
"name": "current_size",
|
|
276
|
-
"type": "str",
|
|
277
|
-
"value": self.current_size,
|
|
278
|
-
"description": "Default image size (e.g., 1024x1024). Format: widthxheight.",
|
|
279
|
-
"options": model_props.get("sizes", []), # Options relevant to the current default model
|
|
280
|
-
"category": "Image Generation Defaults"
|
|
281
|
-
}
|
|
282
|
-
]
|
|
283
|
-
|
|
284
|
-
if model_props.get("supports_quality", False):
|
|
285
|
-
settings.append({
|
|
286
|
-
"name": "current_quality",
|
|
287
|
-
"type": "str",
|
|
288
|
-
"value": self.current_quality,
|
|
289
|
-
"description": "Default image quality (e.g., 'standard', 'hd' for DALL-E 3).",
|
|
290
|
-
"options": model_props.get("qualities", []),
|
|
291
|
-
"category": "Image Generation Defaults"
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
if model_props.get("supports_style", False):
|
|
295
|
-
settings.append({
|
|
296
|
-
"name": "current_style",
|
|
297
|
-
"type": "str",
|
|
298
|
-
"value": self.current_style,
|
|
299
|
-
"description": "Default image style (e.g., 'vivid', 'natural' for DALL-E 3).",
|
|
300
|
-
"options": model_props.get("styles", []),
|
|
301
|
-
"category": "Image Generation Defaults"
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
settings.append({
|
|
305
|
-
"name": "api_key_status",
|
|
306
|
-
"type": "str",
|
|
307
|
-
"value": "Set (loaded)" if self.api_key else "Not Set", # Indicate if API key is present
|
|
308
|
-
"description": f"OpenAI API Key status (set at initialization or via '{OPENAI_API_KEY_ENV_VAR}', not changeable here).",
|
|
309
|
-
"category": "Authentication",
|
|
310
|
-
"read_only": True # Custom attribute indicating it's informational
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
return settings
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def set_settings(self, settings: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs) -> bool:
|
|
317
|
-
"""
|
|
318
|
-
Applies new default settings to the DALL-E binding instance.
|
|
319
|
-
`client_id` from kwargs is ignored.
|
|
320
|
-
|
|
321
|
-
Args:
|
|
322
|
-
settings (Union[Dict[str, Any], List[Dict[str, Any]]]):
|
|
323
|
-
New settings to apply.
|
|
324
|
-
Can be a flat dict: `{"model_name": "dall-e-2", "current_size": "512x512"}`
|
|
325
|
-
Or a list of dicts (ConfigTemplate format):
|
|
326
|
-
`[{"name": "model_name", "value": "dall-e-2"}, ...]`
|
|
327
|
-
|
|
328
|
-
Returns:
|
|
329
|
-
bool: True if at least one setting was successfully applied, False otherwise.
|
|
330
|
-
"""
|
|
331
|
-
applied_some_settings = False
|
|
332
|
-
original_model_name = self.model_name # To detect if model changes
|
|
333
|
-
|
|
334
|
-
# Normalize settings input to a flat dictionary
|
|
335
|
-
if isinstance(settings, list):
|
|
336
|
-
parsed_settings = {}
|
|
337
|
-
for item in settings:
|
|
338
|
-
if isinstance(item, dict) and "name" in item and "value" in item:
|
|
339
|
-
if item["name"] == "api_key_status": # This is read-only
|
|
340
|
-
continue
|
|
341
|
-
parsed_settings[item["name"]] = item["value"]
|
|
342
|
-
settings_dict = parsed_settings
|
|
343
|
-
elif isinstance(settings, dict):
|
|
344
|
-
settings_dict = settings
|
|
345
|
-
else:
|
|
346
|
-
ASCIIColors.error("Invalid settings format. Expected a dictionary or list of dictionaries.")
|
|
347
|
-
return False
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
# Phase 1: Apply model_name change if present, as it affects other settings' validity
|
|
351
|
-
if "model_name" in settings_dict:
|
|
352
|
-
new_model_name = settings_dict["model_name"]
|
|
353
|
-
if new_model_name not in DALLE_MODELS:
|
|
354
|
-
ASCIIColors.warning(f"Invalid model_name '{new_model_name}' provided in settings. Keeping current model '{self.model_name}'.")
|
|
355
|
-
elif self.model_name != new_model_name:
|
|
356
|
-
self.model_name = new_model_name
|
|
357
|
-
ASCIIColors.info(f"Default model changed to: {self.model_name}")
|
|
358
|
-
applied_some_settings = True # Mark that model name was processed
|
|
359
|
-
|
|
360
|
-
# Phase 2: If model changed, or for initial setup, adjust dependent settings to be consistent
|
|
361
|
-
# Run this phase if model_name was specifically in settings_dict and changed, OR if this is the first time settings are processed.
|
|
362
|
-
# The 'applied_some_settings' flag after model_name processing indicates a change.
|
|
363
|
-
if "model_name" in settings_dict and applied_some_settings :
|
|
364
|
-
new_model_props = self._get_model_properties(self.model_name)
|
|
365
|
-
|
|
366
|
-
# Update current_size if invalid for new model or if model changed
|
|
367
|
-
if self.current_size not in new_model_props["sizes"]:
|
|
368
|
-
old_val = self.current_size
|
|
369
|
-
self.current_size = new_model_props["default_size"]
|
|
370
|
-
if old_val != self.current_size: ASCIIColors.info(f"Default size reset to '{self.current_size}' for model '{self.model_name}'.")
|
|
371
|
-
|
|
372
|
-
# Update current_quality
|
|
373
|
-
if new_model_props["supports_quality"]:
|
|
374
|
-
if self.current_quality not in new_model_props.get("qualities", []):
|
|
375
|
-
old_val = self.current_quality
|
|
376
|
-
self.current_quality = new_model_props["default_quality"]
|
|
377
|
-
if old_val != self.current_quality: ASCIIColors.info(f"Default quality reset to '{self.current_quality}' for model '{self.model_name}'.")
|
|
378
|
-
elif self.current_quality is not None: # New model doesn't support quality
|
|
379
|
-
self.current_quality = None
|
|
380
|
-
ASCIIColors.info(f"Quality setting removed as model '{self.model_name}' does not support it.")
|
|
381
|
-
|
|
382
|
-
# Update current_style
|
|
383
|
-
if new_model_props["supports_style"]:
|
|
384
|
-
if self.current_style not in new_model_props.get("styles", []):
|
|
385
|
-
old_val = self.current_style
|
|
386
|
-
self.current_style = new_model_props["default_style"]
|
|
387
|
-
if old_val != self.current_style: ASCIIColors.info(f"Default style reset to '{self.current_style}' for model '{self.model_name}'.")
|
|
388
|
-
elif self.current_style is not None: # New model doesn't support style
|
|
389
|
-
self.current_style = None
|
|
390
|
-
ASCIIColors.info(f"Style setting removed as model '{self.model_name}' does not support it.")
|
|
391
|
-
|
|
392
|
-
# Phase 3: Apply other specific settings from input, validating against the (potentially new) model
|
|
393
|
-
current_model_props = self._get_model_properties(self.model_name) # Re-fetch props if model changed
|
|
394
|
-
|
|
395
|
-
if "current_size" in settings_dict:
|
|
396
|
-
new_size = settings_dict["current_size"]
|
|
397
|
-
if new_size not in current_model_props["sizes"]:
|
|
398
|
-
ASCIIColors.warning(f"Invalid size '{new_size}' for model '{self.model_name}'. Keeping '{self.current_size}'. Supported: {current_model_props['sizes']}")
|
|
399
|
-
elif self.current_size != new_size:
|
|
400
|
-
self.current_size = new_size
|
|
401
|
-
ASCIIColors.info(f"Default size set to: {self.current_size}")
|
|
402
|
-
applied_some_settings = True
|
|
403
|
-
|
|
404
|
-
if "current_quality" in settings_dict:
|
|
405
|
-
if current_model_props["supports_quality"]:
|
|
406
|
-
new_quality = settings_dict["current_quality"]
|
|
407
|
-
if new_quality not in current_model_props["qualities"]:
|
|
408
|
-
ASCIIColors.warning(f"Invalid quality '{new_quality}' for model '{self.model_name}'. Keeping '{self.current_quality}'. Supported: {current_model_props['qualities']}")
|
|
409
|
-
elif self.current_quality != new_quality:
|
|
410
|
-
self.current_quality = new_quality
|
|
411
|
-
ASCIIColors.info(f"Default quality set to: {self.current_quality}")
|
|
412
|
-
applied_some_settings = True
|
|
413
|
-
elif "current_quality" in settings_dict: # Only warn if user explicitly tried to set it
|
|
414
|
-
ASCIIColors.warning(f"Model '{self.model_name}' does not support quality. Ignoring 'current_quality' setting.")
|
|
415
|
-
|
|
416
|
-
if "current_style" in settings_dict:
|
|
417
|
-
if current_model_props["supports_style"]:
|
|
418
|
-
new_style = settings_dict["current_style"]
|
|
419
|
-
if new_style not in current_model_props["styles"]:
|
|
420
|
-
ASCIIColors.warning(f"Invalid style '{new_style}' for model '{self.model_name}'. Keeping '{self.current_style}'. Supported: {current_model_props['styles']}")
|
|
421
|
-
elif self.current_style != new_style:
|
|
422
|
-
self.current_style = new_style
|
|
423
|
-
ASCIIColors.info(f"Default style set to: {self.current_style}")
|
|
424
|
-
applied_some_settings = True
|
|
425
|
-
elif "current_style" in settings_dict: # Only warn if user explicitly tried to set it
|
|
426
|
-
ASCIIColors.warning(f"Model '{self.model_name}' does not support style. Ignoring 'current_style' setting.")
|
|
427
|
-
|
|
428
|
-
if "api_key" in settings_dict: # Should not be settable here
|
|
429
|
-
ASCIIColors.warning("API key cannot be changed after initialization via set_settings. This setting was ignored.")
|
|
430
|
-
|
|
431
|
-
return applied_some_settings
|
|
432
|
-
|
|
433
|
-
except Exception as e:
|
|
434
|
-
trace_exception(e)
|
|
435
|
-
ASCIIColors.error(f"Failed to apply settings due to an unexpected error: {e}")
|
|
436
|
-
return False
|
|
437
|
-
|
|
438
|
-
def listModels(self) -> list:
|
|
439
|
-
"""Lists models"""
|
|
440
|
-
formatted_models=[
|
|
441
|
-
{
|
|
442
|
-
'model_name': "dall-e-2",
|
|
443
|
-
'display_name': "Dall-e 2",
|
|
444
|
-
'description': "Dalle 2 model",
|
|
445
|
-
'owned_by': 'openai'
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
'model_name': "dall-e-3",
|
|
449
|
-
'display_name': "Dall-e 3",
|
|
450
|
-
'description': "Dalle 3 model",
|
|
451
|
-
'owned_by': 'openai'
|
|
452
|
-
}
|
|
453
|
-
]
|
|
454
|
-
return formatted_models
|
|
File without changes
|
|
File without changes
|
|
File without changes
|