diff --git a/cypress/e2e/public-api.cy.ts b/cypress/e2e/public-api.cy.ts index 5581cae..8bddc56 100644 --- a/cypress/e2e/public-api.cy.ts +++ b/cypress/e2e/public-api.cy.ts @@ -1,252 +1,3 @@ -import { Chat } from 'openai/resources'; -import { supabaseExecute } from '../../src/lib/public-api/database'; -import { - DocumentsResponse, - CreateIndexResponse, - GetIndexesResponse, - ChatCompletionResponse, - RagCompletionResponse, - SimilarDocumentsResponse, -} from '../../src/lib/public-api/validation'; -import { Project, Index } from '../../src/types/supabase-entities'; - -let state = { - project: null as Project | null, - index: null as Index | null, -}; - -const routes = { - getIndexes: '/api/indexes', - searchIndex: '/api/indexes/search', - postDocuments: '/api/documents', - getDocuments: '/api/documents', - searchDocuments: '/api/documents/search', - chat: '/api/chat/completions', - rag: '/api/chat/rag', -}; - context('Test Public API', () => { before(async () => {}); - - it('gets the project to be tested from database', () => { - const query = `select * from projects where name = 'Test project'`; - cy.task('supabaseExecute', query).then(({ data, error }: any) => { - expect(data[0].name).to.eq('Test project'); - state.project = data[0]; - }); - }); - - context(`POST ${routes.getIndexes}`, () => { - it('should fail when there are no correct headers', () => { - cy.request({ - method: 'POST', - url: routes.getIndexes, - failOnStatusCode: false, - }).then((response) => { - expect(response.status).to.eq(401); - }); - }); - - it('should create a new index', () => { - const name = `Test index ${new Date().getTime()}`; - cy.request({ - method: 'POST', - url: routes.getIndexes, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: { - name, - }, - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.name).to.eq(name); - CreateIndexResponse.parse(response.body); - }); - }); - }); - - context(`GET ${routes.getIndexes}`, () => { - it('should fail when there are no correct headers', () => { - cy.request({ - method: 'GET', - url: routes.getIndexes, - failOnStatusCode: false, - }).then((response) => { - expect(response.status).to.eq(401); - }); - }); - - it('should return the indexes', () => { - cy.request({ - method: 'GET', - url: routes.getIndexes, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.length).to.greaterThan(0); - GetIndexesResponse.parse(response.body); - state.index = response.body[0]; - }); - }); - }); - - context(`POST ${routes.postDocuments}`, () => { - it('should load documents to an index along with metadata ', () => { - cy.request({ - method: 'POST', - url: `${routes.postDocuments}?index_id=${state.index?.id}`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: [ - { - content: 'The name of the company is SquareDev.', - source: 'the founders', - metadata: { string: 'string' }, - }, - ], - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.length).to.greaterThan(0); - DocumentsResponse.parse(response.body); - }); - }); - - it('should fail when there is no content in the document', () => { - cy.request({ - failOnStatusCode: false, - method: 'POST', - url: `${routes.postDocuments}?index_id=${state.index?.id}`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: [ - { - source: 'string', - metadata: { string: 'string' }, - }, - ], - }).then((response) => { - // TODO: This should be 400 - expect(response.status).to.eq(500); - }); - }); - }); - - context(`POST ${routes.postDocuments}/upload`, () => { - it.skip('json', () => {}); - it.skip('pdf', () => {}); - }); - - context(`POST ${routes.postDocuments}/web`, () => {}); - - context(`GET ${routes.getDocuments}`, () => { - it('should get the documents of an index', () => { - cy.request({ - method: 'GET', - url: `${routes.postDocuments}?index_id=${state.index?.id}`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.length).to.greaterThan(0); - DocumentsResponse.parse(response.body); - }); - }); - - it.skip('return the correct error for wrong indexes', () => {}); - }); - - context(`GET ${routes.searchIndex}`, () => { - it('should get the documents of an index that are contextually similar to the search term', () => { - cy.request({ - method: 'GET', - url: `${routes.searchIndex}?index_id=${state.index?.id}&search=A random`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.length).to.greaterThan(0); - SimilarDocumentsResponse.parse(response.body); - }); - }); - - it.skip('return the correct error for wrong indexes', () => {}); - it.skip('return the correct error for no search term', () => {}); - }); - - context(`POST /chat/completions`, () => { - it('should chat with the LLM, only completing the requested prompt', () => { - cy.request({ - method: 'POST', - url: `/api/chat/completions?model=gpt-3.5-turbo`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: { - model: 'gpt-3.5-turbo', - messages: { - system: 'Hello, how are you?', - user: 'I am fine, how are you?', - }, - }, - }).then((response) => { - expect(response.status).to.eq(200); - ChatCompletionResponse.parse(response.body); - }); - }); - }); - - context(`POST ${routes.rag}`, () => { - it('chats with RAG', () => { - cy.request({ - method: 'POST', - url: `${routes.rag}?model=gpt-3.5-turbo`, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: { - model: 'gpt-3.5-turbo', - messages: { - system: - 'You are a helpful assistant. Try to answer the question using the given context.', - user: 'Context: {context} \nQuestion: What is the name of the company?', - }, - indexId: state.index?.id, - }, - }).then((response) => { - expect(response.status).to.eq(200); - expect(response.body.message).to.include('SquareDev'); - console.log(response.body); - RagCompletionResponse.parse(response.body); - }); - }); - - it('returns an error if no {context} placeholder', () => { - cy.request({ - method: 'POST', - url: `${routes.rag}`, - failOnStatusCode: false, - headers: { - authentication: `Bearer ${state.project?.api_key}`, - }, - body: { - model: 'gpt-3.5-turbo', - messages: { - system: - 'You are a helpful assistant. Try to answer the question using the given context.', - user: 'Context: no context placeholder \nQuestion: What is the name of the company?', - }, - indexId: state.index?.id, - }, - }).then((response) => { - expect(response.status).to.eq(400); - }); - }); - }); }); diff --git a/package.json b/package.json index ed830d2..d9bce6e 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,6 @@ "build": "next build", "start": "next start", "lint": "next lint", - "reset": "npx supabase db reset", - "diff": "npx supabase db diff -f feature", - "migration": "npx supabase migration new feature", - "update-types": "npx supabase gen types typescript --local > src/types/supabase.ts", "update-db": "npm run diff && npm run reset && npm run update-types", "prepare": "husky install", "pretty-staged": "pretty-quick --staged", @@ -28,7 +24,6 @@ "@radix-ui/react-dropdown-menu": "2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-slot": "^1.0.2", - "@supabase/auth-helpers-nextjs": "^0.8.1", "@vercel/analytics": "^1.1.1", "bcrypt": "^5.1.1", "class-variance-authority": "^0.7.0", @@ -69,7 +64,6 @@ "husky": "^8.0.3", "postcss": "^8.4.14", "pretty-quick": "^3.1.3", - "supabase": "^1.99.5", "tailwindcss": "^3.3.3", "typescript": "^5.2.2" } diff --git a/src/app/api/chat/completions/route.ts b/src/app/api/chat/completions/route.ts deleted file mode 100644 index 7550920..0000000 --- a/src/app/api/chat/completions/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import { authApiKey } from '@/lib/public-api/auth'; -import { AVAILABLE_MODELS, llm } from '@/lib/public-api/llm'; -import { ChatCompletionRequestType } from '@/lib/public-api/validation'; - -// Add documents to a knowledge base -export async function POST(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const { messages, model }: ChatCompletionRequestType = await request.json(); - - if (!AVAILABLE_MODELS.includes(model)) { - return NextResponse.json( - { error: `Model ${model} not found.` }, - { status: 400 } - ); - } - - const response = await llm({ - message: messages.user, - system: messages.system, - model, - }); - - return NextResponse.json({ - message: response.choices[0].message.content, - model: response.model, - }); -} diff --git a/src/app/api/chat/rag/route.ts b/src/app/api/chat/rag/route.ts deleted file mode 100644 index a3d8b44..0000000 --- a/src/app/api/chat/rag/route.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import { authApiKey } from '@/lib/public-api/auth'; -import { OpenAI } from 'openai'; -import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; -import { supabaseExecute } from '@/lib/public-api/database'; -import { Document } from '@/types/supabase-entities'; -import { AVAILABLE_MODELS, llm } from '@/lib/public-api/llm'; -import { RagCompletionRequestType } from '@/lib/public-api/validation'; - -// Add documents to a knowledge base -export async function POST(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const { messages, model, indexId }: RagCompletionRequestType = - await request.json(); - - if (!AVAILABLE_MODELS.includes(model)) { - return NextResponse.json( - { error: `Model ${model} not found.` }, - { status: 400 } - ); - } - - if (indexId && !messages.user.includes('{context}')) { - const error = `The user message must include {context} placeholder to use an index.`; - return NextResponse.json({ error }, { status: 400 }); - } - - let promptMessage = messages.user; - let sources: Document[] = []; - if (indexId) { - // If user specifies a knowledge base, we use RAG. - const openAIEmbeddings = new OpenAIEmbeddings(); - const embeddings = await openAIEmbeddings.embedDocuments([messages.user]); - - const query = ` - select 1 - (embedding <=> '[${embeddings.toString()}]') as similarity, content, id, metadata, source - from documents - where index_id = '${indexId}' - order by similarity desc - limit 3; - `; - - const { data: relevantDocuments, error } = await supabaseExecute( - query - ); - - if (error) { - return NextResponse.json({ error }, { status: 400 }); - } - - const context = relevantDocuments[0]?.content || ''; - promptMessage = messages.user.replace('{context}', context); - sources = [...relevantDocuments]; - } - - const response = await llm({ - message: promptMessage, - system: messages.system, - model, - }); - - return NextResponse.json({ - message: response.choices[0].message.content, - model: response.model, - sources, - }); -} diff --git a/src/app/api/docs/page.tsx b/src/app/api/docs/page.tsx deleted file mode 100644 index a35e8b1..0000000 --- a/src/app/api/docs/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -'use client'; - -import { RedocStandalone } from 'redoc'; - -export default function ApiDoc() { - return ( - - ); -} diff --git a/src/app/api/documents/pdf/route.ts b/src/app/api/documents/pdf/route.ts deleted file mode 100644 index eaa6357..0000000 --- a/src/app/api/documents/pdf/route.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import { authApiKey } from '@/lib/public-api/auth'; -import { WebPDFLoader } from 'langchain/document_loaders/web/pdf'; -import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; -import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'; -import { DocumentInsert } from '../../../../types/supabase-entities'; -import { supabaseExecute } from '../../../../lib/public-api/database'; - -export async function POST(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const indexId = request.nextUrl.searchParams.get('index_id'); - if (!indexId) { - return NextResponse.json( - { error: 'Missing index_id query parameter' }, - { status: 400 } - ); - } - - const formData = await request.formData(); - const file: File | null = formData.get('file') as unknown as File; - - if (!file) { - return NextResponse.json( - { error: 'Missing file in request body' }, - { status: 400 } - ); - } - if (file.type !== 'application/pdf') { - return NextResponse.json({ error: 'File must be a pdf' }, { status: 400 }); - } - - const loader = new WebPDFLoader(file); - - const docs = await loader.load(); - - const splitter = new RecursiveCharacterTextSplitter({ - // TODO: This should be dynamic - chunkSize: 1000, - chunkOverlap: 200, - }); - - const documents = await splitter.createDocuments( - docs.map((doc) => doc.pageContent) - ); - - const openAIEmbeddings = new OpenAIEmbeddings({ - batchSize: 512, // Default value if omitted is 512. Max is 2048 - }); - - const embeddings = await openAIEmbeddings.embedDocuments( - documents.map((doc) => doc.pageContent) - ); - - const documentInsert: DocumentInsert[] = documents.map((doc, index) => ({ - embedding: embeddings[index] as unknown as string, // This is not right. The type generation from supabase is wrong here. - content: doc.pageContent, - metadata: doc.metadata.loc, - index_id: indexId, - source: file.name, - })); - - const query = ` - INSERT INTO documents (embedding, content, metadata, index_id, source, user_id) - VALUES ${documentInsert - .map( - (doc) => - `('[${doc.embedding.toString()}]', '${doc.content}', '${JSON.stringify( - doc.metadata - )}', '${doc.index_id}', '${doc.source}', '${doc.user_id}')` - ) - .join(',')} - RETURNING content, metadata, index_id, source, user_id, created_at, id;`; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data: formData, error }, { status: 400 }); - } - - return NextResponse.json(data); -} diff --git a/src/app/api/documents/route.ts b/src/app/api/documents/route.ts deleted file mode 100644 index c69f1b0..0000000 --- a/src/app/api/documents/route.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import { authApiKey } from '@/lib/public-api/auth'; -import { Document, DocumentInsert } from '@/types/supabase-entities'; -import { supabaseExecute } from '@/lib/public-api/database'; -import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; -import { CreateDocumentRequestType } from '@/lib/public-api/validation'; - -// Get all documents from a index -export async function GET(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const indexId = request.nextUrl.searchParams.get('index_id'); - if (!indexId) { - return NextResponse.json( - { error: 'Missing index_id query parameter' }, - { status: 400 } - ); - } - - const query = `select id, content, metadata, index_id, source, user_id, created_at - from documents where index_id = '${indexId}' limit 50;`; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data, error }, { status: 400 }); - } - - return NextResponse.json(data); -} - -// Add documents to a index -export async function POST(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const indexId = request.nextUrl.searchParams.get('index_id'); - if (!indexId) { - return NextResponse.json( - { error: 'Missing index_id query parameter' }, - { status: 400 } - ); - } - - const documents = (await request.json()) as CreateDocumentRequestType; - // TODO: Validate documents - - if (!documents || !documents.length) { - return NextResponse.json( - { error: 'Missing documents in request body' }, - { status: 400 } - ); - } - - const openAIEmbeddings = new OpenAIEmbeddings({ - batchSize: 512, // Default value if omitted is 512. Max is 2048 - }); - - const embeddings = await openAIEmbeddings.embedDocuments( - documents.map((doc) => doc.content) - ); - - const documentInsert: DocumentInsert[] = documents.map((doc, index) => ({ - embedding: embeddings[index] as unknown as string, // This is not right. The type generation from supabase is wrong here. - content: doc.content, - metadata: doc.metadata, - index_id: indexId, - source: doc.source, - user_id: project.user_id as string, - })); - - const query = ` - INSERT INTO documents (embedding, content, metadata, index_id, source, user_id) - VALUES ${documentInsert - .map( - (doc) => - `('[${doc.embedding.toString()}]', '${doc.content}', '${JSON.stringify( - doc.metadata - )}', '${doc.index_id}', '${doc.source}', '${doc.user_id}')` - ) - .join(',')} - RETURNING content, metadata, index_id, source, user_id, created_at, id;`; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data, error }, { status: 400 }); - } - - return NextResponse.json(data); -} diff --git a/src/app/api/indexes/route.ts b/src/app/api/indexes/route.ts deleted file mode 100644 index 0dd0660..0000000 --- a/src/app/api/indexes/route.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { headers } from 'next/headers'; -import { authApiKey } from '@/lib/public-api/auth'; -import { supabaseExecute } from '@/lib/public-api/database'; -import { Index } from '@/types/supabase-entities'; - -export async function GET(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const query = `select * from indexes where project_id = '${project.id}'`; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data, error }, { status: 400 }); - } - - return NextResponse.json(data); -} - -export async function POST(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const { name } = await request.json(); - - const query = `insert into indexes (name, project_id, user_id) - values ('${name}', '${project.id}', '${project.user_id}') returning *`; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data, error }, { status: 400 }); - } - - return NextResponse.json(data[0]); -} diff --git a/src/app/api/indexes/search/route.ts b/src/app/api/indexes/search/route.ts deleted file mode 100644 index de1ac81..0000000 --- a/src/app/api/indexes/search/route.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @swagger - * /api/documents/search: - * post: - * summary: Search for similar documents - * description: Returns the documents that are contextually similar to the search term - * tags: - * - Documents - * requestBody: - * description: The search query and knowledge base ID - * required: true - * responses: - * 200: - * description: Returns the documents that are contextually similar to the search term - * content: - * application/json: - * schema: - * type: array - * items: - * 400: - * description: Bad request - * content: - * application/json: - * schema: - * type: object - * properties: - * error: - * type: string - * description: The error message - */ - -import { headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import { authApiKey } from '../../../../lib/public-api/auth'; -import { supabaseExecute } from '../../../../lib/public-api/database'; -import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; - -export async function GET(request: NextRequest) { - const { data: project, error: authError } = await authApiKey(headers()); - - if (!project || authError) { - return NextResponse.json({ error: authError }, { status: 401 }); - } - - const indexId = request.nextUrl.searchParams.get('index_id'); - if (!indexId) { - return NextResponse.json( - { error: 'Missing index_id query parameter' }, - { status: 400 } - ); - } - - const search = request.nextUrl.searchParams.get('search'); - if (!search) { - return NextResponse.json( - { error: 'Missing search query parameter' }, - { status: 400 } - ); - } - - const openAIEmbeddings = new OpenAIEmbeddings({ batchSize: 512 }); - const embeddings = await openAIEmbeddings.embedDocuments([search]); - - // Search for similar documents using cosine similarity - const query = ` - select 1 - (embedding <=> '[${embeddings.toString()}]') as similarity, id, content, metadata, index_id, source, user_id, created_at - from documents - where index_id = '${indexId}' - order by similarity desc - limit 3; - `; - - const { data, error } = await supabaseExecute(query); - - if (error) { - return NextResponse.json({ data, error }, { status: 400 }); - } - - return NextResponse.json(data); -} diff --git a/src/app/api/route.ts b/src/app/api/route.ts deleted file mode 100644 index 15c7d36..0000000 --- a/src/app/api/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createSwaggerSpec } from 'next-swagger-doc'; -import { NextResponse } from 'next/server'; -import { generateOpenApi } from '@/lib/public-api/openapi'; - -export function GET() { - const spec = generateOpenApi(); - - return NextResponse.json(spec); -} diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts deleted file mode 100644 index 7b5f3ea..0000000 --- a/src/app/auth/callback/route.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs"; -import { cookies } from "next/headers"; -import { NextResponse } from "next/server"; - -export const dynamic = "force-dynamic"; - -export async function GET(request: Request) { - // The `/auth/callback` route is required for the server-side auth flow implemented - // by the Auth Helpers package. It exchanges an auth code for the user's session. - // https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange - const requestUrl = new URL(request.url); - const code = requestUrl.searchParams.get("code"); - - if (code) { - const supabase = createRouteHandlerClient({ cookies }); - await supabase.auth.exchangeCodeForSession(code); - } - - // URL to redirect to after sign in process completes - return NextResponse.redirect(requestUrl.origin); -} diff --git a/src/app/auth/sign-in/route.ts b/src/app/auth/sign-in/route.ts deleted file mode 100644 index 2ecba2a..0000000 --- a/src/app/auth/sign-in/route.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; -import { cookies } from 'next/headers'; -import { NextResponse } from 'next/server'; - -export const dynamic = 'force-dynamic'; - -export async function POST(request: Request) { - const requestUrl = new URL(request.url); - const formData = await request.formData(); - const email = String(formData.get('email')); - const password = String(formData.get('password')); - const supabase = createRouteHandlerClient({ cookies }); - - const { error } = await supabase.auth.signInWithPassword({ - email, - password, - }); - - if (error) { - return NextResponse.redirect( - `${requestUrl.origin}/login?error=${error.message}`, - { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - } - ); - } - - return NextResponse.redirect(`${requestUrl.origin}/dashboard`, { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - }); -} diff --git a/src/app/auth/sign-out/route.ts b/src/app/auth/sign-out/route.ts deleted file mode 100644 index 658e5c7..0000000 --- a/src/app/auth/sign-out/route.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs' -import { cookies } from 'next/headers' -import { NextResponse } from 'next/server' - -export const dynamic = 'force-dynamic' - -export async function POST(request: Request) { - const requestUrl = new URL(request.url) - const supabase = createRouteHandlerClient({ cookies }) - - await supabase.auth.signOut() - - return NextResponse.redirect(`${requestUrl.origin}/login`, { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - }) -} diff --git a/src/app/auth/sign-up-early/route.ts b/src/app/auth/sign-up-early/route.ts deleted file mode 100644 index 8fe6070..0000000 --- a/src/app/auth/sign-up-early/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; -import { cookies } from 'next/headers'; -import { NextResponse } from 'next/server'; - -export const dynamic = 'force-dynamic'; - -export async function POST(request: Request) { - const requestUrl = new URL(request.url); - const formData = await request.formData(); - const email = String(formData.get('email')); - const password = String(formData.get('password')); - const supabase = createRouteHandlerClient({ cookies }); - - const { data, error } = await supabase.auth.signUp({ - email, - password, - }); - - if (error) { - console.log(error); - return NextResponse.redirect( - `${requestUrl.origin}?error=${error.message}`, - { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - } - ); - } - - return NextResponse.redirect(`${requestUrl.origin}/thank-you`, { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - }); -} diff --git a/src/app/auth/sign-up/route.ts b/src/app/auth/sign-up/route.ts deleted file mode 100644 index f7d9691..0000000 --- a/src/app/auth/sign-up/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; -import { cookies } from 'next/headers'; -import { NextResponse } from 'next/server'; - -export const dynamic = 'force-dynamic'; - -export async function POST(request: Request) { - const requestUrl = new URL(request.url); - const formData = await request.formData(); - const email = String(formData.get('email')); - const password = String(formData.get('password')); - const supabase = createRouteHandlerClient({ cookies }); - - const { data, error } = await supabase.auth.signUp({ - email, - password, - }); - - if (error) { - console.log(error); - return NextResponse.redirect( - `${requestUrl.origin}/login?error=${error.message}`, - { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - } - ); - } - - return NextResponse.redirect(`${requestUrl.origin}/login`, { - // a 301 status is required to redirect from a POST to a GET route - status: 301, - }); -} diff --git a/src/lib/public-api/auth.ts b/src/lib/public-api/auth.ts deleted file mode 100644 index 80fd518..0000000 --- a/src/lib/public-api/auth.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Project } from '@/types/supabase-entities'; -import { Result, supabaseExecute } from './database'; - -export function getApiKey(headers: any) { - const authorization = headers.get('authorization'); - if (!authorization) { - return null; - } - - const [type, key] = authorization.split(' '); - if (type !== 'Bearer') { - return null; - } - - return key; -} - -export async function authApiKey(headers: any): Promise> { - const apiKey = getApiKey(headers); - if (!apiKey) { - return { - data: null, - error: { - code: '401', - hint: 'Did you forget to add headers?', - message: 'No Bearer token found in Headers', - }, - }; - } - - const query = `select * from projects where api_key = '${apiKey}'`; - const { data, error } = await supabaseExecute(query); - if (error) { - return { data, error }; - } - - if (!data.length) { - return { - data: null, - error: { - code: '401', - hint: '', - message: 'Invalid API key.', - }, - }; - } - - return { data: data[0], error: null }; -} diff --git a/src/lib/public-api/database.ts b/src/lib/public-api/database.ts deleted file mode 100644 index 55f9312..0000000 --- a/src/lib/public-api/database.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Client } from 'pg'; - -export interface ResultError { - data: null; - error: { - code: string; - hint: string; - message: string; - }; -} - -export interface ResultSuccess { - data: T; - error: null; -} - -export type Result = ResultSuccess | ResultError; - -export async function supabaseExecute( - query: string, - parameters: any[] = [] -): Promise> { - const client = new Client({ - connectionString: - process.env.DATABASE_URL || - 'postgresql://postgres:postgres@localhost:54322/postgres', - }); - try { - await client.connect(); - const result = await client.query(query, parameters); - await client.end(); - - return { data: result.rows, error: null }; - } catch (err: any) { - console.error('Error executing query: ', query, parameters); - console.error(err); - await client.end(); - - return { - data: null, - error: { code: err.code, hint: err.hint, message: err.message }, - }; - } -} diff --git a/src/lib/public-api/llm.ts b/src/lib/public-api/llm.ts deleted file mode 100644 index 8852dcc..0000000 --- a/src/lib/public-api/llm.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { OpenAI } from 'openai'; - -const openAIModels = ['gpt-3.5-turbo', 'gpt-3.5-turbo-16k ']; -const anyScaleModels = [ - 'mistralai/Mistral-7B-Instruct-v0.1', - 'meta-llama/Llama-2-7b-chat-hf', - 'meta-llama/Llama-2-13b-chat-hf', - 'meta-llama/Llama-2-70b-chat-hf', -]; - -export const AVAILABLE_MODELS = [anyScaleModels, ...openAIModels]; - -interface LlmOptions { - system: string | undefined; - message: string; - temperature?: number; - max_tokens?: number; - stream?: boolean; - model?: string; -} - -export async function llm({ - message, - system = '', - temperature = 0.7, - max_tokens = 500, - stream = false, - model = 'mistralai/Mistral-7B-Instruct-v0.1', -}: LlmOptions): Promise { - const llm = anyScaleModels.includes(model) - ? // AnyScale is compatible with Open AI API. So all we have to do is change the base API URL. - new OpenAI({ - baseURL: process.env.ANYSCALE_API_BASE!, - apiKey: process.env.ANYSCALE_API_KEY!, - }) - : new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); - - const response = await llm.chat.completions.create({ - model, - stream, - messages: [ - { - role: 'system', - content: system, - }, - { - role: 'user', - content: message, - }, - ], - max_tokens, - temperature, - top_p: 1, - frequency_penalty: 1, - presence_penalty: 1, - }); - - return response as OpenAI.Chat.ChatCompletion; -} diff --git a/src/lib/public-api/openapi.ts b/src/lib/public-api/openapi.ts deleted file mode 100644 index e15bded..0000000 --- a/src/lib/public-api/openapi.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { - OpenAPIRegistry, - OpenApiGeneratorV3, -} from '@asteasolutions/zod-to-openapi'; -import { - ChatCompletionRequest, - ChatCompletionResponse, - CreateDocumentRequest, - CreateIndexRequest, - CreateIndexResponse, - ErrorResponse, - GetIndexesResponse, - RagCompletionRequest, - RagCompletionResponse, - SimilarDocumentsResponse, -} from './validation'; -import { ZodSchema, ZodType, ZodTypeDef } from 'zod'; - -const errorResponse = { - 400: { - description: 'Bad Request', - content: { - 'application/json': { - schema: ErrorResponse, - }, - }, - }, -}; - -const request = ( - schema: ZodType, - description: string = '' -) => ({ - body: { - description, - content: { - 'application/json': { - schema, - }, - }, - }, -}); - -const response = ( - schema: ZodType, - description: string = '' -) => ({ - 200: { - description, - content: { - 'application/json': { - schema, - }, - }, - }, -}); - -// Docs: https://www.npmjs.com/package/@asteasolutions/zod-to-openap -export const publicAPIRegistry = new OpenAPIRegistry(); - -const bearerAuth = publicAPIRegistry.registerComponent( - 'securitySchemes', - 'bearerAuth', - { - type: 'http', - scheme: 'bearer', - bearerFormat: 'Bearer', - } -); - -publicAPIRegistry.registerPath({ - method: 'get', - path: '/indexes', - tags: ['Indexes'], - summary: 'Get all indexes', - description: 'Get all indexes of the current project.', - security: [{ [bearerAuth.name]: [] }], - responses: { - ...response(GetIndexesResponse, 'Array with all the indexes'), - ...errorResponse, - }, -}); - -publicAPIRegistry.registerPath({ - method: 'post', - path: '/indexes', - tags: ['Indexes'], - summary: 'Create new index', - description: 'Create new index under the current project.', - security: [{ [bearerAuth.name]: [] }], - request: request(CreateIndexRequest, 'The index name'), - responses: { - ...response(CreateIndexResponse, 'The newly created index'), - ...errorResponse, - }, -}); - -publicAPIRegistry.registerPath({ - method: 'post', - path: '/indexes/search', - tags: ['Indexes'], - summary: 'Search for similar documents', - description: - 'Contextually search an index for similar documents. The search is done by comparing the embeddings of the documents using cosine similarity.', - security: [{ [bearerAuth.name]: [] }], - // TODO: - // request: request(____, ''), - responses: { - ...response(SimilarDocumentsResponse, 'Similar documents found'), - ...errorResponse, - }, -}); - -publicAPIRegistry.registerPath({ - method: 'post', - path: '/documents', - tags: ['Documents'], - summary: 'Load document', - description: - 'Load a document into an index. Before the document is loaded, SquareDev creates embeddings for the document so that it can be contextually searched later.', - security: [{ [bearerAuth.name]: [] }], - request: request(CreateDocumentRequest, 'The new document'), - responses: { - ...response(CreateDocumentRequest, 'Newly created document'), - ...errorResponse, - }, -}); - -publicAPIRegistry.registerPath({ - method: 'post', - path: '/chat/completions', - tags: ['Language models'], - summary: 'Chat', - description: - 'Given a prompt, the model will return one or more predicted completions', - security: [{ [bearerAuth.name]: [] }], - request: request(ChatCompletionRequest, 'The new document'), - responses: { - ...response(ChatCompletionResponse, 'Newly created document'), - ...errorResponse, - }, -}); - -publicAPIRegistry.registerPath({ - method: 'post', - path: '/chat/rag', - tags: ['Language models'], - summary: 'Retrieval augmented generation (RAG)', - description: - 'SquareDev contextually searches for related documents in the specified index and uses them to improve the generation of the completion.', - security: [{ [bearerAuth.name]: [] }], - request: request(RagCompletionRequest, 'The retrieval request object.'), - responses: { - ...response( - RagCompletionResponse, - 'Response object containing completions and sources' - ), - ...errorResponse, - }, -}); - -export function generateOpenApi() { - return new OpenApiGeneratorV3(publicAPIRegistry.definitions).generateDocument( - { - openapi: '3.0.0', - info: { - version: '0.1.0-alpha', - title: 'SquareDev API', - description: - 'This is the SquareDev API. It still in alpha phase so it may change', - }, - tags: [ - { - name: 'Authorization', - description: `Authorization is done via a Bearer token in the Authorization header. Your API token can be found in your project via the studio. - `, - }, - { - name: 'Indexes', - description: `Indexes are the main way to store and organize documents. - They contain the documents, their content, embeddings and metadata.`, - }, - { - name: 'Documents', - description: `Documents are the main way content is stored in SquareDev. They are stored in Indexes and contain the content, embeddings and metadata. - Usually documents are created from files or unstructured data. After a file is uploaded in SquareDev platform it is splitted into documents, - and these documents along with their embeddings (vector representations of the content) are stored in the index. - After that documents can be searched and retrieved contextually. - `, - }, - { - name: 'Language models', - description: `Language model endpoints are the main way to interact with a LLMs. They can be used to generate simple completions from an input, - or combined with documents for retrieval augmented generation (RAG). The RAG endpoint can be used to generate completions from a prompt, that - are also context aware from the documents in the index that best answer the prompt (user question). - .`, - }, - ], - servers: [{ url: 'api' }], - } - ); -} diff --git a/src/lib/public-api/validation.ts b/src/lib/public-api/validation.ts deleted file mode 100644 index 5ee5acc..0000000 --- a/src/lib/public-api/validation.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { type } from 'os'; -import * as z from 'zod'; -import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; - -extendZodWithOpenApi(z); - -/** - * IMPORTANT - * - * This file contains the validation schemas for the API requests and responses. - * It is used by the API routes and the Cypress tests. - * Make sure to make every object schema strict and backwards compatible. - * Zod strict: https://zod.dev/?id=strict - */ - -// Errors -// --- - -export const ErrorResponse = z - .object({ - error: z.string(), - }) - .openapi('ErrorResponse'); - -// Documents -// --- - -const DocumentBase = z - .object({ - content: z.string(), - source: z.string().optional(), - metadata: z.object({}).optional(), - }) - .strict(); - -export const CreateDocumentRequest = z - .array(DocumentBase) - .openapi('CreateDocumentRequest'); - -export type CreateDocumentRequestType = z.infer; - -export const SingleDocumentResponse = DocumentBase.merge( - DocumentBase.merge( - z - .object({ - id: z.string(), - index_id: z.string(), - created_at: z.string(), - user_id: z.string(), - // Embeddings are not returned by the API - }) - .strict() - ) -).openapi('SingleDocumentResponse'); - -export const DocumentsResponse = z - .array(SingleDocumentResponse) - .openapi('DocumentsResponse'); - -export type CreateDocumentResponseType = z.infer; - -export const SimilarDocumentsResponse = z - .array(SingleDocumentResponse.merge(z.object({ similarity: z.number() }))) - .openapi('SimilarDocumentsResponse'); - -// Indexes -// --- - -export const CreateIndexRequest = z - .object({ - name: z.string(), - }) - .strict() - .openapi('CreateIndexRequest'); - -export const CreateIndexResponse = CreateIndexRequest.merge( - z - .object({ - id: z.string(), - project_id: z.string(), - created_at: z.string(), - user_id: z.string(), - }) - .strict() -).openapi('CreateIndexResponse'); - -export type CreateIndexRequestType = z.infer; - -export const GetIndexesResponse = z - .array(CreateIndexResponse) - .openapi('GetIndexesResponse'); - -export type GetIndexesResponseType = z.infer; - -// Chat -// --- - -export const ChatCompletionRequest = z - .object({ - messages: z.object({ - system: z.string().optional(), - user: z.string(), - }), - model: z.string(), - }) - .strict() - .openapi('ChatCompletionRequest'); - -export type ChatCompletionRequestType = z.infer; - -export const ChatCompletionResponse = z - .object({ - message: z.string(), - model: z.string(), - }) - .strict() - .openapi('ChatCompletionResponse'); - -export type ChatCompletionResponseType = z.infer; - -// RAG -// --- - -export const RagCompletionRequest = z - .object({ - messages: z.object({ - system: z.string().optional(), - user: z.string(), - }), - model: z.string(), - indexId: z.string(), - withMemory: z.boolean().optional(), - }) - .strict() - .openapi('RagCompletionRequest'); - -export type RagCompletionRequestType = z.infer; - -export const RagCompletionResponse = z - .object({ - message: z.string(), - model: z.string(), - sources: z.array( - DocumentBase.merge( - z.object({ id: z.string(), similarity: z.number() }).strict() - ) - ), - }) - .strict() - .openapi('RagCompletionResponse'); diff --git a/src/middleware.ts b/src/middleware.ts index 5bae7fb..386ab60 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,25 +1,9 @@ -import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'; import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export async function middleware(req: NextRequest) { const res = NextResponse.next(); - // Create a Supabase client configured to use cookies - const supabase = createMiddlewareClient({ req, res }); - - const requestUrl = new URL(req.url); - const code = requestUrl.searchParams.get('code'); - // The `/auth/callback` route is required for the server-side auth flow implemented - // other wise it will be stuck in a redirect loop - if (code && !req.url.includes('/auth/callback')) { - // URL to redirect to after sign in process completes - return NextResponse.redirect(`${requestUrl.origin}/dashboard`); - } - - // Refresh session if expired - required for Server Components - // https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware - const { data, error } = await supabase.auth.getSession(); return res; } diff --git a/src/types/supabase-entities.ts b/src/types/supabase-entities.ts deleted file mode 100644 index 2014f6e..0000000 --- a/src/types/supabase-entities.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Database } from './supabase'; - -export type Project = Database['public']['Tables']['projects']['Row']; -export type Index = Database['public']['Tables']['indexes']['Row']; -export type IndexInsert = Database['public']['Tables']['indexes']['Insert']; - -export type DocumentInsert = - Database['public']['Tables']['documents']['Insert']; -export type Document = Database['public']['Tables']['documents']['Row']; diff --git a/src/types/supabase.ts b/src/types/supabase.ts deleted file mode 100644 index 24cc949..0000000 --- a/src/types/supabase.ts +++ /dev/null @@ -1,265 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[]; - -export interface Database { - public: { - Tables: { - documents: { - Row: { - content: string; - created_at: string; - embedding: string; - id: string; - index_id: string; - metadata: Json | null; - source: string; - user_id: string; - }; - Insert: { - content: string; - created_at?: string; - embedding: string; - id?: string; - index_id: string; - metadata?: Json | null; - source: string; - user_id?: string; - }; - Update: { - content?: string; - created_at?: string; - embedding?: string; - id?: string; - index_id?: string; - metadata?: Json | null; - source?: string; - user_id?: string; - }; - Relationships: [ - { - foreignKeyName: 'documents_index_id_fkey'; - columns: ['index_id']; - isOneToOne: false; - referencedRelation: 'indexes'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'documents_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; - } - ]; - }; - indexes: { - Row: { - created_at: string; - id: string; - name: string; - project_id: string | null; - user_id: string; - }; - Insert: { - created_at?: string; - id?: string; - name: string; - project_id?: string | null; - user_id?: string; - }; - Update: { - created_at?: string; - id?: string; - name?: string; - project_id?: string | null; - user_id?: string; - }; - Relationships: [ - { - foreignKeyName: 'indexes_project_id_fkey'; - columns: ['project_id']; - isOneToOne: false; - referencedRelation: 'projects'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'indexes_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; - } - ]; - }; - memory_logs: { - Row: { - created_at: string; - id: string; - latency_milliseconds: number; - prompt: string; - prompt_tokens: number; - response: string; - response_tokens: number; - session_id: string; - user_question: string; - user_question_embedding: string; - }; - Insert: { - created_at?: string; - id?: string; - latency_milliseconds: number; - prompt: string; - prompt_tokens: number; - response: string; - response_tokens: number; - session_id: string; - user_question: string; - user_question_embedding: string; - }; - Update: { - created_at?: string; - id?: string; - latency_milliseconds?: number; - prompt?: string; - prompt_tokens?: number; - response?: string; - response_tokens?: number; - session_id?: string; - user_question?: string; - user_question_embedding?: string; - }; - Relationships: [ - { - foreignKeyName: 'memory_logs_session_id_fkey'; - columns: ['session_id']; - isOneToOne: false; - referencedRelation: 'memory_sessions'; - referencedColumns: ['id']; - } - ]; - }; - memory_sessions: { - Row: { - created_at: string; - id: string; - project_id: string; - sumamry: string; - }; - Insert: { - created_at?: string; - id?: string; - project_id: string; - sumamry: string; - }; - Update: { - created_at?: string; - id?: string; - project_id?: string; - sumamry?: string; - }; - Relationships: [ - { - foreignKeyName: 'memory_sessions_project_id_fkey'; - columns: ['project_id']; - isOneToOne: false; - referencedRelation: 'projects'; - referencedColumns: ['id']; - } - ]; - }; - projects: { - Row: { - api_key: string | null; - created_at: string; - description: string | null; - id: string; - name: string; - user_id: string | null; - }; - Insert: { - api_key?: string | null; - created_at?: string; - description?: string | null; - id?: string; - name: string; - user_id?: string | null; - }; - Update: { - api_key?: string | null; - created_at?: string; - description?: string | null; - id?: string; - name?: string; - user_id?: string | null; - }; - Relationships: [ - { - foreignKeyName: 'projects_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; - } - ]; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - ivfflathandler: { - Args: { - '': unknown; - }; - Returns: unknown; - }; - vector_avg: { - Args: { - '': number[]; - }; - Returns: string; - }; - vector_dims: { - Args: { - '': string; - }; - Returns: number; - }; - vector_norm: { - Args: { - '': string; - }; - Returns: number; - }; - vector_out: { - Args: { - '': string; - }; - Returns: unknown; - }; - vector_send: { - Args: { - '': string; - }; - Returns: string; - }; - vector_typmod_in: { - Args: { - '': unknown[]; - }; - Returns: number; - }; - }; - Enums: { - [_ in never]: never; - }; - CompositeTypes: { - [_ in never]: never; - }; - }; -} diff --git a/supabase/.gitignore b/supabase/.gitignore deleted file mode 100644 index 773c7c3..0000000 --- a/supabase/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Supabase -.branches -.temp diff --git a/supabase/config.toml b/supabase/config.toml deleted file mode 100644 index 22b74a0..0000000 --- a/supabase/config.toml +++ /dev/null @@ -1,134 +0,0 @@ -# A string used to distinguish different Supabase projects on the same host. Defaults to the -# working directory name when running `supabase init`. -project_id = "studio" - -[api] -enabled = true -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. public and storage are always included. -schemas = ["public", "storage", "graphql_public"] -# Extra schemas to add to the search_path of every request. public is always included. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 - -[db] -# Port to use for the local database URL. -port = 54322 -# Port used by db diff command to initialise the shadow database. -shadow_port = 54320 -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 15 - -[db.pooler] -enabled = false -# Port to use for the local connection pooler. -port = 54329 -# Specifies when a server connection can be reused by other clients. -# Configure one of the supported pooler modes: `transaction`, `session`. -pool_mode = "transaction" -# How many server connections to allow per user/database pair. -default_pool_size = 20 -# Maximum number of client connections allowed. -max_client_conn = 100 - -[realtime] -enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv6) -# ip_version = "IPv6" - -[studio] -enabled = true -# Port to use for Supabase Studio. -port = 54323 -# External URL of the API server that frontend connects to. -api_url = "http://localhost" - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[inbucket] -enabled = true -# Port to use for the email testing server web interface. -port = 54324 -# Uncomment to expose additional ports for testing user applications that send emails. -# smtp_port = 54325 -# pop3_port = 54326 - -[storage] -enabled = true -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -[auth] -enabled = true -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://localhost:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://localhost:3000"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). -jwt_expiry = 3600 -# If disabled, the refresh token will never expire. -enable_refresh_token_rotation = true -# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. -# Requires enable_refresh_token_rotation = true. -refresh_token_reuse_interval = 10 -# Allow/disallow new user signups to your project. -enable_signup = true - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = true -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false - -# Uncomment to customize email template -# [auth.email.template.invite] -# subject = "You have been invited" -# content_path = "./supabase/templates/invite.html" - -[auth.sms] -# Allow/disallow new user signups via SMS to your project. -enable_signup = true -# If enabled, users need to confirm their phone number before signing in. -enable_confirmations = false - -# Use pre-defined map of phone number to OTP for testing. -[auth.sms.test_otp] -# 4152127777 = "123456" - -# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. -[auth.sms.twilio] -enabled = false -account_sid = "" -message_service_sid = "" -# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: -auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`, -# `twitter`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" -# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" -# Overrides the default auth redirectUrl. -redirect_uri = "" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "" - -[analytics] -enabled = false -port = 54327 -vector_port = 54328 -# Configure one of the supported backends: `postgres`, `bigquery`. -backend = "postgres" \ No newline at end of file diff --git a/supabase/migrations/20231014182810_initial.sql b/supabase/migrations/20231014182810_initial.sql deleted file mode 100644 index eeeedb4..0000000 --- a/supabase/migrations/20231014182810_initial.sql +++ /dev/null @@ -1,103 +0,0 @@ -create extension if not exists "vector" with schema "public" version '0.4.0'; -create table "public"."apps" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "user_id" uuid default auth.uid(), - "name" text not null, - "api_key" uuid default gen_random_uuid() -); -alter table "public"."apps" enable row level security; -create table "public"."apps_knowledge_bases" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "app_id" uuid not null, - "knowledge_base_id" uuid not null, - "user_id" uuid not null -); -alter table "public"."apps_knowledge_bases" enable row level security; -create table "public"."documents" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "embedding" vector not null, - "content" text not null, - "metadata" json, - "source" text not null, - "user_id" uuid not null default auth.uid(), - "knowledge_base_id" uuid not null -); -alter table "public"."documents" enable row level security; -create table "public"."knowledge_bases" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "name" text not null, - "user_id" uuid not null default auth.uid() -); -alter table "public"."knowledge_bases" enable row level security; -create table "public"."memory_logs" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "user_question" text not null, - "user_question_embedding" vector not null, - "prompt" text not null, - "session_id" uuid not null, - "prompt_tokens" bigint not null, - "response" text not null, - "response_tokens" bigint not null, - "latency_milliseconds" bigint not null -); -alter table "public"."memory_logs" enable row level security; -create table "public"."memory_sessions" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "sumamry" text not null, - "app_id" uuid not null -); -alter table "public"."memory_sessions" enable row level security; -CREATE UNIQUE INDEX apps_knowledge_bases_pkey ON public.apps_knowledge_bases USING btree (id); -CREATE UNIQUE INDEX apps_pkey ON public.apps USING btree (id); -CREATE UNIQUE INDEX documents_pkey ON public.documents USING btree (id); -CREATE UNIQUE INDEX knowledge_bases_pkey ON public.knowledge_bases USING btree (id); -CREATE UNIQUE INDEX memory_logs_pkey ON public.memory_logs USING btree (id); -CREATE UNIQUE INDEX memory_session_pkey ON public.memory_sessions USING btree (id); -alter table "public"."apps" -add constraint "apps_pkey" PRIMARY KEY using index "apps_pkey"; -alter table "public"."apps_knowledge_bases" -add constraint "apps_knowledge_bases_pkey" PRIMARY KEY using index "apps_knowledge_bases_pkey"; -alter table "public"."documents" -add constraint "documents_pkey" PRIMARY KEY using index "documents_pkey"; -alter table "public"."knowledge_bases" -add constraint "knowledge_bases_pkey" PRIMARY KEY using index "knowledge_bases_pkey"; -alter table "public"."memory_logs" -add constraint "memory_logs_pkey" PRIMARY KEY using index "memory_logs_pkey"; -alter table "public"."memory_sessions" -add constraint "memory_session_pkey" PRIMARY KEY using index "memory_session_pkey"; -alter table "public"."apps_knowledge_bases" -add constraint "apps_knowledge_bases_app_id_fkey" FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE not valid; -alter table "public"."apps_knowledge_bases" validate constraint "apps_knowledge_bases_app_id_fkey"; -alter table "public"."apps_knowledge_bases" -add constraint "apps_knowledge_bases_knowledge_base_id_fkey" FOREIGN KEY (knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE not valid; -alter table "public"."apps_knowledge_bases" validate constraint "apps_knowledge_bases_knowledge_base_id_fkey"; -alter table "public"."apps_knowledge_bases" -add constraint "apps_knowledge_bases_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; -alter table "public"."apps_knowledge_bases" validate constraint "apps_knowledge_bases_user_id_fkey"; -alter table "public"."documents" -add constraint "documents_knowledge_base_id_fkey" FOREIGN KEY (knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE not valid; -alter table "public"."documents" validate constraint "documents_knowledge_base_id_fkey"; -alter table "public"."documents" -add constraint "documents_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; -alter table "public"."documents" validate constraint "documents_user_id_fkey"; -alter table "public"."knowledge_bases" -add constraint "knowledge_bases_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; -alter table "public"."knowledge_bases" validate constraint "knowledge_bases_user_id_fkey"; -alter table "public"."memory_logs" -add constraint "memory_logs_session_id_fkey" FOREIGN KEY (session_id) REFERENCES memory_sessions(id) ON DELETE CASCADE not valid; -alter table "public"."memory_logs" validate constraint "memory_logs_session_id_fkey"; -alter table "public"."memory_sessions" -add constraint "memory_sessions_app_id_fkey" FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE not valid; -alter table "public"."memory_sessions" validate constraint "memory_sessions_app_id_fkey"; -create policy "Enable access for all authed users" on "public"."apps" as permissive for all to authenticated using ((auth.uid() = user_id)) with check ((auth.uid() = user_id)); -create policy "Enable access for all authed users" on "public"."apps_knowledge_bases" as permissive for all to authenticated using ((auth.uid() = user_id)) with check ((auth.uid() = user_id)); -create policy "Enable access for all authed users" on "public"."documents" as permissive for all to authenticated using ((auth.uid() = user_id)) with check ((auth.uid() = user_id)); -create policy "Enable access for all authed users" on "public"."knowledge_bases" as permissive for all to authenticated using ((auth.uid() = user_id)) with check ((auth.uid() = user_id)); -alter table "public"."documents" -alter column "embedding" type vector(1536); \ No newline at end of file diff --git a/supabase/migrations/20231015132106_feature.sql b/supabase/migrations/20231015132106_feature.sql deleted file mode 100644 index 05d90fa..0000000 --- a/supabase/migrations/20231015132106_feature.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table "public"."apps_knowledge_bases" alter column "user_id" set default auth.uid(); - - diff --git a/supabase/migrations/20231027154727_feature.sql b/supabase/migrations/20231027154727_feature.sql deleted file mode 100644 index 49ed394..0000000 --- a/supabase/migrations/20231027154727_feature.sql +++ /dev/null @@ -1,105 +0,0 @@ -drop policy "Enable access for all authed users" on "public"."apps"; - -drop policy "Enable access for all authed users" on "public"."apps_knowledge_bases"; - -drop policy "Enable access for all authed users" on "public"."knowledge_bases"; - -alter table "public"."apps_knowledge_bases" drop constraint "apps_knowledge_bases_app_id_fkey"; - -alter table "public"."apps_knowledge_bases" drop constraint "apps_knowledge_bases_knowledge_base_id_fkey"; - -alter table "public"."apps_knowledge_bases" drop constraint "apps_knowledge_bases_user_id_fkey"; - -alter table "public"."knowledge_bases" drop constraint "knowledge_bases_user_id_fkey"; - -alter table "public"."memory_sessions" drop constraint "memory_sessions_app_id_fkey"; - -alter table "public"."documents" drop constraint "documents_knowledge_base_id_fkey"; - -alter table "public"."apps" drop constraint "apps_pkey"; - -alter table "public"."apps_knowledge_bases" drop constraint "apps_knowledge_bases_pkey"; - -alter table "public"."knowledge_bases" drop constraint "knowledge_bases_pkey"; - -drop index if exists "public"."apps_knowledge_bases_pkey"; - -drop index if exists "public"."apps_pkey"; - -drop index if exists "public"."knowledge_bases_pkey"; - -drop table "public"."apps"; - -drop table "public"."apps_knowledge_bases"; - -drop table "public"."knowledge_bases"; - -create table "public"."index" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "name" text not null, - "user_id" uuid not null default auth.uid(), - "project_id" uuid -); - - -alter table "public"."index" enable row level security; - -create table "public"."project" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "user_id" uuid default auth.uid(), - "name" text not null, - "api_key" uuid default gen_random_uuid() -); - - -alter table "public"."project" enable row level security; - -alter table "public"."memory_sessions" drop column "app_id"; - -alter table "public"."memory_sessions" add column "project_id" uuid not null; - -CREATE UNIQUE INDEX apps_pkey ON public.project USING btree (id); - -CREATE UNIQUE INDEX knowledge_bases_pkey ON public.index USING btree (id); - -alter table "public"."index" add constraint "knowledge_bases_pkey" PRIMARY KEY using index "knowledge_bases_pkey"; - -alter table "public"."project" add constraint "apps_pkey" PRIMARY KEY using index "apps_pkey"; - -alter table "public"."index" add constraint "index_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE not valid; - -alter table "public"."index" validate constraint "index_project_id_fkey"; - -alter table "public"."index" add constraint "index_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; - -alter table "public"."index" validate constraint "index_user_id_fkey"; - -alter table "public"."memory_sessions" add constraint "memory_sessions_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE not valid; - -alter table "public"."memory_sessions" validate constraint "memory_sessions_project_id_fkey"; - -alter table "public"."documents" add constraint "documents_knowledge_base_id_fkey" FOREIGN KEY (knowledge_base_id) REFERENCES index(id) ON DELETE CASCADE not valid; - -alter table "public"."documents" validate constraint "documents_knowledge_base_id_fkey"; - -create policy "Enable access for all authed users" -on "public"."index" -as permissive -for all -to authenticated -using ((auth.uid() = user_id)) -with check ((auth.uid() = user_id)); - - -create policy "Enable access for all authed users" -on "public"."project" -as permissive -for all -to authenticated -using ((auth.uid() = user_id)) -with check ((auth.uid() = user_id)); - - - diff --git a/supabase/migrations/20231027155201_feature.sql b/supabase/migrations/20231027155201_feature.sql deleted file mode 100644 index 3f061f4..0000000 --- a/supabase/migrations/20231027155201_feature.sql +++ /dev/null @@ -1,89 +0,0 @@ -drop policy "Enable access for all authed users" on "public"."index"; - -drop policy "Enable access for all authed users" on "public"."project"; - -alter table "public"."index" drop constraint "index_project_id_fkey"; - -alter table "public"."index" drop constraint "index_user_id_fkey"; - -alter table "public"."documents" drop constraint "documents_knowledge_base_id_fkey"; - -alter table "public"."memory_sessions" drop constraint "memory_sessions_project_id_fkey"; - -alter table "public"."index" drop constraint "knowledge_bases_pkey"; - -alter table "public"."project" drop constraint "apps_pkey"; - -drop index if exists "public"."apps_pkey"; - -drop index if exists "public"."knowledge_bases_pkey"; - -drop table "public"."index"; - -drop table "public"."project"; - -create table "public"."indexes" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "name" text not null, - "user_id" uuid not null default auth.uid(), - "project_id" uuid -); - - -alter table "public"."indexes" enable row level security; - -create table "public"."projects" ( - "id" uuid not null default gen_random_uuid(), - "created_at" timestamp with time zone not null default now(), - "user_id" uuid default auth.uid(), - "name" text not null, - "api_key" uuid default gen_random_uuid() -); - - -alter table "public"."projects" enable row level security; - -CREATE UNIQUE INDEX apps_pkey ON public.projects USING btree (id); - -CREATE UNIQUE INDEX knowledge_bases_pkey ON public.indexes USING btree (id); - -alter table "public"."indexes" add constraint "knowledge_bases_pkey" PRIMARY KEY using index "knowledge_bases_pkey"; - -alter table "public"."projects" add constraint "apps_pkey" PRIMARY KEY using index "apps_pkey"; - -alter table "public"."indexes" add constraint "indexes_project_id_fkey" FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE not valid; - -alter table "public"."indexes" validate constraint "indexes_project_id_fkey"; - -alter table "public"."indexes" add constraint "indexes_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; - -alter table "public"."indexes" validate constraint "indexes_user_id_fkey"; - -alter table "public"."documents" add constraint "documents_knowledge_base_id_fkey" FOREIGN KEY (knowledge_base_id) REFERENCES indexes(id) ON DELETE CASCADE not valid; - -alter table "public"."documents" validate constraint "documents_knowledge_base_id_fkey"; - -alter table "public"."memory_sessions" add constraint "memory_sessions_project_id_fkey" FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE not valid; - -alter table "public"."memory_sessions" validate constraint "memory_sessions_project_id_fkey"; - -create policy "Enable access for all authed users" -on "public"."indexes" -as permissive -for all -to authenticated -using ((auth.uid() = user_id)) -with check ((auth.uid() = user_id)); - - -create policy "Enable access for all authed users" -on "public"."projects" -as permissive -for all -to authenticated -using ((auth.uid() = user_id)) -with check ((auth.uid() = user_id)); - - - diff --git a/supabase/migrations/20231027163314_feature.sql b/supabase/migrations/20231027163314_feature.sql deleted file mode 100644 index a1de062..0000000 --- a/supabase/migrations/20231027163314_feature.sql +++ /dev/null @@ -1,11 +0,0 @@ -alter table "public"."documents" drop constraint "documents_knowledge_base_id_fkey"; - -alter table "public"."documents" drop column "knowledge_base_id"; - -alter table "public"."documents" add column "index_id" uuid not null; - -alter table "public"."documents" add constraint "documents_index_id_fkey" FOREIGN KEY (index_id) REFERENCES indexes(id) ON DELETE CASCADE not valid; - -alter table "public"."documents" validate constraint "documents_index_id_fkey"; - - diff --git a/supabase/seed.sql b/supabase/seed.sql deleted file mode 100644 index e69de29..0000000