发布

  • [OPIK-6040] [FE/BE] Deployment-level default rows-per-page for Experiments tables (#6463)

    frostbyte_neo 发布于 2026-04-28 10:18:09 +00:00

    • [OPIK-6040] docs: add design spec for deployment-level default page size

    Design for exposing a deployment-configurable default rows-per-page value
    to the v2 Experiments tables via a new /v1/private/ui-config endpoint and
    UIConfigProvider. User in-session overrides flow through the URL query
    param only; no localStorage persistence (per customer requirement).

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] docs: clarify ui-config cache scope in design spec

    Deployment-level config is identical across workspaces; clarify that the
    React Query cache key is global, not workspace-scoped.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] docs: add implementation plan for deployment default page size

    13 bite-sized tasks covering backend config class + resource + test,
    frontend API hook + provider + test + wiring, three v2 Experiments
    consumers, Helm values entry, and a manual verification gate.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(backend): add UIConfig with defaultPageSize

    Introduce a Dropwizard UIConfig class that holds deployment-level UI
    defaults. Field defaultPageSize (validated @Min(1), default 100) lets
    admins lower the global default rows-per-page for table views.

    • [OPIK-6040] refactor(backend): bound defaultPageSize with @Max(1000)

    Defense-in-depth cap against misconfigured deployments; the UI dropdown
    caps at 100 today and 1000 is a generous ceiling. Also splits @Min onto
    its own line to match plan style.

    • [OPIK-6040] feat(backend): expose UIConfig via /v1/private/ui-config

    Add UIConfigResource mirroring ServiceTogglesResource. The endpoint is
    auto-discovered by Guicey (enableAutoConfig) and serves the uiConfig
    block from OpikConfiguration.

    • [OPIK-6040] docs(plan): use snake_case wire format for UIConfig

    Backend ObjectMapper applies SnakeCaseStrategy globally, so
    defaultPageSize serializes as default_page_size on the wire. Update the
    frontend type, provider default, test fixtures, curl expectation, and
    consumers to match, aligning with the rest of the codebase's
    snake_case API types.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] test(backend): unit test UIConfigResource returns config value

    Cover the custom-value and default-value paths. Kept as a unit test to
    avoid booting ClickHouse/MySQL/Redis for a read-only config endpoint.

    • [OPIK-6040] test(backend): add @Max(1000) bounds test for UIConfig

    Closes Task 3 code review gap: exercises both @Min(1) and @Max(1000)
    bean validation constraints using a local Validator, ensuring a
    silent removal of @Max(1000) would break the build. Also drops the
    redundant assertion in returnsConfiguredDefaultPageSize.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(backend): wire UI_DEFAULT_PAGE_SIZE env var

    Add uiConfig block to config.yml with env var substitution so
    operators can override the default rows-per-page at deploy time
    without a rebuild.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): add UIConfig type, endpoint, and fetch hook

    Cache key is global ("ui-config") because the value is deployment-wide
    and does not vary per workspace. The second query-key element is an
    empty params object to satisfy the project's QueryConfig tuple shape
    ([string, Record<string, unknown>, ...string[]]).

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): add UIConfigProvider context

    Exposes deployment UI config to the component tree with a 100-default
    fallback so consumers always have a valid number during fetch / on
    error.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] test(frontend): cover UIConfigProvider success and fallback paths

    Verifies the provider exposes fetched data and falls back to
    default_page_size=100 when the query is loading or errors.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): mount UIConfigProvider in v2 WorkspaceGuard

    Provider is nested inside FeatureTogglesProvider since the UI config
    fetch is independent of feature toggles and should cover the same set
    of workspace-scoped layouts.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): use deployment default in v2 experiments list

    Replace useQueryParamAndLocalStorageState with useQueryParam for the
    page-size state. The deployment default comes from UIConfigProvider and
    is no longer persisted to localStorage; in-session override lives only
    on the URL query param per customer requirement.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): use deployment default in experiment items viewer

    Only the page-size state drops localStorage persistence; row height,
    sorting, and other items-viewer state remain in localStorage per the
    existing UX contract.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(frontend): use deployment default in prompt experiments tab

    Replace useQueryParamAndLocalStorageState with useQueryParam for the
    page-size state so the v2 prompt experiments tab honors the deployment
    default and drops stale localStorage dependency.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] feat(helm): expose UI_DEFAULT_PAGE_SIZE as a backend env var

    Operators can lower the default rows-per-page (e.g. to 25) without
    rebuilding, and the backend substitutes the value into config.yml at
    startup.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] refactor(frontend): simplify useUIConfig to a stable singleton key

    Drop the unused options parameter and the object-padded tuple key so the
    hook mirrors useIsAlive: a single-element queryKey (["ui-config"]) for a
    deployment-wide value that never varies by caller. Avoids confusion
    around passing an empty params object only to satisfy QueryConfig's
    tuple shape.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] [BE] test: add HTTP integration test + boundary cases for UIConfig
    • Add wireFormatIsSnakeCase test using ResourceExtension to verify
      the API serializes default_page_size as snake_case end-to-end.
      This catches Jackson naming-strategy drift regardless of
      annotation style on the config class.
    • Extend the bean-validation test to cover boundaries (1 and 1000)
      and a typical in-range value (500), not just out-of-bounds cases.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] [DEPLOY] docs: cross-reference dual UI_DEFAULT_PAGE_SIZE defaults

    Both apps/opik-backend/config.yml and deployment/helm_chart/opik/values.yaml
    carry a literal fallback of 100 for UI_DEFAULT_PAGE_SIZE. Add reciprocal
    comments in each file so anyone changing one default is prompted to
    update the other, preventing silent drift between the backend fallback
    and the Helm default.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] [FE] test: verify ?size URL param overrides UIConfig default

    Add an integration test that exercises the exact coercion pattern used
    by v2 Experiments tables (GeneralDatasetsTab, PromptPage/ExperimentsTab,
    useExperimentItemsState):

    const size = sizeParam ?? defaultPageSize;

    Two cases under real use-query-params + UIConfigProvider:

    • No ?size in URL → provider default_page_size wins
    • ?size=50 in URL → URL value wins over provider default

    This guards the documented override semantics so later refactors
    cannot silently invert the precedence.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] refactor: reuse ServiceTogglesConfig + shared hook for page-size default

    Addresses PR review: drop the parallel /v1/private/ui-config/ endpoint in
    favor of ServiceTogglesConfig (consumed through the existing feature-toggles
    provider), and wire the deployment default into both v1 and v2 experiment
    tables.

    Backend

    • ServiceTogglesConfig gains @Min(1) @Max(1000) int defaultPageSize = 100
    • config.yml binds it to UI_DEFAULT_PAGE_SIZE with a cross-ref to Helm
    • Remove UIConfig, UIConfigResource, and UIConfigResourceTest

    Frontend

    • FeatureTogglesProvider exposes useDefaultPageSize(), reading the
      snake_case default_page_size field from /v1/private/toggles/
    • New shared hook useTablePageSize() centralizes URL + fallback wiring and
      hardens against malformed ?size= input (NaN, 0, negative, fractional)
    • v2 experiment call sites adopt useTablePageSize() directly
    • v1 experiment call sites pass useDefaultPageSize() as the
      useQueryParamAndLocalStorageState defaultValue (preserves localStorage)
    • Remove UIConfigProvider, useUIConfig, ui-config types, and related tests

    Docs / deploy

    • Delete internal planning docs from docs/superpowers/
    • Update Helm cross-reference comment to serviceToggles.defaultPageSize

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] fix: fold v1 tables into shared useTablePageSize hook

    Baz review flagged a race in v1 pagination: useQueryParamAndLocalStorageState
    reads defaultValue only at first render, so before the feature-toggles fetch
    resolves the hook seeds localStorage with the fallback (100). When the
    deployment value arrives later, localStorage already holds 100 and the new
    default is never reflected.

    Fix: drop localStorage persistence for the page-size on v1 and adopt the same
    useTablePageSize() hook v2 uses. URL ?size= still wins; otherwise each
    render resolves the current deployment default. This also collapses the
    three duplicated useQueryParamAndLocalStorageState blocks.

    Also regenerates deployment/helm_chart/opik/README.md (helm-docs v1.14.2) to
    include the new UI_DEFAULT_PAGE_SIZE env var row, unblocking the
    update-readme CI check.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] [BE] address review: drop java default, tighten bounds, sync configs
    • ServiceTogglesConfig: drop = 100 default (sourced from YAML), bounds 5..100
      to match the UI's existing min and current max.
    • config.yml: remove cross-reference NOTE.
    • config-test.yml: bring comments in line with prod (convention: identical
      except for env vars).
    • values.yaml: drop comments (config.yml is the source of truth).

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] [FE] restore v1 cross-session page-size persistence

    The previous v1 fold onto useTablePageSize traded localStorage persistence
    to dodge the init race against the toggles fetch. v1 users (the actual
    target of this PR) lost their cross-session page-size preference.

    Add useTablePageSizeWithStorage(localStorageKey) variant: URL → localStorage
    → deployment default. Pass NO defaultValue to useLocalStorageState so the
    FE fallback (100) is never seeded into storage on first render — only
    user-picked values get written. That avoids the race the previous commit
    was working around.

    v2 keeps the URL-only useTablePageSize.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] address final review: widen bounds, lock wire format, fold v2 keys
    • BE: ServiceTogglesConfig.defaultPageSize bounds widened to @Min(10)
      @Max(1000) to match documented operator surface (was @Min(5) @Max(100),
      which left zero headroom above the 100 Helm default).
    • BE: add ServiceTogglesConfigTest pinning the default_page_size snake_case
      wire format - guards against silent FE fallback if the field is renamed
      or the global SnakeCaseStrategy regresses.
    • FE: extend FeatureToggles type with default_page_size?: number; drop the
      load-bearing (data as unknown) cast in feature-toggles-provider.
    • FE: tighten provider guard to Number.isInteger(raw) && raw >= 10 to
      match the new backend floor.
    • FE: collapse useTablePageSize / useTablePageSizeWithStorage into a
      single useTablePageSize(localStorageKey) export; restore the v2
      pagination-size localStorage keys (experiments-pagination-size,
      prompt-experiments-pagination-size, ${storagePrefix}-pagination-size)
      that an earlier iteration dropped, so v2 users keep cross-session
      page-size persistence at parity with v1.
    • FE: comment the in-flight-fetch race in useTablePageSize so the
      no-defaultValue-to-useLocalStorageState pattern is not naively
      "simplified" away.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

    • [OPIK-6040] address review: clamp FE upper bound + sync helm-docs
    • feature-toggles-provider: enforce raw <= 1000 to mirror BE bounds; out-of-range
      values fall through to the 100 fallback (baz-reviewer Low).
    • helm_chart README: regenerate via helm-docs v1.14.2 to pick up Chart.yaml 2.0.15
      (unblocks update-readme CI).
    • [OPIK-6040] address review: revert bounds to 5..100, drop snake_case test
    • ServiceTogglesConfig: bounds back to @Min(5) @Max(100) per the originally
      agreed UI min/max; the 10..1000 widening was unnecessary.
    • feature-toggles-provider: tighten guard to raw >= 5 && raw <= 100 so the
      FE mirrors the BE bounds.
    • Drop ServiceTogglesConfigTest (snake_case wire-format pin) per review -
      low-value coverage.

    Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com


    Co-authored-by: Claude Opus 4.7 noreply@anthropic.com

    下载附件