Skip to content

Commit

Permalink
Checks that the openAI api key provided is valid
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanger2000 committed Mar 30, 2023
1 parent a0c53e5 commit bfd7f8d
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 44 deletions.
137 changes: 95 additions & 42 deletions src/components/settingsPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,61 +183,114 @@ export function SettingsPopup() {

export function OpenAIPanel() {
const settings = useAppSelector(ssel.getSettings)
const [localAPIKey, setLocalAPIKey] = useState('')
const [models, setAvailableModels] = useState<string[]>([])
const [keyError, showKeyError] = useState(false)
const dispatch = useAppDispatch()

// When the global openai key changes, we change this one
useEffect(() => {
if (settings.openAIKey && settings.openAIKey != localAPIKey) {
setLocalAPIKey(settings.openAIKey)
ssel.getModels(settings.openAIKey).then(({models, isValidKey}) => {
if (models) {
setAvailableModels(models)
}
})
}
}, [settings.openAIKey])

useEffect(() => {
showKeyError(false)
}, [localAPIKey]);

const handleNewAPIKey = useCallback(async () => {
const {models, isValidKey} = await ssel.getModels(localAPIKey)
console.log({models, isValidKey})
if (!isValidKey) {
// Error, and we let them know
showKeyError(true)
setAvailableModels([])
} else {
setAvailableModels(models)
dispatch(
changeSettings({
openAIKey: localAPIKey,
})
)
}
}, [localAPIKey])

return (
<div className="settings__item">

<div className="settings__item_title">
OpenAI API Key
</div>
<div className="settings__item_description">
We'll use your key for any requests to OpenAI. This will help you avoid "maximum capacity" limits.
</div>
<input
className="settings__item_textarea"
placeholder="Enter your OpenAI API Key"
onChange={(e) => {
dispatch(
changeSettings({
openAIKey: e.target.value,
})
)
}}
value={settings.openAIKey || ""}
/>
<div className="flex items-center">
<Switch
checked={settings.useOpenAIKey}
onChange={(value) => dispatch(changeSettings({useOpenAIKey: value}))}
className={`${settings.useOpenAIKey ? 'bg-green-500' : 'bg-red-500'}
mt-2 relative inline-flex h-[38px] w-[74px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className={`${settings.useOpenAIKey ? 'translate-x-9' : 'translate-x-0'}
pointer-events-none inline-block h-[34px] w-[34px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
/>
</Switch>
{settings.useOpenAIKey ? (

<span className="ml-2">Enabled</span>
) : (
<span className="ml-2">Disabled</span>
)}
</div>
{settings.useOpenAIKey &&
<Dropdown
options={['gpt4', 'gpt3.5']}
<div className="flex">
<input
className={`settings__item_textarea
${keyError ? 'input-error' : ''}`}
placeholder="Enter your OpenAI API Key"
onChange={(e) => {
dispatch(
changeSettings({
openAIModel: e.value,
})
)
setLocalAPIKey(e.target.value)
}}
value={settings.openAIModel}
value={localAPIKey || ""}
spellCheck="false"
/>
<button className='settings__button'
onClick={() => {
handleNewAPIKey()
}}
>
Submit
</button>
</div>
{keyError && (
<div className="error-message">
Invalid API Key. Please try again.
</div>
)}
{settings.openAIKey &&
<>
<div className="flex items-center">
<Switch
checked={settings.useOpenAIKey}
onChange={(value) => dispatch(changeSettings({useOpenAIKey: value}))}
className={`${settings.useOpenAIKey ? 'bg-green-500' : 'bg-red-500'}
mt-2 relative inline-flex h-[38px] w-[74px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className={`${settings.useOpenAIKey ? 'translate-x-9' : 'translate-x-0'}
pointer-events-none inline-block h-[34px] w-[34px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
/>
</Switch>
{settings.useOpenAIKey ? (

<span className="ml-2">Enabled</span>
) : (
<span className="ml-2">Disabled</span>
)}
</div>
{settings.useOpenAIKey &&
<Dropdown
options={models}
onChange={(e) => {
dispatch(
changeSettings({
openAIModel: e.value,
})
)
}}
value={settings.openAIModel}
/>
}
</>
}
</div>
)
Expand Down
25 changes: 25 additions & 0 deletions src/features/settings/settingsSelectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { FullState, SettingsState } from '../window/state'
import { createSelector } from 'reselect'

const availableServerModels = [
'gpt-4',
'gpt-3.5-turbo'
]

export const getSettingsIsOpen = createSelector(
(state: FullState) => state.settingsState,
(settings: SettingsState) => settings.isOpen
Expand All @@ -10,3 +15,23 @@ export const getSettings = createSelector(
(state: FullState) => state.settingsState,
(settings: SettingsState) => settings.settings
)

export async function getModels(secretKey: string) {
return await fetch('https://api.openai.com/v1/models', {
headers: {
Authorization: `Bearer ${secretKey}`,
},
}).then(async(response) => {
if (response.status == 401) {
return {
models: [],
isValidKey: false
}
}
const models = await response.json() as {data: {id: string}[]}
return {
models: models.data.filter(datum => availableServerModels.includes(datum.id)).map(datum => datum.id),
isValidKey: true
}
})
}
1 change: 1 addition & 0 deletions src/features/settings/settingsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const changeSettings = createAsyncThunk(
}
)


export const settingsSlice = createSlice({
name: 'settings',
initialState: initialSettingsState as SettingsState,
Expand Down
3 changes: 1 addition & 2 deletions src/features/window/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export interface Settings {
textWrapping: string
openAIKey?: string
useOpenAIKey?: boolean
openAIModel: 'gpt4' | 'gpt3.5'
openAIModel?: string
tabSize?: string
}

Expand Down Expand Up @@ -412,7 +412,6 @@ export const initialSettingsState = {
contextType: 'none',
textWrapping: 'disabled',
tabSize: undefined,
openAIModel: 'gpt3.5'
},
}

Expand Down
10 changes: 10 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,11 @@ cm-diff-cancel > div {
flex-grow: 1;
}

.input-error {
border: 1px solid red !important;
/* background-color: #ffe6e6; */
}

.feedbackarea_button {
color: #ccc;
margin-right: 8px;
Expand All @@ -1683,6 +1688,7 @@ cm-diff-cancel > div {
cursor: pointer;
}

.settings__button,
.copilot__signin button {
background-color: var(--vscode-blue);
padding: 4px 12px;
Expand All @@ -1691,6 +1697,10 @@ cm-diff-cancel > div {
margin-top: 8px;
}

.settings__button {
margin-left: 8px;
}

.settings a {
color: var(--link-vscode-blue);
text-decoration: underline;
Expand Down

0 comments on commit bfd7f8d

Please sign in to comment.