Skip to content

Commit 7b3e133

Browse files
author
Keivan Vosoughi
committed
Add Edit Modal
1 parent ecb8576 commit 7b3e133

File tree

5 files changed

+292
-30
lines changed

5 files changed

+292
-30
lines changed

app/client/src/pages/Settings/AddModelProviderButton.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const AddModelProviderButton: React.FC<Props> = ({ refetch }) => {
8181
message: 'Success',
8282
description: `THe model provider has been created successfully!.`
8383
});
84+
form.resetFields();
8485
setShowModal(false);
8586
refetch();
8687
}
@@ -90,7 +91,7 @@ const AddModelProviderButton: React.FC<Props> = ({ refetch }) => {
9091
form.resetFields();
9192
setShowModal(false);
9293
}
93-
94+
9495
const onSubmit = async () => {
9596
try {
9697
await form.validateFields();
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import { useEffect, useState } from 'react';
2+
import { PlusCircleOutlined } from '@ant-design/icons';
3+
import { Alert, Button, Form, Input, Modal, notification, Radio, Select } from 'antd';
4+
import type { CheckboxGroupProps } from 'antd/es/checkbox';
5+
import get from 'lodash/get';
6+
import isEqual from 'lodash/isEqual';
7+
import { useMutation } from "@tanstack/react-query";
8+
import { addModelProvider, useGetModelProvider } from './hooks';
9+
import Loading from '../Evaluator/Loading';
10+
import { CustomModel } from './SettingsPage';
11+
import isEmpty from 'lodash/isEmpty';
12+
13+
export enum ModelProviderType {
14+
OPENAI = 'openai',
15+
GEMINIE = 'gemini',
16+
CAII = 'caii'
17+
}
18+
19+
20+
const modelProviderTypeOptions: CheckboxGroupProps<string>['options'] = [
21+
{ label: 'OpenAI', value: 'openai' },
22+
// { label: 'CAII', value: 'caii' },
23+
{ label: 'Gemini', value: 'gemini' },
24+
];
25+
26+
const OPENAI_MODELS = [
27+
"gpt-4.1", // Latest GPT-4.1 series (April 2025)
28+
"gpt-4.1-mini",
29+
"gpt-4.1-nano",
30+
"o3", // Latest reasoning models (April 2025)
31+
"o4-mini",
32+
"o3-mini", // January 2025
33+
"o1", // December 2024
34+
"gpt-4o", // November 2024
35+
"gpt-4o-mini", // July 2024
36+
"gpt-4-turbo", // April 2024
37+
"gpt-3.5-turbo" // Legacy but still widely used
38+
];
39+
40+
const OPENAI_MODELS_OPTIONS = OPENAI_MODELS.map((model: string) => ({
41+
label: model,
42+
value: model
43+
}));
44+
45+
const GEMINI_MODELS = [
46+
"gemini-2.5-pro", // June 2025 - most powerful thinking model
47+
"gemini-2.5-flash", // June 2025 - best price-performance
48+
"gemini-2.5-flash-lite", // June 2025 - cost-efficient
49+
"gemini-2.0-flash", // February 2025 - next-gen features
50+
"gemini-2.0-flash-lite", // February 2025 - low latency
51+
"gemini-1.5-pro", // September 2024 - complex reasoning
52+
"gemini-1.5-flash", // September 2024 - fast & versatile
53+
"gemini-1.5-flash-8b" // October 2024 - lightweight
54+
];
55+
56+
const GEMINI_MODELS_OPTIONS = GEMINI_MODELS.map((model: string) => ({
57+
label: model,
58+
value: model
59+
}));
60+
61+
interface Props {
62+
refetch: () => void;
63+
onClose: () => void;
64+
model: CustomModel;
65+
}
66+
67+
const EditModelProvider: React.FC<Props> = ({ model, refetch, onClose }) => {
68+
const [form] = Form.useForm();
69+
const modelProviderReq = useGetModelProvider(model.endpoint_id);
70+
console.log('modelProviderReq', modelProviderReq);
71+
const [models, setModels] = useState(OPENAI_MODELS_OPTIONS);
72+
const mutation = useMutation({
73+
mutationFn: addModelProvider
74+
});
75+
76+
useEffect(() => {
77+
if (!isEmpty(modelProviderReq.data)) {
78+
console.log('-------->', modelProviderReq.data);
79+
const endpoint = get(modelProviderReq, 'data.endpoint');
80+
form.setFieldsValue({
81+
...endpoint
82+
});
83+
}
84+
}, [modelProviderReq.data]);
85+
86+
87+
useEffect(() => {
88+
if (mutation.isError) {
89+
notification.error({
90+
message: 'Error',
91+
description: `An error occurred while fetching the model.\n ${mutation.error}`
92+
});
93+
}
94+
if (mutation.isSuccess) {
95+
notification.success({
96+
message: 'Success',
97+
description: `THe model provider has been edited successfully!.`
98+
});
99+
refetch();
100+
}
101+
}, [mutation.error, mutation.isSuccess]);
102+
103+
const onCancel = () => {
104+
form.resetFields();
105+
onClose();
106+
}
107+
108+
const onSubmit = async () => {
109+
try {
110+
await form.validateFields();
111+
const values = form.getFieldsValue();
112+
console.log('values', values);
113+
mutation.mutate({
114+
endpoint_config: {
115+
display_name: values.display_name,
116+
endpoint_id: values.endpoint_id,
117+
model_id: values.model_id,
118+
provider_type: values.provider_type,
119+
api_key: values.api_key,
120+
endpoint_url: values.endpoint_url
121+
}
122+
});
123+
} catch (error) {
124+
console.error(error);
125+
}
126+
};
127+
128+
129+
const initialValues = {
130+
provider_type: 'openai'
131+
};
132+
133+
const onChange = (e: any) => {
134+
console.log('onChange', e);
135+
const value = get(e, 'target.value');
136+
console.log('value:', value);
137+
if (value === 'openai' && !isEqual(OPENAI_MODELS_OPTIONS, models)) {
138+
setModels(OPENAI_MODELS_OPTIONS);
139+
} else if (value === 'gemini' && !isEqual(GEMINI_MODELS_OPTIONS, models)) {
140+
setModels(GEMINI_MODELS_OPTIONS);
141+
}
142+
}
143+
144+
return (
145+
<>
146+
<Modal
147+
visible
148+
okText={`Edit`}
149+
title={`Edit Model Provider`}
150+
onCancel={onCancel}
151+
onOk={onSubmit}
152+
width={800}
153+
>
154+
<Form form={form} layout="vertical" initialValues={initialValues}>
155+
<br />
156+
<br />
157+
{(mutation.isPending || modelProviderReq.isLoading) && <Loading />}
158+
{mutation.error && (
159+
<Alert
160+
type="error"
161+
message="Error Occurred"
162+
description={
163+
<div>{mutation.error instanceof Error ? mutation.error.message : String(mutation.error)}</div>
164+
}
165+
/>
166+
)}
167+
<Form.Item name="provider_type">
168+
<Radio.Group
169+
block
170+
options={modelProviderTypeOptions}
171+
defaultValue="openai"
172+
optionType="button"
173+
buttonStyle="solid"
174+
style={{ width: '40%' }}
175+
onChange={onChange}
176+
/>
177+
</Form.Item>
178+
<Form.Item
179+
name="display_name"
180+
label="Display Name"
181+
rules={[
182+
{
183+
required: true,
184+
message: 'This field is required.'
185+
}
186+
]}>
187+
<Input />
188+
</Form.Item>
189+
<Form.Item
190+
name="endpoint_id"
191+
label="Endpoint ID"
192+
rules={[
193+
{
194+
required: true,
195+
message: 'This field is required.'
196+
}
197+
]}>
198+
<Input />
199+
</Form.Item>
200+
<Form.Item
201+
name="model_id"
202+
label="Model"
203+
rules={[
204+
{
205+
required: true,
206+
message: 'This field is required.'
207+
}
208+
]}>
209+
<Select options={models} />
210+
</Form.Item>
211+
<Form.Item
212+
name="endpoint_url"
213+
label="Endpoint URL"
214+
rules={[
215+
{
216+
required: true,
217+
message: 'This field is required.'
218+
}
219+
]}>
220+
<Input />
221+
</Form.Item>
222+
<Form.Item
223+
name="api_key"
224+
label="API Key"
225+
rules={[
226+
{
227+
required: true,
228+
message: 'This field is required.'
229+
}
230+
]}>
231+
<Input.Password />
232+
</Form.Item>
233+
</Form>
234+
</Modal>
235+
</>
236+
);
237+
}
238+
239+
export default EditModelProvider;
240+

app/client/src/pages/Settings/ModelProvidersTable.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.

app/client/src/pages/Settings/SettingsPage.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
DeleteOutlined
1515
} from '@ant-design/icons';
1616
import { useMutation } from "@tanstack/react-query";
17+
import { useState } from "react";
18+
import EditModelProvider from "./EditModelProvider";
1719

1820

1921

@@ -75,6 +77,8 @@ const StyledButton = styled(Button)`
7577
`;
7678

7779
const SettingsPage: React.FC = () => {
80+
const [showModal, setShowModal] = useState(false);
81+
const [model, setModel] = useState<CustomModel | null>(null);
7882
const filteredModelsReq = useModelProviders();
7983
console.log('filteredModelsReq', filteredModelsReq);
8084
const customModels = get(filteredModelsReq, 'data.endpoints', []);
@@ -86,13 +90,12 @@ const SettingsPage: React.FC = () => {
8690
const onDelete = (model: CustomModel) => {
8791
Modal.confirm({
8892
content: (
89-
<span>{`Are you sure you want to delete the model ${model}`}</span>
93+
<span>{`Are you sure you want to delete the model \'${model.display_name}\'`}</span>
9094
),
9195
onOk: async () => {
9296
try {
9397
mutation.mutate({
94-
model_id: model.model_id,
95-
provider_type: model.provider_type
98+
endpoint_id: model.endpoint_id
9699
})
97100
} catch (error) {
98101
notification.error({
@@ -105,7 +108,12 @@ const SettingsPage: React.FC = () => {
105108
});
106109
};
107110

108-
const onEdit = () => {}
111+
const onEdit = (_model: CustomModel) => {
112+
setShowModal(true);
113+
setModel(_model)
114+
115+
116+
}
109117

110118
const modelProvidersColumns = [{
111119
key: 'display_name',
@@ -174,7 +182,7 @@ const SettingsPage: React.FC = () => {
174182
<StyledButton
175183
type="link"
176184
key={`${model.endpoint_id}-deploy`}
177-
onClick={onEdit}
185+
onClick={() => onEdit(model)}
178186
data-event-category="User Action"
179187
data-event="Edit"
180188
>
@@ -187,9 +195,6 @@ const SettingsPage: React.FC = () => {
187195
}
188196
}];
189197

190-
191-
const onAdd = () => {};
192-
193198
return (
194199
<Layout>
195200
<StyledContent>
@@ -218,6 +223,8 @@ const SettingsPage: React.FC = () => {
218223
</Col>
219224
</Row>
220225
</Container>
226+
{showModal &&
227+
<EditModelProvider model={model as CustomModel} refetch={filteredModelsReq.refetch} onClose={() => setShowModal(false)} />}
221228
</StyledContent>
222229
</Layout>
223230
);

0 commit comments

Comments
 (0)