Skip to content

fix(backend): preserve run message pagination boundary#3188

Open
LittleChenLiya wants to merge 5 commits into
bytedance:mainfrom
LittleChenLiya:fix/run-message-pagination-boundary
Open

fix(backend): preserve run message pagination boundary#3188
LittleChenLiya wants to merge 5 commits into
bytedance:mainfrom
LittleChenLiya:fix/run-message-pagination-boundary

Conversation

@LittleChenLiya
Copy link
Copy Markdown
Collaborator

@LittleChenLiya LittleChenLiya commented May 24, 2026

问题原因

run messages 接口为了判断是否还有更多分页,会向 event store 请求 limit + 1 条记录。默认加载和 before_seq 向旧消息分页时,底层返回的是最新一页消息并按升序排列,多出来的一条位于结果开头,表示更早的分页哨兵记录。

当前接口统一使用 rows[:limit] 裁剪,导致默认分页时丢掉结果末尾的最新消息。如果一个 run 有 66 条 message event,默认 limit=50 时第一页会漏掉最后一条,第二页再用 before_seq 也无法取回该消息。

修改内容

  • 新增统一的 run message 分页裁剪逻辑。
  • after_seq 正向分页继续保留前 limit 条。
  • 默认加载和 before_seq 向旧消息分页改为保留后 limit 条,避免丢失最新消息。
  • 同步修复 thread-scoped run messages 和 run-scoped messages 两个接口。
  • 补充分页边界测试,覆盖 limit + 1 哨兵记录不会导致消息缺失。

关联 issue

关联 #3052


Problem Cause

The run messages endpoints request limit + 1 rows from the event store to detect whether more pages exist. For default loading and before_seq pagination toward older messages, the storage layer returns the latest page in ascending order, and the extra row is at the beginning as an earlier-page sentinel.

The endpoints always trimmed with rows[:limit], which dropped the newest message at the end of the result during default pagination. If a run had 66 message events and the default limit=50 was used, the first page missed the last message, and the second page could not recover it with before_seq.

Changes

  • Added unified run message pagination trimming logic.
  • Kept after_seq forward pagination preserving the first limit rows.
  • Changed default loading and before_seq pagination toward older messages to preserve the last limit rows, avoiding loss of newest messages.
  • Applied the fix to both thread-scoped run messages and run-scoped messages endpoints.
  • Added pagination boundary tests covering the limit + 1 sentinel record without message loss.

Related Issue

Related to #3052

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a pagination edge case in the gateway “run messages” APIs where the backend requests limit + 1 rows (to compute has_more) but then trims from the wrong side for “latest page” and before_seq pagination, causing the newest message on a page to be dropped and unrecoverable via subsequent pagination.

Changes:

  • Introduces a shared trimming helper that preserves the correct pagination boundary when limit + 1 rows are returned.
  • Applies the trimming fix to both thread-scoped (/api/threads/{thread_id}/runs/{run_id}/messages) and run-scoped (/api/runs/{run_id}/messages) message endpoints.
  • Adds tests to cover default (latest), before_seq (backward), and after_seq (forward) pagination behavior with a sentinel extra row.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
backend/app/gateway/routers/thread_runs.py Adds trim_run_message_page() and uses it so default/latest and before_seq pagination keep the newest messages while still computing has_more.
backend/app/gateway/routers/runs.py Reuses the same trimming logic for the run-scoped messages endpoint for consistent pagination behavior.
backend/tests/test_thread_run_messages_pagination.py Adds assertions/tests ensuring the sentinel limit+1 row does not cause newest-message loss across default/before/after pagination.
backend/tests/test_runs_api_endpoints.py Mirrors the pagination boundary tests for /api/runs/{run_id}/messages to prevent regressions in the run-scoped endpoint.

@LittleChenLiya LittleChenLiya requested a review from Copilot May 26, 2026 02:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment thread backend/app/gateway/routers/thread_runs.py Outdated
Comment thread backend/app/gateway/routers/runs.py Outdated
Comment thread backend/tests/test_runs_api_endpoints.py
Comment thread backend/tests/test_runs_api_endpoints.py
Comment thread backend/tests/test_runs_api_endpoints.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants