From 443c4ead6b3cd5b2a0437f91324e261d96bdfaa8 Mon Sep 17 00:00:00 2001 From: Yi Ding Date: Sun, 24 May 2026 11:42:35 -0700 Subject: [PATCH] Fix OpenAI SDK migration with compatible v6 client imports --- package-lock.json | 109 +++++++--------------------------------- package.json | 2 +- pages/api/image.ts | 16 +++--- pages/api/wordgrade.ts | 50 ++++++++++-------- pages/api/wordpieces.ts | 81 ++++++++++++++--------------- pages/api/words.ts | 83 +++++++++++++----------------- 6 files changed, 126 insertions(+), 215 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4b2bf6c..e30bf53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "eslint-config-next": "13.4.4", "eventsource-parser": "^1.0.0", "next": "13.3.2", - "openai": "^3.2.1", + "openai": "^6.39.0", "react": "18.2.0", "react-dom": "18.2.0", "react-loading-skeleton": "^3.2.1", @@ -1575,11 +1575,6 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -1632,14 +1627,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -1848,17 +1835,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1997,14 +1973,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2734,25 +2702,6 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2761,19 +2710,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -3694,25 +3630,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4045,12 +3962,24 @@ } }, "node_modules/openai": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", - "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", - "dependencies": { - "axios": "^0.26.0", - "form-data": "^4.0.0" + "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": { diff --git a/package.json b/package.json index 152cfcc..427ee9c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "eslint-config-next": "13.4.4", "eventsource-parser": "^1.0.0", "next": "13.3.2", - "openai": "^3.2.1", + "openai": "^6.39.0", "react": "18.2.0", "react-dom": "18.2.0", "react-loading-skeleton": "^3.2.1", diff --git a/pages/api/image.ts b/pages/api/image.ts index e2f4f87..ec96168 100644 --- a/pages/api/image.ts +++ b/pages/api/image.ts @@ -1,11 +1,9 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { Configuration, OpenAIApi } from "openai"; +import { OpenAI } from "openai"; -const openai = new OpenAIApi( - new Configuration({ - apiKey: process.env.OPENAI_API_KEY, - }) -); +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); type Data = { error?: string; @@ -23,11 +21,11 @@ export default async function handler( const { word } = req.query; - const response = await openai.createImage({ + const response = await openai.images.generate({ + model: "gpt-image-1", prompt: `clip art of ${word}`, - n: 1, size: "256x256", }); - res.status(200).json({ url: response.data.data[0].url }); + res.status(200).json({ url: response.data?.[0]?.url }); } diff --git a/pages/api/wordgrade.ts b/pages/api/wordgrade.ts index bedb286..15d6743 100644 --- a/pages/api/wordgrade.ts +++ b/pages/api/wordgrade.ts @@ -1,12 +1,11 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type { NextApiRequest, NextApiResponse } from "next"; -import { Configuration, OpenAIApi } from "openai"; +import { OpenAI } from "openai"; -const openai = new OpenAIApi( - new Configuration({ - apiKey: process.env.OPENAI_API_KEY, - }) -); +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +const model = process.env.OPENAI_MODEL ?? "gpt-5.5"; type Data = { error?: string; @@ -19,7 +18,7 @@ export default async function handler( ) { const { word } = req.query; - const prompt = `Use this format: + const instructions = `Use this format: Word: Reading Grade JSON: @@ -34,24 +33,31 @@ Word: instrument Reading Grade JSON: {"grade": "6"} Word: improbable -Reading Grade JSON: {"grade": "6"} - -Word: ${word} -Reading Grade JSON:`; +Reading Grade JSON: {"grade": "6"}`; - const { data } = await openai.createChatCompletion({ - model: "gpt-3.5-turbo", - messages: [{ role: "user", content: prompt }], - max_tokens: 1000, + const response = await openai.responses.create({ + model, + instructions, + input: `Word: ${word}\nReading Grade JSON:`, + max_output_tokens: 200, temperature: 0.1, - top_p: 1, - presence_penalty: 0, - frequency_penalty: 0, - n: 1, - stream: false, + text: { + format: { + type: "json_schema", + name: "reading_grade", + schema: { + type: "object", + properties: { + grade: { type: "string" }, + }, + required: ["grade"], + additionalProperties: false, + }, + }, + }, }); - const responseJson = data.choices[0].message?.content; + const responseJson = response.output_text; if (!responseJson) { res.status(503).json({ error: "No response" }); diff --git a/pages/api/wordpieces.ts b/pages/api/wordpieces.ts index 02c94c4..ccc46d2 100644 --- a/pages/api/wordpieces.ts +++ b/pages/api/wordpieces.ts @@ -1,11 +1,11 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai"; +import { OpenAI } from "openai"; -const openai = new OpenAIApi( - new Configuration({ - apiKey: process.env.OPENAI_API_KEY, - }) -); +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +const model = process.env.OPENAI_MODEL ?? "gpt-5.5"; type Data = { error?: string; @@ -18,14 +18,11 @@ export default async function handler( ) { const { word, grade } = req.query; - let messages: ChatCompletionRequestMessage[]; + let instructions: string; if (grade === "K") { - messages = [ - { - role: "system", - content: `Split the given word into phoneme sequences and its corresponding characters. - Split the word into individual letters. + instructions = `Split the given word into phoneme sequences and its corresponding characters. + Split the word into individual letters. The character letters should add up to the original word. Each character should sound like the phoneme. Do not output anything except JSON. Use this format: @@ -36,27 +33,16 @@ Output: [ { "phonemes": "b", "characters": "b" }, { "phonemes": "eɪ", "characte Word: hat Output: [ { "phonemes": "h", "characters": "h" }, { "phonemes": "æ", "characters": "a" }, { "phonemes": "t", "characters": "t" } ] -Word: pig - +Word: pig Output: [ { "phonemes": "p", "characters": "p" }, { "phonemes": "ɪ", characters: "i" }, { "phonemes": "g", "characters": "g" } ] Word: blue Output: [ { "phonemes": "b", "characters": "b" }, { "phonemes": "l", characters: "l" }, { "phonemes": "u:", "characters": "ue" } ] Word: happy -Output: [ { "phonemes": "h", "characters": "h" }, { "phonemes": "æ", characters: "a" }, { "phonemes": "p", "characters": "pp" },{ "phonemes": "i", "characters": "y" } ]`, - }, - { - role: "user", - content: `Word: ${word} -Output:`, - }, - ]; +Output: [ { "phonemes": "h", "characters": "h" }, { "phonemes": "æ", characters: "a" }, { "phonemes": "p", "characters": "pp" },{ "phonemes": "i", "characters": "y" } ]`; } else { - messages = [ - { - role: "system", - content: `Split the given word into phoneme sequences and its corresponding characters. + instructions = `Split the given word into phoneme sequences and its corresponding characters. The character letters should add up to the given word. If a given word only has 5 or less letters, split it into sequences of 3 or less characters. Each character sequence should sound like the phoneme sequence. @@ -88,29 +74,36 @@ Word: friend Output:[ { "phonemes": "fr", "characters": "fr" }, { "phonemes": "ɛnd", "characters": "iend" } ] Word: laugh -Output: [ { "phonemes": "læ", "characters": "lau" }, { "phonemes": "f", "characters": "gh" } ]`, - }, - { - role: "user", - content: `Word: ${word} -Output:`, - }, - ]; +Output: [ { "phonemes": "læ", "characters": "lau" }, { "phonemes": "f", "characters": "gh" } ]`; } - const { data } = await openai.createChatCompletion({ - model: "gpt-4", - messages, - max_tokens: 1000, + const response = await openai.responses.create({ + model, + instructions, + input: `Word: ${word}\nOutput:`, + max_output_tokens: 1000, temperature: 0.1, - top_p: 1, - presence_penalty: 0, - frequency_penalty: 0, - n: 1, - stream: false, + text: { + format: { + type: "json_schema", + name: "word_pieces", + schema: { + type: "array", + items: { + type: "object", + properties: { + phonemes: { type: "string" }, + characters: { type: "string" }, + }, + required: ["phonemes", "characters"], + additionalProperties: false, + }, + }, + }, + }, }); - const responseJson = data.choices[0].message?.content; + const responseJson = response.output_text; if (!responseJson) { res.status(503).json({ error: "No response" }); diff --git a/pages/api/words.ts b/pages/api/words.ts index 8686ada..992d22c 100644 --- a/pages/api/words.ts +++ b/pages/api/words.ts @@ -1,12 +1,11 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type { NextApiRequest, NextApiResponse } from "next"; -import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai"; +import { OpenAI } from "openai"; -const openai = new OpenAIApi( - new Configuration({ - apiKey: process.env.OPENAI_API_KEY, - }) -); +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +const model = process.env.OPENAI_MODEL ?? "gpt-5.5"; type Data = { error?: string; words?: string[] }; @@ -21,13 +20,11 @@ export default async function handler( phonemeSequence = ""; } - let messages: ChatCompletionRequestMessage[]; - let model: string; + let instructions: string; + let input: string; + if (phonemeSequence && characters) { - messages = [ - { - role: "system", - content: `Output only a JSON list of up to 9 words where this string of characters makes this phoneme sequence. Do not transpose the characters or phonemes. + instructions = `Output only a JSON list of up to 9 words where this string of characters makes this phoneme sequence. Do not transpose the characters or phonemes. Output only JSON. Use this format: @@ -59,23 +56,14 @@ Output: ["inspire", "voyage", "acquire", "conclave", "expanse", "forsake", "intr Characters: k Phonemes: Reading Grade Level: 1 -Output: ["knee", "knob", "knock", "knit", "knot", "know", "knight", "kneel", "known"]`, - }, - { - role: "user", - content: `Characters: ${characters} +Output: ["knee", "knob", "knock", "knit", "knot", "know", "knight", "kneel", "known"]`; + + input = `Characters: ${characters} Phonemes: ${phonemeSequence} Reading Grade Level: ${grade} -Output:`, - }, - ]; - - model = "gpt-4"; +Output:`; } else { - messages = [ - { - role: "system", - content: `Output a JSON list of 9 new words at the appropriate reading grade level. + instructions = `Output a JSON list of 9 new words at the appropriate reading grade level. Use this format: @@ -90,35 +78,32 @@ Output: ["balloon", "riding", ...] Reading Grade Level: 11 Output: ["undulate", "articulate", ...] -`, - }, - { - role: "user", - content: `Reading Grade Level: ${grade} - Output:`, - }, - ]; +`; - model = "gpt-3.5-turbo"; + input = `Reading Grade Level: ${grade} + Output:`; } - console.log(messages); - - const { data } = await openai.createChatCompletion({ + const response = await openai.responses.create({ model, - messages, - max_tokens: 1000, + instructions, + input, + max_output_tokens: 1000, temperature: 0.5, - top_p: 1, - presence_penalty: 0, - frequency_penalty: 0, - n: 1, - stream: false, + text: { + format: { + type: "json_schema", + name: "word_list", + schema: { + type: "array", + maxItems: 9, + items: { type: "string" }, + }, + }, + }, }); - const responseJson = data.choices[0].message?.content; - - console.log(responseJson); + const responseJson = response.output_text; if (!responseJson) { res.status(503).json({ error: "No response" });