mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
feat(ui): OpenAI Native service tier dropdown + tiered pricing table; hide redundant price rows
This commit is contained in:
parent
c25cfdeaef
commit
5d6fc17e13
3 changed files with 128 additions and 16 deletions
|
|
@ -496,7 +496,11 @@ const ApiOptions = ({
|
|||
)}
|
||||
|
||||
{selectedProvider === "openai-native" && (
|
||||
<OpenAI apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
<OpenAI
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
selectedModelInfo={selectedModelInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "mistral" && (
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
<>
|
||||
<span className="font-medium">{t("settings:modelInfo.contextWindow")}</span>{" "}
|
||||
|
|
@ -53,6 +60,21 @@ export const ModelInfoView = ({
|
|||
supportsLabel={t("settings:modelInfo.supportsPromptCache")}
|
||||
doesNotSupportLabel={t("settings:modelInfo.noPromptCache")}
|
||||
/>,
|
||||
apiProvider === "gemini" && (
|
||||
<span className="italic">
|
||||
{selectedModelId.includes("pro-preview")
|
||||
? t("settings:modelInfo.gemini.billingEstimate")
|
||||
: t("settings:modelInfo.gemini.freeRequests", {
|
||||
count: selectedModelId && selectedModelId.includes("flash") ? 15 : 2,
|
||||
})}{" "}
|
||||
<VSCodeLink href="https://ai.google.dev/pricing" className="text-sm">
|
||||
{t("settings:modelInfo.gemini.pricingDetails")}
|
||||
</VSCodeLink>
|
||||
</span>
|
||||
),
|
||||
].filter(Boolean)
|
||||
|
||||
const priceInfoItems = [
|
||||
modelInfo?.inputPrice !== undefined && modelInfo.inputPrice > 0 && (
|
||||
<>
|
||||
<span className="font-medium">{t("settings:modelInfo.inputPrice")}:</span>{" "}
|
||||
|
|
@ -77,20 +99,10 @@ export const ModelInfoView = ({
|
|||
{formatPrice(modelInfo.cacheWritesPrice || 0)} / 1M tokens
|
||||
</>
|
||||
),
|
||||
apiProvider === "gemini" && (
|
||||
<span className="italic">
|
||||
{selectedModelId.includes("pro-preview")
|
||||
? t("settings:modelInfo.gemini.billingEstimate")
|
||||
: t("settings:modelInfo.gemini.freeRequests", {
|
||||
count: selectedModelId && selectedModelId.includes("flash") ? 15 : 2,
|
||||
})}{" "}
|
||||
<VSCodeLink href="https://ai.google.dev/pricing" className="text-sm">
|
||||
{t("settings:modelInfo.gemini.pricingDetails")}
|
||||
</VSCodeLink>
|
||||
</span>
|
||||
),
|
||||
].filter(Boolean)
|
||||
|
||||
const infoItems = shouldShowTierPricingTable ? baseInfoItems : [...baseInfoItems, ...priceInfoItems]
|
||||
|
||||
return (
|
||||
<>
|
||||
{modelInfo?.description && (
|
||||
|
|
@ -106,6 +118,62 @@ export const ModelInfoView = ({
|
|||
<div key={index}>{item}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{shouldShowTierPricingTable && (
|
||||
<div className="mt-2">
|
||||
<div className="text-xs text-vscode-descriptionForeground mb-1">
|
||||
Pricing by service tier (price per 1M tokens)
|
||||
</div>
|
||||
<div className="border border-vscode-dropdown-border rounded-xs overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-vscode-dropdown-background">
|
||||
<tr>
|
||||
<th className="text-left px-3 py-1.5">Tier</th>
|
||||
<th className="text-right px-3 py-1.5">Input</th>
|
||||
<th className="text-right px-3 py-1.5">Output</th>
|
||||
<th className="text-right px-3 py-1.5">Cache reads</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-t border-vscode-dropdown-border/60">
|
||||
<td className="px-3 py-1.5">Standard</td>
|
||||
<td className="px-3 py-1.5 text-right">{fmt(modelInfo?.inputPrice)}</td>
|
||||
<td className="px-3 py-1.5 text-right">{fmt(modelInfo?.outputPrice)}</td>
|
||||
<td className="px-3 py-1.5 text-right">{fmt(modelInfo?.cacheReadsPrice)}</td>
|
||||
</tr>
|
||||
{allowedTiers.includes("flex") && (
|
||||
<tr className="border-t border-vscode-dropdown-border/60">
|
||||
<td className="px-3 py-1.5">Flex</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.flex?.inputPrice ?? modelInfo?.inputPrice)}
|
||||
</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.flex?.outputPrice ?? modelInfo?.outputPrice)}
|
||||
</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.flex?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{allowedTiers.includes("priority") && (
|
||||
<tr className="border-t border-vscode-dropdown-border/60">
|
||||
<td className="px-3 py-1.5">Priority</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.priority?.inputPrice ?? modelInfo?.inputPrice)}
|
||||
</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.priority?.outputPrice ?? modelInfo?.outputPrice)}
|
||||
</td>
|
||||
<td className="px-3 py-1.5 text-right">
|
||||
{fmt(tierPricing?.priority?.cacheReadsPrice ?? modelInfo?.cacheReadsPrice)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")}
|
||||
</VSCodeButtonLink>
|
||||
)}
|
||||
|
||||
{(() => {
|
||||
const allowedTiers = (selectedModelInfo?.allowedServiceTiers || []).filter(
|
||||
(t) => t === "flex" || t === "priority",
|
||||
)
|
||||
if (allowedTiers.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 mt-2" data-testid="openai-service-tier">
|
||||
<div className="flex items-center gap-1">
|
||||
<label className="block font-medium mb-1">Service tier</label>
|
||||
<StandardTooltip content="For faster processing of API requests, try the priority processing service tier. For lower prices with higher latency, try the flex processing tier.">
|
||||
<i className="codicon codicon-info text-vscode-descriptionForeground text-xs" />
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
value={apiConfiguration.openAiNativeServiceTier || "default"}
|
||||
onValueChange={(value) =>
|
||||
setApiConfigurationField(
|
||||
"openAiNativeServiceTier",
|
||||
value as ProviderSettings["openAiNativeServiceTier"],
|
||||
)
|
||||
}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={t("settings:common.select")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="default">Standard</SelectItem>
|
||||
{allowedTiers.includes("flex") && <SelectItem value="flex">Flex</SelectItem>}
|
||||
{allowedTiers.includes("priority") && (
|
||||
<SelectItem value="priority">Priority</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue