Skip to content

Commit 1abb404

Browse files
Add MODEL START and MODEL STOP fusion commands (#101)
* fusion commands to start and stop model * changes * changes * changes * pre commit checks * review changes * checks changes * debug issue commit * dubug issue changes * response changes * Fix double-quoted string in get_message method * replaced inferenceapis with models in model routes * revert get endpoint changes
1 parent 79d1bad commit 1abb404

File tree

3 files changed

+274
-1
lines changed

3 files changed

+274
-1
lines changed

singlestoredb/fusion/handlers/models.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from typing import Dict
55
from typing import Optional
66

7+
from .. import result
78
from ..handler import SQLHandler
89
from ..result import FusionSQLResult
910
from .files import ShowFilesHandler
1011
from .utils import get_file_space
12+
from .utils import get_inference_api
1113

1214

1315
class ShowModelsHandler(ShowFilesHandler):
@@ -248,3 +250,99 @@ def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
248250

249251

250252
DropModelsHandler.register(overwrite=True)
253+
254+
255+
class StartModelHandler(SQLHandler):
256+
"""
257+
START MODEL model_name ;
258+
259+
# Model Name
260+
model_name = '<model-name>'
261+
262+
Description
263+
-----------
264+
Starts an inference API model.
265+
266+
Arguments
267+
---------
268+
* ``<model-name>``: Name of the model to start.
269+
270+
Example
271+
--------
272+
The following command starts a model::
273+
274+
START MODEL my_model;
275+
276+
See Also
277+
--------
278+
* ``STOP MODEL model_name``
279+
* ``SHOW MODELS``
280+
281+
""" # noqa: E501
282+
283+
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
284+
inference_api = get_inference_api(params)
285+
operation_result = inference_api.start()
286+
287+
res = FusionSQLResult()
288+
res.add_field('Status', result.STRING)
289+
res.add_field('Message', result.STRING)
290+
res.set_rows([
291+
(
292+
operation_result.status,
293+
operation_result.get_message(),
294+
),
295+
])
296+
297+
return res
298+
299+
300+
StartModelHandler.register(overwrite=True)
301+
302+
303+
class StopModelHandler(SQLHandler):
304+
"""
305+
STOP MODEL model_name ;
306+
307+
# Model Name
308+
model_name = '<model-name>'
309+
310+
Description
311+
-----------
312+
Stops an inference API model.
313+
314+
Arguments
315+
---------
316+
* ``<model-name>``: Name of the model to stop.
317+
318+
Example
319+
--------
320+
The following command stops a model::
321+
322+
STOP MODEL my_model;
323+
324+
See Also
325+
--------
326+
* ``START MODEL model_name``
327+
* ``SHOW MODELS``
328+
329+
""" # noqa: E501
330+
331+
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
332+
inference_api = get_inference_api(params)
333+
operation_result = inference_api.stop()
334+
335+
res = FusionSQLResult()
336+
res.add_field('Status', result.STRING)
337+
res.add_field('Message', result.STRING)
338+
res.set_rows([
339+
(
340+
operation_result.status,
341+
operation_result.get_message(),
342+
),
343+
])
344+
345+
return res
346+
347+
348+
StopModelHandler.register(overwrite=True)

singlestoredb/fusion/handlers/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from ...management.files import FilesManager
1313
from ...management.files import FileSpace
1414
from ...management.files import manage_files
15+
from ...management.inference_api import InferenceAPIInfo
16+
from ...management.inference_api import InferenceAPIManager
1517
from ...management.workspace import StarterWorkspace
1618
from ...management.workspace import Workspace
1719
from ...management.workspace import WorkspaceGroup
@@ -322,3 +324,16 @@ def get_file_space(params: Dict[str, Any]) -> FileSpace:
322324
raise ValueError(f'invalid file location: {file_location}')
323325

324326
raise KeyError('no file space was specified')
327+
328+
329+
def get_inference_api_manager() -> InferenceAPIManager:
330+
"""Return the inference API manager for the current project."""
331+
wm = get_workspace_manager()
332+
return wm.organization.inference_apis
333+
334+
335+
def get_inference_api(params: Dict[str, Any]) -> InferenceAPIInfo:
336+
"""Return an inference API based on model name in params."""
337+
inference_apis = get_inference_api_manager()
338+
model_name = params['model_name']
339+
return inference_apis.get(model_name)

singlestoredb/management/inference_api.py

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,93 @@
1010
from singlestoredb.management.manager import Manager
1111

1212

13+
class ModelOperationResult(object):
14+
"""
15+
Result of a model start or stop operation.
16+
17+
Attributes
18+
----------
19+
name : str
20+
Name of the model
21+
status : str
22+
Current status of the model (e.g., 'Active', 'Initializing', 'Suspended')
23+
hosting_platform : str
24+
Hosting platform (e.g., 'Nova', 'Amazon', 'Azure')
25+
"""
26+
27+
def __init__(
28+
self,
29+
name: str,
30+
status: str,
31+
hosting_platform: str,
32+
):
33+
self.name = name
34+
self.status = status
35+
self.hosting_platform = hosting_platform
36+
37+
@classmethod
38+
def from_start_response(cls, response: Dict[str, Any]) -> 'ModelOperationResult':
39+
"""
40+
Create a ModelOperationResult from a start operation response.
41+
42+
Parameters
43+
----------
44+
response : dict
45+
Response from the start endpoint
46+
47+
Returns
48+
-------
49+
ModelOperationResult
50+
51+
"""
52+
return cls(
53+
name=response.get('modelName', ''),
54+
status='Initializing',
55+
hosting_platform=response.get('hostingPlatform', ''),
56+
)
57+
58+
@classmethod
59+
def from_stop_response(cls, response: Dict[str, Any]) -> 'ModelOperationResult':
60+
"""
61+
Create a ModelOperationResult from a stop operation response.
62+
63+
Parameters
64+
----------
65+
response : dict
66+
Response from the stop endpoint
67+
68+
Returns
69+
-------
70+
ModelOperationResult
71+
72+
"""
73+
return cls(
74+
name=response.get('name', ''),
75+
status=response.get('status', 'Suspended'),
76+
hosting_platform=response.get('hostingPlatform', ''),
77+
)
78+
79+
def get_message(self) -> str:
80+
"""
81+
Get a human-readable message about the operation.
82+
83+
Returns
84+
-------
85+
str
86+
Message describing the operation result
87+
88+
"""
89+
return f'Model is {self.status}'
90+
91+
def __str__(self) -> str:
92+
"""Return string representation."""
93+
return vars_to_str(self)
94+
95+
def __repr__(self) -> str:
96+
"""Return string representation."""
97+
return str(self)
98+
99+
13100
class InferenceAPIInfo(object):
14101
"""
15102
Inference API definition.
@@ -24,6 +111,7 @@ class InferenceAPIInfo(object):
24111
connection_url: str
25112
project_id: str
26113
hosting_platform: str
114+
_manager: Optional['InferenceAPIManager']
27115

28116
def __init__(
29117
self,
@@ -33,13 +121,15 @@ def __init__(
33121
connection_url: str,
34122
project_id: str,
35123
hosting_platform: str,
124+
manager: Optional['InferenceAPIManager'] = None,
36125
):
37126
self.service_id = service_id
38127
self.connection_url = connection_url
39128
self.model_name = model_name
40129
self.name = name
41130
self.project_id = project_id
42131
self.hosting_platform = hosting_platform
132+
self._manager = manager
43133

44134
@classmethod
45135
def from_dict(
@@ -77,6 +167,34 @@ def __repr__(self) -> str:
77167
"""Return string representation."""
78168
return str(self)
79169

170+
def start(self) -> ModelOperationResult:
171+
"""
172+
Start this inference API model.
173+
174+
Returns
175+
-------
176+
ModelOperationResult
177+
Result object containing status information about the started model
178+
179+
"""
180+
if self._manager is None:
181+
raise ManagementError(msg='No manager associated with this inference API')
182+
return self._manager.start(self.name)
183+
184+
def stop(self) -> ModelOperationResult:
185+
"""
186+
Stop this inference API model.
187+
188+
Returns
189+
-------
190+
ModelOperationResult
191+
Result object containing status information about the stopped model
192+
193+
"""
194+
if self._manager is None:
195+
raise ManagementError(msg='No manager associated with this inference API')
196+
return self._manager.stop(self.name)
197+
80198

81199
class InferenceAPIManager(object):
82200
"""
@@ -102,4 +220,46 @@ def get(self, model_name: str) -> InferenceAPIInfo:
102220
if self._manager is None:
103221
raise ManagementError(msg='Manager not initialized')
104222
res = self._manager._get(f'inferenceapis/{self.project_id}/{model_name}').json()
105-
return InferenceAPIInfo.from_dict(res)
223+
inference_api = InferenceAPIInfo.from_dict(res)
224+
inference_api._manager = self # Associate the manager
225+
return inference_api
226+
227+
def start(self, model_name: str) -> ModelOperationResult:
228+
"""
229+
Start an inference API model.
230+
231+
Parameters
232+
----------
233+
model_name : str
234+
Name of the model to start
235+
236+
Returns
237+
-------
238+
ModelOperationResult
239+
Result object containing status information about the started model
240+
241+
"""
242+
if self._manager is None:
243+
raise ManagementError(msg='Manager not initialized')
244+
res = self._manager._post(f'models/{model_name}/start')
245+
return ModelOperationResult.from_start_response(res.json())
246+
247+
def stop(self, model_name: str) -> ModelOperationResult:
248+
"""
249+
Stop an inference API model.
250+
251+
Parameters
252+
----------
253+
model_name : str
254+
Name of the model to stop
255+
256+
Returns
257+
-------
258+
ModelOperationResult
259+
Result object containing status information about the stopped model
260+
261+
"""
262+
if self._manager is None:
263+
raise ManagementError(msg='Manager not initialized')
264+
res = self._manager._post(f'models/{model_name}/stop')
265+
return ModelOperationResult.from_stop_response(res.json())

0 commit comments

Comments
 (0)