perf: optional orjson JSON codec behind ENABLE_ORJSON (#27583)
Swap the JSON encoder/decoder used across the backend from stdlib json to orjson when ENABLE_ORJSON is set — HTTP request bodies, JSONResponse bodies, upstream provider responses, SSE chunks, and socket.io/Redis payloads. The flag defaults to off, in which case the app uses stdlib json and engineio's codec verbatim, so default behaviour is unchanged. - json_codec exports JSONCodec (stdlib json or the orjson codec) and SOCKETIO_JSON (engineio's codec or the orjson codec); call sites import JSONCodec and stay implementation-agnostic - apply_orjson_http_json() is a no-op when the flag is off, leaving starlette's Request.json / JSONResponse.render untouched - the orjson codec falls back to the stdlib for inputs orjson rejects (non-str dict keys, ints beyond 64 bits, NaN literals) - orjson is imported only when the flag is on - FastAPI(default_response_class=...) is deliberately not used: an explicit default disables the Pydantic direct-to-bytes fast path for response_model routes
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""The app-wide JSON codec, selected by the ``ENABLE_ORJSON`` env var.
|
||||
|
||||
Every module that would otherwise reach for stdlib ``json`` imports ``JSONCodec``
|
||||
from here, so the whole app switches implementation from a single flag. With the
|
||||
flag off these are stdlib ``json`` and engineio's codec verbatim, so the default
|
||||
behaviour is exactly what it was before orjson entered the picture.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json as stdlib_json
|
||||
|
||||
from engineio import json as engineio_json
|
||||
|
||||
from open_webui.env import ENABLE_ORJSON
|
||||
|
||||
if ENABLE_ORJSON:
|
||||
import orjson
|
||||
|
||||
class ORJSONCodec:
|
||||
"""stdlib-``json``-compatible codec backed by orjson.
|
||||
|
||||
Anything orjson rejects (non-str dict keys, ints beyond 64 bits, ``NaN``
|
||||
literals) falls back to engineio's stdlib-based codec, which keeps its
|
||||
oversized-integer guard for untrusted client payloads.
|
||||
"""
|
||||
|
||||
JSONDecodeError = engineio_json.JSONDecodeError
|
||||
|
||||
@staticmethod
|
||||
def dumps(obj, *args, **kwargs):
|
||||
try:
|
||||
return orjson.dumps(obj).decode('utf-8')
|
||||
except (TypeError, ValueError):
|
||||
return engineio_json.dumps(obj, *args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def loads(s, *args, **kwargs):
|
||||
try:
|
||||
return orjson.loads(s)
|
||||
except (TypeError, ValueError):
|
||||
return engineio_json.loads(s, *args, **kwargs)
|
||||
|
||||
# Drop-in for stdlib ``json``: ``JSONCodec.dumps`` / ``JSONCodec.loads``.
|
||||
JSONCodec = ORJSONCodec
|
||||
# Codec handed to the socket.io/engineio managers, which default to their own.
|
||||
SOCKETIO_JSON = ORJSONCodec
|
||||
else:
|
||||
JSONCodec = stdlib_json
|
||||
SOCKETIO_JSON = engineio_json
|
||||
Reference in New Issue
Block a user