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.
Files changed (52) hide show
  1. {litserve-0.2.8.dev0/src/litserve.egg-info → litserve-0.2.9.dev0}/PKG-INFO +22 -28
  2. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/README.md +21 -26
  3. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/requirements.txt +0 -1
  4. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__about__.py +1 -1
  5. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/api.py +44 -2
  6. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/connector.py +7 -12
  7. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/base.py +30 -21
  8. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/continuous_batching_loop.py +3 -7
  9. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/loops.py +17 -15
  10. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/simple_loops.py +131 -13
  11. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/streaming_loops.py +6 -10
  12. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/server.py +51 -31
  13. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/openai.py +1 -0
  14. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/openai_embedding.py +5 -2
  15. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/openai_spec_example.py +22 -3
  16. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/base.py +0 -2
  17. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0/src/litserve.egg-info}/PKG-INFO +22 -28
  18. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/requires.txt +0 -1
  19. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/LICENSE +0 -0
  20. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/MANIFEST.in +0 -0
  21. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/setup.cfg +0 -0
  22. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/setup.py +0 -0
  23. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__init__.py +0 -0
  24. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/__main__.py +0 -0
  25. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/__init__.py +0 -0
  26. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/base.py +0 -0
  27. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/defaults/__init__.py +0 -0
  28. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/callbacks/defaults/metric_callback.py +0 -0
  29. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/cli.py +0 -0
  30. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/docker_builder.py +0 -0
  31. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loggers.py +0 -0
  32. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/loops/__init__.py +0 -0
  33. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/middlewares.py +0 -0
  34. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/python_client.py +0 -0
  35. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/schema/__init__.py +0 -0
  36. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/schema/image.py +0 -0
  37. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/__init__.py +0 -0
  38. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/specs/base.py +0 -0
  39. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/__init__.py +0 -0
  40. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/openai_embedding_spec_example.py +0 -0
  41. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/test_examples/simple_example.py +0 -0
  42. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/__init__.py +0 -0
  43. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/factory.py +0 -0
  44. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/process_transport.py +0 -0
  45. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/zmq_queue.py +0 -0
  46. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/transport/zmq_transport.py +0 -0
  47. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve/utils.py +0 -0
  48. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/SOURCES.txt +0 -0
  49. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/dependency_links.txt +0 -0
  50. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/entry_points.txt +0 -0
  51. {litserve-0.2.8.dev0 → litserve-0.2.9.dev0}/src/litserve.egg-info/not-zip-safe +0 -0
  52. {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.8.dev0
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
- # Easily serve AI models Lightning fast
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
- &nbsp;
79
-
80
- <strong>Lightning-fast serving engine for AI models.</strong>
81
- Easy. Flexible. Enterprise-scale.
77
+ &nbsp;
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
- Compound AI ✅ Integrate with vLLM, etc ✅ Serverless  
90
+ Inference pipeline ✅ Integrate with vLLM, etc ✅ Serverless  
97
91
 
98
92
  </pre>
99
93
 
100
94
  <div align='center'>
101
95
 
96
+ [![PyPI Downloads](https://static.pepy.tech/badge/litserve)](https://pepy.tech/projects/litserve)
102
97
  [![Discord](https://img.shields.io/discord/1077906959069626439?label=Get%20help%20on%20Discord)](https://discord.gg/WajDThKAur)
103
98
  ![cpu-tests](https://github.com/Lightning-AI/litserve/actions/workflows/ci-testing.yml/badge.svg)
104
99
  [![codecov](https://codecov.io/gh/Lightning-AI/litserve/graph/badge.svg?token=SmzX8mnKlA)](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 (AI compound system) shows LitServe's flexibility ([see real examples](#examples)):
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 (compound AI system)
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. Build a compound AI system (1+ models), connect DBs, load data, etc...
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
- # Easily build compound systems. Run inference and return the output.
158
- squared = self.model1(x)
159
- cubed = self.model2(x)
160
- output = squared + cubed
161
- return {"output": 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
- Run the auto-generated test client:
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
  &nbsp;
219
209
 
220
210
  # Featured examples
221
- Use LitServe to deploy any model or AI service: (Compound AI, Gen AI, classic ML, embeddings, LLMs, vision, audio, etc...)
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
- To host on [Lightning AI](https://lightning.ai/deploy), simply add the `--cloud` arg, login and choose the cloud of your choice.
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 api server.py --cloud
244
+ lightning serve server.py
253
245
  ```
254
246
 
255
247
  &nbsp;
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
- # Easily serve AI models Lightning fast
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
- &nbsp;
8
-
9
- <strong>Lightning-fast serving engine for AI models.</strong>
10
- Easy. Flexible. Enterprise-scale.
7
+ &nbsp;
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
- Compound AI ✅ Integrate with vLLM, etc ✅ Serverless  
20
+ Inference pipeline ✅ Integrate with vLLM, etc ✅ Serverless  
26
21
 
27
22
  </pre>
28
23
 
29
24
  <div align='center'>
30
25
 
26
+ [![PyPI Downloads](https://static.pepy.tech/badge/litserve)](https://pepy.tech/projects/litserve)
31
27
  [![Discord](https://img.shields.io/discord/1077906959069626439?label=Get%20help%20on%20Discord)](https://discord.gg/WajDThKAur)
32
28
  ![cpu-tests](https://github.com/Lightning-AI/litserve/actions/workflows/ci-testing.yml/badge.svg)
33
29
  [![codecov](https://codecov.io/gh/Lightning-AI/litserve/graph/badge.svg?token=SmzX8mnKlA)](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 (AI compound system) shows LitServe's flexibility ([see real examples](#examples)):
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 (compound AI system)
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. Build a compound AI system (1+ models), connect DBs, load data, etc...
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
- # Easily build compound systems. Run inference and return the output.
87
- squared = self.model1(x)
88
- cubed = self.model2(x)
89
- output = squared + cubed
90
- return {"output": 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
- Run the auto-generated test client:
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
  &nbsp;
148
139
 
149
140
  # Featured examples
150
- Use LitServe to deploy any model or AI service: (Compound AI, Gen AI, classic ML, embeddings, LLMs, vision, audio, etc...)
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
- To host on [Lightning AI](https://lightning.ai/deploy), simply add the `--cloud` arg, login and choose the cloud of your choice.
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 api server.py --cloud
174
+ lightning serve server.py
182
175
  ```
183
176
 
184
177
  &nbsp;
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) |
@@ -1,4 +1,3 @@
1
1
  fastapi >=0.100
2
2
  uvicorn[standard] >=0.29.0
3
3
  pyzmq >=22.0.0
4
- starlette
@@ -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.8.dev0"
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, max_batch_size: int, spec: Optional[LitSpec]):
120
- self.max_batch_size = max_batch_size
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 == "cpu":
26
- self._accelerator = "cpu"
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._auto_device_count(self._accelerator)
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 ["cuda", "mps"]:
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 _auto_device_count(self, accelerator) -> int:
84
- if accelerator == "cuda":
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, request_queue: Queue, max_batch_size: int, batch_timeout: float
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, max_batch_size, batch_timeout, transport)
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 get_batch_requests(self, lit_api: LitAPI, request_queue: Queue, max_batch_size: int, batch_timeout: float):
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
- return (
32
- BatchedStreamingLoop()
33
- if stream and max_batch_size > 1
34
- else StreamingLoop()
35
- if stream
36
- else BatchedLoop()
37
- if max_batch_size > 1
38
- else SingleLoop()
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,