fix: move bypass_system_prompt off query parameter onto request.state (#25156)

bypass_system_prompt is an internal flag used by utils/middleware.py and utils/chat.py to skip applying the model system prompt on recursive base-model calls, but it was still declared as a positional argument on the openai/ollama chat-completion route handlers, so FastAPI bound it from the query string. Move it to request.state so external clients cannot set it, matching how bypass_filter is handled.

Drop the argument from both route signatures and read getattr(request.state, 'bypass_system_prompt', False); utils/chat.py sets request.state.bypass_system_prompt alongside bypass_filter and drops the kwarg from the two route-handler calls (the recursive self-calls keep it). Mirrors c0385f60b.

Co-authored-by: anishgirianish <161533316+anishgirianish@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Classic298 2026-05-31 23:53:53 +02:00 committed by GitHub
parent f2650353da
commit 4719881105
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 14 additions and 12 deletions

View file

@ -974,7 +974,6 @@ async def generate_chat_completion(
form_data: dict,
url_idx: int | None = None,
user=Depends(get_verified_user), # noqa: B008
bypass_system_prompt: bool = False,
):
"""Forward a chat completion request to an Ollama backend."""
if not request.app.state.config.ENABLE_OLLAMA_API:
@ -985,12 +984,14 @@ async def generate_chat_completion(
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
# which would exhaust the connection pool under concurrent load.
# bypass_filter is read from request.state to prevent external clients from
# setting it via query parameter (CVE fix). Only internal server-side callers
# (e.g. utils/chat.py) should set request.state.bypass_filter = True.
# bypass_filter and bypass_system_prompt are read from request.state to prevent
# external clients from setting them via query parameter. Only internal
# server-side callers (e.g. utils/chat.py) should set
# request.state.bypass_filter / request.state.bypass_system_prompt = True.
bypass_filter = getattr(request.state, 'bypass_filter', False)
if BYPASS_MODEL_ACCESS_CONTROL:
bypass_filter = True
bypass_system_prompt = getattr(request.state, 'bypass_system_prompt', False)
metadata = form_data.pop('metadata', None)
try:

View file

@ -1045,19 +1045,20 @@ async def generate_chat_completion(
request: Request,
form_data: dict,
user=Depends(get_verified_user),
bypass_system_prompt: bool = False,
):
# NOTE: We intentionally do NOT use Depends(get_async_session) here.
# Database operations (get_model_by_id, AccessGrants.has_access) manage their own short-lived sessions.
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
# which would exhaust the connection pool under concurrent load.
# bypass_filter is read from request.state to prevent external clients from
# setting it via query parameter (CVE fix). Only internal server-side callers
# (e.g. utils/chat.py) should set request.state.bypass_filter = True.
# bypass_filter and bypass_system_prompt are read from request.state to prevent
# external clients from setting them via query parameter. Only internal
# server-side callers (e.g. utils/chat.py) should set
# request.state.bypass_filter / request.state.bypass_system_prompt = True.
bypass_filter = getattr(request.state, 'bypass_filter', False)
if BYPASS_MODEL_ACCESS_CONTROL:
bypass_filter = True
bypass_system_prompt = getattr(request.state, 'bypass_system_prompt', False)
idx = 0

View file

@ -160,9 +160,11 @@ async def generate_chat_completion(
if BYPASS_MODEL_ACCESS_CONTROL:
bypass_filter = True
# Propagate bypass_filter via request.state so that downstream route
# handlers (openai/ollama) can read it without exposing it as a query param.
# Propagate bypass_filter and bypass_system_prompt via request.state so that
# downstream route handlers (openai/ollama) can read them without exposing
# them as query parameters.
request.state.bypass_filter = bypass_filter
request.state.bypass_system_prompt = bypass_system_prompt
if hasattr(request.state, 'metadata'):
if 'metadata' not in form_data:
@ -279,7 +281,6 @@ async def generate_chat_completion(
request=request,
form_data=form_data,
user=user,
bypass_system_prompt=bypass_system_prompt,
)
if form_data.get('stream'):
response.headers['content-type'] = 'text/event-stream'
@ -295,7 +296,6 @@ async def generate_chat_completion(
request=request,
form_data=form_data,
user=user,
bypass_system_prompt=bypass_system_prompt,
)