Skip to content

Commit c9816e0

Browse files
authored
paper: add vision paper (vllm-project#1641)
* paper: add vision paper Signed-off-by: xunzhuo <xunzhuo@vllm-semantic-router.ai> * init Signed-off-by: xunzhuo <xunzhuo@vllm-semantic-router.ai> * init Signed-off-by: xunzhuo <xunzhuo@vllm-semantic-router.ai> --------- Signed-off-by: xunzhuo <xunzhuo@vllm-semantic-router.ai>
1 parent 5604bed commit c9816e0

File tree

9 files changed

+353
-280
lines changed

9 files changed

+353
-280
lines changed

website/docusaurus.config.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ const config: Config = {
130130
],
131131

132132
themeConfig: {
133-
image: 'img/docusaurus-social-card.jpg',
133+
image: 'img/vllm-sr-logo.light.png',
134134
metadata: [
135135
{ name: 'description', content: 'System Level Intelligent Router for Mixture-of-Models' },
136136
{ name: 'keywords', content: 'LLM, Semantic Router, Mixture of Models, vLLM, Routing, AI Gateway, Envoy, ExtProc' },
@@ -175,10 +175,20 @@ const config: Config = {
175175
label: 'Docs',
176176
},
177177
{
178-
to: '/white-paper',
178+
type: 'dropdown',
179179
className: 'nav-primary',
180-
label: 'Paper',
180+
label: 'About',
181181
position: 'left',
182+
items: [
183+
{
184+
label: 'White Paper',
185+
to: '/white-paper',
186+
},
187+
{
188+
label: 'Vision Paper',
189+
to: '/vision-paper',
190+
},
191+
],
182192
},
183193
{
184194
to: '/publications',
@@ -296,6 +306,10 @@ const config: Config = {
296306
label: 'White Paper',
297307
to: '/white-paper',
298308
},
309+
{
310+
label: 'Vision Paper',
311+
to: '/vision-paper',
312+
},
299313

300314
{
301315
label: 'License',
File renamed without changes.
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import React, { useState, useCallback, useEffect } from 'react'
2+
import Layout from '@theme/Layout'
3+
import Head from '@docusaurus/Head'
4+
import BrowserOnly from '@docusaurus/BrowserOnly'
5+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
6+
import { PageIntro, PillLink } from '@site/src/components/site/Chrome'
7+
import styles from './index.module.css'
8+
9+
const MOBILE_BREAKPOINT = 768
10+
const MAX_SPREAD_VIEWPORT_WIDTH = 1400
11+
const VIEWPORT_SIDE_PADDING = 120
12+
const SPREAD_GAP = 16
13+
const PDF_PAGE_RATIO = Math.SQRT2
14+
const PAGINATION_HEIGHT = 56
15+
const VIEWER_VERTICAL_PADDING = 40
16+
const MIN_SPREAD_PAGE_WIDTH = 620
17+
const SPREAD_FILL_THRESHOLD = 0.82
18+
const MAX_SINGLE_PAGE_WIDTH = 980
19+
20+
interface PaperViewerPageProps {
21+
heroDescription: string
22+
metaDescription: string
23+
pdfUrl: string
24+
shareImagePath: string
25+
socialTitle?: string
26+
title: string
27+
}
28+
29+
interface PaperViewerContentProps {
30+
pdfUrl: string
31+
}
32+
33+
function PaperViewerContent({ pdfUrl }: PaperViewerContentProps): JSX.Element {
34+
// Lazily load react-pdf only in the browser to avoid SSG DOMMatrix errors.
35+
// eslint-disable-next-line @typescript-eslint/no-require-imports
36+
const { Document, Page, pdfjs } = require('react-pdf') as typeof import('react-pdf')
37+
// eslint-disable-next-line @typescript-eslint/no-require-imports
38+
require('react-pdf/dist/Page/AnnotationLayer.css')
39+
// eslint-disable-next-line @typescript-eslint/no-require-imports
40+
require('react-pdf/dist/Page/TextLayer.css')
41+
42+
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`
43+
44+
const [numPages, setNumPages] = useState<number>(0)
45+
const [pageNumber, setPageNumber] = useState<number>(1)
46+
const [pageWidth, setPageWidth] = useState<number>(600)
47+
const [isMobile, setIsMobile] = useState<boolean>(false)
48+
const [isSpread, setIsSpread] = useState<boolean>(true)
49+
const [loading, setLoading] = useState<boolean>(true)
50+
const [error, setError] = useState<boolean>(false)
51+
52+
useEffect(() => {
53+
const updateSize = () => {
54+
const vw = window.innerWidth
55+
const vh = window.innerHeight
56+
const mobile = vw <= MOBILE_BREAKPOINT
57+
setIsMobile(mobile)
58+
59+
if (mobile) {
60+
setIsSpread(false)
61+
setPageWidth(Math.max(320, vw - 2))
62+
return
63+
}
64+
65+
const available = Math.min(vw - VIEWPORT_SIDE_PADDING, MAX_SPREAD_VIEWPORT_WIDTH)
66+
const spreadPageWidth = Math.floor(available / 2) - SPREAD_GAP
67+
const viewerHeight = Math.max(vh - PAGINATION_HEIGHT - VIEWER_VERTICAL_PADDING, 1)
68+
const spreadPageHeight = spreadPageWidth * PDF_PAGE_RATIO
69+
const spreadFillsViewport = spreadPageHeight >= viewerHeight * SPREAD_FILL_THRESHOLD
70+
const canUseSpread = spreadPageWidth >= MIN_SPREAD_PAGE_WIDTH && spreadFillsViewport
71+
72+
setIsSpread(canUseSpread)
73+
74+
if (canUseSpread) {
75+
setPageWidth(spreadPageWidth)
76+
return
77+
}
78+
79+
const widthByViewport = Math.min(vw - 96, MAX_SINGLE_PAGE_WIDTH)
80+
const widthByHeight = Math.floor(viewerHeight / PDF_PAGE_RATIO)
81+
setPageWidth(Math.max(320, Math.min(widthByViewport, widthByHeight)))
82+
}
83+
84+
updateSize()
85+
window.addEventListener('resize', updateSize)
86+
return () => window.removeEventListener('resize', updateSize)
87+
}, [])
88+
89+
useEffect(() => {
90+
setPageNumber((current) => {
91+
const maxPage = numPages > 0
92+
? numPages
93+
: 1
94+
let next = Math.min(Math.max(current, 1), maxPage)
95+
if (isSpread && next % 2 === 0)
96+
next = Math.max(1, next - 1)
97+
return next
98+
})
99+
}, [isSpread, numPages])
100+
101+
const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => {
102+
setNumPages(numPages)
103+
setLoading(false)
104+
}, [])
105+
106+
const onDocumentLoadError = useCallback(() => {
107+
setError(true)
108+
setLoading(false)
109+
}, [])
110+
111+
const step = isMobile || !isSpread
112+
? 1
113+
: 2
114+
const goToPrev = () => setPageNumber(p => Math.max(1, p - step))
115+
const goToNext = () => setPageNumber(p => Math.min(numPages, p + step))
116+
117+
const rightPage = pageNumber + 1
118+
const hasRight = isSpread && rightPage <= numPages
119+
const isNextDisabled = pageNumber >= numPages - (step - 1)
120+
const isDesktopSinglePage = !isMobile && !isSpread
121+
const documentClassName = isDesktopSinglePage
122+
? `${styles.document} ${styles.documentSinglePage}`
123+
: styles.document
124+
125+
return (
126+
<div className={styles.viewerShell}>
127+
<div className={styles.viewerArea}>
128+
{error
129+
? (
130+
<div className={styles.fallback}>
131+
<p>Unable to load PDF preview.</p>
132+
<a href={pdfUrl} target="_blank" rel="noopener noreferrer">
133+
Click here to open the PDF in a new tab
134+
</a>
135+
</div>
136+
)
137+
: (
138+
<Document
139+
file={pdfUrl}
140+
onLoadSuccess={onDocumentLoadSuccess}
141+
onLoadError={onDocumentLoadError}
142+
loading={<div className={styles.loadingText}>Loading PDF…</div>}
143+
className={documentClassName}
144+
>
145+
{isMobile
146+
? (
147+
<div className={styles.mobileStack}>
148+
{Array.from({ length: numPages }, (_, i) => (
149+
<div key={i + 1} className={styles.pageWrapper}>
150+
<Page
151+
pageNumber={i + 1}
152+
width={pageWidth}
153+
renderTextLayer={true}
154+
renderAnnotationLayer={true}
155+
/>
156+
</div>
157+
))}
158+
</div>
159+
)
160+
: isSpread
161+
? (
162+
<div className={styles.pagesRow}>
163+
<div className={styles.pageWrapper}>
164+
<Page
165+
pageNumber={pageNumber}
166+
width={pageWidth}
167+
renderTextLayer={true}
168+
renderAnnotationLayer={true}
169+
/>
170+
</div>
171+
{hasRight && (
172+
<div className={styles.pageWrapper}>
173+
<Page
174+
pageNumber={rightPage}
175+
width={pageWidth}
176+
renderTextLayer={true}
177+
renderAnnotationLayer={true}
178+
/>
179+
</div>
180+
)}
181+
</div>
182+
)
183+
: (
184+
<div className={styles.pageWrapper}>
185+
<Page
186+
pageNumber={pageNumber}
187+
width={pageWidth}
188+
renderTextLayer={true}
189+
renderAnnotationLayer={true}
190+
/>
191+
</div>
192+
)}
193+
</Document>
194+
)}
195+
</div>
196+
197+
{!error && !loading && !isMobile && (
198+
<div className={styles.pagination}>
199+
<div />
200+
<div className={styles.paginationCenter}>
201+
<button
202+
className={styles.pageBtn}
203+
onClick={goToPrev}
204+
disabled={pageNumber <= 1}
205+
aria-label="Previous page"
206+
>
207+
← Prev
208+
</button>
209+
<span className={styles.pageInfo}>
210+
{pageNumber}
211+
{hasRight ? `–${rightPage}` : ''}
212+
{' '}
213+
/
214+
{numPages}
215+
</span>
216+
<button
217+
className={styles.pageBtn}
218+
onClick={goToNext}
219+
disabled={isNextDisabled}
220+
aria-label="Next page"
221+
>
222+
Next →
223+
</button>
224+
</div>
225+
<div />
226+
</div>
227+
)}
228+
</div>
229+
)
230+
}
231+
232+
export default function PaperViewerPage({
233+
heroDescription,
234+
metaDescription,
235+
pdfUrl,
236+
shareImagePath,
237+
socialTitle,
238+
title,
239+
}: PaperViewerPageProps): JSX.Element {
240+
const { siteConfig } = useDocusaurusContext()
241+
const ogImage = new URL(shareImagePath, siteConfig.url).toString()
242+
const resolvedSocialTitle = socialTitle ?? `${title} — vLLM Semantic Router`
243+
244+
return (
245+
<Layout
246+
title={title}
247+
description={metaDescription}
248+
>
249+
<Head>
250+
<meta property="og:title" content={resolvedSocialTitle} />
251+
<meta property="og:description" content={metaDescription} />
252+
<meta property="og:image" content={ogImage} />
253+
<meta property="og:type" content="article" />
254+
<meta name="twitter:card" content="summary_large_image" />
255+
<meta name="twitter:title" content={resolvedSocialTitle} />
256+
<meta name="twitter:description" content={metaDescription} />
257+
<meta name="twitter:image" content={ogImage} />
258+
</Head>
259+
<main className={styles.page}>
260+
<div className="site-shell-container">
261+
<div className={styles.hero}>
262+
<PageIntro
263+
label="Research document"
264+
title={title}
265+
description={heroDescription}
266+
actions={(
267+
<>
268+
<PillLink href={pdfUrl} target="_blank" rel="noreferrer">
269+
Download PDF
270+
</PillLink>
271+
<PillLink to="/publications" muted>
272+
Research routes
273+
</PillLink>
274+
</>
275+
)}
276+
/>
277+
</div>
278+
</div>
279+
280+
<div className="site-shell-container">
281+
<BrowserOnly fallback={<div className={styles.loadingText}>Loading PDF...</div>}>
282+
{() => <PaperViewerContent pdfUrl={pdfUrl} />}
283+
</BrowserOnly>
284+
</div>
285+
</main>
286+
</Layout>
287+
)
288+
}

website/src/data/researchContent.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ export const researchPapers = [
1515
featured: true,
1616
sortOrder: 10,
1717
},
18+
{
19+
id: 'wrp-vision-paper',
20+
type: 'paper',
21+
spotlight: true,
22+
categoryLabel: 'VISION PAPER',
23+
title: 'The Workload-Router-Pool Architecture for LLM Inference Optimization: A Vision Paper from the vLLM Semantic Router Project',
24+
authors: 'Huamin Chen, Xunzhuo Liu, Bowei He, Fuyuan Lyu, Yankai Chen, Xue Liu, Yuhan Liu, Junchen Jiang',
25+
venue: 'arXiv Technical Report',
26+
year: '2026',
27+
abstract: 'We synthesize the project’s recent routing, fleet, multimodal, and governance results into the Workload-Router-Pool (WRP) architecture, connecting signal-driven routing to a full-stack inference optimization framework and outlining future research directions across workload, router, and pool design.',
28+
links: [
29+
{ type: 'paper', url: 'https://arxiv.org/abs/2603.21354', label: 'Paper' },
30+
],
31+
featured: true,
32+
sortOrder: 11,
33+
},
1834
{
1935
id: 'visual-confused-deputy',
2036
type: 'paper',

website/src/pages/publications.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@
126126
}
127127

128128
.spotlightCard {
129+
position: relative;
130+
isolation: isolate;
129131
overflow: hidden;
130132
border-color: var(--site-border-strong);
131133
background:
@@ -141,6 +143,7 @@
141143
inset: 0;
142144
border-radius: inherit;
143145
border: 1px solid rgba(255, 255, 255, 0.08);
146+
z-index: 0;
144147
pointer-events: none;
145148
}
146149

@@ -160,6 +163,8 @@
160163
}
161164

162165
.awardFrame {
166+
position: relative;
167+
z-index: 1;
163168
display: grid;
164169
gap: 1rem;
165170
}

website/src/pages/vision-paper.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react'
2+
import PaperViewerPage from '@site/src/components/PaperViewerPage'
3+
4+
const PDF_URL = '/vision-paper.pdf'
5+
const SHARE_IMAGE = '/img/vllm-sr-logo.light.png'
6+
7+
export default function VisionPaper(): JSX.Element {
8+
return (
9+
<PaperViewerPage
10+
title="Vision Paper"
11+
socialTitle="Our Vision — vLLM Semantic Router"
12+
metaDescription="The Workload-Router-Pool Architecture for LLM Inference Optimization: A Vision Paper from the vLLM Semantic Router Project"
13+
heroDescription="The Workload-Router-Pool Architecture for LLM Inference Optimization, presented as a full PDF reader for the vLLM Semantic Router vision paper."
14+
pdfUrl={PDF_URL}
15+
shareImagePath={SHARE_IMAGE}
16+
/>
17+
)
18+
}

0 commit comments

Comments
 (0)