From f0751a614719fa25e21d5eb0a34315f1f31969c9 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 10:05:18 +0000 Subject: [PATCH 01/18] First pass at provider pricing page --- .../pricing/components/model-card.tsx | 89 ++++++++ .../pricing/components/search-bar.tsx | 23 ++ .../pricing/components/sort-dropdown.tsx | 29 +++ .../src/app/provider/pricing/page.tsx | 212 ++++++++++++++++++ apps/web-roo-code/src/lib/formatters.ts | 22 ++ apps/web-roo-code/src/lib/types/models.ts | 30 +++ 6 files changed, 405 insertions(+) create mode 100644 apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx create mode 100644 apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx create mode 100644 apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx create mode 100644 apps/web-roo-code/src/app/provider/pricing/page.tsx create mode 100644 apps/web-roo-code/src/lib/formatters.ts create mode 100644 apps/web-roo-code/src/lib/types/models.ts diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx new file mode 100644 index 0000000000..b6a2b69eeb --- /dev/null +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -0,0 +1,89 @@ +import { ModelWithTotalPrice } from "@/lib/types/models" +import { formatCurrency, formatTokens } from "@/lib/formatters" + +interface ModelCardProps { + model: ModelWithTotalPrice +} + +export function ModelCard({ model }: ModelCardProps) { + const inputPrice = parseFloat(model.pricing.input) + const outputPrice = parseFloat(model.pricing.output) + const cacheReadPrice = parseFloat(model.pricing.input_cache_read) + const cacheWritePrice = parseFloat(model.pricing.input_cache_write) + + return ( +
+
+

{model.name}

+

{model.description}

+
+ + {model.tags.length > 0 && ( +
+ {model.tags.map((tag) => ( + + {tag} + + ))} +
+ )} + +
+ + + + + + + + + + + + + + + + + + + + + + + {(cacheReadPrice > 0 || cacheWritePrice > 0) && ( + <> + + + + + + + + + + )} + + + + + +
Provider{model.owned_by || "N/A"}
Context Window{formatTokens(model.context_window)}
Max Output Tokens{formatTokens(model.max_tokens)}
Input Price + {inputPrice === 0 ? "Free" : `${formatCurrency(inputPrice)}/1M tokens`} +
Output Price + {outputPrice === 0 ? "Free" : `${formatCurrency(outputPrice)}/1M tokens`} +
Cache Read + {cacheReadPrice === 0 ? "Free" : `${formatCurrency(cacheReadPrice)}/1M tokens`} +
Cache Write + {cacheWritePrice === 0 + ? "Free" + : `${formatCurrency(cacheWritePrice)}/1M tokens`} +
Total Price + {model.totalPrice === 0 ? "Free" : `${formatCurrency(model.totalPrice)}/1M tokens`} +
+
+
+ ) +} diff --git a/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx b/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx new file mode 100644 index 0000000000..e94eedd791 --- /dev/null +++ b/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx @@ -0,0 +1,23 @@ +"use client" + +import { Search } from "lucide-react" + +interface SearchBarProps { + value: string + onChange: (value: string) => void +} + +export function SearchBar({ value, onChange }: SearchBarProps) { + return ( +
+ + onChange(e.target.value)} + className="w-full rounded-lg border border-input bg-background px-10 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> +
+ ) +} diff --git a/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx b/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx new file mode 100644 index 0000000000..f02be2b010 --- /dev/null +++ b/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx @@ -0,0 +1,29 @@ +"use client" + +import { SortOption } from "@/lib/types/models" + +interface SortDropdownProps { + value: SortOption + onChange: (value: SortOption) => void +} + +export function SortDropdown({ value, onChange }: SortDropdownProps) { + return ( +
+ + +
+ ) +} diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx new file mode 100644 index 0000000000..905539e920 --- /dev/null +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -0,0 +1,212 @@ +"use client" + +import { useEffect, useMemo, useState } from "react" +import { AnimatedBackground } from "@/components/homepage" +import { ModelCard } from "./components/model-card" +import { SearchBar } from "./components/search-bar" +import { SortDropdown } from "./components/sort-dropdown" +import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/types/models" +import Link from "next/link" + +const API_URL = "https://api.roocode.com/proxy/v1/models" + +const faqs = [ + { + question: "What are AI model providers?", + answer: "AI model providers offer various language models with different capabilities and pricing.", + }, + { + question: "How is pricing calculated?", + answer: "Pricing is based on token usage for input and output, measured per million tokens.", + }, + { + question: "Wehat is the Roo Code Cloud Provider?", + answer: ( + <> +

This is our very own model provider, optimized to work seamlessly with Roo Code Cloud.

+

+ It offers a selection of state-of-the-art LLMs (both closed and open weight) we know work well with + Roo for you to choose, with no markup. +

+

+ We also often feature 100% free models which labs share with us for the community to use and provide + feedback. +

+ + ), + }, +] + +function calculateTotalPrice(model: Model): number { + return parseFloat(model.pricing.input) + parseFloat(model.pricing.output) +} + +function enrichModelWithTotalPrice(model: Model): ModelWithTotalPrice { + return { + ...model, + totalPrice: calculateTotalPrice(model), + } +} + +export default function ProviderPricingPage() { + const [models, setModels] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [searchQuery, setSearchQuery] = useState("") + const [sortOption, setSortOption] = useState("alphabetical") + + useEffect(() => { + async function fetchModels() { + try { + setLoading(true) + setError(null) + const response = await fetch(API_URL) + if (!response.ok) { + throw new Error(`Failed to fetch models: ${response.statusText}`) + } + const data: ModelsResponse = await response.json() + const enrichedModels = data.data.map(enrichModelWithTotalPrice) + setModels(enrichedModels) + } catch (err) { + setError(err instanceof Error ? err.message : "An error occurred while fetching models") + } finally { + setLoading(false) + } + } + + fetchModels() + }, []) + + const filteredAndSortedModels = useMemo(() => { + let filtered = models + + // Filter by search query + if (searchQuery.trim()) { + const query = searchQuery.toLowerCase() + filtered = models.filter((model) => { + return ( + model.name.toLowerCase().includes(query) || + model.owned_by.toLowerCase().includes(query) || + model.description.toLowerCase().includes(query) + ) + }) + } + + // Sort filtered results + const sorted = [...filtered] + switch (sortOption) { + case "alphabetical": + sorted.sort((a, b) => a.name.localeCompare(b.name)) + break + case "price-asc": + sorted.sort((a, b) => a.totalPrice - b.totalPrice) + break + case "price-desc": + sorted.sort((a, b) => b.totalPrice - a.totalPrice) + break + case "context-window-asc": + sorted.sort((a, b) => a.context_window - b.context_window) + break + case "context-window-desc": + sorted.sort((a, b) => b.context_window - a.context_window) + break + } + + return sorted + }, [models, searchQuery, sortOption]) + + return ( + <> + + + {/* Hero Section */} +
+
+
+

Roo Code Cloud Provider Pricing

+

+ See pricing and features for all models we support. +
+ + Looking for general Roo Code Cloud pricing? + +

+
+
+
+ + {/* Search and Sort Controls */} +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + {/* Models Grid */} +
+
+
+ {loading && ( +
+

Loading models...

+
+ )} + + {error && ( +
+

Error: {error}

+
+ )} + + {!loading && !error && filteredAndSortedModels.length === 0 && ( +
+

+ No models found matching your search criteria. +

+
+ )} + + {!loading && !error && filteredAndSortedModels.length > 0 && ( + <> +
+ Showing {filteredAndSortedModels.length} of {models.length} models +
+
+ {filteredAndSortedModels.map((model) => ( + + ))} +
+ + )} +
+
+
+ + {/* FAQ Section */} +
+
+
+

Frequently Asked Questions

+
+
+ {faqs.map((faq, index) => ( +
+

{faq.question}

+

{faq.answer}

+
+ ))} +
+
+
+ + ) +} diff --git a/apps/web-roo-code/src/lib/formatters.ts b/apps/web-roo-code/src/lib/formatters.ts new file mode 100644 index 0000000000..05235a0222 --- /dev/null +++ b/apps/web-roo-code/src/lib/formatters.ts @@ -0,0 +1,22 @@ +const formatter = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", +}) + +export const formatCurrency = (amount: number) => formatter.format(amount) + +export const formatTokens = (tokens: number) => { + if (tokens < 1000) { + return tokens.toString() + } + + if (tokens < 1000000) { + return `${(tokens / 1000).toFixed(1)}k` + } + + if (tokens < 1000000000) { + return `${(tokens / 1000000).toFixed(1)}M` + } + + return `${(tokens / 1000000000).toFixed(1)}B` +} diff --git a/apps/web-roo-code/src/lib/types/models.ts b/apps/web-roo-code/src/lib/types/models.ts new file mode 100644 index 0000000000..b405101383 --- /dev/null +++ b/apps/web-roo-code/src/lib/types/models.ts @@ -0,0 +1,30 @@ +export interface ModelPricing { + input: string + output: string + input_cache_read: string + input_cache_write: string +} + +export interface Model { + id: string + object: string + created: number + owned_by: string + name: string + description: string + context_window: number + max_tokens: number + type: string + tags: string[] + pricing: ModelPricing +} + +export interface ModelsResponse { + data: Model[] +} + +export interface ModelWithTotalPrice extends Model { + totalPrice: number +} + +export type SortOption = "alphabetical" | "price-asc" | "price-desc" | "context-window-asc" | "context-window-desc" From 958430bbce97f2eaa75314073ed39d7b7e257496 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 10:07:20 +0000 Subject: [PATCH 02/18] Small fix --- apps/web-roo-code/src/app/pricing/page.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/web-roo-code/src/app/pricing/page.tsx b/apps/web-roo-code/src/app/pricing/page.tsx index 9985881e1d..40f5d434e8 100644 --- a/apps/web-roo-code/src/app/pricing/page.tsx +++ b/apps/web-roo-code/src/app/pricing/page.tsx @@ -267,11 +267,24 @@ export default function PricingPage() {

-

How do Cloud Agent credits work?

+

How do credits work?

- Cloud Agents are a version of Roo running in the cloud without depending on your IDE. - You can run as many as you want, and bring your own inference provider key. + Roo Code Cloud credits can be used in two ways:

+
    +
  • To pay for Cloud Agents running time (${PRICE_CREDITS}/hour)
  • +
  • + To pay for AI model inference costs ( + + varies by model + + ) +
  • +

To cover our infrastructure costs, we charge ${PRICE_CREDITS}/hour while the agent is running (independent of inference costs). From 1ee57b017085c66cbee112c7db616aa8c7e47dec Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 10:25:00 +0000 Subject: [PATCH 03/18] Improvements to filtering/sorting/errors --- .../src/app/provider/pricing/page.tsx | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 905539e920..f38dc8241a 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -3,10 +3,9 @@ import { useEffect, useMemo, useState } from "react" import { AnimatedBackground } from "@/components/homepage" import { ModelCard } from "./components/model-card" -import { SearchBar } from "./components/search-bar" -import { SortDropdown } from "./components/sort-dropdown" import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/types/models" import Link from "next/link" +import { ChevronDown, CircleX, Loader, LoaderCircle, Search } from "lucide-react" const API_URL = "https://api.roocode.com/proxy/v1/models" @@ -141,10 +140,36 @@ export default function ProviderPricingPage() {

- +
+ + setSearchQuery(e.target.value)} + className="w-full rounded-full border border-input bg-background px-10 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> + +
+ {filteredAndSortedModels.length} of {models.length} models +
+
- +
+ + +
@@ -156,30 +181,35 @@ export default function ProviderPricingPage() {
{loading && ( -
-

Loading models...

+
+ +

Loading model list...

)} {error && ( -
-

Error: {error}

+
+ +

Oops, couldn't load the model list.

+

Try again in a bit please.

)} {!loading && !error && filteredAndSortedModels.length === 0 && ( -
-

- No models found matching your search criteria. +

+ +

No models match your search.

+

+ Keep in mind we don't have every model under the sun – only the ones we think + are worth using. +
+ You can always use a third-party provider to access a wider selection.

)} {!loading && !error && filteredAndSortedModels.length > 0 && ( <> -
- Showing {filteredAndSortedModels.length} of {models.length} models -
{filteredAndSortedModels.map((model) => ( From 17d7efce243b4464549960dbde10e7fc4469d6af Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 10:52:30 +0000 Subject: [PATCH 04/18] Styling and refactoring --- .../pricing/components/model-card.tsx | 22 +++++++-- .../pricing/components/search-bar.tsx | 23 --------- .../pricing/components/sort-dropdown.tsx | 29 ----------- .../src/app/provider/pricing/page.tsx | 49 ++++++++++--------- 4 files changed, 44 insertions(+), 79 deletions(-) delete mode 100644 apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx delete mode 100644 apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index b6a2b69eeb..dbd61fd114 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -1,5 +1,6 @@ import { ModelWithTotalPrice } from "@/lib/types/models" import { formatCurrency, formatTokens } from "@/lib/formatters" +import { Gift } from "lucide-react" interface ModelCardProps { model: ModelWithTotalPrice @@ -10,11 +11,20 @@ export function ModelCard({ model }: ModelCardProps) { const outputPrice = parseFloat(model.pricing.output) const cacheReadPrice = parseFloat(model.pricing.input_cache_read) const cacheWritePrice = parseFloat(model.pricing.input_cache_write) + const free = model.tags.includes("free") return (
-

{model.name}

+

+ {model.name} + {free && ( + + + Free! + + )} +

{model.description}

@@ -33,10 +43,12 @@ export function ModelCard({ model }: ModelCardProps) {
- - - - + {model.owned_by && ( + + + + + )} diff --git a/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx b/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx deleted file mode 100644 index e94eedd791..0000000000 --- a/apps/web-roo-code/src/app/provider/pricing/components/search-bar.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client" - -import { Search } from "lucide-react" - -interface SearchBarProps { - value: string - onChange: (value: string) => void -} - -export function SearchBar({ value, onChange }: SearchBarProps) { - return ( -
- - onChange(e.target.value)} - className="w-full rounded-lg border border-input bg-background px-10 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" - /> -
- ) -} diff --git a/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx b/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx deleted file mode 100644 index f02be2b010..0000000000 --- a/apps/web-roo-code/src/app/provider/pricing/components/sort-dropdown.tsx +++ /dev/null @@ -1,29 +0,0 @@ -"use client" - -import { SortOption } from "@/lib/types/models" - -interface SortDropdownProps { - value: SortOption - onChange: (value: SortOption) => void -} - -export function SortDropdown({ value, onChange }: SortDropdownProps) { - return ( -
- - -
- ) -} diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index f38dc8241a..92bfea7199 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -1,7 +1,6 @@ "use client" import { useEffect, useMemo, useState } from "react" -import { AnimatedBackground } from "@/components/homepage" import { ModelCard } from "./components/model-card" import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/types/models" import Link from "next/link" @@ -34,6 +33,17 @@ const faqs = [ ), }, + { + question: "But how much does the Roo Code Cloud service cost?", + answer: ( + <> + Our{" "} + + service pricing is here. + + + ), + }, ] function calculateTotalPrice(model: Model): number { @@ -116,26 +126,25 @@ export default function ProviderPricingPage() { return ( <> - - - {/* Hero Section */} -
+

Roo Code Cloud Provider Pricing

- See pricing and features for all models we support. + See pricing and features for all models we offer in our selection.
- - Looking for general Roo Code Cloud pricing? + You can always bring your own key ( + + FAQ + ).

- {/* Search and Sort Controls */} -
+
+
@@ -174,11 +183,8 @@ export default function ProviderPricingPage() {
-
- {/* Models Grid */} -
-
+
{loading && (
@@ -209,20 +215,19 @@ export default function ProviderPricingPage() { )} {!loading && !error && filteredAndSortedModels.length > 0 && ( - <> -
- {filteredAndSortedModels.map((model) => ( - - ))} -
- +
+ {filteredAndSortedModels.map((model) => ( + + ))} +
)}
{/* FAQ Section */} -
+
+

Frequently Asked Questions

From 391de26530f99f8f3bf36a6b4874e4d5dd6d249b Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 11:12:49 +0000 Subject: [PATCH 05/18] Visual tweaks --- .../pricing/components/model-card.tsx | 98 ++++++++++++------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index dbd61fd114..315d56bf88 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -1,22 +1,36 @@ import { ModelWithTotalPrice } from "@/lib/types/models" import { formatCurrency, formatTokens } from "@/lib/formatters" -import { Gift } from "lucide-react" +import { + ArrowLeftToLine, + ArrowRightToLine, + Check, + Expand, + Gift, + HardDriveDownload, + HardDriveUpload, + ListChecks, + RulerDimensionLine, +} from "lucide-react" interface ModelCardProps { model: ModelWithTotalPrice } export function ModelCard({ model }: ModelCardProps) { - const inputPrice = parseFloat(model.pricing.input) + let inputPrice = parseFloat(model.pricing.input) const outputPrice = parseFloat(model.pricing.output) const cacheReadPrice = parseFloat(model.pricing.input_cache_read) const cacheWritePrice = parseFloat(model.pricing.input_cache_write) + const free = model.tags.includes("free") + const usefulTags = model.tags.filter((tag) => tag !== "free") + + inputPrice += 100 return ( -
+
-

+

{model.name} {free && ( @@ -28,58 +42,64 @@ export function ModelCard({ model }: ModelCardProps) {

{model.description}

- {model.tags.length > 0 && ( -
- {model.tags.map((tag) => ( - - {tag} - - ))} -
- )} -
-
Provider{model.owned_by || "N/A"}
Vendor{model.owned_by || "N/A"}
Context Window {formatTokens(model.context_window)}
+
{model.owned_by && ( - - + + )} - - + + - - + + - - + - - + {(cacheReadPrice > 0 || cacheWritePrice > 0) && ( <> - - + - - + )} - - - + + From 7943716f684a6be36b4d17e2fe07c64473f96d6a Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 11:15:55 +0000 Subject: [PATCH 06/18] Visual improvements --- .../src/app/provider/pricing/components/model-card.tsx | 4 +--- apps/web-roo-code/src/app/provider/pricing/page.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 315d56bf88..e8d152ef08 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -17,7 +17,7 @@ interface ModelCardProps { } export function ModelCard({ model }: ModelCardProps) { - let inputPrice = parseFloat(model.pricing.input) + const inputPrice = parseFloat(model.pricing.input) const outputPrice = parseFloat(model.pricing.output) const cacheReadPrice = parseFloat(model.pricing.input_cache_read) const cacheWritePrice = parseFloat(model.pricing.input_cache_write) @@ -25,8 +25,6 @@ export function ModelCard({ model }: ModelCardProps) { const free = model.tags.includes("free") const usefulTags = model.tags.filter((tag) => tag !== "free") - inputPrice += 100 - return (
diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 92bfea7199..9eca52f371 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -129,8 +129,10 @@ export default function ProviderPricingPage() {
-

Roo Code Cloud Provider Pricing

-

+

+ Roo Code Cloud Provider Pricing +

+

See pricing and features for all models we offer in our selection.
You can always bring your own key ( @@ -170,7 +172,7 @@ export default function ProviderPricingPage() { id="sort" value={sortOption} onChange={(e) => setSortOption(e.target.value as SortOption)} - className="rounded-full cursor-pointer border border-input bg-background hover:bg-muted pl-4 pr-9 py-2.5 text-base ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative appearance-none"> + className="rounded-full cursor-pointer border border-input bg-background hover:bg-muted pl-4 w-full md:w-auto pr-9 py-2.5 text-base ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 relative appearance-none"> From 70e4082a264e88936cfe85951019fa9f4dfd51e7 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 11:20:42 +0000 Subject: [PATCH 07/18] More responsiveness --- .../pricing/components/model-card.tsx | 107 ++++++++++++++---- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index e8d152ef08..9dc0261896 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -10,7 +10,10 @@ import { HardDriveUpload, ListChecks, RulerDimensionLine, + ChevronDown, + ChevronUp, } from "lucide-react" +import { useState } from "react" interface ModelCardProps { model: ModelWithTotalPrice @@ -25,8 +28,17 @@ export function ModelCard({ model }: ModelCardProps) { const free = model.tags.includes("free") const usefulTags = model.tags.filter((tag) => tag !== "free") + // Mobile collapsed/expanded state + const [expanded, setExpanded] = useState(false) + return ( -

+
+ {/* Header: always visible */}

{model.name} @@ -37,18 +49,24 @@ export function ModelCard({ model }: ModelCardProps) { )}

-

{model.description}

+

+ {model.description} +

+ {/* Content */}
Vendor{model.owned_by || "N/A"}Vendor{model.owned_by}
Context Window{formatTokens(model.context_window)} + + Context Window + {formatTokens(model.context_window)}
Max Output Tokens{formatTokens(model.max_tokens)} + + Max Output Tokens + {formatTokens(model.max_tokens)}
Input Price + + + Input Price + {inputPrice === 0 ? "Free" : `${formatCurrency(inputPrice)}/1M tokens`}
Output Price + + + Output Price + {outputPrice === 0 ? "Free" : `${formatCurrency(outputPrice)}/1M tokens`}
Cache Read + + + Cache Read + {cacheReadPrice === 0 ? "Free" : `${formatCurrency(cacheReadPrice)}/1M tokens`}
Cache Write + + + Cache Write + {cacheWritePrice === 0 ? "Free" : `${formatCurrency(cacheWritePrice)}/1M tokens`} @@ -87,10 +107,18 @@ export function ModelCard({ model }: ModelCardProps) {
Total Price - {model.totalPrice === 0 ? "Free" : `${formatCurrency(model.totalPrice)}/1M tokens`} +
+ + Features + + {usefulTags.map((tag) => ( + + + {tag} + + ))}
- {model.owned_by && ( - - - - - )} + {/* Context Window: always visible */} - - - - + + {/* Input Price: always visible */} - + + {/* Output Price: always visible */} + + + {/* Extra details: only visible on mobile when expanded, always visible on >=sm */} + {model.owned_by && ( + + + + + )} + + + + + + {(cacheReadPrice > 0 || cacheWritePrice > 0) && ( <> - + - + )} - + + - + + {/* Mobile-only toggle row */} + + +
Vendor{model.owned_by}
@@ -56,13 +74,8 @@ export function ModelCard({ model }: ModelCardProps) { {formatTokens(model.context_window)}
- - Max Output Tokens - {formatTokens(model.max_tokens)}
@@ -72,7 +85,14 @@ export function ModelCard({ model }: ModelCardProps) { {inputPrice === 0 ? "Free" : `${formatCurrency(inputPrice)}/1M tokens`}
Output Price @@ -81,9 +101,38 @@ export function ModelCard({ model }: ModelCardProps) { {outputPrice === 0 ? "Free" : `${formatCurrency(outputPrice)}/1M tokens`}
Vendor{model.owned_by}
+ + Max Output Tokens + {formatTokens(model.max_tokens)}
Cache Read @@ -92,7 +141,11 @@ export function ModelCard({ model }: ModelCardProps) { {cacheReadPrice === 0 ? "Free" : `${formatCurrency(cacheReadPrice)}/1M tokens`}
Cache Write @@ -105,12 +158,13 @@ export function ModelCard({ model }: ModelCardProps) {
Features + {usefulTags.map((tag) => ( @@ -119,6 +173,19 @@ export function ModelCard({ model }: ModelCardProps) { ))}
+ +
From 3a7e29bce0cb1ca8303f5b70f67cfeb68e878de0 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 14:54:23 +0000 Subject: [PATCH 08/18] Inference credits in general pricing --- apps/web-roo-code/src/app/pricing/page.tsx | 100 +++++++++++------- .../src/app/provider/pricing/page.tsx | 52 ++++++++- 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/apps/web-roo-code/src/app/pricing/page.tsx b/apps/web-roo-code/src/app/pricing/page.tsx index 40f5d434e8..12fe00bec4 100644 --- a/apps/web-roo-code/src/app/pricing/page.tsx +++ b/apps/web-roo-code/src/app/pricing/page.tsx @@ -1,4 +1,4 @@ -import { Users, Building2, ArrowRight, Star, LucideIcon, Check, Cloud } from "lucide-react" +import { Users, Building2, ArrowRight, Star, LucideIcon, Check, Cloud, PlugZap } from "lucide-react" import type { Metadata } from "next" import Link from "next/link" @@ -64,7 +64,6 @@ interface PricingTier { period?: string creditPrice?: string trial?: string - cancellation?: string description: string featuresIntro?: string features: string[] @@ -80,13 +79,12 @@ const pricingTiers: PricingTier[] = [ name: "Cloud Free", icon: Cloud, price: "$0", - cancellation: "Cancel anytime", description: "For folks just getting started", features: [ "Token usage analytics", + "Access to the Roo Code Cloud Provider, including early access to free stealth models", "Follow your tasks from anywhere", "Share tasks with friends and co-workers", - "Early access to free AI Models", "Community support", ], cta: { @@ -95,18 +93,18 @@ const pricingTiers: PricingTier[] = [ }, }, { - name: "Pro", + name: "Cloud Pro", icon: Star, price: "$20", period: "/mo", - trial: "Free 14-day trial · ", + trial: "Free for 14 days, then", creditPrice: `$${PRICE_CREDITS}`, - cancellation: "Cancel anytime", description: "For pro Roo coders", featuresIntro: "Everything in Free +", features: [ - "Cloud Agents: PR Reviewer and more", - "Roomote Control: Start, stop and control tasks from anywhere", + "Cloud Agents: Coder, Explainer, Planner, Reviewer, Fixer and more", + "Start tasks from Slack", + "Roomote Control: Start, stop and control extension tasks from anywhere", "Paid support", ], cta: { @@ -115,12 +113,12 @@ const pricingTiers: PricingTier[] = [ }, }, { - name: "Team", + name: "Cloud Team", icon: Users, price: "$99", period: "/mo", creditPrice: `$${PRICE_CREDITS}`, - trial: "Free 14-day trial · ", + trial: "Free for 14 days, then", cancellation: "Cancel anytime", description: "For AI-forward teams", featuresIntro: "Everything in Pro +", @@ -151,12 +149,30 @@ export default function PricingPage() {
- {/* Free Extension Notice */} -
-
+
+

- The Roo Code extension is free! - Roo Code Cloud is an optional service which takes it to the next level. + The Roo Code extension is totally free! + But Cloud takes you so much further. +

+
+
+ +
+

+ Roo Code Provider + +

+
+

+ On any plan, you can bring your own provider key or use the built-in Roo Code Cloud provider. +

+

+ We offer a select mix of tested state of the art closed and open weight LLMs for you to choose, + with no markup. + + See detailed pricing +

@@ -183,7 +199,7 @@ export default function PricingPage() {

{tier.featuresIntro} 

-
    +
      {tier.features.map((feature) => (
    • @@ -193,20 +209,28 @@ export default function PricingPage() {
-

- {tier.price} - {tier.period} -

+

{tier.trial}

- {tier.creditPrice && ( -

- + {tier.creditPrice}/hour for Cloud tasks -

- )} +

+ {tier.price} + {tier.period} + prepaid credits +

-

- {tier.trial} - {tier.cancellation} +

+ {tier.creditPrice && ( + <> + Cloud Agents: {tier.creditPrice}/hour if used +
+ + )} + Inference:{" "} + + Roo Provider pricing + {" "} + or{" "} + + BYOK +

{tier.cta.isContactForm ? ( @@ -249,7 +273,7 @@ export default function PricingPage() {

Frequently Asked Questions

-
+

Wait, is Roo Code free or not?

Yes! The Roo Code VS Code extension is open source and free forever. The extension acts @@ -257,7 +281,7 @@ export default function PricingPage() { Code Cloud.

-
+

Is there a free trial?

Yes, all paid plans come with a 14-day free trial to try out functionality. @@ -266,7 +290,7 @@ export default function PricingPage() { To use Cloud Agents, you can buy credits.

-
+

How do credits work?

Roo Code Cloud credits can be used in two ways: @@ -293,25 +317,25 @@ export default function PricingPage() { There are no markups, no tiers, no dumbing-down of models to increase our profit.

-
+

Do I need a credit card for the free trial?

Yes, but you won't be charged until your trial ends, except for credit purchases.

You can cancel anytime with one click.

-
+

What payment methods do you accept?

We accept all major credit cards, debit cards, and can arrange invoice billing for Enterprise customers.

-
-

Can I change plans anytime?

+
+

Can I cancel or change plans?

- Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in - your next billing cycle. + Yes, you can upgrade, downgrade or cancel your plan at any time. Changes will be + reflected in your next billing cycle.

diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 9eca52f371..75d9a5bb57 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -5,9 +5,57 @@ import { ModelCard } from "./components/model-card" import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/types/models" import Link from "next/link" import { ChevronDown, CircleX, Loader, LoaderCircle, Search } from "lucide-react" +import { ogImageUrl } from "@/lib/og" +import { SEO } from "@/lib/seo" +import { Metadata } from "next" const API_URL = "https://api.roocode.com/proxy/v1/models" +const TITLE = "Roo Code Cloud Provider Pricing" +const DESCRIPTION = "See pricing and features for all models we offer in our selection." +const OG_DESCRIPTION = "Pricing for all of models we host" +const PATH = "/provider/pricing" + +export const metadata: Metadata = { + title: TITLE, + description: DESCRIPTION, + alternates: { + canonical: `${SEO.url}${PATH}`, + }, + openGraph: { + title: TITLE, + description: DESCRIPTION, + url: `${SEO.url}${PATH}`, + siteName: SEO.name, + images: [ + { + url: ogImageUrl(TITLE, OG_DESCRIPTION), + width: 1200, + height: 630, + alt: TITLE, + }, + ], + locale: SEO.locale, + type: "website", + }, + twitter: { + card: SEO.twitterCard, + title: TITLE, + description: DESCRIPTION, + images: [ogImageUrl(TITLE, OG_DESCRIPTION)], + }, + keywords: [ + ...SEO.keywords, + "pricing", + "plans", + "subscription", + "cloud pricing", + "AI development pricing", + "team pricing", + "enterprise pricing", + ], +} + const faqs = [ { question: "What are AI model providers?", @@ -15,10 +63,10 @@ const faqs = [ }, { question: "How is pricing calculated?", - answer: "Pricing is based on token usage for input and output, measured per million tokens.", + answer: "Pricing is based on token usage for input and output, measured per million tokens, like pretty much any other provider out there.", }, { - question: "Wehat is the Roo Code Cloud Provider?", + question: "What is the Roo Code Cloud Provider?", answer: ( <>

This is our very own model provider, optimized to work seamlessly with Roo Code Cloud.

From a84b68a309d57fc9a7c91f1e76a771170b3ef34e Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 14:54:55 +0000 Subject: [PATCH 09/18] Inference credits in general pricing --- apps/web-roo-code/src/app/pricing/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web-roo-code/src/app/pricing/page.tsx b/apps/web-roo-code/src/app/pricing/page.tsx index 12fe00bec4..664f0ea904 100644 --- a/apps/web-roo-code/src/app/pricing/page.tsx +++ b/apps/web-roo-code/src/app/pricing/page.tsx @@ -119,7 +119,6 @@ const pricingTiers: PricingTier[] = [ period: "/mo", creditPrice: `$${PRICE_CREDITS}`, trial: "Free for 14 days, then", - cancellation: "Cancel anytime", description: "For AI-forward teams", featuresIntro: "Everything in Pro +", features: ["Unlimited users (no per-seat cost)", "Shared configuration & policies", "Centralized billing"], From 57d07c5af307beb31082cb50b8a884acdf4e757b Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:44:40 +0000 Subject: [PATCH 10/18] Screw makign this work on the server side for now --- .../src/app/provider/pricing/page.tsx | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 75d9a5bb57..0eac791a27 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -5,57 +5,9 @@ import { ModelCard } from "./components/model-card" import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/types/models" import Link from "next/link" import { ChevronDown, CircleX, Loader, LoaderCircle, Search } from "lucide-react" -import { ogImageUrl } from "@/lib/og" -import { SEO } from "@/lib/seo" -import { Metadata } from "next" const API_URL = "https://api.roocode.com/proxy/v1/models" -const TITLE = "Roo Code Cloud Provider Pricing" -const DESCRIPTION = "See pricing and features for all models we offer in our selection." -const OG_DESCRIPTION = "Pricing for all of models we host" -const PATH = "/provider/pricing" - -export const metadata: Metadata = { - title: TITLE, - description: DESCRIPTION, - alternates: { - canonical: `${SEO.url}${PATH}`, - }, - openGraph: { - title: TITLE, - description: DESCRIPTION, - url: `${SEO.url}${PATH}`, - siteName: SEO.name, - images: [ - { - url: ogImageUrl(TITLE, OG_DESCRIPTION), - width: 1200, - height: 630, - alt: TITLE, - }, - ], - locale: SEO.locale, - type: "website", - }, - twitter: { - card: SEO.twitterCard, - title: TITLE, - description: DESCRIPTION, - images: [ogImageUrl(TITLE, OG_DESCRIPTION)], - }, - keywords: [ - ...SEO.keywords, - "pricing", - "plans", - "subscription", - "cloud pricing", - "AI development pricing", - "team pricing", - "enterprise pricing", - ], -} - const faqs = [ { question: "What are AI model providers?", From 213c8d572114e17f69485cab010f7caea91d23a6 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:49:39 +0000 Subject: [PATCH 11/18] Update apps/web-roo-code/src/app/provider/pricing/page.tsx Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> --- apps/web-roo-code/src/app/provider/pricing/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 0eac791a27..b6ebe9617f 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -95,7 +95,7 @@ export default function ProviderPricingPage() { filtered = models.filter((model) => { return ( model.name.toLowerCase().includes(query) || - model.owned_by.toLowerCase().includes(query) || +model.owned_by?.toLowerCase().includes(query) || model.description.toLowerCase().includes(query) ) }) From 25c5bea5e783fb3cba8407b78b8a2d5856afcfe1 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:49:50 +0000 Subject: [PATCH 12/18] Update apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> --- .../src/app/provider/pricing/components/model-card.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 9dc0261896..5d4f74325b 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -87,12 +87,10 @@ export function ModelCard({ model }: ModelCardProps) { {/* Output Price: always visible */} - + className={[ + "border-b border-border", + // Add subtle separation from toggle on mobile + ].join(" ")} Output Price From 1e0c25b7bf1008780b5d35cb6e5fc1f38a16b362 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:50:06 +0000 Subject: [PATCH 13/18] Update apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> --- .../src/app/provider/pricing/components/model-card.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 5d4f74325b..116b7b3a22 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -102,12 +102,10 @@ export function ModelCard({ model }: ModelCardProps) { {/* Extra details: only visible on mobile when expanded, always visible on >=sm */} {model.owned_by && ( - + className={[ + "border-b border-border", + expanded ? "table-row" : "hidden sm:table-row", + ].join(" ")} Vendor {model.owned_by} From 10d5852cc33904dec372688f2063a257740dde9d Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:50:21 +0000 Subject: [PATCH 14/18] Update apps/web-roo-code/src/app/provider/pricing/page.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- apps/web-roo-code/src/app/provider/pricing/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index b6ebe9617f..dc7a516e61 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -198,7 +198,7 @@ model.owned_by?.toLowerCase().includes(query) || {error && (
-

Oops, couldn't load the model list.

+

Oops, couldn't load the model list.

Try again in a bit please.

)} From 4e5984b54c122ad2fb53330dd677fc9b1598ae71 Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 12 Nov 2025 15:57:11 +0000 Subject: [PATCH 15/18] Fixing the code review fixes --- .../provider/pricing/components/model-card.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 116b7b3a22..49f5ed75a1 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -87,10 +87,11 @@ export function ModelCard({ model }: ModelCardProps) { {/* Output Price: always visible */} - className={[ - "border-b border-border", - // Add subtle separation from toggle on mobile - ].join(" ")} + Output Price @@ -102,10 +103,11 @@ export function ModelCard({ model }: ModelCardProps) { {/* Extra details: only visible on mobile when expanded, always visible on >=sm */} {model.owned_by && ( - className={[ - "border-b border-border", - expanded ? "table-row" : "hidden sm:table-row", - ].join(" ")} + Vendor {model.owned_by} From ba890b5b22ce840462d8a06790b348adb87a2786 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sat, 15 Nov 2025 23:13:43 -0500 Subject: [PATCH 16/18] Tweaks --- .../pricing/components/model-card.tsx | 59 ++++++++++--------- .../src/app/provider/pricing/page.tsx | 12 ++-- apps/web-roo-code/src/lib/formatters.ts | 2 +- apps/web-roo-code/src/lib/types/models.ts | 1 + 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 49f5ed75a1..079ddd93ee 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -8,7 +8,6 @@ import { Gift, HardDriveDownload, HardDriveUpload, - ListChecks, RulerDimensionLine, ChevronDown, ChevronUp, @@ -26,7 +25,8 @@ export function ModelCard({ model }: ModelCardProps) { const cacheWritePrice = parseFloat(model.pricing.input_cache_write) const free = model.tags.includes("free") - const usefulTags = model.tags.filter((tag) => tag !== "free") + // Filter tags to only show vision and reasoning + const displayTags = model.tags.filter((tag) => tag === "vision" || tag === "reasoning") // Mobile collapsed/expanded state const [expanded, setExpanded] = useState(false) @@ -62,8 +62,8 @@ export function ModelCard({ model }: ModelCardProps) {

- {/* Content */} -
+ {/* Content - pinned to bottom */} +
{/* Context Window: always visible */} @@ -75,6 +75,18 @@ export function ModelCard({ model }: ModelCardProps) { + {/* Max Output Tokens: always visible on >=sm, expandable on mobile */} + + + + + {/* Input Price: always visible */} )} - - - - - {(cacheReadPrice > 0 || cacheWritePrice > 0) && ( <> )} - - - - + {/* Tags row: only show if there are vision or reasoning tags */} + {displayTags.length > 0 && ( + + + + + )} {/* Mobile-only toggle row */} diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index dc7a516e61..5468671e35 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -87,15 +87,16 @@ export default function ProviderPricingPage() { }, []) const filteredAndSortedModels = useMemo(() => { - let filtered = models + // Filter out deprecated models + let filtered = models.filter((model) => !model.deprecated) // Filter by search query if (searchQuery.trim()) { const query = searchQuery.toLowerCase() - filtered = models.filter((model) => { + filtered = filtered.filter((model) => { return ( model.name.toLowerCase().includes(query) || -model.owned_by?.toLowerCase().includes(query) || + model.owned_by?.toLowerCase().includes(query) || model.description.toLowerCase().includes(query) ) }) @@ -124,6 +125,9 @@ model.owned_by?.toLowerCase().includes(query) || return sorted }, [models, searchQuery, sortOption]) + // Count non-deprecated models for the display + const nonDeprecatedCount = useMemo(() => models.filter((model) => !model.deprecated).length, [models]) + return ( <>
@@ -162,7 +166,7 @@ model.owned_by?.toLowerCase().includes(query) || />
- {filteredAndSortedModels.length} of {models.length} models + {filteredAndSortedModels.length} of {nonDeprecatedCount} models
diff --git a/apps/web-roo-code/src/lib/formatters.ts b/apps/web-roo-code/src/lib/formatters.ts index 05235a0222..88ca558764 100644 --- a/apps/web-roo-code/src/lib/formatters.ts +++ b/apps/web-roo-code/src/lib/formatters.ts @@ -11,7 +11,7 @@ export const formatTokens = (tokens: number) => { } if (tokens < 1000000) { - return `${(tokens / 1000).toFixed(1)}k` + return `${(tokens / 1000).toFixed(1)}K` } if (tokens < 1000000000) { diff --git a/apps/web-roo-code/src/lib/types/models.ts b/apps/web-roo-code/src/lib/types/models.ts index b405101383..4bcd3116ab 100644 --- a/apps/web-roo-code/src/lib/types/models.ts +++ b/apps/web-roo-code/src/lib/types/models.ts @@ -17,6 +17,7 @@ export interface Model { type: string tags: string[] pricing: ModelPricing + deprecated?: boolean } export interface ModelsResponse { From 15dd6b3bc54ede25d28787d65c23e22f483d35e8 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sat, 15 Nov 2025 23:40:23 -0500 Subject: [PATCH 17/18] Include paid models and fix pricing display --- .../pricing/components/model-card.tsx | 66 +++++++++---------- .../src/app/provider/pricing/page.tsx | 2 +- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 079ddd93ee..1b1155ef65 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -19,10 +19,11 @@ interface ModelCardProps { } export function ModelCard({ model }: ModelCardProps) { - const inputPrice = parseFloat(model.pricing.input) - const outputPrice = parseFloat(model.pricing.output) - const cacheReadPrice = parseFloat(model.pricing.input_cache_read) - const cacheWritePrice = parseFloat(model.pricing.input_cache_write) + // Prices are per token, multiply by 1M to get price per million tokens + const inputPrice = parseFloat(model.pricing.input) * 1_000_000 + const outputPrice = parseFloat(model.pricing.output) * 1_000_000 + const cacheReadPrice = parseFloat(model.pricing.input_cache_read || "0") * 1_000_000 + const cacheWritePrice = parseFloat(model.pricing.input_cache_write || "0") * 1_000_000 const free = model.tags.includes("free") // Filter tags to only show vision and reasoning @@ -125,37 +126,32 @@ export function ModelCard({ model }: ModelCardProps) {
)} - {(cacheReadPrice > 0 || cacheWritePrice > 0) && ( - <> - - - - - - - - - + {cacheReadPrice > 0 && ( + + + + + )} + + {cacheWritePrice > 0 && ( + + + + )} {/* Tags row: only show if there are vision or reasoning tags */} diff --git a/apps/web-roo-code/src/app/provider/pricing/page.tsx b/apps/web-roo-code/src/app/provider/pricing/page.tsx index 5468671e35..4558355d2c 100644 --- a/apps/web-roo-code/src/app/provider/pricing/page.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/page.tsx @@ -6,7 +6,7 @@ import { Model, ModelWithTotalPrice, ModelsResponse, SortOption } from "@/lib/ty import Link from "next/link" import { ChevronDown, CircleX, Loader, LoaderCircle, Search } from "lucide-react" -const API_URL = "https://api.roocode.com/proxy/v1/models" +const API_URL = "https://api.roocode.com/proxy/v1/models?include_paid=true" const faqs = [ { From 7fa4cb99f7e6cb327320d2e0dba968dee46e2684 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 16 Nov 2025 00:12:43 -0500 Subject: [PATCH 18/18] Add provider --- .../pricing/components/model-card.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx index 1b1155ef65..26f3545791 100644 --- a/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx +++ b/apps/web-roo-code/src/app/provider/pricing/components/model-card.tsx @@ -3,6 +3,7 @@ import { formatCurrency, formatTokens } from "@/lib/formatters" import { ArrowLeftToLine, ArrowRightToLine, + Building2, Check, Expand, Gift, @@ -67,6 +68,17 @@ export function ModelCard({ model }: ModelCardProps) {
{formatTokens(model.context_window)}
+ + Max Output Tokens + {formatTokens(model.max_tokens)}
@@ -113,17 +125,6 @@ export function ModelCard({ model }: ModelCardProps) {
- - Max Output Tokens - {formatTokens(model.max_tokens)}
- - Features - - {usefulTags.map((tag) => ( - - - {tag} - - ))} -
Features + {displayTags.map((tag) => ( + + + {tag} + + ))} +
- - Cache Read - - {cacheReadPrice === 0 ? "Free" : `${formatCurrency(cacheReadPrice)}/1M tokens`} -
- - Cache Write - - {cacheWritePrice === 0 - ? "Free" - : `${formatCurrency(cacheWritePrice)}/1M tokens`} -
+ + Cache Read + {formatCurrency(cacheReadPrice)}/1M tokens
+ + Cache Write + {formatCurrency(cacheWritePrice)}/1M tokens
+ {/* Provider: always visible if present */} + {model.owned_by && ( + + + + + )} + {/* Context Window: always visible */} - {/* Extra details: only visible on mobile when expanded, always visible on >=sm */} - {model.owned_by && ( - - - - - )} - + {/* Cache pricing: only visible on mobile when expanded, always visible on >=sm */} {cacheReadPrice > 0 && (
+ + Provider + {model.owned_by}
@@ -114,18 +126,7 @@ export function ModelCard({ model }: ModelCardProps) {
Vendor{model.owned_by}