Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model config #121

Merged
merged 12 commits into from
Apr 11, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "koala-client",
"private": true,
"version": "2.1.0",
"version": "2.1.1",
"type": "module",
"homepage": "./",
"main": "electron/index.cjs",
Expand Down
18 changes: 18 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,21 @@ export const submitShareGPT = async (body: ShareGPTSubmitBodyInterface) => {
const url = `https://shareg.pt/${id}`;
window.open(url, '_blank');
};

export const fetchOpenAIModels = async (apiKey: string) => {
try {
const response = await fetch('https://api.openai.com/v1/models', {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
const data = await response.json();
const openaiModels = data.data.filter(
(model: { owned_by: string }) => model.owned_by === 'openai'
);
return openaiModels.map((model: { name: any }) => model.name);
} catch (error) {
console.error('Error fetching models:', error);
return [];
}
};
196 changes: 12 additions & 184 deletions src/components/ApiMenu/ApiMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import VisibleIcon from '@icon/VisibleIcon';

import DownChevronArrow from '@icon/DownChevronArrow';
import { tokenCostToCost } from '@utils/messageUtils';
import { ModelInput } from './ModelInput';

const ApiMenu = ({
setIsModalOpen,
Expand Down Expand Up @@ -136,7 +137,7 @@ const ApiMenu = ({
}}
>
<div className='min-w-fit text-custom-white text-sm flex flex-col gap-2 leading-relaxed'>
<p>Deleting an endpoint will delete all asociated models.</p>
<p>Deleting an endpoint will delete all associated models.</p>
<p>Whisper record always uses the first endpoint.</p>
<div className='flex flex-col max-w-full'>
<div className='flex items-center border-b border-neutral-base/50 mb-1 p-1'>
Expand Down Expand Up @@ -198,194 +199,21 @@ const ApiMenu = ({
<PlusIcon />
</div>
</div>

<div className='flex flex-col max-w-full'>
<div className='text-center font-bold items-center border-b border-neutral-base/50 mb-1 p-1'>
Models
</div>
{_modelDefs.map((modelDef, index) => (
<div key={'model' + index} className='mb-4'>
<div className='flex items-center border-b border-neutral-base/50 mb-1 p-1'>
<div className='flex-1'>
<input
type='text'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Nickname'
value={modelDef.name}
onChange={(e) => {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].name = e.target.value;
return newModelDefs;
});
}}
/>
</div>
<div className='flex-1 px-1'>
<input
type='text'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Model'
value={modelDef.model}
onChange={(e) => {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].model = e.target.value;
return newModelDefs;
});
}}
/>
</div>
<div className='flex-1'>
<button
className='btn bg-custom-white text-custom-black btn-small overflow-clip relative pr-6 w-80'
type='button'
onClick={() => {
if (activeDropdown === index) {
setActiveDropdown(null);
} else {
setActiveDropdown(index);
}
}}
>
<span className='inline-block truncate max-w-full'>
{_apiAuth[modelDef.endpoint]
? _apiAuth[modelDef.endpoint].endpoint.replace(
/^https?:\/\//,
''
)
: 'Endpoint Undefined'}
</span>

<DownChevronArrow className='absolute right-0 mr-1 flex items-center' />
</button>

<div
id='dropdown'
className={`${
activeDropdown != null && activeDropdown == index
? ''
: 'hidden'
} absolute top-100 bottom-100 z-10 w-80 bg-custom-white text-custom-black shadow-xl rounded-lg border border-neutral-base group`}
>
<ul
className='text-sm p-0 m-0 max-h-72 overflow-clip'
aria-labelledby='dropdownDefaultButton'
>
{_apiAuth.map((auth, authIndex) => (
<li
className='btn btn-small w-full overflow-clip hover:bg-neutral-light hover:text-custom-white cursor-pointer'
onClick={() => {
setModelEndpoint(index, authIndex);
setActiveDropdown(null);
}}
key={'dropdown' + authIndex}
>
{auth.endpoint.replace(/^https?:\/\//, '')}
</li>
))}
</ul>
</div>
</div>
<div
className='p-1 ml-2 hover:text-neutral-dark hover:bg-custom-white hover:rounded'
onClick={() => deleteModel(index)}
>
<CrossIcon
className={`${_modelDefs.length > 1 ? '' : 'invisible'}`}
/>
</div>
</div>
<div className='flex items-center border-b border-neutral-base/50 px-1'>
<div className='flex-1 pr-1'>
<input
type='text'
pattern='[0-9]*'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Max Tokens'
value={modelDef.model_max_tokens || ''}
onChange={(e) => {
const value = Number(e.target.value);

if (!isNaN(value)) {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].model_max_tokens = value;
return newModelDefs;
});
}
}}
/>
</div>
<div className='flex-1 pr-1'>
<input
type='text'
pattern='[0-9]*'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Max Context'
value={modelDef.model_max_context || ''}
onChange={(e) => {
const value = Number(e.target.value);

if (!isNaN(value)) {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].model_max_context = value;
return newModelDefs;
});
}
}}
/>
</div>
<div className='flex-1 pr-1'>
<input
type='text'
pattern='[0-9]*'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Prompt Cost*'
value={modelDef?.prompt_cost_1000 || ''}
onChange={(e) => {
const value = Number(e.target.value);

if (!isNaN(value)) {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].prompt_cost_1000 = value;
return newModelDefs;
});
}
}}
/>
</div>
<div className='flex-1'>
<input
type='text'
pattern='[0-9]*'
className='text-custom-black p-3 text-sm border-none bg-custom-white rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
placeholder='Completion Cost*'
value={modelDef.completion_cost_1000 || ''}
onChange={(e) => {
const value = Number(e.target.value);

if (!isNaN(value)) {
_setModelDefs((prev) => {
const newModelDefs = [...prev];
newModelDefs[index].completion_cost_1000 = value;
return newModelDefs;
});
}
}}
/>
</div>
<div className='p-1 ml-2 hover:text-custom-black hover:bg-custom-white hover:rounded'>
{_modelDefs[index].swap_visible ? (
<VisibleIcon onClick={() => setHideModel(index, false)} />
) : (
<HiddenIcon onClick={() => setHideModel(index, true)} />
)}
</div>
</div>
</div>
<ModelInput
key={index}
modelDef={modelDef}
index={index}
apiAuth={_apiAuth}
setModelDefs={_setModelDefs}
setHideModel={setHideModel}
deleteModel={deleteModel}
setModelEndpoint={setModelEndpoint}
/>
))}
</div>
<div className='flex justify-center mt-0 mb-8'>
Expand Down
Loading
Loading