mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-16 04:28:36 +00:00
feat: record the requested model as its own field in the audit log (#32429)
The audit log only showed which model a request used inside the logged request body, which is cut off at MAX_BODY_LOG_SIZE, so the model went missing whenever a long prompt came before it. Audit entries for chat and completion requests, including the OpenAI, Ollama and Anthropic-compatible endpoints, now carry a model_id field with the model the client asked for, at every AUDIT_LOG_LEVEL and however long the request is. Other requests log it as null. Fixes #30097
This commit is contained in:
parent
980225e582
commit
fb3d92a6ef
5 changed files with 17 additions and 0 deletions
|
|
@ -1152,6 +1152,7 @@ async def chat_completion(
|
|||
await get_all_models(request, user=user)
|
||||
|
||||
model_id = form_data.get('model', None)
|
||||
request.state.model_id = model_id
|
||||
model_item = form_data.pop('model_item', {})
|
||||
tasks = form_data.pop('background_tasks', None)
|
||||
|
||||
|
|
@ -2059,6 +2060,7 @@ async def generate_messages(
|
|||
Anthropic's x-api-key header (via middleware translation).
|
||||
"""
|
||||
requested_model = form_data.get('model', '')
|
||||
request.state.model_id = requested_model
|
||||
input_tokens = None
|
||||
try:
|
||||
input_tokens = await openai.count_anthropic_tokens(request, form_data, user)
|
||||
|
|
|
|||
|
|
@ -1033,6 +1033,7 @@ async def generate_completion(
|
|||
if not await Config.get('ollama.enable'):
|
||||
raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED)
|
||||
|
||||
request.state.model_id = form_data.model
|
||||
await check_model_access(user, await Models.get_model_by_id(form_data.model), BYPASS_MODEL_ACCESS_CONTROL)
|
||||
await validate_ollama_backend_idx(request, form_data.model, url_idx, user)
|
||||
|
||||
|
|
@ -1164,6 +1165,8 @@ async def generate_chat_completion(
|
|||
payload.pop('metadata', None)
|
||||
|
||||
model_id = payload['model']
|
||||
if not hasattr(request.state, 'model_id'):
|
||||
request.state.model_id = model_id
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
|
||||
if model_info is not None:
|
||||
|
|
@ -1266,6 +1269,7 @@ async def generate_openai_completion(
|
|||
payload = form_data.model_dump(exclude_none=True, exclude=['metadata'])
|
||||
|
||||
model_id = form_data.model
|
||||
request.state.model_id = model_id
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
if model_info is not None:
|
||||
if model_info.base_model_id:
|
||||
|
|
@ -1370,6 +1374,7 @@ async def generate_openai_chat_completion(
|
|||
payload = form_data.model_dump(exclude_none=True, exclude=['metadata'])
|
||||
|
||||
model_id = form_data.model
|
||||
request.state.model_id = model_id
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
if model_info is not None:
|
||||
if model_info.base_model_id:
|
||||
|
|
@ -1427,6 +1432,7 @@ async def generate_anthropic_messages(
|
|||
|
||||
payload = {**form_data}
|
||||
model_id = payload.get('model', '')
|
||||
request.state.model_id = model_id
|
||||
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
if model_info:
|
||||
|
|
@ -1485,6 +1491,7 @@ async def generate_responses(
|
|||
|
||||
payload = form_data.model_dump()
|
||||
model_id = form_data.model
|
||||
request.state.model_id = model_id
|
||||
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
if model_info:
|
||||
|
|
|
|||
|
|
@ -1434,6 +1434,9 @@ async def generate_chat_completion(
|
|||
metadata = payload.pop('metadata', None)
|
||||
|
||||
model_id = form_data.get('model')
|
||||
# keep the model the client asked for when reached from /api/chat/completions
|
||||
if not hasattr(request.state, 'model_id'):
|
||||
request.state.model_id = model_id
|
||||
model_info = await Models.get_model_by_id(model_id)
|
||||
|
||||
# Check model info and override the payload
|
||||
|
|
@ -1821,6 +1824,7 @@ async def responses(
|
|||
|
||||
idx = 0
|
||||
model_id = form_data.model
|
||||
request.state.model_id = model_id
|
||||
|
||||
# Enforce per-model access control
|
||||
await check_model_access(user, await Models.get_model_by_id(model_id), BYPASS_MODEL_ACCESS_CONTROL)
|
||||
|
|
@ -1944,6 +1948,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
|||
|
||||
idx = 0
|
||||
model_id = payload.get('model') if isinstance(payload, dict) else None
|
||||
request.state.model_id = model_id
|
||||
if model_id:
|
||||
models = request.app.state.OPENAI_MODELS
|
||||
if not models or model_id not in models:
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ class AuditLogEntry:
|
|||
request_uri: str
|
||||
user_agent: Optional[str] = None
|
||||
source_ip: Optional[str] = None
|
||||
model_id: Optional[str] = None
|
||||
# `Request` audit level properties
|
||||
request_object: Any = None
|
||||
# `Request Response` level
|
||||
|
|
@ -309,6 +310,7 @@ class AuditLoggingMiddleware:
|
|||
response_status_code=context.metadata.get('response_status_code', None),
|
||||
source_ip=request.client.host if request.client else None,
|
||||
user_agent=request.headers.get('user-agent'),
|
||||
model_id=getattr(request.state, 'model_id', None),
|
||||
request_object=request_body,
|
||||
response_object=response_body,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ def file_format(record: 'Record'):
|
|||
'response_status_code': record['extra'].get('response_status_code', 0),
|
||||
'source_ip': record['extra'].get('source_ip', ''),
|
||||
'user_agent': record['extra'].get('user_agent', ''),
|
||||
'model_id': record['extra'].get('model_id', ''),
|
||||
'request_object': record['extra'].get('request_object', b''),
|
||||
'response_object': record['extra'].get('response_object', b''),
|
||||
'extra': record['extra'].get('extra', {}),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue