Skip to content

Commit c50c7e6

Browse files
authored
remove margin and make sticky (#4705)
1 parent c103729 commit c50c7e6

File tree

8 files changed

+327
-246
lines changed

8 files changed

+327
-246
lines changed

bifrost/app/components/Layout.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import { BLOG_CONTENT } from "../blog/page";
77
export const Layout = async ({
88
children,
99
hideFooter,
10+
noNavbarMargin,
1011
}: {
1112
children: React.ReactNode;
1213
hideFooter?: boolean;
14+
noNavbarMargin?: boolean;
1315
}) => {
1416
const githubResponse = await fetch(
1517
"https://api.github.com/repos/helicone/helicone"
@@ -24,10 +26,15 @@ export const Layout = async ({
2426
<>
2527
{/* <Banner /> */}
2628

27-
<NavBar stars={stars} featuredBlogMetadata={featuredBlogMetadata || {
28-
title: "Check out our latest blog",
29-
description: "Open-source LLM observability and monitoring platform for developers",
30-
}} featuredBlogFolderName={featuredBlogFolderName} />
29+
<NavBar
30+
stars={stars}
31+
featuredBlogMetadata={featuredBlogMetadata || {
32+
title: "Check out our latest blog",
33+
description: "Open-source LLM observability and monitoring platform for developers",
34+
}}
35+
featuredBlogFolderName={featuredBlogFolderName}
36+
noMargin={noNavbarMargin}
37+
/>
3138
{children}
3239
{!hideFooter && <Footer />}
3340
</>

bifrost/app/model/[modelName]/ModelDetailPage.tsx

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,29 @@ const formatContext = (tokens: number) => {
4747

4848
const parameterLabels = PARAMETER_LABELS as Record<StandardParameter, string>;
4949

50-
export function ModelDetailPage() {
50+
interface ModelDetailPageProps {
51+
initialModel: Model | null;
52+
}
53+
54+
export function ModelDetailPage({ initialModel }: ModelDetailPageProps) {
5155
const params = useParams();
5256
const router = useRouter();
5357
const searchParams = useSearchParams();
5458
const modelName = params.modelName as string;
5559
const decodedModelName = decodeURIComponent(modelName);
5660

57-
const [model, setModel] = useState<Model | null>(null);
58-
const [loading, setLoading] = useState(true);
61+
const [model, setModel] = useState<Model | null>(initialModel);
62+
const [loading, setLoading] = useState(!initialModel);
5963
const [copiedText, setCopiedText] = useState<string | null>(null);
6064

6165
const jawnClient = useJawnClient();
6266

6367
useEffect(() => {
68+
// Skip fetching if we have initial data from SSG
69+
if (initialModel) {
70+
return;
71+
}
72+
6473
async function loadModel() {
6574
try {
6675
const response = await jawnClient.GET(
@@ -86,7 +95,7 @@ export function ModelDetailPage() {
8695
}
8796

8897
loadModel();
89-
}, [decodedModelName]);
98+
}, [decodedModelName, initialModel, jawnClient]);
9099

91100
const copyToClipboard = (text: string, label: string) => {
92101
navigator.clipboard.writeText(text);
@@ -144,27 +153,13 @@ export function ModelDetailPage() {
144153
""
145154
);
146155

147-
const cheapestEndpoint = model.endpoints.reduce((min, ep) => {
148-
// Use base pricing or first tier for comparison
149-
const epPricing = ep.pricingTiers && ep.pricingTiers.length > 0
150-
? ep.pricingTiers[0]
151-
: ep.pricing;
152-
const minPricing = min.pricingTiers && min.pricingTiers.length > 0
153-
? min.pricingTiers[0]
154-
: min.pricing;
155-
156-
if (!epPricing || !minPricing) return min;
157-
158-
const epCost = (epPricing.prompt + epPricing.completion) / 2;
159-
const minCost = (minPricing.prompt + minPricing.completion) / 2;
160-
return epCost < minCost ? ep : min;
161-
});
156+
const firstEndpoint = model.endpoints[0];
162157

163158
const capabilities: { key: string; label: string; cost?: string }[] = [];
164159
// Use first pricing tier for capabilities or base pricing
165-
const basePricing = cheapestEndpoint.pricingTiers && cheapestEndpoint.pricingTiers.length > 0
166-
? cheapestEndpoint.pricingTiers[0]
167-
: cheapestEndpoint.pricing;
160+
const basePricing = firstEndpoint.pricingTiers && firstEndpoint.pricingTiers.length > 0
161+
? firstEndpoint.pricingTiers[0]
162+
: firstEndpoint.pricing;
168163

169164
if (basePricing && basePricing.audio && basePricing.audio > 0) {
170165
capabilities.push({
@@ -328,35 +323,20 @@ export function ModelDetailPage() {
328323
return aAvg - bAvg;
329324
})
330325
.map((endpoint) => {
331-
const isCheapest = endpoint === cheapestEndpoint;
332326
// Check for pricing tiers
333327
const pricingArray = endpoint.pricingTiers;
334328
const hasTiers =
335329
pricingArray && pricingArray.length > 0;
336330

337331
return (
338-
<TableRow
339-
key={endpoint.provider}
340-
className={
341-
isCheapest
342-
? "bg-green-50 dark:bg-green-950/10"
343-
: ""
344-
}
345-
>
332+
<TableRow key={endpoint.provider}>
346333
<TableCell className="font-medium">
347-
<div className="flex items-center gap-2">
348-
<Link
349-
href={`/providers/${endpoint.providerSlug}`}
350-
className="hover:underline"
351-
>
352-
{endpoint.provider}
353-
</Link>
354-
{isCheapest && (
355-
<span className="text-xs px-2 py-0.5 rounded-full bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-200">
356-
Cheapest
357-
</span>
358-
)}
359-
</div>
334+
<Link
335+
href={`/providers/${endpoint.providerSlug}`}
336+
className="hover:underline"
337+
>
338+
{endpoint.provider}
339+
</Link>
360340
</TableCell>
361341
<TableCell className="font-mono text-sm">
362342
{formatContext(model.contextLength)}

bifrost/app/model/[modelName]/page.tsx

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,58 @@ import { Metadata } from "next";
22
import { Suspense } from "react";
33
import { ModelDetailPage } from "./ModelDetailPage";
44
import { Layout } from "@/app/components/Layout";
5+
import { getJawnClient } from "@/lib/clients/jawn";
6+
import { components } from "@/lib/clients/jawnTypes/public";
7+
8+
type ModelRegistryItem = components["schemas"]["ModelRegistryItem"];
9+
10+
// Revalidate every hour to keep data fresh
11+
export const revalidate = 3600;
12+
13+
export async function generateStaticParams() {
14+
try {
15+
const jawnClient = getJawnClient();
16+
const response = await jawnClient.GET("/v1/public/model-registry/models");
17+
18+
if (response.data?.data?.models) {
19+
return response.data.data.models.map((model: ModelRegistryItem) => ({
20+
modelName: encodeURIComponent(model.id),
21+
}));
22+
}
23+
} catch (error) {
24+
console.error("Failed to generate static params for models:", error);
25+
}
26+
27+
return [];
28+
}
29+
30+
async function fetchModelData(modelId: string): Promise<ModelRegistryItem | null> {
31+
try {
32+
const jawnClient = getJawnClient();
33+
const response = await jawnClient.GET("/v1/public/model-registry/models");
34+
35+
if (response.data?.data?.models) {
36+
const model = response.data.data.models.find(
37+
(m: ModelRegistryItem) => m.id === modelId
38+
);
39+
return model || null;
40+
}
41+
} catch (error) {
42+
console.error("Failed to fetch model data:", error);
43+
}
44+
45+
return null;
46+
}
547

648
export async function generateMetadata({
749
params,
850
}: {
951
params: { modelName: string };
1052
}): Promise<Metadata> {
1153
const decodedModelName = decodeURIComponent(params.modelName);
12-
const displayName = decodedModelName
54+
const model = await fetchModelData(decodedModelName);
55+
56+
const displayName = model?.name || decodedModelName
1357
.split("-")
1458
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
1559
.join(" ");
@@ -40,8 +84,11 @@ export default async function ModelPage({
4084
}: {
4185
params: { modelName: string };
4286
}) {
87+
const decodedModelName = decodeURIComponent(params.modelName);
88+
const model = await fetchModelData(decodedModelName);
89+
4390
return (
44-
<Layout>
91+
<Layout noNavbarMargin={true}>
4592
<Suspense
4693
fallback={
4794
<div className="flex flex-col gap-4 w-full max-w-6xl mx-auto px-4 py-8">
@@ -58,7 +105,7 @@ export default async function ModelPage({
58105
</div>
59106
}
60107
>
61-
<ModelDetailPage />
108+
<ModelDetailPage initialModel={model} />
62109
</Suspense>
63110
</Layout>
64111
);

0 commit comments

Comments
 (0)