Skip to content

fix(ui/log-drawer): paginate session trace list to surface logs beyond the first 50#28225

Closed
shenshouer wants to merge 10 commits into
BerriAI:shin_agent_oss_staging_05_21_2026from
shenshouer:feat/log-drawer-session-pagination
Closed

fix(ui/log-drawer): paginate session trace list to surface logs beyond the first 50#28225
shenshouer wants to merge 10 commits into
BerriAI:shin_agent_oss_staging_05_21_2026from
shenshouer:feat/log-drawer-session-pagination

Conversation

@shenshouer

@shenshouer shenshouer commented May 19, 2026

Copy link
Copy Markdown

Relevant issues

Fixes #28224

Pre-Submission checklist

  • I have Added testing in the tests/test_litellm/ directory — see note below; tests live next to the changed files in ui/litellm-dashboard/... per existing convention.
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • My PR passes all unit tests on make test-unit — UI-only PR; ran npx vitest run src/components/view_logs/LogDetailsDrawer/109 specs pass (Python make test-unit is not applicable to a frontend-only change).
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 — Greptile gave 5/5 (see review comment dated 2026-05-19T07:10Z); both initially flagged P1/P2 issues addressed in follow-up commits.

Note on test placement: tests/test_litellm/ is for the Python SDK / proxy. The LiteLLM dashboard already follows the convention of co-locating *.test.tsx next to source files (e.g. view_logs/time_cell.test.tsx, view_logs/columns neighbors, the existing tests inside LogDetailsDrawer/). I added a vitest spec under the same path to match.

Type

🐛 Bug Fix

Changes

When the Log Details drawer opens in session mode, the left "Session" sidebar fetches /spend/logs/session/ui to enumerate every spend log for that session. The frontend ignored the endpoint's pagination params, so on sessions with more than 50 entries the sidebar silently truncated to the earliest 50 (ordered by startTime ASC) — see #28224 for the reproduction.

This PR threads the existing backend pagination (page, page_size, total_pages) through the UI:

  • ui/litellm-dashboard/src/components/networking.tsxsessionSpendLogsCall now takes page / page_size and forwards them in the query string. Defaults preserve the previous behavior.
  • view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx — adds sessionPage state, includes it in the React Query key so the page is keyed independently, and reads total_pages from the response. goToPreviousPage / goToNextPage clear the selected request id so the existing effect picks the first row of the new page. Page state resets to 1 on drawer close.
  • view_logs/LogDetailsDrawer/DrawerHeader.tsxNavigationSection grows two new buttons (DoubleLeftOutlined / DoubleRightOutlined) that mirror the K/J style, only render in session mode, and disable at page boundaries.
  • view_logs/LogDetailsDrawer/useKeyboardNavigation.ts — routes Shift+J / Shift+K to onNextPage / onPreviousPage. Plain J/K still walks rows within the current page; the hook now checks e.shiftKey so the two paths stay mutually exclusive (with shift held, e.key is already "J"/"K", which would otherwise conflate them).

Tests

view_logs/LogDetailsDrawer/useKeyboardNavigation.test.ts (new) covers:

  • J / K walk the current page within bounds and no-op at head/tail
  • Shift+J / Shift+K invoke the page handlers and never fall through to row-walk
  • Shift+J without an onNextPage handler is a safe no-op (doesn't silently walk forward)
  • Escape and isOpen=false honor existing semantics
$ npx vitest run src/components/view_logs/LogDetailsDrawer/
PASS (109) FAIL (0)

Manual verification

  • Session with 137 spend logs (3 pages):
    • Drawer opens on page 1 with the earliest 50 events; both ⇧K button and Shift+K disabled.
    • ⇧J / Shift+J → page 2, sidebar refreshes, currentLog jumps to the new page's first row, both prev and next page controls are enabled.
    • ⇧J again → page 3, ⇧J button disabled.
    • J / K continue to walk only within the visible page (50 rows).
  • Non-session mode (clicking a row without session_id): the page-control buttons are not rendered, and Shift+J / Shift+K are no-ops; existing behavior unchanged.

What this does NOT do

  • Does not change the backend default page_size=50 or upper bound 100. If a maintainer would prefer auto-fetching all pages over surfacing pagination controls, happy to re-spin.
  • Does not add cumulative / infinite-scroll behavior — each page independently replaces the sidebar. Mirrors how the outer logs table already paginates.

Screenshots / Proof of Fix

(UI-only change; will attach screenshots in a follow-up comment after the PR opens.)

Sameerlite and others added 3 commits May 18, 2026 19:55
…ing (BerriAI#28156)

* feat(ui): add Interactions API support to playground with streaming

Adds /v1beta/interactions as a selectable endpoint in the UI playground.
Uses SSE streaming (stream=true) and parses content.delta events for real-time output.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(interactions): remove forced gemini provider so all providers work via interactions API

Proxy endpoint was hardcoding custom_llm_provider="gemini" before routing,
preventing non-Gemini models from using the litellm_responses bridge.
Also reverts the UI Gemini-only model filter.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(interactions): fix streaming for non-gemini providers via bridge

Two bugs in LiteLLMResponsesInteractionsStreamingIterator:
1. content.delta was emitted without "type":"text" in delta dict, so the
   UI type-check always failed and no tokens were displayed
2. First OutputTextDeltaEvent was silently dropped (used to emit content.start
   with empty text); fixed by handling ResponsePartAddedEvent for content.start
   so text deltas go directly to content.delta

Co-authored-by: Cursor <cursoragent@cursor.com>

* undo unrelated changes

* fix(ui): extract model from top-level field in interactions bridge events

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* test(interactions): remove tautological gemini-provider assertion

The test_no_forced_gemini_provider_in_request_data check only asserted
against dict literals it had just constructed, so it always passed and
did not exercise the create_interaction endpoint. The endpoint
deliberately defaults custom_llm_provider to gemini, so the assertion
was also factually incorrect. Drop the misleading test.

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix(interactions): use ContentPartAddedEvent and guard interaction.start ordering

- ResponsePartAddedEvent corresponds to reasoning summary parts, not text
  content parts. Use ContentPartAddedEvent which is the event emitted before
  text output deltas (type response.content_part.added).
- Mirror the OutputTextDeltaEvent ordering guard: if interaction.start has
  not been sent yet, emit it first before content.start to honor the
  documented event ordering contract.

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* test(interactions): cover ContentPartAddedEvent ordering and no-op paths

* fix(tests): treat corrupt VCR cassette payloads as cache miss + use gpt-realtime in OpenAI realtime guardrails test

VCR redis persister was raising UnicodeDecodeError on cached payloads that
fail to UTF-8 decode (e.g. legacy entries written by another version of
the persister), failing tests at fixture setup instead of degrading to a
cache miss. Wrap decode+deserialize in a try/except so corrupt cache
entries are treated as CassetteNotFoundError, surfacing the failure via
the existing _record_cache_failure / VCRCassetteCacheWarning path.

OpenAI shut down gpt-4o-realtime-preview-2024-12-17 (and the entire
gpt-4o-realtime-preview family) on 2026-05-07. The live realtime
guardrails integration test now fails with model_not_found instead of
receiving session.created. Point OPENAI_REALTIME_URL at the current GA
model gpt-realtime, and relax the assertion in
test_text_message_blocked_by_guardrail_no_ai_response to also accept the
model's refusal-to-repeat the block message (gpt-realtime declines
verbatim-repeat instructions, which is still a safe outcome since the
original user message was blocked before reaching OpenAI). The
BLOCKED_PHRASE leak check is preserved as a hard invariant.

* fix(tests): migrate realtime + nvidia_nim rerank tests off shut-down upstream models

OpenAI shut down the entire gpt-4o-realtime-preview family (including the
undated alias) on 2026-05-07. The live realtime tests still connected
with that dead alias and failed with messages_received=1 (an error event
'The model gpt-4o-realtime-preview does not exist' instead of
session.created). Point the live OpenAI realtime tests at gpt-realtime,
the current GA realtime model:

- test_openai_realtime_simple.py: get_model() -> gpt-realtime
- test_openai_realtime.py: test_openai_realtime_direct_call_no_intent and
  test_openai_realtime_direct_call_with_intent -> openai/gpt-realtime

Mocked unit tests (test_realtime_query_params_construction,
test_realtime_query_params_use_normalized_model_name) are left as-is:
they never hit the network and assert string plumbing only.

NVIDIA reached end-of-life for the hosted
nvidia/llama-3.2-nv-rerankqa-1b-v2 rerank API on 2026-05-18 with no
published replacement, so the live BaseLLMRerankTest.test_basic_rerank
for nvidia_nim now returns HTTP 410 ('Gone'). NVIDIA's hosted catalog
rotates on a schedule, so swapping in another live model would only
defer the failure. Override test_basic_rerank in TestNvidiaNim to mock
the sync/async HTTP transport (same pattern as
test_nvidia_nim_rerank_ranking_endpoint in this file) and inject a fake
NVIDIA_NIM_API_KEY via monkeypatch. The request/response transformation
and cost calculation stay covered offline.

* test(callbacks): harden flaky proxy callback-leak detector

The proxy callback-leak detector (test_check_num_callbacks_on_lowest_latency)
was failing on this PR with 'abs(85 - 95) <= 4' — a bounded one-time
registration jump caused by switching to latency-based-routing
(+LowestLatencyLoggingHandler, +SlackAlerting). The count then plateaus
under load, so this is pollution from the test's own config update, not a
leak.

Replace the brittle two-sample diff threshold with a sampler that settles
past the deliberate config switch and only flags sustained monotonic
per-type growth, with a terminal-burst confirmation pass for leaks that
would otherwise escape the >=2-interval guard. Normalizes instance
addresses so identical callbacks at different memory locations collapse,
and names the leaking type on failure.

* fix(interactions): preserve first text token when both start events are missing

When OutputTextDeltaEvent arrived before any ResponseCreatedEvent or
ContentPartAddedEvent, the double-fallback path emitted interaction.start
and silently dropped the first delta's text — the second delta's
content.start carried only that chunk's delta, and the first token never
made it to any content.delta event consumed by the UI.

Queue a content.start that carries the first delta's text alongside the
interaction.start emission, and drain pending events before pulling the
next upstream chunk.

* chore(ui): remove unused InteractionOutput/InteractionResponse interfaces

Co-authored-by: Yassin Kortam <yassin@berri.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
…mGenerateContent (BerriAI#27444) (BerriAI#28213)

* fix(proxy): decode bytes and pass-through SSE for Google-native streamGenerateContent (BerriAI#27444)

* fix(proxy): address Greptile review on Google-native SSE bytes path

Remove unreachable try/except around SSE pass-through yield and add a
unit test covering pre-formatted SSE bytes, terminator padding, and
non-SSE byte fallback wrapping.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Tai An <antai12232931@outlook.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
BerriAI#28189)

* refactor(bedrock/sagemaker): switch to lazy loading for response stream shapes

- Replace eager loading of BEDROCK_RESPONSE_STREAM_SHAPE and SAGEMAKER_RESPONSE_STREAM_SHAPE with lazy loading via get_bedrock_response_stream_shape() and get_sagemaker_response_stream_shape() respectively.
- This change optimizes performance by avoiding unnecessary imports and logging warnings unless the response stream shapes are actually needed.
- Update relevant classes and tests to utilize the new lazy loading functions, ensuring consistent behavior across the codebase.

* test(bedrock/sagemaker): add fixtures to clear response stream shape cache

- Introduced `_reset_bedrock_response_stream_shape_cache` and `_reset_sagemaker_response_stream_shape_cache` fixtures to prevent lru_cache leakage between tests in their respective modules.
- Updated tests to utilize these fixtures, ensuring that the response stream shape cache is cleared before and after each test run.
- Added `pytest.importorskip("botocore")` to ensure that tests are skipped if the botocore library is not available.
@CLAassistant

CLAassistant commented May 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed-hq

codspeed-hq Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing shenshouer:feat/log-drawer-session-pagination (1accdc0) with main (a72414a)

Open in CodSpeed

@codecov

codecov Bot commented May 19, 2026

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent 50-entry truncation in the session log sidebar by threading backend pagination (page, page_size, total_pages) through the UI. Both previously flagged issues — event-listener churn from unstable callback references and stale page state when the active session changes while the drawer remains open — are fully addressed in this revision.

  • networking.tsx: sessionSpendLogsCall gains page/page_size params with backward-compatible defaults; URL construction is clean.
  • LogDetailsDrawer.tsx: sessionPage state added; goToPreviousPage/goToNextPage are useCallback-stabilised; a dedicated useEffect on [sessionId, isSessionMode] resets the page whenever the parent swaps sessions without closing the drawer; sessionTotal from response.total is used for the req-count display so it reflects the full session rather than the current page.
  • DrawerHeader.tsx / useKeyboardNavigation.ts: DoubleLeft/DoubleRight buttons and Shift+J/Shift+K shortcuts are gated on session mode and correctly guarded at page boundaries.
  • useKeyboardNavigation.test.ts: New vitest spec covers row-walk, page-nav, boundary no-ops, safe no-op without a handler, and a regression test that validates stable-reference callbacks do not churn the event listener.

Confidence Score: 5/5

UI-only change scoped to the session log sidebar; no backend modifications, no auth or data-mutation paths touched.

The two issues raised in the previous review round are both resolved: useCallback wrapping prevents event-listener churn, and a dedicated useEffect on sessionId prevents stale page state when the active session swaps. The new test file provides direct regression coverage for both fixes. The networking change is additive and backwards-compatible.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/networking.tsx Adds page and page_size query params to sessionSpendLogsCall with backwards-compatible defaults; no issues found.
ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx Adds sessionPage state, useCallback-stabilised page-nav functions, a second useEffect to reset on sessionId change, and the sessionTotal fix for the req-count display; both previously flagged issues are resolved.
ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/DrawerHeader.tsx Adds optional DoubleLeft/DoubleRight page-nav buttons that only render in session mode; interface and defaults are clean.
ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/useKeyboardNavigation.ts Adds Shift+J/Shift+K page shortcuts with correct e.shiftKey guard; onPreviousPage/onNextPage correctly added to effect deps; pre-existing onClose/onSelectLog stale-closure pattern unchanged.
ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/useKeyboardNavigation.test.ts New vitest spec covers J/K row navigation, Shift+J/K page navigation, boundary no-ops, safe-no-op without handler, Escape, closed-drawer guard, and stable-callback regression test; no issues found.

Reviews (5): Last reviewed commit: "fix(ui/log-drawer): stabilise page-nav c..." | Re-trigger Greptile

@shenshouer

shenshouer commented May 19, 2026

Copy link
Copy Markdown
Author

Paginate Seesion Log Details drawer
image
image

@oss-pr-review-agent-shin

Copy link
Copy Markdown
Contributor

🤖 litellm-agent: This PR is currently BLOCKED from merge.

Score: 2/5

Why blocked:

  • 1 PR-related CI failure (Greptile gate: score 3/5 below required 4/5 — request a Greptile review (@greptileai) and resolve its comments before maintainer review.) (pr_related_failures, -2 pts)
  • Greptile 3/5 (greptile_low, -1 pts)

Details: Score docked for: 1 PR-related CI failure (Greptile gate: score 3/5 below required 4/5 — request a Greptile review (@greptileai) and resolve its comments before maintainer review.); Greptile 3/5.

Fix the issues above and push an update — the bot will re-review automatically.

Note: This bot is still in beta and might not always work as expected. Please share any feedback via Slack.

@shenshouer

Copy link
Copy Markdown
Author

No Seesion Log Details drawer
image

@shenshouer

Copy link
Copy Markdown
Author

Thanks for the review @greptileai — both findings were valid. Pushed 8c4... (will fill in once GitHub assigns the SHA) addressing them:

P1: sessionPage not reset on sessionId change while drawer stays open
Added a dedicated effect keyed on sessionId that clears both sessionPage and selectedSessionRequestId. The original reset effect (keyed on open / isSessionMode) is left intact so its semantics are unchanged; the new effect strictly handles the in-place session swap path.

P2: per-page aggregate stats labeled as session totals

  • The "{N} req" count now reads total from the backend response, so it always reflects the full session.
  • Cost / duration / LLM / Agent / MCP counts still reflect only the current page (computing the full session would require fetching every page on every drawer open). When total_pages > 1, the sidebar header now appends page X/Y (cost/duration/counts shown for this page) so the narrower scope is explicit.
  • When total_pages === 1 the line is identical to the pre-PR output.

Tests in useKeyboardNavigation.test.ts continue to pass (vitest 109/109 in view_logs/LogDetailsDrawer/). Could you re-trigger Greptile when you have a moment?

@greptileai please re-review.

@shenshouer

Copy link
Copy Markdown
Author

@greptileai re: the unstable function refs comment — fixed in the latest push:

  • goToPreviousPage / goToNextPage are now useCallback'd with [canGoPreviousPage] / [canGoNextPage] deps.
  • Removed the boundary ? fn : undefined ternary at the useKeyboardNavigation call site — the callbacks self-guard, and the ternary itself was producing a new value every render which would have defeated the useCallback.
  • Added a regression test in useKeyboardNavigation.test.ts that spies on window.addEventListener and asserts the keydown listener is not re-registered on a re-render with identical prop references.

Vitest in view_logs/LogDetailsDrawer/ is now 110/110.

Please re-review when convenient.

@shenshouer

Copy link
Copy Markdown
Author

Hi maintainers — the guard-main-branch workflow is failing on this PR with:

External contributors should open PRs against the litellm_oss_branch branch instead.

But litellm_oss_branch does not appear to exist in this repo (gh api repos/BerriAI/litellm/branches/litellm_oss_branch → 404). The closest long-lived branch I can find is litellm_oss_staging (plus dated snapshots like litellm_oss_staging_06_06_2026).

Could you confirm which branch external/fork PRs should actually target? Happy to retarget the base of this PR once I know the right one. CONTRIBUTING.md doesn't currently document this either — I can also send a small docs PR to clarify if helpful.

Source of the message: .github/workflows/guard-main-branch.yml

@shenshouer shenshouer changed the base branch from main to litellm_oss_staging May 19, 2026 07:48
@shenshouer

Copy link
Copy Markdown
Author

Update: retargeted the base to litellm_oss_staging since that's the closest long-lived branch that actually exists. Happy to redirect again if maintainers prefer something else.

@shenshouer

Copy link
Copy Markdown
Author

@greptileai review

@shenshouer

Copy link
Copy Markdown
Author

@greptileai please re-review the latest commits — 97cbe78 and 1accdc0 address the previous P1/P2 findings.

@oss-pr-review-agent-shin

Copy link
Copy Markdown
Contributor

🤖 litellm-agent: This PR is currently BLOCKED from merge.

Score: 0/5

Why blocked:

  • all Phase B agent checks non-approving (phase_b_none_approved, -5 pts)

Details: Score docked for: all Phase B agent checks non-approving (karpathy + security + coverage gap).

Fix the issues above and push an update — the bot will re-review automatically.

Note: This bot is still in beta and might not always work as expected. Please share any feedback via Slack.

@shenshouer

Copy link
Copy Markdown
Author

@oss-pr-review-agent-shin Could you please post the specific feedback from each Phase B agent (karpathy / security / coverage gap)? The summary score (0/5) doesn't include actionable details, so I can't tell what needs to change.

For context, this PR is a UI-only change in ui/litellm-dashboard/:

  • Frontend pagination wiring (4 files modified)
  • 1 new test file with 9 unit tests covering the new keyboard navigation paths (useKeyboardNavigation.test.ts)
  • No backend, no security-sensitive surface, no new dependencies

Happy to address concrete findings, but I need to know what they are.

@shenshouer

Copy link
Copy Markdown
Author

Hi maintainers — could someone help unblock this PR?

The oss-pr-review-agent-shin bot scored it 0/5 with the reason "all Phase B agent checks non-approving (karpathy + security + coverage gap)" but didn't include the specific findings from each sub-agent, so there's nothing concrete to act on. My ping asking it to elaborate also got no response (looks like the bot only triggers on push events, not mentions).

For context, this PR is purely a UI-only change in ui/litellm-dashboard/:

  • 4 frontend files modified (session-pagination wiring + keyboard nav)
  • 1 new test file with 9 unit tests
  • No backend, no auth/security surface, no new deps
  • Greptile review already passed (👍 reaction on the latest re-review request)
  • All other 30+ CI checks passing

Two questions:

  1. Is the oss-pr-review-agent-shin score a hard merge gate, or advisory while it's in beta?
  2. If it's a hard gate, is there any way to see the per-sub-agent feedback so I know what to fix?

Happy to address concrete findings — just need visibility into them. Thanks!

shenshouer and others added 5 commits May 19, 2026 16:30
…cuts

The session-mode trace sidebar was implicitly capped at the backend
default page_size=50, so sessions with more than 50 spend logs silently
truncated to the earliest 50. This wires the existing
/spend/logs/session/ui pagination through the UI:

- sessionSpendLogsCall now accepts page/page_size and forwards them.
- LogDetailsDrawer keeps a sessionPage state, includes it in the React
  Query key, and reads total_pages from the response. Page-prev / next
  callbacks reset the selected request id so the effect picks the first
  row of the new page.
- DrawerHeader's NavigationSection grows two new buttons (DoubleLeft /
  DoubleRight) that mirror the K/J style and only render in session
  mode; they disable at page boundaries.
- useKeyboardNavigation routes Shift+J / Shift+K to onNextPage /
  onPreviousPage. Plain J/K still walks rows within the current page
  (we now check e.shiftKey to keep the two paths mutually exclusive).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…yboardNavigation

The split between row-walk (J/K) and page (Shift+J/K) is the riskiest
part of the previous commit — with shift held, e.key is already "J"/"K"
(uppercase), so the natural switch on e.key conflates the two paths.
This adds a vitest spec asserting:

- J / K walk the current page within bounds and no-op at head/tail
- Shift+J / Shift+K invoke the page handlers and never fall through to
  the row-walk handlers
- Shift+J without an onNextPage handler is a safe no-op (doesn't
  silently walk to the next row)
- Escape and isOpen=false honor existing semantics

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two issues raised by Greptile (BerriAI#28225):

P1 — sessionId change while drawer open
The page-reset effect only watched [open, isSessionMode], so swapping
sessionId without closing the drawer could request a non-existent
page (e.g. clicking from a 137-row session on page 3 directly to a
20-row session). Add a dedicated effect keyed on sessionId that
clears sessionPage and the selected request id.

P2 — per-page aggregates were labeled as session totals
totalSessionCost / sessionDurationSeconds / llmCount / agentCount /
mcpCount are now derived from the current page only, but the sidebar
header presented them as session totals. Surface the backend's
total-row count for "{N} req", and append a "page X/Y (cost/duration/
counts shown for this page)" disclosure when total_pages > 1 so the
narrower scope of the other aggregates is explicit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…r churn

Greptile P2 (BerriAI#28225):
goToPreviousPage / goToNextPage were plain inline arrow functions, so
they got new identities every render. Because useKeyboardNavigation
depends on them in its effect deps, the global keydown listener was
torn down and re-attached on every parent re-render.

- Wrap goToPreviousPage / goToNextPage with useCallback, scoped to the
  matching boundary flag (canGoPreviousPage / canGoNextPage).
- Drop the `boundary ? fn : undefined` ternary at the call site — the
  callbacks self-guard against out-of-bounds, and the ternary itself
  produced a fresh value each render, defeating the useCallback.
- Add a regression test in useKeyboardNavigation.test.ts that spies on
  window.addEventListener and asserts the keydown listener is not
  re-registered when the hook re-renders with identical prop refs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@shenshouer shenshouer force-pushed the feat/log-drawer-session-pagination branch from 31dccf1 to 0bd812d Compare May 19, 2026 08:30
@shenshouer

Copy link
Copy Markdown
Author

recheck

@shenshouer

Copy link
Copy Markdown
Author

@ishaan-jaff @mateo-berri sorry to ping — would either of you have a chance to take a look when you have a moment?

Quick status:

  • ✅ Greptile review: 5/5 confidence (re-reviewed after the P1/P2 fixes)
  • ✅ All 4 pre-submission boxes checked off
  • ✅ CLA signed
  • ✅ CI: passing on the last push (the earlier guard-main-branch failure is from before the base was retargeted to litellm_oss_staging and no longer applies)
  • ⚠️ oss-pr-review-agent-shin shows 0/5 with "all Phase B agent checks non-approving" but doesn't include per-sub-agent feedback — left an open question above asking whether that's a hard gate while the bot is in beta

The change is UI-only (4 frontend files + 1 new vitest spec) and fixes the silent 50-entry truncation in the session log sidebar described in #28224. Happy to address any concerns.

@shenshouer

Copy link
Copy Markdown
Author

@yuneng-berri sorry to ping — saw you've been active in the repo recently and you're already a CLA signer on this PR. Would you have a moment to take a look or help route this for review/merge?

Quick status:

  • ✅ Greptile review: 5/5 confidence
  • ✅ All pre-submission boxes checked
  • ✅ CLA signed for all current committers (only my commits remain after the rebase)
  • ✅ CI passing on the latest push

The change is UI-only — fixes the silent 50-entry truncation in the session log sidebar (#28224). Happy to address any concerns. Thanks!

@shenshouer shenshouer changed the base branch from litellm_oss_staging to shin_agent_oss_staging_05_21_2026 May 21, 2026 11:21
@shenshouer

Copy link
Copy Markdown
Author

Update: I noticed today's external PRs (e.g. #28431, #28430, #28449) all target shin_agent_oss_staging_05_21_2026, and the merge automation seems to expect that base. Retargeted this PR accordingly so it can flow through the same staging → litellm_internal_staging pipeline.

If a different dated branch is preferred, happy to redirect again.

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.

4 participants