From bd76aa9078394110b5375ec908aab9807ad576cf Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Fri, 9 Feb 2024 21:49:19 +0100 Subject: [PATCH] Add some error handling when OPENAI_API_KEY is not in the env --- src/components/App.tsx | 16 +++++- src/components/Plot.tsx | 1 - src/components/QueriesDisabledModal.tsx | 73 +++++++++++++++++++++++++ src/hooks/useQueryEmbeddings.ts | 40 +++++++++----- 4 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 src/components/QueriesDisabledModal.tsx diff --git a/src/components/App.tsx b/src/components/App.tsx index 5a609bf..01050ac 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -12,6 +12,7 @@ import { EmbeddingsComboBox } from "./EmbeddingsComboBox"; import { DotPlot, PlotData } from "./Plot"; import { toLatLng, toUnitSphere } from "../helpers/layout"; import { cosineDistance } from "../helpers/embeddings"; +import { QueriesDisabledModal } from "./QueriesDisabledModal"; function App() { const [selectedQuery, setSelectedQuery] = useState( @@ -23,7 +24,11 @@ function App() { const [embeddingsData, setEmbeddingsData] = useState([]); const [layoutData, setLayoutData] = useState([]); const [animate, setAnimate] = useState(false); - const activeEmbedding = useQueryEmbedding(selectedQuery.query); + const [queryError, setQueryError] = useState(""); + const onQueryError = useCallback((err: string) => { + setQueryError(err); + }, []); + const activeEmbedding = useQueryEmbedding(selectedQuery.query, onQueryError); const [displacement, setDisplacement] = useState(1); const [exponent, setExponent] = useState(4); @@ -91,12 +96,21 @@ function App() { setSelectedLayoutPoint(index); }, []); + const closeQueriesDisabledModal = useCallback(() => { + setQueryError(""); + }, []); + if (!data) { return "Loading..."; } return (
+

Embedding Sphere

diff --git a/src/components/Plot.tsx b/src/components/Plot.tsx index d2282ce..2e2a0da 100644 --- a/src/components/Plot.tsx +++ b/src/components/Plot.tsx @@ -48,7 +48,6 @@ export function PlotData({ similarities, onSelect }: PlotDataProps) { } containerRef.current.append(plot); - console.log("remove"); return () => plot.remove(); }, [similarities, onSelect]); diff --git a/src/components/QueriesDisabledModal.tsx b/src/components/QueriesDisabledModal.tsx new file mode 100644 index 0000000..24b5e86 --- /dev/null +++ b/src/components/QueriesDisabledModal.tsx @@ -0,0 +1,73 @@ +import { Dialog, Transition } from "@headlessui/react"; +import { Fragment } from "react"; + +export function QueriesDisabledModal({ + isOpen, + closeModal, + message, +}: { + isOpen: boolean; + message: string; + closeModal: () => void; +}) { + return ( + <> + + + +
+ + +
+
+ + + + An error occurred + +
+

{message}

+

+ Querying for custom values is only currently supported + running this UI locally and reading an OPENAI_API_KEY from + the process / terminal env. +

+
+ +
+ +
+
+
+
+
+
+
+ + ); +} diff --git a/src/hooks/useQueryEmbeddings.ts b/src/hooks/useQueryEmbeddings.ts index 3480cbd..493e8c0 100644 --- a/src/hooks/useQueryEmbeddings.ts +++ b/src/hooks/useQueryEmbeddings.ts @@ -2,7 +2,10 @@ import OpenAI from "openai"; import { useEffect, useMemo, useState } from "react"; import { ExampleQuery, exampleQueries } from "../helpers/example-queries"; -export function useQueryEmbedding(value: string) { +export function useQueryEmbedding( + value: string, + onError: (err: string) => void +) { const [embeddingQueries, setEmbeddingQueries] = useState([]); const allEmbeddings = useMemo( @@ -12,27 +15,36 @@ export function useQueryEmbedding(value: string) { useEffect(() => { const q = allEmbeddings.find((e) => e.query === value); + // already got the embeddings if (q) { return; } - const openai = new OpenAI({ - apiKey: import.meta.env["VITE_OPENAI_API_KEY"], - dangerouslyAllowBrowser: true, - }); const fetchEmbeddings = async () => { - const chatCompletion = await openai.embeddings.create({ - input: value, - model: "text-embedding-3-small", - }); - setEmbeddingQueries((prev) => [ - ...prev, - { query: value, embedding: chatCompletion.data[0].embedding }, - ]); + try { + const openai = new OpenAI({ + apiKey: import.meta.env["VITE_OPENAI_API_KEY"], + dangerouslyAllowBrowser: true, + }); + + const chatCompletion = await openai.embeddings.create({ + input: value, + model: "text-embedding-3-small", + }); + + setEmbeddingQueries((prev) => [ + ...prev, + { query: value, embedding: chatCompletion.data[0].embedding }, + ]); + } catch (e) { + if (onError) { + onError((e as Error).message); + } + } }; fetchEmbeddings(); - }, [value, allEmbeddings]); + }, [value, allEmbeddings, onError]); const activeEmbedding = allEmbeddings.find((e) => e.query === value); return activeEmbedding;