33# Known KV config settings are defined in
44# https://github.com/lmstudio-ai/lmstudio-js/blob/main/packages/lms-kv-config/src/schema.ts
55from dataclasses import dataclass
6- from typing import Any , Container , Iterable , Sequence , Type , TypeAlias , TypeVar , cast
6+ from typing import (
7+ TYPE_CHECKING ,
8+ Any ,
9+ Callable ,
10+ Container ,
11+ Iterable ,
12+ Sequence ,
13+ Type ,
14+ TypeAlias ,
15+ TypeVar ,
16+ cast ,
17+ get_args ,
18+ )
19+ from typing_extensions import (
20+ # Native in 3.11+
21+ assert_never ,
22+ )
723
824from .sdk_api import LMStudioValueError
925from .schemas import DictObject , DictSchema , ModelSchema , MutableDictObject
1026from ._sdk_models import (
1127 EmbeddingLoadModelConfig ,
1228 EmbeddingLoadModelConfigDict ,
29+ GpuSettingDict ,
30+ GpuSplitConfig ,
31+ GpuSplitConfigDict ,
1332 KvConfig ,
1433 KvConfigFieldDict ,
1534 KvConfigStack ,
1837 LlmLoadModelConfigDict ,
1938 LlmPredictionConfig ,
2039 LlmPredictionConfigDict ,
40+ LlmSplitStrategy ,
2141 LlmStructuredPredictionSetting ,
2242 LlmStructuredPredictionSettingDict ,
2343)
@@ -54,7 +74,7 @@ def to_kv_field(
5474 def update_client_config (
5575 self , client_config : MutableDictObject , value : DictObject
5676 ) -> None :
57- if value .get ("key " , False ):
77+ if value .get ("checked " , False ):
5878 client_config [self .client_key ] = value ["value" ]
5979
6080
@@ -84,26 +104,24 @@ def update_client_config(
84104@dataclass (frozen = True )
85105class MultiPartField (ConfigField ):
86106 nested_keys : tuple [str , ...]
107+ client_to_server : Callable [..., Any ]
108+ server_to_client : Callable [[DictObject , MutableDictObject ], None ]
87109
88110 def to_kv_field (
89111 self , server_key : str , client_config : DictObject
90112 ) -> KvConfigFieldDict | None :
91- containing_value = client_config [self .client_key ]
92- value : dict [str , Any ] = {}
93- for key in self .nested_keys :
94- value [key ] = containing_value [key ]
113+ client_container : DictObject = client_config [self .client_key ]
114+ values = (client_container .get (key , None ) for key in self .nested_keys )
95115 return {
96116 "key" : server_key ,
97- "value" : value ,
117+ "value" : self . client_to_server ( * values ) ,
98118 }
99119
100120 def update_client_config (
101- self , client_config : MutableDictObject , value : DictObject
121+ self , client_config : MutableDictObject , server_value : DictObject
102122 ) -> None :
103- containing_value = client_config .setdefault (self .client_key , {})
104- for key in self .nested_keys :
105- if key in value :
106- containing_value [key ] = value [key ]
123+ client_container : MutableDictObject = client_config .setdefault (self .client_key , {})
124+ self .server_to_client (server_value , client_container )
107125
108126
109127# TODO: figure out a way to compare this module against the lmstudio-js mappings
@@ -125,10 +143,68 @@ def update_client_config(
125143 "contextLength" : ConfigField ("contextLength" ),
126144}
127145
146+
147+ def _gpu_settings_to_gpu_split_config (
148+ main_gpu : int | None ,
149+ llm_split_strategy : LlmSplitStrategy | None ,
150+ disabledGpus : Sequence [int ] | None ,
151+ ) -> GpuSplitConfigDict :
152+ gpu_split_config : GpuSplitConfigDict = {
153+ "disabledGpus" : [* disabledGpus ] if disabledGpus else [],
154+ "strategy" : "evenly" ,
155+ "priority" : [],
156+ "customRatio" : [],
157+ }
158+ match llm_split_strategy :
159+ case "evenly" | None :
160+ pass
161+ case "favorMainGpu" :
162+ gpu_split_config ["strategy" ] = "priorityOrder"
163+ if main_gpu is not None :
164+ gpu_split_config ["priority" ] = [main_gpu ]
165+ case _:
166+ if TYPE_CHECKING :
167+ assert_never (llm_split_strategy )
168+ err_msg = f"Unknown LLM GPU offload split strategy: { llm_split_strategy } "
169+ hint = f"Known strategies: { get_args (LlmSplitStrategy )} "
170+ raise LMStudioValueError (f"{ err_msg } ({ hint } )" )
171+ return gpu_split_config
172+
173+
174+ def _gpu_split_config_to_gpu_settings (
175+ server_dict : DictObject , client_dict : MutableDictObject
176+ ) -> None :
177+ gpu_settings_dict : GpuSettingDict = cast (GpuSettingDict , client_dict )
178+ gpu_split_config = GpuSplitConfig ._from_any_api_dict (server_dict )
179+ disabled_gpus = gpu_split_config .disabled_gpus
180+ if disabled_gpus is not None :
181+ gpu_settings_dict ["disabledGpus" ] = disabled_gpus
182+ match gpu_split_config .strategy :
183+ case "evenly" :
184+ gpu_settings_dict ["splitStrategy" ] = "evenly"
185+ case "priorityOrder" :
186+ # For now, this can only map to "favorMainGpu"
187+ # Skip reporting the GPU offload details otherwise
188+ priority = gpu_split_config .priority
189+ if priority is not None and len (priority ) == 1 :
190+ gpu_settings_dict ["splitStrategy" ] = "favorMainGpu"
191+ gpu_settings_dict ["mainGpu" ] = priority [0 ]
192+ case "custom" :
193+ # Currently no way to set up or report custom offload settings
194+ pass
195+ case _:
196+ if TYPE_CHECKING :
197+ assert_never (gpu_split_config .strategy )
198+ # Simply don't report details for unknown server strategies
199+
200+
128201SUPPORTED_SERVER_KEYS : dict [str , DictObject ] = {
129202 "load" : {
130203 "gpuSplitConfig" : MultiPartField (
131- "gpu" , ("mainGpu" , "splitStrategy" , "disabledGpus" )
204+ "gpu" ,
205+ ("mainGpu" , "splitStrategy" , "disabledGpus" ),
206+ _gpu_settings_to_gpu_split_config ,
207+ _gpu_split_config_to_gpu_settings ,
132208 ),
133209 "gpuStrictVramCap" : ConfigField ("gpuStrictVramCap" ),
134210 },
0 commit comments