nexaai 1.0.13rc1__cp310-cp310-win_amd64.whl → 1.0.14__cp310-cp310-win_amd64.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.
- nexaai/_stub.cp310-win_amd64.pyd +0 -0
- nexaai/_version.py +1 -1
- nexaai/binds/common_bind.cp310-win_amd64.pyd +0 -0
- nexaai/binds/embedder_bind.cp310-win_amd64.pyd +0 -0
- nexaai/binds/llm_bind.cp310-win_amd64.pyd +0 -0
- nexaai/binds/nexa_bridge.dll +0 -0
- nexaai/binds/nexa_llama_cpp/ggml-base.dll +0 -0
- nexaai/binds/nexa_llama_cpp/ggml-cpu.dll +0 -0
- nexaai/binds/nexa_llama_cpp/ggml-cuda.dll +0 -0
- nexaai/binds/nexa_llama_cpp/ggml-vulkan.dll +0 -0
- nexaai/binds/nexa_llama_cpp/ggml.dll +0 -0
- nexaai/binds/nexa_llama_cpp/llama.dll +0 -0
- nexaai/binds/nexa_llama_cpp/mtmd.dll +0 -0
- nexaai/binds/nexa_llama_cpp/nexa_plugin.dll +0 -0
- nexaai/binds/vlm_bind.cp310-win_amd64.pyd +0 -0
- nexaai/embedder_impl/pybind_embedder_impl.py +2 -1
- nexaai/utils/manifest_utils.py +78 -34
- nexaai/utils/model_manager.py +98 -14
- {nexaai-1.0.13rc1.dist-info → nexaai-1.0.14.dist-info}/METADATA +1 -1
- {nexaai-1.0.13rc1.dist-info → nexaai-1.0.14.dist-info}/RECORD +22 -22
- {nexaai-1.0.13rc1.dist-info → nexaai-1.0.14.dist-info}/WHEEL +0 -0
- {nexaai-1.0.13rc1.dist-info → nexaai-1.0.14.dist-info}/top_level.txt +0 -0
nexaai/_stub.cp310-win_amd64.pyd
CHANGED
|
Binary file
|
nexaai/_version.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
nexaai/binds/nexa_bridge.dll
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -31,7 +31,8 @@ class PyBindEmbedderImpl(Embedder):
|
|
|
31
31
|
_ensure_runtime()
|
|
32
32
|
# Convert enum to string for C++ binding
|
|
33
33
|
plugin_id_str = plugin_id.value if isinstance(plugin_id, PluginID) else plugin_id
|
|
34
|
-
|
|
34
|
+
# New parameter order: model_path, plugin_id, tokenizer_path (optional)
|
|
35
|
+
handle = embedder_bind.ml_embedder_create(model_path, plugin_id_str, tokenizer_file)
|
|
35
36
|
return cls(handle)
|
|
36
37
|
|
|
37
38
|
def eject(self):
|
nexaai/utils/manifest_utils.py
CHANGED
|
@@ -11,7 +11,7 @@ This module provides utilities to:
|
|
|
11
11
|
import os
|
|
12
12
|
import json
|
|
13
13
|
from datetime import datetime
|
|
14
|
-
from typing import Dict, Any, List, Optional
|
|
14
|
+
from typing import Dict, Any, List, Optional, Union
|
|
15
15
|
|
|
16
16
|
from .quantization_utils import (
|
|
17
17
|
extract_quantization_from_filename,
|
|
@@ -101,7 +101,7 @@ def save_download_metadata(directory_path: str, metadata: Dict[str, Any]) -> Non
|
|
|
101
101
|
pass
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
def create_gguf_manifest(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any]) -> Dict[str, Any]:
|
|
104
|
+
def create_gguf_manifest(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any], is_mmproj: bool = False, file_name: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]:
|
|
105
105
|
"""Create GGUF format manifest."""
|
|
106
106
|
|
|
107
107
|
# Load existing manifest to merge GGUF files if it exists
|
|
@@ -111,14 +111,27 @@ def create_gguf_manifest(repo_id: str, files: List[str], directory_path: str, ol
|
|
|
111
111
|
if existing_manifest and "ModelFile" in existing_manifest:
|
|
112
112
|
model_files = existing_manifest["ModelFile"].copy()
|
|
113
113
|
|
|
114
|
+
# Initialize MMProjFile from existing manifest or empty
|
|
115
|
+
mmproj_file = {
|
|
116
|
+
"Name": "",
|
|
117
|
+
"Downloaded": False,
|
|
118
|
+
"Size": 0
|
|
119
|
+
}
|
|
120
|
+
if existing_manifest and "MMProjFile" in existing_manifest:
|
|
121
|
+
mmproj_file = existing_manifest["MMProjFile"].copy()
|
|
122
|
+
|
|
114
123
|
# Process GGUF files
|
|
115
|
-
for
|
|
116
|
-
if
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
for current_file_name in files:
|
|
125
|
+
if current_file_name.endswith('.gguf'):
|
|
126
|
+
# Check if this file is an mmproj file
|
|
127
|
+
is_current_mmproj = 'mmproj' in current_file_name.lower()
|
|
128
|
+
|
|
129
|
+
# If we're downloading specific files and this is marked as mmproj, respect that
|
|
130
|
+
if is_mmproj and file_name is not None:
|
|
131
|
+
filenames_to_check = file_name if isinstance(file_name, list) else [file_name]
|
|
132
|
+
is_current_mmproj = current_file_name in filenames_to_check
|
|
120
133
|
|
|
121
|
-
file_path = os.path.join(directory_path,
|
|
134
|
+
file_path = os.path.join(directory_path, current_file_name)
|
|
122
135
|
file_size = 0
|
|
123
136
|
if os.path.exists(file_path):
|
|
124
137
|
try:
|
|
@@ -126,22 +139,31 @@ def create_gguf_manifest(repo_id: str, files: List[str], directory_path: str, ol
|
|
|
126
139
|
except (OSError, IOError):
|
|
127
140
|
pass
|
|
128
141
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
if is_current_mmproj:
|
|
143
|
+
# This is an mmproj file, put it in MMProjFile
|
|
144
|
+
mmproj_file = {
|
|
145
|
+
"Name": current_file_name,
|
|
146
|
+
"Downloaded": True,
|
|
147
|
+
"Size": file_size
|
|
148
|
+
}
|
|
149
|
+
else:
|
|
150
|
+
# Regular model file, put in ModelFile
|
|
151
|
+
# Use the new enum-based quantization extraction
|
|
152
|
+
quantization_type = extract_quantization_from_filename(current_file_name)
|
|
153
|
+
quant_level = quantization_type.value if quantization_type else "UNKNOWN"
|
|
154
|
+
|
|
155
|
+
model_files[quant_level] = {
|
|
156
|
+
"Name": current_file_name,
|
|
157
|
+
"Downloaded": True,
|
|
158
|
+
"Size": file_size
|
|
159
|
+
}
|
|
134
160
|
|
|
135
161
|
manifest = {
|
|
136
162
|
"Name": repo_id,
|
|
137
163
|
"ModelType": PIPELINE_TO_MODEL_TYPE.get(old_metadata.get('pipeline_tag'), "other"),
|
|
138
164
|
"PluginId": "llama_cpp",
|
|
139
165
|
"ModelFile": model_files,
|
|
140
|
-
"MMProjFile":
|
|
141
|
-
"Name": "",
|
|
142
|
-
"Downloaded": False,
|
|
143
|
-
"Size": 0
|
|
144
|
-
},
|
|
166
|
+
"MMProjFile": mmproj_file,
|
|
145
167
|
"TokenizerFile": {
|
|
146
168
|
"Name": "",
|
|
147
169
|
"Downloaded": False,
|
|
@@ -157,20 +179,27 @@ def create_gguf_manifest(repo_id: str, files: List[str], directory_path: str, ol
|
|
|
157
179
|
return manifest
|
|
158
180
|
|
|
159
181
|
|
|
160
|
-
def create_mlx_manifest(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any]) -> Dict[str, Any]:
|
|
182
|
+
def create_mlx_manifest(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any], is_mmproj: bool = False, file_name: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]:
|
|
161
183
|
"""Create MLX format manifest."""
|
|
162
184
|
|
|
163
185
|
model_files = {}
|
|
164
186
|
extra_files = []
|
|
165
187
|
|
|
188
|
+
# Initialize MMProjFile
|
|
189
|
+
mmproj_file = {
|
|
190
|
+
"Name": "",
|
|
191
|
+
"Downloaded": False,
|
|
192
|
+
"Size": 0
|
|
193
|
+
}
|
|
194
|
+
|
|
166
195
|
# Try different methods to extract quantization for MLX models
|
|
167
196
|
quantization_type = detect_quantization_for_mlx(repo_id, directory_path)
|
|
168
197
|
|
|
169
198
|
# Use the detected quantization or default to "DEFAULT"
|
|
170
199
|
quant_level = quantization_type.value if quantization_type else "DEFAULT"
|
|
171
200
|
|
|
172
|
-
for
|
|
173
|
-
file_path = os.path.join(directory_path,
|
|
201
|
+
for current_file_name in files:
|
|
202
|
+
file_path = os.path.join(directory_path, current_file_name)
|
|
174
203
|
file_size = 0
|
|
175
204
|
if os.path.exists(file_path):
|
|
176
205
|
try:
|
|
@@ -178,17 +207,32 @@ def create_mlx_manifest(repo_id: str, files: List[str], directory_path: str, old
|
|
|
178
207
|
except (OSError, IOError):
|
|
179
208
|
pass
|
|
180
209
|
|
|
210
|
+
# Check if this file is an mmproj file
|
|
211
|
+
is_current_mmproj = 'mmproj' in current_file_name.lower()
|
|
212
|
+
|
|
213
|
+
# If we're downloading specific files and this is marked as mmproj, respect that
|
|
214
|
+
if is_mmproj and file_name is not None:
|
|
215
|
+
filenames_to_check = file_name if isinstance(file_name, list) else [file_name]
|
|
216
|
+
is_current_mmproj = current_file_name in filenames_to_check
|
|
217
|
+
|
|
218
|
+
if is_current_mmproj:
|
|
219
|
+
# This is an mmproj file, put it in MMProjFile
|
|
220
|
+
mmproj_file = {
|
|
221
|
+
"Name": current_file_name,
|
|
222
|
+
"Downloaded": True,
|
|
223
|
+
"Size": file_size
|
|
224
|
+
}
|
|
181
225
|
# Check if this is a main model file (safetensors but not index files)
|
|
182
|
-
|
|
226
|
+
elif (current_file_name.endswith('.safetensors') and not current_file_name.endswith('.index.json')):
|
|
183
227
|
model_files[quant_level] = {
|
|
184
|
-
"Name":
|
|
228
|
+
"Name": current_file_name,
|
|
185
229
|
"Downloaded": True,
|
|
186
230
|
"Size": file_size
|
|
187
231
|
}
|
|
188
232
|
else:
|
|
189
233
|
# Add to extra files
|
|
190
234
|
extra_files.append({
|
|
191
|
-
"Name":
|
|
235
|
+
"Name": current_file_name,
|
|
192
236
|
"Downloaded": True,
|
|
193
237
|
"Size": file_size
|
|
194
238
|
})
|
|
@@ -198,11 +242,7 @@ def create_mlx_manifest(repo_id: str, files: List[str], directory_path: str, old
|
|
|
198
242
|
"ModelType": PIPELINE_TO_MODEL_TYPE.get(old_metadata.get('pipeline_tag'), "other"),
|
|
199
243
|
"PluginId": "mlx",
|
|
200
244
|
"ModelFile": model_files,
|
|
201
|
-
"MMProjFile":
|
|
202
|
-
"Name": "",
|
|
203
|
-
"Downloaded": False,
|
|
204
|
-
"Size": 0
|
|
205
|
-
},
|
|
245
|
+
"MMProjFile": mmproj_file,
|
|
206
246
|
"TokenizerFile": {
|
|
207
247
|
"Name": "",
|
|
208
248
|
"Downloaded": False,
|
|
@@ -232,7 +272,7 @@ def detect_model_type(files: List[str]) -> str:
|
|
|
232
272
|
return "mlx"
|
|
233
273
|
|
|
234
274
|
|
|
235
|
-
def create_manifest_from_files(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any]) -> Dict[str, Any]:
|
|
275
|
+
def create_manifest_from_files(repo_id: str, files: List[str], directory_path: str, old_metadata: Dict[str, Any], is_mmproj: bool = False, file_name: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]:
|
|
236
276
|
"""
|
|
237
277
|
Create appropriate manifest format based on detected model type.
|
|
238
278
|
|
|
@@ -241,6 +281,8 @@ def create_manifest_from_files(repo_id: str, files: List[str], directory_path: s
|
|
|
241
281
|
files: List of files in the model directory
|
|
242
282
|
directory_path: Path to the model directory
|
|
243
283
|
old_metadata: Existing metadata (pipeline_tag, download_time, avatar_url)
|
|
284
|
+
is_mmproj: Whether the downloaded file is an mmproj file
|
|
285
|
+
file_name: The specific file(s) that were downloaded (None if entire repo was downloaded)
|
|
244
286
|
|
|
245
287
|
Returns:
|
|
246
288
|
Dict containing the appropriate manifest format
|
|
@@ -248,12 +290,12 @@ def create_manifest_from_files(repo_id: str, files: List[str], directory_path: s
|
|
|
248
290
|
model_type = detect_model_type(files)
|
|
249
291
|
|
|
250
292
|
if model_type == "gguf":
|
|
251
|
-
return create_gguf_manifest(repo_id, files, directory_path, old_metadata)
|
|
293
|
+
return create_gguf_manifest(repo_id, files, directory_path, old_metadata, is_mmproj, file_name)
|
|
252
294
|
else: # mlx or other
|
|
253
|
-
return create_mlx_manifest(repo_id, files, directory_path, old_metadata)
|
|
295
|
+
return create_mlx_manifest(repo_id, files, directory_path, old_metadata, is_mmproj, file_name)
|
|
254
296
|
|
|
255
297
|
|
|
256
|
-
def save_manifest_with_files_metadata(repo_id: str, local_dir: str, old_metadata: Dict[str, Any]) -> None:
|
|
298
|
+
def save_manifest_with_files_metadata(repo_id: str, local_dir: str, old_metadata: Dict[str, Any], is_mmproj: bool = False, file_name: Optional[Union[str, List[str]]] = None) -> None:
|
|
257
299
|
"""
|
|
258
300
|
Create and save manifest based on files found in the directory.
|
|
259
301
|
|
|
@@ -261,6 +303,8 @@ def save_manifest_with_files_metadata(repo_id: str, local_dir: str, old_metadata
|
|
|
261
303
|
repo_id: Repository ID
|
|
262
304
|
local_dir: Local directory containing the model files
|
|
263
305
|
old_metadata: Existing metadata to preserve
|
|
306
|
+
is_mmproj: Whether the downloaded file is an mmproj file
|
|
307
|
+
file_name: The specific file(s) that were downloaded (None if entire repo was downloaded)
|
|
264
308
|
"""
|
|
265
309
|
# Get list of files in the directory
|
|
266
310
|
files = []
|
|
@@ -274,7 +318,7 @@ def save_manifest_with_files_metadata(repo_id: str, local_dir: str, old_metadata
|
|
|
274
318
|
pass
|
|
275
319
|
|
|
276
320
|
# Create appropriate manifest
|
|
277
|
-
manifest = create_manifest_from_files(repo_id, files, local_dir, old_metadata)
|
|
321
|
+
manifest = create_manifest_from_files(repo_id, files, local_dir, old_metadata, is_mmproj, file_name)
|
|
278
322
|
|
|
279
323
|
# Save manifest
|
|
280
324
|
save_download_metadata(local_dir, manifest)
|
nexaai/utils/model_manager.py
CHANGED
|
@@ -21,6 +21,13 @@ from .manifest_utils import (
|
|
|
21
21
|
# Default path for model storage
|
|
22
22
|
DEFAULT_MODEL_SAVING_PATH = "~/.cache/nexa.ai/nexa_sdk/models/"
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class MMProjInfo:
|
|
27
|
+
"""Data class for mmproj file information."""
|
|
28
|
+
mmproj_path: Optional[str] = None
|
|
29
|
+
size: int = 0
|
|
30
|
+
|
|
24
31
|
@dataclass
|
|
25
32
|
class DownloadedModel:
|
|
26
33
|
"""Data class representing a downloaded model with all its metadata."""
|
|
@@ -34,6 +41,7 @@ class DownloadedModel:
|
|
|
34
41
|
pipeline_tag: Optional[str] = None # Pipeline tag from HuggingFace model info
|
|
35
42
|
download_time: Optional[str] = None # ISO format timestamp of download
|
|
36
43
|
avatar_url: Optional[str] = None # Avatar URL for the model author
|
|
44
|
+
mmproj_info: Optional[MMProjInfo] = None # mmproj file information
|
|
37
45
|
|
|
38
46
|
def to_dict(self) -> Dict[str, Any]:
|
|
39
47
|
"""Convert to dictionary format for backward compatibility."""
|
|
@@ -47,7 +55,11 @@ class DownloadedModel:
|
|
|
47
55
|
'full_repo_download_complete': self.full_repo_download_complete,
|
|
48
56
|
'pipeline_tag': self.pipeline_tag,
|
|
49
57
|
'download_time': self.download_time,
|
|
50
|
-
'avatar_url': self.avatar_url
|
|
58
|
+
'avatar_url': self.avatar_url,
|
|
59
|
+
'mmproj_info': {
|
|
60
|
+
'mmproj_path': self.mmproj_info.mmproj_path,
|
|
61
|
+
'size': self.mmproj_info.size
|
|
62
|
+
} if self.mmproj_info else None
|
|
51
63
|
}
|
|
52
64
|
return result
|
|
53
65
|
|
|
@@ -125,6 +137,46 @@ def _has_valid_metadata(directory_path: str) -> bool:
|
|
|
125
137
|
return os.path.exists(manifest_path) or os.path.exists(old_metadata_path)
|
|
126
138
|
|
|
127
139
|
|
|
140
|
+
def _extract_mmproj_info(manifest: Dict[str, Any], local_path: str) -> Optional[MMProjInfo]:
|
|
141
|
+
"""
|
|
142
|
+
Extract mmproj information from manifest data.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
manifest: Dictionary containing manifest data
|
|
146
|
+
local_path: Local path to the model directory
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
MMProjInfo object if mmproj file exists, None otherwise
|
|
150
|
+
"""
|
|
151
|
+
# Check if manifest has MMProjFile information
|
|
152
|
+
mmproj_file_info = manifest.get('MMProjFile')
|
|
153
|
+
if not mmproj_file_info or not mmproj_file_info.get('Downloaded') or not mmproj_file_info.get('Name'):
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
mmproj_filename = mmproj_file_info.get('Name', '')
|
|
157
|
+
if not mmproj_filename:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
# Construct full path to mmproj file
|
|
161
|
+
mmproj_path = os.path.join(local_path, mmproj_filename)
|
|
162
|
+
|
|
163
|
+
# Get size from manifest, but verify file exists
|
|
164
|
+
mmproj_size = mmproj_file_info.get('Size', 0)
|
|
165
|
+
if os.path.exists(mmproj_path):
|
|
166
|
+
try:
|
|
167
|
+
# Verify size matches actual file size
|
|
168
|
+
actual_size = os.path.getsize(mmproj_path)
|
|
169
|
+
mmproj_size = actual_size # Use actual size if different
|
|
170
|
+
except (OSError, IOError):
|
|
171
|
+
# If we can't get actual size, use size from manifest
|
|
172
|
+
pass
|
|
173
|
+
else:
|
|
174
|
+
# File doesn't exist, don't include mmproj info
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
return MMProjInfo(mmproj_path=mmproj_path, size=mmproj_size)
|
|
178
|
+
|
|
179
|
+
|
|
128
180
|
def _scan_for_repo_folders(base_path: str) -> List[DownloadedModel]:
|
|
129
181
|
"""Scan a directory for repository folders and return model information."""
|
|
130
182
|
models = []
|
|
@@ -159,6 +211,10 @@ def _scan_for_repo_folders(base_path: str) -> List[DownloadedModel]:
|
|
|
159
211
|
# Load metadata if it exists
|
|
160
212
|
repo_id = f"{item}/{subitem}"
|
|
161
213
|
metadata = load_download_metadata(subitem_path, repo_id)
|
|
214
|
+
|
|
215
|
+
# Extract mmproj information
|
|
216
|
+
mmproj_info = _extract_mmproj_info(metadata, subitem_path)
|
|
217
|
+
|
|
162
218
|
models.append(DownloadedModel(
|
|
163
219
|
repo_id=repo_id,
|
|
164
220
|
files=files,
|
|
@@ -169,7 +225,8 @@ def _scan_for_repo_folders(base_path: str) -> List[DownloadedModel]:
|
|
|
169
225
|
full_repo_download_complete=download_complete,
|
|
170
226
|
pipeline_tag=metadata.get('pipeline_tag'),
|
|
171
227
|
download_time=metadata.get('download_time'),
|
|
172
|
-
avatar_url=metadata.get('avatar_url')
|
|
228
|
+
avatar_url=metadata.get('avatar_url'),
|
|
229
|
+
mmproj_info=mmproj_info
|
|
173
230
|
))
|
|
174
231
|
else:
|
|
175
232
|
direct_files.append(subitem)
|
|
@@ -188,6 +245,10 @@ def _scan_for_repo_folders(base_path: str) -> List[DownloadedModel]:
|
|
|
188
245
|
# Load metadata if it exists
|
|
189
246
|
repo_id = item
|
|
190
247
|
metadata = load_download_metadata(item_path, repo_id)
|
|
248
|
+
|
|
249
|
+
# Extract mmproj information
|
|
250
|
+
mmproj_info = _extract_mmproj_info(metadata, item_path)
|
|
251
|
+
|
|
191
252
|
models.append(DownloadedModel(
|
|
192
253
|
repo_id=repo_id,
|
|
193
254
|
files=files,
|
|
@@ -198,7 +259,8 @@ def _scan_for_repo_folders(base_path: str) -> List[DownloadedModel]:
|
|
|
198
259
|
full_repo_download_complete=download_complete,
|
|
199
260
|
pipeline_tag=metadata.get('pipeline_tag'),
|
|
200
261
|
download_time=metadata.get('download_time'),
|
|
201
|
-
avatar_url=metadata.get('avatar_url')
|
|
262
|
+
avatar_url=metadata.get('avatar_url'),
|
|
263
|
+
mmproj_info=mmproj_info
|
|
202
264
|
))
|
|
203
265
|
|
|
204
266
|
except (OSError, IOError):
|
|
@@ -236,6 +298,7 @@ def list_downloaded_models(local_dir: Optional[str] = None) -> List[DownloadedMo
|
|
|
236
298
|
- pipeline_tag: Optional[str] - Pipeline tag from HuggingFace model info
|
|
237
299
|
- download_time: Optional[str] - ISO format timestamp when the model was downloaded
|
|
238
300
|
- avatar_url: Optional[str] - Avatar URL for the model author
|
|
301
|
+
- mmproj_info: Optional[MMProjInfo] - mmproj file information with mmproj_path and size
|
|
239
302
|
"""
|
|
240
303
|
|
|
241
304
|
# Set up local directory
|
|
@@ -727,7 +790,7 @@ class HuggingFaceDownloader:
|
|
|
727
790
|
# If no expected size, just check that file is not empty
|
|
728
791
|
return os.path.getsize(file_path) > 0
|
|
729
792
|
|
|
730
|
-
def _fetch_and_save_metadata(self, repo_id: str, local_dir: str) -> None:
|
|
793
|
+
def _fetch_and_save_metadata(self, repo_id: str, local_dir: str, is_mmproj: bool = False, file_name: Optional[Union[str, List[str]]] = None) -> None:
|
|
731
794
|
"""Fetch model info and save metadata after successful download."""
|
|
732
795
|
# Initialize metadata with defaults to ensure manifest is always created
|
|
733
796
|
old_metadata = {
|
|
@@ -757,7 +820,7 @@ class HuggingFaceDownloader:
|
|
|
757
820
|
|
|
758
821
|
# CRITICAL: Always create the manifest file, regardless of metadata fetch failures
|
|
759
822
|
try:
|
|
760
|
-
save_manifest_with_files_metadata(repo_id, local_dir, old_metadata)
|
|
823
|
+
save_manifest_with_files_metadata(repo_id, local_dir, old_metadata, is_mmproj, file_name)
|
|
761
824
|
print(f"[OK] Successfully created nexa.manifest for {repo_id}")
|
|
762
825
|
except Exception as e:
|
|
763
826
|
# This is critical - if manifest creation fails, we should know about it
|
|
@@ -819,7 +882,7 @@ class HuggingFaceDownloader:
|
|
|
819
882
|
progress_tracker.stop_tracking()
|
|
820
883
|
|
|
821
884
|
# Save metadata after successful download
|
|
822
|
-
self._fetch_and_save_metadata(repo_id, file_local_dir)
|
|
885
|
+
self._fetch_and_save_metadata(repo_id, file_local_dir, self._current_is_mmproj, self._current_file_name)
|
|
823
886
|
|
|
824
887
|
return downloaded_path
|
|
825
888
|
|
|
@@ -864,7 +927,7 @@ class HuggingFaceDownloader:
|
|
|
864
927
|
progress_tracker.stop_tracking()
|
|
865
928
|
|
|
866
929
|
# Save metadata after successful download
|
|
867
|
-
self._fetch_and_save_metadata(repo_id, repo_local_dir)
|
|
930
|
+
self._fetch_and_save_metadata(repo_id, repo_local_dir, self._current_is_mmproj, self._current_file_name)
|
|
868
931
|
|
|
869
932
|
return downloaded_path
|
|
870
933
|
|
|
@@ -926,7 +989,7 @@ class HuggingFaceDownloader:
|
|
|
926
989
|
progress_tracker.stop_tracking()
|
|
927
990
|
|
|
928
991
|
# Save metadata after successful download
|
|
929
|
-
self._fetch_and_save_metadata(repo_id, repo_local_dir)
|
|
992
|
+
self._fetch_and_save_metadata(repo_id, repo_local_dir, self._current_is_mmproj, self._current_file_name)
|
|
930
993
|
|
|
931
994
|
return repo_local_dir
|
|
932
995
|
|
|
@@ -951,7 +1014,8 @@ class HuggingFaceDownloader:
|
|
|
951
1014
|
local_dir: Optional[str] = None,
|
|
952
1015
|
progress_callback: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
953
1016
|
show_progress: bool = True,
|
|
954
|
-
force_download: bool = False
|
|
1017
|
+
force_download: bool = False,
|
|
1018
|
+
is_mmproj: bool = False
|
|
955
1019
|
) -> str:
|
|
956
1020
|
"""
|
|
957
1021
|
Main download method that handles all download scenarios.
|
|
@@ -970,6 +1034,10 @@ class HuggingFaceDownloader:
|
|
|
970
1034
|
# Validate and normalize parameters
|
|
971
1035
|
repo_id, file_name = self._validate_and_setup_params(repo_id, file_name)
|
|
972
1036
|
|
|
1037
|
+
# Store parameters as instance variables for use in _fetch_and_save_metadata
|
|
1038
|
+
self._current_is_mmproj = is_mmproj
|
|
1039
|
+
self._current_file_name = file_name
|
|
1040
|
+
|
|
973
1041
|
# Set up local directory
|
|
974
1042
|
local_dir = self._created_dir_if_not_exists(local_dir)
|
|
975
1043
|
|
|
@@ -1038,7 +1106,8 @@ def download_from_huggingface(
|
|
|
1038
1106
|
show_progress: bool = True,
|
|
1039
1107
|
token: Union[bool, str, None] = None,
|
|
1040
1108
|
custom_endpoint: Optional[str] = None,
|
|
1041
|
-
force_download: bool = False
|
|
1109
|
+
force_download: bool = False,
|
|
1110
|
+
is_mmproj: Optional[bool] = None
|
|
1042
1111
|
) -> str:
|
|
1043
1112
|
"""
|
|
1044
1113
|
Download models or files from HuggingFace Hub or custom mirror endpoints.
|
|
@@ -1065,6 +1134,9 @@ def download_from_huggingface(
|
|
|
1065
1134
|
The endpoint will be used to initialize HfApi for all downloads.
|
|
1066
1135
|
force_download (bool, optional): If True, download files even if they already exist locally.
|
|
1067
1136
|
Default False (skip existing files).
|
|
1137
|
+
is_mmproj (bool, optional): Whether the file being downloaded is an mmproj file. Only used when
|
|
1138
|
+
file_name is not None. If None, defaults to True if 'mmproj' is in
|
|
1139
|
+
the filename, False otherwise.
|
|
1068
1140
|
|
|
1069
1141
|
Returns:
|
|
1070
1142
|
str: Path to the downloaded file or directory
|
|
@@ -1101,6 +1173,15 @@ def download_from_huggingface(
|
|
|
1101
1173
|
}
|
|
1102
1174
|
}
|
|
1103
1175
|
"""
|
|
1176
|
+
# Set default value for is_mmproj based on filename if not explicitly provided
|
|
1177
|
+
if is_mmproj is None and file_name is not None:
|
|
1178
|
+
# Check if any filename contains 'mmproj'
|
|
1179
|
+
filenames_to_check = file_name if isinstance(file_name, list) else [file_name]
|
|
1180
|
+
is_mmproj = any('mmproj' in filename.lower() for filename in filenames_to_check)
|
|
1181
|
+
elif is_mmproj is None:
|
|
1182
|
+
# Default to False if no file_name is provided
|
|
1183
|
+
is_mmproj = False
|
|
1184
|
+
|
|
1104
1185
|
# Create downloader instance with custom endpoint if provided
|
|
1105
1186
|
downloader = HuggingFaceDownloader(
|
|
1106
1187
|
endpoint=custom_endpoint,
|
|
@@ -1115,7 +1196,8 @@ def download_from_huggingface(
|
|
|
1115
1196
|
local_dir=local_dir,
|
|
1116
1197
|
progress_callback=progress_callback,
|
|
1117
1198
|
show_progress=show_progress,
|
|
1118
|
-
force_download=force_download
|
|
1199
|
+
force_download=force_download,
|
|
1200
|
+
is_mmproj=is_mmproj
|
|
1119
1201
|
)
|
|
1120
1202
|
|
|
1121
1203
|
|
|
@@ -1128,7 +1210,8 @@ def _download_model_if_needed(
|
|
|
1128
1210
|
model_path: str,
|
|
1129
1211
|
param_name: str,
|
|
1130
1212
|
progress_callback: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
1131
|
-
token: Union[bool, str, None] = None
|
|
1213
|
+
token: Union[bool, str, None] = None,
|
|
1214
|
+
is_mmproj: bool = False
|
|
1132
1215
|
) -> str:
|
|
1133
1216
|
"""
|
|
1134
1217
|
Helper function to download a model from HuggingFace if it doesn't exist locally.
|
|
@@ -1163,7 +1246,8 @@ def _download_model_if_needed(
|
|
|
1163
1246
|
enable_transfer=True,
|
|
1164
1247
|
progress_callback=progress_callback,
|
|
1165
1248
|
show_progress=True,
|
|
1166
|
-
token=token
|
|
1249
|
+
token=token,
|
|
1250
|
+
is_mmproj=is_mmproj
|
|
1167
1251
|
)
|
|
1168
1252
|
|
|
1169
1253
|
return downloaded_path
|
|
@@ -1254,7 +1338,7 @@ def auto_download_model(func: Callable) -> Callable:
|
|
|
1254
1338
|
if mmproj_path is not None:
|
|
1255
1339
|
try:
|
|
1256
1340
|
downloaded_mmproj_path = _download_model_if_needed(
|
|
1257
|
-
mmproj_path, 'mmproj_path', progress_callback, token
|
|
1341
|
+
mmproj_path, 'mmproj_path', progress_callback, token, is_mmproj=True
|
|
1258
1342
|
)
|
|
1259
1343
|
|
|
1260
1344
|
# Replace mmproj_path with downloaded path
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
nexaai/__init__.py,sha256=Lt8NU57eTMtWrDYzpFeYR9XtGAPXqizynP83TPU0UW0,2105
|
|
2
|
-
nexaai/_stub.cp310-win_amd64.pyd,sha256=
|
|
3
|
-
nexaai/_version.py,sha256=
|
|
2
|
+
nexaai/_stub.cp310-win_amd64.pyd,sha256=lr_9G0K-4W1yIOs1WoUDB38_FFioP1-KeX3BeViFxmA,10752
|
|
3
|
+
nexaai/_version.py,sha256=o_A77BAaa_6l7sdnPuuDg5dCnS5V0Fap0M1dHWeyK9o,143
|
|
4
4
|
nexaai/asr.py,sha256=_fsGaxpiU137bUtO5ujtFSYCI1RLsyeEm3Gf4GhHVRk,2118
|
|
5
5
|
nexaai/base.py,sha256=qQBCiQVNzgpkQjZX9aiFDEdbAAe56TROKC3WnWra2Zg,1021
|
|
6
6
|
nexaai/common.py,sha256=6keIpdX5XS5us4z79EMoa6RSkVze9SbbXax13IJ9yvs,3525
|
|
@@ -16,27 +16,27 @@ nexaai/asr_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
16
16
|
nexaai/asr_impl/mlx_asr_impl.py,sha256=XwMX3LYMeulp8cDS0TCCYcjvttFHAyDWQ_oMvABwQmI,3349
|
|
17
17
|
nexaai/asr_impl/pybind_asr_impl.py,sha256=20o5SOPzhF9x41ra8L_qIM7YxCkYeLb5csSrNde-dds,1560
|
|
18
18
|
nexaai/binds/__init__.py,sha256=ENl-uoIF9-3XGIXitVgZ2QmJ6p7Yet4h1-X7nUDZ0Hk,108
|
|
19
|
-
nexaai/binds/common_bind.cp310-win_amd64.pyd,sha256=
|
|
20
|
-
nexaai/binds/embedder_bind.cp310-win_amd64.pyd,sha256=
|
|
19
|
+
nexaai/binds/common_bind.cp310-win_amd64.pyd,sha256=_ZzqrWJAP5E_VY8zDb2w8J4cocZ34JOqxLaf0eLQfuI,201216
|
|
20
|
+
nexaai/binds/embedder_bind.cp310-win_amd64.pyd,sha256=Fwdx9UTaa_CN54HtCS8ucvZWC3dtyrdySBlZwNEAp1o,182784
|
|
21
21
|
nexaai/binds/libcrypto-3-x64.dll,sha256=PYwJJtM4xpo2IQfxm-of6VR6rtVcHCyodwcvBHmfzig,7315968
|
|
22
22
|
nexaai/binds/libssl-3-x64.dll,sha256=mp_RoRjYcCeAuqMy3QDYvxQ-cjAMUNCuwA320oXVVpg,1313792
|
|
23
|
-
nexaai/binds/llm_bind.cp310-win_amd64.pyd,sha256=
|
|
24
|
-
nexaai/binds/nexa_bridge.dll,sha256=
|
|
25
|
-
nexaai/binds/vlm_bind.cp310-win_amd64.pyd,sha256=
|
|
26
|
-
nexaai/binds/nexa_llama_cpp/ggml-base.dll,sha256=
|
|
27
|
-
nexaai/binds/nexa_llama_cpp/ggml-cpu.dll,sha256=
|
|
28
|
-
nexaai/binds/nexa_llama_cpp/ggml-cuda.dll,sha256=
|
|
29
|
-
nexaai/binds/nexa_llama_cpp/ggml-vulkan.dll,sha256=
|
|
30
|
-
nexaai/binds/nexa_llama_cpp/ggml.dll,sha256=
|
|
31
|
-
nexaai/binds/nexa_llama_cpp/llama.dll,sha256=
|
|
32
|
-
nexaai/binds/nexa_llama_cpp/mtmd.dll,sha256=
|
|
33
|
-
nexaai/binds/nexa_llama_cpp/nexa_plugin.dll,sha256=
|
|
23
|
+
nexaai/binds/llm_bind.cp310-win_amd64.pyd,sha256=EEn5lJzk9AmXE3lSm2YOxeukEloyCGLq0eQG0YB92lA,162816
|
|
24
|
+
nexaai/binds/nexa_bridge.dll,sha256=jdE1EROd2YvK3aqpWuZ9VwYcN5k0jCgNFdow9oY8ndk,168448
|
|
25
|
+
nexaai/binds/vlm_bind.cp310-win_amd64.pyd,sha256=jimDElj3ukGIxope1tntQczGgI7G6tPVB3O9JxPlU54,168960
|
|
26
|
+
nexaai/binds/nexa_llama_cpp/ggml-base.dll,sha256=fG4HcLVs9FIoKoRP0N3ZtISsrdE-izmX3LBUD-twhm8,532480
|
|
27
|
+
nexaai/binds/nexa_llama_cpp/ggml-cpu.dll,sha256=scQ0YhWRbUo0-WChqHg0Cb6oJlzRTdARg1D895eCg1c,672768
|
|
28
|
+
nexaai/binds/nexa_llama_cpp/ggml-cuda.dll,sha256=LfVjVahRABD_nqWLYvGuQ4w3VgV0VeyGFvDiN3zDtOQ,313528832
|
|
29
|
+
nexaai/binds/nexa_llama_cpp/ggml-vulkan.dll,sha256=o8dbtu-FJfbWNc8N_vo8J95_cvrZWRNVJxVSUJqwQd0,36627456
|
|
30
|
+
nexaai/binds/nexa_llama_cpp/ggml.dll,sha256=YysmaS6dDlKRbYlypvQCPtcpM3mKHEIAp9uikp1itbs,66560
|
|
31
|
+
nexaai/binds/nexa_llama_cpp/llama.dll,sha256=r19TxNsfCos3PwIVqGu21WC16o_azGsZJORjKOtluEY,1611776
|
|
32
|
+
nexaai/binds/nexa_llama_cpp/mtmd.dll,sha256=K2cAVQ3EPByhxzpLx9UvObQyMtaxu0VrGIbujzG8OFE,561152
|
|
33
|
+
nexaai/binds/nexa_llama_cpp/nexa_plugin.dll,sha256=BQj7P953y9inMx9tEMxTcIUkdp3-1_S-RVPXp0QqQKc,1405440
|
|
34
34
|
nexaai/cv_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
nexaai/cv_impl/mlx_cv_impl.py,sha256=QLd_8w90gtxH8kmssaDYatCTRvQNIJuUGKZNnYrmx6E,3317
|
|
36
36
|
nexaai/cv_impl/pybind_cv_impl.py,sha256=aSOCAxmHrwJbEkSN6VX3Cykqlj_9RIpVrZXILul04GA,1096
|
|
37
37
|
nexaai/embedder_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
38
|
nexaai/embedder_impl/mlx_embedder_impl.py,sha256=Kzd-veLNl95FbI2oEJMtr6qKbjtPDDajzsGUVjJfTRA,4598
|
|
39
|
-
nexaai/embedder_impl/pybind_embedder_impl.py,sha256=
|
|
39
|
+
nexaai/embedder_impl/pybind_embedder_impl.py,sha256=eH2L--L6BAl-46UOzm84pfjIkJtQKVd63-r62eb0Vg0,3670
|
|
40
40
|
nexaai/image_gen_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
nexaai/image_gen_impl/mlx_image_gen_impl.py,sha256=peUE9ue9ApaPlZVOICBWiHtd13sY40OWQbE8EjfIUMU,11511
|
|
42
42
|
nexaai/image_gen_impl/pybind_image_gen_impl.py,sha256=514RFQMSO0Rhacq0IYzlEhEr6QfaprnGew0Rjz8HZI4,3777
|
|
@@ -51,15 +51,15 @@ nexaai/tts_impl/mlx_tts_impl.py,sha256=LcH9bVdIl3Q6lOzSUB_X2s-_nWFmlCl1yL7XSUK0f
|
|
|
51
51
|
nexaai/tts_impl/pybind_tts_impl.py,sha256=n3z4zmPQayQJgAwcvETw0IBUCp8IYROuYFSg0tAy_8Y,1487
|
|
52
52
|
nexaai/utils/avatar_fetcher.py,sha256=D01f8je-37Nd68zGw8MYK2m7y3fvGlC6h0KR-aN9kdU,3925
|
|
53
53
|
nexaai/utils/decode.py,sha256=0Z9jDH4ICzw4YXj8nD4L-sMouDaev-TISGRQ4KzidWE,421
|
|
54
|
-
nexaai/utils/manifest_utils.py,sha256=
|
|
55
|
-
nexaai/utils/model_manager.py,sha256=
|
|
54
|
+
nexaai/utils/manifest_utils.py,sha256=nPqK24srLX41x3WU4R-OGQR9u0XA7ZTHY1MQXBKXdrM,12652
|
|
55
|
+
nexaai/utils/model_manager.py,sha256=3rDODGClgnM24LFWIoM-TDpXkpV1vyJSdlv7Qk4ZaPE,57434
|
|
56
56
|
nexaai/utils/model_types.py,sha256=arIyb9q-1uG0nyUGdWZaxxDJAxv0cfnJEpjCzyELL5Q,1416
|
|
57
57
|
nexaai/utils/progress_tracker.py,sha256=BztrFqtjwNUmeREwZ5m7H6ZcrVzQEbpZfsxndWh4z0A,15778
|
|
58
58
|
nexaai/utils/quantization_utils.py,sha256=jjQaz7K4qH6TdP8Tnv5Ktb2viz8BaVBSOrb_jm3ns28,7889
|
|
59
59
|
nexaai/vlm_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
60
|
nexaai/vlm_impl/mlx_vlm_impl.py,sha256=oY_qb9z_iF0zArBuY5CCYIvZcA3R0i_NKXrr_r-QSgg,10989
|
|
61
61
|
nexaai/vlm_impl/pybind_vlm_impl.py,sha256=7Bo0kpSrmOdr--bWSpQBvcaexkPPRt3x1yt9e_jIyDs,8686
|
|
62
|
-
nexaai-1.0.
|
|
63
|
-
nexaai-1.0.
|
|
64
|
-
nexaai-1.0.
|
|
65
|
-
nexaai-1.0.
|
|
62
|
+
nexaai-1.0.14.dist-info/METADATA,sha256=9DWi42R2ygtt8bY7W2IxJNq2N-REFdoUPLhbvc9bSSo,1230
|
|
63
|
+
nexaai-1.0.14.dist-info/WHEEL,sha256=KUuBC6lxAbHCKilKua8R9W_TM71_-9Sg5uEP3uDWcoU,101
|
|
64
|
+
nexaai-1.0.14.dist-info/top_level.txt,sha256=LRE2YERlrZk2vfuygnSzsEeqSknnZbz3Z1MHyNmBU4w,7
|
|
65
|
+
nexaai-1.0.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|