Skip to content

Commit 73c9254

Browse files
authored
Improve model api keys page (#67)
1 parent f4b09c0 commit 73c9254

File tree

11 files changed

+112
-74
lines changed

11 files changed

+112
-74
lines changed

app/controllers/llm_api_key.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,28 @@ func (c *LLMAPIKeyController) CreateLLMAPIKey(context *gin.Context) {
1919
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
2020
return
2121
}
22+
2223
userID, exists := context.Get("user_id")
2324
if !exists {
2425
context.JSON(http.StatusInternalServerError, gin.H{"error": "User ID not found in context"})
2526
return
2627
}
28+
2729
orgId, err := c.userService.FetchOrganisationIDByUserID(uint(userID.(int)))
2830
if err != nil {
2931
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
3032
return
3133
}
32-
err = c.llmAPIKeyService.CreateOrUpdateLLMAPIKey(orgId, createLLMAPIKey.LLMModel, createLLMAPIKey.LLMAPIKey)
33-
if err != nil {
34-
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
35-
return
34+
35+
for _, apiKey := range createLLMAPIKey.APIKeys {
36+
err = c.llmAPIKeyService.CreateOrUpdateLLMAPIKey(orgId, apiKey.LLMModel, apiKey.LLMAPIKey)
37+
if err != nil {
38+
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
39+
return
40+
}
3641
}
37-
context.JSON(http.StatusOK, gin.H{"message": "LLM API Key created successfully"})
42+
43+
context.JSON(http.StatusOK, gin.H{"message": "LLM API Keys created successfully"})
3844
}
3945

4046
func (c *LLMAPIKeyController) FetchAllLLMAPIKeyByOrganisationID(context *gin.Context) {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package request
22

33
type CreateLLMAPIKeyRequest struct {
4+
APIKeys []LLMAPIKey `json:"api_keys" binding:"required,dive,required"`
5+
}
6+
7+
type LLMAPIKey struct {
48
LLMModel string `json:"llm_model" binding:"required"`
59
LLMAPIKey string `json:"llm_api_key" binding:"required"`
610
}

gui/public/arrows/back_arrow.svg

Lines changed: 10 additions & 0 deletions
Loading

gui/public/icons/claude_icon.svg

Lines changed: 9 additions & 0 deletions
Loading

gui/public/icons/openai_icon.svg

Lines changed: 9 additions & 0 deletions
Loading

gui/src/api/DashboardService.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ export const createOrUpdateLLMAPIKey = (
122122
};
123123

124124
// design Story APIs
125-
126125
export const getAllDesignStoriesOfProject = (project_id: string) => {
127126
return api.get(`/projects/${project_id}/design/stories`);
128127
};

gui/src/app/imagePath.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,7 @@ export default {
7777
redCrossDeleteIcon: '/icons/red_cross_delete_icon.svg',
7878
copyIcon: '/icons/copy_icon.svg',
7979
emptyFilesIcons: '/icons/empty_files_icon.svg',
80+
openAIIcon: '/icons/openai_icon.svg',
81+
claudeIcon: '/icons/claude_icon.svg',
82+
backArrow: '/arrows/back_arrow.svg',
8083
};

gui/src/app/settings/SettingsOptions/Models.tsx

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,50 @@
11
'use client';
2-
import CustomSelect from '@/components/CustomSelect/CustomSelect';
32
import React, { useEffect, useState } from 'react';
43
import { Button } from '@nextui-org/react';
54
import { createOrUpdateLLMAPIKey, getLLMAPIKeys } from '@/api/DashboardService';
65
import toast from 'react-hot-toast';
7-
8-
interface ModelsList {
9-
model_name: string;
10-
api_key: string;
11-
}
6+
import CustomInput from '@/components/CustomInput/CustomInput';
7+
import imagePath from '@/app/imagePath';
8+
import { ModelsList } from '../../../../types/settingTypes';
129

1310
export default function Models() {
14-
const [selectedValue, setSelectedValue] = useState<string>('');
15-
const [apiKey, setApiKey] = useState<string | null>('');
1611
const [modelsList, setModelsList] = useState<ModelsList[] | null>(null);
12+
const modelDetails = {
13+
'gpt-4o': { text: 'Open AI API Key (gpt-4o)', icon: imagePath.openAIIcon },
14+
'claude-3': {
15+
text: 'Anthropic API Key (claude-3.4-sonnet)',
16+
icon: imagePath.claudeIcon,
17+
},
18+
};
1719

1820
const handleButtonClick = () => {
1921
toCreateOrUpdateLLMAPIKey().then().catch();
2022
};
2123

24+
const handleApiKeyChange = (model_name: string, value: string) => {
25+
setModelsList(
26+
(prev) =>
27+
prev?.map((model) =>
28+
model.model_name === model_name
29+
? { ...model, api_key: value }
30+
: model,
31+
) || null,
32+
);
33+
};
34+
2235
useEffect(() => {
2336
toGetLLMAPIKeys().then().catch();
2437
}, []);
2538

26-
useEffect(() => {
27-
if (modelsList && selectedValue.length > 0) {
28-
const selectedModel = modelsList.find(
29-
(model) => model.model_name === selectedValue,
30-
);
31-
if (selectedModel) {
32-
setApiKey(selectedModel.api_key);
33-
}
34-
}
35-
}, [selectedValue]);
36-
37-
useEffect(() => {
38-
if (modelsList) setSelectedValue(modelsList[0].model_name);
39-
}, [modelsList]);
40-
4139
async function toGetLLMAPIKeys() {
4240
try {
4341
const organisation_id = localStorage.getItem('organisationId');
4442
const response = await getLLMAPIKeys(organisation_id);
4543
if (response) {
46-
const data = response.data;
47-
console.log(data);
44+
const data = response.data.map((model: ModelsList) => ({
45+
...model,
46+
...modelDetails[model.model_name],
47+
}));
4848
setModelsList(data);
4949
}
5050
} catch (error) {
@@ -54,19 +54,17 @@ export default function Models() {
5454

5555
async function toCreateOrUpdateLLMAPIKey() {
5656
try {
57-
let id = null;
58-
if (typeof window !== 'undefined') {
59-
id = localStorage.getItem('organisationId');
60-
}
57+
const apiKeys = modelsList.map((model) => ({
58+
llm_model: model.model_name,
59+
llm_api_key: model.api_key,
60+
}));
61+
6162
const payload = {
62-
organisation_id: Number(id),
63-
llm_model: selectedValue,
64-
llm_api_key: apiKey,
63+
api_keys: apiKeys,
6564
};
65+
6666
const response = await createOrUpdateLLMAPIKey(payload);
6767
if (response) {
68-
const data = response.data;
69-
console.log(data);
7068
toast.success('Model is setup successfully');
7169
toGetLLMAPIKeys().then().catch();
7270
}
@@ -81,40 +79,28 @@ export default function Models() {
8179
return (
8280
<div id={'models_section'} className={'proxima_nova flex flex-col gap-6'}>
8381
<span id={'title'} className={'text-xl font-semibold text-white'}>
84-
{' '}
85-
Setup Model{' '}
82+
Setup Models
8683
</span>
87-
<div id={'model_selection_section'} className={'flex flex-col gap-2'}>
88-
<span className={'secondary_color text-[13px] font-medium'}>Model</span>
89-
<CustomSelect
90-
selectedValues={selectedValue}
91-
setSelectedValues={setSelectedValue}
92-
>
93-
{modelsList &&
94-
modelsList.length > 0 &&
95-
modelsList.map((model, index) => (
96-
<CustomSelect.Item key={index} value={model.model_name}>
97-
{model.model_name}
98-
</CustomSelect.Item>
99-
))}
100-
</CustomSelect>
101-
</div>
102-
<div id={'api_key_section'} className={'flex flex-col gap-2'}>
103-
<span className={'secondary_color text-[13px] font-medium'}>
104-
{' '}
105-
Open AI API Key{' '}
106-
</span>
107-
<input
108-
value={apiKey}
109-
type={'password'}
110-
className={'input_medium'}
111-
placeholder={'Enter API Key here'}
112-
onChange={(event) => setApiKey(event.target.value)}
113-
/>
84+
<div id={'api_key_section'} className={'flex flex-col gap-6'}>
85+
{modelsList?.map((model) => (
86+
<div key={model.model_name} className={'flex flex-col gap-2'}>
87+
<span className={'secondary_color text-[13px] font-medium'}>
88+
{model.text}
89+
</span>
90+
<CustomInput
91+
format={'password'}
92+
value={model.api_key || ''}
93+
setter={(value) => handleApiKeyChange(model.model_name, value)}
94+
placeholder={'Enter API Key here'}
95+
icon={model.icon}
96+
iconCSS={'size-4'}
97+
alt={`${model.model_name}_icon`}
98+
/>
99+
</div>
100+
))}
114101
</div>
115102
<Button className={'primary_medium w-fit'} onClick={handleButtonClick}>
116-
{' '}
117-
Update Changes{' '}
103+
Update Changes
118104
</Button>
119105
</div>
120106
);

gui/src/app/settings/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react';
33
import Models from '@/app/settings/SettingsOptions/Models';
44
import CustomSidebar from '@/components/CustomSidebar/CustomSidebar';
55
import imagePath from '@/app/imagePath';
6+
import BackButton from '@/components/BackButton/BackButton';
67

78
export default function Settings() {
89
const options = [
@@ -21,7 +22,9 @@ export default function Settings() {
2122
};
2223

2324
return (
24-
<div className={'mx-[20vw] my-8'}>
25+
<div className={'mx-[20vw] my-8 flex flex-col gap-3'}>
26+
<BackButton id={'settings'} url={'/board'} />
27+
2528
<CustomSidebar
2629
id={'settings'}
2730
title={'Settings'}

gui/types/modelsTypes.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
export interface CreateOrUpdateLLMAPIKeyPayload {
2-
organisation_id: number;
1+
export interface LLMAPIKey {
32
llm_model: string;
43
llm_api_key: string;
54
}
5+
6+
export interface CreateOrUpdateLLMAPIKeyPayload {
7+
api_keys: LLMAPIKey[];
8+
}

0 commit comments

Comments
 (0)