Skip to content

Commit 9db0e32

Browse files
FEAT: Switch default frontend to Next.js app (#5111)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent d0a58ff commit 9db0e32

35 files changed

Lines changed: 985 additions & 221 deletions

.github/workflows/python.yaml

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,26 @@ jobs:
3636
- name: Set up Node.js
3737
uses: actions/setup-node@v1
3838
with:
39-
node-version: 16
39+
node-version: 20.19.0
4040
# ESLint and Prettier must be in `package.json`
4141
- name: Install Node.js dependencies
42-
run: cd xinference/ui/web/ui && npm ci
42+
run: cd frontend && npm ci
4343
- name: ESLint Check
44-
run: cd xinference/ui/web/ui && npx eslint .
45-
- name: Prettier Check
46-
run: cd xinference/ui/web/ui && ./node_modules/.bin/prettier --check .
44+
run: cd frontend && npx eslint .
45+
- name: Build frontend (static export)
46+
run: cd frontend && npm run build
47+
- name: Verify static export output
48+
run: |
49+
test -f frontend/out/index.html
50+
test -f frontend/out/404.html
51+
# Dynamic routes must emit __shell__ placeholder pages.
52+
find frontend/out -name '__shell__*' | grep -q .
53+
- name: Serve static export from backend
54+
run: |
55+
pip install fastapi httpx pytest xoscar
56+
pytest --noconftest -vv \
57+
xinference/api/tests/test_frontend_static.py \
58+
xinference/api/tests/test_frontend_static_real_export.py
4759
4860
build_test_job:
4961
runs-on: ${{ matrix.os }}
@@ -335,4 +347,4 @@ jobs:
335347
xinference
336348
337349
fi
338-
working-directory: .
350+
working-directory: .

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,7 @@ asv/results
162162
!SECURITY.md
163163
.history/
164164

165-
# frontend
165+
# frontend
166166
**/pnpm-lock.yaml
167+
# staged frontend static export served by the backend
168+
xinference/ui/web/dist/

MANIFEST.in

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@ include xinference/locale/*.json
1313
include xinference/model/llm/*.json
1414
include xinference/model/embedding/*.json
1515
graft xinference/thirdparty
16-
global-include xinference/ui/web/ui/build/**/*
16+
graft xinference/ui/web/dist
17+
graft frontend
18+
prune frontend/.next
19+
prune frontend/out
20+
prune frontend/node_modules
21+
prune frontend/.npm-cache
22+
prune frontend/.npm-logs

doc/source/development/contributing_environment.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ is to compile the frontend.
8686
Frontend Compilation
8787
--------------------
8888

89-
Navigate to the ``inference/xinference/ui/web/ui`` directory. Then, execute the following command
89+
Navigate to the ``inference/frontend`` directory. Then, execute the following command
9090
to clear the cache:
9191

9292
::
@@ -104,11 +104,21 @@ frontend:
104104

105105
::
106106

107-
npm install
107+
npm ci
108108
npm run build
109109

110110
Still, if the first command fails to execute, you can try adding the ``--force`` option.
111111

112+
The build emits a static export and stages it at ``xinference/ui/web/dist``,
113+
where the Xinference backend serves it directly — no Node.js runtime is needed
114+
after building.
115+
116+
For local frontend development, start the Xinference backend separately and then run:
117+
118+
::
119+
120+
XINFERENCE_API_URL=http://127.0.0.1:9997 npm run dev
121+
112122
After compiling the frontend, you can ``cd`` back to the directory
113123
where the ``setup.cfg`` and ``setup.py`` files are located,
114124
and install Xinference via ``pip install -e .``.

doc/source/development/xinference_internals.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,4 @@ The main code is located in the `xinference/ <https://github.com/xorbitsai/infer
279279
- `model/ <https://github.com/xorbitsai/inference/tree/main/xinference/model>`_: It provides a structure for model descriptions, creation,
280280
and caching. See `Model`_ for more information.
281281

282-
- `web/ui/ <https://github.com/xorbitsai/inference/tree/main/xinference/web/ui>`_: The js code of the frontend (Web UI).
282+
- `frontend/ <https://github.com/xorbitsai/inference/tree/main/frontend>`_: The Next.js code of the frontend (Web UI).

doc/source/getting_started/using_xinference.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,17 @@ To start a local instance of Xinference, run the following command:
4949
Congrats! You now have Xinference running on your local machine. Once Xinference is running, there are multiple ways
5050
we can try it: via the web UI, via cURL, via the command line, or via the Xinference's python client.
5151

52-
You can visit the web UI at `http://127.0.0.1:9997/ui <http://127.0.0.1:9997/ui>`_ and visit `http://127.0.0.1:9997/docs <http://127.0.0.1:9997/docs>`_
52+
You can visit the web UI at `http://127.0.0.1:9997 <http://127.0.0.1:9997>`_ and visit `http://127.0.0.1:9997/docs <http://127.0.0.1:9997/docs>`_
5353
to inspect the API docs.
5454

55+
.. note::
56+
57+
The web UI is a Next.js application bundled into the ``xinference`` package as a static
58+
export and served by the backend itself, so no Node.js runtime is required at runtime.
59+
When developing Xinference from a source checkout, build it once with
60+
``cd frontend && npm ci && npm run build``, or work on the frontend live with
61+
``npm run dev`` (see the "Creating a development environment" page).
62+
5563
You can install the Xinference command line tool and Python client using the following command:
5664

5765
.. code-block:: bash

frontend/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ npm-debug.log*
2929
yarn-debug.log*
3030
yarn-error.log*
3131
.pnpm-debug.log*
32+
.npm-cache/
33+
.npm-logs/
3234

3335
# env files (can opt-in for committing if needed)
3436
.env*

frontend/README.md

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,55 @@
1-
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
1+
# Xinference Frontend
22

3-
## Getting Started
3+
This is the Xinference Web UI, a Next.js app. In production it is built as a
4+
**static export**, bundled into the `xinference` Python package, and served by
5+
the Xinference backend itself — a single process, no Node.js runtime needed.
46

5-
First, run the development server:
7+
## Local Development
8+
9+
Start the backend first:
610

711
```bash
8-
npm run dev
9-
# or
10-
yarn dev
11-
# or
12-
pnpm dev
13-
# or
14-
bun dev
12+
xinference-local --host 127.0.0.1 --port 9997
1513
```
1614

17-
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18-
19-
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
15+
Then start the frontend dev server:
2016

21-
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
17+
```bash
18+
cd frontend
19+
npm ci
20+
npm run dev
21+
```
2222

23-
## Learn More
23+
Open http://127.0.0.1:3999.
2424

25-
To learn more about Next.js, take a look at the following resources:
25+
In dev mode, the frontend proxies API requests to `http://127.0.0.1:9997`. To
26+
use a different backend endpoint:
2627

27-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
28+
```bash
29+
XINFERENCE_API_URL=http://127.0.0.1:6735 npm run dev
30+
```
2931

30-
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
32+
For direct browser requests to a non-default backend, use
33+
`NEXT_PUBLIC_API_URL` instead.
3134

32-
## Deploy on Vercel
35+
## Build
3336

34-
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
37+
```bash
38+
npm run build
39+
```
3540

36-
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
41+
This produces the static export in `out/` and stages it at
42+
`xinference/ui/web/dist` (postbuild hook), where the backend serves it. After
43+
building, `http://<backend-host>:9997/` serves the UI directly.
44+
45+
Notes on the static export:
46+
47+
- Dynamic routes (`launch-model/[modelType]`, `running-model/[modelUid]`, ...)
48+
are emitted as `__shell__` placeholder pages; the backend maps any real URL
49+
to the matching shell and the client reads the actual params from the URL
50+
(see `src/lib/route-params.ts` and `xinference/api/frontend_static.py`).
51+
- `next.config.ts` only enables rewrites (the dev API proxy) outside export
52+
mode; in production the UI is same-origin with the API, so no proxy is
53+
needed.
54+
- To build a self-contained Node server instead (no static export), use
55+
`NEXT_OUTPUT=standalone npm run build`.

frontend/next.config.ts

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
1+
// Output mode (build only):
2+
// 'export' -> static assets in out/, bundled into the Python wheel and
3+
// served by the Xinference backend (single-process pip path).
4+
// This is the default for builds.
5+
// 'standalone' -> self-contained Node server (set NEXT_OUTPUT=standalone) for
6+
// deployments that run the frontend as a separate service.
7+
//
8+
// Never set an output mode in `next dev`: 'export' forces dynamicParams=false,
9+
// so navigating to a real dynamic route (/launch-model/llm, ...) whose params
10+
// are not in generateStaticParams throws. In production the backend SPA
11+
// fallback maps those paths to the __shell__ page; the dev server has no such
12+
// layer.
13+
const isDev = process.env.NODE_ENV === 'development';
14+
const outputMode = isDev
15+
? undefined
16+
: process.env.NEXT_OUTPUT === 'standalone'
17+
? 'standalone'
18+
: 'export';
19+
120
/** @type {import('next').NextConfig} */
221
const nextConfig = {
3-
// Enable standalone output for Docker deployment
4-
output: 'standalone',
22+
...(outputMode ? { output: outputMode } : {}),
23+
images: { unoptimized: true },
524
experimental: {
625
optimizeCss: false,
726
},
@@ -21,32 +40,44 @@ const nextConfig = {
2140
eslint: {
2241
ignoreDuringBuilds: false,
2342
},
24-
async redirects() {
25-
return [
26-
{
27-
source: '/',
28-
destination: '/launch-model',
29-
permanent: true, // false = 307 (temporary redirect), true = 301 (permanent redirect)
30-
},
31-
];
32-
},
43+
// rewrites/redirects are unsupported in static export; in the single-process
44+
// deployment the backend serves the UI same-origin so no proxy is needed.
45+
// Keep the API proxy for `next dev` and standalone builds.
46+
...(outputMode !== 'export'
47+
? {
48+
async rewrites() {
49+
const apiUrl = (
50+
process.env.XINFERENCE_API_URL ||
51+
process.env.NEXT_PUBLIC_API_URL ||
52+
'http://127.0.0.1:9997'
53+
).replace(/\/+$/, '');
54+
return [
55+
{
56+
source: '/v1/:path*',
57+
destination: `${apiUrl}/v1/:path*`,
58+
},
59+
{
60+
source: '/token',
61+
destination: `${apiUrl}/token`,
62+
},
63+
];
64+
},
65+
}
66+
: {}),
3367
webpack(config: any) {
34-
const fileLoaderRule = config.module.rules.find(
35-
(rule: any) =>
36-
rule.test?.test?.('.svg')
37-
)
68+
const fileLoaderRule = config.module.rules.find((rule: any) => rule.test?.test?.('.svg'));
3869

3970
if (fileLoaderRule) {
40-
fileLoaderRule.exclude = /\.svg$/i
71+
fileLoaderRule.exclude = /\.svg$/i;
4172
}
4273

4374
config.module.rules.push({
4475
test: /\.svg$/i,
4576
issuer: /\.[jt]sx?$/,
4677
use: ['@svgr/webpack'],
47-
})
78+
});
4879

49-
return config
80+
return config;
5081
},
5182
};
5283

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev -p 3999",
6+
"dev": "next dev -H 127.0.0.1 -p 3999",
77
"build": "next build",
8+
"postbuild": "node scripts/stage-export.mjs",
89
"start": "next start",
910
"lint": "eslint .",
1011
"format": "prettier . --write"

0 commit comments

Comments
 (0)