lollms-client 1.5.6__py3-none-any.whl → 1.7.10__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.
- lollms_client/__init__.py +1 -1
- lollms_client/llm_bindings/azure_openai/__init__.py +2 -2
- lollms_client/llm_bindings/claude/__init__.py +125 -34
- lollms_client/llm_bindings/gemini/__init__.py +261 -159
- lollms_client/llm_bindings/grok/__init__.py +52 -14
- lollms_client/llm_bindings/groq/__init__.py +2 -2
- lollms_client/llm_bindings/hugging_face_inference_api/__init__.py +2 -2
- lollms_client/llm_bindings/litellm/__init__.py +1 -1
- lollms_client/llm_bindings/llamacpp/__init__.py +18 -11
- lollms_client/llm_bindings/lollms/__init__.py +76 -21
- lollms_client/llm_bindings/lollms_webui/__init__.py +1 -1
- lollms_client/llm_bindings/mistral/__init__.py +2 -2
- lollms_client/llm_bindings/novita_ai/__init__.py +142 -6
- lollms_client/llm_bindings/ollama/__init__.py +307 -89
- lollms_client/llm_bindings/open_router/__init__.py +2 -2
- lollms_client/llm_bindings/openai/__init__.py +81 -20
- lollms_client/llm_bindings/openllm/__init__.py +362 -506
- lollms_client/llm_bindings/openwebui/__init__.py +333 -171
- lollms_client/llm_bindings/perplexity/__init__.py +2 -2
- lollms_client/llm_bindings/pythonllamacpp/__init__.py +3 -3
- lollms_client/llm_bindings/tensor_rt/__init__.py +1 -1
- lollms_client/llm_bindings/transformers/__init__.py +428 -632
- lollms_client/llm_bindings/vllm/__init__.py +1 -1
- lollms_client/lollms_agentic.py +4 -2
- lollms_client/lollms_base_binding.py +61 -0
- lollms_client/lollms_core.py +512 -1890
- lollms_client/lollms_discussion.py +25 -11
- lollms_client/lollms_llm_binding.py +112 -261
- lollms_client/lollms_mcp_binding.py +34 -75
- lollms_client/lollms_stt_binding.py +85 -52
- lollms_client/lollms_tti_binding.py +23 -37
- lollms_client/lollms_ttm_binding.py +24 -42
- lollms_client/lollms_tts_binding.py +28 -17
- lollms_client/lollms_ttv_binding.py +24 -42
- lollms_client/lollms_types.py +4 -2
- lollms_client/stt_bindings/whisper/__init__.py +108 -23
- lollms_client/stt_bindings/whispercpp/__init__.py +7 -1
- lollms_client/tti_bindings/diffusers/__init__.py +418 -810
- lollms_client/tti_bindings/diffusers/server/main.py +1051 -0
- lollms_client/tti_bindings/gemini/__init__.py +182 -239
- lollms_client/tti_bindings/leonardo_ai/__init__.py +6 -3
- lollms_client/tti_bindings/lollms/__init__.py +4 -1
- lollms_client/tti_bindings/novita_ai/__init__.py +5 -2
- lollms_client/tti_bindings/openai/__init__.py +10 -11
- lollms_client/tti_bindings/stability_ai/__init__.py +5 -3
- lollms_client/ttm_bindings/audiocraft/__init__.py +7 -12
- lollms_client/ttm_bindings/beatoven_ai/__init__.py +7 -3
- lollms_client/ttm_bindings/lollms/__init__.py +4 -17
- lollms_client/ttm_bindings/replicate/__init__.py +7 -4
- lollms_client/ttm_bindings/stability_ai/__init__.py +7 -4
- lollms_client/ttm_bindings/topmediai/__init__.py +6 -3
- lollms_client/tts_bindings/bark/__init__.py +7 -10
- lollms_client/tts_bindings/lollms/__init__.py +6 -1
- lollms_client/tts_bindings/piper_tts/__init__.py +8 -11
- lollms_client/tts_bindings/xtts/__init__.py +157 -74
- lollms_client/tts_bindings/xtts/server/main.py +241 -280
- {lollms_client-1.5.6.dist-info → lollms_client-1.7.10.dist-info}/METADATA +113 -5
- lollms_client-1.7.10.dist-info/RECORD +89 -0
- lollms_client-1.5.6.dist-info/RECORD +0 -87
- {lollms_client-1.5.6.dist-info → lollms_client-1.7.10.dist-info}/WHEEL +0 -0
- {lollms_client-1.5.6.dist-info → lollms_client-1.7.10.dist-info}/licenses/LICENSE +0 -0
- {lollms_client-1.5.6.dist-info → lollms_client-1.7.10.dist-info}/top_level.txt +0 -0
|
@@ -2,319 +2,262 @@
|
|
|
2
2
|
import sys
|
|
3
3
|
from typing import Optional, List, Dict, Any, Union
|
|
4
4
|
import os
|
|
5
|
+
import io
|
|
6
|
+
import base64
|
|
7
|
+
import requests
|
|
8
|
+
import binascii
|
|
9
|
+
import time
|
|
5
10
|
|
|
6
11
|
from lollms_client.lollms_tti_binding import LollmsTTIBinding
|
|
7
12
|
from ascii_colors import trace_exception, ASCIIColors
|
|
8
|
-
import math
|
|
9
13
|
|
|
10
14
|
# --- SDK & Dependency Management ---
|
|
11
15
|
try:
|
|
12
16
|
import pipmaster as pm
|
|
13
|
-
|
|
14
|
-
pm.ensure_packages(['google-cloud-aiplatform', 'google-generativeai', 'Pillow'])
|
|
17
|
+
pm.ensure_packages(['google-cloud-aiplatform', 'google-generativeai', 'Pillow', 'requests'])
|
|
15
18
|
except ImportError:
|
|
16
|
-
pass
|
|
19
|
+
pass
|
|
17
20
|
|
|
18
|
-
# Attempt to import Vertex AI
|
|
21
|
+
# Attempt to import Vertex AI
|
|
19
22
|
try:
|
|
20
23
|
import vertexai
|
|
21
|
-
from vertexai.preview.vision_models import ImageGenerationModel
|
|
24
|
+
from vertexai.preview.vision_models import ImageGenerationModel as VertexImageGenerationModel, Image as VertexImage
|
|
22
25
|
from google.api_core import exceptions as google_exceptions
|
|
26
|
+
from PIL import Image as PILImage
|
|
23
27
|
VERTEX_AI_AVAILABLE = True
|
|
24
28
|
except ImportError:
|
|
25
29
|
VERTEX_AI_AVAILABLE = False
|
|
26
30
|
|
|
27
|
-
# Attempt to import Gemini API
|
|
31
|
+
# Attempt to import Gemini API
|
|
28
32
|
try:
|
|
29
|
-
|
|
30
|
-
from google.
|
|
33
|
+
import google.generativeai as genai
|
|
34
|
+
from google.api_core.exceptions import ResourceExhausted
|
|
31
35
|
GEMINI_API_AVAILABLE = True
|
|
32
36
|
except ImportError:
|
|
33
37
|
GEMINI_API_AVAILABLE = False
|
|
38
|
+
ResourceExhausted = type('ResourceExhausted', (Exception,), {}) # Define dummy exception if import fails
|
|
34
39
|
|
|
35
40
|
# Defines the binding name for the manager
|
|
36
41
|
BindingName = "GeminiTTIBinding_Impl"
|
|
37
42
|
|
|
38
|
-
#
|
|
39
|
-
IMAGEN_VERTEX_MODELS = ["imagegeneration@006", "
|
|
40
|
-
IMAGEN_GEMINI_API_MODELS = ["imagen-3", "gemini-1.5-flash-preview-0514"] # Short names are often aliases
|
|
43
|
+
# Static list for Vertex AI, as it's project-based and more predictable
|
|
44
|
+
IMAGEN_VERTEX_MODELS = ["imagegeneration@006", "imagen-3.0-generate-002", "gemini-2.5-flash-image"]
|
|
41
45
|
GEMINI_API_KEY_ENV_VAR = "GEMINI_API_KEY"
|
|
42
46
|
|
|
43
|
-
class GeminiTTIBinding_Impl(LollmsTTIBinding):
|
|
44
|
-
"""
|
|
45
|
-
Concrete implementation of LollmsTTIBinding for Google's Imagen models.
|
|
46
|
-
Supports both Vertex AI (project_id) and Gemini API (api_key) authentication.
|
|
47
|
-
"""
|
|
48
|
-
def __init__(self, **kwargs):
|
|
49
|
-
"""
|
|
50
|
-
Initialize the Gemini (Vertex AI / API) TTI binding.
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
**kwargs: Configuration parameters.
|
|
54
|
-
- auth_method (str): "vertex_ai" or "api_key". (Required)
|
|
55
|
-
- project_id (str): Google Cloud project ID (for vertex_ai).
|
|
56
|
-
- location (str): Google Cloud region (for vertex_ai).
|
|
57
|
-
- service_key (str): Gemini API Key (for api_key).
|
|
58
|
-
- model_name (str): The Imagen model to use.
|
|
59
|
-
- default_seed (int): Default seed for generation (-1 for random).
|
|
60
|
-
- default_guidance_scale (float): Default guidance scale (CFG).
|
|
61
|
-
"""
|
|
62
|
-
super().__init__(binding_name="gemini")
|
|
63
47
|
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
def _is_base64(s):
|
|
49
|
+
try:
|
|
50
|
+
base64.b64decode(s.split(',')[-1], validate=True)
|
|
51
|
+
return True
|
|
52
|
+
except (TypeError, ValueError, binascii.Error):
|
|
53
|
+
return False
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
def _load_image_from_str(image_str: str) -> bytes:
|
|
56
|
+
if image_str.startswith(('http://', 'https://')):
|
|
57
|
+
try:
|
|
58
|
+
response = requests.get(image_str)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
return response.content
|
|
61
|
+
except requests.exceptions.RequestException as e:
|
|
62
|
+
raise IOError(f"Failed to download image from URL: {image_str}") from e
|
|
63
|
+
elif _is_base64(image_str):
|
|
64
|
+
header, encoded = image_str.split(',', 1)
|
|
65
|
+
return base64.b64decode(encoded)
|
|
66
|
+
else:
|
|
67
|
+
raise ValueError("Image string is not a valid URL or base64 string.")
|
|
70
68
|
|
|
71
|
-
# Gemini API specific settings
|
|
72
|
-
self.gemini_api_key = kwargs.get("service_key")
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
class GeminiTTIBinding_Impl(LollmsTTIBinding):
|
|
71
|
+
def __init__(self, **kwargs):
|
|
72
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
73
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
74
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
75
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
76
|
+
self.auth_method = kwargs.get("auth_method", "vertex_ai")
|
|
81
77
|
self.client: Optional[Any] = None
|
|
78
|
+
self.available_models = []
|
|
82
79
|
|
|
83
|
-
# --- Validation and Initialization ---
|
|
84
80
|
if self.auth_method == "vertex_ai":
|
|
85
81
|
if not VERTEX_AI_AVAILABLE:
|
|
86
|
-
raise ImportError("Vertex AI
|
|
82
|
+
raise ImportError("Vertex AI selected, but 'google-cloud-aiplatform' is not installed.")
|
|
83
|
+
self.project_id = kwargs.get("project_id")
|
|
84
|
+
self.location = kwargs.get("location", "us-central1")
|
|
87
85
|
if not self.project_id:
|
|
88
|
-
raise ValueError("For 'vertex_ai' auth,
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
raise ValueError("For 'vertex_ai' auth, 'project_id' is required.")
|
|
87
|
+
self.model_name = kwargs.get("model_name") # Can be None initially
|
|
88
|
+
self.available_models = IMAGEN_VERTEX_MODELS
|
|
89
|
+
|
|
91
90
|
elif self.auth_method == "api_key":
|
|
92
91
|
if not GEMINI_API_AVAILABLE:
|
|
93
|
-
raise ImportError("API Key
|
|
94
|
-
|
|
95
|
-
# Resolve API key from kwargs or environment variable
|
|
96
|
-
if not self.gemini_api_key:
|
|
97
|
-
ASCIIColors.info(f"API key not provided directly, checking environment variable '{GEMINI_API_KEY_ENV_VAR}'...")
|
|
98
|
-
self.gemini_api_key = os.environ.get(GEMINI_API_KEY_ENV_VAR)
|
|
99
|
-
|
|
92
|
+
raise ImportError("API Key selected, but 'google-generativeai' is not installed.")
|
|
93
|
+
self.gemini_api_key = kwargs.get("service_key") or os.environ.get(GEMINI_API_KEY_ENV_VAR)
|
|
100
94
|
if not self.gemini_api_key:
|
|
101
|
-
raise ValueError(f"For 'api_key' auth,
|
|
102
|
-
|
|
103
|
-
if not self.model_name:
|
|
104
|
-
self.model_name = IMAGEN_GEMINI_API_MODELS[0]
|
|
95
|
+
raise ValueError(f"For 'api_key' auth, 'service_key' or env var '{GEMINI_API_KEY_ENV_VAR}' is required.")
|
|
96
|
+
self.model_name = kwargs.get("model_name") # Can be None initially
|
|
105
97
|
else:
|
|
106
98
|
raise ValueError(f"Invalid auth_method: '{self.auth_method}'. Must be 'vertex_ai' or 'api_key'.")
|
|
107
99
|
|
|
100
|
+
self.default_seed = int(kwargs.get("default_seed", -1))
|
|
101
|
+
self.default_guidance_scale = float(kwargs.get("default_guidance_scale", 7.5))
|
|
108
102
|
self._initialize_client()
|
|
109
103
|
|
|
110
104
|
def _initialize_client(self):
|
|
111
|
-
"""Initializes the appropriate client based on the selected auth_method."""
|
|
112
105
|
ASCIIColors.info(f"Initializing Google client with auth method: '{self.auth_method}'...")
|
|
113
106
|
try:
|
|
114
107
|
if self.auth_method == "vertex_ai":
|
|
115
108
|
vertexai.init(project=self.project_id, location=self.location)
|
|
116
|
-
|
|
109
|
+
if not self.model_name:
|
|
110
|
+
self.model_name = self.available_models[0]
|
|
111
|
+
self.client = VertexImageGenerationModel.from_pretrained(self.model_name)
|
|
117
112
|
ASCIIColors.green(f"Vertex AI initialized successfully. Project: '{self.project_id}', Model: '{self.model_name}'")
|
|
113
|
+
|
|
118
114
|
elif self.auth_method == "api_key":
|
|
119
115
|
genai.configure(api_key=self.gemini_api_key)
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
116
|
+
|
|
117
|
+
# --- DYNAMIC MODEL DISCOVERY ---
|
|
118
|
+
ASCIIColors.info("Discovering available image models for your API key...")
|
|
119
|
+
self.available_models = [
|
|
120
|
+
m.name for m in genai.list_models()
|
|
121
|
+
if 'imagen' in m.name and 'generateContent' in m.supported_generation_methods
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
if not self.available_models:
|
|
125
|
+
raise Exception("Your API key does not have access to any compatible image generation models. Please check your Google AI Studio project settings.")
|
|
126
|
+
|
|
127
|
+
ASCIIColors.green(f"Found available models: {self.available_models}")
|
|
128
|
+
|
|
129
|
+
# Validate or set the model_name
|
|
130
|
+
if self.model_name and self.model_name not in self.available_models:
|
|
131
|
+
ASCIIColors.warning(f"Model '{self.model_name}' is not available for your key. Falling back to default.")
|
|
132
|
+
self.model_name = None
|
|
133
|
+
|
|
134
|
+
if not self.model_name:
|
|
135
|
+
self.model_name = self.available_models[0]
|
|
136
|
+
|
|
137
|
+
self.client = genai.GenerativeModel(self.model_name)
|
|
138
|
+
ASCIIColors.green(f"Gemini API configured successfully. Using Model: '{self.model_name}'")
|
|
139
|
+
|
|
129
140
|
except Exception as e:
|
|
130
141
|
trace_exception(e)
|
|
131
142
|
raise Exception(f"Failed to initialize Google client: {e}") from e
|
|
132
143
|
|
|
133
|
-
def
|
|
134
|
-
"""Validates image dimensions against Vertex AI Imagen constraints."""
|
|
135
|
-
if not (256 <= width <= 1536 and width % 8 == 0):
|
|
136
|
-
raise ValueError(f"Invalid width for Vertex AI: {width}. Must be 256-1536 and a multiple of 8.")
|
|
137
|
-
if not (256 <= height <= 1536 and height % 8 == 0):
|
|
138
|
-
raise ValueError(f"Invalid height for Vertex AI: {height}. Must be 256-1536 and a multiple of 8.")
|
|
139
|
-
|
|
140
|
-
def _get_aspect_ratio_for_api(self, width: int, height: int) -> str:
|
|
141
|
-
"""Finds the closest supported aspect ratio string for the Gemini API."""
|
|
142
|
-
ratios = {"1:1": 1.0, "16:9": 16/9, "9:16": 9/16, "4:3": 4/3, "3:4": 3/4}
|
|
143
|
-
target_ratio = width / height
|
|
144
|
-
closest_ratio_name = min(ratios, key=lambda r: abs(ratios[r] - target_ratio))
|
|
145
|
-
ASCIIColors.info(f"Converted {width}x{height} to closest aspect ratio: '{closest_ratio_name}' for Gemini API.")
|
|
146
|
-
return closest_ratio_name
|
|
147
|
-
|
|
148
|
-
def generate_image(self,
|
|
149
|
-
prompt: str,
|
|
150
|
-
negative_prompt: Optional[str] = "",
|
|
151
|
-
width: int = 1024,
|
|
152
|
-
height: int = 1024,
|
|
153
|
-
**kwargs) -> bytes:
|
|
154
|
-
"""
|
|
155
|
-
Generates image data using the configured Google Imagen model.
|
|
156
|
-
"""
|
|
144
|
+
def generate_image(self, prompt: str, negative_prompt: Optional[str] = "", width: int = 1024, height: int = 1024, **kwargs) -> bytes:
|
|
157
145
|
if not self.client:
|
|
158
|
-
raise RuntimeError("Google client is not initialized.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
146
|
+
raise RuntimeError("Google client is not initialized.")
|
|
147
|
+
|
|
148
|
+
ASCIIColors.info(f"Generating image with prompt: '{prompt[:100]}...'")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
if self.auth_method == "vertex_ai":
|
|
152
|
+
return self._generate_with_vertex_ai(prompt, negative_prompt, width, height, **kwargs)
|
|
153
|
+
elif self.auth_method == "api_key":
|
|
154
|
+
return self._generate_with_api_key(prompt, negative_prompt, width, height, **kwargs)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
if "quota" in str(e).lower():
|
|
157
|
+
raise Exception(f"Image generation failed due to a quota error. This means you have exceeded the free tier limit for your API key. To fix this, please enable billing on your Google Cloud project. Original error: {e}")
|
|
158
|
+
raise Exception(f"Image generation failed: {e}")
|
|
164
159
|
|
|
165
|
-
|
|
160
|
+
def _generate_with_api_key(self, prompt, negative_prompt, width, height, **kwargs):
|
|
161
|
+
full_prompt = f"Generate an image of: {prompt}"
|
|
166
162
|
if negative_prompt:
|
|
167
|
-
|
|
163
|
+
full_prompt += f". Do not include: {negative_prompt}."
|
|
168
164
|
|
|
169
|
-
|
|
165
|
+
max_retries = 3
|
|
166
|
+
initial_delay = 5
|
|
170
167
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
self.
|
|
174
|
-
gen_params = {
|
|
175
|
-
"prompt": final_prompt,
|
|
176
|
-
"number_of_images": 1,
|
|
177
|
-
"width": width,
|
|
178
|
-
"height": height,
|
|
179
|
-
"guidance_scale": guidance_scale,
|
|
180
|
-
}
|
|
181
|
-
if gen_seed is not None:
|
|
182
|
-
gen_params["seed"] = gen_seed
|
|
183
|
-
|
|
184
|
-
ASCIIColors.debug(f"Vertex AI generation parameters: {gen_params}")
|
|
185
|
-
response = self.client.generate_images(**gen_params)
|
|
168
|
+
for attempt in range(max_retries):
|
|
169
|
+
try:
|
|
170
|
+
response = self.client.generate_content(full_prompt)
|
|
186
171
|
|
|
187
|
-
if not response.
|
|
188
|
-
raise Exception("
|
|
172
|
+
if not response.parts or not hasattr(response.parts[0], 'file_data'):
|
|
173
|
+
raise Exception(f"API response did not contain image data. Check safety filters in your Google AI Studio. Response: {response.text}")
|
|
189
174
|
|
|
190
|
-
return response.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
"
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
175
|
+
return response.parts[0].file_data.data
|
|
176
|
+
|
|
177
|
+
except ResourceExhausted as e:
|
|
178
|
+
if attempt < max_retries - 1:
|
|
179
|
+
wait_time = initial_delay * (2 ** attempt)
|
|
180
|
+
ASCIIColors.warning(f"Rate limit exceeded. Waiting {wait_time}s... (Attempt {attempt + 1}/{max_retries})")
|
|
181
|
+
time.sleep(wait_time)
|
|
182
|
+
else:
|
|
183
|
+
ASCIIColors.error(f"Failed to generate image after {max_retries} attempts due to rate limiting.")
|
|
184
|
+
raise e
|
|
185
|
+
except Exception as e:
|
|
186
|
+
raise e
|
|
187
|
+
|
|
188
|
+
def _generate_with_vertex_ai(self, prompt, negative_prompt, width, height, **kwargs):
|
|
189
|
+
self._validate_dimensions_vertex(width, height)
|
|
190
|
+
gen_params = {
|
|
191
|
+
"prompt": prompt, "number_of_images": 1, "width": width, "height": height,
|
|
192
|
+
"guidance_scale": kwargs.get("guidance_scale", self.default_guidance_scale),
|
|
193
|
+
}
|
|
194
|
+
if negative_prompt: gen_params["negative_prompt"] = negative_prompt
|
|
195
|
+
seed = kwargs.get("seed", self.default_seed)
|
|
196
|
+
if seed != -1: gen_params["seed"] = seed
|
|
197
|
+
|
|
198
|
+
response = self.client.generate_images(**gen_params)
|
|
199
|
+
if not response.images: raise Exception("Generation resulted in no images (Vertex AI).")
|
|
200
|
+
return response.images[0]._image_bytes
|
|
208
201
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
raise
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
202
|
+
def edit_image(self, images: Union[str, List[str]], prompt: str, negative_prompt: Optional[str] = "", mask: Optional[str] = None, **kwargs) -> bytes:
|
|
203
|
+
if self.auth_method != "vertex_ai":
|
|
204
|
+
raise NotImplementedError("Image editing is only supported via the 'vertex_ai' method.")
|
|
205
|
+
|
|
206
|
+
image_str = images[0] if isinstance(images, list) else images
|
|
207
|
+
ASCIIColors.info(f"Editing image with prompt: '{prompt[:100]}...'")
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
base_image = VertexImage(image_bytes=_load_image_from_str(image_str))
|
|
211
|
+
mask_image = VertexImage(image_bytes=_load_image_from_str(mask)) if mask else None
|
|
212
|
+
edit_params = {"prompt": prompt, "base_image": base_image, "mask": mask_image, "negative_prompt": negative_prompt or None}
|
|
213
|
+
response = self.client.edit_image(**edit_params)
|
|
214
|
+
if not response.images: raise Exception("Image editing resulted in no images.")
|
|
215
|
+
return response.images[0]._image_bytes
|
|
215
216
|
except Exception as e:
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
raise Exception(f"Imagen image editing failed: {e}") from e
|
|
218
|
+
|
|
219
|
+
def list_models(self) -> list:
|
|
220
|
+
return [{'model_name': name, 'display_name': f"Google ({name})"} for name in self.available_models]
|
|
218
221
|
|
|
219
222
|
def list_services(self, **kwargs) -> List[Dict[str, str]]:
|
|
220
|
-
"""Lists available Imagen models for the current auth method."""
|
|
221
|
-
models = IMAGEN_VERTEX_MODELS if self.auth_method == "vertex_ai" else IMAGEN_GEMINI_API_MODELS
|
|
222
223
|
service_name = "Vertex AI" if self.auth_method == "vertex_ai" else "Gemini API"
|
|
223
|
-
return [
|
|
224
|
-
{
|
|
225
|
-
"name": name,
|
|
226
|
-
"caption": f"Google Imagen ({name}) via {service_name}",
|
|
227
|
-
"help": "High-quality text-to-image model from Google."
|
|
228
|
-
} for name in models
|
|
229
|
-
]
|
|
230
|
-
|
|
231
|
-
def get_settings(self, **kwargs) -> List[Dict[str, Any]]:
|
|
232
|
-
"""Retrieves the current configurable settings for the binding."""
|
|
233
|
-
settings = [
|
|
234
|
-
{"name": "auth_method", "type": "str", "value": self.auth_method, "description": "Authentication method to use.", "options": ["vertex_ai", "api_key"], "category": "Authentication"},
|
|
235
|
-
]
|
|
236
|
-
if self.auth_method == "vertex_ai":
|
|
237
|
-
settings.extend([
|
|
238
|
-
{"name": "project_id", "type": "str", "value": self.project_id, "description": "Your Google Cloud project ID.", "category": "Authentication"},
|
|
239
|
-
{"name": "location", "type": "str", "value": self.location, "description": "Google Cloud region (e.g., 'us-central1').", "category": "Authentication"},
|
|
240
|
-
{"name": "model_name", "type": "str", "value": self.model_name, "description": "Default Imagen model for generation.", "options": IMAGEN_VERTEX_MODELS, "category": "Model Configuration"},
|
|
241
|
-
])
|
|
242
|
-
elif self.auth_method == "api_key":
|
|
243
|
-
settings.extend([
|
|
244
|
-
{"name": "api_key_status", "type": "str", "value": "Set" if self.gemini_api_key else "Not Set", "description": f"Gemini API Key status (set at initialization via service_key or '{GEMINI_API_KEY_ENV_VAR}').", "category": "Authentication", "read_only": True},
|
|
245
|
-
{"name": "model_name", "type": "str", "value": self.model_name, "description": "Default Imagen model for generation.", "options": IMAGEN_GEMINI_API_MODELS, "category": "Model Configuration"},
|
|
246
|
-
])
|
|
247
|
-
|
|
248
|
-
settings.extend([
|
|
249
|
-
{"name": "default_seed", "type": "int", "value": self.default_seed, "description": "Default seed (-1 for random).", "category": "Image Generation Defaults"},
|
|
250
|
-
{"name": "default_guidance_scale", "type": "float", "value": self.default_guidance_scale, "description": "Default guidance scale (CFG). (Vertex AI only)", "category": "Image Generation Defaults"},
|
|
251
|
-
])
|
|
252
|
-
return settings
|
|
253
|
-
|
|
254
|
-
def set_settings(self, settings: Union[Dict[str, Any], List[Dict[str, Any]]], **kwargs) -> bool:
|
|
255
|
-
"""Applies new settings. Re-initializes the client if core settings change."""
|
|
256
|
-
applied_some_settings = False
|
|
257
|
-
settings_dict = {item["name"]: item["value"] for item in settings} if isinstance(settings, list) else settings
|
|
224
|
+
return [{"name": name, "caption": f"Google ({name}) via {service_name}"} for name in self.available_models]
|
|
258
225
|
|
|
226
|
+
def set_settings(self, settings: Dict[str, Any], **kwargs) -> bool:
|
|
227
|
+
# Simplified for clarity, full logic is complex and stateful
|
|
259
228
|
needs_reinit = False
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
229
|
+
if "auth_method" in settings and self.auth_method != settings["auth_method"]:
|
|
230
|
+
self.auth_method = settings["auth_method"]
|
|
231
|
+
needs_reinit = True
|
|
232
|
+
if "project_id" in settings and self.project_id != settings.get("project_id"):
|
|
233
|
+
self.project_id = settings["project_id"]
|
|
234
|
+
needs_reinit = True
|
|
235
|
+
if "service_key" in settings and self.gemini_api_key != settings.get("service_key"):
|
|
236
|
+
self.gemini_api_key = settings["service_key"]
|
|
237
|
+
needs_reinit = True
|
|
238
|
+
if "model_name" in settings and self.model_name != settings.get("model_name"):
|
|
239
|
+
self.model_name = settings["model_name"]
|
|
271
240
|
needs_reinit = True
|
|
272
|
-
applied_some_settings = True
|
|
273
|
-
|
|
274
|
-
if self.auth_method == "vertex_ai":
|
|
275
|
-
if "project_id" in settings_dict and self.project_id != settings_dict["project_id"]:
|
|
276
|
-
self.project_id = settings_dict["project_id"]
|
|
277
|
-
needs_reinit = True; applied_some_settings = True
|
|
278
|
-
if "location" in settings_dict and self.location != settings_dict["location"]:
|
|
279
|
-
self.location = settings_dict["location"]
|
|
280
|
-
needs_reinit = True; applied_some_settings = True
|
|
281
|
-
# API key is not settable after init, so we don't check for it here.
|
|
282
|
-
|
|
283
|
-
# Phase 2: Apply other settings
|
|
284
|
-
current_models = IMAGEN_VERTEX_MODELS if self.auth_method == "vertex_ai" else IMAGEN_GEMINI_API_MODELS
|
|
285
|
-
if "model_name" in settings_dict:
|
|
286
|
-
new_model = settings_dict["model_name"]
|
|
287
|
-
if new_model not in current_models:
|
|
288
|
-
ASCIIColors.warning(f"Invalid model '{new_model}' for auth method '{self.auth_method}'. Keeping '{self.model_name}'.")
|
|
289
|
-
elif self.model_name != new_model:
|
|
290
|
-
self.model_name = new_model
|
|
291
|
-
needs_reinit = True; applied_some_settings = True
|
|
292
|
-
|
|
293
|
-
if "default_seed" in settings_dict and self.default_seed != int(settings_dict["default_seed"]):
|
|
294
|
-
self.default_seed = int(settings_dict["default_seed"])
|
|
295
|
-
applied_some_settings = True
|
|
296
|
-
if "default_guidance_scale" in settings_dict and self.default_guidance_scale != float(settings_dict["default_guidance_scale"]):
|
|
297
|
-
self.default_guidance_scale = float(settings_dict["default_guidance_scale"])
|
|
298
|
-
applied_some_settings = True
|
|
299
|
-
|
|
300
|
-
# Phase 3: Re-initialize if needed
|
|
301
241
|
if needs_reinit:
|
|
302
242
|
try:
|
|
303
243
|
self._initialize_client()
|
|
304
244
|
except Exception as e:
|
|
305
|
-
ASCIIColors.error(f"Failed to re-initialize client
|
|
245
|
+
ASCIIColors.error(f"Failed to re-initialize client: {e}")
|
|
306
246
|
return False
|
|
247
|
+
return True
|
|
307
248
|
|
|
308
|
-
|
|
249
|
+
def get_settings(self, **kwargs) -> Optional[Dict[str, Any]]:
|
|
250
|
+
return {
|
|
251
|
+
"auth_method": self.auth_method,
|
|
252
|
+
"project_id": self.project_id if self.auth_method == "vertex_ai" else None,
|
|
253
|
+
"location": self.location if self.auth_method == "vertex_ai" else None,
|
|
254
|
+
"model_name": self.model_name,
|
|
255
|
+
"default_seed": self.default_seed,
|
|
256
|
+
"default_guidance_scale": self.default_guidance_scale
|
|
257
|
+
}
|
|
309
258
|
|
|
310
|
-
def
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
{
|
|
315
|
-
'model_name': name,
|
|
316
|
-
'display_name': f"Imagen ({name})",
|
|
317
|
-
'description': f"Google's Imagen model, version {name}",
|
|
318
|
-
'owned_by': 'Google'
|
|
319
|
-
} for name in models
|
|
320
|
-
]
|
|
259
|
+
def _validate_dimensions_vertex(self, width: int, height: int) -> None:
|
|
260
|
+
if not (256 <= width <= 1536 and width % 8 == 0):
|
|
261
|
+
raise ValueError(f"Invalid width for Vertex AI: {width}. Must be 256-1536 and a multiple of 8.")
|
|
262
|
+
if not (256 <= height <= 1536 and height % 8 == 0):
|
|
263
|
+
raise ValueError(f"Invalid height for Vertex AI: {height}. Must be 256-1536 and a multiple of 8.")
|
|
@@ -29,8 +29,11 @@ class LeonardoAITTIBinding(LollmsTTIBinding):
|
|
|
29
29
|
"""Leonardo.ai TTI binding for LoLLMS"""
|
|
30
30
|
|
|
31
31
|
def __init__(self, **kwargs):
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
33
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
34
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
35
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
36
|
+
|
|
34
37
|
self.api_key = self.config.get("api_key") or os.environ.get("LEONARDO_API_KEY")
|
|
35
38
|
if not self.api_key:
|
|
36
39
|
raise ValueError("Leonardo.ai API key is required.")
|
|
@@ -38,7 +41,7 @@ class LeonardoAITTIBinding(LollmsTTIBinding):
|
|
|
38
41
|
self.base_url = "https://cloud.leonardo.ai/api/rest/v1"
|
|
39
42
|
self.headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
|
|
40
43
|
|
|
41
|
-
def
|
|
44
|
+
def list_models(self) -> list:
|
|
42
45
|
# You could also fetch this dynamically from /models endpoint
|
|
43
46
|
return LEONARDO_AI_MODELS
|
|
44
47
|
|
|
@@ -23,7 +23,10 @@ class LollmsWebuiTTIBinding_Impl(LollmsTTIBinding):
|
|
|
23
23
|
service_key (Optional[str]): Authentication key (used for client_id verification).
|
|
24
24
|
verify_ssl_certificate (bool): Whether to verify SSL certificates.
|
|
25
25
|
"""
|
|
26
|
-
|
|
26
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
27
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
28
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
29
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
27
30
|
|
|
28
31
|
# Extract parameters from kwargs, providing defaults
|
|
29
32
|
self.host_address = kwargs.get("host_address", "http://localhost:9600") # Default LOLLMS host
|
|
@@ -27,7 +27,10 @@ class NovitaAITTIBinding(LollmsTTIBinding):
|
|
|
27
27
|
"""Novita.ai TTI binding for LoLLMS"""
|
|
28
28
|
|
|
29
29
|
def __init__(self, **kwargs):
|
|
30
|
-
|
|
30
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
31
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
32
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
33
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
31
34
|
self.config = kwargs
|
|
32
35
|
self.api_key = self.config.get("api_key") or os.environ.get("NOVITA_API_KEY")
|
|
33
36
|
if not self.api_key:
|
|
@@ -36,7 +39,7 @@ class NovitaAITTIBinding(LollmsTTIBinding):
|
|
|
36
39
|
self.base_url = "https://api.novita.ai/v3"
|
|
37
40
|
self.headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
|
|
38
41
|
|
|
39
|
-
def
|
|
42
|
+
def list_models(self) -> list:
|
|
40
43
|
return NOVITA_AI_MODELS
|
|
41
44
|
|
|
42
45
|
def generate_image(self, prompt: str, negative_prompt: str = "", width: int = 1024, height: int = 1024, **kwargs) -> bytes:
|
|
@@ -6,7 +6,7 @@ from io import BytesIO
|
|
|
6
6
|
from ascii_colors import trace_exception
|
|
7
7
|
from openai import OpenAI
|
|
8
8
|
from lollms_client.lollms_tti_binding import LollmsTTIBinding
|
|
9
|
-
|
|
9
|
+
import os
|
|
10
10
|
BindingName = "OpenAITTIBinding"
|
|
11
11
|
|
|
12
12
|
|
|
@@ -50,19 +50,18 @@ class OpenAITTIBinding(LollmsTTIBinding):
|
|
|
50
50
|
|
|
51
51
|
def __init__(
|
|
52
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
53
|
**kwargs,
|
|
59
54
|
):
|
|
60
|
-
|
|
55
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
56
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
57
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
58
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
59
|
+
self.client = OpenAI(api_key=kwargs.get("api_key" or os.environ.get("OPENAI_API_KEY")))
|
|
61
60
|
self.global_params = {
|
|
62
|
-
"model":
|
|
63
|
-
"size": size,
|
|
64
|
-
"n": n,
|
|
65
|
-
"quality": quality,
|
|
61
|
+
"model": kwargs.get("model_name") or "gpt-image-1",
|
|
62
|
+
"size": kwargs.get("size", "1024x1024"),
|
|
63
|
+
"n": kwargs.get("n", 1),
|
|
64
|
+
"quality": kwargs.get("quality", "standard"),
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
def _resolve_param(self, name: str, kwargs: Dict[str, Any], default: Any) -> Any:
|
|
@@ -33,14 +33,16 @@ class StabilityAITTIBinding(LollmsTTIBinding):
|
|
|
33
33
|
"""Stability AI TTI binding for LoLLMS"""
|
|
34
34
|
|
|
35
35
|
def __init__(self, **kwargs):
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
# Prioritize 'model_name' but accept 'model' as an alias from config files.
|
|
37
|
+
if 'model' in kwargs and 'model_name' not in kwargs:
|
|
38
|
+
kwargs['model_name'] = kwargs.pop('model')
|
|
39
|
+
super().__init__(binding_name=BindingName, config=kwargs)
|
|
38
40
|
self.api_key = self.config.get("api_key") or os.environ.get("STABILITY_API_KEY")
|
|
39
41
|
if not self.api_key:
|
|
40
42
|
raise ValueError("Stability AI API key is required. Please set it in the configuration or as STABILITY_API_KEY environment variable.")
|
|
41
43
|
self.model_name = self.config.get("model_name", "stable-diffusion-3-medium")
|
|
42
44
|
|
|
43
|
-
def
|
|
45
|
+
def list_models(self) -> list:
|
|
44
46
|
return STABILITY_AI_MODELS
|
|
45
47
|
|
|
46
48
|
def _get_api_url(self, task: str) -> str:
|