Skip to content
Merged
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
10 changes: 8 additions & 2 deletions i18n/en/code.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"catalog.recipeNameAsc": {
"message": "Recipe name A–Z"
},
"catalog.recipeNameDesc": {
"message": "Recipe name Z–A"
},
"catalog.loadingDetails": {
"message": "Loading details…",
"description": "正在加载详情…"
Expand Down Expand Up @@ -1676,8 +1682,8 @@
"description": "按模型名称 Z–A"
},
"site.6592019eaa05": {
"message": "Recipe release date",
"description": "按配方发布日期"
"message": "Newest first",
"description": "按最新发布排序"
},
"site.7904ff565aaa": {
"message": "Card view",
Expand Down
44 changes: 3 additions & 41 deletions src/features/catalog/CatalogPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
reportHref,
} from './routes';
import CatalogSelect from './CatalogSelect';
import CatalogSort from './CatalogSort';
import ModelCard, { ModelRecipeList, ModelTuning } from './ModelCard';
import styles from './styles.module.css';

Expand Down Expand Up @@ -316,49 +317,10 @@ export default function CatalogPage() {
})}
</span>
<span className={styles.select}>
<CatalogSelect
label={translate({
id: 'site.1ce6b1d7c95c',
message: '排序方式',
})}
<CatalogSort
value={q.sort}
onValueChange={(value) => update({ sort: value })}
options={[
{
value: 'improvement-desc',
label:
q.view === 'models'
? translate({
id: 'site.f7e6d40ff9f5',
message: '按调优配方最佳提升排序',
})
: translate({
id: 'site.26ba23cf9e57',
message: '按吞吐量提升排序',
}),
},
{
value: 'name-asc',
label: translate({
id: 'site.e273b1d2eca6',
message: '按模型名称 A–Z',
}),
},
{
value: 'name-desc',
label: translate({
id: 'site.1322e7738e4a',
message: '按模型名称 Z–A',
}),
},
{
value: 'updated-desc',
label: translate({
id: 'site.6592019eaa05',
message: '按配方发布日期',
}),
},
]}
context={q.view === 'models' ? 'models' : 'recipes'}
/>
</span>
<div className={styles.viewButtons}>
Expand Down
69 changes: 69 additions & 0 deletions src/features/catalog/CatalogSort.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { translate } from '@docusaurus/Translate';
import CatalogSelect from './CatalogSelect';

export default function CatalogSort({
value,
onValueChange,
context = 'recipes',
}: {
value: string;
onValueChange: (value: string) => void;
context?: 'models' | 'recipes' | 'model-recipes';
}) {
return (
<CatalogSelect
label={translate({ id: 'site.1ce6b1d7c95c', message: '排序方式' })}
value={value}
onValueChange={onValueChange}
options={[
{
value: 'improvement-desc',
label:
context === 'models'
? translate({
id: 'site.f7e6d40ff9f5',
message: '按调优配方最佳提升排序',
})
: translate({
id: 'site.26ba23cf9e57',
message: '按吞吐量提升排序',
}),
},
{
value: 'name-asc',
label:
context === 'model-recipes'
? translate({
id: 'catalog.recipeNameAsc',
message: '按配方名称 A–Z',
})
: translate({
id: 'site.e273b1d2eca6',
message: '按模型名称 A–Z',
}),
},
{
value: 'name-desc',
label:
context === 'model-recipes'
? translate({
id: 'catalog.recipeNameDesc',
message: '按配方名称 Z–A',
})
: translate({
id: 'site.1322e7738e4a',
message: '按模型名称 Z–A',
}),
},
{
value: 'updated-desc',
label: translate({
id: 'site.6592019eaa05',
message: '按最新发布排序',
}),
},
]}
/>
);
}
35 changes: 33 additions & 2 deletions src/features/catalog/DetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import ReportPanel from './ReportPanel';
import { availableReportTunings } from './report';
import CatalogLink from './CatalogLink';
import CatalogSelect from './CatalogSelect';
import CatalogSort from './CatalogSort';
import { compare, initialQuery, parseSort } from './query';
import RecipeOverview from './RecipeOverview';
import styles from './styles.module.css';

Expand Down Expand Up @@ -81,6 +83,7 @@ function DetailContent({ kind }: { kind: 'model' | 'recipe' }) {
params.get('dataVersion') ?? navigation.catalogDataVersion;
const state = useCatalog(dataVersion),
c = state.catalog;
const sort = parseSort(params.get('sort'));
const recipeParams = new URLSearchParams(location.search);
recipeParams.delete('returnTo');
if (recipeParams.get('dataVersion') === state.currentCommit)
Expand All @@ -90,7 +93,11 @@ function DetailContent({ kind }: { kind: 'model' | 'recipe' }) {
!browser || !modelId
? null
: kind === 'model'
? modelRoute(modelId, params.get('dataVersion') ?? '')
? modelRoute(
modelId,
params.get('dataVersion') ?? '',
sort === initialQuery.sort ? '' : sort,
)
: recipeId
? recipeRoute(modelId, recipeId, recipeParams.toString())
: null;
Expand Down Expand Up @@ -129,6 +136,10 @@ function DetailContent({ kind }: { kind: 'model' | 'recipe' }) {
[c, modelId],
);
const recipe = recipes.find((r) => r.variant.id === recipeId);
const sortedRecipes = useMemo(
() => [...recipes].sort((a, b) => compare(a, b, sort)),
[recipes, sort],
);
const [revisions, setRevisions] = useState<History | null>(null),
[historyError, setHistoryError] = useState(''),
[text, setText] = useState(''),
Expand Down Expand Up @@ -230,6 +241,19 @@ function DetailContent({ kind }: { kind: 'model' | 'recipe' }) {
modelNavigationState(dataVersion, returnTo),
);
}
function chooseSort(value: string) {
if (value === sort) return;
history.push(
siteHref(
modelRoute(
modelId,
params.get('dataVersion') ?? '',
value === initialQuery.sort ? '' : value,
),
) + location.hash,
modelNavigationState(dataVersion, returnTo),
);
}
function chooseTab(tab: RecipeTab) {
if (tab !== activeTab)
history.push(
Expand Down Expand Up @@ -487,9 +511,16 @@ function DetailContent({ kind }: { kind: 'model' | 'recipe' }) {
{recipes.length}
</span>
</h2>
<span className={styles.select}>
<CatalogSort
value={sort}
onValueChange={chooseSort}
context="model-recipes"
/>
</span>
</div>
<div className={styles.grid}>
{recipes.map((r) => (
{sortedRecipes.map((r) => (
<RecipeCard
key={r.key}
r={r}
Expand Down
17 changes: 11 additions & 6 deletions src/features/catalog/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export interface Query {
dataVersion: string;
}
export const PAGE_SIZE = 12;
const sortOptions = [
'improvement-desc',
'name-asc',
'name-desc',
'updated-desc',
];
export function parseSort(value: string | null) {
return value && sortOptions.includes(value) ? value : 'improvement-desc';
}
export const initialQuery: Query = {
q: '',
family: '',
Expand Down Expand Up @@ -98,11 +107,7 @@ export function parseQuery(search: string, c: Catalog): Query {
topology: allowed('topology', ['single-node', 'lws', 'pd']),
view: allowed('view', ['models', 'recipes'], 'recipes'),
layout: allowed('layout', ['cards', 'table'], 'cards'),
sort: allowed(
'sort',
['improvement-desc', 'name-asc', 'name-desc', 'updated-desc'],
'improvement-desc',
),
sort: parseSort(p.get('sort')),
page:
/^[1-9]\d*$/.test(rawPage) && Number.isSafeInteger(+rawPage)
? +rawPage
Expand Down Expand Up @@ -258,5 +263,5 @@ export function compare(a: Recipe, b: Recipe, sort: string) {
if (d) return d;
}
const name = a.model.name.localeCompare(b.model.name);
return (sort === 'name-desc' ? -name : name) || a.key.localeCompare(b.key);
return (sort === 'name-desc' ? -1 : 1) * (name || a.key.localeCompare(b.key));
}
3 changes: 2 additions & 1 deletion src/features/catalog/routes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Recipe } from './data';

export function modelRoute(modelId: string, dataVersion = '') {
export function modelRoute(modelId: string, dataVersion = '', sort = '') {
const params = new URLSearchParams({ id: modelId });
if (dataVersion) params.set('dataVersion', dataVersion);
if (sort) params.set('sort', sort);
return '/model?' + params.toString();
}

Expand Down
2 changes: 2 additions & 0 deletions src/features/catalog/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -1573,6 +1573,8 @@
.sectionHeading {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding: 0 0 16px;
margin-bottom: 20px;
Expand Down
12 changes: 8 additions & 4 deletions tests/e2e/catalog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1202,11 +1202,13 @@ test('original report retries a failed request without requiring structured JSON
page,
}) => {
let attempts = 0;
let available = false;
// Keep the report unavailable until Retry, including any redirect remounts.
await page.route('**/mimo-v2.5-h100-report.html', (route) => {
attempts++;
return attempts === 1
? route.fulfill({ status: 503, body: 'Unavailable' })
: route.fulfill({ body: report, contentType: 'text/html' });
return available
? route.fulfill({ body: report, contentType: 'text/html' })
: route.fulfill({ status: 503, body: 'Unavailable' });
});
await page.goto(
`/models/recipe?modelId=mimo-v2.5&recipeId=${variant.id}&tab=performance`,
Expand All @@ -1218,12 +1220,14 @@ test('original report retries a failed request without requiring structured JSON
await expect(
page.getByRole('button', { name: '全屏观看', exact: true }),
).toBeDisabled();
const attemptsBeforeRetry = attempts;
available = true;
await page.getByRole('button', { name: '重试', exact: true }).click();
await expect(reportBody(page)).toContainText('119.9%');
await expect(
page.getByRole('button', { name: '全屏观看', exact: true }),
).toBeEnabled();
expect(attempts).toBe(2);
expect(attempts).toBe(attemptsBeforeRetry + 1);
});

test('history opens the exact configuration and keeps reports bound to their measured version', async ({
Expand Down
Loading
Loading