Fix multi-file uploads tripping the per-IP concurrency guard (#1346) (#1362)

* Stop multi-file uploads from tripping the per-IP concurrency guard

The /api/upload concurrency check summed its condition over `files`, but the
condition didn't reference the loop variable — so it collapsed to len(files)
whenever the IP had any recent upload. A single multi-file batch sent right
after another upload therefore counted itself as N concurrent uploads and hit
max_concurrent_uploads (3), returning 429. The browser swallows the 429 (no
`files` in the body) and sends the chat with no attachments, so the model
"doesn't even see" them (issue #1346).

Count genuine recent upload events instead, via a pure count_recent_uploads()
helper, independent of the current batch's file count. save_upload still
enforces the per-minute sliding-window rate limit per file, so throttling is
preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Also reconcile the per-minute upload rate limit with the batch cap

Follow-up within #1346: even after the concurrency-guard fix, a 6+ file batch
still failed because save_upload() counts each file against upload_rate_limit
(was 5/min) while the composer allows MAX_FILES=10 per batch — the reporter saw
"5 attachments work, 6 fail". Raise the per-minute file cap to 60 so a single
full batch (and a few of them) isn't self-rejected; burst abuse stays bounded by
max_concurrent_uploads. Add a real 6-file regression + a config guard that the
cap exceeds the frontend MAX_FILES.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lekt8
2026-06-03 03:04:19 +08:00
committed by GitHub
parent fd37ccebae
commit 0ec8415f0e
3 changed files with 196 additions and 9 deletions
+12 -8
View File
@@ -8,6 +8,7 @@ from typing import List
import logging
from core.middleware import require_admin
from src.auth_helpers import get_current_user
from src.upload_handler import count_recent_uploads
logger = logging.getLogger(__name__)
@@ -24,15 +25,18 @@ def setup_upload_routes(upload_handler):
client_ip = request.client.host if request.client else "unknown"
out = []
# Limit concurrent uploads per IP
ip_upload_count = sum(
1 for f in files
if client_ip in upload_handler.upload_rate_log and
any(now > time.time() - 10 for now in upload_handler.upload_rate_log[client_ip][-len(files):])
# Limit concurrent uploads per IP. Count genuine recent upload events —
# NOT the number of files in this batch. The previous check summed over
# `files`, so a single multi-file request counted itself as N concurrent
# uploads and tripped the limit (issue #1346: "attach more than one file
# → the model doesn't even see them"). save_upload still enforces the
# per-minute sliding-window rate limit per file.
recent_uploads = count_recent_uploads(
upload_handler.upload_rate_log.get(client_ip, []), time.time()
)
if ip_upload_count >= upload_handler.max_concurrent_uploads:
if recent_uploads >= upload_handler.max_concurrent_uploads:
raise HTTPException(
status_code=429,
detail=f"Maximum concurrent uploads ({upload_handler.max_concurrent_uploads}) exceeded"