add api key
This commit is contained in:
		
							parent
							
								
									396fe4ec6f
								
							
						
					
					
						commit
						e6449998ef
					
				|  | @ -0,0 +1,66 @@ | |||
| import { IconCheck, IconKey, IconX } from "@tabler/icons-react"; | ||||
| import { FC, KeyboardEvent, useState } from "react"; | ||||
| import { SidebarButton } from "./SidebarButton"; | ||||
| 
 | ||||
| interface Props { | ||||
|   apiKey: string; | ||||
|   onApiKeyChange: (apiKey: string) => void; | ||||
| } | ||||
| 
 | ||||
| export const Key: FC<Props> = ({ apiKey, onApiKeyChange }) => { | ||||
|   const [isChanging, setIsChanging] = useState(false); | ||||
|   const [newKey, setNewKey] = useState(apiKey); | ||||
| 
 | ||||
|   const handleEnterDown = (e: KeyboardEvent<HTMLDivElement>) => { | ||||
|     if (e.key === "Enter") { | ||||
|       e.preventDefault(); | ||||
|       handleUpdateKey(newKey); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const handleUpdateKey = (newKey: string) => { | ||||
|     onApiKeyChange(newKey); | ||||
|     setIsChanging(false); | ||||
|   }; | ||||
| 
 | ||||
|   return isChanging ? ( | ||||
|     <div className="flex hover:bg-[#343541] py-2 px-2 rounded-md cursor-pointer w-full items-center"> | ||||
|       <IconKey size={16} /> | ||||
| 
 | ||||
|       <input | ||||
|         className="ml-2 flex-1 bg-transparent border-b border-neutral-400 focus:border-neutral-100 text-left overflow-hidden overflow-ellipsis pr-1 outline-none text-white" | ||||
|         type="password" | ||||
|         value={newKey} | ||||
|         onChange={(e) => setNewKey(e.target.value)} | ||||
|         onKeyDown={handleEnterDown} | ||||
|       /> | ||||
| 
 | ||||
|       <div className="flex w-[40px]"> | ||||
|         <IconCheck | ||||
|           className="ml-auto min-w-[20px] text-neutral-400 hover:text-neutral-100" | ||||
|           size={18} | ||||
|           onClick={(e) => { | ||||
|             e.stopPropagation(); | ||||
|             handleUpdateKey(newKey); | ||||
|           }} | ||||
|         /> | ||||
| 
 | ||||
|         <IconX | ||||
|           className="ml-auto min-w-[20px] text-neutral-400 hover:text-neutral-100" | ||||
|           size={18} | ||||
|           onClick={(e) => { | ||||
|             e.stopPropagation(); | ||||
|             setIsChanging(false); | ||||
|             setNewKey(apiKey); | ||||
|           }} | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ) : ( | ||||
|     <SidebarButton | ||||
|       text="OpenAI API Key" | ||||
|       icon={<IconKey size={16} />} | ||||
|       onClick={() => setIsChanging(true)} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
|  | @ -9,15 +9,17 @@ interface Props { | |||
|   conversations: Conversation[]; | ||||
|   lightMode: "light" | "dark"; | ||||
|   selectedConversation: Conversation; | ||||
|   apiKey: string; | ||||
|   onNewConversation: () => void; | ||||
|   onToggleLightMode: (mode: "light" | "dark") => void; | ||||
|   onSelectConversation: (conversation: Conversation) => void; | ||||
|   onDeleteConversation: (conversation: Conversation) => void; | ||||
|   onToggleSidebar: () => void; | ||||
|   onRenameConversation: (conversation: Conversation, name: string) => void; | ||||
|   onApiKeyChange: (apiKey: string) => void; | ||||
| } | ||||
| 
 | ||||
| export const Sidebar: FC<Props> = ({ loading, conversations, lightMode, selectedConversation, onNewConversation, onToggleLightMode, onSelectConversation, onDeleteConversation, onToggleSidebar, onRenameConversation }) => { | ||||
| export const Sidebar: FC<Props> = ({ loading, conversations, lightMode, selectedConversation, apiKey, onNewConversation, onToggleLightMode, onSelectConversation, onDeleteConversation, onToggleSidebar, onRenameConversation, onApiKeyChange }) => { | ||||
|   return ( | ||||
|     <div className="flex flex-col bg-[#202123] min-w-[260px] max-w-[260px]"> | ||||
|       <div className="flex items-center h-[60px] pl-2"> | ||||
|  | @ -52,7 +54,9 @@ export const Sidebar: FC<Props> = ({ loading, conversations, lightMode, selected | |||
| 
 | ||||
|       <SidebarSettings | ||||
|         lightMode={lightMode} | ||||
|         apiKey={apiKey} | ||||
|         onToggleLightMode={onToggleLightMode} | ||||
|         onApiKeyChange={onApiKeyChange} | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
|  |  | |||
|  | @ -1,20 +1,27 @@ | |||
| import { IconMoon, IconSun, IconTrash } from "@tabler/icons-react"; | ||||
| import { IconMoon, IconSun } from "@tabler/icons-react"; | ||||
| import { FC } from "react"; | ||||
| import { Key } from "./Key"; | ||||
| import { SidebarButton } from "./SidebarButton"; | ||||
| 
 | ||||
| interface Props { | ||||
|   lightMode: "light" | "dark"; | ||||
|   apiKey: string; | ||||
|   onToggleLightMode: (mode: "light" | "dark") => void; | ||||
|   onApiKeyChange: (apiKey: string) => void; | ||||
| } | ||||
| 
 | ||||
| export const SidebarSettings: FC<Props> = ({ lightMode, onToggleLightMode }) => { | ||||
| export const SidebarSettings: FC<Props> = ({ lightMode, apiKey, onToggleLightMode, onApiKeyChange }) => { | ||||
|   return ( | ||||
|     <div className="flex flex-col items-center border-t border-neutral-500 p-2 text-sm"> | ||||
|     <div className="flex flex-col items-center border-t border-neutral-500 px-2 py-4 text-sm space-y-4"> | ||||
|       <SidebarButton | ||||
|         text={lightMode === "light" ? "Dark mode" : "Light mode"} | ||||
|         icon={lightMode === "light" ? <IconMoon size={16} /> : <IconSun size={16} />} | ||||
|         onClick={() => onToggleLightMode(lightMode === "light" ? "dark" : "light")} | ||||
|       /> | ||||
|       <Key | ||||
|         apiKey={apiKey} | ||||
|         onApiKeyChange={onApiKeyChange} | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -7,9 +7,10 @@ export const config = { | |||
| 
 | ||||
| const handler = async (req: Request): Promise<Response> => { | ||||
|   try { | ||||
|     const { model, messages } = (await req.json()) as { | ||||
|     const { model, messages, key } = (await req.json()) as { | ||||
|       model: OpenAIModel; | ||||
|       messages: Message[]; | ||||
|       key: string; | ||||
|     }; | ||||
| 
 | ||||
|     const charLimit = 12000; | ||||
|  | @ -25,7 +26,7 @@ const handler = async (req: Request): Promise<Response> => { | |||
|       messagesToSend.push(message); | ||||
|     } | ||||
| 
 | ||||
|     const stream = await OpenAIStream(model, messagesToSend); | ||||
|     const stream = await OpenAIStream(model, key, messagesToSend); | ||||
| 
 | ||||
|     return new Response(stream); | ||||
|   } catch (error) { | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ export default function Home() { | |||
|   const [lightMode, setLightMode] = useState<"dark" | "light">("dark"); | ||||
|   const [messageIsStreaming, setMessageIsStreaming] = useState<boolean>(false); | ||||
|   const [showSidebar, setShowSidebar] = useState<boolean>(true); | ||||
|   const [apiKey, setApiKey] = useState<string>(""); | ||||
| 
 | ||||
|   const handleSend = async (message: Message) => { | ||||
|     if (selectedConversation) { | ||||
|  | @ -32,7 +33,8 @@ export default function Home() { | |||
|         }, | ||||
|         body: JSON.stringify({ | ||||
|           model, | ||||
|           messages: updatedConversation.messages | ||||
|           messages: updatedConversation.messages, | ||||
|           key: apiKey | ||||
|         }) | ||||
|       }); | ||||
| 
 | ||||
|  | @ -184,12 +186,22 @@ export default function Home() { | |||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const handleApiKeyChange = (apiKey: string) => { | ||||
|     setApiKey(apiKey); | ||||
|     localStorage.setItem("apiKey", apiKey); | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const theme = localStorage.getItem("theme"); | ||||
|     if (theme) { | ||||
|       setLightMode(theme as "dark" | "light"); | ||||
|     } | ||||
| 
 | ||||
|     const apiKey = localStorage.getItem("apiKey"); | ||||
|     if (apiKey) { | ||||
|       setApiKey(apiKey); | ||||
|     } | ||||
| 
 | ||||
|     const conversationHistory = localStorage.getItem("conversationHistory"); | ||||
| 
 | ||||
|     if (conversationHistory) { | ||||
|  | @ -234,12 +246,14 @@ export default function Home() { | |||
|               conversations={conversations} | ||||
|               lightMode={lightMode} | ||||
|               selectedConversation={selectedConversation} | ||||
|               apiKey={apiKey} | ||||
|               onToggleLightMode={handleLightMode} | ||||
|               onNewConversation={handleNewConversation} | ||||
|               onSelectConversation={handleSelectConversation} | ||||
|               onDeleteConversation={handleDeleteConversation} | ||||
|               onToggleSidebar={() => setShowSidebar(!showSidebar)} | ||||
|               onRenameConversation={handleRenameConversation} | ||||
|               onApiKeyChange={handleApiKeyChange} | ||||
|             /> | ||||
|           ) : ( | ||||
|             <IconArrowBarRight | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| import { Message, OpenAIModel } from "@/types"; | ||||
| import { createParser, ParsedEvent, ReconnectInterval } from "eventsource-parser"; | ||||
| 
 | ||||
| export const OpenAIStream = async (model: OpenAIModel, messages: Message[]) => { | ||||
| export const OpenAIStream = async (model: OpenAIModel, key: string, messages: Message[]) => { | ||||
|   const encoder = new TextEncoder(); | ||||
|   const decoder = new TextDecoder(); | ||||
| 
 | ||||
|   const res = await fetch("https://api.openai.com/v1/chat/completions", { | ||||
|     headers: { | ||||
|       "Content-Type": "application/json", | ||||
|       Authorization: `Bearer ${process.env.OPENAI_API_KEY}` | ||||
|       Authorization: `Bearer ${key ? key : process.env.OPENAI_API_KEY}` | ||||
|     }, | ||||
|     method: "POST", | ||||
|     body: JSON.stringify({ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue