nexaai 1.0.21rc5__cp313-cp313-win_arm64.whl → 1.0.21rc14__cp313-cp313-win_arm64.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 nexaai might be problematic. Click here for more details.

Files changed (105) hide show
  1. nexaai/__init__.py +95 -95
  2. nexaai/_stub.cp313-win_arm64.pyd +0 -0
  3. nexaai/_version.py +4 -1
  4. nexaai/asr.py +68 -65
  5. nexaai/asr_impl/mlx_asr_impl.py +92 -92
  6. nexaai/asr_impl/pybind_asr_impl.py +127 -44
  7. nexaai/base.py +39 -39
  8. nexaai/binds/__init__.py +6 -5
  9. nexaai/binds/asr_bind.cp313-win_arm64.pyd +0 -0
  10. nexaai/binds/common_bind.cp313-win_arm64.pyd +0 -0
  11. nexaai/binds/cpu_gpu/ggml-base.dll +0 -0
  12. nexaai/binds/cpu_gpu/ggml-cpu.dll +0 -0
  13. nexaai/binds/cpu_gpu/ggml-opencl.dll +0 -0
  14. nexaai/binds/cpu_gpu/ggml.dll +0 -0
  15. nexaai/binds/cpu_gpu/mtmd.dll +0 -0
  16. nexaai/binds/cpu_gpu/nexa_cpu_gpu.dll +0 -0
  17. nexaai/binds/cpu_gpu/nexa_plugin.dll +0 -0
  18. nexaai/binds/embedder_bind.cp313-win_arm64.pyd +0 -0
  19. nexaai/binds/libcrypto-3-arm64.dll +0 -0
  20. nexaai/binds/libssl-3-arm64.dll +0 -0
  21. nexaai/binds/llm_bind.cp313-win_arm64.pyd +0 -0
  22. nexaai/binds/nexa_bridge.dll +0 -0
  23. nexaai/binds/npu/convnext-sdk.dll +0 -0
  24. nexaai/binds/npu/embed-gemma-sdk.dll +0 -0
  25. nexaai/binds/npu/ggml-base.dll +0 -0
  26. nexaai/binds/npu/ggml-cpu.dll +0 -0
  27. nexaai/binds/npu/ggml-opencl.dll +0 -0
  28. nexaai/binds/npu/ggml.dll +0 -0
  29. nexaai/binds/npu/granite-nano-sdk.dll +0 -0
  30. nexaai/binds/npu/granite4-sdk.dll +0 -0
  31. nexaai/binds/npu/jina-rerank-sdk.dll +0 -0
  32. nexaai/binds/npu/liquid-sdk.dll +0 -0
  33. nexaai/binds/npu/llama3-3b-sdk.dll +0 -0
  34. nexaai/binds/npu/nexa-mm-process.dll +0 -0
  35. nexaai/binds/npu/nexa-sampling.dll +0 -0
  36. nexaai/binds/npu/nexa_plugin.dll +0 -0
  37. nexaai/binds/npu/omni-neural-sdk.dll +0 -0
  38. nexaai/binds/npu/openblas.dll +0 -0
  39. nexaai/binds/npu/paddleocr-sdk.dll +0 -0
  40. nexaai/binds/npu/parakeet-sdk.dll +0 -0
  41. nexaai/binds/npu/phi3-5-sdk.dll +0 -0
  42. nexaai/binds/npu/phi4-sdk.dll +0 -0
  43. nexaai/binds/npu/pyannote-sdk.dll +0 -0
  44. nexaai/binds/npu/qwen3-4b-sdk.dll +0 -0
  45. nexaai/binds/npu/qwen3vl-sdk.dll +0 -0
  46. nexaai/binds/npu/qwen3vl-vision.dll +0 -0
  47. nexaai/binds/npu/yolov12-sdk.dll +0 -0
  48. nexaai/binds/npu/zlib1.dll +0 -0
  49. nexaai/binds/rerank_bind.cp313-win_arm64.pyd +0 -0
  50. nexaai/binds/vlm_bind.cp313-win_arm64.pyd +0 -0
  51. nexaai/common.py +105 -105
  52. nexaai/cv.py +93 -93
  53. nexaai/cv_impl/mlx_cv_impl.py +89 -89
  54. nexaai/cv_impl/pybind_cv_impl.py +32 -32
  55. nexaai/embedder.py +73 -73
  56. nexaai/embedder_impl/mlx_embedder_impl.py +118 -118
  57. nexaai/embedder_impl/pybind_embedder_impl.py +96 -96
  58. nexaai/image_gen.py +141 -141
  59. nexaai/image_gen_impl/mlx_image_gen_impl.py +292 -292
  60. nexaai/image_gen_impl/pybind_image_gen_impl.py +85 -85
  61. nexaai/llm.py +98 -98
  62. nexaai/llm_impl/mlx_llm_impl.py +271 -271
  63. nexaai/llm_impl/pybind_llm_impl.py +220 -220
  64. nexaai/log.py +92 -92
  65. nexaai/rerank.py +57 -57
  66. nexaai/rerank_impl/mlx_rerank_impl.py +94 -94
  67. nexaai/rerank_impl/pybind_rerank_impl.py +136 -136
  68. nexaai/runtime.py +68 -68
  69. nexaai/runtime_error.py +24 -24
  70. nexaai/tts.py +75 -75
  71. nexaai/tts_impl/mlx_tts_impl.py +94 -94
  72. nexaai/tts_impl/pybind_tts_impl.py +43 -43
  73. nexaai/utils/decode.py +17 -17
  74. nexaai/utils/manifest_utils.py +531 -531
  75. nexaai/utils/model_manager.py +1562 -1562
  76. nexaai/utils/model_types.py +49 -49
  77. nexaai/utils/progress_tracker.py +384 -384
  78. nexaai/utils/quantization_utils.py +245 -245
  79. nexaai/vlm.py +129 -129
  80. nexaai/vlm_impl/mlx_vlm_impl.py +258 -258
  81. nexaai/vlm_impl/pybind_vlm_impl.py +256 -256
  82. {nexaai-1.0.21rc5.dist-info → nexaai-1.0.21rc14.dist-info}/METADATA +1 -1
  83. nexaai-1.0.21rc14.dist-info/RECORD +154 -0
  84. nexaai/binds/nexaml/FLAC.dll +0 -0
  85. nexaai/binds/nexaml/fftw3.dll +0 -0
  86. nexaai/binds/nexaml/fftw3f.dll +0 -0
  87. nexaai/binds/nexaml/ggml-base.dll +0 -0
  88. nexaai/binds/nexaml/ggml-cpu.dll +0 -0
  89. nexaai/binds/nexaml/ggml-opencl.dll +0 -0
  90. nexaai/binds/nexaml/ggml.dll +0 -0
  91. nexaai/binds/nexaml/libmp3lame.DLL +0 -0
  92. nexaai/binds/nexaml/mpg123.dll +0 -0
  93. nexaai/binds/nexaml/nexa-mm-process.dll +0 -0
  94. nexaai/binds/nexaml/nexa-sampling.dll +0 -0
  95. nexaai/binds/nexaml/nexa_plugin.dll +0 -0
  96. nexaai/binds/nexaml/nexaproc.dll +0 -0
  97. nexaai/binds/nexaml/ogg.dll +0 -0
  98. nexaai/binds/nexaml/opus.dll +0 -0
  99. nexaai/binds/nexaml/qwen3-vl.dll +0 -0
  100. nexaai/binds/nexaml/qwen3vl-vision.dll +0 -0
  101. nexaai/binds/nexaml/vorbis.dll +0 -0
  102. nexaai/binds/nexaml/vorbisenc.dll +0 -0
  103. nexaai-1.0.21rc5.dist-info/RECORD +0 -162
  104. {nexaai-1.0.21rc5.dist-info → nexaai-1.0.21rc14.dist-info}/WHEEL +0 -0
  105. {nexaai-1.0.21rc5.dist-info → nexaai-1.0.21rc14.dist-info}/top_level.txt +0 -0
@@ -1,292 +1,292 @@
1
- # Note: This code is generated by Cursor, not tested yet.
2
-
3
- from typing import List, Optional, Union
4
- import os
5
-
6
- from nexaai.common import PluginID
7
- from nexaai.image_gen import ImageGen, ImageGenerationConfig, ImageSamplerConfig, SchedulerConfig, Image
8
- from nexaai.mlx_backend.sd.interface import ImageGen as MLXImageGenInterface
9
-
10
-
11
- class MLXImageGenImpl(ImageGen):
12
- def __init__(self):
13
- """Initialize MLX Image Generation implementation."""
14
- super().__init__()
15
- self._mlx_image_gen = None
16
-
17
- @classmethod
18
- def _load_from(cls,
19
- model_path: str,
20
- scheduler_config_path: str = "",
21
- plugin_id: Union[PluginID, str] = PluginID.MLX,
22
- device_id: Optional[str] = None,
23
- float16: bool = True,
24
- quantize: bool = False
25
- ) -> 'MLXImageGenImpl':
26
- """Load image generation model from local path using MLX backend."""
27
- try:
28
- # MLX Image Generation interface is already imported
29
-
30
- # Create instance and load MLX image generation model
31
- instance = cls()
32
- instance._mlx_image_gen = MLXImageGenInterface(
33
- model_path=model_path,
34
- scheduler_config_path=scheduler_config_path,
35
- device=device_id,
36
- float16=float16,
37
- quantize=quantize
38
- )
39
-
40
- return instance
41
- except Exception as e:
42
- raise RuntimeError(f"Failed to load MLX Image Generation: {str(e)}")
43
-
44
- def eject(self):
45
- """Destroy the model and free resources."""
46
- if self._mlx_image_gen:
47
- self._mlx_image_gen.destroy()
48
- self._mlx_image_gen = None
49
-
50
- def load_model(self, model_path: str, extra_data: Optional[str] = None) -> bool:
51
- """Load model from path."""
52
- if not self._mlx_image_gen:
53
- raise RuntimeError("MLX Image Generator not initialized")
54
-
55
- try:
56
- return self._mlx_image_gen.load_model(model_path, extra_data)
57
- except Exception as e:
58
- raise RuntimeError(f"Failed to load image generation model: {str(e)}")
59
-
60
- def set_scheduler(self, config: SchedulerConfig) -> None:
61
- """Set scheduler configuration."""
62
- if not self._mlx_image_gen:
63
- raise RuntimeError("MLX Image Generator not loaded")
64
-
65
- try:
66
- # Convert our config to MLX format
67
- import sys
68
- mlx_path = self._get_mlx_path()
69
- if mlx_path not in sys.path:
70
- sys.path.insert(0, mlx_path)
71
-
72
- # MLX ImageGen uses a simpler interface, we'll store the config for use in generation
73
- self._scheduler_config = config
74
- self._mlx_image_gen.set_scheduler(config)
75
- except Exception as e:
76
- raise RuntimeError(f"Failed to set scheduler: {str(e)}")
77
-
78
- def set_sampler(self, config: ImageSamplerConfig) -> None:
79
- """Set sampler configuration."""
80
- if not self._mlx_image_gen:
81
- raise RuntimeError("MLX Image Generator not loaded")
82
-
83
- try:
84
- # Convert our config to MLX format
85
- import sys
86
- mlx_path = self._get_mlx_path()
87
- if mlx_path not in sys.path:
88
- sys.path.insert(0, mlx_path)
89
- from sd.interface import ImageSamplerConfig as MLXImageSamplerConfig
90
-
91
- mlx_config = MLXImageSamplerConfig(
92
- method=config.method,
93
- steps=config.steps,
94
- guidance_scale=config.guidance_scale,
95
- eta=config.eta,
96
- seed=config.seed
97
- )
98
-
99
- self._mlx_image_gen.set_sampler(mlx_config)
100
- except Exception as e:
101
- raise RuntimeError(f"Failed to set sampler: {str(e)}")
102
-
103
- def reset_sampler(self) -> None:
104
- """Reset sampler to default configuration."""
105
- if not self._mlx_image_gen:
106
- raise RuntimeError("MLX Image Generator not loaded")
107
-
108
- try:
109
- self._mlx_image_gen.reset_sampler()
110
- except Exception as e:
111
- raise RuntimeError(f"Failed to reset sampler: {str(e)}")
112
-
113
- def txt2img(self, prompt: str, config: ImageGenerationConfig) -> Image:
114
- """Generate image from text prompt."""
115
- if not self._mlx_image_gen:
116
- raise RuntimeError("MLX Image Generator not loaded")
117
-
118
- try:
119
- # Convert our config to MLX format
120
- import sys
121
- mlx_path = self._get_mlx_path()
122
- if mlx_path not in sys.path:
123
- sys.path.insert(0, mlx_path)
124
- from sd.interface import ImageGenerationConfig as MLXImageGenerationConfig
125
-
126
- mlx_config = self._convert_generation_config(config)
127
-
128
- # Use MLX text-to-image generation
129
- result = self._mlx_image_gen.txt2img(prompt, mlx_config)
130
-
131
- # Convert MLX result to our format
132
- return self._convert_image_result(result)
133
-
134
- except Exception as e:
135
- raise RuntimeError(f"Failed to generate image from text: {str(e)}")
136
-
137
- def img2img(self, init_image: Image, prompt: str, config: ImageGenerationConfig) -> Image:
138
- """Generate image from initial image and text prompt."""
139
- if not self._mlx_image_gen:
140
- raise RuntimeError("MLX Image Generator not loaded")
141
-
142
- try:
143
- # Convert our config to MLX format
144
- mlx_config = self._convert_generation_config(config)
145
-
146
- # Convert our image to MLX format
147
- import sys
148
- mlx_path = self._get_mlx_path()
149
- if mlx_path not in sys.path:
150
- sys.path.insert(0, mlx_path)
151
- from sd.interface import Image as MLXImage
152
-
153
- mlx_init_image = MLXImage(
154
- data=init_image.data,
155
- width=init_image.width,
156
- height=init_image.height,
157
- channels=init_image.channels
158
- )
159
-
160
- # Use MLX image-to-image generation
161
- result = self._mlx_image_gen.img2img(mlx_init_image, prompt, mlx_config)
162
-
163
- # Convert MLX result to our format
164
- return self._convert_image_result(result)
165
-
166
- except Exception as e:
167
- raise RuntimeError(f"Failed to generate image from image: {str(e)}")
168
-
169
- def generate(self, config: ImageGenerationConfig) -> Image:
170
- """Generate image from configuration."""
171
- if not self._mlx_image_gen:
172
- raise RuntimeError("MLX Image Generator not loaded")
173
-
174
- try:
175
- # Convert our config to MLX format
176
- mlx_config = self._convert_generation_config(config)
177
-
178
- # Use MLX generation
179
- result = self._mlx_image_gen.generate(mlx_config)
180
-
181
- # Convert MLX result to our format
182
- return self._convert_image_result(result)
183
-
184
- except Exception as e:
185
- raise RuntimeError(f"Failed to generate image: {str(e)}")
186
-
187
- def set_lora(self, lora_id: int) -> None:
188
- """Set active LoRA adapter."""
189
- if not self._mlx_image_gen:
190
- raise RuntimeError("MLX Image Generator not loaded")
191
-
192
- try:
193
- self._mlx_image_gen.set_lora(lora_id)
194
- except Exception as e:
195
- raise RuntimeError(f"Failed to set LoRA: {str(e)}")
196
-
197
- def add_lora(self, lora_path: str) -> int:
198
- """Add LoRA adapter and return its ID."""
199
- if not self._mlx_image_gen:
200
- raise RuntimeError("MLX Image Generator not loaded")
201
-
202
- try:
203
- return self._mlx_image_gen.add_lora(lora_path)
204
- except Exception as e:
205
- raise RuntimeError(f"Failed to add LoRA: {str(e)}")
206
-
207
- def remove_lora(self, lora_id: int) -> None:
208
- """Remove LoRA adapter."""
209
- if not self._mlx_image_gen:
210
- raise RuntimeError("MLX Image Generator not loaded")
211
-
212
- try:
213
- self._mlx_image_gen.remove_lora(lora_id)
214
- except Exception as e:
215
- raise RuntimeError(f"Failed to remove LoRA: {str(e)}")
216
-
217
- def list_loras(self) -> List[int]:
218
- """List available LoRA adapters."""
219
- if not self._mlx_image_gen:
220
- raise RuntimeError("MLX Image Generator not loaded")
221
-
222
- try:
223
- return self._mlx_image_gen.list_loras()
224
- except Exception as e:
225
- raise RuntimeError(f"Failed to list LoRAs: {str(e)}")
226
-
227
- def _convert_generation_config(self, config: ImageGenerationConfig):
228
- """Convert our config to MLX format."""
229
- import sys
230
- mlx_path = self._get_mlx_path()
231
- if mlx_path not in sys.path:
232
- sys.path.insert(0, mlx_path)
233
- from sd.interface import ImageGenerationConfig as MLXImageGenerationConfig, ImageSamplerConfig as MLXImageSamplerConfig
234
-
235
- mlx_sampler_config = None
236
- if config.sampler_config:
237
- mlx_sampler_config = MLXImageSamplerConfig(
238
- method=config.sampler_config.method,
239
- steps=config.sampler_config.steps,
240
- guidance_scale=config.sampler_config.guidance_scale,
241
- eta=config.sampler_config.eta,
242
- seed=config.sampler_config.seed
243
- )
244
-
245
- mlx_init_image = None
246
- if config.init_image:
247
- from sd.interface import Image as MLXImage
248
- mlx_init_image = MLXImage(
249
- data=config.init_image.data,
250
- width=config.init_image.width,
251
- height=config.init_image.height,
252
- channels=config.init_image.channels
253
- )
254
-
255
- return MLXImageGenerationConfig(
256
- prompts=config.prompts,
257
- negative_prompts=config.negative_prompts,
258
- height=config.height,
259
- width=config.width,
260
- sampler_config=mlx_sampler_config,
261
- lora_id=config.lora_id,
262
- init_image=mlx_init_image,
263
- strength=config.strength,
264
- n_images=config.n_images,
265
- n_rows=config.n_rows,
266
- decoding_batch_size=config.decoding_batch_size
267
- )
268
-
269
- def _convert_image_result(self, mlx_image):
270
- """Convert MLX image to our format."""
271
- return Image(
272
- data=mlx_image.data,
273
- width=mlx_image.width,
274
- height=mlx_image.height,
275
- channels=mlx_image.channels
276
- )
277
-
278
- @staticmethod
279
- def _get_mlx_path():
280
- """Get the MLX py-lib path."""
281
- from pathlib import Path
282
- current_dir = Path(__file__).parent.parent.parent.parent # Go back to nexa root
283
- possible_paths = [
284
- current_dir / "plugins" / "mlx" / "py-lib",
285
- Path(os.environ.get("NEXA_MLX_PATH", "")) if os.environ.get("NEXA_MLX_PATH") else None,
286
- ]
287
-
288
- for path in possible_paths:
289
- if path and path.exists() and (path / "sd" / "interface.py").exists():
290
- return str(path.absolute())
291
-
292
- raise ImportError("Could not find MLX py-lib directory")
1
+ # Note: This code is generated by Cursor, not tested yet.
2
+
3
+ from typing import List, Optional, Union
4
+ import os
5
+
6
+ from nexaai.common import PluginID
7
+ from nexaai.image_gen import ImageGen, ImageGenerationConfig, ImageSamplerConfig, SchedulerConfig, Image
8
+ from nexaai.mlx_backend.sd.interface import ImageGen as MLXImageGenInterface
9
+
10
+
11
+ class MLXImageGenImpl(ImageGen):
12
+ def __init__(self):
13
+ """Initialize MLX Image Generation implementation."""
14
+ super().__init__()
15
+ self._mlx_image_gen = None
16
+
17
+ @classmethod
18
+ def _load_from(cls,
19
+ model_path: str,
20
+ scheduler_config_path: str = "",
21
+ plugin_id: Union[PluginID, str] = PluginID.MLX,
22
+ device_id: Optional[str] = None,
23
+ float16: bool = True,
24
+ quantize: bool = False
25
+ ) -> 'MLXImageGenImpl':
26
+ """Load image generation model from local path using MLX backend."""
27
+ try:
28
+ # MLX Image Generation interface is already imported
29
+
30
+ # Create instance and load MLX image generation model
31
+ instance = cls()
32
+ instance._mlx_image_gen = MLXImageGenInterface(
33
+ model_path=model_path,
34
+ scheduler_config_path=scheduler_config_path,
35
+ device=device_id,
36
+ float16=float16,
37
+ quantize=quantize
38
+ )
39
+
40
+ return instance
41
+ except Exception as e:
42
+ raise RuntimeError(f"Failed to load MLX Image Generation: {str(e)}")
43
+
44
+ def eject(self):
45
+ """Destroy the model and free resources."""
46
+ if self._mlx_image_gen:
47
+ self._mlx_image_gen.destroy()
48
+ self._mlx_image_gen = None
49
+
50
+ def load_model(self, model_path: str, extra_data: Optional[str] = None) -> bool:
51
+ """Load model from path."""
52
+ if not self._mlx_image_gen:
53
+ raise RuntimeError("MLX Image Generator not initialized")
54
+
55
+ try:
56
+ return self._mlx_image_gen.load_model(model_path, extra_data)
57
+ except Exception as e:
58
+ raise RuntimeError(f"Failed to load image generation model: {str(e)}")
59
+
60
+ def set_scheduler(self, config: SchedulerConfig) -> None:
61
+ """Set scheduler configuration."""
62
+ if not self._mlx_image_gen:
63
+ raise RuntimeError("MLX Image Generator not loaded")
64
+
65
+ try:
66
+ # Convert our config to MLX format
67
+ import sys
68
+ mlx_path = self._get_mlx_path()
69
+ if mlx_path not in sys.path:
70
+ sys.path.insert(0, mlx_path)
71
+
72
+ # MLX ImageGen uses a simpler interface, we'll store the config for use in generation
73
+ self._scheduler_config = config
74
+ self._mlx_image_gen.set_scheduler(config)
75
+ except Exception as e:
76
+ raise RuntimeError(f"Failed to set scheduler: {str(e)}")
77
+
78
+ def set_sampler(self, config: ImageSamplerConfig) -> None:
79
+ """Set sampler configuration."""
80
+ if not self._mlx_image_gen:
81
+ raise RuntimeError("MLX Image Generator not loaded")
82
+
83
+ try:
84
+ # Convert our config to MLX format
85
+ import sys
86
+ mlx_path = self._get_mlx_path()
87
+ if mlx_path not in sys.path:
88
+ sys.path.insert(0, mlx_path)
89
+ from sd.interface import ImageSamplerConfig as MLXImageSamplerConfig
90
+
91
+ mlx_config = MLXImageSamplerConfig(
92
+ method=config.method,
93
+ steps=config.steps,
94
+ guidance_scale=config.guidance_scale,
95
+ eta=config.eta,
96
+ seed=config.seed
97
+ )
98
+
99
+ self._mlx_image_gen.set_sampler(mlx_config)
100
+ except Exception as e:
101
+ raise RuntimeError(f"Failed to set sampler: {str(e)}")
102
+
103
+ def reset_sampler(self) -> None:
104
+ """Reset sampler to default configuration."""
105
+ if not self._mlx_image_gen:
106
+ raise RuntimeError("MLX Image Generator not loaded")
107
+
108
+ try:
109
+ self._mlx_image_gen.reset_sampler()
110
+ except Exception as e:
111
+ raise RuntimeError(f"Failed to reset sampler: {str(e)}")
112
+
113
+ def txt2img(self, prompt: str, config: ImageGenerationConfig) -> Image:
114
+ """Generate image from text prompt."""
115
+ if not self._mlx_image_gen:
116
+ raise RuntimeError("MLX Image Generator not loaded")
117
+
118
+ try:
119
+ # Convert our config to MLX format
120
+ import sys
121
+ mlx_path = self._get_mlx_path()
122
+ if mlx_path not in sys.path:
123
+ sys.path.insert(0, mlx_path)
124
+ from sd.interface import ImageGenerationConfig as MLXImageGenerationConfig
125
+
126
+ mlx_config = self._convert_generation_config(config)
127
+
128
+ # Use MLX text-to-image generation
129
+ result = self._mlx_image_gen.txt2img(prompt, mlx_config)
130
+
131
+ # Convert MLX result to our format
132
+ return self._convert_image_result(result)
133
+
134
+ except Exception as e:
135
+ raise RuntimeError(f"Failed to generate image from text: {str(e)}")
136
+
137
+ def img2img(self, init_image: Image, prompt: str, config: ImageGenerationConfig) -> Image:
138
+ """Generate image from initial image and text prompt."""
139
+ if not self._mlx_image_gen:
140
+ raise RuntimeError("MLX Image Generator not loaded")
141
+
142
+ try:
143
+ # Convert our config to MLX format
144
+ mlx_config = self._convert_generation_config(config)
145
+
146
+ # Convert our image to MLX format
147
+ import sys
148
+ mlx_path = self._get_mlx_path()
149
+ if mlx_path not in sys.path:
150
+ sys.path.insert(0, mlx_path)
151
+ from sd.interface import Image as MLXImage
152
+
153
+ mlx_init_image = MLXImage(
154
+ data=init_image.data,
155
+ width=init_image.width,
156
+ height=init_image.height,
157
+ channels=init_image.channels
158
+ )
159
+
160
+ # Use MLX image-to-image generation
161
+ result = self._mlx_image_gen.img2img(mlx_init_image, prompt, mlx_config)
162
+
163
+ # Convert MLX result to our format
164
+ return self._convert_image_result(result)
165
+
166
+ except Exception as e:
167
+ raise RuntimeError(f"Failed to generate image from image: {str(e)}")
168
+
169
+ def generate(self, config: ImageGenerationConfig) -> Image:
170
+ """Generate image from configuration."""
171
+ if not self._mlx_image_gen:
172
+ raise RuntimeError("MLX Image Generator not loaded")
173
+
174
+ try:
175
+ # Convert our config to MLX format
176
+ mlx_config = self._convert_generation_config(config)
177
+
178
+ # Use MLX generation
179
+ result = self._mlx_image_gen.generate(mlx_config)
180
+
181
+ # Convert MLX result to our format
182
+ return self._convert_image_result(result)
183
+
184
+ except Exception as e:
185
+ raise RuntimeError(f"Failed to generate image: {str(e)}")
186
+
187
+ def set_lora(self, lora_id: int) -> None:
188
+ """Set active LoRA adapter."""
189
+ if not self._mlx_image_gen:
190
+ raise RuntimeError("MLX Image Generator not loaded")
191
+
192
+ try:
193
+ self._mlx_image_gen.set_lora(lora_id)
194
+ except Exception as e:
195
+ raise RuntimeError(f"Failed to set LoRA: {str(e)}")
196
+
197
+ def add_lora(self, lora_path: str) -> int:
198
+ """Add LoRA adapter and return its ID."""
199
+ if not self._mlx_image_gen:
200
+ raise RuntimeError("MLX Image Generator not loaded")
201
+
202
+ try:
203
+ return self._mlx_image_gen.add_lora(lora_path)
204
+ except Exception as e:
205
+ raise RuntimeError(f"Failed to add LoRA: {str(e)}")
206
+
207
+ def remove_lora(self, lora_id: int) -> None:
208
+ """Remove LoRA adapter."""
209
+ if not self._mlx_image_gen:
210
+ raise RuntimeError("MLX Image Generator not loaded")
211
+
212
+ try:
213
+ self._mlx_image_gen.remove_lora(lora_id)
214
+ except Exception as e:
215
+ raise RuntimeError(f"Failed to remove LoRA: {str(e)}")
216
+
217
+ def list_loras(self) -> List[int]:
218
+ """List available LoRA adapters."""
219
+ if not self._mlx_image_gen:
220
+ raise RuntimeError("MLX Image Generator not loaded")
221
+
222
+ try:
223
+ return self._mlx_image_gen.list_loras()
224
+ except Exception as e:
225
+ raise RuntimeError(f"Failed to list LoRAs: {str(e)}")
226
+
227
+ def _convert_generation_config(self, config: ImageGenerationConfig):
228
+ """Convert our config to MLX format."""
229
+ import sys
230
+ mlx_path = self._get_mlx_path()
231
+ if mlx_path not in sys.path:
232
+ sys.path.insert(0, mlx_path)
233
+ from sd.interface import ImageGenerationConfig as MLXImageGenerationConfig, ImageSamplerConfig as MLXImageSamplerConfig
234
+
235
+ mlx_sampler_config = None
236
+ if config.sampler_config:
237
+ mlx_sampler_config = MLXImageSamplerConfig(
238
+ method=config.sampler_config.method,
239
+ steps=config.sampler_config.steps,
240
+ guidance_scale=config.sampler_config.guidance_scale,
241
+ eta=config.sampler_config.eta,
242
+ seed=config.sampler_config.seed
243
+ )
244
+
245
+ mlx_init_image = None
246
+ if config.init_image:
247
+ from sd.interface import Image as MLXImage
248
+ mlx_init_image = MLXImage(
249
+ data=config.init_image.data,
250
+ width=config.init_image.width,
251
+ height=config.init_image.height,
252
+ channels=config.init_image.channels
253
+ )
254
+
255
+ return MLXImageGenerationConfig(
256
+ prompts=config.prompts,
257
+ negative_prompts=config.negative_prompts,
258
+ height=config.height,
259
+ width=config.width,
260
+ sampler_config=mlx_sampler_config,
261
+ lora_id=config.lora_id,
262
+ init_image=mlx_init_image,
263
+ strength=config.strength,
264
+ n_images=config.n_images,
265
+ n_rows=config.n_rows,
266
+ decoding_batch_size=config.decoding_batch_size
267
+ )
268
+
269
+ def _convert_image_result(self, mlx_image):
270
+ """Convert MLX image to our format."""
271
+ return Image(
272
+ data=mlx_image.data,
273
+ width=mlx_image.width,
274
+ height=mlx_image.height,
275
+ channels=mlx_image.channels
276
+ )
277
+
278
+ @staticmethod
279
+ def _get_mlx_path():
280
+ """Get the MLX py-lib path."""
281
+ from pathlib import Path
282
+ current_dir = Path(__file__).parent.parent.parent.parent # Go back to nexa root
283
+ possible_paths = [
284
+ current_dir / "plugins" / "mlx" / "py-lib",
285
+ Path(os.environ.get("NEXA_MLX_PATH", "")) if os.environ.get("NEXA_MLX_PATH") else None,
286
+ ]
287
+
288
+ for path in possible_paths:
289
+ if path and path.exists() and (path / "sd" / "interface.py").exists():
290
+ return str(path.absolute())
291
+
292
+ raise ImportError("Could not find MLX py-lib directory")