diff --git a/convex/lib/embeddings.ts b/convex/lib/embeddings.ts index 972e1c4..2eceacd 100644 --- a/convex/lib/embeddings.ts +++ b/convex/lib/embeddings.ts @@ -12,27 +12,40 @@ export async function generateEmbedding(text: string) { return emptyEmbedding() } - const response = await fetch('https://api.openai.com/v1/embeddings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKey}`, - }, - body: JSON.stringify({ - model: EMBEDDING_MODEL, - input: text, - }), - }) + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout - if (!response.ok) { - const message = await response.text() - throw new Error(`Embedding failed: ${message}`) - } + try { + const response = await fetch('https://api.openai.com/v1/embeddings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + model: EMBEDDING_MODEL, + input: text, + }), + signal: controller.signal, + }) + + if (!response.ok) { + const message = await response.text() + throw new Error(`Embedding failed: ${message}`) + } - const payload = (await response.json()) as { - data?: Array<{ embedding: number[] }> + const payload = (await response.json()) as { + data?: Array<{ embedding: number[] }> + } + const embedding = payload.data?.[0]?.embedding + if (!embedding) throw new Error('Embedding missing from response') + return embedding + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + throw new Error('OpenAI API request timed out after 10 seconds') + } + throw error + } finally { + clearTimeout(timeoutId) } - const embedding = payload.data?.[0]?.embedding - if (!embedding) throw new Error('Embedding missing from response') - return embedding } diff --git a/convex/search.ts b/convex/search.ts index c5f9234..332d1c7 100644 --- a/convex/search.ts +++ b/convex/search.ts @@ -120,7 +120,7 @@ export const hydrateResults = internalQuery({ }), ) - return entries.filter((entry): entry is HydratedEntry => entry !== null) + return entries.filter((entry) => entry !== null) as HydratedEntry[] }, }) diff --git a/src/routes/skills/index.tsx b/src/routes/skills/index.tsx index 3c95e1b..b406809 100644 --- a/src/routes/skills/index.tsx +++ b/src/routes/skills/index.tsx @@ -222,17 +222,17 @@ export function SkillsIndex() { return (
-
-
-

- Skills -

-

- {isLoadingSkills - ? 'Loading skills…' - : `Browse the skill library${highlightedOnly ? ' (highlighted)' : ''}.`} -

-
+
+

+ Skills +

+

+ {isLoadingSkills + ? 'Loading skills…' + : `Browse the skill library${highlightedOnly ? ' (highlighted)' : ''}.`} +

+
+
-
{isLoadingSkills ? (
@@ -394,6 +393,7 @@ export function SkillsIndex() { })}
)} + {canLoadMore ? (