Roo-Code/webview-ui/src/components/settings/ModelInfoView.tsx

126 lines
3.9 KiB
TypeScript

import { useMemo } from "react"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { formatPrice } from "@/utils/formatPrice"
import { cn } from "@/lib/utils"
import { ModelInfo, geminiModels } from "../../../../src/shared/api"
import { ModelDescriptionMarkdown } from "./ModelDescriptionMarkdown"
type ModelInfoViewProps = {
selectedModelId: string
modelInfo: ModelInfo
isDescriptionExpanded: boolean
setIsDescriptionExpanded: (isExpanded: boolean) => void
}
export const ModelInfoView = ({
selectedModelId,
modelInfo,
isDescriptionExpanded,
setIsDescriptionExpanded,
}: ModelInfoViewProps) => {
const { t } = useAppTranslation()
const isGemini = useMemo(() => Object.keys(geminiModels).includes(selectedModelId), [selectedModelId])
const infoItems = [
<ModelInfoSupportsItem
isSupported={modelInfo.supportsImages ?? false}
supportsLabel={t("settings:modelInfo.supportsImages")}
doesNotSupportLabel={t("settings:modelInfo.noImages")}
/>,
<ModelInfoSupportsItem
isSupported={modelInfo.supportsComputerUse ?? false}
supportsLabel={t("settings:modelInfo.supportsComputerUse")}
doesNotSupportLabel={t("settings:modelInfo.noComputerUse")}
/>,
!isGemini && (
<ModelInfoSupportsItem
isSupported={modelInfo.supportsPromptCache}
supportsLabel={t("settings:modelInfo.supportsPromptCache")}
doesNotSupportLabel={t("settings:modelInfo.noPromptCache")}
/>
),
typeof modelInfo.maxTokens === "number" && modelInfo.maxTokens > 0 && (
<>
<span className="font-medium">{t("settings:modelInfo.maxOutput")}:</span>{" "}
{modelInfo.maxTokens?.toLocaleString()} tokens
</>
),
modelInfo.inputPrice !== undefined && modelInfo.inputPrice > 0 && (
<>
<span className="font-medium">{t("settings:modelInfo.inputPrice")}:</span>{" "}
{formatPrice(modelInfo.inputPrice)} / 1M tokens
</>
),
modelInfo.outputPrice !== undefined && modelInfo.outputPrice > 0 && (
<>
<span className="font-medium">{t("settings:modelInfo.outputPrice")}:</span>{" "}
{formatPrice(modelInfo.outputPrice)} / 1M tokens
</>
),
modelInfo.supportsPromptCache && modelInfo.cacheReadsPrice && (
<>
<span className="font-medium">{t("settings:modelInfo.cacheReadsPrice")}:</span>{" "}
{formatPrice(modelInfo.cacheReadsPrice || 0)} / 1M tokens
</>
),
modelInfo.supportsPromptCache && modelInfo.cacheWritesPrice && (
<>
<span className="font-medium">{t("settings:modelInfo.cacheWritesPrice")}:</span>{" "}
{formatPrice(modelInfo.cacheWritesPrice || 0)} / 1M tokens
</>
),
isGemini && (
<span className="italic">
{selectedModelId === "gemini-2.5-pro-preview-03-25"
? 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)
return (
<>
{modelInfo.description && (
<ModelDescriptionMarkdown
key="description"
markdown={modelInfo.description}
isExpanded={isDescriptionExpanded}
setIsExpanded={setIsDescriptionExpanded}
/>
)}
<div className="text-sm text-vscode-descriptionForeground">
{infoItems.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
</>
)
}
const ModelInfoSupportsItem = ({
isSupported,
supportsLabel,
doesNotSupportLabel,
}: {
isSupported: boolean
supportsLabel: string
doesNotSupportLabel: string
}) => (
<div
className={cn(
"flex items-center gap-1 font-medium",
isSupported ? "text-vscode-charts-green" : "text-vscode-errorForeground",
)}>
<span className={cn("codicon", isSupported ? "codicon-check" : "codicon-x")} />
{isSupported ? supportsLabel : doesNotSupportLabel}
</div>
)