From 00538d82fbac3e8745d8fac4fa9fb78fbeae5626 Mon Sep 17 00:00:00 2001 From: functionstackx <47992694+functionstackx@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:54:27 +0000 Subject: [PATCH] fix(charts): replace deprecated watermarks with a source suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert b39ac9c58415976e9d3b0c8ae3ce53d466a7f0f0 and mark deprecated models in the existing source caption instead. Preserve PNG exports, unofficial attribution, and Chinese localization. 中文:移除已弃用模型的背景水印,改为在来源署名后添加状态后缀;保留 PNG 导出、非官方来源说明和中文文案。 --- .../app/cypress/e2e/inference-chart.cy.ts | 77 +++++++++---------- .../components/inference/ui/ChartDisplay.tsx | 2 + .../src/components/inference/ui/GPUGraph.tsx | 7 +- .../components/inference/ui/PowerTimeline.tsx | 5 +- .../components/inference/ui/ScatterGraph.tsx | 2 - .../src/components/ui/result-context.test.tsx | 31 ++++++++ .../app/src/components/ui/result-context.tsx | 7 +- .../app/src/lib/catalog/data-mappings.test.ts | 22 ------ packages/app/src/lib/catalog/data-mappings.ts | 8 -- .../app/src/lib/d3-chart/D3Chart/D3Chart.tsx | 2 - .../app/src/lib/d3-chart/D3Chart/types.ts | 1 - .../d3-chart/D3Chart/useD3ChartRenderer.ts | 3 - .../app/src/lib/d3-chart/chart-setup.test.ts | 71 ----------------- packages/app/src/lib/d3-chart/chart-setup.ts | 4 - packages/app/src/lib/d3-chart/types.ts | 1 - packages/app/src/lib/d3-chart/typography.ts | 1 - packages/app/src/lib/d3-chart/watermark.ts | 46 ----------- .../app/src/lib/views-api/docs/options.ts | 4 +- .../skills/inferencex-api/integrity.json | 2 +- .../references/dashboard-views.md | 24 +++--- 20 files changed, 92 insertions(+), 228 deletions(-) diff --git a/packages/app/cypress/e2e/inference-chart.cy.ts b/packages/app/cypress/e2e/inference-chart.cy.ts index 5fa283b07..6568ef243 100644 --- a/packages/app/cypress/e2e/inference-chart.cy.ts +++ b/packages/app/cypress/e2e/inference-chart.cy.ts @@ -155,7 +155,6 @@ describe('Inference Chart', () => { .and('not.contain.text', 'Workload:') .and('not.contain.text', 'Precision:') .and('not.contain.text', 'Metric:'); - cy.get('[data-testid="deprecated-watermark"]').should('not.exist'); }); it('plots OpenRouter-priced token revenue for official and unofficial runs', () => { @@ -390,8 +389,8 @@ describe('Inference Chart', () => { }); }); -describe('Deprecated model background watermark', () => { - it('marks every deprecated model and clears the mark for an active model', () => { +describe('Deprecated model source attribution', () => { + it('marks every deprecated model in the source, without a background watermark', () => { cy.viewport(1280, 900); for (const [display, dbKey, deprecated] of [ ['MiniMax-M3', 'minimaxm3', true], @@ -411,19 +410,14 @@ describe('Deprecated model background watermark', () => { onBeforeLoad: unlockAgenticGate, }); cy.get('#chart-0 svg[data-testid="d3-chart-svg"]').should('be.visible'); - if (deprecated) { - cy.get('#chart-0 .deprecated-watermark') - .should('have.length', 2) - .each(($mark) => { - cy.wrap($mark).should('have.text', 'Deprecated').and('have.attr', 'opacity', '0.3'); - }); - } else { - cy.get('#chart-0 .deprecated-watermark').should('not.exist'); - } + cy.get('#chart-0 [data-testid="result-context"]') + .should('contain.text', 'Source: SemiAnalysis InferenceX™') + .and(deprecated ? 'contain.text' : 'not.contain.text', '(deprecated)'); + cy.get('.deprecated-watermark').should('not.exist'); } }); - it('keeps a large plot watermark in PNG exports, overlays, and the Chinese mobile chart', () => { + it('keeps the suffix in PNG exports, overlays, and the Chinese mobile chart', () => { const model = 'minimaxm3'; const officialRows = b300Rows(null).map((row) => ({ ...row, model })); const overlayRows = b300Rows(OVERLAY_RUN_URL).map((row) => ({ ...row, model })); @@ -454,58 +448,61 @@ describe('Deprecated model background watermark', () => { cy.visit('/inference/minimax-m3?i_seq=agentic-traces', { onBeforeLoad: unlockAgenticGate, }); - cy.get('#chart-0 [data-testid="result-context"]') - .should('contain.text', 'Source: SemiAnalysis InferenceX™') - .and('not.contain.text', 'Historical benchmark — submissions closed'); - cy.get('#chart-0 svg [data-testid="deprecated-watermark"]').should('be.visible'); + cy.get('#chart-0 [data-testid="result-context"]').should( + 'contain.text', + 'Source: SemiAnalysis InferenceX™ (deprecated)', + ); let exportedPng = ''; - let exportedLabels: string[] = []; + let exportedCaption = ''; cy.window().then((win) => { win.HTMLAnchorElement.prototype.click = function () { if (this.download.endsWith('.png')) { exportedPng = this.href; - exportedLabels = [ - ...win.document.querySelectorAll('#chart-0-export .deprecated-watermark'), - ].map((mark) => mark.textContent ?? ''); + exportedCaption = + win.document.querySelector('#chart-0-export [data-testid="result-context"]') + ?.textContent ?? ''; + expect(win.document.querySelector('#chart-0-export .deprecated-watermark')).to.equal( + null, + ); } }; }); cy.get('[data-testid="chart-figure"] [data-testid="export-button"]').first().click(); cy.get('[data-testid="export-png-button"]').click(); - cy.window() - .should(() => expect(exportedPng).to.match(/^data:image\/png;base64,/u)) - .then(() => { - expect(exportedLabels).to.deep.equal(['Deprecated', 'Deprecated']); - cy.writeFile( - 'cypress/downloads/deprecated-model-watermark.png', - exportedPng.split(',')[1], - 'base64', - ); - }); + cy.window().should(() => { + expect(exportedPng).to.match(/^data:image\/png;base64,/u); + expect(exportedCaption).to.contain('Source: SemiAnalysis InferenceX™ (deprecated)'); + }); cy.visit(`/inference/minimax-m3?i_seq=agentic-traces&unofficialrun=${OVERLAY_RUN_ID}`, { onBeforeLoad: unlockAgenticGate, }); cy.wait('@historicalM3Overlay'); cy.get('#chart-0 .unofficial-overlay-pt').should('have.length.greaterThan', 0); - cy.get('#chart-0 .deprecated-watermark').should('contain.text', 'Deprecated'); + cy.get('#chart-0 [data-testid="result-context"]').should( + 'contain.text', + 'Source: SemiAnalysis InferenceX™ + UNOFFICIAL overlays (deprecated)', + ); cy.get('#chart-0 .unofficial-watermark-image').should('exist'); + cy.get('.deprecated-watermark').should('not.exist'); cy.get('[aria-label="Dismiss historical-m3-overlay"]').click(); cy.get('#chart-0 .unofficial-overlay-pt').should('not.exist'); - cy.get('#chart-0 svg [data-testid="deprecated-watermark"]').should('be.visible'); + cy.get('#chart-0 [data-testid="result-context"]').should( + 'contain.text', + 'Source: SemiAnalysis InferenceX™ (deprecated)', + ); cy.viewport(375, 900); cy.visit(`/zh/inference/minimax-m3?i_seq=agentic-traces&unofficialrun=${OVERLAY_RUN_ID}`, { onBeforeLoad: unlockAgenticGate, }); cy.wait('@historicalM3Overlay'); - cy.get('#chart-0 svg [data-testid="deprecated-watermark"]') - .should('be.visible') - .and('have.length', 2) - .each(($mark) => cy.wrap($mark).should('have.text', '已弃用')); - cy.document().then((doc) => { - expect(doc.documentElement.scrollWidth).to.be.at.most(doc.documentElement.clientWidth); - }); + cy.get('#chart-0 [data-testid="result-context"]').should( + 'contain.text', + '来源: SemiAnalysis InferenceX™ + 非官方 overlays (已弃用)', + ); + cy.get('.deprecated-watermark').should('not.exist'); + expectNoPageOverflow(); }); }); diff --git a/packages/app/src/components/inference/ui/ChartDisplay.tsx b/packages/app/src/components/inference/ui/ChartDisplay.tsx index ca8ba5ed3..407972338 100644 --- a/packages/app/src/components/inference/ui/ChartDisplay.tsx +++ b/packages/app/src/components/inference/ui/ChartDisplay.tsx @@ -85,6 +85,7 @@ import { getModelLabel, getPrecisionLabel, getSequenceLabel, + isModelDeprecated, sequenceKind, } from '@/lib/catalog/data-mappings'; import { useComparisonChangelogs } from '@/hooks/api/use-comparison-changelogs'; @@ -1085,6 +1086,7 @@ export default function ChartDisplay({ embedded = false }: { embedded?: boolean 0 ? 'unofficial' : 'logo'} - backgroundWatermark={getDeprecatedChartWatermark(selectedModel, locale)} testId="power-timeline-chart-svg" grabCursor instructions={t.instructions} diff --git a/packages/app/src/components/inference/ui/ScatterGraph.tsx b/packages/app/src/components/inference/ui/ScatterGraph.tsx index 1bec27b01..1418457f7 100644 --- a/packages/app/src/components/inference/ui/ScatterGraph.tsx +++ b/packages/app/src/components/inference/ui/ScatterGraph.tsx @@ -45,7 +45,6 @@ import { } from '@/lib/catalog/constants'; import { getChartWatermark, - getDeprecatedChartWatermark, getPrecisionLabel, getSequenceLabel, type Precision, @@ -3724,7 +3723,6 @@ const ScatterGraph = React.memo( metricIdentity={metricIdentity} margin={CHART_MARGIN} watermark={getChartWatermark(isUnofficialRun)} - backgroundWatermark={getDeprecatedChartWatermark(modelLabel, locale)} testId="scatter-graph" grabCursor={true} caption={caption} diff --git a/packages/app/src/components/ui/result-context.test.tsx b/packages/app/src/components/ui/result-context.test.tsx index e60e54b72..97d000cce 100644 --- a/packages/app/src/components/ui/result-context.test.tsx +++ b/packages/app/src/components/ui/result-context.test.tsx @@ -27,6 +27,37 @@ function renderContext(locale: 'en' | 'zh') { } describe('ResultContext', () => { + it.each([ + ['en', 'SemiAnalysis InferenceX™', 'Source: SemiAnalysis InferenceX™ (deprecated)'], + ['zh', 'SemiAnalysis InferenceX™', '来源: SemiAnalysis InferenceX™ (已弃用)'], + [ + 'en', + 'SemiAnalysis InferenceX™ + UNOFFICIAL overlays', + 'Source: SemiAnalysis InferenceX™ + UNOFFICIAL overlays (deprecated)', + ], + [ + 'zh', + 'SemiAnalysis InferenceX™ + 非官方 overlays', + '来源: SemiAnalysis InferenceX™ + 非官方 overlays (已弃用)', + ], + ] as const)('appends the deprecated status to the %s source %s', (locale, source, expected) => { + const container = document.createElement('div'); + const root = createRoot(container); + act(() => root.render()); + expect(container.textContent).toBe(expected); + act(() => root.render()); + expect(container.textContent).toBe(`${locale === 'zh' ? '来源' : 'Source'}: ${source}`); + act(() => root.unmount()); + }); + + it('does not render an orphan deprecated label without a source', () => { + const container = document.createElement('div'); + const root = createRoot(container); + act(() => root.render()); + expect(container.textContent).toBe(''); + act(() => root.unmount()); + }); + it('renders all effective result inputs in English', () => { const context = renderContext('en'); expect(context.querySelector('[data-testid="result-context"]')?.textContent).toContain( diff --git a/packages/app/src/components/ui/result-context.tsx b/packages/app/src/components/ui/result-context.tsx index 7853ed85f..88f8b01f7 100644 --- a/packages/app/src/components/ui/result-context.tsx +++ b/packages/app/src/components/ui/result-context.tsx @@ -24,6 +24,7 @@ export interface ResultContextProps { dates?: readonly string[]; dateRange?: { start: string; end: string }; source?: string; + deprecated?: boolean; costBasis?: string; costBasisTestId?: string; } @@ -83,6 +84,7 @@ export function ResultContext({ dates, dateRange, source, + deprecated = false, costBasis, costBasisTestId, }: ResultContextProps) { @@ -204,7 +206,10 @@ export function ResultContext({ {source && (
{labels.source}:
{' '} -
{source}
+
+ {source} + {deprecated && (locale === 'zh' ? ' (已弃用)' : ' (deprecated)')} +
)} {costBasis && ( diff --git a/packages/app/src/lib/catalog/data-mappings.test.ts b/packages/app/src/lib/catalog/data-mappings.test.ts index b61d7ad83..a3c694b14 100644 --- a/packages/app/src/lib/catalog/data-mappings.test.ts +++ b/packages/app/src/lib/catalog/data-mappings.test.ts @@ -4,7 +4,6 @@ import { getModelAndSequence, getModelAndSequenceFromArtifact, getModelLabel, - getDeprecatedChartWatermark, getOpenRouterModelId, MODEL_OPTIONS, SEQUENCE_OPTIONS, @@ -168,27 +167,6 @@ describe('getModelAndSequenceFromArtifact', () => { }); describe('isModelDeprecated', () => { - it.each([ - Model.MiniMax_M3, - Model.MiniMax_M2_5, - Model.Kimi_K2_5, - Model.GLM_5, - Model.GptOss, - Model.Llama3_3_70B, - ])('adds a localized Deprecated watermark to %s', (model) => { - expect(getDeprecatedChartWatermark(model, 'en')).toBe('Deprecated'); - expect(getDeprecatedChartWatermark(model, 'zh')).toBe('已弃用'); - }); - - it.each([ - Model.MiniMax_M3_1_Flash, - Model.DeepSeek_R1, - Model.Qwen3_8_27B, - Model.Llama3_1_70B, - undefined, - ])('does not mark the non-deprecated model %s', (model) => { - expect(getDeprecatedChartWatermark(model, 'en')).toBeUndefined(); - }); it('returns true for deprecated model Llama3_3_70B', () => { expect(isModelDeprecated(Model.Llama3_3_70B)).toBe(true); }); diff --git a/packages/app/src/lib/catalog/data-mappings.ts b/packages/app/src/lib/catalog/data-mappings.ts index b7eee9bf1..37a38fb15 100644 --- a/packages/app/src/lib/catalog/data-mappings.ts +++ b/packages/app/src/lib/catalog/data-mappings.ts @@ -257,14 +257,6 @@ export function getChartWatermark(isUnofficialRun = false): 'logo' | 'unofficial return isUnofficialRun ? 'unofficial' : 'logo'; } -export function getDeprecatedChartWatermark( - model: string | undefined, - locale: 'en' | 'zh', -): string | undefined { - if (!model || !isModelDeprecated(model as Model)) return undefined; - return locale === 'zh' ? '已弃用' : 'Deprecated'; -} - export const MODEL_PREFIX_MAPPING: Record = Object.fromEntries( (Object.entries(MODEL_CONFIG) as [Model, (typeof MODEL_CONFIG)[Model]][]) .filter(([, c]) => c.prefix) diff --git a/packages/app/src/lib/d3-chart/D3Chart/D3Chart.tsx b/packages/app/src/lib/d3-chart/D3Chart/D3Chart.tsx index a6ebf28a1..4a466736d 100644 --- a/packages/app/src/lib/d3-chart/D3Chart/D3Chart.tsx +++ b/packages/app/src/lib/d3-chart/D3Chart/D3Chart.tsx @@ -21,7 +21,6 @@ function D3ChartInner( height = 600, margin, watermark, - backgroundWatermark, testId, grabCursor = true, instructions, @@ -108,7 +107,6 @@ function D3ChartInner( height, margin, watermark, - backgroundWatermark, testId, grabCursor, instructions, diff --git a/packages/app/src/lib/d3-chart/D3Chart/types.ts b/packages/app/src/lib/d3-chart/D3Chart/types.ts index 027fdcd11..775cf2ef8 100644 --- a/packages/app/src/lib/d3-chart/D3Chart/types.ts +++ b/packages/app/src/lib/d3-chart/D3Chart/types.ts @@ -195,7 +195,6 @@ export interface D3ChartProps { height?: number; margin?: ChartMargin; watermark?: 'logo' | 'unofficial' | 'none'; - backgroundWatermark?: string; testId?: string; grabCursor?: boolean; instructions?: string; diff --git a/packages/app/src/lib/d3-chart/D3Chart/useD3ChartRenderer.ts b/packages/app/src/lib/d3-chart/D3Chart/useD3ChartRenderer.ts index f84970f33..b50cf7d21 100644 --- a/packages/app/src/lib/d3-chart/D3Chart/useD3ChartRenderer.ts +++ b/packages/app/src/lib/d3-chart/D3Chart/useD3ChartRenderer.ts @@ -232,7 +232,6 @@ export function useD3ChartRenderer(props: D3ChartProps, deps: RendererDeps data, margin = { top: 24, right: 10, bottom: 40, left: 60 }, watermark = 'logo', - backgroundWatermark, clipContent = true, dataIdentity, metricIdentity, @@ -318,7 +317,6 @@ export function useD3ChartRenderer(props: D3ChartProps, deps: RendererDeps containerHeight: dimensions.height, margin, watermark: getDomainAwareChartWatermark(watermark, window.location.hostname), - backgroundWatermark, xLabel: xAxisConfig?.label, yLabel: yAxisConfig?.label, clipContent, @@ -333,7 +331,6 @@ export function useD3ChartRenderer(props: D3ChartProps, deps: RendererDeps margin.bottom, margin.left, watermark, - backgroundWatermark, xAxisConfig?.label, yAxisConfig?.label, clipContent, diff --git a/packages/app/src/lib/d3-chart/chart-setup.test.ts b/packages/app/src/lib/d3-chart/chart-setup.test.ts index f7a673e3f..faf5682f3 100644 --- a/packages/app/src/lib/d3-chart/chart-setup.test.ts +++ b/packages/app/src/lib/d3-chart/chart-setup.test.ts @@ -186,77 +186,6 @@ describe('setupChartStructure', () => { }); describe('watermark variants', () => { - it.each([ - [400, 600], - [1200, 600], - [800, 300], - ])( - 'preserves the SemiAnalysis logo and places Deprecated above and below it at %s×%s', - (containerWidth, containerHeight) => { - const svgEl = makeSvgEl(); - const config = defaultConfig({ watermark: 'logo', containerWidth, containerHeight }); - setupChartStructure(svgEl, config); - const originalLogo = svgEl.querySelector('#logo-pattern-test-chart')!.outerHTML; - const originalRect = svgEl.querySelector('.watermark-rect')!.outerHTML; - - setupChartStructure(svgEl, { ...config, backgroundWatermark: 'Deprecated' }); - expect(svgEl.querySelector('#logo-pattern-test-chart')!.outerHTML).toBe(originalLogo); - expect(svgEl.querySelector('.watermark-rect')!.outerHTML).toBe(originalRect); - const logo = svgEl.querySelector('#logo-pattern-test-chart image')!; - const above = svgEl.querySelector('.deprecated-watermark[data-position="above"]')!; - const status = svgEl.querySelector('.deprecated-watermark[data-position="below"]')!; - expect(svgEl.querySelectorAll('.deprecated-watermark')).toHaveLength(2); - expect(Number(above.getAttribute('y'))).toBeGreaterThanOrEqual(config.margin.top); - expect(Number(above.getAttribute('y')) + Number(above.getAttribute('height'))).toBeLessThan( - Number(logo.getAttribute('y')), - ); - expect(above.getAttribute('opacity')).toBe('0.3'); - expect(status.getAttribute('opacity')).toBe('0.3'); - const logoBottom = Number(logo.getAttribute('y')) + Number(logo.getAttribute('height')); - expect(Number(status.getAttribute('y'))).toBeGreaterThan(logoBottom); - expect( - Number(status.getAttribute('y')) + Number(status.getAttribute('height')), - ).toBeLessThanOrEqual(containerHeight - config.margin.bottom); - }, - ); - it('keeps a large deprecated watermark behind the plot alongside unofficial marks through resize and removal', () => { - const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - setupChartStructure( - svgEl, - defaultConfig({ - watermark: 'unofficial', - backgroundWatermark: 'Deprecated', - }), - ); - const mark = svgEl.querySelector('.deprecated-watermark')!; - expect(mark.parentElement).toBe(svgEl); - expect([...svgEl.children].indexOf(mark)).toBeLessThan( - [...svgEl.children].indexOf(svgEl.querySelector('.chart-root')!), - ); - expect(mark.getAttribute('viewBox')).toBe('0 0 1000 200'); - expect(mark.getAttribute('pointer-events')).toBe('none'); - expect([...mark.querySelectorAll('text')].map((node) => node.textContent)).toEqual([ - 'Deprecated', - ]); - expect(svgEl.querySelector('.unofficial-watermark-image')).not.toBeNull(); - - const resized = defaultConfig({ - watermark: 'unofficial', - backgroundWatermark: '已弃用', - containerWidth: 400, - }); - setupChartStructure(svgEl, resized); - expect(svgEl.querySelectorAll('.deprecated-watermark')).toHaveLength(2); - expect(svgEl.querySelector('.deprecated-watermark')?.getAttribute('width')).toBe( - String((400 - resized.margin.left - resized.margin.right) * 0.8), - ); - expect(svgEl.querySelector('.deprecated-watermark')?.textContent).toBe('已弃用'); - - setupChartStructure(svgEl, defaultConfig({ watermark: 'logo' })); - expect(svgEl.querySelector('.deprecated-watermark')).toBeNull(); - expect(svgEl.querySelector('.unofficial-watermark-image')).toBeNull(); - expect(svgEl.querySelector('.watermark-rect')).not.toBeNull(); - }); it('creates logo watermark when watermark is "logo"', () => { const svgEl = makeSvgEl(); const config = defaultConfig({ watermark: 'logo' }); diff --git a/packages/app/src/lib/d3-chart/chart-setup.ts b/packages/app/src/lib/d3-chart/chart-setup.ts index 4c3fbda15..da556206a 100644 --- a/packages/app/src/lib/d3-chart/chart-setup.ts +++ b/packages/app/src/lib/d3-chart/chart-setup.ts @@ -6,7 +6,6 @@ import { createLogoWatermark, createUnofficialWatermark, positionUnofficialWatermarkImage, - updateBackgroundWatermark, } from './watermark'; export function setupChartStructure( @@ -120,7 +119,6 @@ export function setupChartStructure( .text(config.xLabel); } - updateBackgroundWatermark(svg, config.backgroundWatermark, width, height, margin); return { svg, g, zoomGroup, xAxisGroup, yAxisGroup, gridGroup, defs, width, height, margin }; } @@ -197,8 +195,6 @@ export function setupChartStructure( } } - updateBackgroundWatermark(svg, config.backgroundWatermark, width, height, margin); - const yLabel = svg.select('.y-axis-label'); if (!yLabel.empty()) { yLabel.attr('x', -(margin.top + height / 2)).text(config.yLabel ?? ''); diff --git a/packages/app/src/lib/d3-chart/types.ts b/packages/app/src/lib/d3-chart/types.ts index caa53d592..a9cc10e9c 100644 --- a/packages/app/src/lib/d3-chart/types.ts +++ b/packages/app/src/lib/d3-chart/types.ts @@ -26,7 +26,6 @@ export interface ChartSetupConfig { containerHeight: number; margin: ChartMargin; watermark: 'logo' | 'unofficial' | 'none'; - backgroundWatermark?: string; xLabel?: string; yLabel?: string; clipContent?: boolean; diff --git a/packages/app/src/lib/d3-chart/typography.ts b/packages/app/src/lib/d3-chart/typography.ts index f60a13613..d44c5a60f 100644 --- a/packages/app/src/lib/d3-chart/typography.ts +++ b/packages/app/src/lib/d3-chart/typography.ts @@ -7,7 +7,6 @@ export const CHART_TYPE = { lineLabel: 13, micro: 9, watermark: 20, - backgroundWatermark: 108, } as const; export function px(size: number): string { diff --git a/packages/app/src/lib/d3-chart/watermark.ts b/packages/app/src/lib/d3-chart/watermark.ts index 56805b888..f08f15914 100644 --- a/packages/app/src/lib/d3-chart/watermark.ts +++ b/packages/app/src/lib/d3-chart/watermark.ts @@ -5,52 +5,6 @@ import { CHART_TYPE, px } from './typography'; const UNOFFICIAL_WATERMARK_IMAGE_PATH = '/decorative/kanye-west.png'; -export function updateBackgroundWatermark( - svg: d3.Selection, - label: string | undefined, - innerWidth: number, - innerHeight: number, - margin: ChartMargin, -): void { - svg.selectAll('.deprecated-watermark').remove(); - if (!label) return; - - // Main's centered logo occupies at most the middle 60% of the plot height. - // Reserve separate bands above and below it without moving or resizing it. - for (const [position, offset] of [ - ['above', 0.02], - ['below', 0.82], - ] as const) { - const mark = svg - .insert('svg', '.chart-root') - .attr('class', 'deprecated-watermark') - .attr('data-testid', 'deprecated-watermark') - .attr('data-position', position) - .attr('x', margin.left + innerWidth * 0.1) - .attr('y', margin.top + innerHeight * offset) - .attr('width', innerWidth * 0.8) - .attr('height', innerHeight * 0.16) - .attr('viewBox', '0 0 1000 200') - .attr('preserveAspectRatio', 'xMidYMid meet') - .attr('fill', 'currentColor') - .attr('opacity', 0.3) - .attr('pointer-events', 'none') - .attr('role', 'img') - .attr('aria-label', label) - .attr('aria-hidden', position === 'below' ? 'true' : null); - - mark - .append('text') - .attr('x', 500) - .attr('y', 100) - .attr('dominant-baseline', 'middle') - .attr('text-anchor', 'middle') - .attr('font-size', px(CHART_TYPE.backgroundWatermark)) - .attr('font-weight', 800) - .text(label); - } -} - function insertWatermarkRect( svg: d3.Selection, innerWidth: number, diff --git a/packages/app/src/lib/views-api/docs/options.ts b/packages/app/src/lib/views-api/docs/options.ts index 3c7e3b2d0..659183c83 100644 --- a/packages/app/src/lib/views-api/docs/options.ts +++ b/packages/app/src/lib/views-api/docs/options.ts @@ -239,8 +239,8 @@ export const operations: ApiOperation[] = [ path: '/api/v1/views/options', summary: text('Discover every views-API option domain', '获取 views API 的全部选项域'), description: text( - 'Static discovery endpoint for the views API: models (with DB keys, category and release date), sequences, precisions, hardware (with vendor, architecture, TDP and per-provider hourly cost), frameworks, speculative-decoding methods, percentiles, x-axis and scale modes, chart metrics (bilingual labels, unit, polarity), quick-filter domains, reliability ranges, and overview/calculator/fleet option sets — plus the defaults the dashboard itself uses. Values come from the same registries the dashboard renders its controls from, so this response is the authoritative input catalog for the other /api/v1/views endpoints. Models with category deprecated display Deprecated background watermarks in separate bands above and below the existing SemiAnalysis logo. This is presentation only and does not change view data. No database access.', - 'views API 的静态发现端点:模型(含数据库键、分类与发布日期)、序列、精度、硬件(含厂商、架构、TDP 及各供应商每小时成本)、推理框架、投机解码方法、百分位、x 轴与坐标缩放模式、图表指标(中英文标签、单位、极性)、快捷筛选域、可靠性时间范围,以及 overview/calculator/fleet 的选项集,并附仪表盘实际使用的默认值。所有取值直接来自仪表盘控件渲染所用的注册表,因此该响应是其余 /api/v1/views 端点的权威输入目录。分类为 deprecated 的模型会在现有 SemiAnalysis 标志上方和下方的独立区域显示“已弃用”背景水印。该水印仅用于展示,不改变视图数据。不访问数据库。', + 'Static discovery endpoint for the views API: models (with DB keys, category and release date), sequences, precisions, hardware (with vendor, architecture, TDP and per-provider hourly cost), frameworks, speculative-decoding methods, percentiles, x-axis and scale modes, chart metrics (bilingual labels, unit, polarity), quick-filter domains, reliability ranges, and overview/calculator/fleet option sets — plus the defaults the dashboard itself uses. Values come from the same registries the dashboard renders its controls from, so this response is the authoritative input catalog for the other /api/v1/views endpoints. Inference charts for models with category deprecated append (deprecated) to the source attribution, including PNG exports and unofficial-run overlays, without deprecated background watermarks. This is presentation only and does not change view data. No database access.', + 'views API 的静态发现端点:模型(含数据库键、分类与发布日期)、序列、精度、硬件(含厂商、架构、TDP 及各供应商每小时成本)、推理框架、投机解码方法、百分位、x 轴与坐标缩放模式、图表指标(中英文标签、单位、极性)、快捷筛选域、可靠性时间范围,以及 overview/calculator/fleet 的选项集,并附仪表盘实际使用的默认值。所有取值直接来自仪表盘控件渲染所用的注册表,因此该响应是其余 /api/v1/views 端点的权威输入目录。分类为 deprecated 的模型会在推理图表的来源署名后添加 (已弃用),英文页面显示 (deprecated);PNG 导出和非官方 run overlay 中也保留该后缀,不再显示“已弃用”背景水印。此变更仅影响展示,不改变视图数据。不访问数据库。', ), audience: 'public', stability: 'beta', diff --git a/packages/skills/skills/inferencex-api/integrity.json b/packages/skills/skills/inferencex-api/integrity.json index 1f8eccdca..17b377872 100644 --- a/packages/skills/skills/inferencex-api/integrity.json +++ b/packages/skills/skills/inferencex-api/integrity.json @@ -9,7 +9,7 @@ "references/cli-contract.md": "4f070877eeefffad2860c58f79f68432fbe27aa21fc578a1c696f0309a3d7d7b", "references/cli.md": "11e1183f7ed70d669019e8435ac16a6a9f873de027507b8850a9943f6659bfd9", "references/collectivex.md": "f79cf8e2779a9289ab69f79f4a4b338e0b9a5f76a3750a3e969f33a744e726a1", - "references/dashboard-views.md": "47ef953ebdb4125144d801a697ef9dcabaa4dd02bcb083a052c127635e163990", + "references/dashboard-views.md": "cad0dfc02b105f91a333842328e674b8300fffa733600d6bdd9183dc160d1e97", "references/offline-exports.md": "95aa565dcd4c9e592159baa560a9a58217bc61e395de1c6daf0576795c7f4ec9", "references/pareto.md": "1b4d2d163f982e3f2d97addce310789eae9c1e5badd82dd7501708c9e0385027", "references/powerx.md": "5af5527d54a7e6191a5769080c122638b333b1ceb59e19fdee7df662a5cd3c8f", diff --git a/packages/skills/skills/inferencex-api/references/dashboard-views.md b/packages/skills/skills/inferencex-api/references/dashboard-views.md index 710a25a4e..1cedbe485 100644 --- a/packages/skills/skills/inferencex-api/references/dashboard-views.md +++ b/packages/skills/skills/inferencex-api/references/dashboard-views.md @@ -33,19 +33,17 @@ results or treat its interpolated values as separately measured operating points for static model, hardware, sequence and metric registries; use each specialized view's `options`, `configurations`, `workloads`, or raw run-list operation for data-dependent choices. - Every model with `category=deprecated` in `options.models` receives large - “Deprecated” plot watermarks (“已弃用” in Chinese), including PNG exports and - unofficial-run overlays. They appear in separate bands above and below the existing SemiAnalysis logo - so the marks do not overlap; logo styling and official-domain behavior follow the - main site. This is presentation only; view API values and filters are unchanged. - Deprecated models remain selectable through explicit model parameters and - overview `modelScope=all`. MiniMax M3 is deprecated; M3.1 Flash remains default. - - 中文:`options.models` 中分类为 `deprecated` 的所有模型,都会在绘图区背景中显示大号 - “已弃用”水印,PNG 导出和非官方 run overlay 中同样保留。水印分别放在现有 SemiAnalysis - 标志的上方和下方,彼此不会重叠;标志样式及其在正式域名上的显示规则与主站一致。该水印仅用于 - 展示,不改变视图 API 的数值或筛选行为。已弃用模型仍可通过显式模型参数或 overview 的 - `modelScope=all` 查询。MiniMax M3 已弃用,M3.1 Flash 仍保留在默认模型组中。 + MiniMax M3 has `category=deprecated` in `options.models`; it remains selectable + through explicit model parameters and overview `modelScope=all`. MiniMax M3.1 + Flash remains in the default model group. + For models with `category=deprecated`, inference charts append `(deprecated)` + to the source attribution, or `(已弃用)` in Chinese. The suffix is also included + in PNG exports and unofficial-run overlays; there are no deprecated background + watermarks. This is presentation only: view API values and filters are unchanged. + + 中文:对于 `category=deprecated` 的模型,推理图表在来源署名后添加 `(已弃用)`, + 英文页面显示 `(deprecated)`。PNG 导出和非官方 run overlay 中也保留该后缀, + 不再显示“已弃用”背景水印。此变更仅影响展示,不改变视图 API 的数值或筛选行为。 2. Build one public HTTPS GET URL using `URLSearchParams`, especially for JSON costs, commas, slashes, and run-specific comparison dates. No credentials are