litserve 0.2.8.dev0__tar.gz → 0.2.9.dev0__tar.gz
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.
- {litserve-0.2.8.dev0/src/litserve.egg-info → litserve-0.2.9.dev0}/PKG-INFO +22 -28
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/README.md +21 -26
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/requirements.txt +0 -1
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__about__.py +1 -1
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/api.py +44 -2
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/connector.py +7 -12
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/base.py +30 -21
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/continuous_batching_loop.py +3 -7
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/loops.py +17 -15
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/simple_loops.py +131 -13
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/streaming_loops.py +6 -10
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/server.py +51 -31
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/openai.py +1 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/openai_embedding.py +5 -2
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/openai_spec_example.py +22 -3
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/base.py +0 -2
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0/src/litserve.egg-info}/PKG-INFO +22 -28
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/requires.txt +0 -1
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/LICENSE +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/MANIFEST.in +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/setup.cfg +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/setup.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__main__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/base.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/defaults/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/defaults/metric_callback.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/cli.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/docker_builder.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loggers.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/middlewares.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/python_client.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/schema/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/schema/image.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/base.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/openai_embedding_spec_example.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/simple_example.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/__init__.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/factory.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/process_transport.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/zmq_queue.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/zmq_transport.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/utils.py +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/SOURCES.txt +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/dependency_links.txt +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/entry_points.txt +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/not-zip-safe +0 -0
- {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: litserve
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9.dev0
|
|
4
4
|
Summary: Lightweight AI server.
|
|
5
5
|
Home-page: https://github.com/Lightning-AI/litserve
|
|
6
6
|
Download-URL: https://github.com/Lightning-AI/litserve
|
|
@@ -30,7 +30,6 @@ License-File: LICENSE
|
|
|
30
30
|
Requires-Dist: fastapi>=0.100
|
|
31
31
|
Requires-Dist: uvicorn[standard]>=0.29.0
|
|
32
32
|
Requires-Dist: pyzmq>=22.0.0
|
|
33
|
-
Requires-Dist: starlette
|
|
34
33
|
Provides-Extra: test
|
|
35
34
|
Requires-Dist: asgi-lifespan; extra == "test"
|
|
36
35
|
Requires-Dist: coverage[toml]>=7.5.3; extra == "test"
|
|
@@ -71,19 +70,14 @@ Dynamic: summary
|
|
|
71
70
|
|
|
72
71
|
<div align='center'>
|
|
73
72
|
|
|
74
|
-
#
|
|
73
|
+
# Deploy AI models and inference pipelines - ⚡ fast
|
|
75
74
|
|
|
76
75
|
<img alt="Lightning" src="https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/app-2/ls_banner2.png" width="800px" style="max-width: 100%;">
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<strong>Lightning-fast serving engine for AI models.</strong>
|
|
81
|
-
Easy. Flexible. Enterprise-scale.
|
|
77
|
+
|
|
82
78
|
</div>
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
**LitServe** is an easy-to-use, flexible serving engine for AI models built on FastAPI. It augments FastAPI with features like batching, streaming, and GPU autoscaling eliminate the need to rebuild a FastAPI server per model.
|
|
80
|
+
**LitServe** lets you build high-performance AI inference pipelines on top of FastAPI - no boilerplate. Define one or more models, connect vector DBs, stream responses, batch requests, and autoscale on GPUs out of the box.
|
|
87
81
|
|
|
88
82
|
LitServe is at least [2x faster](#performance) than plain FastAPI due to AI-specific multi-worker handling.
|
|
89
83
|
|
|
@@ -93,12 +87,13 @@ LitServe is at least [2x faster](#performance) than plain FastAPI due to AI-spec
|
|
|
93
87
|
✅ (2x)+ faster serving ✅ Easy to use ✅ LLMs, non LLMs and more
|
|
94
88
|
✅ Bring your own model ✅ PyTorch/JAX/TF/... ✅ Built on FastAPI
|
|
95
89
|
✅ GPU autoscaling ✅ Batching, Streaming ✅ Self-host or ⚡️ managed
|
|
96
|
-
✅
|
|
90
|
+
✅ Inference pipeline ✅ Integrate with vLLM, etc ✅ Serverless
|
|
97
91
|
|
|
98
92
|
</pre>
|
|
99
93
|
|
|
100
94
|
<div align='center'>
|
|
101
95
|
|
|
96
|
+
[](https://pepy.tech/projects/litserve)
|
|
102
97
|
[](https://discord.gg/WajDThKAur)
|
|
103
98
|

|
|
104
99
|
[](https://codecov.io/gh/Lightning-AI/litserve)
|
|
@@ -136,16 +131,16 @@ pip install litserve
|
|
|
136
131
|
```
|
|
137
132
|
|
|
138
133
|
### Define a server
|
|
139
|
-
This toy example with 2 models (
|
|
134
|
+
This toy example with 2 models (inference pipeline) shows LitServe's flexibility ([see real examples](#featured-examples)):
|
|
140
135
|
|
|
141
136
|
```python
|
|
142
137
|
# server.py
|
|
143
138
|
import litserve as ls
|
|
144
139
|
|
|
145
|
-
# (STEP 1) - DEFINE THE API (
|
|
140
|
+
# (STEP 1) - DEFINE THE API ("inference" pipeline)
|
|
146
141
|
class SimpleLitAPI(ls.LitAPI):
|
|
147
142
|
def setup(self, device):
|
|
148
|
-
# setup is called once at startup.
|
|
143
|
+
# setup is called once at startup. Defines elements of the pipeline: models, connect DBs, load data, etc...
|
|
149
144
|
self.model1 = lambda x: x**2
|
|
150
145
|
self.model2 = lambda x: x**3
|
|
151
146
|
|
|
@@ -154,11 +149,11 @@ class SimpleLitAPI(ls.LitAPI):
|
|
|
154
149
|
return request["input"]
|
|
155
150
|
|
|
156
151
|
def predict(self, x):
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return {"output":
|
|
152
|
+
# Run the inference pipeline and return the output
|
|
153
|
+
a = self.model1(x)
|
|
154
|
+
b = self.model2(x)
|
|
155
|
+
c = a + b
|
|
156
|
+
return {"output": c}
|
|
162
157
|
|
|
163
158
|
def encode_response(self, output):
|
|
164
159
|
# Convert the model output to a response payload.
|
|
@@ -189,12 +184,7 @@ python server.py
|
|
|
189
184
|
```
|
|
190
185
|
|
|
191
186
|
### Test the server
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
python client.py
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Or use this terminal command:
|
|
187
|
+
Simulate an http request (run this on any terminal):
|
|
198
188
|
```bash
|
|
199
189
|
curl -X POST http://127.0.0.1:8000/predict -H "Content-Type: application/json" -d '{"input": 4.0}'
|
|
200
190
|
```
|
|
@@ -218,7 +208,7 @@ litgpt serve microsoft/phi-2
|
|
|
218
208
|
|
|
219
209
|
|
|
220
210
|
# Featured examples
|
|
221
|
-
|
|
211
|
+
Here are examples of inference pipelines for common model types and use cases.
|
|
222
212
|
|
|
223
213
|
<pre>
|
|
224
214
|
<strong>Toy model:</strong> <a target="_blank" href="#define-a-server">Hello world</a>
|
|
@@ -247,13 +237,17 @@ https://github.com/user-attachments/assets/ff83dab9-0c9f-4453-8dcb-fb9526726344
|
|
|
247
237
|
|
|
248
238
|
Self-hosting is ideal for hackers, students, and DIY developers while fully managed hosting is ideal for enterprise developers needing easy autoscaling, security, release management, and 99.995% uptime and observability.
|
|
249
239
|
|
|
250
|
-
|
|
240
|
+
*Note:* Lightning offers a generous free tier for developers.
|
|
241
|
+
|
|
242
|
+
To host on [Lightning AI](https://lightning.ai/deploy), simply run the command, login and choose the cloud of your choice.
|
|
251
243
|
```bash
|
|
252
|
-
lightning serve
|
|
244
|
+
lightning serve server.py
|
|
253
245
|
```
|
|
254
246
|
|
|
255
247
|
|
|
256
248
|
|
|
249
|
+
## Features
|
|
250
|
+
|
|
257
251
|
<div align='center'>
|
|
258
252
|
|
|
259
253
|
| [Feature](https://lightning.ai/docs/litserve/features) | Self Managed | [Fully Managed on Lightning](https://lightning.ai/deploy) |
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
<div align='center'>
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Deploy AI models and inference pipelines - ⚡ fast
|
|
4
4
|
|
|
5
5
|
<img alt="Lightning" src="https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/app-2/ls_banner2.png" width="800px" style="max-width: 100%;">
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<strong>Lightning-fast serving engine for AI models.</strong>
|
|
10
|
-
Easy. Flexible. Enterprise-scale.
|
|
7
|
+
|
|
11
8
|
</div>
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
**LitServe** is an easy-to-use, flexible serving engine for AI models built on FastAPI. It augments FastAPI with features like batching, streaming, and GPU autoscaling eliminate the need to rebuild a FastAPI server per model.
|
|
10
|
+
**LitServe** lets you build high-performance AI inference pipelines on top of FastAPI - no boilerplate. Define one or more models, connect vector DBs, stream responses, batch requests, and autoscale on GPUs out of the box.
|
|
16
11
|
|
|
17
12
|
LitServe is at least [2x faster](#performance) than plain FastAPI due to AI-specific multi-worker handling.
|
|
18
13
|
|
|
@@ -22,12 +17,13 @@ LitServe is at least [2x faster](#performance) than plain FastAPI due to AI-spec
|
|
|
22
17
|
✅ (2x)+ faster serving ✅ Easy to use ✅ LLMs, non LLMs and more
|
|
23
18
|
✅ Bring your own model ✅ PyTorch/JAX/TF/... ✅ Built on FastAPI
|
|
24
19
|
✅ GPU autoscaling ✅ Batching, Streaming ✅ Self-host or ⚡️ managed
|
|
25
|
-
✅
|
|
20
|
+
✅ Inference pipeline ✅ Integrate with vLLM, etc ✅ Serverless
|
|
26
21
|
|
|
27
22
|
</pre>
|
|
28
23
|
|
|
29
24
|
<div align='center'>
|
|
30
25
|
|
|
26
|
+
[](https://pepy.tech/projects/litserve)
|
|
31
27
|
[](https://discord.gg/WajDThKAur)
|
|
32
28
|

|
|
33
29
|
[](https://codecov.io/gh/Lightning-AI/litserve)
|
|
@@ -65,16 +61,16 @@ pip install litserve
|
|
|
65
61
|
```
|
|
66
62
|
|
|
67
63
|
### Define a server
|
|
68
|
-
This toy example with 2 models (
|
|
64
|
+
This toy example with 2 models (inference pipeline) shows LitServe's flexibility ([see real examples](#featured-examples)):
|
|
69
65
|
|
|
70
66
|
```python
|
|
71
67
|
# server.py
|
|
72
68
|
import litserve as ls
|
|
73
69
|
|
|
74
|
-
# (STEP 1) - DEFINE THE API (
|
|
70
|
+
# (STEP 1) - DEFINE THE API ("inference" pipeline)
|
|
75
71
|
class SimpleLitAPI(ls.LitAPI):
|
|
76
72
|
def setup(self, device):
|
|
77
|
-
# setup is called once at startup.
|
|
73
|
+
# setup is called once at startup. Defines elements of the pipeline: models, connect DBs, load data, etc...
|
|
78
74
|
self.model1 = lambda x: x**2
|
|
79
75
|
self.model2 = lambda x: x**3
|
|
80
76
|
|
|
@@ -83,11 +79,11 @@ class SimpleLitAPI(ls.LitAPI):
|
|
|
83
79
|
return request["input"]
|
|
84
80
|
|
|
85
81
|
def predict(self, x):
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return {"output":
|
|
82
|
+
# Run the inference pipeline and return the output
|
|
83
|
+
a = self.model1(x)
|
|
84
|
+
b = self.model2(x)
|
|
85
|
+
c = a + b
|
|
86
|
+
return {"output": c}
|
|
91
87
|
|
|
92
88
|
def encode_response(self, output):
|
|
93
89
|
# Convert the model output to a response payload.
|
|
@@ -118,12 +114,7 @@ python server.py
|
|
|
118
114
|
```
|
|
119
115
|
|
|
120
116
|
### Test the server
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
python client.py
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Or use this terminal command:
|
|
117
|
+
Simulate an http request (run this on any terminal):
|
|
127
118
|
```bash
|
|
128
119
|
curl -X POST http://127.0.0.1:8000/predict -H "Content-Type: application/json" -d '{"input": 4.0}'
|
|
129
120
|
```
|
|
@@ -147,7 +138,7 @@ litgpt serve microsoft/phi-2
|
|
|
147
138
|
|
|
148
139
|
|
|
149
140
|
# Featured examples
|
|
150
|
-
|
|
141
|
+
Here are examples of inference pipelines for common model types and use cases.
|
|
151
142
|
|
|
152
143
|
<pre>
|
|
153
144
|
<strong>Toy model:</strong> <a target="_blank" href="#define-a-server">Hello world</a>
|
|
@@ -176,13 +167,17 @@ https://github.com/user-attachments/assets/ff83dab9-0c9f-4453-8dcb-fb9526726344
|
|
|
176
167
|
|
|
177
168
|
Self-hosting is ideal for hackers, students, and DIY developers while fully managed hosting is ideal for enterprise developers needing easy autoscaling, security, release management, and 99.995% uptime and observability.
|
|
178
169
|
|
|
179
|
-
|
|
170
|
+
*Note:* Lightning offers a generous free tier for developers.
|
|
171
|
+
|
|
172
|
+
To host on [Lightning AI](https://lightning.ai/deploy), simply run the command, login and choose the cloud of your choice.
|
|
180
173
|
```bash
|
|
181
|
-
lightning serve
|
|
174
|
+
lightning serve server.py
|
|
182
175
|
```
|
|
183
176
|
|
|
184
177
|
|
|
185
178
|
|
|
179
|
+
## Features
|
|
180
|
+
|
|
186
181
|
<div align='center'>
|
|
187
182
|
|
|
188
183
|
| [Feature](https://lightning.ai/docs/litserve/features) | Self Managed | [Fully Managed on Lightning](https://lightning.ai/deploy) |
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
__version__ = "0.2.
|
|
14
|
+
__version__ = "0.2.9.dev0"
|
|
15
15
|
__author__ = "Lightning-AI et al."
|
|
16
16
|
__author_email__ = "community@lightning.ai"
|
|
17
17
|
__license__ = "Apache-2.0"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
import asyncio
|
|
14
15
|
import json
|
|
15
16
|
import warnings
|
|
16
17
|
from abc import ABC, abstractmethod
|
|
@@ -30,6 +31,45 @@ class LitAPI(ABC):
|
|
|
30
31
|
_logger_queue: Optional[Queue] = None
|
|
31
32
|
request_timeout: Optional[float] = None
|
|
32
33
|
|
|
34
|
+
def __init__(self, max_batch_size: int = 1, batch_timeout: float = 0.0, enable_async: bool = False):
|
|
35
|
+
"""Initialize a LitAPI instance.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
max_batch_size: Maximum number of requests to process in a batch.
|
|
39
|
+
batch_timeout: Maximum time to wait for a batch to fill before processing.
|
|
40
|
+
enable_async: Enable async support.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
if max_batch_size <= 0:
|
|
45
|
+
raise ValueError("max_batch_size must be greater than 0")
|
|
46
|
+
|
|
47
|
+
if batch_timeout < 0:
|
|
48
|
+
raise ValueError("batch_timeout must be greater than or equal to 0")
|
|
49
|
+
self.max_batch_size = max_batch_size
|
|
50
|
+
self.batch_timeout = batch_timeout
|
|
51
|
+
self.enable_async = enable_async
|
|
52
|
+
self._validate_async_methods()
|
|
53
|
+
|
|
54
|
+
def _validate_async_methods(self):
|
|
55
|
+
"""Validate that async methods are properly implemented when enable_async is True."""
|
|
56
|
+
if self.enable_async:
|
|
57
|
+
# check if LitAPI methods are coroutines
|
|
58
|
+
for method in ["decode_request", "predict", "encode_response"]:
|
|
59
|
+
if not asyncio.iscoroutinefunction(getattr(self, method)):
|
|
60
|
+
raise ValueError("""LitAPI(enable_async=True) requires all methods to be coroutines.
|
|
61
|
+
|
|
62
|
+
Please either set enable_async=False or implement the following methods as coroutines:
|
|
63
|
+
Example:
|
|
64
|
+
class MyLitAPI(LitAPI):
|
|
65
|
+
async def decode_request(self, request, **kwargs):
|
|
66
|
+
return request
|
|
67
|
+
async def predict(self, x, **kwargs):
|
|
68
|
+
return x
|
|
69
|
+
async def encode_response(self, output, **kwargs):
|
|
70
|
+
return output
|
|
71
|
+
""")
|
|
72
|
+
|
|
33
73
|
@abstractmethod
|
|
34
74
|
def setup(self, device):
|
|
35
75
|
"""Setup the model so it can be called in `predict`."""
|
|
@@ -116,8 +156,10 @@ class LitAPI(ABC):
|
|
|
116
156
|
def device(self, value):
|
|
117
157
|
self._device = value
|
|
118
158
|
|
|
119
|
-
def pre_setup(self,
|
|
120
|
-
self.
|
|
159
|
+
def pre_setup(self, spec: Optional[LitSpec]):
|
|
160
|
+
if self.batch_timeout > self.request_timeout and self.request_timeout not in (False, -1):
|
|
161
|
+
raise ValueError("batch_timeout must be less than request_timeout")
|
|
162
|
+
|
|
121
163
|
if self.stream:
|
|
122
164
|
self._default_unbatch = self._unbatch_stream
|
|
123
165
|
else:
|
|
@@ -22,20 +22,15 @@ from typing import List, Optional, Union
|
|
|
22
22
|
class _Connector:
|
|
23
23
|
def __init__(self, accelerator: str = "auto", devices: Union[List[int], int, str] = "auto"):
|
|
24
24
|
accelerator = self._sanitize_accelerator(accelerator)
|
|
25
|
-
if accelerator
|
|
26
|
-
self._accelerator =
|
|
27
|
-
elif accelerator == "cuda":
|
|
28
|
-
self._accelerator = "cuda"
|
|
29
|
-
elif accelerator == "mps":
|
|
30
|
-
self._accelerator = "mps"
|
|
31
|
-
|
|
25
|
+
if accelerator in ("cpu", "cuda", "mps"):
|
|
26
|
+
self._accelerator = accelerator
|
|
32
27
|
elif accelerator == "auto":
|
|
33
28
|
self._accelerator = self._choose_auto_accelerator()
|
|
34
29
|
elif accelerator == "gpu":
|
|
35
30
|
self._accelerator = self._choose_gpu_accelerator_backend()
|
|
36
31
|
|
|
37
32
|
if devices == "auto":
|
|
38
|
-
self._devices = self.
|
|
33
|
+
self._devices = self._accelerator_device_count()
|
|
39
34
|
else:
|
|
40
35
|
self._devices = devices
|
|
41
36
|
|
|
@@ -43,7 +38,7 @@ class _Connector:
|
|
|
43
38
|
|
|
44
39
|
def check_devices_and_accelerators(self):
|
|
45
40
|
"""Check if the devices are in a valid fomra and raise an error if they are not."""
|
|
46
|
-
if self._accelerator in
|
|
41
|
+
if self._accelerator in ("cuda", "mps"):
|
|
47
42
|
if not isinstance(self._devices, int) and not (
|
|
48
43
|
isinstance(self._devices, list) and all(isinstance(device, int) for device in self._devices)
|
|
49
44
|
):
|
|
@@ -68,7 +63,7 @@ class _Connector:
|
|
|
68
63
|
accelerator = accelerator.lower()
|
|
69
64
|
|
|
70
65
|
if accelerator not in ["auto", "cpu", "mps", "cuda", "gpu", None]:
|
|
71
|
-
raise ValueError("accelerator must be one of 'auto', 'cpu', 'mps', 'cuda', or 'gpu'")
|
|
66
|
+
raise ValueError(f"accelerator must be one of 'auto', 'cpu', 'mps', 'cuda', or 'gpu'. Found: {accelerator}")
|
|
72
67
|
|
|
73
68
|
if accelerator is None:
|
|
74
69
|
return "auto"
|
|
@@ -80,8 +75,8 @@ class _Connector:
|
|
|
80
75
|
return gpu_backend
|
|
81
76
|
return "cpu"
|
|
82
77
|
|
|
83
|
-
def
|
|
84
|
-
if
|
|
78
|
+
def _accelerator_device_count(self) -> int:
|
|
79
|
+
if self._accelerator == "cuda":
|
|
85
80
|
return check_cuda_with_nvidia_smi()
|
|
86
81
|
return 1
|
|
87
82
|
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
import asyncio
|
|
15
15
|
import inspect
|
|
16
16
|
import logging
|
|
17
|
+
import os
|
|
17
18
|
import pickle
|
|
19
|
+
import signal
|
|
18
20
|
import sys
|
|
19
21
|
import time
|
|
20
22
|
from abc import ABC
|
|
@@ -43,17 +45,25 @@ def _inject_context(context: Union[List[dict], dict], func, *args, **kwargs):
|
|
|
43
45
|
return func(*args, **kwargs)
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
async def _async_inject_context(context: Union[List[dict], dict], func, *args, **kwargs):
|
|
49
|
+
sig = inspect.signature(func)
|
|
50
|
+
if "context" in sig.parameters:
|
|
51
|
+
return await func(*args, **kwargs, context=context)
|
|
52
|
+
return await func(*args, **kwargs)
|
|
53
|
+
|
|
54
|
+
|
|
46
55
|
def collate_requests(
|
|
47
|
-
lit_api: LitAPI,
|
|
56
|
+
lit_api: LitAPI,
|
|
57
|
+
request_queue: Queue,
|
|
48
58
|
) -> Tuple[List, List]:
|
|
49
59
|
payloads = []
|
|
50
60
|
timed_out_uids = []
|
|
51
61
|
entered_at = time.monotonic()
|
|
52
|
-
end_time = entered_at + batch_timeout
|
|
62
|
+
end_time = entered_at + lit_api.batch_timeout
|
|
53
63
|
apply_timeout = lit_api.request_timeout not in (-1, False)
|
|
54
64
|
|
|
55
|
-
if batch_timeout == 0:
|
|
56
|
-
while len(payloads) < max_batch_size:
|
|
65
|
+
if lit_api.batch_timeout == 0:
|
|
66
|
+
while len(payloads) < lit_api.max_batch_size:
|
|
57
67
|
try:
|
|
58
68
|
response_queue_id, uid, timestamp, x_enc = request_queue.get_nowait()
|
|
59
69
|
if apply_timeout and time.monotonic() - timestamp > lit_api.request_timeout:
|
|
@@ -64,7 +74,7 @@ def collate_requests(
|
|
|
64
74
|
break
|
|
65
75
|
return payloads, timed_out_uids
|
|
66
76
|
|
|
67
|
-
while time.monotonic() < end_time and len(payloads) < max_batch_size:
|
|
77
|
+
while time.monotonic() < end_time and len(payloads) < lit_api.max_batch_size:
|
|
68
78
|
remaining_time = end_time - time.monotonic()
|
|
69
79
|
if remaining_time <= 0:
|
|
70
80
|
break
|
|
@@ -111,8 +121,6 @@ class _BaseLoop(ABC):
|
|
|
111
121
|
worker_id: int,
|
|
112
122
|
request_queue: Queue,
|
|
113
123
|
response_queues: List[Queue],
|
|
114
|
-
max_batch_size: int,
|
|
115
|
-
batch_timeout: float,
|
|
116
124
|
stream: bool,
|
|
117
125
|
workers_setup_status: Dict[int, str],
|
|
118
126
|
callback_runner: CallbackRunner,
|
|
@@ -140,8 +148,6 @@ class _BaseLoop(ABC):
|
|
|
140
148
|
lit_api: LitAPI,
|
|
141
149
|
lit_spec: Optional[LitSpec],
|
|
142
150
|
request_queue: Queue,
|
|
143
|
-
max_batch_size: int,
|
|
144
|
-
batch_timeout: float,
|
|
145
151
|
response_queues: List[Queue],
|
|
146
152
|
):
|
|
147
153
|
pass
|
|
@@ -154,8 +160,6 @@ class _BaseLoop(ABC):
|
|
|
154
160
|
worker_id: int,
|
|
155
161
|
request_queue: Queue,
|
|
156
162
|
transport: MessageTransport,
|
|
157
|
-
max_batch_size: int,
|
|
158
|
-
batch_timeout: float,
|
|
159
163
|
stream: bool,
|
|
160
164
|
workers_setup_status: Dict[int, str],
|
|
161
165
|
callback_runner: CallbackRunner,
|
|
@@ -165,7 +169,7 @@ class _BaseLoop(ABC):
|
|
|
165
169
|
|
|
166
170
|
async def _wrapper():
|
|
167
171
|
logger.info("Running LitLoop in a asyncio event loop")
|
|
168
|
-
future = self.schedule_task(lit_api, lit_spec, request_queue,
|
|
172
|
+
future = self.schedule_task(lit_api, lit_spec, request_queue, transport)
|
|
169
173
|
_ = event_loop.create_task(future)
|
|
170
174
|
while True:
|
|
171
175
|
try:
|
|
@@ -176,8 +180,6 @@ class _BaseLoop(ABC):
|
|
|
176
180
|
worker_id,
|
|
177
181
|
request_queue,
|
|
178
182
|
transport,
|
|
179
|
-
max_batch_size,
|
|
180
|
-
batch_timeout,
|
|
181
183
|
stream,
|
|
182
184
|
workers_setup_status,
|
|
183
185
|
callback_runner,
|
|
@@ -196,8 +198,6 @@ class _BaseLoop(ABC):
|
|
|
196
198
|
worker_id,
|
|
197
199
|
request_queue,
|
|
198
200
|
transport,
|
|
199
|
-
max_batch_size,
|
|
200
|
-
batch_timeout,
|
|
201
201
|
stream,
|
|
202
202
|
workers_setup_status,
|
|
203
203
|
callback_runner,
|
|
@@ -211,8 +211,6 @@ class _BaseLoop(ABC):
|
|
|
211
211
|
worker_id: int,
|
|
212
212
|
request_queue: Queue,
|
|
213
213
|
transport: MessageTransport,
|
|
214
|
-
max_batch_size: int,
|
|
215
|
-
batch_timeout: float,
|
|
216
214
|
stream: bool,
|
|
217
215
|
workers_setup_status: Dict[int, str],
|
|
218
216
|
callback_runner: CallbackRunner,
|
|
@@ -223,13 +221,24 @@ class _BaseLoop(ABC):
|
|
|
223
221
|
class LitLoop(_BaseLoop):
|
|
224
222
|
def __init__(self):
|
|
225
223
|
self._context = {}
|
|
224
|
+
self._server_pid = os.getpid()
|
|
226
225
|
|
|
227
|
-
def
|
|
226
|
+
def kill(self):
|
|
227
|
+
try:
|
|
228
|
+
print(f"Stop Server Requested - Kill parent pid [{self._server_pid}] from [{os.getpid()}]")
|
|
229
|
+
os.kill(self._server_pid, signal.SIGTERM)
|
|
230
|
+
except PermissionError:
|
|
231
|
+
# Access Denied because pid already killed...
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
def get_batch_requests(
|
|
235
|
+
self,
|
|
236
|
+
lit_api: LitAPI,
|
|
237
|
+
request_queue: Queue,
|
|
238
|
+
):
|
|
228
239
|
batches, timed_out_uids = collate_requests(
|
|
229
240
|
lit_api,
|
|
230
241
|
request_queue,
|
|
231
|
-
max_batch_size,
|
|
232
|
-
batch_timeout,
|
|
233
242
|
)
|
|
234
243
|
return batches, timed_out_uids
|
|
235
244
|
|
|
@@ -120,9 +120,9 @@ requires the lit_api to have a has_finished method. Please implement the has_fin
|
|
|
120
120
|
lit_api: LitAPI,
|
|
121
121
|
lit_spec: Optional[LitSpec],
|
|
122
122
|
request_queue: Queue,
|
|
123
|
+
response_queues: List[Queue] = None,
|
|
123
124
|
max_batch_size: Optional[int] = None,
|
|
124
125
|
batch_timeout: Optional[float] = None,
|
|
125
|
-
response_queues: List[Queue] = None,
|
|
126
126
|
) -> List[Tuple[str, Any]]:
|
|
127
127
|
"""Fill available capacity with pending and new requests."""
|
|
128
128
|
# First process existing pending requests
|
|
@@ -151,8 +151,6 @@ requires the lit_api to have a has_finished method. Please implement the has_fin
|
|
|
151
151
|
lit_api: LitAPI,
|
|
152
152
|
lit_spec: Optional[LitSpec],
|
|
153
153
|
request_queue: Queue,
|
|
154
|
-
max_batch_size: int,
|
|
155
|
-
batch_timeout: float,
|
|
156
154
|
response_queues: List[Queue],
|
|
157
155
|
):
|
|
158
156
|
logger.info("Running prefill in background")
|
|
@@ -164,9 +162,9 @@ requires the lit_api to have a has_finished method. Please implement the has_fin
|
|
|
164
162
|
lit_api,
|
|
165
163
|
lit_spec,
|
|
166
164
|
request_queue,
|
|
167
|
-
max_batch_size,
|
|
168
|
-
batch_timeout,
|
|
169
165
|
response_queues,
|
|
166
|
+
max_batch_size=lit_api.max_batch_size,
|
|
167
|
+
batch_timeout=lit_api.batch_timeout,
|
|
170
168
|
)
|
|
171
169
|
await asyncio.sleep(0)
|
|
172
170
|
except Exception as e:
|
|
@@ -187,8 +185,6 @@ requires the lit_api to have a has_finished method. Please implement the has_fin
|
|
|
187
185
|
worker_id: int,
|
|
188
186
|
request_queue: Queue,
|
|
189
187
|
transport: MessageTransport,
|
|
190
|
-
max_batch_size: int,
|
|
191
|
-
batch_timeout: float,
|
|
192
188
|
stream: bool,
|
|
193
189
|
workers_setup_status: Dict[int, str],
|
|
194
190
|
callback_runner: CallbackRunner,
|
|
@@ -27,16 +27,22 @@ from litserve.utils import WorkerSetupStatus
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def get_default_loop(stream: bool, max_batch_size: int) -> _BaseLoop:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
def get_default_loop(stream: bool, max_batch_size: int, enable_async: bool = False) -> _BaseLoop:
|
|
31
|
+
if enable_async:
|
|
32
|
+
if stream:
|
|
33
|
+
raise ValueError("Async streaming is not supported. Please use enable_async=False with streaming.")
|
|
34
|
+
if max_batch_size > 1:
|
|
35
|
+
raise ValueError("Async batching is not supported. Please use enable_async=False with batching.")
|
|
36
|
+
return SingleLoop() # Only SingleLoop supports async currently
|
|
37
|
+
|
|
38
|
+
if stream:
|
|
39
|
+
if max_batch_size > 1:
|
|
40
|
+
return BatchedStreamingLoop()
|
|
41
|
+
return StreamingLoop()
|
|
42
|
+
|
|
43
|
+
if max_batch_size > 1:
|
|
44
|
+
return BatchedLoop()
|
|
45
|
+
return SingleLoop()
|
|
40
46
|
|
|
41
47
|
|
|
42
48
|
def inference_worker(
|
|
@@ -46,8 +52,6 @@ def inference_worker(
|
|
|
46
52
|
worker_id: int,
|
|
47
53
|
request_queue: Queue,
|
|
48
54
|
transport: MessageTransport,
|
|
49
|
-
max_batch_size: int,
|
|
50
|
-
batch_timeout: float,
|
|
51
55
|
stream: bool,
|
|
52
56
|
workers_setup_status: Dict[int, str],
|
|
53
57
|
callback_runner: CallbackRunner,
|
|
@@ -72,7 +76,7 @@ def inference_worker(
|
|
|
72
76
|
logging.info(f"LitServe will use {lit_spec.__class__.__name__} spec")
|
|
73
77
|
|
|
74
78
|
if loop == "auto":
|
|
75
|
-
loop = get_default_loop(stream, max_batch_size)
|
|
79
|
+
loop = get_default_loop(stream, lit_api.max_batch_size, lit_api.enable_async)
|
|
76
80
|
|
|
77
81
|
loop(
|
|
78
82
|
lit_api,
|
|
@@ -81,8 +85,6 @@ def inference_worker(
|
|
|
81
85
|
worker_id,
|
|
82
86
|
request_queue,
|
|
83
87
|
transport,
|
|
84
|
-
max_batch_size,
|
|
85
|
-
batch_timeout,
|
|
86
88
|
stream,
|
|
87
89
|
workers_setup_status,
|
|
88
90
|
callback_runner,
|