From d595ff217a56e83837c25c99aacebe28947180ed Mon Sep 17 00:00:00 2001 From: sepo-agent <279869237+sepo-agent@users.noreply.github.com> Date: Wed, 27 May 2026 20:19:23 +0000 Subject: [PATCH 1/2] chore: migrate OpenAI lib files to official SDK, upgrade gpt-4-vision-preview to gpt-4o --- app/lib/generateCode.tsx | 7 +-- app/lib/getCodeFromOpenAI.ts | 89 ++++----------------------- app/lib/getHtmlFromOpenAI.ts | 85 ++++---------------------- app/lib/getInterpretationFromAI.ts | 97 ++++-------------------------- app/lib/interpretShapes.tsx | 7 +-- package-lock.json | 44 +++++++++++--- package.json | 1 + 7 files changed, 75 insertions(+), 255 deletions(-) diff --git a/app/lib/generateCode.tsx b/app/lib/generateCode.tsx index b260f49..0313098 100644 --- a/app/lib/generateCode.tsx +++ b/app/lib/generateCode.tsx @@ -95,12 +95,7 @@ export async function generateCode( throw Error('Could not contact OpenAI.') } - if (json?.error) { - throw Error(`${json.error.message?.slice(0, 128)}...`) - } - - - let message = json.choices[0].message.content + let message = json.choices[0].message.content ?? '' const regex = /```json\n([\s\S]*?)```/; const matches = message.match(regex); if (matches && matches[1]) { diff --git a/app/lib/getCodeFromOpenAI.ts b/app/lib/getCodeFromOpenAI.ts index 4ea40ca..8c6a514 100644 --- a/app/lib/getCodeFromOpenAI.ts +++ b/app/lib/getCodeFromOpenAI.ts @@ -1,3 +1,4 @@ +import OpenAI from 'openai' import { CodeEditorShape } from '../CodeEditorShape/CodeEditorShape' import { OPENAI_MAKE_CODE_PROMPT, @@ -29,7 +30,9 @@ export async function getCodeFromOpenAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') - const messages: GPT4oCompletionRequest['messages'] = [ + const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) + + const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: 'system', content: intended_edit?.length ? OPENAI_EDIT_PARTIAL_CODE_PROMPT : OPENAI_MAKE_CODE_PROMPT, @@ -40,14 +43,13 @@ export async function getCodeFromOpenAI({ }, ] - const userContent = messages[1].content as Exclude + const userContent = messages[1].content as OpenAI.Chat.ChatCompletionContentPart[] userContent.push({ type: 'text', text: intended_edit?.length ? OPENAI_USER_EDIT_PARTIAL_CODE_PROMPT : OPENAI_USER_MAKE_CODE_PROMPT, }) - // Add the image userContent.push({ type: 'image_url', image_url: { @@ -63,7 +65,6 @@ export async function getCodeFromOpenAI({ }) } - // Add the strings of text if (text) { userContent.push({ type: 'text', @@ -78,7 +79,6 @@ export async function getCodeFromOpenAI({ }) } - // Add the previous previews code for (let i = 0; i < previousCodeEditors.length; i++) { const preview = previousCodeEditors[i] userContent.push({ @@ -94,81 +94,16 @@ export async function getCodeFromOpenAI({ }) } - // Prompt the theme - // userContent.push({ - // type: 'text', - // text: `Please make your result use the ${theme} theme.`, - // }) - - const body: GPT4oCompletionRequest = { - model: 'gpt-4o', - max_tokens: 4096, - temperature: 0, - messages, - seed: 42, - n: 1, - } - - let json = null - try { - const resp = await fetch('https://api.openai.com/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKey}`, - }, - body: JSON.stringify(body), + return await client.chat.completions.create({ + model: 'gpt-4o', + max_tokens: 4096, + temperature: 0, + messages, + seed: 42, + n: 1, }) - json = await resp.json() } catch (e: any) { throw Error(`Could not contact OpenAI: ${e.message}`) } - - return json -} - -type MessageContent = - | string - | ( - | string - | { - type: 'image_url' - image_url: - | string - | { - url: string - detail: 'low' | 'high' | 'auto' - } - } - | { - type: 'text' - text: string - } - )[] - -export type GPT4oCompletionRequest = { - model: 'gpt-4o' - messages: { - role: 'system' | 'user' | 'assistant' | 'function' - content: MessageContent - name?: string | undefined - }[] - functions?: any[] | undefined - function_call?: any | undefined - stream?: boolean | undefined - temperature?: number | undefined - top_p?: number | undefined - max_tokens?: number | undefined - n?: number | undefined - best_of?: number | undefined - frequency_penalty?: number | undefined - presence_penalty?: number | undefined - seed?: number | undefined - logit_bias?: - | { - [x: string]: number - } - | undefined - stop?: (string[] | string) | undefined } diff --git a/app/lib/getHtmlFromOpenAI.ts b/app/lib/getHtmlFromOpenAI.ts index f6868fe..770e454 100644 --- a/app/lib/getHtmlFromOpenAI.ts +++ b/app/lib/getHtmlFromOpenAI.ts @@ -1,3 +1,4 @@ +import OpenAI from 'openai' import { PreviewShape } from '../PreviewShape/PreviewShape' import { OPENAI_USER_PROMPT, @@ -26,7 +27,9 @@ export async function getHtmlFromOpenAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') - const messages: GPT4VCompletionRequest['messages'] = [ + const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) + + const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: 'system', content: OPEN_AI_SYSTEM_PROMPT, @@ -37,16 +40,14 @@ export async function getHtmlFromOpenAI({ }, ] - const userContent = messages[1].content as Exclude + const userContent = messages[1].content as OpenAI.Chat.ChatCompletionContentPart[] - // Add the prompt into userContent.push({ type: 'text', text: previousPreviews?.length > 0 ? OPENAI_USER_PROMPT_WITH_PREVIOUS_DESIGN : OPENAI_USER_PROMPT, }) - // Add the image userContent.push({ type: 'image_url', image_url: { @@ -55,7 +56,6 @@ export async function getHtmlFromOpenAI({ }, }) - // Add the strings of text if (text) { userContent.push({ type: 'text', @@ -70,7 +70,6 @@ export async function getHtmlFromOpenAI({ }) } - // Add the previous previews as HTML for (let i = 0; i < previousPreviews.length; i++) { const preview = previousPreviews[i] userContent.push( @@ -85,81 +84,21 @@ export async function getHtmlFromOpenAI({ ) } - // Prompt the theme userContent.push({ type: 'text', text: `Please make your result use the ${theme} theme.`, }) - const body: GPT4VCompletionRequest = { - model: 'gpt-4-vision-preview', - max_tokens: 4096, - temperature: 0, - messages, - seed: 42, - n: 1, - } - - let json = null - try { - const resp = await fetch('https://api.openai.com/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKey}`, - }, - body: JSON.stringify(body), + return await client.chat.completions.create({ + model: 'gpt-4o', + max_tokens: 4096, + temperature: 0, + messages, + seed: 42, + n: 1, }) - json = await resp.json() } catch (e: any) { throw Error(`Could not contact OpenAI: ${e.message}`) } - - return json -} - -type MessageContent = - | string - | ( - | string - | { - type: 'image_url' - image_url: - | string - | { - url: string - detail: 'low' | 'high' | 'auto' - } - } - | { - type: 'text' - text: string - } - )[] - -export type GPT4VCompletionRequest = { - model: 'gpt-4-vision-preview' - messages: { - role: 'system' | 'user' | 'assistant' | 'function' - content: MessageContent - name?: string | undefined - }[] - functions?: any[] | undefined - function_call?: any | undefined - stream?: boolean | undefined - temperature?: number | undefined - top_p?: number | undefined - max_tokens?: number | undefined - n?: number | undefined - best_of?: number | undefined - frequency_penalty?: number | undefined - presence_penalty?: number | undefined - seed?: number | undefined - logit_bias?: - | { - [x: string]: number - } - | undefined - stop?: (string[] | string) | undefined } diff --git a/app/lib/getInterpretationFromAI.ts b/app/lib/getInterpretationFromAI.ts index 7d2fb4f..1bbafbd 100644 --- a/app/lib/getInterpretationFromAI.ts +++ b/app/lib/getInterpretationFromAI.ts @@ -1,3 +1,4 @@ +import OpenAI from 'openai' import { CodeEditorShape } from '../CodeEditorShape/CodeEditorShape' import { OPENAI_INTERPRETATION_SKETCH_PROMPT, @@ -23,7 +24,9 @@ export async function getInterpretationFromAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') - const messages: GPT4oCompletionRequest['messages'] = [ + const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) + + const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: 'system', content: OPENAI_INTERPRETATION_SKETCH_PROMPT, @@ -34,14 +37,13 @@ export async function getInterpretationFromAI({ }, ] - const userContent = messages[1].content as Exclude + const userContent = messages[1].content as OpenAI.Chat.ChatCompletionContentPart[] userContent.push({ type: 'text', text: OPENAI_USER_INTERPRETATION_SKETCH_PROMPT, }) - // Add the image userContent.push({ type: 'image_url', image_url: { @@ -50,7 +52,6 @@ export async function getInterpretationFromAI({ }, }) - if (text) { userContent.push({ type: 'text', @@ -58,14 +59,6 @@ export async function getInterpretationFromAI({ }) } - // if (grid) { - // userContent.push({ - // type: 'text', - // text: `The user have a ${grid.color} grid overlaid on top. Each cell of the grid is ${grid.size}x${grid.size}px.`, - // }) - // } - - // userContent.push( { type: 'text', @@ -73,82 +66,16 @@ export async function getInterpretationFromAI({ } ) - // Prompt the theme - // userContent.push({ - // type: 'text', - // text: `Please make your result use the ${theme} theme.`, - // }) - - const body: GPT4oCompletionRequest = { - model: 'gpt-4o', - max_tokens: 4096, - temperature: 1, - messages, - seed: 42, - n: 1, - } - - let json = null - try { - const resp = await fetch('https://api.openai.com/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKey}`, - }, - body: JSON.stringify(body), + return await client.chat.completions.create({ + model: 'gpt-4o', + max_tokens: 4096, + temperature: 1, + messages, + seed: 42, + n: 1, }) - json = await resp.json() - // console.log(json) } catch (e: any) { throw Error(`Could not contact OpenAI: ${e.message}`) } - - return json } - -type MessageContent = - | string - | ( - | string - | { - type: 'image_url' - image_url: - | string - | { - url: string - detail: 'low' | 'high' | 'auto' - } - } - | { - type: 'text' - text: string - } - )[] - -export type GPT4oCompletionRequest = { - model: 'gpt-4o' | 'gpt-4o-mini' | 'gpt-4-turbo' - messages: { - role: 'system' | 'user' | 'assistant' | 'function' - content: MessageContent - name?: string | undefined - }[] - functions?: any[] | undefined - function_call?: any | undefined - stream?: boolean | undefined - temperature?: number | undefined - top_p?: number | undefined - max_tokens?: number | undefined - n?: number | undefined - best_of?: number | undefined - frequency_penalty?: number | undefined - presence_penalty?: number | undefined - seed?: number | undefined - logit_bias?: - | { - [x: string]: number - } - | undefined - stop?: (string[] | string) | undefined -} \ No newline at end of file diff --git a/app/lib/interpretShapes.tsx b/app/lib/interpretShapes.tsx index ab01dbb..2620148 100644 --- a/app/lib/interpretShapes.tsx +++ b/app/lib/interpretShapes.tsx @@ -136,12 +136,7 @@ export async function interpretShapes(editor: Editor, apiKey: string, codeShapeI throw Error('Could not contact OpenAI.') } - if (json?.error) { - throw Error(`${json.error.message?.slice(0, 128)}...`) - } - - - let message = json.choices[0].message.content + let message = json.choices[0].message.content ?? '' const regex = /```json\n([\s\S]*?)```/; const matches = message.match(regex); if (matches && matches[1]) { diff --git a/package-lock.json b/package-lock.json index b42ece3..ddeba76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "html2canvas": "^1.4.1", "jszip": "^3.10.1", "next": "14.0.3", + "openai": "^6.39.0", "react": "^18", "react-chartjs-2": "^5.2.0", "react-codemirror-merge": "^4.22.2", @@ -6519,6 +6520,27 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "6.39.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.39.0.tgz", + "integrity": "sha512-O61LIsimY3acVabwvomwFhwrnN36yvHY2quIfy9keEcFytGgWeV35yLHQ6NVMLSBxRpHmcg2yuhCnlu2HT4pLQ==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -7999,10 +8021,10 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -12386,6 +12408,12 @@ "wrappy": "1" } }, + "openai": { + "version": "6.39.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.39.0.tgz", + "integrity": "sha512-O61LIsimY3acVabwvomwFhwrnN36yvHY2quIfy9keEcFytGgWeV35yLHQ6NVMLSBxRpHmcg2yuhCnlu2HT4pLQ==", + "requires": {} + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -13389,10 +13417,10 @@ "dev": true }, "ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "devOptional": true, "requires": {} }, "y18n": { diff --git a/package.json b/package.json index 3e3f376..7c45b0c 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "html2canvas": "^1.4.1", "jszip": "^3.10.1", "next": "14.0.3", + "openai": "^6.39.0", "react": "^18", "react-chartjs-2": "^5.2.0", "react-codemirror-merge": "^4.22.2", From d62a5d197c8bdf43785357c7424ecb2cb326b716 Mon Sep 17 00:00:00 2001 From: sepo-agent <279869237+sepo-agent@users.noreply.github.com> Date: Wed, 27 May 2026 20:35:22 +0000 Subject: [PATCH 2/2] Remove dead-code null guards and document dangerouslyAllowBrowser intent --- app/lib/generateCode.tsx | 4 ---- app/lib/getCodeFromOpenAI.ts | 1 + app/lib/getHtmlFromOpenAI.ts | 1 + app/lib/getInterpretationFromAI.ts | 1 + app/lib/interpretShapes.tsx | 4 ---- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/lib/generateCode.tsx b/app/lib/generateCode.tsx index 0313098..25aaa14 100644 --- a/app/lib/generateCode.tsx +++ b/app/lib/generateCode.tsx @@ -91,10 +91,6 @@ export async function generateCode( intended_edit, }); - if (!json) { - throw Error('Could not contact OpenAI.') - } - let message = json.choices[0].message.content ?? '' const regex = /```json\n([\s\S]*?)```/; const matches = message.match(regex); diff --git a/app/lib/getCodeFromOpenAI.ts b/app/lib/getCodeFromOpenAI.ts index 8c6a514..cde496f 100644 --- a/app/lib/getCodeFromOpenAI.ts +++ b/app/lib/getCodeFromOpenAI.ts @@ -30,6 +30,7 @@ export async function getCodeFromOpenAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') + // Client-side demo app; apiKey is user-supplied at runtime, mirroring the prior raw-fetch Authorization header pattern. const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/app/lib/getHtmlFromOpenAI.ts b/app/lib/getHtmlFromOpenAI.ts index 770e454..92d118f 100644 --- a/app/lib/getHtmlFromOpenAI.ts +++ b/app/lib/getHtmlFromOpenAI.ts @@ -27,6 +27,7 @@ export async function getHtmlFromOpenAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') + // Client-side demo app; apiKey is user-supplied at runtime, mirroring the prior raw-fetch Authorization header pattern. const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/app/lib/getInterpretationFromAI.ts b/app/lib/getInterpretationFromAI.ts index 1bbafbd..11c0a73 100644 --- a/app/lib/getInterpretationFromAI.ts +++ b/app/lib/getInterpretationFromAI.ts @@ -24,6 +24,7 @@ export async function getInterpretationFromAI({ }) { if (!apiKey) throw Error('You need to provide an API key (sorry)') + // Client-side demo app; apiKey is user-supplied at runtime, mirroring the prior raw-fetch Authorization header pattern. const client = new OpenAI({ apiKey, dangerouslyAllowBrowser: true }) const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/app/lib/interpretShapes.tsx b/app/lib/interpretShapes.tsx index 2620148..cbcf94d 100644 --- a/app/lib/interpretShapes.tsx +++ b/app/lib/interpretShapes.tsx @@ -132,10 +132,6 @@ export async function interpretShapes(editor: Editor, apiKey: string, codeShapeI handleStoreLog({ type: 'start-interpretation', data: dataUrl }) - if (!json) { - throw Error('Could not contact OpenAI.') - } - let message = json.choices[0].message.content ?? '' const regex = /```json\n([\s\S]*?)```/; const matches = message.match(regex);