liumaolin commited on
Commit
bdc3b7b
·
1 Parent(s): 6f77a29

Introduce Apple Silicon hardware optimization and dynamic LLM configuration

Browse files

- Add `llm_config.py` to manage LLM model parameters dynamically via Apple Silicon detection.
- Implement `apple_silicon.py` utility to retrieve and summarize hardware specifications.
- Refactor `generator.py` to leverage dynamic parameter fetching and optimize threading for performance cores.
- Enhance `system.py` with `is_apple_silicon` check and augment system info output.

src/voice_dialogue/config/llm_config.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """LLM模型配置管理"""
2
+
3
+ from typing import Dict, Any
4
+ from voice_dialogue.utils.apple_silicon import get_optimal_llama_cpp_config, get_apple_silicon_info
5
+
6
+ __all__ = ('get_llm_model_params', 'get_apple_silicon_summary')
7
+
8
+
9
+ def get_llm_model_params() -> Dict[str, Any]:
10
+ """
11
+ 获取LLM模型参数,基于Apple Silicon芯片信息动态配置
12
+
13
+ Returns:
14
+ Dict[str, Any]: LLM模型参数配置
15
+ """
16
+ # 获取Apple Silicon优化配置
17
+ optimal_config = get_optimal_llama_cpp_config()
18
+
19
+ # 基础模型参数
20
+ model_params = {
21
+ 'streaming': True,
22
+ 'temperature': 0.7,
23
+ 'top_p': 0.9,
24
+ 'top_k': 20,
25
+ 'model_kwargs': {
26
+ 'mini_p': 0,
27
+ 'presence_penalty': 1.5
28
+ },
29
+ 'verbose': False,
30
+ }
31
+
32
+ # 应用Apple Silicon优化配置
33
+ model_params.update(optimal_config)
34
+
35
+ return model_params
36
+
37
+
38
+ def get_apple_silicon_summary() -> Dict[str, Any]:
39
+ """
40
+ 获取Apple Silicon芯片信息摘要
41
+
42
+ Returns:
43
+ Dict[str, Any]: 芯片信息摘要
44
+ """
45
+ chip_info = get_apple_silicon_info()
46
+ optimal_config = get_optimal_llama_cpp_config()
47
+
48
+ return {
49
+ 'chip_name': chip_info.chip_name,
50
+ 'is_apple_silicon': chip_info.is_apple_silicon,
51
+ 'total_cores': chip_info.total_cores,
52
+ 'performance_cores': chip_info.performance_cores,
53
+ 'efficiency_cores': chip_info.efficiency_cores,
54
+ 'memory_gb': chip_info.memory_gb,
55
+ 'gpu_cores': chip_info.gpu_cores,
56
+ 'optimal_n_threads': optimal_config['n_threads'],
57
+ 'optimal_n_ctx': optimal_config['n_ctx'],
58
+ 'config_note': '仅使用性能核心(P-cores)以获得最佳性能' if chip_info.is_apple_silicon else '标准配置'
59
+ }
60
+
61
+
62
+ # 预设配置模板
63
+ LLAMA_CPP_CONFIG_PRESETS = {
64
+ 'high_performance': {
65
+ 'description': '高性能配置 - 适合Apple M1 Pro/Max及以上',
66
+ 'n_ctx': 8192,
67
+ 'temperature': 0.7,
68
+ 'top_p': 0.9,
69
+ 'top_k': 20,
70
+ },
71
+ 'balanced': {
72
+ 'description': '平衡配置 - 适合Apple M1/M2基础版',
73
+ 'n_ctx': 4096,
74
+ 'temperature': 0.7,
75
+ 'top_p': 0.9,
76
+ 'top_k': 20,
77
+ },
78
+ 'memory_efficient': {
79
+ 'description': '内存优化配置 - 适合内存较小的设备',
80
+ 'n_ctx': 2048,
81
+ 'temperature': 0.7,
82
+ 'top_p': 0.9,
83
+ 'top_k': 20,
84
+ }
85
+ }
86
+
87
+
88
+ def get_config_preset(preset_name: str) -> Dict[str, Any]:
89
+ """
90
+ 获取预设配置
91
+
92
+ Args:
93
+ preset_name: 预设名称 ('high_performance', 'balanced', 'memory_efficient')
94
+
95
+ Returns:
96
+ Dict[str, Any]: 预设配置
97
+ """
98
+ if preset_name not in LLAMA_CPP_CONFIG_PRESETS:
99
+ raise ValueError(f"未知的预设配置: {preset_name}")
100
+
101
+ preset = LLAMA_CPP_CONFIG_PRESETS[preset_name].copy()
102
+
103
+ # 移除描述字段
104
+ preset.pop('description', None)
105
+
106
+ # 添加n_threads配置(基于当前硬件)
107
+ optimal_config = get_optimal_llama_cpp_config()
108
+ preset['n_threads'] = optimal_config['n_threads']
109
+
110
+ return preset
src/voice_dialogue/services/text/generator.py CHANGED
@@ -6,6 +6,7 @@ from langchain.memory import ConversationBufferWindowMemory
6
  from langchain_core.chat_history import InMemoryChatMessageHistory
7
 
8
  from voice_dialogue.config import paths
 
9
  from voice_dialogue.core.base import BaseThread
10
  from voice_dialogue.core.constants import chat_history_cache
11
  from voice_dialogue.models.voice_task import VoiceTask, QuestionDisplayMessage
@@ -191,18 +192,17 @@ class LLMResponseGenerator(BaseThread):
191
 
192
  def run(self):
193
  model_path = paths.LLM_MODELS_PATH / 'qwen' / 'Qwen3-8B-Q6_K.gguf'
194
- model_params = {
195
- 'streaming': True,
196
- 'n_ctx': 32768,
197
- 'temperature': 0.7,
198
- 'top_p': 0.9,
199
- 'top_k': 20,
200
- 'model_kwargs': {
201
- 'mini_p': 0,
202
- 'presence_penalty': 1.5
203
- },
204
- 'verbose': False,
205
- }
206
  self.model_instance = create_langchain_chat_llamacpp_instance(
207
  local_model_path=model_path, model_params=model_params
208
  )
 
6
  from langchain_core.chat_history import InMemoryChatMessageHistory
7
 
8
  from voice_dialogue.config import paths
9
+ from voice_dialogue.config.llm_config import get_llm_model_params, get_apple_silicon_summary
10
  from voice_dialogue.core.base import BaseThread
11
  from voice_dialogue.core.constants import chat_history_cache
12
  from voice_dialogue.models.voice_task import VoiceTask, QuestionDisplayMessage
 
192
 
193
  def run(self):
194
  model_path = paths.LLM_MODELS_PATH / 'qwen' / 'Qwen3-8B-Q6_K.gguf'
195
+
196
+ model_params = get_llm_model_params()
197
+
198
+ # 打印芯片信息和优化配置
199
+ chip_summary = get_apple_silicon_summary()
200
+ print(f"检测到芯片: {chip_summary['chip_name']}")
201
+ print(f"性能核心数: {chip_summary['performance_cores']}")
202
+ print(f"使用线程数: {chip_summary['optimal_n_threads']} (仅使用性能核心)")
203
+ print(f"上下文窗口: {chip_summary['optimal_n_ctx']}")
204
+ print(f"配置说明: {chip_summary['config_note']}")
205
+
 
206
  self.model_instance = create_langchain_chat_llamacpp_instance(
207
  local_model_path=model_path, model_params=model_params
208
  )
src/voice_dialogue/utils/apple_silicon.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Apple Silicon 芯片信息获取工具"""
2
+
3
+ import json
4
+ import subprocess
5
+ import platform
6
+ from typing import Dict, Optional
7
+ from dataclasses import dataclass
8
+
9
+ __all__ = ('AppleSiliconInfo', 'get_apple_silicon_info')
10
+
11
+
12
+ @dataclass
13
+ class AppleSiliconInfo:
14
+ """Apple Silicon 芯片信息"""
15
+ chip_name: str # 如 "Apple M1", "Apple M1 Pro", "Apple M2 Max"
16
+ total_cores: int # 总核心数
17
+ performance_cores: int # 性能核心数(P-cores)
18
+ efficiency_cores: int # 效率核心数(E-cores)
19
+ gpu_cores: int # GPU核心数
20
+ memory_gb: int # 内存大小(GB)
21
+ is_apple_silicon: bool # 是否为Apple Silicon
22
+
23
+
24
+ def _get_system_profiler_info() -> Optional[Dict]:
25
+ """通过system_profiler获取硬件信息"""
26
+ try:
27
+ # 获取硬件信息
28
+ result = subprocess.run([
29
+ 'system_profiler', 'SPHardwareDataType', '-json'
30
+ ], capture_output=True, text=True, timeout=10)
31
+
32
+ if result.returncode == 0:
33
+ data = json.loads(result.stdout)
34
+ return data.get('SPHardwareDataType', [{}])[0]
35
+ except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
36
+ pass
37
+ return None
38
+
39
+
40
+ def _parse_cpu_info_from_sysctl() -> Optional[Dict]:
41
+ """通过sysctl获取CPU信息"""
42
+ try:
43
+ # 获取CPU核心信息
44
+ result = subprocess.run([
45
+ 'sysctl', '-n',
46
+ 'hw.perflevel0.physicalcpu', # 性能核心数
47
+ 'hw.perflevel1.physicalcpu', # 效率核心数
48
+ 'hw.logicalcpu', # 逻辑核心数
49
+ 'hw.memsize' # 内存大小
50
+ ], capture_output=True, text=True, timeout=5)
51
+
52
+ if result.returncode == 0:
53
+ lines = result.stdout.strip().split('\n')
54
+ if len(lines) >= 4:
55
+ return {
56
+ 'performance_cores': int(lines[0]) if lines[0].isdigit() else 0,
57
+ 'efficiency_cores': int(lines[1]) if lines[1].isdigit() else 0,
58
+ 'total_cores': int(lines[2]) if lines[2].isdigit() else 0,
59
+ 'memory_bytes': int(lines[3]) if lines[3].isdigit() else 0
60
+ }
61
+ except (subprocess.TimeoutExpired, ValueError, IndexError, FileNotFoundError):
62
+ pass
63
+ return None
64
+
65
+
66
+ def _get_gpu_cores_from_system_profiler() -> int:
67
+ """获取GPU核心数"""
68
+ try:
69
+ result = subprocess.run([
70
+ 'system_profiler', 'SPDisplaysDataType', '-json'
71
+ ], capture_output=True, text=True, timeout=10)
72
+
73
+ if result.returncode == 0:
74
+ data = json.loads(result.stdout)
75
+ displays = data.get('SPDisplaysDataType', [])
76
+ for display in displays:
77
+ if 'sppci_cores' in display:
78
+ return int(display['sppci_cores'])
79
+ # 根据芯片名称估算GPU核心数
80
+ model = display.get('sppci_model', '').lower()
81
+ if 'm1 max' in model:
82
+ return 32
83
+ elif 'm1 pro' in model:
84
+ return 16 if '16-core' in model else 14
85
+ elif 'm1' in model:
86
+ return 8 if '8-core' in model else 7
87
+ elif 'm2 max' in model:
88
+ return 38
89
+ elif 'm2 pro' in model:
90
+ return 19 if '19-core' in model else 16
91
+ elif 'm2' in model:
92
+ return 10 if '10-core' in model else 8
93
+ elif 'm3 max' in model:
94
+ return 40
95
+ elif 'm3 pro' in model:
96
+ return 18 if '18-core' in model else 14
97
+ elif 'm3' in model:
98
+ return 10 if '10-core' in model else 8
99
+ except (subprocess.TimeoutExpired, json.JSONDecodeError, ValueError, FileNotFoundError):
100
+ pass
101
+ return 8 # 默认值
102
+
103
+
104
+ def get_apple_silicon_info() -> AppleSiliconInfo:
105
+ """
106
+ 获取Apple Silicon芯片信息
107
+
108
+ Returns:
109
+ AppleSiliconInfo: 芯片信息对象
110
+ """
111
+ # 检查是否为Apple Silicon
112
+ machine = platform.machine()
113
+ is_apple_silicon = machine in ('arm64', 'arm64e') and platform.system() == 'Darwin'
114
+
115
+ if not is_apple_silicon:
116
+ return AppleSiliconInfo(
117
+ chip_name="非Apple Silicon",
118
+ total_cores=1,
119
+ performance_cores=1,
120
+ efficiency_cores=0,
121
+ gpu_cores=0,
122
+ memory_gb=8,
123
+ is_apple_silicon=False
124
+ )
125
+
126
+ # 获取系统信息
127
+ hw_info = _get_system_profiler_info()
128
+ cpu_info = _parse_cpu_info_from_sysctl()
129
+
130
+ # 解析芯片名称
131
+ chip_name = "Apple Silicon"
132
+ if hw_info and 'chip_type' in hw_info:
133
+ chip_name = hw_info['chip_type']
134
+ elif hw_info and 'cpu_type' in hw_info:
135
+ chip_name = hw_info['cpu_type']
136
+
137
+ # 解析核心信息
138
+ performance_cores = 4 # 默认值
139
+ efficiency_cores = 4 # 默认值
140
+ total_cores = 8 # 默认值
141
+
142
+ if cpu_info:
143
+ performance_cores = cpu_info.get('performance_cores', performance_cores)
144
+ efficiency_cores = cpu_info.get('efficiency_cores', efficiency_cores)
145
+ total_cores = cpu_info.get('total_cores', total_cores)
146
+
147
+ # 如果sysctl失败,尝试从芯片名称推断
148
+ if performance_cores == 0 and efficiency_cores == 0:
149
+ chip_lower = chip_name.lower()
150
+ if 'm1 max' in chip_lower:
151
+ performance_cores, efficiency_cores = 8, 2
152
+ elif 'm1 pro' in chip_lower:
153
+ performance_cores, efficiency_cores = 8, 2
154
+ elif 'm1' in chip_lower:
155
+ performance_cores, efficiency_cores = 4, 4
156
+ elif 'm2 max' in chip_lower:
157
+ performance_cores, efficiency_cores = 8, 4
158
+ elif 'm2 pro' in chip_lower:
159
+ performance_cores, efficiency_cores = 8, 4
160
+ elif 'm2' in chip_lower:
161
+ performance_cores, efficiency_cores = 4, 4
162
+ elif 'm3' in chip_lower:
163
+ if 'max' in chip_lower:
164
+ performance_cores, efficiency_cores = 12, 4
165
+ elif 'pro' in chip_lower:
166
+ performance_cores, efficiency_cores = 8, 4
167
+ else:
168
+ performance_cores, efficiency_cores = 4, 4
169
+
170
+ total_cores = performance_cores + efficiency_cores
171
+
172
+ # 获取内存大小
173
+ memory_gb = 8 # 默认值
174
+ if cpu_info and 'memory_bytes' in cpu_info:
175
+ memory_gb = cpu_info['memory_bytes'] // (1024 ** 3)
176
+ elif hw_info and 'physical_memory' in hw_info:
177
+ memory_str = hw_info['physical_memory'].replace(' GB', '').replace(',', '')
178
+ try:
179
+ memory_gb = int(memory_str)
180
+ except ValueError:
181
+ pass
182
+
183
+ # 获取GPU核心数
184
+ gpu_cores = _get_gpu_cores_from_system_profiler()
185
+
186
+ return AppleSiliconInfo(
187
+ chip_name=chip_name,
188
+ total_cores=total_cores,
189
+ performance_cores=performance_cores,
190
+ efficiency_cores=efficiency_cores,
191
+ gpu_cores=gpu_cores,
192
+ memory_gb=memory_gb,
193
+ is_apple_silicon=True
194
+ )
195
+
196
+
197
+ def get_optimal_llama_cpp_config() -> Dict[str, int]:
198
+ """
199
+ 根据Apple Silicon芯片信息获取最优的llama.cpp配置
200
+
201
+ Returns:
202
+ Dict[str, int]: 包含n_threads和n_ctx的配置
203
+ """
204
+ chip_info = get_apple_silicon_info()
205
+
206
+ if not chip_info.is_apple_silicon:
207
+ # 非Apple Silicon系统的默认配置
208
+ return {
209
+ 'n_threads': 4,
210
+ 'n_ctx': 2048
211
+ }
212
+
213
+ # 对于Apple Silicon,只使用性能核心(P-cores)
214
+ # 避免混合使用效率核心,以获得最佳性能
215
+ n_threads = chip_info.performance_cores
216
+
217
+ # 根据内存大小调整上下文窗口
218
+ if chip_info.memory_gb >= 32:
219
+ n_ctx = 8192
220
+ elif chip_info.memory_gb >= 16:
221
+ n_ctx = 4096
222
+ else:
223
+ n_ctx = 2048
224
+
225
+ return {
226
+ 'n_threads': n_threads,
227
+ 'n_ctx': n_ctx
228
+ }
229
+
230
+
231
+ if __name__ == '__main__':
232
+ # 测试功能
233
+ info = get_apple_silicon_info()
234
+ print(f"芯片信息: {info}")
235
+
236
+ config = get_optimal_llama_cpp_config()
237
+ print(f"推荐配置: {config}")
src/voice_dialogue/utils/system.py CHANGED
@@ -5,7 +5,7 @@ import os
5
  import platform
6
  from typing import Literal
7
 
8
- __all__ = ('get_system_language', 'get_system_info')
9
 
10
 
11
  def get_system_language() -> Literal['zh', 'en']:
@@ -43,13 +43,24 @@ def get_system_language() -> Literal['zh', 'en']:
43
  return 'en'
44
 
45
  except Exception:
46
- # 如果所有方法都失败,返回默认值
47
  pass
 
48
 
49
  # 默认返回中文
50
  return 'zh'
51
 
52
 
 
 
 
 
 
 
 
 
 
 
 
53
  def get_system_info() -> dict:
54
  """
55
  获取系统信息
@@ -57,7 +68,7 @@ def get_system_info() -> dict:
57
  Returns:
58
  dict: 包含系统信息的字典
59
  """
60
- return {
61
  'platform': platform.system(),
62
  'platform_version': platform.version(),
63
  'architecture': platform.architecture()[0],
@@ -65,4 +76,7 @@ def get_system_info() -> dict:
65
  'processor': platform.processor(),
66
  'language': get_system_language(),
67
  'python_version': platform.python_version(),
 
68
  }
 
 
 
5
  import platform
6
  from typing import Literal
7
 
8
+ __all__ = ('get_system_language', 'get_system_info', 'is_apple_silicon')
9
 
10
 
11
  def get_system_language() -> Literal['zh', 'en']:
 
43
  return 'en'
44
 
45
  except Exception:
 
46
  pass
47
+ # 如果所有方法都失败,返回默认值
48
 
49
  # 默认返回中文
50
  return 'zh'
51
 
52
 
53
+ def is_apple_silicon() -> bool:
54
+ """
55
+ 检查当前系统是否为Apple Silicon
56
+
57
+ Returns:
58
+ bool: 如果是Apple Silicon返回True,否则返回False
59
+ """
60
+ return (platform.system() == 'Darwin' and
61
+ platform.machine() in ('arm64', 'arm64e'))
62
+
63
+
64
  def get_system_info() -> dict:
65
  """
66
  获取系统信息
 
68
  Returns:
69
  dict: 包含系统信息的字典
70
  """
71
+ info = {
72
  'platform': platform.system(),
73
  'platform_version': platform.version(),
74
  'architecture': platform.architecture()[0],
 
76
  'processor': platform.processor(),
77
  'language': get_system_language(),
78
  'python_version': platform.python_version(),
79
+ 'is_apple_silicon': is_apple_silicon(),
80
  }
81
+
82
+ return info