发布

  • [OPIK-6066] [FE] perf: improve first load time (cache + v2 default + lazy pages) (#6429)

    frostbyte_neo 发布于 2026-04-22 11:40:16 +00:00

    • [OPIK-6066] [FE] perf: cache workspace version per workspace, default to v2

    Post-signup users landing on / (no workspace in URL) used to optimistically
    load V1App, then hard-reload into V2App once WorkspaceVersionResolver fetched
    the real version from the API — paying the cost twice.

    This change adds a per-workspace opik-workspace-versions localStorage map
    populated whenever the API resolves a version (in WorkspaceVersionGate's
    async path and in WorkspaceVersionResolver's React Query effect). The sync
    resolver now consults the cache before falling back to a Loader, so
    returning users see the correct App on first paint with no API round-trip.

    The optimistic default for the no-workspace case is also flipped from v1 to
    v2, matching the direction of the product. The existing reload-on-mismatch
    self-correction is intentionally left in place to handle stale-cache cases
    (e.g. a workspace migrating from v1 to v2) — the reload writes the corrected
    version to the cache before reloading, so the next visit loads the right
    App synchronously.

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

    • [OPIK-6066] [FE] perf: swap v1/v2 App in place instead of reloading

    WorkspaceVersionResolver was calling window.location.reload() when the
    gate's optimistic version disagreed with the API — throwing away React
    state and re-running the entire bootstrap. Since the Resolver already
    updates the store (which the gate subscribes to via useWorkspaceVersion),
    removing the reload lets React.lazy + Suspense swap V1App ↔ V2App in
    place.

    Worst-case version-mismatch cost drops from "full page reload + JS
    reparse + auth waterfall re-run" to "lazy chunk load + subtree remount".
    Cache write stays on every API resolution so the gate's sync path is
    correct on the next visit.

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

    • [OPIK-6066] [FE] fix: restore reload on version mismatch to avoid router URL desync

    The in-place V1App↔V2App swap introduced in the previous commit caused an
    infinite version-swap loop when users switched between workspaces of
    different versions.

    Root cause: each App module creates its own TanStack Router instance at
    module scope (src/v1/router.tsx, src/v2/router.tsx). These routers have
    internal URL state that does not auto-sync when unmounts
    and later remounts. When V2App navigated to a new URL and then V1App
    mounted, V1's router still reported the previous URL. Its
    WorkspacePreloader consequently set activeWorkspaceName to the stale
    workspace, the Resolver fetched the wrong version, and the gate kept
    swapping apps indefinitely.

    Restoring window.location.reload() on mismatch resolves the loop by
    re-evaluating every module against the current browser URL, so both
    routers start from a consistent state. The Tier 0 cache + #3.5 gate
    simplification remain in place; reload path stays until a proper
    cross-App router-sync fix ships.

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

    • [OPIK-6066] [FE] perf: lazy-load heaviest non-critical page routes

    Converts the heaviest page imports in v1/router.tsx and v2/router.tsx
    from static imports to React.lazy() so each page becomes its own chunk,
    loaded only on navigation. Follows the SMEFlowPage precedent in both
    routers.

    Pages converted:

    • v1: PlaygroundPage, PromptPage, CompareExperimentsPage, AlertsRouteWrapper
    • v2: PlaygroundPage, CompareExperimentsPage, AlertsRouteWrapper

    TracesPage intentionally kept eager — it's on the critical path and
    lazy-loading would cause a visible Loader flash on most sessions.

    Measured impact (production build, dist/assets):

    • V1App chunk: 932 KB -> 801 KB (-131 KB, -14%)
    • V2App chunk: 1074 KB -> 955 KB (-119 KB, -11%)
    • Combined initial App payload: -250 KB
    • Total JS across all chunks: +52 KB (+0.4%) — Rollup duplicates some
      small shared modules across chunks; acceptable tax for the initial
      payload reduction.

    App-level <Suspense fallback={}> in WorkspaceVersionGate
    covers the fallback for all new lazy route components — no new
    Suspense boundaries needed.

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

    • [OPIK-6066] [FE] fix: verify workspace version on pair routes

    Pair routes (/pair/v1 and the /opik/pair/v1 OSS alias) render outside
    WorkspaceGuard — they must work without auth — so WorkspaceVersionResolver
    was never mounted for them. Combined with OPIK-6066's optimistic v2
    default, a v1-workspace user clicking an SDK-generated pair link with a
    cold cache would have landed on V2App's PairingPage instead of V1App's
    "upgrade required" screen.

    Adds PairRouteVersionGuard which seeds activeWorkspaceName from the
    pair URL's ?workspace= query and mounts WorkspaceVersionResolver. The
    Resolver's existing API check + reload-on-mismatch flow now runs for
    pair routes too:

    • v2 workspace + cache miss: V2 PairingPage renders immediately,
      Resolver confirms v2 in background, no reload.
    • v1 workspace + cache miss: gate picks v2 optimistically → Resolver
      detects v1 → reload → V1 PairV1Page (upgrade-required) renders.
    • cache hit: correct App on first paint, Resolver confirms.

    Caught by baz-reviewer[bot] on PR #6429.

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


    Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com

    下载附件