Skip to content

Commit 200cc38

Browse files
fix(compare): drop redundant default params from AgentX hero links / 移除 AgentX 首页链接中冗余的默认参数 (#890)
* fix(compare): drop redundant default params from AgentX hero links / 移除 AgentX 首页链接中冗余的默认参数 The landing page / compare hero 'View results' and 'Full dashboard' links carried ?i_seq=agentic-traces&i_optimal=1, but both values are already the dashboard defaults for AgentX-capable models: resolveEffectiveSequence prefers Agentic Traces whenever availability confirms the model has AgentX data, and Optimal Only is on unless i_optimal=0. The bare /inference/<model> subroute is the canonical shareable address, so the params only added URL noise. * fix(filters): guard i_seq against stale URL snapshot on soft navigation / 防止软导航时残留的 i_seq 参数覆盖 AgentX 默认场景 The module-level URL snapshot retains self-written params across client-side navigations, so a paramless AgentX hero link could re-apply the previous page's i_seq as an explicit pick and block the availability-driven Agentic default. Guard both the initializer and the navigation effect with hasExplicitUrlParam('i_seq') — the same guard g_model already uses for this exact stale-snapshot reason — and add regression coverage.
1 parent 59a1980 commit 200cc38

6 files changed

Lines changed: 156 additions & 23 deletions

File tree

‎packages/app/cypress/e2e/compare-precision.cy.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ describe('Compare precision index page', () => {
1313
cy.get('[data-testid="compare-agentx-model-kimi-k3"]').should(
1414
'have.attr',
1515
'href',
16-
'/inference/kimi-k3?i_seq=agentic-traces&i_optimal=1',
16+
'/inference/kimi-k3',
1717
);
1818
cy.get('[data-testid="compare-agentx-overview-link"]')
1919
.should('contain.text', 'Overview')
2020
.and('have.attr', 'href', '/overview');
2121
cy.get('[data-testid="compare-agentx-dashboard-link"]')
2222
.should('contain.text', 'Full dashboard')
23-
.and('have.attr', 'href', '/inference/kimi-k3?i_seq=agentic-traces&i_optimal=1');
23+
.and('have.attr', 'href', '/inference/kimi-k3');
2424
cy.get('[data-testid="compare-agentx-methodology-link"]').should('not.exist');
2525
});
2626
cy.get('[data-testid="compare-model-catalog"]')
@@ -52,14 +52,14 @@ describe('Compare precision index page', () => {
5252
cy.get('[data-testid="compare-agentx-model-deepseek-v4"]').should(
5353
'have.attr',
5454
'href',
55-
'/zh/inference/deepseek-v4?i_seq=agentic-traces&i_optimal=1',
55+
'/zh/inference/deepseek-v4',
5656
);
5757
cy.get('[data-testid="compare-agentx-overview-link"]')
5858
.should('contain.text', '总览')
5959
.and('have.attr', 'href', '/zh/overview');
6060
cy.get('[data-testid="compare-agentx-dashboard-link"]')
6161
.should('contain.text', '查看完整仪表板')
62-
.and('have.attr', 'href', '/zh/inference/kimi-k3?i_seq=agentic-traces&i_optimal=1');
62+
.and('have.attr', 'href', '/zh/inference/kimi-k3');
6363
cy.get('[data-testid="compare-agentx-methodology-link"]').should('not.exist');
6464
});
6565
cy.get('[data-testid="compare-model-catalog"]').should('contain.text', 'AgentX 与 8K→1K 结果');

‎packages/app/cypress/e2e/navigation.cy.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe('First-load navigation', () => {
121121
.and('have.attr', 'href', '/overview');
122122
cy.get('[data-testid="compare-agentx-dashboard-link"]')
123123
.should('contain.text', 'Full dashboard')
124-
.and('have.attr', 'href', '/inference/kimi-k3?i_seq=agentic-traces&i_optimal=1');
124+
.and('have.attr', 'href', '/inference/kimi-k3');
125125
cy.get('[data-testid="compare-agentx-methodology-link"]').should('not.exist');
126126
cy.get('[data-testid^="compare-agentx-model-"]').should('have.length', 5);
127127
// Editorial order, not alphabetical — see FEATURED_AGENTX_MODEL_SLUGS.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// @vitest-environment jsdom
2+
import { act, memo } from 'react';
3+
import { createRoot, type Root } from 'react-dom/client';
4+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5+
6+
import { Model, Sequence } from '@/lib/data-mappings';
7+
import type * as UrlStateModule from '@/lib/url-state';
8+
9+
/**
10+
* Regression coverage for the stale-`i_seq` snapshot bug behind the paramless
11+
* AgentX hero links (see `agentxDashboardHref`).
12+
*
13+
* The module-level URL snapshot retains self-written params across soft
14+
* navigations: every dashboard visit pins its resolved sequence via the
15+
* URL-sync effect, and `refreshUrlParams` does not evict keys absent from the
16+
* live URL. `getUrlParam('i_seq')` can therefore return the PREVIOUS page's
17+
* sequence on a navigation whose URL carries no `i_seq` at all. Applying that
18+
* value flips `sequenceExplicit`, which blocks the availability-driven AgentX
19+
* default — a bare `/inference/kimi-k3` hero link would open on 8K/1K.
20+
*
21+
* The provider must consult `hasExplicitUrlParam('i_seq')` (which IS refreshed
22+
* per navigation) before honoring the snapshot value.
23+
*/
24+
25+
const mocks = vi.hoisted(() => ({
26+
availability: {
27+
data: [] as unknown[],
28+
error: null as Error | null,
29+
isSuccess: true,
30+
},
31+
workflow: {
32+
data: undefined,
33+
isLoading: false,
34+
error: null as Error | null,
35+
},
36+
getUrlParam: vi.fn((key: string): string | undefined =>
37+
key === 'i_seq' ? '8k/1k' : undefined,
38+
),
39+
setUrlParams: vi.fn(),
40+
hasExplicitUrlParam: vi.fn((_key: string) => false),
41+
}));
42+
43+
vi.mock('@/hooks/api/use-availability', () => ({
44+
useAvailability: () => mocks.availability,
45+
}));
46+
vi.mock('@/hooks/api/use-workflow-info', () => ({
47+
useWorkflowInfo: () => mocks.workflow,
48+
}));
49+
vi.mock('@/hooks/useUrlState', () => ({
50+
useUrlState: () => ({ getUrlParam: mocks.getUrlParam, setUrlParams: mocks.setUrlParams }),
51+
}));
52+
vi.mock('@/lib/url-state', async (importOriginal) => ({
53+
...(await importOriginal<typeof UrlStateModule>()),
54+
hasExplicitUrlParam: (key: string) => mocks.hasExplicitUrlParam(key),
55+
refreshUrlParams: () => ({}),
56+
}));
57+
vi.mock('@/components/unofficial-run-provider', () => ({
58+
useUnofficialRun: () => ({ availableModelsAndSequences: [] }),
59+
}));
60+
61+
import { GlobalFilterProvider, useGlobalFilterSelection } from './GlobalFilterContext';
62+
63+
/** Kimi-K3 availability: both the Agentic and the 8K/1K scenario exist. */
64+
const KIMI_K3_ROWS = [
65+
{ model: 'kimik3', isl: null, osl: null, benchmark_type: 'agentic_traces' },
66+
{ model: 'kimik3', isl: 8192, osl: 1024, benchmark_type: 'single_turn' },
67+
];
68+
69+
let root: Root | undefined;
70+
let container: HTMLDivElement | undefined;
71+
let observedSequence: Sequence | undefined;
72+
let observedResolved: boolean | undefined;
73+
74+
const SequenceProbe = memo(() => {
75+
const selection = useGlobalFilterSelection();
76+
observedSequence = selection.effectiveSequence;
77+
observedResolved = selection.sequenceResolved;
78+
return null;
79+
});
80+
81+
function mountProvider(): void {
82+
container = document.createElement('div');
83+
root = createRoot(container);
84+
act(() =>
85+
root?.render(
86+
<GlobalFilterProvider initialModel={Model.Kimi_K3}>
87+
<SequenceProbe />
88+
</GlobalFilterProvider>,
89+
),
90+
);
91+
}
92+
93+
beforeEach(() => {
94+
mocks.availability.data = KIMI_K3_ROWS;
95+
mocks.getUrlParam.mockClear();
96+
mocks.setUrlParams.mockClear();
97+
mocks.hasExplicitUrlParam.mockClear();
98+
observedSequence = undefined;
99+
observedResolved = undefined;
100+
});
101+
102+
afterEach(() => {
103+
if (root) act(() => root?.unmount());
104+
container?.remove();
105+
root = undefined;
106+
container = undefined;
107+
});
108+
109+
describe('GlobalFilterProvider stale i_seq snapshot', () => {
110+
it('ignores a retained i_seq the current URL does not carry — AgentX default wins', () => {
111+
mocks.hasExplicitUrlParam.mockReturnValue(false);
112+
mountProvider();
113+
expect(observedResolved).toBe(true);
114+
expect(observedSequence).toBe(Sequence.AgenticTraces);
115+
});
116+
117+
it('still honors an i_seq explicitly present in the current URL', () => {
118+
mocks.hasExplicitUrlParam.mockImplementation((key: string) => key === 'i_seq');
119+
mountProvider();
120+
expect(observedResolved).toBe(true);
121+
expect(observedSequence).toBe(Sequence.EightK_OneK);
122+
});
123+
});

‎packages/app/src/components/GlobalFilterContext.tsx‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@ export function GlobalFilterProvider({
225225

226226
const [selectedSequence, setSelectedSequenceRaw] = useState<Sequence>(() => {
227227
if (initialSequence) return initialSequence;
228-
const urlSeq = getUrlParam('i_seq');
228+
// Only honor `i_seq` when the CURRENT navigation's URL carries it. The
229+
// module-level snapshot retains self-written params across soft
230+
// navigations (the URL-sync effect pins the resolved sequence on every
231+
// dashboard visit), so an unguarded read would revive the previous page's
232+
// sequence — e.g. a paramless AgentX hero link opening on a stale 8k/1k.
233+
const urlSeq = hasExplicitUrlParam('i_seq') ? getUrlParam('i_seq') : undefined;
229234
if (urlSeq && Object.values(Sequence).includes(urlSeq as Sequence)) return urlSeq as Sequence;
230235
// Default to the 8K/1K fixed-seq scenario; the effectiveSequence resolution
231236
// below prefers the Agentic scenario when availability confirms the model
@@ -339,7 +344,13 @@ export function GlobalFilterProvider({
339344
if (pathModel === null || hasExplicitUrlParam('g_model')) {
340345
applyIfEnum('g_model', Model, setSelectedModel);
341346
}
342-
applyIfEnum('i_seq', Sequence, setSelectedSequence);
347+
// Same guard as the initializer above: `i_seq` applies (and flips
348+
// `sequenceExplicit`, which blocks the availability-driven AgentX default)
349+
// only when the current URL actually carries it — never from the snapshot's
350+
// retained self-writes of a previously visited page.
351+
if (hasExplicitUrlParam('i_seq')) {
352+
applyIfEnum('i_seq', Sequence, setSelectedSequence);
353+
}
343354
const urlPrec = getUrlParam('i_prec');
344355
if (urlPrec) {
345356
const precs = urlPrec

‎packages/app/src/lib/compare-agentx.test.ts‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,14 @@ describe('AgentX comparison links', () => {
1919
]);
2020
});
2121

22-
it('opens the selected model subroute directly in the Agentic Traces scenario', () => {
23-
expect(agentxDashboardHref('en', FEATURED_AGENTX_MODELS[0])).toBe(
24-
'/inference/kimi-k3?i_seq=agentic-traces&i_optimal=1',
25-
);
26-
expect(agentxDashboardHref('zh', FEATURED_AGENTX_MODELS[1])).toBe(
27-
'/zh/inference/deepseek-v4?i_seq=agentic-traces&i_optimal=1',
28-
);
22+
it('opens the bare model subroute — Agentic + Optimal Only are already the defaults', () => {
23+
expect(agentxDashboardHref('en', FEATURED_AGENTX_MODELS[0])).toBe('/inference/kimi-k3');
24+
expect(agentxDashboardHref('zh', FEATURED_AGENTX_MODELS[1])).toBe('/zh/inference/deepseek-v4');
2925
});
3026

31-
it('routes every featured model to a registered inference page', () => {
27+
it('routes every featured model to a registered inference page without query params', () => {
3228
for (const model of FEATURED_AGENTX_MODELS) {
33-
expect(agentxDashboardHref('en', model)).toBe(
34-
`/inference/${model.slug}?i_seq=agentic-traces&i_optimal=1`,
35-
);
29+
expect(agentxDashboardHref('en', model)).toBe(`/inference/${model.slug}`);
3630
}
3731
});
3832

‎packages/app/src/lib/compare-agentx.ts‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,18 @@ export function agentxDashboardHref(locale: 'en' | 'zh', model: CompareModelSlug
3131
// of a `?g_model=` variant of the base dashboard. Models without a
3232
// registered inference page (none of the featured set today) fall back to
3333
// the query form.
34+
//
35+
// No `i_seq` / `i_optimal` params: every model here has AgentX data, and the
36+
// dashboard already defaults such models to the Agentic scenario
37+
// (resolveEffectiveSequence) with "Optimal Only" on (i_optimal !== '0'), so
38+
// the bare model page IS the AgentX optimal-only view — and the canonical,
39+
// shareable address for it.
3440
const entry = getInferenceModelBySlug(model.slug);
35-
const query = new URLSearchParams();
36-
if (!entry) query.set('g_model', model.displayName);
37-
query.set('i_seq', 'agentic-traces');
38-
query.set('i_optimal', '1');
3941
const path = entry ? inferenceModelPath(entry.slug) : '/inference';
40-
return `${locale === 'zh' ? `/zh${path}` : path}?${query}`;
42+
const localizedPath = locale === 'zh' ? `/zh${path}` : path;
43+
if (entry) return localizedPath;
44+
const query = new URLSearchParams({ g_model: model.displayName });
45+
return `${localizedPath}?${query}`;
4146
}
4247

4348
export function comparisonScenarioForModel(model: CompareModelSlug): ComparisonScenario {

0 commit comments

Comments
 (0)