Skip to content

feat: replace SSE streaming with background job polling for cost recalculation - #5006

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-06-feat_logs_cost_recalc_ui
Jul 9, 2026
Merged

Pratham-Mishra04 merged 1 commit into
devfrom
07-06-feat_logs_cost_recalc_ui

Conversation

@impoiler

@impoiler impoiler commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the SSE-based streaming approach for cost recalculation with a background job model. Instead of holding an open HTTP stream until recalculation finishes, the UI now enqueues a job, receives a job ID, and polls a status endpoint until the job reaches a terminal state. A new confirmation dialog lets users choose between recalculating only logs missing a cost or all logs matching the current filters.

Changes

  • Background job polling: startRecalculateCostJob replaces recalculateCostsWithProgress. It POSTs to the recalculate endpoint and handles 202 Accepted (new job started) and 409 Conflict (job already running), returning the job ID in both cases. A useGetRecalculateCostStatusQuery hook polls /logs/recalculate-cost/status every 2 seconds while a job ID is active, stopping automatically on a terminal status (completed or failed).
  • RecalculateCostDialog: A new confirmation dialog (recalculateCostDialog.tsx) presents two modes — "Missing cost only" and "All selected logs". On open it lazily fetches the count of logs missing a cost for the current filters and displays it inline. The confirm button is disabled when the missing-cost count is zero or still loading.
  • RecalcJobStatus type: Replaces RecalculateCostProgress and RecalculateCostResponse for the new job-based API shape, covering idle | pending | running | completed | failed statuses with progress counters and error fields.
  • totalLogs prop: Passed down from the logs page to the header view and into the dialog so the "All selected logs" mode can display the count without an extra fetch.
  • buildFilterParams exported: Made public so it can be reused by the lazy stats query inside the dialog.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

  1. Open the Logs page and apply any filters or time window.
  2. Click the (more actions) menu and select Recalculate costs.
  3. Verify the confirmation dialog opens showing the count of logs missing a cost.
  4. Switch to "All selected logs" and confirm the total log count is displayed.
  5. Confirm with either mode and verify:
    • A loading toast appears immediately.
    • The toast updates with progress (processed/total checked, N updated, N skipped).
    • On completion, a success toast appears with the final counts.
    • If a recalculation is already running, a "already running" toast appears and polling attaches to the existing job.
  6. Trigger a failure scenario and confirm an error toast is shown.
cd ui
pnpm i
pnpm build

Screenshots/Recordings

Add before/after screenshots of the recalculate costs dialog and toast progression.

Breaking changes

  • Yes
  • No

Related issues

Security considerations

No new auth surfaces. The recalculate endpoint already requires credentials; the polling status endpoint follows the same auth model.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a23242bd-7c9f-4c9c-a266-712a0afadcca

📥 Commits

Reviewing files that changed from the base of the PR and between 439b154 and 570fb54.

📒 Files selected for processing (5)
  • ui/app/workspace/logs/page.tsx
  • ui/app/workspace/logs/views/logsHeaderView.tsx
  • ui/app/workspace/logs/views/recalculateCostDialog.tsx
  • ui/lib/store/apis/logsApi.ts
  • ui/lib/types/logs.ts
📝 Walkthrough

Walkthrough

Replaces the SSE-based recalculation flow with a dialog-driven background job workflow. Adds a job status type, polling endpoint, recalculation dialog, and logs page/header wiring to start jobs, poll status, and refresh on completion.

Changes

Recalculate costs job workflow

Layer / File(s) Summary
Job status type and API endpoint
ui/lib/types/logs.ts, ui/lib/store/apis/logsApi.ts
Adds the RecalcJobStatus interface and a getRecalculateCostStatus query endpoint (hitting /logs/recalculate-cost/status) with its exported hook.
RecalculateCostDialog component
ui/app/workspace/logs/views/recalculateCostDialog.tsx
Adds a dialog for choosing "missing" or "all" recalculation mode, lazily fetching missing-cost counts and rendering selectable mode options with dynamic helper text and disabled states.
Header view integration and job polling
ui/app/workspace/logs/views/logsHeaderView.tsx
Adds totalLogs, dialog/job state, polling via useGetRecalculateCostStatusQuery, background-job start logic for 202/409 responses, and toast/refresh handling for terminal job states.
Page-level totalLogs prop wiring
ui/app/workspace/logs/page.tsx
Passes totalLogs derived from stats.total_requests into LogsHeaderView.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant RecalculateCostDialog
  participant LogsHeaderView
  participant logsApi

  User->>RecalculateCostDialog: Open dialog, select mode
  RecalculateCostDialog->>LogsHeaderView: onConfirm(mode)
  LogsHeaderView->>logsApi: startRecalculateCostJob(filters, mode)
  logsApi-->>LogsHeaderView: 202/409 RecalcJobStatus
  LogsHeaderView->>logsApi: useGetRecalculateCostStatusQuery(id)
  logsApi-->>LogsHeaderView: job status snapshot
  LogsHeaderView->>LogsHeaderView: show success/failure toast, refresh logs/stats
Loading

Possibly related PRs

  • maximhq/bifrost#4778: Removes the SSE-based cost recalculation/progress flow and replaces it with the background-job polling behavior reflected here.

Suggested reviewers: akshaydeo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: replacing SSE recalculation with background job polling.
Description check ✅ Passed All required sections are present and the PR description covers the feature, testing, security, and affected areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-06-feat_logs_cost_recalc_ui

Comment @coderabbitai help to get the list of available commands.

@impoiler impoiler self-assigned this Jul 7, 2026
@impoiler impoiler changed the title feat(logs-ui): recalculate-cost dialog with background job polling feat: replace SSE streaming with background job polling for cost recalculation Jul 7, 2026
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch 3 times, most recently from 5e348d6 to 3d43fb2 Compare July 8, 2026 04:28
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch 2 times, most recently from 36910d0 to c49e018 Compare July 8, 2026 08:55
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 3d43fb2 to d7cd79a Compare July 8, 2026 08:55
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch from c49e018 to ea50175 Compare July 8, 2026 10:02
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from d7cd79a to 4077e3a Compare July 8, 2026 10:02
@impoiler
impoiler marked this pull request as ready for review July 8, 2026 14:10
@coderabbitai
coderabbitai Bot requested a review from akshaydeo July 8, 2026 14:11
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

The polling and toast orchestration in logsHeaderView.tsx has tricky React effect ordering interactions that leave two known behavioral gaps unaddressed from earlier review rounds.

The refactor is architecturally clean and the dialog's guard logic is correct. However, the status-reaction effect (line 147) does not gate on the absence of a poll error, which means a transient HTTP error can overwrite the error toast with a stale loading message. Additionally, a job whose server-side state has been reset to "idle" (e.g., after a service restart before the first poll) is not treated as a terminal condition, leaving the UI polling indefinitely.

ui/app/workspace/logs/views/logsHeaderView.tsx — specifically the interplay between the error effect (line 128) and the status-reaction effect (line 147), and the terminal-state check that omits "idle".

Important Files Changed

Filename Overview
ui/app/workspace/logs/views/logsHeaderView.tsx Core polling and toast orchestration. Adds the ref-backed cleanup fix and the error-stopping effect, but the status effect still does not gate on !recalcJobStatusError and "idle" is not treated as a terminal status; previously raised issues in those areas remain unaddressed.
ui/app/workspace/logs/views/recalculateCostDialog.tsx New confirmation dialog with mode selection and lazy stats fetch. confirmDisabled now correctly guards both modes (totalLogs === 0 for "all", missingCount === 0 for "missing").
ui/lib/store/apis/logsApi.ts Adds getRecalculateCostStatus polling endpoint. Harmless over-engineering in the arg shape.
ui/lib/types/logs.ts Adds RecalcJobStatus interface with all expected fields. id is optional and handled correctly at the call site.
ui/app/workspace/logs/page.tsx One-line prop addition passing totalItems as totalLogs. No regressions; existing testids preserved.

Reviews (12): Last reviewed commit: "feat(logs-ui): recalculate-cost dialog w..." | Re-trigger Greptile

Comment thread ui/app/workspace/logs/views/recalculateCostDialog.tsx Outdated
Comment thread ui/app/workspace/logs/views/recalculateCostDialog.tsx
Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx
Comment thread ui/lib/store/apis/logsApi.ts Outdated
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 4077e3a to 8429b4f Compare July 9, 2026 01:38
Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
ui/app/workspace/logs/views/logsHeaderView.tsx (2)

61-65: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider skipPollingIfUnfocused for consistency.

Every other polling query in this feature (useGetLogsQuery, useGetLogsStatsQuery, useGetLogsHistogramQuery in page.tsx) sets skipPollingIfUnfocused: true. This new job-status poll omits it, so it keeps polling every 2s even when the tab is backgrounded.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/app/workspace/logs/views/logsHeaderView.tsx` around lines 61 - 65, The new
polling call in logsHeaderView’s useGetRecalculateCostStatusQuery is missing the
same unfocused-tab behavior used by the other polling queries in page.tsx.
Update the job-status polling options in logsHeaderView to include
skipPollingIfUnfocused: true alongside the existing pollingInterval and skip
settings so the recalculation status query stops polling when the tab is
backgrounded.

121-153: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Effect deps include unstable fetchLogs/fetchStats references.

fetchLogs and fetchStats are passed down from page.tsx as new inline arrow functions on every render, so including them in this effect's dependency array causes it to re-run (and re-issue toast.loading(...)) far more often than the actual 2s poll cadence, on any unrelated parent re-render.

As per coding guidelines, "Avoid unnecessary or unstable dependencies in hooks... keep dependency arrays accurate and minimal."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/app/workspace/logs/views/logsHeaderView.tsx` around lines 121 - 153, The
`useEffect` in `LogsHeaderView` is depending on unstable `fetchLogs` and
`fetchStats` props, causing the toast/update effect to re-run on unrelated
parent renders. Remove these inline callback references from the dependency list
by making the callbacks stable in the parent `page.tsx` (for example with
`useCallback`) or by otherwise ensuring the effect only depends on the actual
polling state it uses, then keep the effect dependencies minimal and accurate.
ui/app/workspace/logs/views/recalculateCostDialog.tsx (1)

60-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add data-testid to the new interactive controls.

The mode-selection buttons and the Cancel/Recalculate footer buttons are new interactive elements with no data-testid, so E2E specs can't reliably target them.

As per coding guidelines, "If you add new interactive elements, add data-testid."

🧪 Proposed fix
 				<Button variant="outline" size="sm" onClick={() => onOpenChange(false)}>
+					data-testid="recalculate-cost-cancel-btn"
 					Cancel
 				</Button>
-				<Button size="sm" onClick={() => onConfirm(mode)} disabled={confirmDisabled}>
+				<Button data-testid="recalculate-cost-confirm-btn" size="sm" onClick={() => onConfirm(mode)} disabled={confirmDisabled}>
 					Recalculate
 				</Button>

And on RecalculateModeOption's <button>, accept/forward a data-testid prop (e.g. recalculate-cost-mode-missing, recalculate-cost-mode-all).

Also applies to: 99-106, 124-142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/app/workspace/logs/views/recalculateCostDialog.tsx` around lines 60 - 71,
The new interactive controls in RecalculateCostDialog and RecalculateModeOption
need stable test selectors. Update RecalculateModeOption to accept and forward a
data-testid on its button, then pass distinct values for the “missing” and “all”
mode options from recalculateCostDialog.tsx. Also add data-testid attributes to
the Cancel and Recalculate footer buttons in the same dialog so E2E tests can
target all new actions reliably.
ui/lib/store/apis/logsApi.ts (1)

368-376: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove recalculateLogCosts from ui/lib/store/apis/logsApi.ts — no call sites show up outside the endpoint definition and generated hook export, so it only adds confusion about how cost recalculation starts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/lib/store/apis/logsApi.ts` around lines 368 - 376, The cost-recalculation
API entry should be removed because it is no longer used to start recalculation
and only adds confusion. Delete the `recalculateLogCosts` endpoint from the
`logsApi` builder definition and remove any related generated hook/export
references tied to that endpoint, while keeping `getRecalculateCostStatus`
intact for polling the background job status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/app/workspace/logs/views/logsHeaderView.tsx`:
- Around line 61-65: The recalculation status polling in logsHeaderView is only
reading data, so query failures can leave activeRecalcJobId stuck forever.
Update useGetRecalculateCostStatusQuery to also capture the error state (for
example alongside recalcJobStatus), then in the terminal-status effect for the
recalculation flow, treat a persistent recalcJobError as a failure: show
toast.error with a clear message and clear activeRecalcJobId so the loading
state cannot persist indefinitely.
- Around line 121-153: The loading toast in logsHeaderView’s useEffect is never
dismissed if the component unmounts while recalcJobStatus is still in progress,
leaving “Recalculating log costs…” stuck on screen. Add a cleanup path in the
same effect for the toast id used in the progress state, so it is
dismissed/reset on unmount or when the effect is torn down, while keeping the
existing success/failure handling for activeRecalcJobId and recalcJobStatus.

---

Nitpick comments:
In `@ui/app/workspace/logs/views/logsHeaderView.tsx`:
- Around line 61-65: The new polling call in logsHeaderView’s
useGetRecalculateCostStatusQuery is missing the same unfocused-tab behavior used
by the other polling queries in page.tsx. Update the job-status polling options
in logsHeaderView to include skipPollingIfUnfocused: true alongside the existing
pollingInterval and skip settings so the recalculation status query stops
polling when the tab is backgrounded.
- Around line 121-153: The `useEffect` in `LogsHeaderView` is depending on
unstable `fetchLogs` and `fetchStats` props, causing the toast/update effect to
re-run on unrelated parent renders. Remove these inline callback references from
the dependency list by making the callbacks stable in the parent `page.tsx` (for
example with `useCallback`) or by otherwise ensuring the effect only depends on
the actual polling state it uses, then keep the effect dependencies minimal and
accurate.

In `@ui/app/workspace/logs/views/recalculateCostDialog.tsx`:
- Around line 60-71: The new interactive controls in RecalculateCostDialog and
RecalculateModeOption need stable test selectors. Update RecalculateModeOption
to accept and forward a data-testid on its button, then pass distinct values for
the “missing” and “all” mode options from recalculateCostDialog.tsx. Also add
data-testid attributes to the Cancel and Recalculate footer buttons in the same
dialog so E2E tests can target all new actions reliably.

In `@ui/lib/store/apis/logsApi.ts`:
- Around line 368-376: The cost-recalculation API entry should be removed
because it is no longer used to start recalculation and only adds confusion.
Delete the `recalculateLogCosts` endpoint from the `logsApi` builder definition
and remove any related generated hook/export references tied to that endpoint,
while keeping `getRecalculateCostStatus` intact for polling the background job
status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d5266447-e8f5-4b3a-8644-2fae1b88e904

📥 Commits

Reviewing files that changed from the base of the PR and between ea50175 and 8429b4f.

📒 Files selected for processing (5)
  • ui/app/workspace/logs/page.tsx
  • ui/app/workspace/logs/views/logsHeaderView.tsx
  • ui/app/workspace/logs/views/recalculateCostDialog.tsx
  • ui/lib/store/apis/logsApi.ts
  • ui/lib/types/logs.ts

Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx Outdated
Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 8429b4f to 7d2975f Compare July 9, 2026 03:06
@coderabbitai
coderabbitai Bot requested a review from danpiths July 9, 2026 03:07
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 9, 2026
Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx Outdated
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 7d2975f to 5babf22 Compare July 9, 2026 03:58
Comment thread ui/app/workspace/logs/views/logsHeaderView.tsx
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch from ea50175 to 9db256b Compare July 9, 2026 04:07
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 5babf22 to a9c404a Compare July 9, 2026 04:07
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch from 9db256b to e14b9eb Compare July 9, 2026 04:23
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch 2 times, most recently from 53af499 to 6247490 Compare July 9, 2026 04:50
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch 2 times, most recently from 2c53c61 to 05b4663 Compare July 9, 2026 10:58
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 6247490 to 98e1996 Compare July 9, 2026 10:58
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch from 05b4663 to 965fe69 Compare July 9, 2026 11:36
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 98e1996 to 3a04068 Compare July 9, 2026 11:36
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_background_job branch from 965fe69 to 7ef8fb9 Compare July 9, 2026 12:09
@impoiler
impoiler force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 3a04068 to 439b154 Compare July 9, 2026 12:09

Pratham-Mishra04 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Merge activity

  • Jul 9, 12:16 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 9, 12:28 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 9, 12:29 PM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

Comment on lines +147 to +179
useEffect(() => {
if (!activeRecalcJobId || !recalcJobStatus) return;
const toastId = "logs-recalculate-costs";

if (recalcJobStatus.status === "completed" || recalcJobStatus.status === "failed") {
if (recalcJobStatus.status === "failed") {
toast.error("Cost recalculation failed", {
id: toastId,
description: recalcJobStatus.last_error || recalcJobStatus.message || "The job did not complete",
});
} else {
toast.success("Cost recalculation complete", {
id: toastId,
description: recalcJobStatus.message || `${recalcJobStatus.updated} updated, ${recalcJobStatus.skipped} skipped`,
duration: 5000,
});
}
setActiveRecalcJobId(null);
void fetchLogs();
void fetchStats();
return;
}

toast.promise(recalculatePromise, {
const total = recalcJobStatus.total || 0;
const processed = total > 0 ? Math.min(recalcJobStatus.processed, total) : recalcJobStatus.processed;
toast.loading("Recalculating log costs...", {
id: toastId,
loading: "Recalculating log costs...",
success: (response) => ({
message: `Recalculated costs for ${response.updated} logs`,
description: `${response.updated} logs updated, ${response.skipped} logs skipped, ${response.remaining} logs remaining`,
duration: 5000,
}),
error: (err) => getErrorMessage(err),
description:
total > 0
? `${processed}/${total} checked, ${recalcJobStatus.updated} updated, ${recalcJobStatus.skipped} skipped`
: `${recalcJobStatus.processed} checked, ${recalcJobStatus.updated} updated, ${recalcJobStatus.skipped} skipped`,
});

try {
await recalculatePromise;
await fetchLogs();
await fetchStats();
} catch {}
}, [filters, fetchLogs, fetchStats]);
}, [activeRecalcJobId, recalcJobStatus, fetchLogs, fetchStats]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 "idle" status is never treated as a terminal polling state

RecalcJobStatus.status includes "idle" as a valid value, but the status effect only exits polling on "completed" or "failed". If the server returns {status: "idle"} for a queried job ID — for example when a job has been cleaned up after a server restart or TTL expiry between the initial POST and the first status poll — neither the error effect (no HTTP error) nor the status effect (not a terminal string) will call setActiveRecalcJobId(null). Polling continues at 2-second intervals indefinitely, the loading toast never resolves, and the menu item stays permanently disabled for the session.

Adding "idle" to the terminal check (or treating it as an error state) gives the UI an escape path without relying solely on the HTTP-error path.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 07-06-feat_logs_cost_recalc_background_job to graphite-base/5006 July 9, 2026 12:24
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5006 to dev July 9, 2026 12:27
@Pratham-Mishra04
Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review July 9, 2026 12:27

The base branch was changed.

Extract a RecalculateCostDialog that shows the missing-cost count via a lazy stats query, and poll job status with an RTK query pollingInterval instead of a hand-rolled loop.
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-06-feat_logs_cost_recalc_ui branch from 439b154 to 570fb54 Compare July 9, 2026 12:27
@Pratham-Mishra04
Pratham-Mishra04 merged commit ee63540 into dev Jul 9, 2026
12 of 13 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-06-feat_logs_cost_recalc_ui branch July 9, 2026 12:29
akshaydeo pushed a commit that referenced this pull request Jul 14, 2026
…lculation (#5006)

## Summary

Replaces the SSE-based streaming approach for cost recalculation with a background job model. Instead of holding an open HTTP stream until recalculation finishes, the UI now enqueues a job, receives a job ID, and polls a status endpoint until the job reaches a terminal state. A new confirmation dialog lets users choose between recalculating only logs missing a cost or all logs matching the current filters.

## Changes

- **Background job polling**: `startRecalculateCostJob` replaces `recalculateCostsWithProgress`. It POSTs to the recalculate endpoint and handles `202 Accepted` (new job started) and `409 Conflict` (job already running), returning the job ID in both cases. A `useGetRecalculateCostStatusQuery` hook polls `/logs/recalculate-cost/status` every 2 seconds while a job ID is active, stopping automatically on a terminal status (`completed` or `failed`).
- **RecalculateCostDialog**: A new confirmation dialog (`recalculateCostDialog.tsx`) presents two modes — "Missing cost only" and "All selected logs". On open it lazily fetches the count of logs missing a cost for the current filters and displays it inline. The confirm button is disabled when the missing-cost count is zero or still loading.
- **`RecalcJobStatus` type**: Replaces `RecalculateCostProgress` and `RecalculateCostResponse` for the new job-based API shape, covering `idle | pending | running | completed | failed` statuses with progress counters and error fields.
- **`totalLogs` prop**: Passed down from the logs page to the header view and into the dialog so the "All selected logs" mode can display the count without an extra fetch.
- **`buildFilterParams` exported**: Made public so it can be reused by the lazy stats query inside the dialog.

## Type of change

- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Open the Logs page and apply any filters or time window.
2. Click the **⋮** (more actions) menu and select **Recalculate costs**.
3. Verify the confirmation dialog opens showing the count of logs missing a cost.
4. Switch to "All selected logs" and confirm the total log count is displayed.
5. Confirm with either mode and verify:
   - A loading toast appears immediately.
   - The toast updates with progress (`processed/total checked, N updated, N skipped`).
   - On completion, a success toast appears with the final counts.
   - If a recalculation is already running, a "already running" toast appears and polling attaches to the existing job.
6. Trigger a failure scenario and confirm an error toast is shown.

```sh
cd ui
pnpm i
pnpm build
```

## Screenshots/Recordings

_Add before/after screenshots of the recalculate costs dialog and toast progression._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth surfaces. The recalculate endpoint already requires credentials; the polling status endpoint follows the same auth model.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
…lculation (maximhq#5006)

## Summary

Replaces the SSE-based streaming approach for cost recalculation with a background job model. Instead of holding an open HTTP stream until recalculation finishes, the UI now enqueues a job, receives a job ID, and polls a status endpoint until the job reaches a terminal state. A new confirmation dialog lets users choose between recalculating only logs missing a cost or all logs matching the current filters.

## Changes

- **Background job polling**: `startRecalculateCostJob` replaces `recalculateCostsWithProgress`. It POSTs to the recalculate endpoint and handles `202 Accepted` (new job started) and `409 Conflict` (job already running), returning the job ID in both cases. A `useGetRecalculateCostStatusQuery` hook polls `/logs/recalculate-cost/status` every 2 seconds while a job ID is active, stopping automatically on a terminal status (`completed` or `failed`).
- **RecalculateCostDialog**: A new confirmation dialog (`recalculateCostDialog.tsx`) presents two modes — "Missing cost only" and "All selected logs". On open it lazily fetches the count of logs missing a cost for the current filters and displays it inline. The confirm button is disabled when the missing-cost count is zero or still loading.
- **`RecalcJobStatus` type**: Replaces `RecalculateCostProgress` and `RecalculateCostResponse` for the new job-based API shape, covering `idle | pending | running | completed | failed` statuses with progress counters and error fields.
- **`totalLogs` prop**: Passed down from the logs page to the header view and into the dialog so the "All selected logs" mode can display the count without an extra fetch.
- **`buildFilterParams` exported**: Made public so it can be reused by the lazy stats query inside the dialog.

## Type of change

- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Open the Logs page and apply any filters or time window.
2. Click the **⋮** (more actions) menu and select **Recalculate costs**.
3. Verify the confirmation dialog opens showing the count of logs missing a cost.
4. Switch to "All selected logs" and confirm the total log count is displayed.
5. Confirm with either mode and verify:
   - A loading toast appears immediately.
   - The toast updates with progress (`processed/total checked, N updated, N skipped`).
   - On completion, a success toast appears with the final counts.
   - If a recalculation is already running, a "already running" toast appears and polling attaches to the existing job.
6. Trigger a failure scenario and confirm an error toast is shown.

```sh
cd ui
pnpm i
pnpm build
```

## Screenshots/Recordings

_Add before/after screenshots of the recalculate costs dialog and toast progression._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth surfaces. The recalculate endpoint already requires credentials; the polling status endpoint follows the same auth model.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
…lculation (maximhq#5006)

## Summary

Replaces the SSE-based streaming approach for cost recalculation with a background job model. Instead of holding an open HTTP stream until recalculation finishes, the UI now enqueues a job, receives a job ID, and polls a status endpoint until the job reaches a terminal state. A new confirmation dialog lets users choose between recalculating only logs missing a cost or all logs matching the current filters.

## Changes

- **Background job polling**: `startRecalculateCostJob` replaces `recalculateCostsWithProgress`. It POSTs to the recalculate endpoint and handles `202 Accepted` (new job started) and `409 Conflict` (job already running), returning the job ID in both cases. A `useGetRecalculateCostStatusQuery` hook polls `/logs/recalculate-cost/status` every 2 seconds while a job ID is active, stopping automatically on a terminal status (`completed` or `failed`).
- **RecalculateCostDialog**: A new confirmation dialog (`recalculateCostDialog.tsx`) presents two modes — "Missing cost only" and "All selected logs". On open it lazily fetches the count of logs missing a cost for the current filters and displays it inline. The confirm button is disabled when the missing-cost count is zero or still loading.
- **`RecalcJobStatus` type**: Replaces `RecalculateCostProgress` and `RecalculateCostResponse` for the new job-based API shape, covering `idle | pending | running | completed | failed` statuses with progress counters and error fields.
- **`totalLogs` prop**: Passed down from the logs page to the header view and into the dialog so the "All selected logs" mode can display the count without an extra fetch.
- **`buildFilterParams` exported**: Made public so it can be reused by the lazy stats query inside the dialog.

## Type of change

- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Open the Logs page and apply any filters or time window.
2. Click the **⋮** (more actions) menu and select **Recalculate costs**.
3. Verify the confirmation dialog opens showing the count of logs missing a cost.
4. Switch to "All selected logs" and confirm the total log count is displayed.
5. Confirm with either mode and verify:
   - A loading toast appears immediately.
   - The toast updates with progress (`processed/total checked, N updated, N skipped`).
   - On completion, a success toast appears with the final counts.
   - If a recalculation is already running, a "already running" toast appears and polling attaches to the existing job.
6. Trigger a failure scenario and confirm an error toast is shown.

```sh
cd ui
pnpm i
pnpm build
```

## Screenshots/Recordings

_Add before/after screenshots of the recalculate costs dialog and toast progression._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth surfaces. The recalculate endpoint already requires credentials; the polling status endpoint follows the same auth model.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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