nexaai 1.0.4rc13__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.

Potentially problematic release.


This version of nexaai might be problematic. Click here for more details.

Files changed (59) hide show
  1. nexaai/__init__.py +71 -0
  2. nexaai/_stub.cp310-win_amd64.pyd +0 -0
  3. nexaai/_version.py +4 -0
  4. nexaai/asr.py +60 -0
  5. nexaai/asr_impl/__init__.py +0 -0
  6. nexaai/asr_impl/mlx_asr_impl.py +91 -0
  7. nexaai/asr_impl/pybind_asr_impl.py +43 -0
  8. nexaai/base.py +39 -0
  9. nexaai/binds/__init__.py +3 -0
  10. nexaai/binds/common_bind.cp310-win_amd64.pyd +0 -0
  11. nexaai/binds/embedder_bind.cp310-win_amd64.pyd +0 -0
  12. nexaai/binds/llm_bind.cp310-win_amd64.pyd +0 -0
  13. nexaai/binds/nexa_bridge.dll +0 -0
  14. nexaai/binds/nexa_llama_cpp/ggml-base.dll +0 -0
  15. nexaai/binds/nexa_llama_cpp/ggml-cpu.dll +0 -0
  16. nexaai/binds/nexa_llama_cpp/ggml-cuda.dll +0 -0
  17. nexaai/binds/nexa_llama_cpp/ggml-vulkan.dll +0 -0
  18. nexaai/binds/nexa_llama_cpp/ggml.dll +0 -0
  19. nexaai/binds/nexa_llama_cpp/llama.dll +0 -0
  20. nexaai/binds/nexa_llama_cpp/mtmd.dll +0 -0
  21. nexaai/binds/nexa_llama_cpp/nexa_plugin.dll +0 -0
  22. nexaai/common.py +61 -0
  23. nexaai/cv.py +87 -0
  24. nexaai/cv_impl/__init__.py +0 -0
  25. nexaai/cv_impl/mlx_cv_impl.py +88 -0
  26. nexaai/cv_impl/pybind_cv_impl.py +31 -0
  27. nexaai/embedder.py +68 -0
  28. nexaai/embedder_impl/__init__.py +0 -0
  29. nexaai/embedder_impl/mlx_embedder_impl.py +114 -0
  30. nexaai/embedder_impl/pybind_embedder_impl.py +91 -0
  31. nexaai/image_gen.py +136 -0
  32. nexaai/image_gen_impl/__init__.py +0 -0
  33. nexaai/image_gen_impl/mlx_image_gen_impl.py +291 -0
  34. nexaai/image_gen_impl/pybind_image_gen_impl.py +84 -0
  35. nexaai/llm.py +89 -0
  36. nexaai/llm_impl/__init__.py +0 -0
  37. nexaai/llm_impl/mlx_llm_impl.py +249 -0
  38. nexaai/llm_impl/pybind_llm_impl.py +207 -0
  39. nexaai/rerank.py +51 -0
  40. nexaai/rerank_impl/__init__.py +0 -0
  41. nexaai/rerank_impl/mlx_rerank_impl.py +91 -0
  42. nexaai/rerank_impl/pybind_rerank_impl.py +42 -0
  43. nexaai/runtime.py +64 -0
  44. nexaai/tts.py +70 -0
  45. nexaai/tts_impl/__init__.py +0 -0
  46. nexaai/tts_impl/mlx_tts_impl.py +93 -0
  47. nexaai/tts_impl/pybind_tts_impl.py +42 -0
  48. nexaai/utils/avatar_fetcher.py +104 -0
  49. nexaai/utils/decode.py +18 -0
  50. nexaai/utils/model_manager.py +1195 -0
  51. nexaai/utils/progress_tracker.py +372 -0
  52. nexaai/vlm.py +120 -0
  53. nexaai/vlm_impl/__init__.py +0 -0
  54. nexaai/vlm_impl/mlx_vlm_impl.py +205 -0
  55. nexaai/vlm_impl/pybind_vlm_impl.py +228 -0
  56. nexaai-1.0.4rc13.dist-info/METADATA +26 -0
  57. nexaai-1.0.4rc13.dist-info/RECORD +59 -0
  58. nexaai-1.0.4rc13.dist-info/WHEEL +5 -0
  59. nexaai-1.0.4rc13.dist-info/top_level.txt +1 -0
nexaai/runtime.py ADDED
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+ import atexit
3
+ import threading
4
+ from typing import Optional, Any
5
+
6
+ from nexaai.binds import common_bind
7
+
8
+ _init_lock = threading.Lock()
9
+ _runtime_alive = False # global flag
10
+
11
+ def _ensure_runtime() -> None:
12
+ """Initialise the runtime exactly once (thread‑safe, lazy)."""
13
+ global _runtime_alive
14
+ if not _runtime_alive:
15
+ with _init_lock:
16
+ if not _runtime_alive: # double‑checked locking
17
+ common_bind.ml_init()
18
+ _runtime_alive = True
19
+ atexit.register(_shutdown_runtime)
20
+
21
+ def _shutdown_runtime() -> None:
22
+ """Tear the runtime down; idempotent and registered with atexit."""
23
+ global _runtime_alive
24
+ if _runtime_alive:
25
+ common_bind.ml_deinit()
26
+ _runtime_alive = False
27
+
28
+ # Public helper so advanced users can reclaim memory on demand
29
+ shutdown = _shutdown_runtime
30
+
31
+ # ----------------------------------------------------------------------
32
+ # Single public class
33
+ # ----------------------------------------------------------------------
34
+ class Session:
35
+ """
36
+ Model session **and** runtime guard in one object.
37
+
38
+ sess = myrt.Session("foo.mdl")
39
+ out = sess.run(inputs)
40
+ sess.close() # optional (model only)
41
+
42
+ The global runtime is initialised lazily when the first Session
43
+ is created and stays alive until:
44
+ • the interpreter exits, or
45
+ • `myrt.shutdown()` is called.
46
+ """
47
+
48
+ # ---- construction -------------------------------------------------
49
+ def __init__(self, model_path: str) -> None:
50
+ _ensure_runtime()
51
+
52
+ # safety net – make GC close the model
53
+ def __del__(self) -> None:
54
+ try:
55
+ self.close()
56
+ except Exception:
57
+ pass
58
+
59
+ # allow `with Session(...) as s:` syntax
60
+ def __enter__(self) -> "Session":
61
+ return self
62
+
63
+ def __exit__(self, exc_type, exc, tb) -> None:
64
+ self.close()
nexaai/tts.py ADDED
@@ -0,0 +1,70 @@
1
+ from typing import List, Optional
2
+ from abc import abstractmethod
3
+ from dataclasses import dataclass
4
+
5
+ from nexaai.base import BaseModel
6
+
7
+
8
+ @dataclass
9
+ class TTSConfig:
10
+ """Configuration for TTS."""
11
+ voice: str = "default"
12
+ speed: float = 1.0
13
+ seed: int = -1 # –1 for random
14
+ sample_rate: int = 22050
15
+
16
+
17
+ @dataclass
18
+ class TTSSamplerConfig:
19
+ """Configuration for TTS sampling."""
20
+ temperature: float = 1.0
21
+ noise_scale: float = 0.667
22
+ length_scale: float = 1.0
23
+
24
+
25
+ @dataclass
26
+ class TTSResult:
27
+ """Result from TTS processing."""
28
+ audio_path: str # Path where the synthesized audio is saved
29
+ duration_seconds: float
30
+ sample_rate: int
31
+ channels: int
32
+ num_samples: int
33
+
34
+
35
+ class TTS(BaseModel):
36
+ """Abstract base class for Text-to-Speech models."""
37
+
38
+ def __init__(self):
39
+ """Initialize base TTS class."""
40
+ pass
41
+
42
+ @classmethod
43
+ def _load_from(cls,
44
+ model_path: str,
45
+ vocoder_path: str,
46
+ plugin_id: str = "llama_cpp",
47
+ device_id: Optional[str] = None
48
+ ) -> 'TTS':
49
+ """Load TTS model from local path, routing to appropriate implementation."""
50
+ if plugin_id == "mlx":
51
+ from nexaai.tts_impl.mlx_tts_impl import MLXTTSImpl
52
+ return MLXTTSImpl._load_from(model_path, vocoder_path, plugin_id, device_id)
53
+ else:
54
+ from nexaai.tts_impl.pybind_tts_impl import PyBindTTSImpl
55
+ return PyBindTTSImpl._load_from(model_path, vocoder_path, plugin_id, device_id)
56
+
57
+ @abstractmethod
58
+ def synthesize(
59
+ self,
60
+ text: str,
61
+ config: Optional[TTSConfig] = None,
62
+ output_path: Optional[str] = None,
63
+ ) -> TTSResult:
64
+ """Synthesize speech from text and save to filesystem."""
65
+ pass
66
+
67
+ @abstractmethod
68
+ def list_available_voices(self) -> List[str]:
69
+ """List available voices."""
70
+ pass
File without changes
@@ -0,0 +1,93 @@
1
+ # Note: This code is generated by Cursor, not tested yet.
2
+
3
+ from typing import List, Optional
4
+ import os
5
+
6
+ from nexaai.tts import TTS, TTSConfig, TTSResult
7
+ from nexaai.mlx_backend.tts.interface import MlxTts as MLXTTSInterface
8
+
9
+
10
+ class MLXTTSImpl(TTS):
11
+ def __init__(self):
12
+ """Initialize MLX TTS implementation."""
13
+ super().__init__()
14
+ self._mlx_tts = None
15
+
16
+ @classmethod
17
+ def _load_from(cls,
18
+ model_path: str,
19
+ vocoder_path: str,
20
+ plugin_id: str = "mlx",
21
+ device_id: Optional[str] = None
22
+ ) -> 'MLXTTSImpl':
23
+ """Load TTS model from local path using MLX backend."""
24
+ try:
25
+ # MLX TTS interface is already imported
26
+
27
+ # Create instance and load MLX TTS
28
+ instance = cls()
29
+ instance._mlx_tts = MLXTTSInterface(
30
+ model_path=model_path,
31
+ vocoder_path=vocoder_path,
32
+ device=device_id
33
+ )
34
+
35
+ return instance
36
+ except Exception as e:
37
+ raise RuntimeError(f"Failed to load MLX TTS: {str(e)}")
38
+
39
+ def eject(self):
40
+ """Destroy the model and free resources."""
41
+ if self._mlx_tts:
42
+ self._mlx_tts.destroy()
43
+ self._mlx_tts = None
44
+
45
+ def synthesize(
46
+ self,
47
+ text: str,
48
+ config: Optional[TTSConfig] = None,
49
+ output_path: Optional[str] = None,
50
+ ) -> TTSResult:
51
+ """Synthesize speech from text and save to filesystem."""
52
+ if not self._mlx_tts:
53
+ raise RuntimeError("MLX TTS not loaded")
54
+
55
+ try:
56
+ # Convert our config to MLX format if provided
57
+ mlx_config = None
58
+ if config:
59
+ from nexaai.mlx_backend.ml import TTSConfig as MLXTTSConfig
60
+
61
+ mlx_config = MLXTTSConfig(
62
+ voice=config.voice,
63
+ speed=config.speed,
64
+ seed=config.seed,
65
+ sample_rate=config.sample_rate
66
+ )
67
+
68
+ # Use MLX TTS synthesis
69
+ result = self._mlx_tts.synthesize(text, mlx_config, output_path)
70
+
71
+ # Convert MLX result to our format
72
+ return TTSResult(
73
+ audio_path=result.audio_path,
74
+ duration_seconds=result.duration_seconds,
75
+ sample_rate=result.sample_rate,
76
+ channels=result.channels,
77
+ num_samples=result.num_samples
78
+ )
79
+
80
+ except Exception as e:
81
+ raise RuntimeError(f"Failed to synthesize speech: {str(e)}")
82
+
83
+ def list_available_voices(self) -> List[str]:
84
+ """List available voices."""
85
+ if not self._mlx_tts:
86
+ raise RuntimeError("MLX TTS not loaded")
87
+
88
+ try:
89
+ return self._mlx_tts.list_available_voices()
90
+ except Exception as e:
91
+ raise RuntimeError(f"Failed to list available voices: {str(e)}")
92
+
93
+
@@ -0,0 +1,42 @@
1
+ from typing import List, Optional
2
+
3
+ from nexaai.tts import TTS, TTSConfig, TTSResult
4
+
5
+
6
+ class PyBindTTSImpl(TTS):
7
+ def __init__(self):
8
+ """Initialize PyBind TTS implementation."""
9
+ super().__init__()
10
+ # TODO: Add PyBind-specific initialization
11
+
12
+ @classmethod
13
+ def _load_from(cls,
14
+ model_path: str,
15
+ vocoder_path: str,
16
+ plugin_id: str = "llama_cpp",
17
+ device_id: Optional[str] = None
18
+ ) -> 'PyBindTTSImpl':
19
+ """Load TTS model from local path using PyBind backend."""
20
+ # TODO: Implement PyBind TTS loading
21
+ instance = cls()
22
+ return instance
23
+
24
+ def eject(self):
25
+ """Destroy the model and free resources."""
26
+ # TODO: Implement PyBind TTS cleanup
27
+ pass
28
+
29
+ def synthesize(
30
+ self,
31
+ text: str,
32
+ config: Optional[TTSConfig] = None,
33
+ output_path: Optional[str] = None,
34
+ ) -> TTSResult:
35
+ """Synthesize speech from text and save to filesystem."""
36
+ # TODO: Implement PyBind TTS synthesis
37
+ raise NotImplementedError("PyBind TTS synthesis not yet implemented")
38
+
39
+ def list_available_voices(self) -> List[str]:
40
+ """List available voices."""
41
+ # TODO: Implement PyBind TTS voice listing
42
+ raise NotImplementedError("PyBind TTS voice listing not yet implemented")
@@ -0,0 +1,104 @@
1
+ """Utility for fetching avatar URLs from HuggingFace."""
2
+
3
+ import logging
4
+ from typing import Dict, Optional
5
+ import httpx
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def fetch_avatar_urls_from_hf_api(query: str, custom_endpoint: Optional[str] = None) -> Dict[str, str]:
11
+ """
12
+ Fetch avatar URLs from HuggingFace models-json endpoint.
13
+
14
+ Args:
15
+ query: Search query to fetch models for
16
+ custom_endpoint: Optional custom HuggingFace endpoint
17
+
18
+ Returns:
19
+ Dictionary mapping author names to avatar URLs
20
+ """
21
+ avatar_map = {}
22
+ try:
23
+ # Use the base URL from the configured endpoint
24
+ base_url = custom_endpoint if custom_endpoint else "https://huggingface.co"
25
+
26
+ # Build the URL with query parameter
27
+ url = f"{base_url}/models-json?sort=trending&search={query}&withCount=true"
28
+
29
+ # Make the HTTP request with a timeout
30
+ with httpx.Client(timeout=2.0) as client:
31
+ response = client.get(url)
32
+
33
+ if response.status_code == 200:
34
+ data = response.json()
35
+ models = data.get("models", [])
36
+
37
+ # Build a map of author names to avatar URLs
38
+ for model in models:
39
+ author = model.get("author")
40
+ author_data = model.get("authorData", {})
41
+ avatar_url = author_data.get("avatarUrl")
42
+
43
+ if author and avatar_url:
44
+ # Handle relative URLs by prepending appropriate base URL
45
+ if avatar_url.startswith("/"):
46
+ avatar_url = f"{base_url}{avatar_url}"
47
+ avatar_map[author] = avatar_url
48
+
49
+ logger.debug(f"Fetched {len(avatar_map)} avatar URLs from HuggingFace API")
50
+ else:
51
+ logger.warning(f"Failed to fetch avatar URLs: HTTP {response.status_code}")
52
+
53
+ except Exception as e:
54
+ logger.warning(f"Error fetching avatar URLs from HuggingFace API: {e}")
55
+ # Return empty map on error - we'll fall back to default behavior
56
+
57
+ return avatar_map
58
+
59
+
60
+ def get_avatar_url_for_repo(repo_id: str, search_query: Optional[str] = None,
61
+ custom_endpoint: Optional[str] = None) -> Optional[str]:
62
+ """
63
+ Get avatar URL for a repository ID.
64
+
65
+ This method tries multiple strategies:
66
+ 1. If search_query is provided, fetch from HuggingFace API with that query
67
+ 2. Try fetching with the full repo_id as query
68
+ 3. Try fetching with just the organization name as query
69
+ 4. Fall back to CDN URL pattern
70
+
71
+ Args:
72
+ repo_id: Repository ID in format "owner/repo"
73
+ search_query: Optional search query to use for fetching avatars
74
+ custom_endpoint: Optional custom HuggingFace endpoint
75
+
76
+ Returns:
77
+ Avatar URL or None if not found
78
+ """
79
+ if "/" not in repo_id:
80
+ return None
81
+
82
+ org_name = repo_id.split("/")[0]
83
+
84
+ # Try with search query if provided
85
+ if search_query:
86
+ avatar_map = fetch_avatar_urls_from_hf_api(search_query, custom_endpoint)
87
+ avatar_url = avatar_map.get(org_name)
88
+ if avatar_url:
89
+ return avatar_url
90
+
91
+ # Try with full repo_id
92
+ avatar_map = fetch_avatar_urls_from_hf_api(repo_id, custom_endpoint)
93
+ avatar_url = avatar_map.get(org_name)
94
+ if avatar_url:
95
+ return avatar_url
96
+
97
+ # Try with just organization name
98
+ avatar_map = fetch_avatar_urls_from_hf_api(org_name, custom_endpoint)
99
+ avatar_url = avatar_map.get(org_name)
100
+ if avatar_url:
101
+ return avatar_url
102
+
103
+ # Fallback to CDN URL pattern
104
+ return f"https://cdn-thumbnails.huggingface.co/social-thumbnails/{org_name}.png"
nexaai/utils/decode.py ADDED
@@ -0,0 +1,18 @@
1
+ """
2
+ Utility functions for text decoding operations.
3
+ """
4
+
5
+
6
+ def safe_decode(data):
7
+ """
8
+ Safely decode bytes or text, handling UTF-8 errors.
9
+
10
+ Args:
11
+ data: Input data that can be bytes or text
12
+
13
+ Returns:
14
+ str: Decoded string with errors replaced if any
15
+ """
16
+ if isinstance(data, bytes):
17
+ return data.decode('utf-8', errors='replace')
18
+ return str(data)