Migrate all apps and packages to use OpenAI v4 lib (#19270)

* Migrate all apps and packages to use OpenAI v4 lib.

* Minor fix.

* refactor: improve types for completion chunks

---------

Co-authored-by: Greg Richardson <greg.nmr@gmail.com>
This commit is contained in:
Ivan VasilovandGreg Richardson authored and GitHub committed 2023-11-30 09:01:51 +00:00
1 parent 38dfd14c31
commit 33f617a6cc
8 files changed
+90 -237

No files matched your search

+1 -2
View File
@@ -63,8 +63,7 @@
"monaco-editor": "0.33.0",
"next": "^13.5.6",
"next-themes": "^0.2.1",
"openai-old": "npm:openai@^3.3.0",
"openai": "^4.0.0",
"openai": "^4.20.1",
"openapi-fetch": "^0.6.1",
"p-queue": "^6.6.2",
"papaparse": "^5.3.1",
+15 -25
View File
@@ -4,12 +4,7 @@ import { isError } from 'data/utils/error-check'
import { jsonrepair } from 'jsonrepair'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import type {
ChatCompletionRequestMessage,
CreateChatCompletionRequest,
CreateChatCompletionResponse,
ErrorResponse,
} from 'openai-old'
import { OpenAI } from 'openai'
const openAiKey = process.env.OPENAI_KEY
@@ -23,7 +18,10 @@ const debugSqlSchema = SchemaBuilder.emptySchema()
type DebugSqlResult = typeof debugSqlSchema.T
const completionFunctions = {
const completionFunctions: Record<
string,
OpenAI.Chat.Completions.ChatCompletionCreateParams.Function
> = {
debugSql: {
name: 'debugSql',
description: stripIndent`
@@ -38,7 +36,7 @@ const completionFunctions = {
- Use vector(384) data type for any embedding/vector related query
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
`,
parameters: debugSqlSchema.schema,
parameters: debugSqlSchema.schema as Record<string, unknown>,
},
}
@@ -61,6 +59,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const openAI = new OpenAI({ apiKey: openAiKey })
const {
body: { errorMessage, sql, entityDefinitions },
} = req
@@ -69,7 +68,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const maxCompletionTokenCount = 2048
const hasEntityDefinitions = entityDefinitions !== undefined && entityDefinitions.length > 0
const completionMessages: ChatCompletionRequestMessage[] = []
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
if (hasEntityDefinitions) {
completionMessages.push({
@@ -98,7 +97,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
}
)
const completionOptions: CreateChatCompletionRequest = {
const completionOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model,
messages: completionMessages,
max_tokens: maxCompletionTokenCount,
@@ -110,20 +109,13 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
stream: false,
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
Authorization: `Bearer ${openAiKey}`,
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(completionOptions),
})
let completionResponse: OpenAI.Chat.Completions.ChatCompletion
try {
completionResponse = await openAI.chat.completions.create(completionOptions)
} catch (error: any) {
console.error(`AI SQL debugging failed: ${error.message}`)
if (!response.ok) {
const errorResponse: ErrorResponse = await response.json()
console.error(`AI SQL debugging failed: ${errorResponse.error.message}`)
if ('code' in errorResponse.error && errorResponse.error.code === 'context_length_exceeded') {
if ('code' in error && error.code === 'context_length_exceeded') {
if (hasEntityDefinitions) {
const definitionsLength = entityDefinitions.reduce(
(sum: number, def: string) => sum + def.length,
@@ -149,8 +141,6 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
const completionResponse: CreateChatCompletionResponse = await response.json()
const [firstChoice] = completionResponse.choices
const sqlResponseString = firstChoice.message?.function_call?.arguments
+15 -29
View File
@@ -4,12 +4,7 @@ import { isError } from 'data/utils/error-check'
import { jsonrepair } from 'jsonrepair'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import type {
ChatCompletionRequestMessage,
CreateChatCompletionRequest,
CreateChatCompletionResponse,
ErrorResponse,
} from 'openai-old'
import { OpenAI } from 'openai'
const openAiKey = process.env.OPENAI_KEY
@@ -29,11 +24,14 @@ const editSqlSchema = SchemaBuilder.emptySchema().addString('sql', {
type EditSqlResult = typeof editSqlSchema.T
const completionFunctions = {
const completionFunctions: Record<
string,
OpenAI.Chat.Completions.ChatCompletionCreateParams.Function
> = {
editSql: {
name: 'editSql',
description: "Edits a Postgres SQL query based on the user's instructions",
parameters: editSqlSchema.schema,
parameters: editSqlSchema.schema as Record<string, unknown>,
},
}
@@ -60,11 +58,12 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
body: { prompt, sql, entityDefinitions },
} = req
const openAI = new OpenAI({ apiKey: openAiKey })
const model = 'gpt-3.5-turbo-0613'
const maxCompletionTokenCount = 2048
const hasEntityDefinitions = entityDefinitions !== undefined && entityDefinitions.length > 0
const completionMessages: ChatCompletionRequestMessage[] = []
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
if (hasEntityDefinitions) {
completionMessages.push({
@@ -90,7 +89,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
}
)
const completionOptions: CreateChatCompletionRequest = {
const completionOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model,
messages: completionMessages,
max_tokens: maxCompletionTokenCount,
@@ -102,26 +101,17 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
stream: false,
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
Authorization: `Bearer ${openAiKey}`,
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(completionOptions),
})
if (!response.ok) {
const errorResponse: ErrorResponse = await response.json()
console.error(`AI SQL editing failed: ${errorResponse.error.message}`)
if ('code' in errorResponse.error && errorResponse.error.code === 'context_length_exceeded') {
let completionResponse: OpenAI.Chat.Completions.ChatCompletion
try {
completionResponse = await openAI.chat.completions.create(completionOptions)
} catch (error: any) {
console.error(`AI SQL editing failed: ${error.message}`)
if ('code' in error && error.code === 'context_length_exceeded') {
if (hasEntityDefinitions) {
const definitionsLength = entityDefinitions.reduce(
(sum: number, def: string) => sum + def.length,
0
)
if (definitionsLength > sql.length) {
return res.status(400).json({
error:
@@ -129,20 +119,16 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
}
return res.status(400).json({
error:
'Your SQL query is too large for Supabase AI to ingest. Try splitting it into smaller queries.',
})
}
return res.status(500).json({
error: 'There was an unknown error editing the SQL snippet. Please try again.',
})
}
const completionResponse: CreateChatCompletionResponse = await response.json()
const [firstChoice] = completionResponse.choices
const sqlResponseString = firstChoice.message?.function_call?.arguments
+15 -29
View File
@@ -4,12 +4,7 @@ import { isError } from 'data/utils/error-check'
import { jsonrepair } from 'jsonrepair'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import type {
ChatCompletionRequestMessage,
CreateChatCompletionRequest,
CreateChatCompletionResponse,
ErrorResponse,
} from 'openai-old'
import { OpenAI } from 'openai'
const openAiKey = process.env.OPENAI_KEY
@@ -34,11 +29,14 @@ const generateSqlSchema = SchemaBuilder.emptySchema()
type GenerateSqlResult = typeof generateSqlSchema.T
const completionFunctions = {
const completionFunctions: Record<
string,
OpenAI.Chat.Completions.ChatCompletionCreateParams.Function
> = {
generateSql: {
name: 'generateSql',
description: 'Generates Postgres SQL based on a natural language prompt',
parameters: generateSqlSchema.schema,
parameters: generateSqlSchema.schema as Record<string, unknown>,
},
}
@@ -61,6 +59,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const openAI = new OpenAI({ apiKey: openAiKey })
const {
body: { prompt, entityDefinitions },
} = req
@@ -69,7 +68,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const maxCompletionTokenCount = 1024
const hasEntityDefinitions = entityDefinitions !== undefined && entityDefinitions.length > 0
const completionMessages: ChatCompletionRequestMessage[] = []
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
if (hasEntityDefinitions) {
completionMessages.push({
@@ -86,7 +85,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
content: prompt,
})
const completionOptions: CreateChatCompletionRequest = {
const completionOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model,
messages: completionMessages,
max_tokens: maxCompletionTokenCount,
@@ -98,24 +97,13 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
stream: false,
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
Authorization: `Bearer ${openAiKey}`,
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(completionOptions),
})
let completionResponse: OpenAI.Chat.Completions.ChatCompletion
try {
completionResponse = await openAI.chat.completions.create(completionOptions)
} catch (error: any) {
console.error(`AI SQL generation failed: ${error.message}`)
if (!response.ok) {
const errorResponse: ErrorResponse = await response.json()
console.error(`AI SQL generation failed: ${errorResponse.error.message}`)
if (
'code' in errorResponse.error &&
errorResponse.error.code === 'context_length_exceeded' &&
hasEntityDefinitions
) {
if ('code' in error && error.code === 'context_length_exceeded' && hasEntityDefinitions) {
return res.status(400).json({
error:
'Your database metadata is too large for Supabase AI to ingest. Try disabling database metadata in AI settings.',
@@ -127,8 +115,6 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
const completionResponse: CreateChatCompletionResponse = await response.json()
const [firstChoice] = completionResponse.choices
const sqlResponseString = firstChoice.message?.function_call?.arguments
+12 -25
View File
@@ -4,12 +4,7 @@ import { isError } from 'data/utils/error-check'
import { jsonrepair } from 'jsonrepair'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import type {
ChatCompletionRequestMessage,
CreateChatCompletionRequest,
CreateChatCompletionResponse,
ErrorResponse,
} from 'openai-old'
import { OpenAI } from 'openai'
const openAiKey = process.env.OPENAI_KEY
@@ -29,11 +24,11 @@ const generateTitleSchema = SchemaBuilder.emptySchema()
type GenerateTitleResult = typeof generateTitleSchema.T
const completionFunctions = {
const completionFunctions: Record<string, OpenAI.ChatCompletionCreateParams.Function> = {
generateTitle: {
name: 'generateTitle',
description: 'Generates a short title and detailed description for a Postgres SQL snippet',
parameters: generateTitleSchema.schema,
parameters: generateTitleSchema.schema as Record<string, unknown>,
},
}
@@ -56,6 +51,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const openAI = new OpenAI({ apiKey: openAiKey })
const {
body: { sql },
} = req
@@ -63,14 +59,14 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const model = 'gpt-3.5-turbo-0613'
const maxCompletionTokenCount = 1024
const completionMessages: ChatCompletionRequestMessage[] = [
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{
role: 'user',
content: sql,
},
]
const completionOptions: CreateChatCompletionRequest = {
const completionOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model,
messages: completionMessages,
max_tokens: maxCompletionTokenCount,
@@ -82,20 +78,13 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
stream: false,
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
Authorization: `Bearer ${openAiKey}`,
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify(completionOptions),
})
let completionResponse: OpenAI.Chat.Completions.ChatCompletion
try {
completionResponse = await openAI.chat.completions.create(completionOptions)
} catch (error: any) {
console.error(`AI title generation failed: ${error.message}`)
if (!response.ok) {
const errorResponse: ErrorResponse = await response.json()
console.error(`AI title generation failed: ${errorResponse.error.message}`)
if ('code' in errorResponse.error && errorResponse.error.code === 'context_length_exceeded') {
if ('code' in error && error.code === 'context_length_exceeded') {
return res.status(400).json({
error:
'Your SQL query is too large for Supabase AI to ingest. Try splitting it into smaller queries.',
@@ -107,8 +96,6 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
const completionResponse: CreateChatCompletionResponse = await response.json()
const [firstChoice] = completionResponse.choices
const titleResponseString = firstChoice.message?.function_call?.arguments
+24 -108
View File
@@ -248,6 +248,7 @@
"version": "18.18.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz",
"integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -451,25 +452,6 @@
}
}
},
"apps/database-new/node_modules/openai": {
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.20.0.tgz",
"integrity": "sha512-VbAYerNZFfIIeESS+OL9vgDkK8Mnri55n+jN0UN/HZeuM0ghGh6nDN6UGRZxslNgyJ7XmY/Ca9DO4YYyvrszGA==",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"bin": {
"openai": "bin/cli"
}
},
"apps/database-new/node_modules/resolve": {
"version": "2.0.0-next.5",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
@@ -580,6 +562,7 @@
},
"apps/docs/node_modules/@types/node": {
"version": "18.18.8",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
@@ -662,25 +645,6 @@
],
"license": "MIT"
},
"apps/docs/node_modules/openai": {
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.20.0.tgz",
"integrity": "sha512-VbAYerNZFfIIeESS+OL9vgDkK8Mnri55n+jN0UN/HZeuM0ghGh6nDN6UGRZxslNgyJ7XmY/Ca9DO4YYyvrszGA==",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"bin": {
"openai": "bin/cli"
}
},
"apps/docs/node_modules/puppeteer": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-16.2.0.tgz",
@@ -786,8 +750,7 @@
"monaco-editor": "0.33.0",
"next": "^13.5.6",
"next-themes": "^0.2.1",
"openai": "^4.0.0",
"openai-old": "npm:openai@^3.3.0",
"openai": "^4.20.1",
"openapi-fetch": "^0.6.1",
"p-queue": "^6.6.2",
"papaparse": "^5.3.1",
@@ -886,13 +849,6 @@
"glob": "7.1.7"
}
},
"apps/studio/node_modules/@types/node": {
"version": "18.18.10",
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"apps/studio/node_modules/ajv": {
"version": "8.12.0",
"license": "MIT",
@@ -1023,24 +979,6 @@
"version": "1.0.0",
"license": "MIT"
},
"apps/studio/node_modules/openai": {
"version": "4.19.0",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"bin": {
"openai": "bin/cli"
}
},
"apps/studio/node_modules/resolve": {
"version": "2.0.0-next.5",
"dev": true,
@@ -17664,24 +17602,6 @@
"node": ">=0.4.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.3",
"license": "MIT",
@@ -26415,34 +26335,30 @@
}
},
"node_modules/openai": {
"version": "3.3.0",
"license": "MIT",
"version": "4.20.1",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.20.1.tgz",
"integrity": "sha512-Dd3q8EvINfganZFtg6V36HjrMaihqRgIcKiHua4Nq9aw/PxOP48dhbsk8x5klrxajt5Lpnc1KTOG5i1S6BKAJA==",
"dependencies": {
"axios": "^0.26.0",
"form-data": "^4.0.0"
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"bin": {
"openai": "bin/cli"
}
},
"node_modules/openai-old": {
"name": "openai",
"version": "3.3.0",
"license": "MIT",
"node_modules/openai/node_modules/@types/node": {
"version": "18.18.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz",
"integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==",
"dependencies": {
"axios": "^0.26.0",
"form-data": "^4.0.0"
}
},
"node_modules/openai-old/node_modules/axios": {
"version": "0.26.1",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.8"
}
},
"node_modules/openai/node_modules/axios": {
"version": "0.26.1",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.8"
"undici-types": "~5.26.4"
}
},
"node_modules/openapi-fetch": {
@@ -34939,7 +34855,7 @@
"lottie-light-react": "^2.4.0",
"lucide-react": "^0.167.0",
"next-themes": "^0.2.1",
"openai": "^3.3.0",
"openai": "^4.17.0",
"postcss": "^8.4.31",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.1.0",
+1 -1
View File
@@ -64,7 +64,7 @@
"lottie-light-react": "^2.4.0",
"lucide-react": "^0.167.0",
"next-themes": "^0.2.1",
"openai": "^3.3.0",
"openai": "^4.17.0",
"postcss": "^8.4.31",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.1.0",
@@ -1,8 +1,4 @@
import type {
ChatCompletionResponseMessage,
CreateChatCompletionResponse,
CreateChatCompletionResponseChoicesInner,
} from 'openai'
import type OpenAI from 'openai'
import {
Dispatch,
SetStateAction,
@@ -24,15 +20,15 @@ import {
Input,
markdownComponents,
} from 'ui'
import { AiIcon, AiIconChat } from './Command.icons'
import { AiIconChat } from './Command.icons'
import { CommandGroup, CommandItem, useAutoInputFocus, useHistoryKeys } from './Command.utils'
import { AiWarning } from './Command.alerts'
import { useCommandMenu } from './CommandMenuProvider'
import ReactMarkdown from 'react-markdown'
import { cn } from './../../lib/utils'
import remarkGfm from 'remark-gfm'
import { cn } from './../../lib/utils'
const questions = [
'How do I get started with Supabase?',
@@ -43,13 +39,6 @@ const questions = [
'How do I set up authentication?',
]
type CreateChatCompletionResponseChoicesInnerDelta = Omit<
CreateChatCompletionResponseChoicesInner,
'message'
> & {
delta: Partial<ChatCompletionResponseMessage>
}
function getEdgeFunctionUrl() {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL?.replace(/\/$/, '')
@@ -236,12 +225,12 @@ export function useAiChat({
setIsResponding(true)
const completionResponse: CreateChatCompletionResponse = JSON.parse(e.data)
const completionChunk: OpenAI.Chat.Completions.ChatCompletionChunk = JSON.parse(e.data)
const [
{
delta: { content },
},
] = completionResponse.choices as CreateChatCompletionResponseChoicesInnerDelta[]
] = completionChunk.choices
if (content) {
dispatchMessage({
@@ -328,12 +317,12 @@ export function queryAi(messages: Message[], timeout = 0) {
return
}
const completionResponse: CreateChatCompletionResponse = JSON.parse(e.data)
const completionChunk: OpenAI.Chat.Completions.ChatCompletionChunk = JSON.parse(e.data)
const [
{
delta: { content },
},
] = completionResponse.choices as CreateChatCompletionResponseChoicesInnerDelta[]
] = completionChunk.choices
if (content) {
answer += content