diff --git a/backend/onyx/server/pat/models.py b/backend/onyx/server/pat/models.py index b4d796507c..bb6479d26d 100644 --- a/backend/onyx/server/pat/models.py +++ b/backend/onyx/server/pat/models.py @@ -7,6 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field, computed_field, field_validat from onyx.auth.permissions import resolve_effective_permissions from onyx.db.enums import Permission from onyx.db.permissions import parse_permission_values +from onyx.server.settings.models import Tier # Assignable token scopes, grouped by `group_label`. `implies` is derived from the # permission closure and only drives the UI; require_permission re-derives it at @@ -18,6 +19,9 @@ class PatScopeOption(BaseModel): group_label: str label: str description: str + # Minimum license tier at which the FE offers this scope. Not enforced at + # minting time; the gated API surface itself rejects lower tiers. + min_tier: Tier = Tier.COMMUNITY @computed_field @property @@ -50,6 +54,7 @@ _ASSIGNABLE_SCOPES: list[PatScopeOption] = [ group_label="LLM Gateway", label="Use", description="Call the LLM gateway from external tools.", + min_tier=Tier.ENTERPRISE, ), ] diff --git a/web/src/views/SettingsPage.tsx b/web/src/views/SettingsPage.tsx index f16386620b..137500a2d7 100644 --- a/web/src/views/SettingsPage.tsx +++ b/web/src/views/SettingsPage.tsx @@ -69,7 +69,8 @@ import { cn } from "@opal/utils"; import { Interactive } from "@opal/core"; import { useTierAtLeast } from "@/hooks/useTierAtLeast"; import { Tier } from "@/lib/settings/types"; -import { useIsSearchModeAvailable } from "@/lib/settings/hooks"; +import { useIsSearchModeAvailable, useSettings } from "@/lib/settings/hooks"; +import { tierAtLeast } from "@/lib/tiers"; import { Tooltip } from "@opal/components"; import { useCloudSubscription } from "@/hooks/useCloudSubscription"; import { useSmoothStreaming } from "@/hooks/useSmoothStreaming"; @@ -90,6 +91,7 @@ interface PatScopeOption { group_label: string; label: string; description: string; + min_tier: Tier; implies: string[]; } @@ -1363,13 +1365,22 @@ function AccountsAccessSettings() { } ); - const { data: scopeOptions = [], error: scopeOptionsError } = useSWR< + const { data: allScopeOptions = [], error: scopeOptionsError } = useSWR< PatScopeOption[] >( showTokensSection && canCreateTokens ? SWR_KEYS.userPatScopes : null, errorHandlingFetcher, { fallbackData: [] } ); + const currentTier = useSettings().tier; + const scopeOptions = useMemo( + () => + // Undefined tier (settings loading/failed) must not hide Community scopes. + allScopeOptions.filter((option) => + tierAtLeast(currentTier ?? Tier.COMMUNITY, option.min_tier) + ), + [allScopeOptions, currentTier] + ); const scopeLabels = useMemo( () =>