|
6 | 6 |
|
7 | 7 | import logging |
8 | 8 | from datetime import datetime, timezone |
9 | | -from typing import Any, Dict, Optional |
| 9 | +from typing import Any, Callable, Dict, Optional |
| 10 | + |
| 11 | +from starlette.responses import Response |
10 | 12 |
|
11 | 13 | from feast.protos.feast.serving.ServingService_pb2 import GetOnlineFeaturesResponse |
12 | 14 | from feast.protos.feast.types.Value_pb2 import Value |
13 | 15 |
|
14 | 16 | logger = logging.getLogger(__name__) |
15 | 17 |
|
| 18 | +try: |
| 19 | + import orjson |
| 20 | + |
| 21 | + def _make_json_response(data: Dict[str, Any]) -> Response: |
| 22 | + return Response( |
| 23 | + content=orjson.dumps(data), |
| 24 | + media_type="application/json", |
| 25 | + ) |
| 26 | + |
| 27 | +except ImportError: |
| 28 | + import json |
| 29 | + |
| 30 | + def _make_json_response(data: Dict[str, Any]) -> Response: # type: ignore[misc] |
| 31 | + return Response( |
| 32 | + content=json.dumps(data).encode(), |
| 33 | + media_type="application/json", |
| 34 | + ) |
| 35 | + |
| 36 | + |
| 37 | +_make_json_response: Callable[[Dict[str, Any]], Response] |
| 38 | + |
16 | 39 | # FieldStatus enum mapping (protos/feast/serving/ServingService.proto) |
17 | 40 | _STATUS_NAMES: Dict[int, str] = { |
18 | 41 | 0: "INVALID", |
|
24 | 47 |
|
25 | 48 |
|
26 | 49 | def convert_response_to_dict(response: GetOnlineFeaturesResponse) -> Dict[str, Any]: |
27 | | - """Convert GetOnlineFeaturesResponse to dict (matches proto_json.patch() format).""" |
| 50 | + """Convert GetOnlineFeaturesResponse to a JSON-serializable dict. |
| 51 | +
|
| 52 | + Matches the structure produced by MessageToDict(proto, preserving_proto_field_name=True) |
| 53 | + with proto_json.patch() applied, with one intentional difference: |
| 54 | +
|
| 55 | + - double_val fields are returned as Python float objects (json.dumps uses Python 3.1+ |
| 56 | + shortest round-trip form, ~15-17 sig digits) rather than 18 fixed significant digits |
| 57 | + (float_precision=18). Values are numerically identical; only the JSON string length |
| 58 | + may differ. This is safe for all ML feature types and avoids unnecessary precision |
| 59 | + overhead. |
| 60 | + """ |
28 | 61 | result: Dict[str, Any] = { |
29 | 62 | "results": [ |
30 | 63 | { |
|
0 commit comments