Skip to content

Commit 3605fcb

Browse files
committed
Merge remote-tracking branch 'origin/master' into jupyter-llm-8500
2 parents 1caf369 + 8395e83 commit 3605fcb

File tree

13 files changed

+1494
-1305
lines changed

13 files changed

+1494
-1305
lines changed

src/packages/frontend/admin/llm/admin-llm-test.tsx

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { Alert, Button, Input, Select, Space, Table } from "antd";
1+
import {
2+
Alert,
3+
Button,
4+
Input,
5+
Progress,
6+
Select,
7+
Space,
8+
Table,
9+
Tooltip,
10+
} from "antd";
211

312
import {
413
redux,
@@ -20,12 +29,13 @@ import { trunc_middle } from "@cocalc/util/misc";
2029
import { COLORS } from "@cocalc/util/theme";
2130
import { PROMPTS } from "./tests";
2231
import { Value } from "./value";
23-
2432
interface TestResult {
2533
model: string;
2634
status: "pending" | "running" | "passed" | "failed";
2735
output: string;
2836
error?: string;
37+
firstResponseTime?: number; // Time in milliseconds until first token
38+
totalTime?: number; // Total time in milliseconds until completion
2939
}
3040

3141
export function TestLLMAdmin() {
@@ -101,6 +111,10 @@ export function TestLLMAdmin() {
101111

102112
return new Promise((resolve) => {
103113
try {
114+
const startTime = Date.now();
115+
let firstResponseTime: number | undefined;
116+
let totalTime: number | undefined;
117+
104118
const llmStream = webapp_client.openai_client.queryStream({
105119
input: prompt,
106120
project_id: null,
@@ -116,6 +130,10 @@ export function TestLLMAdmin() {
116130
llmStream.on("token", (token) => {
117131
console.log({ model, system, token });
118132
if (token != null) {
133+
// Record first response time if this is the first token
134+
if (firstResponseTime === undefined) {
135+
firstResponseTime = Date.now() - startTime;
136+
}
119137
reply += token;
120138
// Update the result in real-time
121139
setTestResults((prev) =>
@@ -125,22 +143,28 @@ export function TestLLMAdmin() {
125143
);
126144
} else {
127145
// Stream is complete (token is null)
146+
totalTime = Date.now() - startTime;
128147
const passed = expectedRegex.test(reply);
129148
resolve({
130149
model,
131150
status: passed ? "passed" : "failed",
132151
output: reply,
152+
firstResponseTime,
153+
totalTime,
133154
});
134155
}
135156
});
136157

137158
llmStream.on("error", (err) => {
159+
totalTime = Date.now() - startTime;
138160
console.error(`Error in LLM stream for model ${model}:`, err);
139161
resolve({
140162
model,
141163
status: "failed",
142164
output: reply,
143165
error: err?.toString(),
166+
firstResponseTime,
167+
totalTime,
144168
});
145169
});
146170

@@ -241,6 +265,57 @@ export function TestLLMAdmin() {
241265
}
242266
}
243267

268+
function formatTiming(timeMs: number | undefined): string {
269+
if (timeMs === undefined) return "-";
270+
return `${(timeMs / 1000).toFixed(1)}s`;
271+
}
272+
273+
function renderTimingColumn(record: TestResult) {
274+
const { firstResponseTime, totalTime, status } = record;
275+
276+
if (status === "pending" || status === "running") {
277+
return <span style={{ color: COLORS.GRAY_M }}>-</span>;
278+
}
279+
280+
if (firstResponseTime === undefined || totalTime === undefined) {
281+
return <span style={{ color: COLORS.GRAY_M }}>-</span>;
282+
}
283+
284+
// Calculate progress bar values (normalize to 10 seconds max)
285+
const maxTime = Math.max(
286+
10000,
287+
...testResults.filter((r) => r.totalTime).map((r) => r.totalTime!),
288+
);
289+
const totalPercent = Math.min(100, (totalTime / maxTime) * 100);
290+
291+
// Determine if this is one of the slowest (top 10% quantile)
292+
const completedResults = testResults.filter(
293+
(r) => r.totalTime !== undefined,
294+
);
295+
const sortedTimes = completedResults
296+
.map((r) => r.totalTime!)
297+
.sort((a, b) => b - a);
298+
const slowThreshold =
299+
sortedTimes[Math.floor(sortedTimes.length * 0.1)] || 0;
300+
const isSlow = totalTime >= slowThreshold && completedResults.length > 1;
301+
302+
return (
303+
<div>
304+
<Tooltip title="First response time / Total completion time">
305+
<div style={{ marginBottom: 2 }}>
306+
{formatTiming(firstResponseTime)}/{formatTiming(totalTime)}
307+
</div>
308+
</Tooltip>
309+
<Progress
310+
percent={totalPercent}
311+
size="small"
312+
status={isSlow ? "exception" : "normal"}
313+
showInfo={false}
314+
/>
315+
</div>
316+
);
317+
}
318+
244319
function renderTestResults() {
245320
if (testResults.length === 0) {
246321
return (
@@ -292,6 +367,12 @@ export function TestLLMAdmin() {
292367
<span style={{ color: COLORS.GRAY_M }}>-</span>
293368
),
294369
},
370+
{
371+
title: "Timing",
372+
key: "timing",
373+
width: 120,
374+
render: (_, record: TestResult) => renderTimingColumn(record),
375+
},
295376
{
296377
title: "Test",
297378
key: "test",

src/packages/frontend/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"@xterm/addon-webgl": "^0.18.0",
7272
"@xterm/xterm": "^5.5.0",
7373
"anser": "^2.1.1",
74-
"antd": "^5.24.7",
74+
"antd": "^5.27.0",
7575
"antd-img-crop": "^4.21.0",
7676
"async": "^2.6.3",
7777
"audio-extensions": "^0.0.0",
@@ -122,11 +122,11 @@
122122
"plotly.js": "^2.29.1",
123123
"project-name-generator": "^2.1.6",
124124
"re-resizable": "^6.9.0",
125-
"react": "^19.1.0",
125+
"react": "^19.1.1",
126126
"react-color": "^2.19.3",
127127
"react-copy-to-clipboard": "^5.1.0",
128128
"react-debounce-input": "^3.3.0",
129-
"react-dom": "^19.1.0",
129+
"react-dom": "^19.1.1",
130130
"react-draggable": "^4.5.0",
131131
"react-highlight-words": "^0.21.0",
132132
"react-interval-hook": "^1.1.3",
@@ -159,8 +159,8 @@
159159
"@types/lodash": "^4.14.202",
160160
"@types/markdown-it": "12.2.3",
161161
"@types/md5": "^2.2.0",
162-
"@types/react": "^19.1.8",
163-
"@types/react-dom": "^19.1.6",
162+
"@types/react": "^19.1.10",
163+
"@types/react-dom": "^19.1.7",
164164
"@types/react-redux": "^7.1.34",
165165
"coffeescript": "^2.5.1",
166166
"cspell": "^8.17.2",

src/packages/frontend/project/explorer/search-bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const HelpStyle = {
3030
export const outputMinitermStyle: React.CSSProperties = {
3131
background: "white",
3232
position: "absolute",
33-
zIndex: 2,
33+
zIndex: 10,
3434
boxShadow: "-4px 4px 7px #aaa",
3535
maxHeight: "450px",
3636
overflow: "auto",

src/packages/hub/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"ms": "2.1.2",
3838
"parse-domain": "^5.0.0",
3939
"random-key": "^0.3.2",
40-
"react": "^19.1.0",
40+
"react": "^19.1.1",
4141
"uglify-js": "^3.14.1",
4242
"underscore": "^1.12.1",
4343
"uuid": "^8.3.2",

src/packages/next/lib/with-customize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default async function withCustomize(
6767
customize.onCoCalcCom = customize.kucalc === KUCALC_COCALC_COM;
6868
customize.noindex = obj.props?.unlisted ?? false;
6969
customize.imprintOrPolicies =
70-
(customize.imprint ?? "" + customize.policies ?? "") != "";
70+
(customize.imprint ?? "") + (customize.policies ?? "") != "";
7171
customize.serverTime = Date.now();
7272

7373
// this is used for creating new projects from a share

src/packages/next/package.json

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"homepage": "https://github.com/sagemathinc/cocalc/tree/master/src/packages/next",
5858
"private": false,
5959
"dependencies": {
60+
"@ant-design/cssinjs": "^1.24.0",
6061
"@ant-design/icons": "^6.0.0",
6162
"@ant-design/v5-patch-for-react-19": "^1.0.3",
6263
"@cocalc/backend": "workspace:*",
@@ -67,7 +68,7 @@
6768
"@cocalc/util": "workspace:*",
6869
"@openapitools/openapi-generator-cli": "^2.19.1",
6970
"@vscode/vscode-languagedetection": "^1.0.22",
70-
"antd": "^5.24.7",
71+
"antd": "^5.27.0",
7172
"antd-img-crop": "^4.21.0",
7273
"awaiting": "^3.0.0",
7374
"base-64": "^1.0.0",
@@ -83,8 +84,8 @@
8384
"next-translate": "^2.6.2",
8485
"password-hash": "^1.2.2",
8586
"pg": "^8.7.1",
86-
"react": "^19.1.0",
87-
"react-dom": "^19.1.0",
87+
"react": "^19.1.1",
88+
"react-dom": "^19.1.1",
8889
"react-google-recaptcha-v3": "^1.9.7",
8990
"react-intl": "^7.1.11",
9091
"serve-index": "^1.9.1",
@@ -95,19 +96,13 @@
9596
"zod": "^3.23.5"
9697
},
9798
"devDependencies": {
98-
"@ant-design/cssinjs": "^1.23.0",
9999
"@testing-library/jest-dom": "^6.6.3",
100100
"@testing-library/react": "^16.3.0",
101101
"@types/express": "^4.17.21",
102102
"@types/node": "^18.16.14",
103-
"@types/react": "^19.1.8",
103+
"@types/react": "^19.1.10",
104104
"@uiw/react-textarea-code-editor": "^3.1.1",
105-
"next-rspack": "^15.3.4",
105+
"next-rspack": "^15.4.6",
106106
"node-mocks-http": "^1.14.1"
107-
},
108-
"pnpm": {
109-
"overrides": {
110-
"@types/react": "19.1.8"
111-
}
112107
}
113108
}

src/packages/next/pages/_document.tsx

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,50 @@
1-
/*
2-
This custom document is needed to workaround this bug in antd + nextjs:
3-
4-
https://github.com/ant-design/ant-design/issues/38767
5-
6-
The actual fix -- i.e., this entire file -- comes from
7-
8-
https://github.com/ant-design/ant-design/issues/38767#issuecomment-1350362026
9-
10-
which is for a different bug in antd + nextjs, but it happens to fix
11-
the same problem, and fortunately also works with the older nextjs 12.x, which
12-
we are currently stuck with.
13-
14-
See also the discussion at https://github.com/ant-design/ant-design/issues/39891
15-
*/
16-
1+
// pages/_document.tsx
172
import type { DocumentContext, DocumentInitialProps } from "next/document";
183
import Document, { Head, Html, Main, NextScript } from "next/document";
19-
204
import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs";
21-
225
import { Locale } from "@cocalc/util/i18n";
23-
246
import { query2locale } from "locales/misc";
257

26-
export default class MyDocument extends Document {
27-
static async getInitialProps(ctx: DocumentContext): Promise<
28-
DocumentInitialProps & {
29-
locale: Locale;
30-
}
31-
> {
8+
export default class MyDocument extends Document<{ locale: Locale }> {
9+
static async getInitialProps(
10+
ctx: DocumentContext,
11+
): Promise<DocumentInitialProps & { locale: Locale }> {
3212
const locale = query2locale(ctx.query);
33-
3413
const cache = createCache();
3514
const originalRenderPage = ctx.renderPage;
3615

37-
// The IntlProvider is only for english and all components with translations in the frontend
3816
ctx.renderPage = () =>
3917
originalRenderPage({
40-
enhanceApp: (App) => (props) =>
41-
(
42-
<StyleProvider cache={cache}>
43-
<App {...props} {...{ locale }} />
44-
</StyleProvider>
45-
),
18+
enhanceApp: (App: any) => (props: any) => (
19+
<StyleProvider cache={cache} hashPriority="high">
20+
<App {...props} locale={locale} />
21+
</StyleProvider>
22+
),
4623
});
4724

4825
const initialProps = await Document.getInitialProps(ctx);
4926

27+
// inline critical AntD CSS as real <style> tags (no script hack)
28+
const css = extractStyle(cache, { plain: true, types: ["style", "token"] });
29+
5030
return {
5131
...initialProps,
5232
locale,
5333
styles: (
5434
<>
5535
{initialProps.styles}
56-
{/* This is hack, `extractStyle` does not currently support returning JSX or related data. */}
57-
<script
58-
dangerouslySetInnerHTML={{
59-
__html: `</script>${extractStyle(cache)}<script>`,
60-
}}
36+
<style
37+
// keep it obvious for debugging
38+
data-antd="cssinjs-ssr"
39+
// extractStyle returns complete <style> tags; that’s OK here
40+
// If you prefer only the CSS text, you can parse it, but this works well in practice.
41+
dangerouslySetInnerHTML={{ __html: css }}
6142
/>
6243
</>
6344
),
6445
};
6546
}
6647

67-
// TODO: this "lang={...}" is only working for the very first page that's being loaded
68-
// next's dynamic page updates to not have an impact on this. So, to really fix this, we
69-
// probably have to get rid of this _document customization and update to version 15 properly.
7048
render() {
7149
return (
7250
<Html lang={this.props.locale}>

0 commit comments

Comments
 (0)