Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e838cc8

Browse files
Merge branch 'llm_model_selector' into 'master'
feat (ui): Add LLM model selector, remove model selection from chat settings & small UI improvements on small screens See merge request postgres-ai/database-lab!980
2 parents 9fb8476 + 4a7e6b0 commit e838cc8

File tree

5 files changed

+165
-103
lines changed

5 files changed

+165
-103
lines changed

‎ui/packages/platform/src/pages/Bot/ChatsList/ChatsList.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const useStyles = makeStyles<Theme, ChatsListProps>((theme) => ({
3131
[theme.breakpoints.down('sm')]: {
3232
height: '100vh!important',
3333
marginTop: '0!important',
34-
width: 320,
34+
width: 'min(100%, 360px)',
3535
zIndex: 9999
3636
},
3737
'& > ul': {
@@ -57,6 +57,11 @@ const useStyles = makeStyles<Theme, ChatsListProps>((theme) => ({
5757
background: 'white',
5858
[theme.breakpoints.down('sm')]: {
5959
padding: 0
60+
},
61+
"@media (max-width: 960px)": {
62+
"& .MuiFormControl-root": {
63+
display: "none" // Hide model selector in chats list
64+
}
6065
}
6166
},
6267
listItemLink: {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react';
2+
import { FormControl, Select, MenuItem, Typography, InputLabel, useMediaQuery } from "@mui/material";
3+
import { SelectChangeEvent } from "@mui/material/Select";
4+
5+
import { useAiBot } from "../hooks";
6+
7+
export const ModelSelector = () => {
8+
const { aiModel, aiModels, setAiModel } = useAiBot();
9+
const isSmallScreen = useMediaQuery("(max-width: 960px)");
10+
11+
const handleChange = (event: SelectChangeEvent<string | null>) => {
12+
const [vendor, name] = (event.target.value as string).split("/");
13+
const model = aiModels?.find(
14+
(model) => model.vendor === vendor && model.name === name
15+
);
16+
if (model) setAiModel(model);
17+
};
18+
19+
const truncateText = (text: string, maxLength: number) => {
20+
return text.length > maxLength ? text.substring(0, maxLength) + "..." : text;
21+
};
22+
23+
return (
24+
<FormControl
25+
variant="outlined"
26+
size="small"
27+
sx={{ minWidth: isSmallScreen ? 120 : 200 }}
28+
>
29+
<Select
30+
labelId="model-select-label"
31+
id="model-select"
32+
value={aiModel ? `${aiModel.vendor}/${aiModel.name}` : ""}
33+
onChange={handleChange}
34+
displayEmpty
35+
inputProps={{
36+
"aria-describedby": "Select the AI model to be used for generating responses. Different models may vary in performance. Choose the one that best suits your needs.",
37+
sx: {
38+
height: "32px",
39+
fontSize: "0.875rem",
40+
padding: isSmallScreen ? "8px 24px 8px 8px!important" : "8px 14px",
41+
},
42+
}}
43+
sx={{ height: "32px" }}
44+
renderValue={(selected) => {
45+
if (!selected) return "Select Model";
46+
const [vendor, name] = selected.split("/");
47+
return truncateText(`${vendor}/${name}`, isSmallScreen ? 20 : 30);
48+
}}
49+
>
50+
{aiModels &&
51+
aiModels.map((model) => (
52+
<MenuItem
53+
key={`${model.vendor}/${model.name}`}
54+
value={`${model.vendor}/${model.name}`}
55+
title={`${model.vendor}/${model.name}`}
56+
sx={{
57+
display: "flex",
58+
flexDirection: "column",
59+
alignItems: "flex-start"
60+
}}
61+
>
62+
<span>{truncateText(`${model.vendor}/${model.name}`, isSmallScreen ? 33 : 40)}</span>
63+
{model.comment && (
64+
<Typography
65+
variant="body2"
66+
color="textSecondary"
67+
sx={{
68+
fontSize: "0.7rem",
69+
color: "rgba(0, 0, 0, 0.6)",
70+
}}
71+
aria-hidden="true"
72+
>
73+
{model.comment}
74+
</Typography>
75+
)}
76+
</MenuItem>
77+
))}
78+
</Select>
79+
</FormControl>
80+
);
81+
};

‎ui/packages/platform/src/pages/Bot/SettingsDialog/SettingsDialog.tsx

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
makeStyles,
1616
Radio,
1717
RadioGroup,
18-
TextField,
18+
TextField,Theme,
1919
Typography,
2020
} from '@material-ui/core'
2121
import MuiDialogTitle from '@material-ui/core/DialogTitle'
@@ -30,6 +30,8 @@ import { AiModel } from "../../../types/api/entities/bot";
3030
import settings from "../../../utils/settings";
3131
import { Link } from "@postgres.ai/shared/components/Link2";
3232
import { ExternalIcon } from "@postgres.ai/shared/icons/External";
33+
import Divider from "@material-ui/core/Divider";
34+
import cn from "classnames";
3335

3436
type DialogTitleProps = {
3537
id: string
@@ -123,35 +125,30 @@ const DialogActions = (props: { children: React.ReactNode }) => {
123125
)
124126
}
125127

126-
const useDialogStyles = makeStyles(
127-
() => ({
128+
const useDialogStyles = makeStyles<Theme>(
129+
(theme) => ({
128130
textField: {
129131
...styles.inputField,
130132
marginTop: '0px',
131133
width: 480,
134+
[theme.breakpoints.down('sm')]: {
135+
136+
}
132137
},
133138
copyButton: {
134139
marginTop: '-3px',
135140
fontSize: '20px',
136141
},
137-
dialog: {},
138-
remark: {
139-
fontSize: 12,
140-
lineHeight: '12px',
141-
142-
paddingLeft: 20,
143-
paddingBottom: 5,
144-
},
145-
remarkIcon: {
146-
display: 'block',
147-
height: '20px',
148-
width: '22px',
149-
float: 'left',
150-
paddingTop: '5px',
151-
},
152142
urlContainer: {
153-
marginTop: 10,
154-
paddingLeft: 22,
143+
marginTop: 8,
144+
paddingLeft: 20,
145+
[theme.breakpoints.down('sm')]: {
146+
padding: 0,
147+
width: '100%',
148+
'& .MuiTextField-root': {
149+
maxWidth: 'calc(100% - 36px)'
150+
}
151+
},
155152
},
156153
radioGroup: {
157154
fontSize: 12,
@@ -170,16 +167,34 @@ const useDialogStyles = makeStyles(
170167
marginBottom: 0
171168
}
172169
},
170+
unlockNoteDemo: {
171+
paddingLeft: 20
172+
},
173173
formControlLabel: {
174174
'& .Mui-disabled > *, & .Mui-disabled': {
175175
color: 'rgba(0, 0, 0, 0.6)'
176+
},
177+
[theme.breakpoints.down('sm')]: {
178+
marginRight: 0,
179+
alignItems: 'flex-start',
180+
'&:first-child': {
181+
marginTop: 6
182+
}
183+
},
184+
},
185+
formControlLabelRadio: {
186+
[theme.breakpoints.down('sm')]: {
187+
padding: '4px 9px'
176188
}
177189
},
178190
externalIcon: {
179191
width: 14,
180192
height: 14,
181193
marginLeft: 4,
182194
transform: 'translateY(2px)',
195+
},
196+
divider: {
197+
margin: '12px 0'
183198
}
184199
}),
185200
{ index: 1 },
@@ -295,8 +310,8 @@ export const SettingsDialog = (props: PublicChatDialogProps) => {
295310
<>
296311
<FormLabel component="legend">Visibility</FormLabel>
297312
<RadioGroup
298-
aria-label="shareUrl"
299-
name="shareUrl"
313+
aria-label="Thread visibility"
314+
name="threadVisibility"
300315
value={visibility}
301316
onChange={(event) => {
302317
setVisibility(event.target.value as Visibility)
@@ -306,20 +321,22 @@ export const SettingsDialog = (props: PublicChatDialogProps) => {
306321
<FormControlLabel
307322
value={Visibility.PUBLIC}
308323
className={classes.formControlLabel}
309-
control={<Radio />}
324+
control={<Radio className={classes.formControlLabelRadio}/>}
310325
label={<><b>Public:</b> anyone can view chats, but only team members can respond</>}
326+
aria-label="Public: anyone can view chats, but only team members can respond"
311327
/>
312328
{visibility === Visibility.PUBLIC && threadId && (
313329
<div className={classes.urlContainer}>{urlField}</div>
314330
)}
315331
<FormControlLabel
316332
value={Visibility.PRIVATE}
317333
className={classes.formControlLabel}
318-
control={<Radio />}
334+
control={<Radio className={classes.formControlLabelRadio}/>}
319335
label={<><b>Private:</b> chats are visible only to members of your organization</>}
336+
aria-label="Private: chats are visible only to members of your organization"
320337
disabled={Boolean(isDemoOrg) || !isSubscriber}
321338
/>
322-
{Boolean(isDemoOrg) && <Typography className={classes.remark}>Private chats are not allowed in "Demo"</Typography>}
339+
{Boolean(isDemoOrg) && <Typography className={cn(classes.unlockNote,classes.unlockNoteDemo)}>Private chats are not allowed in "Demo"</Typography>}
323340
{!Boolean(isDemoOrg) && !isSubscriber && <Typography variant="body2" className={classes.unlockNote}>
324341
Unlock private conversations by either:
325342
<ol>
@@ -339,29 +356,6 @@ export const SettingsDialog = (props: PublicChatDialogProps) => {
339356
</Typography>}
340357
</RadioGroup>
341358
</>
342-
{aiModels && <>
343-
<FormLabel component="legend">Model</FormLabel>
344-
<RadioGroup
345-
aria-label="model"
346-
name="model"
347-
value={`${model?.vendor}/${model?.name}`}
348-
onChange={(event) => {
349-
const selectedModel = aiModels?.find((model) => `${model.vendor}/${model.name}` === event.target.value)
350-
setModel(selectedModel!)
351-
}}
352-
className={classes.radioGroup}
353-
>
354-
{aiModels.map((model) =>
355-
<FormControlLabel
356-
key={`${model.vendor}/${model.name}`}
357-
value={`${model.vendor}/${model.name}`}
358-
control={<Radio />}
359-
label={`${model.name} ${model.comment ? model.comment : ''}`}
360-
/>
361-
)
362-
}
363-
</RadioGroup>
364-
</>}
365359
</DialogContent>
366360

367361
<DialogActions>

‎ui/packages/platform/src/pages/Bot/SettingsPanel/SettingsPanel.tsx

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { theme } from "@postgres.ai/shared/styles/theme";
77
import { permalinkLinkBuilder } from "../utils";
88
import { useAiBot } from "../hooks";
99
import DeveloperModeIcon from "@material-ui/icons/DeveloperMode";
10+
import { ModelSelector } from "../ModelSelector/ModelSelector";
11+
import { Skeleton } from "@mui/material";
1012

1113
export type SettingsPanelProps = {
1214
onSettingsClick: () => void;
@@ -31,25 +33,14 @@ const useStyles = makeStyles((theme) => ({
3133
}
3234
},
3335
labelVisibility: {
34-
marginLeft: '0.5rem',
36+
marginRight: '0.5rem',
3537
[theme.breakpoints.down('sm')]: {
36-
marginLeft: '0.25rem'
38+
marginRight: '0.25rem'
3739
},
3840
'&:hover': {
3941
backgroundColor: colors.secondary1.main
4042
}
4143
},
42-
labelModel: {
43-
background: colors.secondary1.main,
44-
},
45-
labelModelInvalid: {
46-
background: colors.state.error,
47-
border: "none",
48-
cursor: 'pointer',
49-
'&:hover': {
50-
backgroundColor: colors.primary.dark
51-
}
52-
},
5344
labelPrivate: {
5445
backgroundColor: colors.pgaiDarkGray,
5546
},
@@ -74,48 +65,33 @@ const useStyles = makeStyles((theme) => ({
7465

7566
export const SettingsPanel = (props: SettingsPanelProps) => {
7667
const { onSettingsClick, onConsoleClick } = props;
68+
const { loading } = useAiBot()
7769
const classes = useStyles();
7870
const matches = useMediaQuery(theme.breakpoints.down('sm'));
79-
const { messages, chatVisibility, aiModel,aiModelsLoading } = useAiBot();
71+
const { messages, chatVisibility, aiModelsLoading } = useAiBot();
8072
const permalinkId = useMemo(() => messages?.[0]?.id, [messages]);
8173

82-
let modelLabel;
83-
84-
if (aiModel) {
85-
modelLabel = (
86-
<span
87-
className={cn(classes.label, classes.labelModel)}
88-
>
89-
{aiModel.name}
90-
</span>
91-
)
92-
} else {
93-
modelLabel = (
94-
<button
95-
className={cn(classes.label, classes.labelModelInvalid)}
96-
onClick={onSettingsClick}
97-
>
98-
Model not set
99-
</button>
100-
)
101-
}
102-
10374
return (
10475
<>
105-
{!aiModelsLoading && modelLabel}
106-
{permalinkId && <a
107-
href={permalinkId && chatVisibility === 'public' ? permalinkLinkBuilder(permalinkId) : ''}
108-
className={cn(classes.label, classes.labelVisibility,
109-
{
110-
[classes.labelPrivate]: chatVisibility === 'private',
111-
[classes.disabled]: chatVisibility === 'private' || !permalinkId
112-
}
113-
)}
114-
target="_blank"
115-
aria-disabled={chatVisibility === 'private' || !permalinkId}
116-
>
117-
<span>{chatVisibility}</span> thread
118-
</a>}
76+
{permalinkId && <>
77+
{loading
78+
? <Skeleton variant="rectangular" className={cn(classes.label, classes.labelVisibility)} width={64} height={16} />
79+
: <a
80+
href={permalinkId && chatVisibility === 'public' ? permalinkLinkBuilder(permalinkId) : ''}
81+
className={cn(classes.label, classes.labelVisibility,
82+
{
83+
[classes.labelPrivate]: chatVisibility === 'private',
84+
[classes.disabled]: chatVisibility === 'private' || !permalinkId
85+
}
86+
)}
87+
target="_blank"
88+
aria-disabled={chatVisibility === 'private' || !permalinkId}
89+
>
90+
<span>{chatVisibility}</span> thread
91+
</a>
92+
}
93+
</>}
94+
{!aiModelsLoading && <ModelSelector />}
11995
<Button
12096
variant="outlined"
12197
onClick={onSettingsClick}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /