Skip to content

fix(onboarding): harmonise outbound model alias with rendered payload#5

Merged
aschkanAH merged 4 commits into
feat-onboardingfrom
ai-onboarding-model-alias-sync-e6a8
May 3, 2026
Merged

fix(onboarding): harmonise outbound model alias with rendered payload#5
aschkanAH merged 4 commits into
feat-onboardingfrom
ai-onboarding-model-alias-sync-e6a8

Conversation

@aschkanAH
Copy link
Copy Markdown
Owner

@aschkanAH aschkanAH commented May 3, 2026

Bug

Reported on staging:

Background Hello World batch failed: ApiError: Line 1: Model 'medgemma-4b'
has not been configured or is not available to user

The visible payload rendered against deepseek-ai/DeepSeek-V4-Pro (the user's actual catalog model), but the backend was being asked to run medgemma-4b.

Root cause

Onboarding.tsx had a single modelAlias value that read from useModels({ accessible: true }) and fell back to a hard-coded "medgemma-4b" while the catalog query was still loading. The background "Hello World" useEffect fired the moment auth resolved (almost always before useModels returned), captured the placeholder via closure, and POSTed medgemma-4b to /ai/v1/files + /ai/v1/batches. By the time the visible payload re-rendered with the real catalog alias the request had already gone out with the wrong model.

handleRunNow had the same latent bug if a user clicked "Run Now" before the catalog query resolved.

Fix

Single source of truth for the model alias

Value Behaviour while catalog loads Used for
runnableModelAlias undefined outbound requests (background batch + Run Now)
displayModelAlias <your-model-alias> placeholder rendered code samples / payload preview
When the catalog resolves with a chat model: both collapse to that alias.
  • The hard-coded "medgemma-4b" constant is gone. The display fallback is "<your-model-alias>" — obviously non-runnable, so it can't be mistaken for a real model.
  • The background batch waits for modelsLoading to clear, then either fires the toast + real batch using runnableModelAlias, or skips silently (no toast, no doomed POST) if the user has no entitled chat model.
  • The Run Now button is disabled until runnableModelAlias is populated, with a title tooltip explaining the wait. handleRunNow also defends against the disabled state being bypassed.
  • The visible payload is rendered against displayModelAlias, so as soon as the catalog returns the user sees the same alias the backend will receive.

Post-success redirect goes to the job route

Previously the auto-redirect after a successful run landed users on /models regardless of which workload they ran. Now:

  • /async when workloadType === "async".
  • /batches when workloadType === "batch".
  • /models fallback when the preferred route isn't accessible on this deployment (both routes are config-gated by batches.enabled / batches.async_requests.enabled; the destination is filtered through useAuthorization().canAccessRoute to avoid ProtectedRoute bouncing the user mid-redirect).

The "Skip to Dashboard" header button still goes to /models per the original spec. The CLI listener path also stays on /models because it's the "click to continue" simulation, not an actual workload — there's no specific job for the user to view there.

Inline success-strip copy now reflects the destination ("Taking you to your batch / async request…") so the redirect isn't surprising.

Out of scope

The other lines in the original console log (GET /authentication/register 404, POST /ai/v1/files 403) are environment / permissions issues unrelated to this PR.

QA

  • pnpm run lint clean.
  • pnpm exec tsc -b tsconfig.app.json clean.
  • pnpm test -- --run: 498 / 498 passing.
Open in Web Open in Cursor 

cursoragent and others added 3 commits May 3, 2026 03:04
The background 'Hello World' batch and the Run-Now handler were both
reading from a single `modelAlias` value that fell back to a hard-coded
`medgemma-4b` while the catalog query was still loading. Because the
background effect fired immediately after auth resolved (well before
`useModels` returned), the visible payload would later re-render with
the catalog alias (e.g. 'deepseek') while the backend received
'medgemma-4b' — producing 'Model medgemma-4b has not been configured or
is not available to user' on every onboarding load in environments
without that model.

Split the alias into two values:

  - displayModelAlias: always populated (real alias or placeholder) and
    used only for rendering code samples so the UI is never blank.
  - runnableModelAlias: undefined while the catalog is loading and when
    the user has no accessible chat model. Used for outbound requests.

The background batch now waits for the catalog to resolve and bails
silently (no toast, no request) when there's no entitled chat model.
Run Now does the same in its IIFE so the simulated success timer still
fires while we skip the doomed network call.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
The previous split of displayModelAlias / runnableModelAlias still left
'medgemma-4b' visible in the rendered code samples for the brief window
between mount and the useModels query resolving. Anyone reading the
payload during that window would reasonably expect medgemma to be the
model the backend is asked to run — exactly the divergence we set out
to remove.

Replace the hard-coded fallback with an obviously-non-runnable
'<your-model-alias>' placeholder, and disable the Run Now button until
runnableModelAlias is populated. The visible payload and the outbound
payload now share a single source of truth: when there's a real alias,
both reference it; when there isn't, the placeholder is shown and the
button is disabled.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
Previously the post-success auto-redirect always landed users on
/models — fine as a generic 'where would you like to go next' but it
hides the workload they just queued. Send them instead to the route
that will surface their job:

  - /async when workloadType is 'async'
  - /batches when workloadType is 'batch'

Both routes are config-gated by batches.enabled (and async further
gated by batches.async_requests.enabled), so we consult
useAuthorization().canAccessRoute and fall back to /models when the
preferred destination isn't accessible on this deployment, to avoid
ProtectedRoute bouncing the user mid-redirect.

The 'Skip to Dashboard' header button still lands on /models per the
original spec. The CLI listener path also stays on /models — that path
is the 'click to continue' simulation, not an actual workload, so
there's no specific job for the user to view.

Inline success-strip copy now reflects the destination ('Taking you to
your batch/async request…') so the redirect isn't surprising.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
@aschkanAH aschkanAH marked this pull request as ready for review May 3, 2026 08:00
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f134ea9. Configure here.

Comment thread dashboard/src/components/features/onboarding/Onboarding/Onboarding.tsx Outdated
!runnableModelAlias
? "Loading available models…"
: undefined
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tooltip says "loading" even when no models exist

Low Severity

The title tooltip checks only !runnableModelAlias and always shows "Loading available models…". Once modelsLoading becomes false and no CHAT model is found, runnableModelAlias stays undefined permanently, so the button remains disabled with a "loading" message that will never resolve. The condition conflates the "still loading" state with the "loaded but empty" state. Users with no accessible chat models see a perpetual loading message instead of an explanation that no models are available.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f134ea9. Configure here.

Including browserSuccessRoute in the auto-redirect effect's deps caused
a lost-redirect bug: if the value's identity changed during the 2s
window (workloadType toggle, useAuthorization refetch race, config
invalidation, …) the cleanup function ran clearTimeout, the effect
re-ran, and the idempotency ref short-circuited it before a new timer
could be scheduled. The user would see 'Taking you to your batch…'
indefinitely without ever navigating.

Move the destination into a ref that's updated on every render so the
trigger effect captures the latest value at success-time without
declaring a dep. Deps are now only the success triggers + navigate,
none of which churn between idle and success, so the cleanup-cancel
path is unreachable by construction.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
@aschkanAH aschkanAH merged commit 362dcaf into feat-onboarding May 3, 2026
1 check passed
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