nexaai 0.0.0__cp310-cp310-macosx_14_0_universal2.whl → 1.0.4__cp310-cp310-macosx_14_0_universal2.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.

Binary file
nexaai/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is generated by CMake from _version.py.in
2
2
  # Do not modify this file manually - it will be overwritten
3
3
 
4
- __version__ = "0.0.0"
4
+ __version__ = "1.0.4"
Binary file
Binary file
Binary file
Binary file
Binary file
nexaai/cv.py CHANGED
@@ -69,6 +69,7 @@ class CVModel(BaseModel):
69
69
 
70
70
  @classmethod
71
71
  def _load_from(cls,
72
+ _: str, # TODO: remove this argument, this is a hack to make api design happy
72
73
  config: CVModelConfig,
73
74
  plugin_id: str = "llama_cpp",
74
75
  device_id: Optional[str] = None
@@ -62,8 +62,8 @@ class DownloadProgressTracker:
62
62
  self.last_display_length = 0
63
63
 
64
64
  # Speed tracking
65
- self.last_downloaded = 0
66
- self.last_time = time.time()
65
+ self.last_downloaded = None # Use None to indicate no previous measurement
66
+ self.last_time = None # Use None to indicate no previous time measurement
67
67
  self.speed_history = []
68
68
  self.max_speed_history = 10
69
69
 
@@ -101,18 +101,27 @@ class DownloadProgressTracker:
101
101
  def calculate_speed(self, current_downloaded: int) -> float:
102
102
  """Calculate download speed in bytes per second."""
103
103
  current_time = time.time()
104
- time_diff = current_time - self.last_time
105
104
 
106
- if time_diff > 0 and self.last_downloaded > 0:
107
- bytes_diff = current_downloaded - self.last_downloaded
108
- speed = bytes_diff / time_diff
109
-
110
- # Add to speed history for smoothing
111
- self.speed_history.append(speed)
112
- if len(self.speed_history) > self.max_speed_history:
113
- self.speed_history.pop(0)
105
+ # Check if we have a previous measurement to compare against
106
+ if self.last_time is not None and self.last_downloaded is not None:
107
+ time_diff = current_time - self.last_time
114
108
 
115
- # Return smoothed speed
109
+ # Only calculate if we have a meaningful time difference (avoid division by very small numbers)
110
+ if time_diff > 0.5: # At least 500ms between measurements
111
+ bytes_diff = current_downloaded - self.last_downloaded
112
+
113
+ # Only calculate speed if bytes actually changed
114
+ if bytes_diff >= 0: # Allow 0 for periods with no progress
115
+ speed = bytes_diff / time_diff
116
+
117
+ # Add to speed history for smoothing
118
+ self.speed_history.append(speed)
119
+ if len(self.speed_history) > self.max_speed_history:
120
+ self.speed_history.pop(0)
121
+
122
+ # Return the average of historical speeds if we have any
123
+ # This ensures we show the last known speed even when skipping updates
124
+ if self.speed_history:
116
125
  return sum(self.speed_history) / len(self.speed_history)
117
126
 
118
127
  return 0.0
@@ -76,8 +76,10 @@ class MlxVlmImpl(VLM):
76
76
  raise RuntimeError("MLX VLM not loaded")
77
77
 
78
78
  try:
79
- # Convert MultiModalMessage to MLX format
80
79
  mlx_messages = []
80
+ total_images = 0
81
+ total_audios = 0
82
+
81
83
  for msg in messages:
82
84
  # Create a simple object with role and content attributes
83
85
  class MLXChatMessage:
@@ -85,19 +87,38 @@ class MlxVlmImpl(VLM):
85
87
  self.role = role
86
88
  self.content = content
87
89
 
88
- # For MLX VLM, we need to extract text content from multimodal messages
89
- # This is a simplified approach - the actual implementation may need
90
- # more sophisticated handling of different content types
90
+ # Extract text content and count media files
91
91
  text_content = ""
92
+ first_content = True
93
+
92
94
  for content_item in msg["content"]:
93
- if content_item["type"] == "text":
95
+ content_type = content_item.get("type", "")
96
+
97
+ if content_type == "text":
98
+ if not first_content:
99
+ text_content += " "
94
100
  text_content += content_item.get("text", "")
95
- # Note: image/audio/video content is typically handled separately
96
- # in the generation phase, not in the chat template
101
+ first_content = False
102
+ elif content_type == "image":
103
+ total_images += 1
104
+ elif content_type == "audio":
105
+ total_audios += 1
97
106
 
98
107
  mlx_messages.append(MLXChatMessage(msg["role"], text_content))
99
108
 
100
- return self._mlx_vlm.apply_chat_template(mlx_messages)
109
+ if total_images > 0 or total_audios > 0:
110
+ # Use apply_chat_template_with_media when media is present
111
+ return self._mlx_vlm.apply_chat_template_with_media(
112
+ mlx_messages,
113
+ num_images=total_images,
114
+ num_audios=total_audios,
115
+ tools=tools,
116
+ enable_thinking=False # Default to False, could be made configurable
117
+ )
118
+ else:
119
+ # Use regular apply_chat_template for text-only messages
120
+ return self._mlx_vlm.apply_chat_template(mlx_messages)
121
+
101
122
  except Exception as e:
102
123
  raise RuntimeError(f"Failed to apply chat template: {str(e)}")
103
124
 
@@ -107,9 +128,6 @@ class MlxVlmImpl(VLM):
107
128
  raise RuntimeError("MLX VLM not loaded")
108
129
 
109
130
  try:
110
- # Get MLX config classes
111
- _, MLXSamplerConfig, MLXGenerationConfig, _ = get_mlx_configs()
112
-
113
131
  # Convert GenerationConfig to MLX format
114
132
  mlx_gen_config = MLXGenerationConfig()
115
133
  mlx_gen_config.max_tokens = g_cfg.max_tokens
@@ -130,25 +148,57 @@ class MlxVlmImpl(VLM):
130
148
  mlx_sampler_config.grammar_string = g_cfg.sampler_config.grammar_string
131
149
  mlx_gen_config.sampler_config = mlx_sampler_config
132
150
 
133
- # Create a token callback for streaming
134
- def token_callback(token: str) -> bool:
135
- # Check if generation should be cancelled
136
- return not self._cancel_event.is_set()
151
+ import queue
152
+ import threading
153
+
154
+ # Create a queue for streaming tokens
155
+ token_queue = queue.Queue()
156
+ exception_container = [None]
157
+ self.reset_cancel() # Reset cancel flag before generation
137
158
 
138
- # Use MLX VLM streaming generation
139
- result = self._mlx_vlm.generate_stream(prompt, mlx_gen_config, token_callback)
159
+ def token_callback(token: str, user_data: Any = None) -> bool:
160
+ if self._cancel_event.is_set():
161
+ token_queue.put(('end', None))
162
+ return False
163
+ try:
164
+ token_queue.put(('token', token))
165
+ return True
166
+ except Exception as e:
167
+ exception_container[0] = e
168
+ return False
140
169
 
141
- # MLX VLM interface returns a GenerationResult, extract the text
142
- if hasattr(result, 'text') and result.text:
143
- # Split the result into words and yield them
144
- words = result.text.split()
145
- for i, word in enumerate(words):
146
- if self._cancel_event.is_set():
170
+ # Run generation in a separate thread
171
+ def generate():
172
+ try:
173
+ self._mlx_vlm.generate_stream(prompt, mlx_gen_config, token_callback)
174
+ except Exception as e:
175
+ exception_container[0] = e
176
+ finally:
177
+ token_queue.put(('end', None))
178
+
179
+ thread = threading.Thread(target=generate)
180
+ thread.start()
181
+
182
+ # Yield tokens as they come from the queue
183
+ while True:
184
+ if exception_container[0]:
185
+ raise exception_container[0]
186
+
187
+ try:
188
+ msg_type, token = token_queue.get(timeout=0.1)
189
+ if msg_type == 'end':
147
190
  break
148
- if i == 0:
149
- yield word
150
- else:
151
- yield " " + word
191
+ elif msg_type == 'token':
192
+ yield token
193
+ except queue.Empty:
194
+ if not thread.is_alive():
195
+ break
196
+ continue
197
+
198
+ thread.join()
199
+
200
+ if exception_container[0]:
201
+ raise exception_container[0]
152
202
 
153
203
  except Exception as e:
154
204
  raise RuntimeError(f"Failed to generate streaming text: {str(e)}")
@@ -168,9 +218,6 @@ class MlxVlmImpl(VLM):
168
218
  raise RuntimeError("MLX VLM not loaded")
169
219
 
170
220
  try:
171
- # Get MLX config classes
172
- _, MLXSamplerConfig, MLXGenerationConfig, _ = get_mlx_configs()
173
-
174
221
  # Convert GenerationConfig to MLX format
175
222
  mlx_gen_config = MLXGenerationConfig()
176
223
  mlx_gen_config.max_tokens = g_cfg.max_tokens
@@ -191,15 +238,12 @@ class MlxVlmImpl(VLM):
191
238
  mlx_sampler_config.grammar_string = g_cfg.sampler_config.grammar_string
192
239
  mlx_gen_config.sampler_config = mlx_sampler_config
193
240
 
194
- # Use MLX VLM generation
195
- result = self._mlx_vlm.generate(prompt, mlx_gen_config)
241
+ # Simple token callback that just continues
242
+ def token_callback(token: str, user_data: Any = None) -> bool:
243
+ return not self._cancel_event.is_set()
196
244
 
197
- # MLX VLM interface returns a GenerationResult, extract the text
198
- if hasattr(result, 'text'):
199
- return result.text
200
- else:
201
- # Fallback if result is just a string
202
- return str(result)
245
+ # Use MLX streaming generation and return the full result
246
+ return self._mlx_vlm.generate_stream(prompt, mlx_gen_config, token_callback)
203
247
 
204
248
  except Exception as e:
205
249
  raise RuntimeError(f"Failed to generate text: {str(e)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nexaai
3
- Version: 0.0.0
3
+ Version: 1.0.4
4
4
  Summary: Python bindings for NexaSDK C-lib backend
5
5
  Author-email: "Nexa AI, Inc." <dev@nexa.ai>
6
6
  Project-URL: Homepage, https://github.com/NexaAI/nexasdk-bridge
@@ -17,10 +17,11 @@ Requires-Dist: tqdm
17
17
  Requires-Dist: hf_xet
18
18
  Requires-Dist: numpy
19
19
  Requires-Dist: httpx
20
- Provides-Extra: mlx
21
- Requires-Dist: mlx; extra == "mlx"
22
- Requires-Dist: mlx-lm; extra == "mlx"
23
- Requires-Dist: mlx-vlm; extra == "mlx"
24
- Requires-Dist: tokenizers; extra == "mlx"
25
- Requires-Dist: safetensors; extra == "mlx"
26
- Requires-Dist: Pillow; extra == "mlx"
20
+ Requires-Dist: mlx
21
+ Requires-Dist: mlx-lm
22
+ Requires-Dist: scipy
23
+ Requires-Dist: soundfile
24
+ Requires-Dist: Pillow
25
+ Requires-Dist: opencv-python
26
+ Requires-Dist: shapely
27
+ Requires-Dist: pyclipper
@@ -1,10 +1,10 @@
1
1
  nexaai/__init__.py,sha256=JTjJWdiBXHZyc_91Oe-GNOcODFp9gbUQM43bzNY7S8Q,1906
2
- nexaai/_stub.cpython-310-darwin.so,sha256=f1y4Gg4RGKC4h9GeU7gnRfm9a1QNFwogco7bkH1KnDE,66768
3
- nexaai/_version.py,sha256=8uyKXwDbAQT8F8B_GXLSK40bIOCYzI_DlSjnfsNoc3s,138
2
+ nexaai/_stub.cpython-310-darwin.so,sha256=zHzIOZ9uQ-tqMPBbqJ8yGaEu-Rx-r45Es1FYTp741yI,66768
3
+ nexaai/_version.py,sha256=lhnMmDQ6cHv0o4YRcKJ1pQ1UjlE_R6m7l1igB1Oe6tM,138
4
4
  nexaai/asr.py,sha256=Yg8Yml_nklzJYl3C_lwvEApTdNjY2czAurDaoEjkiIU,1813
5
5
  nexaai/base.py,sha256=N8PRgDFA-XPku2vWnQIofQ7ipz3pPlO6f8YZGnuhquE,982
6
6
  nexaai/common.py,sha256=VPM7NaUNaLTT7quW-u4D2uOeNrQqPjvfcgJlYGS3Qy8,1525
7
- nexaai/cv.py,sha256=CYfLSDU0_QJkcaIDIJ-a-JjA9FMvIUrSfG71_7-79hI,2934
7
+ nexaai/cv.py,sha256=KOaiRouiQ-YFP8FL20QuiieJfHN7DzASEi5_0m6H-E0,3032
8
8
  nexaai/embedder.py,sha256=VheiZEYBuuBjhQcvLawCz26jX0I169Xk4b9VP-ERjqU,2211
9
9
  nexaai/image_gen.py,sha256=IhLQLpmPkK9KcHteUdaQdxrnTIjk6xdyekRqeJtHfWw,4122
10
10
  nexaai/llm.py,sha256=egHa6YafNWyZy5qrmZRNZlFHO8LRUejc_gkOpK0nbnw,3105
@@ -18,15 +18,15 @@ nexaai/asr_impl/pybind_asr_impl.py,sha256=ybvthYgtVbH_JgpSsl0nxjZYvXyk8KGRSKdsJ-
18
18
  nexaai/binds/__init__.py,sha256=T9Ua7SzHNglSeEqXlfH5ymYXRyXhNKkC9z_y_bWCNMo,80
19
19
  nexaai/binds/common_bind.cpython-310-darwin.so,sha256=hVxY76tn7hN6uHDIgM7LWNvgoudHgNZVoaygM9X1RWE,217232
20
20
  nexaai/binds/embedder_bind.cpython-310-darwin.so,sha256=FT8581RNciilskK89PhtnNSjw4Oh0-xk8QdbJVFmOd8,202064
21
- nexaai/binds/libnexa_bridge.dylib,sha256=zgnLSGXKm0e89H08A85b9pKYgcNE5W6Uv0Upc2FgiCg,251256
21
+ nexaai/binds/libnexa_bridge.dylib,sha256=jBHp9IthZkkQu-RIrzHYPOS4PehnQ6TFhMWbVGUTZCk,251256
22
22
  nexaai/binds/llm_bind.cpython-310-darwin.so,sha256=Bv08rn9OBAHy01eAQeANiJSrCxskn1xSx4Gl1Vcrhm0,166064
23
- nexaai/binds/nexa_llama_cpp/libggml-base.dylib,sha256=i2aH2Gt0WRVFkuEfgWsE2hnRE2uZRXH83WQfrBrT-oI,631840
24
- nexaai/binds/nexa_llama_cpp/libggml-cpu.so,sha256=1qFcy_h2kozVyVaTF5oyQA7xrutzkw_LUn_jIw-9mTs,659216
25
- nexaai/binds/nexa_llama_cpp/libggml-metal.so,sha256=fCbK0DD8Uospc653JD0iaf7EJxRiXajIm9OEGE2nd9Q,673104
26
- nexaai/binds/nexa_llama_cpp/libggml.dylib,sha256=aOxGtGHFL5sn7ihZTOrcFYqn941O6RMpK3CwWYniN0g,58592
27
- nexaai/binds/nexa_llama_cpp/libllama.dylib,sha256=hmKPTc8OUHL985DAiVlh8tT4gVV5Fm6Vk7GGBXBPZds,1746592
28
- nexaai/binds/nexa_llama_cpp/libmtmd.dylib,sha256=TuDI-WK7LSNISNCIhTCzvCwgZSFRLC5Xsk5aGh1Ja_Y,586784
29
- nexaai/binds/nexa_llama_cpp/libnexa_plugin.dylib,sha256=sIseaikcmDXra4Zed7nt0KNN2u2bDTwRvPfUJx0B6dk,1750280
23
+ nexaai/binds/nexa_llama_cpp/libggml-base.dylib,sha256=CzsTec_QHlvbBGzmx4MBQ4LUjG7aIqW1rP5p_A90Vds,632048
24
+ nexaai/binds/nexa_llama_cpp/libggml-cpu.so,sha256=RiMhOv6IAWY1zkFTp0JCB7CYoPfOv54vBVQHvj1koBM,661120
25
+ nexaai/binds/nexa_llama_cpp/libggml-metal.so,sha256=L4RQvaD0w4qBjexi4O05RMCH8842fof5QgBEvyx0RcA,673104
26
+ nexaai/binds/nexa_llama_cpp/libggml.dylib,sha256=aOTj_6RrAMkfDO0ZI28_3nfcC-l4Y3dRCiS3C0d0_eI,58592
27
+ nexaai/binds/nexa_llama_cpp/libllama.dylib,sha256=fDPnTG6EQ1JN6aRmnIFQzag_kmtyImRxKjMOOtaTY5Q,1746928
28
+ nexaai/binds/nexa_llama_cpp/libmtmd.dylib,sha256=ccnBRsJNFGTCsjgW03N9PvX26wUirqpxljnxdVPINVc,587008
29
+ nexaai/binds/nexa_llama_cpp/libnexa_plugin.dylib,sha256=iS6dkM6ZualLlzo67SNAqsrAS51WmuUWbylR-GH8s18,1806696
30
30
  nexaai/binds/nexa_mlx/libnexa_plugin.dylib,sha256=aw8if8RwXjb02CehbqGPHOeEKRUcTpzeJZLOkjTBm8A,596328
31
31
  nexaai/binds/nexa_mlx/py-lib/ml.py,sha256=LafDM_TeXmuQkld2tdQxUBGgooT0JPMXngLam2TADqU,23179
32
32
  nexaai/binds/nexa_mlx/py-lib/profiling.py,sha256=Dc-mybFwBdCIKFWL7CbSHjkOJGAoYHG7r_e_XPhzwBU,9361
@@ -510,11 +510,11 @@ nexaai/tts_impl/pybind_tts_impl.py,sha256=Be5QiXzDz6h1LTIQzUBd0ZyBs7rUpNA-pULCXF
510
510
  nexaai/utils/avatar_fetcher.py,sha256=bWy8ujgbOiTHFCjFxTwkn3uXbZ84PgEGUkXkR3MH4bI,3821
511
511
  nexaai/utils/decode.py,sha256=61n4Zf6c5QLyqGoctEitlI9BX3tPlP2a5aaKNHbw3T4,404
512
512
  nexaai/utils/model_manager.py,sha256=c07ocxxw1IHCQw6esbmYK0dX2R2OajfEIGsC_2teHXo,48572
513
- nexaai/utils/progress_tracker.py,sha256=YO24mqqxiUseb1hd9ehLS2dD1t_4lYej_JWi76H7VnI,14374
513
+ nexaai/utils/progress_tracker.py,sha256=76HlPkyN41IMHSsH56-qdlN_aY_oBfJz50J16Cx67R0,15102
514
514
  nexaai/vlm_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
515
- nexaai/vlm_impl/mlx_vlm_impl.py,sha256=4lFZ8ZQnYZ-Uoh9j2Fh2UzpdpMAy_v1Jz-lrqX33XcI,8947
515
+ nexaai/vlm_impl/mlx_vlm_impl.py,sha256=7gm_tFNox3LC78DQEtlMQ-eBK55zDY0xWlJghUAOP5Y,10402
516
516
  nexaai/vlm_impl/pybind_vlm_impl.py,sha256=C-3fa0AIypI33OAGuGfVxo1V7zN0wjQMgruKlDIlW4Q,8333
517
- nexaai-0.0.0.dist-info/METADATA,sha256=zfeUTCcL6g-tj984P5MIcA8SG4sfC2gp9BMGS9LLzB4,948
518
- nexaai-0.0.0.dist-info/WHEEL,sha256=T2p57lol9__xkoU6aJTyN1Pm43ZpRU3q6km7mIbrAMs,114
519
- nexaai-0.0.0.dist-info/top_level.txt,sha256=LRE2YERlrZk2vfuygnSzsEeqSknnZbz3Z1MHyNmBU4w,7
520
- nexaai-0.0.0.dist-info/RECORD,,
517
+ nexaai-1.0.4.dist-info/METADATA,sha256=oFRx34_x3HEkQOp3cRCjUDgyZt13SWdvGiOVeGe6o7g,879
518
+ nexaai-1.0.4.dist-info/WHEEL,sha256=T2p57lol9__xkoU6aJTyN1Pm43ZpRU3q6km7mIbrAMs,114
519
+ nexaai-1.0.4.dist-info/top_level.txt,sha256=LRE2YERlrZk2vfuygnSzsEeqSknnZbz3Z1MHyNmBU4w,7
520
+ nexaai-1.0.4.dist-info/RECORD,,
File without changes