mirror of
https://github.com/supabase/supabase.git
synced 2026-10-19 16:19:10 +03:00
feat: llama3
This commit is contained in:
1 parent
45e04cfa63
commit
0963d40d20
22 files changed
+797
-333
No files matched your search
@@ -3,3 +3,5 @@
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-project-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
OPENAI_API_KEY=your-openai-api-key
|
||||
OPENAI_BASE_URL=your-openai-compatible-base-url
|
||||
OPENAI_MODEL=your-language-model
|
||||
@@ -1,17 +1,12 @@
|
||||
import { stripIndent } from 'common-tags'
|
||||
import { cookies } from 'next/headers'
|
||||
import OpenAI from 'openai'
|
||||
|
||||
// import is weird, what's up with this?
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { createOpenAiClient } from 'ai-commands'
|
||||
import { ContextLengthError } from '../../../../../packages/ai-commands/src/errors'
|
||||
import { getMessages } from './getMessages'
|
||||
|
||||
// Create an OpenAI API client (that's edge friendly!)
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
})
|
||||
|
||||
// Set the runtime to edge for best performance
|
||||
export const runtime = 'edge'
|
||||
|
||||
@@ -26,6 +21,12 @@ export type AiAssistantMessage = {
|
||||
* it.
|
||||
*/
|
||||
export const getAssistantResponse = async (threadId: string, messageId: string) => {
|
||||
const { openai, model, error: openAiError } = createOpenAiClient()
|
||||
|
||||
if (openAiError) {
|
||||
throw openAiError
|
||||
}
|
||||
|
||||
const supabase = createClient()
|
||||
|
||||
const { data: message, error } = await supabase
|
||||
@@ -91,7 +92,7 @@ export const getAssistantResponse = async (threadId: string, messageId: string)
|
||||
|
||||
try {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-16k-0613',
|
||||
model,
|
||||
messages: initMessages,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@supabase/ssr": "^0.1.0",
|
||||
"@tanstack/react-query": "^5.13.4",
|
||||
"ai": "^2.2.31",
|
||||
"ai-commands": "*",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"dayjs": "^1.11.10",
|
||||
@@ -42,4 +43,4 @@
|
||||
"postcss": "^8.4.31",
|
||||
"typescript": "^5.4.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -6,7 +6,6 @@ DEFAULT_PROJECT_NAME=Default Project
|
||||
DASHBOARD_USERNAME=supabase
|
||||
DASHBOARD_PASSWORD=this_password_is_insecure_and_should_be_updated
|
||||
|
||||
|
||||
SUPABASE_URL=http://localhost:8000
|
||||
SUPABASE_PUBLIC_URL=http://localhost:8000
|
||||
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
|
||||
@@ -24,4 +23,11 @@ NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzUwOTQ4MzUsImV4cCI6MTk5MDY3MDgzNX0.0PMlOxtKL4O9GGZuAP_Xl4f-Tut1qOnW4bNEmAtoB8w
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://xguihxuzqibwxjnimxev.supabase.co
|
||||
|
||||
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||
OPENAI_API_KEY="your-openai-api-key"
|
||||
OPENAI_BASE_URL="your-openai-compatible-base-url"
|
||||
OPENAI_MODEL="your-language-model"
|
||||
OPENAI_EMBEDDING_API_KEY="your-openai-api-key"
|
||||
OPENAI_EMBEDDING_BASE_URL="your-openai-compatible-base-url"
|
||||
OPENAI_EMBEDDING_MODEL="your-embedding-model"
|
||||
|
||||
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||
@@ -1,18 +1,9 @@
|
||||
import { ContextLengthError, EmptySqlError, debugSql } from 'ai-commands'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { OpenAI } from 'openai'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!openAiKey) {
|
||||
return res.status(500).json({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
})
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -25,12 +16,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
const {
|
||||
body: { errorMessage, sql, entityDefinitions },
|
||||
} = req
|
||||
|
||||
try {
|
||||
const result = await debugSql(openai, errorMessage, sql, entityDefinitions)
|
||||
const result = await debugSql(openai, model, errorMessage, sql, entityDefinitions)
|
||||
return res.json(result)
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { ContextLengthError, EmptySqlError, editSql } from 'ai-commands'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { OpenAI } from 'openai'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!openAiKey) {
|
||||
return res.status(500).json({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
})
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -25,12 +16,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
const {
|
||||
body: { prompt, sql, entityDefinitions },
|
||||
} = req
|
||||
|
||||
try {
|
||||
const result = await editSql(openai, prompt, sql, entityDefinitions)
|
||||
const result = await editSql(openai, model, prompt, sql, entityDefinitions)
|
||||
return res.json(result)
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
import { StreamingTextResponse } from 'ai'
|
||||
import { generateV2 } from 'ai-commands/edge'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import { NextRequest } from 'next/server'
|
||||
import OpenAI from 'openai'
|
||||
|
||||
export const runtime = 'edge'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
|
||||
export default async function handler(req: NextRequest) {
|
||||
if (!openAiKey) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -37,7 +23,19 @@ export default async function handler(req: NextRequest) {
|
||||
}
|
||||
|
||||
async function handlePost(request: NextRequest) {
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: error.message,
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const body = await (request.json() as Promise<{
|
||||
messages: { content: string; role: 'user' | 'assistant' }[]
|
||||
@@ -48,7 +46,7 @@ async function handlePost(request: NextRequest) {
|
||||
const { messages, existingSql, entityDefinitions } = body
|
||||
|
||||
try {
|
||||
const stream = await generateV2(openai, messages, existingSql, entityDefinitions)
|
||||
const stream = await generateV2(openai, model, messages, existingSql, entityDefinitions)
|
||||
return new StreamingTextResponse(stream)
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { ContextLengthError, EmptySqlError, generateSql } from 'ai-commands'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { OpenAI } from 'openai'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!openAiKey) {
|
||||
return res.status(500).json({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
})
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -25,12 +16,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
const {
|
||||
body: { prompt, entityDefinitions },
|
||||
} = req
|
||||
|
||||
try {
|
||||
const result = await generateSql(openai, prompt, entityDefinitions)
|
||||
const result = await generateSql(openai, model, prompt, entityDefinitions)
|
||||
return res.json(result)
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
import { StreamingTextResponse } from 'ai'
|
||||
import { chatRlsPolicy } from 'ai-commands/edge'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import { NextRequest } from 'next/server'
|
||||
import OpenAI from 'openai'
|
||||
|
||||
export const runtime = 'edge'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
|
||||
export default async function handler(req: NextRequest) {
|
||||
if (!openAiKey) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -37,7 +23,19 @@ export default async function handler(req: NextRequest) {
|
||||
}
|
||||
|
||||
async function handlePost(request: NextRequest) {
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: error.message,
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const body = await (request.json() as Promise<{
|
||||
messages: { content: string; role: 'user' | 'assistant' }[]
|
||||
@@ -48,7 +46,7 @@ async function handlePost(request: NextRequest) {
|
||||
const { messages, entityDefinitions, policyDefinition } = body
|
||||
|
||||
try {
|
||||
const stream = await chatRlsPolicy(openai, messages, entityDefinitions, policyDefinition)
|
||||
const stream = await chatRlsPolicy(openai, model, messages, entityDefinitions, policyDefinition)
|
||||
return new StreamingTextResponse(stream)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { ContextLengthError, titleSql } from 'ai-commands'
|
||||
import { createOpenAiClient } from 'ai-commands/src/openai'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { OpenAI } from 'openai'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!openAiKey) {
|
||||
return res.status(500).json({
|
||||
error: 'No OPENAI_KEY set. Create this environment variable to use AI features.',
|
||||
})
|
||||
}
|
||||
|
||||
const { method } = req
|
||||
|
||||
switch (method) {
|
||||
@@ -25,12 +16,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
const {
|
||||
body: { sql },
|
||||
} = req
|
||||
|
||||
try {
|
||||
const result = await titleSql(openai, sql)
|
||||
const result = await titleSql(openai, model, sql)
|
||||
return res.json(result)
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
Generated
+176
-41
@@ -39,6 +39,7 @@
|
||||
"@supabase/ssr": "^0.1.0",
|
||||
"@tanstack/react-query": "^5.13.4",
|
||||
"ai": "^2.2.31",
|
||||
"ai-commands": "*",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"dayjs": "^1.11.10",
|
||||
@@ -6983,6 +6984,14 @@
|
||||
"react-hook-form": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@huggingface/jinja": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.2.2.tgz",
|
||||
"integrity": "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
@@ -8646,6 +8655,60 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||
},
|
||||
"node_modules/@radix-ui/colors": {
|
||||
"version": "0.1.9",
|
||||
"license": "MIT"
|
||||
@@ -17012,6 +17075,11 @@
|
||||
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
|
||||
},
|
||||
"node_modules/@types/markdown-table": {
|
||||
"version": "3.0.0",
|
||||
"dev": true,
|
||||
@@ -18041,6 +18109,19 @@
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@xenova/transformers": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@xenova/transformers/-/transformers-2.17.1.tgz",
|
||||
"integrity": "sha512-zo702tQAFZXhzeD2GCYUNUqeqkoueOdiSbQWa4s0q7ZE4z8WBIwIsMMPGobpgdqjQ2u0Qulo08wuqVEUrBXjkQ==",
|
||||
"dependencies": {
|
||||
"@huggingface/jinja": "^0.2.2",
|
||||
"onnxruntime-web": "1.14.0",
|
||||
"sharp": "^0.32.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"onnxruntime-node": "1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xobotyi/scrollbar-width": {
|
||||
"version": "1.9.5",
|
||||
"license": "MIT"
|
||||
@@ -18898,8 +18979,7 @@
|
||||
"node_modules/b4a": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz",
|
||||
"integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg=="
|
||||
},
|
||||
"node_modules/babel-core": {
|
||||
"version": "7.0.0-bridge.0",
|
||||
@@ -19345,14 +19425,12 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz",
|
||||
"integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bare-fs": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.0.tgz",
|
||||
"integrity": "sha512-+VhW202E9eTVGkX7p+TNXtZC4RTzj9JfJW7PtfIbZ7mIQ/QT9uOafQTx7lx2n9ERmWsXvLHF4hStAFn4gl2mQw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-events": "^2.0.0",
|
||||
@@ -19365,14 +19443,12 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.2.0.tgz",
|
||||
"integrity": "sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bare-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.0.tgz",
|
||||
"integrity": "sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-os": "^2.1.0"
|
||||
@@ -22207,7 +22283,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
@@ -22280,7 +22355,6 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
@@ -24336,7 +24410,6 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -24533,8 +24606,7 @@
|
||||
"node_modules/fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.2",
|
||||
@@ -24889,6 +24961,11 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flatbuffers": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz",
|
||||
"integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ=="
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.2.9",
|
||||
"license": "ISC"
|
||||
@@ -25502,8 +25579,7 @@
|
||||
"node_modules/github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
||||
},
|
||||
"node_modules/github-slugger": {
|
||||
"version": "2.0.0",
|
||||
@@ -25683,6 +25759,11 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/guid-typescript": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
|
||||
"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ=="
|
||||
},
|
||||
"node_modules/gulp-header": {
|
||||
"version": "1.8.12",
|
||||
"license": "MIT",
|
||||
@@ -27100,8 +27181,7 @@
|
||||
"node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"node_modules/ini-simple-parser": {
|
||||
"version": "1.0.0",
|
||||
@@ -30404,6 +30484,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"node_modules/longest-streak": {
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
@@ -34588,7 +34673,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -35015,8 +35099,7 @@
|
||||
"node_modules/napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
@@ -35197,7 +35280,6 @@
|
||||
"version": "3.56.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz",
|
||||
"integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
@@ -35209,7 +35291,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -35221,7 +35302,6 @@
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@@ -35235,8 +35315,7 @@
|
||||
"node_modules/node-abi/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/node-abort-controller": {
|
||||
"version": "3.1.1",
|
||||
@@ -36364,6 +36443,46 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/onnx-proto": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz",
|
||||
"integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==",
|
||||
"dependencies": {
|
||||
"protobufjs": "^6.8.8"
|
||||
}
|
||||
},
|
||||
"node_modules/onnxruntime-common": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz",
|
||||
"integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew=="
|
||||
},
|
||||
"node_modules/onnxruntime-node": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz",
|
||||
"integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32",
|
||||
"darwin",
|
||||
"linux"
|
||||
],
|
||||
"dependencies": {
|
||||
"onnxruntime-common": "~1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/onnxruntime-web": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz",
|
||||
"integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==",
|
||||
"dependencies": {
|
||||
"flatbuffers": "^1.12.0",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"long": "^4.0.0",
|
||||
"onnx-proto": "^4.0.4",
|
||||
"onnxruntime-common": "~1.14.0",
|
||||
"platform": "^1.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/open": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
|
||||
@@ -37123,6 +37242,11 @@
|
||||
"pathe": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/platform": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.41.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz",
|
||||
@@ -37560,7 +37684,6 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
@@ -37852,6 +37975,31 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "6.11.4",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
|
||||
"integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@types/long": "^4.0.1",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pbjs": "bin/pbjs",
|
||||
"pbts": "bin/pbts"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"dev": true,
|
||||
@@ -38132,8 +38280,7 @@
|
||||
"node_modules/queue-tick": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
|
||||
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
|
||||
},
|
||||
"node_modules/raf-schd": {
|
||||
"version": "4.0.3",
|
||||
@@ -38246,7 +38393,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
@@ -38261,7 +38407,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -41423,7 +41568,6 @@
|
||||
"version": "0.32.6",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz",
|
||||
"integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
@@ -41446,7 +41590,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -41457,14 +41600,12 @@
|
||||
"node_modules/sharp/node_modules/node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
|
||||
},
|
||||
"node_modules/sharp/node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@@ -41479,7 +41620,6 @@
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz",
|
||||
"integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
@@ -41493,7 +41633,6 @@
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
|
||||
"integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"b4a": "^1.6.4",
|
||||
"fast-fifo": "^1.2.0",
|
||||
@@ -41503,8 +41642,7 @@
|
||||
"node_modules/sharp/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
@@ -41619,7 +41757,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -41639,7 +41776,6 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -42301,7 +42437,6 @@
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz",
|
||||
"integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-fifo": "^1.1.0",
|
||||
"queue-tick": "^1.0.1"
|
||||
@@ -44445,7 +44580,6 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
@@ -47343,6 +47477,7 @@
|
||||
"@gregnr/libpg-query": "^13.4.0-dev.12",
|
||||
"@serafin/schema-builder": "^0.18.5",
|
||||
"@supabase/supabase-js": "*",
|
||||
"@xenova/transformers": "^2.17.1",
|
||||
"ai": "^2.2.29",
|
||||
"common-tags": "^1.8.2",
|
||||
"config": "*",
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './src/errors'
|
||||
export * from './src/openai'
|
||||
export * from './src/sql'
|
||||
export * from './src/tokenizer'
|
||||
@@ -10,6 +10,6 @@ module.exports = {
|
||||
setupFilesAfterEnv: ['./test/extensions.ts'],
|
||||
testTimeout: 15000,
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(mdast-.*|micromark|micromark-.*|unist-.*|decode-named-character-reference|character-entities)/)',
|
||||
'node_modules/(?!(mdast-.*|micromark|micromark-.*|unist-.*|decode-named-character-reference|character-entities|@xenova/transformers)/)',
|
||||
],
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
"@gregnr/libpg-query": "^13.4.0-dev.12",
|
||||
"@serafin/schema-builder": "^0.18.5",
|
||||
"@supabase/supabase-js": "*",
|
||||
"@xenova/transformers": "^2.17.1",
|
||||
"ai": "^2.2.29",
|
||||
"common-tags": "^1.8.2",
|
||||
"config": "*",
|
||||
|
||||
@@ -32,9 +32,9 @@ exports[`edit add length constraint 1`] = `
|
||||
exports[`generate single table with specified columns 1`] = `
|
||||
"create table employees (
|
||||
id bigint primary key generated always as identity,
|
||||
name text,
|
||||
email text,
|
||||
position text
|
||||
name text not null,
|
||||
email text unique not null,
|
||||
position text not null
|
||||
);"
|
||||
`;
|
||||
|
||||
@@ -46,3 +46,25 @@ exports[`rls chat select policy using table definition 1`] = `
|
||||
"usingExpression": "user_id = auth.uid ()",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sql chat single table with specified columns 1`] = `
|
||||
"create table employees (
|
||||
id bigint primary key generated always as identity,
|
||||
name text not null,
|
||||
email text unique not null,
|
||||
position text not null,
|
||||
created_at timestamp with time zone default current_timestamp,
|
||||
updated_at timestamp with time zone default current_timestamp
|
||||
);"
|
||||
`;
|
||||
|
||||
exports[`sql chat table referencing users creates fk to auth.users 1`] = `
|
||||
"create table todos (
|
||||
id bigint primary key generated always as identity,
|
||||
title text not null,
|
||||
description text,
|
||||
created_at timestamp with time zone default current_timestamp,
|
||||
author_id uuid references auth.users (id),
|
||||
completed boolean default false
|
||||
);"
|
||||
`;
|
||||
@@ -0,0 +1,129 @@
|
||||
import OpenAI from 'openai'
|
||||
|
||||
export const supportedModels = [
|
||||
'llama3',
|
||||
'llama3-70b-8192',
|
||||
'llama3-8b-8192',
|
||||
'gpt-4-turbo',
|
||||
'gpt-4-turbo-preview',
|
||||
'gpt-4-turbo-2024-04-09',
|
||||
'gpt-4-0125-preview',
|
||||
'gpt-4-1106-preview',
|
||||
'gpt-4',
|
||||
'gpt-4-0613',
|
||||
'gpt-4-0314',
|
||||
'gpt-3.5-turbo',
|
||||
'gpt-3.5-turbo-0125',
|
||||
'gpt-3.5-turbo-1106',
|
||||
]
|
||||
|
||||
export const supportedEmbeddingModels = ['text-embedding-ada-002']
|
||||
|
||||
/**
|
||||
* Instantiates OpenAI client while checking and validating
|
||||
* environment variables.
|
||||
*
|
||||
* Allows you to use your own OpenAI-compatible provider.
|
||||
*
|
||||
* Uses the following env vars:
|
||||
* - OPENAI_API_KEY (backwards compatible with previous OPENAI_KEY)
|
||||
* - OPENAI_BASE_URL
|
||||
* - OPENAI_MODEL
|
||||
*/
|
||||
export function createOpenAiClient() {
|
||||
const apiKey = process.env.OPENAI_API_KEY ?? process.env.OPENAI_KEY
|
||||
const baseURL = process.env.OPENAI_BASE_URL
|
||||
const model = process.env.OPENAI_MODEL
|
||||
|
||||
if (!apiKey) {
|
||||
return {
|
||||
error: new Error(
|
||||
'No OPENAI_API_KEY set. Create this environment variable to use AI features.'
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if (!baseURL) {
|
||||
return {
|
||||
error: new Error(
|
||||
'No OPENAI_BASE_URL set. Create this environment variable to use AI features.'
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
return {
|
||||
error: new Error('No OPENAI_MODEL set. Create this environment variable to use AI features.'),
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportedModels.includes(model)) {
|
||||
return {
|
||||
error: new Error(
|
||||
`The following models are supported for OPENAI_MODEL: ${JSON.stringify(supportedModels)}`
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const openai = new OpenAI({ apiKey, baseURL })
|
||||
|
||||
return { openai, model }
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates OpenAI client for embedding models while checking and validating
|
||||
* environment variables.
|
||||
*
|
||||
* Allows you to use separate OpenAI-compatible providers for embedding models vs. language models.
|
||||
*
|
||||
* Uses the following env vars:
|
||||
* - OPENAI_EMBEDDING_API_KEY
|
||||
* - OPENAI_EMBEDDING_BASE_URL
|
||||
* - OPENAI_EMBEDDING_MODEL
|
||||
*
|
||||
* Falls back to these default env vars if the above vars don't exist:
|
||||
* - OPENAI_API_KEY
|
||||
* - OPENAI_BASE_URL
|
||||
*/
|
||||
export function createOpenAiEmbeddingClient() {
|
||||
const apiKey =
|
||||
process.env.OPENAI_EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY ?? process.env.OPENAI_KEY
|
||||
const baseURL = process.env.OPENAI_EMBEDDING_BASE_URL ?? process.env.OPENAI_BASE_URL
|
||||
const model = process.env.OPENAI_EMBEDDING_MODEL
|
||||
|
||||
if (!apiKey) {
|
||||
return {
|
||||
error: new Error(
|
||||
'No OPENAI_EMBEDDING_API_KEY set. Create this environment variable to use AI embedding features.'
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if (!baseURL) {
|
||||
return {
|
||||
error: new Error(
|
||||
'No OPENAI_EMBEDDING_BASE_URL set. Create this environment variable to use AI embedding features.'
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
return {
|
||||
error: new Error(
|
||||
'No OPENAI_EMBEDDING_MODEL set. Create this environment variable to use AI embedding features.'
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportedEmbeddingModels.includes(model)) {
|
||||
return {
|
||||
error: new Error(
|
||||
`The following models are supported for OPENAI_EMBEDDING_MODEL: ${JSON.stringify(supportedEmbeddingModels)}`
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const openai = new OpenAI({ apiKey, baseURL })
|
||||
|
||||
return { openai, model }
|
||||
}
|
||||
@@ -3,13 +3,8 @@ import { OpenAIStream } from 'ai'
|
||||
import { codeBlock, oneLine, stripIndent } from 'common-tags'
|
||||
import OpenAI from 'openai'
|
||||
|
||||
import { ApplicationError, ContextLengthError, UserError } from './errors'
|
||||
import { getChatRequestTokenCount, getMaxTokenCount, tokenizer } from './tokenizer'
|
||||
|
||||
interface Message {
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
}
|
||||
import { ApplicationError, ContextLengthError } from './errors'
|
||||
import { Message, capMessages, countTokens } from './tokenizer'
|
||||
|
||||
/**
|
||||
* Responds to a conversation about building an RLS policy.
|
||||
@@ -18,6 +13,7 @@ interface Message {
|
||||
*/
|
||||
export async function chatRlsPolicy(
|
||||
openai: OpenAI,
|
||||
model: string,
|
||||
messages: Message[],
|
||||
entityDefinitions?: string[],
|
||||
policyDefinition?: string
|
||||
@@ -26,7 +22,7 @@ export async function chatRlsPolicy(
|
||||
{
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You're an Postgres expert in writing row level security policies. Your purpose is to
|
||||
You're a Postgres expert in writing row level security policies. Your purpose is to
|
||||
generate a policy with the constraints given by the user. You will be provided a schema
|
||||
on which the policy should be applied.
|
||||
|
||||
@@ -35,18 +31,18 @@ export async function chatRlsPolicy(
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- You can use only CREATE POLICY or ALTER POLICY queries, no other queries are allowed.
|
||||
- You can add short explanations to your messages.
|
||||
- The result should be a valid markdown. The SQL code should be wrapped in \`\`\`.
|
||||
- The result should be a valid markdown. The SQL code should be wrapped in \`\`\` (including sql language tag).
|
||||
- Always use "auth.uid()" instead of "current_user".
|
||||
- You can't use "USING" expression on INSERT policies.
|
||||
- Only use "WITH CHECK" expression on INSERT or UPDATE policies.
|
||||
- The policy name should be short text explaining the policy, enclosed in double quotes.
|
||||
- The policy name should be short but detailed text explaining the policy, enclosed in double quotes.
|
||||
- Always put explanations as separate text. Never use inline SQL comments.
|
||||
- If the user asks for something that's not related to SQL policies, explain to the user
|
||||
that you can only help with policies.
|
||||
|
||||
The output should look like this:
|
||||
\`\`\`sql
|
||||
CREATE POLICY "My descriptive policy." ON users FOR INSERT USING (user_name = current_user) WITH (true);
|
||||
CREATE POLICY "My descriptive policy." ON books FOR INSERT USING (author_id = auth.uid()) WITH (true);
|
||||
\`\`\`
|
||||
`,
|
||||
},
|
||||
@@ -77,7 +73,7 @@ export async function chatRlsPolicy(
|
||||
|
||||
try {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: initMessages,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
@@ -101,24 +97,37 @@ export async function chatRlsPolicy(
|
||||
*/
|
||||
export async function generateV2(
|
||||
openai: OpenAI,
|
||||
model: string,
|
||||
messages: Message[],
|
||||
existingSql?: string,
|
||||
entityDefinitions?: string[]
|
||||
): Promise<ReadableStream<Uint8Array>> {
|
||||
const initMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
|
||||
const initMessages: Message[] = [
|
||||
{
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You are a Supabase Postgres SQL generator. The user will have a conversation with you and your job is to generate SQL
|
||||
in the Postgres dialect. Be friendly and relatable, complementing the user when appropriate, but don't go overboard.
|
||||
|
||||
The generated SQL (must be valid SQL).
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- Prefer creating foreign key references in the create statement
|
||||
- When creating new tables, always add foreign key references inline (ie. "my_column my_type references ..." on a single line)
|
||||
- Include all necessary tables and create them in the correct order (eg. book references author, so create author before book)
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Always use semicolons
|
||||
- There is a built-in auth.users table with a uuid id column (but it's immutable)
|
||||
- Prefer referencing auth.users directly via FK instead of creating a new users table
|
||||
- Only create your own users table if you need to store additional data about the user such as avatar
|
||||
- If you do create your own users table, prefer to call it "profiles" and add a FK to auth.users. You must also create a trigger function
|
||||
that automatically creates the profile when there is a new auth.user
|
||||
- Sprinkle in detailed explanations throughout your answer, digging into "why" each snippet is necessary
|
||||
- Explanations should live outside of code blocks
|
||||
- Output as markdown
|
||||
- Always include code snippets if available
|
||||
- Always include code snippets if available (including sql language tag, ie. \`\`\`sql\n...\n\`\`\`)
|
||||
`,
|
||||
},
|
||||
]
|
||||
@@ -148,7 +157,7 @@ export async function generateV2(
|
||||
|
||||
try {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: initMessages,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
@@ -167,6 +176,9 @@ export async function generateV2(
|
||||
|
||||
export async function clippy(
|
||||
openai: OpenAI,
|
||||
openaiEmbedding: OpenAI,
|
||||
model: string,
|
||||
embeddingModel: string,
|
||||
supabaseClient: SupabaseClient<any, 'public', any>,
|
||||
messages: Message[]
|
||||
) {
|
||||
@@ -188,25 +200,9 @@ export async function clippy(
|
||||
throw new Error("No message with role 'user'")
|
||||
}
|
||||
|
||||
// Moderate the content to comply with OpenAI T&C
|
||||
const moderationResponses = await Promise.all(
|
||||
contextMessages.map((message) => openai.moderations.create({ input: message.content }))
|
||||
)
|
||||
|
||||
for (const moderationResponse of moderationResponses) {
|
||||
const [results] = moderationResponse.results
|
||||
|
||||
if (results.flagged) {
|
||||
throw new UserError('Flagged content', {
|
||||
flagged: true,
|
||||
categories: results.categories,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const embeddingResponse = await openai.embeddings
|
||||
const embeddingResponse = await openaiEmbedding.embeddings
|
||||
.create({
|
||||
model: 'text-embedding-ada-002',
|
||||
model: embeddingModel,
|
||||
input: userMessage.content.replaceAll('\n', ' '),
|
||||
})
|
||||
.catch((error: any) => {
|
||||
@@ -235,8 +231,7 @@ export async function clippy(
|
||||
for (let i = 0; i < pageSections.length; i++) {
|
||||
const pageSection = pageSections[i]
|
||||
const content = pageSection.content
|
||||
const encoded = tokenizer.encode(content)
|
||||
tokenCount += encoded.length
|
||||
tokenCount += await countTokens(content, model)
|
||||
|
||||
if (tokenCount >= 1500) {
|
||||
break
|
||||
@@ -245,7 +240,7 @@ export async function clippy(
|
||||
contextText += `${content.trim()}\n---\n`
|
||||
}
|
||||
|
||||
const initMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
|
||||
const initMessages: Message[] = [
|
||||
{
|
||||
role: 'system',
|
||||
content: codeBlock`
|
||||
@@ -307,20 +302,19 @@ export async function clippy(
|
||||
},
|
||||
]
|
||||
|
||||
const model = 'gpt-3.5-turbo-0301'
|
||||
const maxCompletionTokenCount = 1024
|
||||
const maxCompletionTokens = 1024
|
||||
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = capMessages(
|
||||
const completionMessages: Message[] = await capMessages(
|
||||
initMessages,
|
||||
contextMessages,
|
||||
maxCompletionTokenCount,
|
||||
model
|
||||
model,
|
||||
maxCompletionTokens
|
||||
)
|
||||
|
||||
const completionOptions = {
|
||||
model,
|
||||
messages: completionMessages,
|
||||
max_tokens: 1024,
|
||||
max_tokens: maxCompletionTokens,
|
||||
temperature: 0,
|
||||
stream: true,
|
||||
}
|
||||
@@ -342,32 +336,3 @@ export async function clippy(
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove context messages until the entire request fits
|
||||
* the max total token count for that model.
|
||||
*
|
||||
* Accounts for both message and completion token counts.
|
||||
*/
|
||||
function capMessages(
|
||||
initMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
contextMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
maxCompletionTokenCount: number,
|
||||
model: string
|
||||
) {
|
||||
const maxTotalTokenCount = getMaxTokenCount(model)
|
||||
const cappedContextMessages = [...contextMessages]
|
||||
let tokenCount =
|
||||
getChatRequestTokenCount([...initMessages, ...cappedContextMessages], model) +
|
||||
maxCompletionTokenCount
|
||||
|
||||
// Remove earlier context messages until we fit
|
||||
while (tokenCount >= maxTotalTokenCount) {
|
||||
cappedContextMessages.shift()
|
||||
tokenCount =
|
||||
getChatRequestTokenCount([...initMessages, ...cappedContextMessages], model) +
|
||||
maxCompletionTokenCount
|
||||
}
|
||||
|
||||
return [...initMessages, ...cappedContextMessages]
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
import { describe, expect, test } from '@jest/globals'
|
||||
import { codeBlock } from 'common-tags'
|
||||
import OpenAI from 'openai'
|
||||
import { collectStream, extractMarkdownSql, formatSql, getPolicyInfo } from '../test/util'
|
||||
import { createOpenAiClient } from './openai'
|
||||
import { debugSql, editSql, generateSql, titleSql } from './sql'
|
||||
import { chatRlsPolicy } from './sql.edge'
|
||||
import { chatRlsPolicy, generateV2 } from './sql.edge'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
console.info(`Running AI tests against model '${model}'`)
|
||||
|
||||
describe('generate', () => {
|
||||
test('single table with specified columns', async () => {
|
||||
const { sql, title } = await generateSql(
|
||||
openai,
|
||||
model,
|
||||
'create a table to track employees with name, email, and position'
|
||||
)
|
||||
|
||||
@@ -24,6 +30,7 @@ describe('edit', () => {
|
||||
test('add length constraint', async () => {
|
||||
const { sql } = await editSql(
|
||||
openai,
|
||||
model,
|
||||
'force name to be at least 4 characters',
|
||||
codeBlock`
|
||||
create table employees (
|
||||
@@ -42,6 +49,7 @@ describe('debug', () => {
|
||||
test('fix order of operations', async () => {
|
||||
const { sql } = await debugSql(
|
||||
openai,
|
||||
model,
|
||||
'relation "departments" does not exist',
|
||||
codeBlock`
|
||||
create table employees (
|
||||
@@ -64,6 +72,7 @@ describe('debug', () => {
|
||||
test('fix typos', async () => {
|
||||
const { sql, solution } = await debugSql(
|
||||
openai,
|
||||
model,
|
||||
'syntax error at or near "fromm"',
|
||||
codeBlock`
|
||||
select * fromm employees;
|
||||
@@ -79,6 +88,7 @@ describe('title', () => {
|
||||
test('title matches content', async () => {
|
||||
const { title, description } = await titleSql(
|
||||
openai,
|
||||
model,
|
||||
codeBlock`
|
||||
create table employees (
|
||||
id bigint primary key generated always as identity,
|
||||
@@ -103,6 +113,7 @@ describe('rls chat', () => {
|
||||
test('select policy using table definition', async () => {
|
||||
const responseStream = await chatRlsPolicy(
|
||||
openai,
|
||||
model,
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
@@ -131,3 +142,32 @@ describe('rls chat', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sql chat', () => {
|
||||
test('single table with specified columns', async () => {
|
||||
const responseStream = await generateV2(openai, model, [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'create a table to track employees with name, email, and position',
|
||||
},
|
||||
])
|
||||
const responseText = await collectStream(responseStream)
|
||||
const [sql] = extractMarkdownSql(responseText)
|
||||
|
||||
expect(sql).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('table referencing users creates fk to auth.users', async () => {
|
||||
const responseStream = await generateV2(openai, model, [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'create a todos table and track author',
|
||||
},
|
||||
])
|
||||
|
||||
const responseText = await collectStream(responseStream)
|
||||
const [sql] = extractMarkdownSql(responseText)
|
||||
|
||||
expect(sql).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
@@ -9,13 +9,6 @@ const generateSqlSchema = SchemaBuilder.emptySchema()
|
||||
.addString('sql', {
|
||||
description: stripIndent`
|
||||
The generated SQL (must be valid SQL).
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- Prefer creating foreign key references in the create statement
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Always use semicolons
|
||||
`,
|
||||
})
|
||||
.addString('title', {
|
||||
@@ -28,16 +21,6 @@ const generateSqlSchema = SchemaBuilder.emptySchema()
|
||||
const editSqlSchema = SchemaBuilder.emptySchema().addString('sql', {
|
||||
description: stripIndent`
|
||||
The modified SQL (must be valid SQL).
|
||||
- Assume the query hasn't been executed yet
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- When creating tables, always add foreign key references inline
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Use real examples when possible
|
||||
- Add constraints if requested
|
||||
- Always use semicolons
|
||||
`,
|
||||
})
|
||||
|
||||
@@ -83,28 +66,12 @@ const completionFunctions = {
|
||||
},
|
||||
debugSql: {
|
||||
name: 'debugSql',
|
||||
description: stripIndent`
|
||||
Debugs a Postgres SQL error. Returns the fixed SQL and a solution explaining it.
|
||||
- Create extensions if they are missing (only for valid extensions)
|
||||
- Suggest creating tables if they are missing
|
||||
- Include all of the original SQL
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- When creating tables, always add foreign key references inline
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Always use semicolons
|
||||
`,
|
||||
description: 'Debugs a Postgres SQL error. Returns the fixed SQL and a solution explaining it.',
|
||||
parameters: debugSqlSchema.schema as Record<string, unknown>,
|
||||
},
|
||||
generateTitle: {
|
||||
name: 'generateTitle',
|
||||
description: stripIndent`
|
||||
Generates a short title and summarized description for a Postgres SQL snippet.
|
||||
|
||||
The description should describe why this table was created (eg. "Table to track todos")
|
||||
`,
|
||||
description: 'Generates a short title and summarized description for a Postgres SQL snippet.',
|
||||
parameters: generateTitleSchema.schema as Record<string, unknown>,
|
||||
},
|
||||
} satisfies Record<string, OpenAI.Chat.Completions.ChatCompletionCreateParams.Function>
|
||||
@@ -114,11 +81,36 @@ const completionFunctions = {
|
||||
*
|
||||
* @returns The generated SQL along with a title for it.
|
||||
*/
|
||||
export async function generateSql(openai: OpenAI, prompt: string, entityDefinitions?: string[]) {
|
||||
export async function generateSql(
|
||||
openai: OpenAI,
|
||||
model: string,
|
||||
prompt: string,
|
||||
entityDefinitions?: string[]
|
||||
) {
|
||||
const hasEntityDefinitions = entityDefinitions !== undefined && entityDefinitions.length > 0
|
||||
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
|
||||
|
||||
completionMessages.push({
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You are a Postgres SQL generator. You must generate Postgres SQL based on a natural language prompt
|
||||
|
||||
The generated SQL (must be valid SQL).
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- Prefer creating foreign key references in the create statement
|
||||
- When creating new tables, always add foreign key references inline (ie. "my_column my_type references ..." on a single line)
|
||||
- Include all necessary tables and create them in the correct order (eg. book references author, so create author before book)
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Always use semicolons
|
||||
|
||||
Response must contain only JSON satisfying the 'tool_calls'.
|
||||
`,
|
||||
})
|
||||
|
||||
if (hasEntityDefinitions) {
|
||||
completionMessages.push({
|
||||
role: 'user',
|
||||
@@ -136,7 +128,7 @@ export async function generateSql(openai: OpenAI, prompt: string, entityDefiniti
|
||||
|
||||
try {
|
||||
const completionResponse = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: completionMessages,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
@@ -188,6 +180,7 @@ export async function generateSql(openai: OpenAI, prompt: string, entityDefiniti
|
||||
*/
|
||||
export async function editSql(
|
||||
openai: OpenAI,
|
||||
model: string,
|
||||
prompt: string,
|
||||
sql: string,
|
||||
entityDefinitions?: string[]
|
||||
@@ -196,6 +189,27 @@ export async function editSql(
|
||||
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
|
||||
|
||||
completionMessages.push({
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You are a Postgres SQL editor. You must edit Postgres SQL based on the user's instructions.
|
||||
|
||||
- Assume the query hasn't been executed yet
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- When creating new tables, always add foreign key references inline (ie. "my_column my_type references ..." on a single line)
|
||||
- Include all necessary tables and create them in the correct order (eg. book references author, so create author before book)
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Use real examples when possible
|
||||
- Add constraints if requested
|
||||
- Always use semicolons
|
||||
|
||||
Response must contain only JSON satisfying the 'tool_calls'.
|
||||
`,
|
||||
})
|
||||
|
||||
if (hasEntityDefinitions) {
|
||||
completionMessages.push({
|
||||
role: 'user',
|
||||
@@ -222,7 +236,7 @@ export async function editSql(
|
||||
|
||||
try {
|
||||
const completionResponse = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: completionMessages,
|
||||
max_tokens: 2048,
|
||||
temperature: 0,
|
||||
@@ -274,6 +288,7 @@ export async function editSql(
|
||||
*/
|
||||
export async function debugSql(
|
||||
openai: OpenAI,
|
||||
model: string,
|
||||
errorMessage: string,
|
||||
sql: string,
|
||||
entityDefinitions?: string[]
|
||||
@@ -282,6 +297,25 @@ export async function debugSql(
|
||||
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
|
||||
|
||||
completionMessages.push({
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You are a Postgres SQL debugger. You must fix the user's SQL and a provide solution explaining it.
|
||||
|
||||
- Create extensions if they are missing (only for valid extensions)
|
||||
- Suggest creating tables if they are missing
|
||||
- Include all of the original SQL
|
||||
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
|
||||
- When creating tables, always add foreign key references inline
|
||||
- Prefer 'text' over 'varchar'
|
||||
- Prefer 'timestamp with time zone' over 'date'
|
||||
- Use vector(384) data type for any embedding/vector related query
|
||||
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
|
||||
- Always use semicolons
|
||||
- Do not mix up the user's error with "tool use failed"
|
||||
`,
|
||||
})
|
||||
|
||||
if (hasEntityDefinitions) {
|
||||
completionMessages.push({
|
||||
role: 'user',
|
||||
@@ -311,7 +345,7 @@ export async function debugSql(
|
||||
|
||||
try {
|
||||
const completionResponse = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: completionMessages,
|
||||
max_tokens: 2048,
|
||||
temperature: 0,
|
||||
@@ -361,17 +395,29 @@ export async function debugSql(
|
||||
*
|
||||
* @returns A title and description for the SQL snippet.
|
||||
*/
|
||||
export async function titleSql(openai: OpenAI, sql: string) {
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
|
||||
{
|
||||
role: 'user',
|
||||
content: sql,
|
||||
},
|
||||
]
|
||||
export async function titleSql(openai: OpenAI, model: string, sql: string) {
|
||||
const completionMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = []
|
||||
|
||||
completionMessages.push({
|
||||
role: 'system',
|
||||
content: stripIndent`
|
||||
You are a Postgres SQL title/description generator.
|
||||
You must generates a short title and summarized description for a Postgres SQL snippet.
|
||||
|
||||
- The title should be short but still address all parts of the code.
|
||||
- The description should describe why this table was created (eg. "Table to track todos")
|
||||
while addressing all parts of the code.
|
||||
`,
|
||||
})
|
||||
|
||||
completionMessages.push({
|
||||
role: 'user',
|
||||
content: sql,
|
||||
})
|
||||
|
||||
try {
|
||||
const completionResponse = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo-0125',
|
||||
model,
|
||||
messages: completionMessages,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, test } from '@jest/globals'
|
||||
import { Message, tokenizeChat } from './tokenizer'
|
||||
|
||||
const sampleMessages: Message[] = [
|
||||
{ role: 'system', content: 'You are a helpful Postgres SQL assistant' },
|
||||
{ role: 'user', content: 'Help me write SQL to get all movies that start with T' },
|
||||
{
|
||||
role: 'assistant',
|
||||
content: "Sure!\n```sql\nselect * from movies where title like 'T%';\n```",
|
||||
},
|
||||
{ role: 'user', content: 'Thanks! What about S?' },
|
||||
]
|
||||
|
||||
describe('tokenizer', () => {
|
||||
test('counts llama3 tokens correctly', async () => {
|
||||
const tokens = await tokenizeChat(sampleMessages, 'llama3')
|
||||
expect(tokens.length).toBe(68)
|
||||
})
|
||||
|
||||
test('counts gpt-3.5-turbo tokens correctly', async () => {
|
||||
const tokens = await tokenizeChat(sampleMessages, 'gpt-3.5-turbo')
|
||||
expect(tokens.length).toBe(66)
|
||||
})
|
||||
})
|
||||
@@ -1,88 +1,191 @@
|
||||
import { getEncoding } from 'js-tiktoken'
|
||||
import OpenAI from 'openai'
|
||||
import { AutoTokenizer, PreTrainedTokenizer } from '@xenova/transformers'
|
||||
|
||||
export const tokenizer = getEncoding('cl100k_base')
|
||||
export interface Message {
|
||||
role: 'system' | 'user' | 'assistant'
|
||||
content: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the tokens for multi-message chat completion requests
|
||||
*/
|
||||
export function getChatRequestTokenCount(
|
||||
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
model = 'gpt-3.5-turbo-0301'
|
||||
): number {
|
||||
const tokensPerRequest = 3 // every reply is primed with <|im_start|>assistant<|im_sep|>
|
||||
const numTokens = messages.reduce((acc, message) => acc + getMessageTokenCount(message, model), 0)
|
||||
|
||||
return numTokens + tokensPerRequest
|
||||
function isNumberArray(value: any): value is number[] {
|
||||
return Array.isArray(value) && typeof value[0] === 'number'
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the tokens for a single message within a chat completion request
|
||||
*
|
||||
* See "Counting tokens for chat API calls"
|
||||
* from https://github.com/openai/openai-cookbook/blob/834181d5739740eb8380096dac7056c925578d9a/examples/How_to_count_tokens_with_tiktoken.ipynb
|
||||
* Gets the Hugging Face model path for the given model.
|
||||
*/
|
||||
export function getMessageTokenCount(
|
||||
message: OpenAI.Chat.Completions.ChatCompletionMessageParam,
|
||||
model = 'gpt-3.5-turbo-0301'
|
||||
): number {
|
||||
let tokensPerMessage: number
|
||||
let tokensPerName: number
|
||||
|
||||
function getModelPath(model: string) {
|
||||
switch (model) {
|
||||
case 'gpt-3.5-turbo':
|
||||
console.warn(
|
||||
'Warning: gpt-3.5-turbo may change over time. Returning num tokens assuming gpt-3.5-turbo-0301.'
|
||||
)
|
||||
return getMessageTokenCount(message, 'gpt-3.5-turbo-0301')
|
||||
case 'llama3':
|
||||
case 'llama3-70b-8192':
|
||||
case 'llama3-8b-8192':
|
||||
return 'Xenova/llama-3-tokenizer'
|
||||
case 'gpt-4-turbo':
|
||||
case 'gpt-4-turbo-preview':
|
||||
case 'gpt-4-turbo-2024-04-09':
|
||||
case 'gpt-4-0125-preview':
|
||||
case 'gpt-4-1106-preview':
|
||||
case 'gpt-4-vision-preview':
|
||||
case 'gpt-4-1106-vision-preview':
|
||||
case 'gpt-4-32k':
|
||||
case 'gpt-4-32k-0613':
|
||||
case 'gpt-4':
|
||||
console.warn('Warning: gpt-4 may change over time. Returning num tokens assuming gpt-4-0314.')
|
||||
return getMessageTokenCount(message, 'gpt-4-0314')
|
||||
case 'gpt-3.5-turbo-0301':
|
||||
tokensPerMessage = 4 // every message follows <|start|>{role/name}\n{content}<|end|>\n
|
||||
tokensPerName = -1 // if there's a name, the role is omitted
|
||||
break
|
||||
case 'gpt-4-0613':
|
||||
case 'gpt-4-0314':
|
||||
tokensPerMessage = 3
|
||||
tokensPerName = 1
|
||||
break
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown model '${model}'. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.`
|
||||
)
|
||||
}
|
||||
|
||||
return Object.entries(message).reduce((acc, [key, value]) => {
|
||||
acc += tokenizer.encode(value).length
|
||||
if (key === 'name') {
|
||||
acc += tokensPerName
|
||||
}
|
||||
return acc
|
||||
}, tokensPerMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of tokens for a model's context.
|
||||
*
|
||||
* Includes tokens in both message and completion.
|
||||
*/
|
||||
export function getMaxTokenCount(model: string): number {
|
||||
switch (model) {
|
||||
case 'gpt-3.5-turbo':
|
||||
console.warn(
|
||||
'Warning: gpt-3.5-turbo may change over time. Returning max num tokens assuming gpt-3.5-turbo-0301.'
|
||||
)
|
||||
return getMaxTokenCount('gpt-3.5-turbo-0301')
|
||||
case 'gpt-4':
|
||||
console.warn(
|
||||
'Warning: gpt-4 may change over time. Returning max num tokens assuming gpt-4-0314.'
|
||||
)
|
||||
return getMaxTokenCount('gpt-4-0314')
|
||||
case 'gpt-3.5-turbo-0125':
|
||||
case 'gpt-3.5-turbo-1106':
|
||||
case 'gpt-3.5-turbo-16k':
|
||||
case 'gpt-3.5-turbo-16k-0613':
|
||||
case 'gpt-3.5-turbo-instruct':
|
||||
case 'gpt-3.5-turbo-0613':
|
||||
case 'gpt-3.5-turbo-0301':
|
||||
return 4097
|
||||
case 'gpt-4-0314':
|
||||
return 4097
|
||||
return 'Xenova/gpt-4'
|
||||
default:
|
||||
throw new Error(`Unknown model '${model}'`)
|
||||
}
|
||||
}
|
||||
|
||||
const tokenizers = new Map<string, PreTrainedTokenizer>()
|
||||
|
||||
/**
|
||||
* Creates a tokenizer for the specified model using
|
||||
* Transformers.js and config downloaded from Hugging Face.
|
||||
*
|
||||
* Caches the tokenizer in memory for subsequent uses.
|
||||
*/
|
||||
async function getTokenizer(model: string) {
|
||||
const cachedTokenizer = tokenizers.get(model)
|
||||
|
||||
if (cachedTokenizer) {
|
||||
return cachedTokenizer
|
||||
}
|
||||
|
||||
const modelPath = getModelPath(model)
|
||||
const tokenizer = await AutoTokenizer.from_pretrained(modelPath)
|
||||
|
||||
tokenizers.set(model, tokenizer)
|
||||
|
||||
return tokenizer
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes plain text for the specified model.
|
||||
*/
|
||||
export async function tokenize(content: string, model: string) {
|
||||
const tokenizer = await getTokenizer(model)
|
||||
|
||||
const tokens = tokenizer.encode(content)
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes chat messages for the specified model.
|
||||
*
|
||||
* Includes delimiter tokens like <|im_start|>, <|im_end|>, etc
|
||||
*/
|
||||
export async function tokenizeChat(messages: Message[], model: string) {
|
||||
const tokenizer = await getTokenizer(model)
|
||||
|
||||
const tokens = tokenizer.apply_chat_template(messages, {
|
||||
tokenize: true,
|
||||
return_tensor: false,
|
||||
add_generation_prompt: true,
|
||||
})
|
||||
|
||||
if (!isNumberArray(tokens)) {
|
||||
throw new Error(`Unexpected tokenizer output. Expected number array.`)
|
||||
}
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of tokens in plain text for the specified model.
|
||||
*/
|
||||
export async function countTokens(content: string, model: string) {
|
||||
const tokens = await tokenize(content, model)
|
||||
|
||||
return tokens.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of tokens in the chat messages for the specified model.
|
||||
*
|
||||
* Accounts for delimiter tokens like <|im_start|>, <|im_end|>, etc
|
||||
*/
|
||||
export async function countChatTokens(messages: Message[], model: string) {
|
||||
const tokens = await tokenizeChat(messages, model)
|
||||
|
||||
return tokens.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of tokens in a model's context window.
|
||||
*
|
||||
* Defaults to 4096 for unknown models.
|
||||
*/
|
||||
export function getContextWindow(model: string): number {
|
||||
switch (model) {
|
||||
case 'gpt-4-turbo':
|
||||
case 'gpt-4-turbo-preview':
|
||||
case 'gpt-4-turbo-2024-04-09':
|
||||
case 'gpt-4-0125-preview':
|
||||
case 'gpt-4-1106-preview':
|
||||
case 'gpt-4-vision-preview':
|
||||
case 'gpt-4-1106-vision-preview':
|
||||
return 128000
|
||||
case 'gpt-4-32k':
|
||||
case 'gpt-4-32k-0613':
|
||||
return 32768
|
||||
case 'gpt-3.5-turbo':
|
||||
case 'gpt-3.5-turbo-0125':
|
||||
case 'gpt-3.5-turbo-1106':
|
||||
case 'gpt-3.5-turbo-16k':
|
||||
case 'gpt-3.5-turbo-16k-0613':
|
||||
return 16385
|
||||
case 'gpt-4':
|
||||
case 'gpt-4-0613':
|
||||
case 'llama3':
|
||||
case 'llama3-70b-8192':
|
||||
case 'llama3-8b-8192':
|
||||
return 8192
|
||||
case 'gpt-4-0314':
|
||||
case 'gpt-3.5-turbo-instruct':
|
||||
case 'gpt-3.5-turbo-0613':
|
||||
case 'gpt-3.5-turbo-0301':
|
||||
default:
|
||||
return 4096
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove conversation messages as needed until the entire
|
||||
* request fits the context window of the specified model.
|
||||
*
|
||||
* @param systemMessages The initial system messages that must always exist
|
||||
* @param conversationMessages The conversation messages that can be trimmed if necessary
|
||||
* @param model The model that will be responding to these messages
|
||||
* @param maxCompletionTokens The number of response tokens to leave room for
|
||||
*
|
||||
* @returns The final set of messages that will fit in the context window of the model.
|
||||
*/
|
||||
export async function capMessages(
|
||||
systemMessages: Message[],
|
||||
conversationMessages: Message[],
|
||||
model: string,
|
||||
maxCompletionTokens = 0
|
||||
) {
|
||||
const contextWindow = getContextWindow(model)
|
||||
const trimmedConversationMessages = [...conversationMessages]
|
||||
|
||||
let tokenCount = await countChatTokens([...systemMessages, ...conversationMessages], model)
|
||||
|
||||
// Remove earlier conversation messages until we fit
|
||||
while (tokenCount + maxCompletionTokens >= contextWindow) {
|
||||
trimmedConversationMessages.shift()
|
||||
|
||||
tokenCount = await countChatTokens([...systemMessages, ...conversationMessages], model)
|
||||
}
|
||||
|
||||
return [...systemMessages, ...trimmedConversationMessages]
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
import { expect } from '@jest/globals'
|
||||
import { codeBlock } from 'common-tags'
|
||||
import OpenAI from 'openai'
|
||||
import { createOpenAiClient } from '../src/openai'
|
||||
|
||||
const openAiKey = process.env.OPENAI_KEY
|
||||
const openai = new OpenAI({ apiKey: openAiKey })
|
||||
const { openai, model, error } = createOpenAiClient()
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
expect.extend({
|
||||
async toMatchCriteria(received: string, criteria: string) {
|
||||
const model = 'gpt-4-1106-preview'
|
||||
|
||||
const completionResponse = await openai.chat.completions.create({
|
||||
model,
|
||||
messages: [
|
||||
@@ -21,7 +22,9 @@ expect.extend({
|
||||
- \`{ "pass": true, "reason": "<reason>" }\` if 'Received' adheres to the test 'Criteria'
|
||||
- \`{ "pass": false, "reason": "<reason>" }\` if 'Received' does not adhere to the test 'Criteria'
|
||||
|
||||
Be critical and make sure every part of 'Criteria' is addressed in 'Received'.
|
||||
The "reason" must explain exactly which part of 'Received' did or did not pass the test 'Criteria'.
|
||||
Be detailed as to why you failed the test.
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user