cortexgrid 0.3.17__tar.gz → 0.3.19__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 (31) hide show
  1. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/PKG-INFO +1 -1
  2. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/serve.py +54 -7
  3. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/pyproject.toml +1 -1
  4. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/.gitignore +0 -0
  5. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/LICENSE +0 -0
  6. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/__init__.py +0 -0
  7. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/_bundle.py +0 -0
  8. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/_model_scheduler.py +0 -0
  9. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/_ray_job_driver.py +0 -0
  10. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/_serve_entry.py +0 -0
  11. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/checkpoint.py +0 -0
  12. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/experiment.py +0 -0
  13. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/infra.py +0 -0
  14. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/jobs.py +0 -0
  15. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/mlflow_util.py +0 -0
  16. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/__init__.py +0 -0
  17. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/application_spec.py +0 -0
  18. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/deployment_key.py +0 -0
  19. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/deployment_records.py +0 -0
  20. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/lifecycle.py +0 -0
  21. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/placement.py +0 -0
  22. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/registry_tags.py +0 -0
  23. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/serve_bundle.py +0 -0
  24. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_serving/status.py +0 -0
  25. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/model_storage.py +0 -0
  26. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/py.typed +0 -0
  27. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/ray_util.py +0 -0
  28. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/s3_util.py +0 -0
  29. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/secrets.py +0 -0
  30. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/cortexgrid/state.py +0 -0
  31. {cortexgrid-0.3.17 → cortexgrid-0.3.19}/docs/cortexgrid/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cortexgrid
3
- Version: 0.3.17
3
+ Version: 0.3.19
4
4
  Summary: Connect your ML code to the RoboLab compute cluster — Ray, MLflow, and S3
5
5
  Project-URL: Homepage, https://github.com/robodatalab/cortexgrid
6
6
  Project-URL: Repository, https://github.com/robodatalab/cortexgrid
@@ -8,8 +8,8 @@
8
8
  async def predict(self, xs: list[float]) -> list[float]: ...
9
9
 
10
10
  Unlike `ray.serve.ingress`, it builds the FastAPI app from the class's
11
- `serve.endpoint` methods, and the class is left unwrapped: the FastAPI app
12
- and the client generated for it are only recorded on it, and
11
+ `serve.endpoint` methods, and the class is left unwrapped: the FastAPI app,
12
+ its routes and the client generated for it are only recorded on it, and
13
13
  `cortexgrid._serve_entry.build` applies Ray's ingress when it builds the Serve
14
14
  application on the cluster.
15
15
 
@@ -24,11 +24,13 @@ locatable everywhere else (the laptop, Ray jobs, tests).
24
24
  from __future__ import annotations
25
25
 
26
26
  import inspect
27
+ from collections.abc import AsyncIterator
27
28
  from dataclasses import dataclass
28
- from typing import Any, Callable, TypeVar, get_type_hints
29
+ from typing import Any, Callable, TypeVar, get_args, get_type_hints
29
30
 
30
31
  import httpx
31
32
  from fastapi import FastAPI
33
+ from fastapi.responses import StreamingResponse
32
34
  from pydantic import TypeAdapter
33
35
 
34
36
  from cortexgrid.model_serving.lifecycle import Deployment, DeploymentClient
@@ -57,6 +59,10 @@ def ingress(cls: _T) -> _T:
57
59
  if getattr(method, _ENDPOINT_ATTR, False):
58
60
  marshalling = _EndpointMarshalling.of(method)
59
61
  route = _route_of(method, marshalling)
62
+ route_name = f"__cortexgrid_{name}_route__"
63
+ route.__module__ = cls.__module__
64
+ route.__qualname__ = f"{cls.__qualname__}.{route_name}"
65
+ setattr(cls, route_name, route)
60
66
  app.add_api_route(f"/{name}", route, methods=["POST"])
61
67
  calls[name] = _call_of(name, method, marshalling)
62
68
  client = type(f"{cls.__name__}Client", (_EndpointsClient,), calls)
@@ -88,7 +94,12 @@ class _EndpointMarshalling:
88
94
  parameters_in_order = list(parameter_values)
89
95
  signature_without_self = signature.replace(parameters=parameters_in_order[1:])
90
96
  hints = get_type_hints(method)
91
- answer_hint = hints.pop("return")
97
+ returned_hint = hints.pop("return")
98
+ if inspect.isasyncgenfunction(method):
99
+ streamed_hints = get_args(returned_hint)
100
+ answer_hint = streamed_hints[0]
101
+ else:
102
+ answer_hint = returned_hint
92
103
  answer = TypeAdapter(answer_hint)
93
104
  parameters = {name: TypeAdapter(hint) for name, hint in hints.items()}
94
105
  return cls(signature_without_self, parameters, answer)
@@ -113,11 +124,27 @@ class _EndpointMarshalling:
113
124
  def answer_from_json(self, answered: Any) -> Any:
114
125
  return self.answer.validate_python(answered)
115
126
 
127
+ def answer_to_json_line(self, answer: Any) -> bytes:
128
+ answered = self.answer.dump_json(answer)
129
+ return answered + b"\n"
130
+
131
+ def answer_from_json_line(self, line: str) -> Any:
132
+ return self.answer.validate_json(line)
133
+
116
134
 
117
135
  def _route_of(
118
136
  method: Callable[..., Any], marshalling: _EndpointMarshalling
119
137
  ) -> Callable[..., Any]:
120
- if inspect.iscoroutinefunction(method):
138
+ if inspect.isasyncgenfunction(method):
139
+
140
+ async def route(self: Any, body: dict[str, Any]) -> Any:
141
+ arguments = marshalling.arguments_from_json(body)
142
+ answers = method(self, **arguments)
143
+ lines = _json_lines_of(answers, marshalling)
144
+ streamed = StreamingResponse(lines, media_type="application/x-ndjson")
145
+ return streamed
146
+
147
+ elif inspect.iscoroutinefunction(method):
121
148
 
122
149
  async def route(self: Any, body: dict[str, Any]) -> Any:
123
150
  arguments = marshalling.arguments_from_json(body)
@@ -134,14 +161,34 @@ def _route_of(
134
161
  return answered
135
162
 
136
163
  route.__name__ = method.__name__
137
- route.__qualname__ = method.__qualname__
138
164
  return route
139
165
 
140
166
 
167
+ async def _json_lines_of(
168
+ answers: AsyncIterator[Any], marshalling: _EndpointMarshalling
169
+ ) -> AsyncIterator[bytes]:
170
+ async for answer in answers:
171
+ line = marshalling.answer_to_json_line(answer)
172
+ yield line
173
+
174
+
141
175
  def _call_of(
142
176
  name: str, method: Callable[..., Any], marshalling: _EndpointMarshalling
143
177
  ) -> Callable[..., Any]:
144
- if inspect.iscoroutinefunction(method):
178
+ if inspect.isasyncgenfunction(method):
179
+
180
+ async def call(self: _EndpointsClient, *args: Any, **kwargs: Any) -> Any:
181
+ body = marshalling.arguments_to_json(*args, **kwargs)
182
+ async with httpx.AsyncClient(timeout=None) as client:
183
+ async with client.stream(
184
+ "POST", f"{self.url}/{name}", json=body
185
+ ) as response:
186
+ response.raise_for_status()
187
+ async for line in response.aiter_lines():
188
+ answer = marshalling.answer_from_json_line(line)
189
+ yield answer
190
+
191
+ elif inspect.iscoroutinefunction(method):
145
192
 
146
193
  async def call(self: _EndpointsClient, *args: Any, **kwargs: Any) -> Any:
147
194
  body = marshalling.arguments_to_json(*args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cortexgrid"
3
- version = "0.3.17"
3
+ version = "0.3.19"
4
4
  description = "Connect your ML code to the RoboLab compute cluster — Ray, MLflow, and S3"
5
5
  readme = "docs/cortexgrid/README.md"
6
6
  license = "Apache-2.0"
File without changes
File without changes