Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { JsonLd } from '@/app/(landing)/components/json-ld'
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
import { LANDING_CONTENT_WIDTH, LANDING_GUTTER } from '@/app/(landing)/components/landing-layout'
import { ShareButton } from '@/app/(landing)/components/share-button'
import { getModelComparisonsFor } from '@/app/(landing)/models/comparisons'
import { ComparisonLinks } from '@/app/(landing)/models/components/comparison-links'
import { FeaturedModelCard, ProviderIcon } from '@/app/(landing)/models/components/model-primitives'
import {
buildModelCapabilityFacts,
Expand Down Expand Up @@ -109,6 +111,7 @@ export default async function ModelPage({
const capabilityFacts = buildModelCapabilityFacts(model)
const pricingBounds = getPricingBounds(model.pricing)
const relatedModels = getRelatedModels(model, 6)
const comparisons = getModelComparisonsFor(model)

const breadcrumbJsonLd = {
'@context': 'https://schema.org',
Expand Down Expand Up @@ -195,7 +198,7 @@ export default async function ModelPage({
<ChipLink variant='primary' href='/'>
Build with this model
</ChipLink>
<ChipLink href={provider.href} className='border border-[var(--border-1)]'>
<ChipLink href={provider.href} variant='outline'>
All {provider.name} models
</ChipLink>
<ShareButton url={`${baseUrl}${model.href}`} title={model.displayName} />
Expand Down Expand Up @@ -273,6 +276,21 @@ export default async function ModelPage({
</>
)}

{comparisons.length > 0 && (
<>
<div className='h-px w-full bg-[var(--border)]' />
<nav aria-labelledby='model-comparisons-heading' className='px-6 py-10'>
<h2
id='model-comparisons-heading'
className='mb-6 text-[24px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] max-lg:text-[20px]'
>
Compare {model.displayName}
</h2>
<ComparisonLinks comparisons={comparisons} />
</nav>
</>
)}

<div className='h-px w-full bg-[var(--border)]' />

<section aria-labelledby='model-faq-heading' className='px-6 py-10'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { notFound } from 'next/navigation'
import { COVER_OG_SIZE, createCoverOgImage } from '@/lib/og/cover-image'
import { getProviderBySlug, MODEL_PROVIDERS_WITH_CATALOGS } from '@/app/(landing)/models/utils'
import {
getProviderBySlug,
getProviderPageTitle,
MODEL_PROVIDERS_WITH_CATALOGS,
} from '@/app/(landing)/models/utils'

export const contentType = 'image/png'
export const size = COVER_OG_SIZE
Expand All @@ -21,7 +25,7 @@ export default async function Image({ params }: { params: Promise<{ provider: st
}

return createCoverOgImage({
title: `${provider.name} models`,
title: getProviderPageTitle(provider),
subtitle: `Browse ${provider.modelCount} tracked ${provider.name} models with pricing, context windows, default model selection, and capability coverage.`,
})
}
32 changes: 19 additions & 13 deletions apps/sim/app/(landing)/models/(shell)/[provider]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
formatPrice,
formatTokenCount,
getProviderBySlug,
getProviderPageDescription,
getProviderPageTitle,
MODEL_PROVIDERS_WITH_CATALOGS,
MODEL_PROVIDERS_WITH_MODELS,
TOP_MODEL_PROVIDERS,
Expand Down Expand Up @@ -50,31 +52,35 @@ export async function generateMetadata({
return {}
}

const providerFaqs = buildProviderFaqs(provider)
const title = getProviderPageTitle(provider)
const description = getProviderPageDescription(provider)

return {
title: `${provider.name} Models`,
description: `Browse ${provider.modelCount} ${provider.name} models tracked in Sim. Compare pricing, context windows, default model selection, and capabilities for ${provider.name}'s AI model lineup.`,
title,
description,
keywords: [
`${provider.name} models`,
`${provider.name} pricing`,
`${provider.name} context window`,
`${provider.name} model list`,
`${provider.name} AI models`,
...provider.models.slice(0, 6).map((model) => model.displayName),
...new Set([
`${provider.apiName} API pricing`,
`${provider.modelFamily} API pricing`,
`${provider.name} pricing`,
`${provider.modelFamily} context window`,
`${provider.name} models`,
`${provider.name} model list`,
...provider.models.slice(0, 6).map((model) => model.displayName),
]),
],
// og:image/twitter:image come from the sibling opengraph-image.tsx -
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
openGraph: {
title: `${provider.name} Models | Sim`,
description: `Explore ${provider.modelCount} ${provider.name} models with pricing and capability details.`,
title: `${title} | Sim`,
description,
url: `${baseUrl}${provider.href}`,
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: `${provider.name} Models | Sim`,
description: providerFaqs[0]?.answer ?? provider.summary,
title: `${title} | Sim`,
description,
},
alternates: {
canonical: `${baseUrl}${provider.href}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { notFound } from 'next/navigation'
import { COVER_OG_SIZE, createCoverOgImage } from '@/lib/og/cover-image'
import {
buildComparisonVerdict,
getModelComparisonBySlug,
MODEL_COMPARISONS,
} from '@/app/(landing)/models/comparisons'

export const contentType = 'image/png'
export const size = COVER_OG_SIZE

/** Pre-render catalog images separately from their sibling pages. */
export async function generateStaticParams() {
return MODEL_COMPARISONS.map((comparison) => ({ pair: comparison.slug }))
}

export default async function Image({ params }: { params: Promise<{ pair: string }> }) {
const { pair } = await params
const comparison = getModelComparisonBySlug(pair)

if (!comparison) {
notFound()
}

return createCoverOgImage({
title: comparison.title,
subtitle: buildComparisonVerdict(comparison)[0],
})
}
Loading
Loading