diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx
index 80ecd75ae4..a147ca1b86 100644
--- a/webview-ui/src/components/settings/ApiOptions.tsx
+++ b/webview-ui/src/components/settings/ApiOptions.tsx
@@ -496,7 +496,11 @@ const ApiOptions = ({
)}
{selectedProvider === "openai-native" && (
-
+
)}
{selectedProvider === "mistral" && (
diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx
index 5091fb1a68..7705bcf0a1 100644
--- a/webview-ui/src/components/settings/ModelInfoView.tsx
+++ b/webview-ui/src/components/settings/ModelInfoView.tsx
@@ -25,7 +25,14 @@ export const ModelInfoView = ({
}: ModelInfoViewProps) => {
const { t } = useAppTranslation()
- const infoItems = [
+ // Show tiered pricing table for OpenAI Native when model supports non-standard tiers
+ const allowedTiers =
+ (modelInfo?.allowedServiceTiers || []).filter((tier) => tier === "flex" || tier === "priority") ?? []
+ const tierPricing = modelInfo?.serviceTierPricing
+ const shouldShowTierPricingTable = apiProvider === "openai-native" && allowedTiers.length > 0 && !!tierPricing
+ const fmt = (n?: number) => (typeof n === "number" ? `${formatPrice(n)}` : "—")
+
+ const baseInfoItems = [
typeof modelInfo?.contextWindow === "number" && modelInfo.contextWindow > 0 && (
<>
{t("settings:modelInfo.contextWindow")}{" "}
@@ -53,6 +60,21 @@ export const ModelInfoView = ({
supportsLabel={t("settings:modelInfo.supportsPromptCache")}
doesNotSupportLabel={t("settings:modelInfo.noPromptCache")}
/>,
+ apiProvider === "gemini" && (
+
+ {selectedModelId.includes("pro-preview")
+ ? t("settings:modelInfo.gemini.billingEstimate")
+ : t("settings:modelInfo.gemini.freeRequests", {
+ count: selectedModelId && selectedModelId.includes("flash") ? 15 : 2,
+ })}{" "}
+
+ {t("settings:modelInfo.gemini.pricingDetails")}
+
+
+ ),
+ ].filter(Boolean)
+
+ const priceInfoItems = [
modelInfo?.inputPrice !== undefined && modelInfo.inputPrice > 0 && (
<>
{t("settings:modelInfo.inputPrice")}:{" "}
@@ -77,20 +99,10 @@ export const ModelInfoView = ({
{formatPrice(modelInfo.cacheWritesPrice || 0)} / 1M tokens
>
),
- apiProvider === "gemini" && (
-
- {selectedModelId.includes("pro-preview")
- ? t("settings:modelInfo.gemini.billingEstimate")
- : t("settings:modelInfo.gemini.freeRequests", {
- count: selectedModelId && selectedModelId.includes("flash") ? 15 : 2,
- })}{" "}
-
- {t("settings:modelInfo.gemini.pricingDetails")}
-
-
- ),
].filter(Boolean)
+ const infoItems = shouldShowTierPricingTable ? baseInfoItems : [...baseInfoItems, ...priceInfoItems]
+
return (
<>
{modelInfo?.description && (
@@ -106,6 +118,62 @@ export const ModelInfoView = ({
{item}
))}
+
+ {shouldShowTierPricingTable && (
+
+
+ Pricing by service tier (price per 1M tokens)
+
+
+
+
+
+ | Tier |
+ Input |
+ Output |
+ Cache reads |
+
+
+
+
+ | Standard |
+ {fmt(modelInfo?.inputPrice)} |
+ {fmt(modelInfo?.outputPrice)} |
+ {fmt(modelInfo?.cacheReadsPrice)} |
+
+ {allowedTiers.includes("flex") && (
+
+ | Flex |
+
+ {fmt(tierPricing?.flex?.inputPrice ?? modelInfo?.inputPrice)}
+ |
+
+ {fmt(tierPricing?.flex?.outputPrice ?? modelInfo?.outputPrice)}
+ |
+
+ {fmt(tierPricing?.flex?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice)}
+ |
+
+ )}
+ {allowedTiers.includes("priority") && (
+
+ | Priority |
+
+ {fmt(tierPricing?.priority?.inputPrice ?? modelInfo?.inputPrice)}
+ |
+
+ {fmt(tierPricing?.priority?.outputPrice ?? modelInfo?.outputPrice)}
+ |
+
+ {fmt(tierPricing?.priority?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice)}
+ |
+
+ )}
+
+
+
+
+ )}
>
)
}
diff --git a/webview-ui/src/components/settings/providers/OpenAI.tsx b/webview-ui/src/components/settings/providers/OpenAI.tsx
index e2f7857fe0..666d786e1f 100644
--- a/webview-ui/src/components/settings/providers/OpenAI.tsx
+++ b/webview-ui/src/components/settings/providers/OpenAI.tsx
@@ -2,19 +2,21 @@ import { useCallback, useState } from "react"
import { Checkbox } from "vscrui"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
-import type { ProviderSettings } from "@roo-code/types"
+import type { ModelInfo, ProviderSettings } from "@roo-code/types"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui"
import { inputEventTransform } from "../transforms"
type OpenAIProps = {
apiConfiguration: ProviderSettings
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+ selectedModelInfo?: ModelInfo
}
-export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIProps) => {
+export const OpenAI = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: OpenAIProps) => {
const { t } = useAppTranslation()
const [openAiNativeBaseUrlSelected, setOpenAiNativeBaseUrlSelected] = useState(
@@ -72,6 +74,44 @@ export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIPro
{t("settings:providers.getOpenAiApiKey")}
)}
+
+ {(() => {
+ const allowedTiers = (selectedModelInfo?.allowedServiceTiers || []).filter(
+ (t) => t === "flex" || t === "priority",
+ )
+ if (allowedTiers.length === 0) return null
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+ })()}
>
)
}