From be2968aabca29b13c6b31dce72ac03f47101f8ed Mon Sep 17 00:00:00 2001 From: Jack Merrill Date: Wed, 7 May 2025 22:34:04 -0400 Subject: [PATCH] fuck it we do it live --- app/(auth-pages)/forgot-password/page.tsx | 37 -- app/(auth-pages)/layout.tsx | 9 - app/(auth-pages)/sign-in/page.tsx | 44 -- app/(auth-pages)/sign-up/page.tsx | 51 -- app/(auth-pages)/smtp-message.tsx | 25 - app/api/process-document/route.ts | 8 +- app/dashboard/documents/[id]/page.tsx | 8 +- app/dashboard/page.tsx | 3 - bun.lockb | Bin 352728 -> 361444 bytes components/MarkdownRenderer.tsx | 121 +++- components/TTSProvider.tsx | 28 +- components/UploadZone.tsx | 6 +- lib/utils.ts | 1 + package.json | 1 + supabase/.gitignore | 8 - supabase/config.toml | 319 --------- supabase/functions/generate-tts/.npmrc | 3 - supabase/functions/generate-tts/deno.json | 3 - supabase/functions/generate-tts/deno.lock | 664 ------------------- supabase/functions/generate-tts/index.ts | 72 -- supabase/functions/process-document/index.ts | 510 -------------- tailwind.config.ts | 6 +- 22 files changed, 152 insertions(+), 1775 deletions(-) delete mode 100644 app/(auth-pages)/forgot-password/page.tsx delete mode 100644 app/(auth-pages)/layout.tsx delete mode 100644 app/(auth-pages)/sign-in/page.tsx delete mode 100644 app/(auth-pages)/sign-up/page.tsx delete mode 100644 app/(auth-pages)/smtp-message.tsx delete mode 100644 supabase/.gitignore delete mode 100644 supabase/config.toml delete mode 100644 supabase/functions/generate-tts/.npmrc delete mode 100644 supabase/functions/generate-tts/deno.json delete mode 100644 supabase/functions/generate-tts/deno.lock delete mode 100644 supabase/functions/generate-tts/index.ts delete mode 100644 supabase/functions/process-document/index.ts diff --git a/app/(auth-pages)/forgot-password/page.tsx b/app/(auth-pages)/forgot-password/page.tsx deleted file mode 100644 index ac67693..0000000 --- a/app/(auth-pages)/forgot-password/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { forgotPasswordAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; - -export default async function ForgotPassword(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - return ( - <> -
-
-

Reset Password

-

- Already have an account?{" "} - - Sign in - -

-
-
- - - - Reset Password - - -
-
- - - ); -} diff --git a/app/(auth-pages)/layout.tsx b/app/(auth-pages)/layout.tsx deleted file mode 100644 index 47c4dd7..0000000 --- a/app/(auth-pages)/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export default async function Layout({ - children, -}: { - children: React.ReactNode; -}) { - return ( -
{children}
- ); -} diff --git a/app/(auth-pages)/sign-in/page.tsx b/app/(auth-pages)/sign-in/page.tsx deleted file mode 100644 index 85fc62f..0000000 --- a/app/(auth-pages)/sign-in/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { signInAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; - -export default async function Login(props: { searchParams: Promise }) { - const searchParams = await props.searchParams; - return ( -
-

Sign in

-

- Don't have an account?{" "} - - Sign up - -

-
- - -
- - - Forgot Password? - -
- - - Sign in - - -
-
- ); -} diff --git a/app/(auth-pages)/sign-up/page.tsx b/app/(auth-pages)/sign-up/page.tsx deleted file mode 100644 index 1bbe9dd..0000000 --- a/app/(auth-pages)/sign-up/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { signUpAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; - -export default async function Signup(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - if ("message" in searchParams) { - return ( -
- -
- ); - } - - return ( - <> -
-

Sign up

-

- Already have an account?{" "} - - Sign in - -

-
- - - - - - Sign up - - -
-
- - - ); -} diff --git a/app/(auth-pages)/smtp-message.tsx b/app/(auth-pages)/smtp-message.tsx deleted file mode 100644 index a3d050e..0000000 --- a/app/(auth-pages)/smtp-message.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ArrowUpRight, InfoIcon } from "lucide-react"; -import Link from "next/link"; - -export function SmtpMessage() { - return ( -
- -
- - Note: Emails are rate limited. Enable Custom SMTP to - increase the rate limit. - -
- - Learn more - -
-
-
- ); -} diff --git a/app/api/process-document/route.ts b/app/api/process-document/route.ts index 503011c..695b003 100644 --- a/app/api/process-document/route.ts +++ b/app/api/process-document/route.ts @@ -21,7 +21,7 @@ Do not return the Markdown as a code block, only as a raw string, without any ne No data or information should ever be removed, it should only be processed and formatted. -There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. If any citations contain JSON-breaking characters, ensure they are properly escaped. This includes characters like double quotes, backslashes, and newlines. +There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. (**Note that there may be multiple citations, usually split by commas. Ensure these are added too.**) If any citations contain JSON-breaking characters, ensure they are properly escaped. This includes characters like double quotes, backslashes, and newlines. The Markdown should be human-readable and well-formatted. The markdown string should properly sanitized and should not break a JSON parser when returned as the final format. @@ -263,7 +263,11 @@ export async function POST(req: NextRequest) { ], }); - const split = response.choices[0].message.content.split("---------"); + const contentData = response.choices?.[0]?.message?.content; + const split = + typeof contentData === "string" + ? contentData.split("---------") + : ["", "{}"]; const content = split[0].trim(); const citationsStr = split[1]?.trim() || "{}"; console.log("Citations string:", citationsStr); diff --git a/app/dashboard/documents/[id]/page.tsx b/app/dashboard/documents/[id]/page.tsx index d223d0b..922f11a 100644 --- a/app/dashboard/documents/[id]/page.tsx +++ b/app/dashboard/documents/[id]/page.tsx @@ -19,7 +19,11 @@ import { redirect } from "next/navigation"; import { TTSProvider } from "@/components/TTSProvider"; import MarkdownRenderer from "@/components/MarkdownRenderer"; -export default async function DocumentPage(props: { params: { id: string } }) { +export default async function DocumentPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { const supabase = await createClient(); const { @@ -35,7 +39,7 @@ export default async function DocumentPage(props: { params: { id: string } }) { return redirect("/login"); } - const { id } = await props.params; + const { id } = await params; // Fetch the document details based on the ID from params const { data: document, error } = await supabase diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index ad3a32a..094522f 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -73,9 +73,6 @@ export default async function Page() { -
- -
diff --git a/bun.lockb b/bun.lockb index f87940c5467af20bf5c3b63b7b26207cd9043b50..4bf9419e08b5ba164b2f675ddc0ab340586b9d57 100755 GIT binary patch delta 72029 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 XqI20kw41I^2l~_&k2bq>r&s8` delta 67575 zcmeFad3+Q_->yG1Fpy?-1tNlgil9ctEgKm?Pze}BWCe|5qGF2$ zC@LxnBJK)_M#U9WR9u6KiYut#t`J4eb$8Y9TTq&4c&W#>^nT8!

Zq`a|txnn}p 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 page.markdown).join("\n") || ""; + // Join all markdown content from the OCR data into a single string. Add page separators. + const rawContent = ocr + .map((page) => page.markdown) + .map((page, index) => { + const pageIndex = index + 1; // 1-based index for pages + const pageSeparator = `\n\n###### Page ${pageIndex}\n\n`; + return `${page}${pageSeparator}`; + }) + .join("\n\n"); const citations: { text: string; page: number; index: string; number: number; + inTextNumber: string; }[] = []; const totalPages = ocr.length; const totalSentences = sentences.length; @@ -75,6 +92,7 @@ export default function MarkdownRenderer({ page: page.index, index: (totalCitations + index).toString(), // unique index across all pages number: totalCitations + index + 1, // 1-based numbering + inTextNumber: citation.number, }); }); }); @@ -92,16 +110,61 @@ export default function MarkdownRenderer({ h4: ({ node, ...props }) => (

), + h6: ({ node, ...props }) => { + const text = props.children!.toString().split(" ")[1]; + const pageIndex = parseInt(text) - 1; // Convert to 0-based index + const page = ocr[pageIndex]; + + return ( +
+
+ + Page {pageIndex + 1} of {totalPages} + +
+
+ ); + }, p: ({ node, ...props }) => ( -

- ), - img: ({ node, ...props }) => ( - +

), + img: ({ node, ...props }) => { + const { src, alt } = props as any; + + const pageIndex = ocr.findIndex((p) => + p.images.find((image) => image.id === src) + ); + if (pageIndex === -1) return null; // Handle the case where the page is not found + + const page = ocr.find((p) => p.index === pageIndex); + if (!page) return null; // Handle the case where the page is not found + + const img = page.images.find((image) => { + if (image.id === src) { + return true; + } + }); + + if (!img) return null; // Handle the case where the image is not found + + // Calculate the width and height based on the image dimensions and DPI + const dpi = page.dimensions.dpi; + const width = ((img.bottomRightX - img.topLeftX) / dpi) * 96; // Convert to pixels + const height = ((img.bottomRightY - img.topLeftY) / dpi) * 96; // Convert to pixels + + return ( +

+ {alt} +
+ ); + }, a: ({ node, ...props }) => (
), @@ -122,6 +185,10 @@ export default function MarkdownRenderer({ ), sup: ({ node, ...props }) => { // Check if the text contains a reference number + if (!props.children) { + return ; + } + const text = props.children!.toString(); const referenceNumber = text; @@ -139,7 +206,6 @@ export default function MarkdownRenderer({ } return ( - // TODO: get the references from the document and display them in a popover

{citation.text}

+

+ Page {citation.page}, Reference {citation.inTextNumber} +

); }, + table: ({ node, ...props }) => ( +
+ + + ), + thead: ({ node, ...props }) => ( + + ), + tbody: ({ node, ...props }) => , + tr: ({ node, ...props }) => ( + + ), + th: ({ node, ...props }) => ( +
+ ), + td: ({ node, ...props }) => , }; return ( @@ -162,6 +260,7 @@ export default function MarkdownRenderer({ children={rawContent} components={components} rehypePlugins={rehypePlugins} + remarkPlugins={[remarkGfm]} /> ); } diff --git a/components/TTSProvider.tsx b/components/TTSProvider.tsx index 803a760..1e8b5aa 100644 --- a/components/TTSProvider.tsx +++ b/components/TTSProvider.tsx @@ -85,6 +85,8 @@ export const TTSProvider = ({ audioCache.current.set(i, audioUrl); // Cache the audio URL } catch (error) { console.error(`Error preloading audio for sentence ${i}:`, error); + } finally { + setProcessing((prev) => prev.filter((item) => item !== i)); // Remove from processing } } } @@ -112,6 +114,7 @@ export const TTSProvider = ({ } const playSentence = async (index: number) => { + if (index === currentSentence) return; // Prevent redundant updates setCurrentSentence(index); const sentence = removeMarkdown(sentences[index]); @@ -142,13 +145,12 @@ export const TTSProvider = ({ playSentence(index); }; + let shouldContinue = true; + const playInOrder = async (index: number) => { if (index < 0 || index >= sentences.length) return; setCurrentSentence(index); - // Introduce a flag to track whether playback should continue - let shouldContinue = true; - for (let i = index; i < sentences.length; i++) { if (!shouldContinue) { console.log("Playback stopped or paused."); @@ -158,14 +160,13 @@ export const TTSProvider = ({ console.log("Playing sentence:", i, sentences[i]); try { await playSentence(i); - preloadAudio(i + 1); // Preload the next sentence after playing + await preloadAudio(i + 1); // Preload the next sentence after playing } catch (error) { console.error("Error playing sentence:", error); break; // Stop playback on error } } - // Reset the playback state when done setStatus("ready"); }; @@ -198,6 +199,23 @@ export const TTSProvider = ({ preloadAudio(currentSentence); }, [currentSentence]); + useEffect(() => { + if (audioRef.current) { + const handleEnded = () => { + console.log("Audio playback ended."); + if (status === "running") { + playInOrder(currentSentence + 1); // Play the next sentence + } + }; + + audioRef.current.addEventListener("ended", handleEnded); + + return () => { + audioRef.current?.removeEventListener("ended", handleEnded); + }; + } + }, [currentSentence, status]); + const value: TTSContextType = { sentences, currentSentence, diff --git a/components/UploadZone.tsx b/components/UploadZone.tsx index 2c52db0..1a172bf 100644 --- a/components/UploadZone.tsx +++ b/components/UploadZone.tsx @@ -39,7 +39,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { }, }); - eventSource.addEventListener("status", (event) => { + eventSource.addEventListener("status", (event: any) => { const data = JSON.parse(event.data); console.log("Status Event:", data); supabase.auth.setSession; @@ -47,7 +47,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { setStatus(data.message); }); - eventSource.addEventListener("error", (event) => { + eventSource.addEventListener("error", (event: any) => { console.error("SSE Error:", event); toast.error("An error occurred while processing the document", { description: event.data || "Unknown error", @@ -56,7 +56,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { eventSource.close(); }); - eventSource.addEventListener("complete", (event) => { + eventSource.addEventListener("complete", (event: any) => { const data = JSON.parse(event.data); console.log("Processing Complete:", data); toast.success("Document processing complete!"); diff --git a/lib/utils.ts b/lib/utils.ts index 50ea601..f76239f 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -9,6 +9,7 @@ export function cn(...inputs: ClassValue[]) { export async function detectWebGPU() { try { + // @ts-ignore const adapter = await navigator.gpu.requestAdapter(); return !!adapter; } catch (e) { diff --git a/package.json b/package.json index 2dae9b9..17d5438 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-markdown": "^10.1.0", "rehype-raw": "^7.0.0", "remark": "^15.0.1", + "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", "remove-markdown": "^0.6.0", "sonner": "^2.0.3", diff --git a/supabase/.gitignore b/supabase/.gitignore deleted file mode 100644 index ad9264f..0000000 --- a/supabase/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Supabase -.branches -.temp - -# dotenvx -.env.keys -.env.local -.env.*.local diff --git a/supabase/config.toml b/supabase/config.toml deleted file mode 100644 index 2691649..0000000 --- a/supabase/config.toml +++ /dev/null @@ -1,319 +0,0 @@ -# For detailed configuration reference documentation, visit: -# https://supabase.com/docs/guides/local-development/cli/config -# A string used to distinguish different Supabase projects on the same host. Defaults to the -# working directory name when running `supabase init`. -project_id = "neuro-read" - -[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 `graphql_public` schemas are included by default. -schemas = ["public", "graphql_public"] -# Extra schemas to add to the search_path of every request. -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 - -[api.tls] -# Enable HTTPS endpoints locally using a self-signed certificate. -enabled = false - -[db] -# Port to use for the local database URL. -port = 54322 -# Port used by db diff command to initialize 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 - -# [db.vault] -# secret_key = "env(SECRET_VALUE)" - -[db.migrations] -# Specifies an ordered list of schema files that describe your database. -# Supports glob patterns relative to supabase directory: "./schemas/*.sql" -schema_paths = [] - -[db.seed] -# If enabled, seeds the database after migrations during a db reset. -enabled = true -# Specifies an ordered list of seed files to load during db reset. -# Supports glob patterns relative to supabase directory: "./seeds/*.sql" -sql_paths = ["./seed.sql"] - -[realtime] -enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv4) -# ip_version = "IPv6" -# The maximum length in bytes of HTTP request headers. (default: 4096) -# max_header_length = 4096 - -[studio] -enabled = true -# Port to use for Supabase Studio. -port = 54323 -# External URL of the API server that frontend connects to. -api_url = "http://127.0.0.1" -# OpenAI API Key to use for Supabase AI in the Supabase Studio. -openai_api_key = "env(OPENAI_API_KEY)" - -# 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 -# admin_email = "admin@email.com" -# sender_name = "Admin" - -[storage] -enabled = true -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -# Image transformation API is available to Supabase Pro plan. -# [storage.image_transformation] -# enabled = true - -# Uncomment to configure local storage buckets -# [storage.buckets.images] -# public = false -# file_size_limit = "50MiB" -# allowed_mime_types = ["image/png", "image/jpeg"] -# objects_path = "./images" - -[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://127.0.0.1:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://127.0.0.1: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 -# Allow/disallow anonymous sign-ins to your project. -enable_anonymous_sign_ins = false -# Allow/disallow testing manual linking of accounts -enable_manual_linking = false -# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. -minimum_password_length = 6 -# Passwords that do not meet the following requirements will be rejected as weak. Supported values -# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` -password_requirements = "" - -[auth.rate_limit] -# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. -email_sent = 2 -# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. -sms_sent = 30 -# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. -anonymous_users = 30 -# Number of sessions that can be refreshed in a 5 minute interval per IP address. -token_refresh = 150 -# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). -sign_in_sign_ups = 30 -# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. -token_verifications = 30 - -# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. -# [auth.captcha] -# enabled = true -# provider = "hcaptcha" -# secret = "" - -[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 -# If enabled, users will need to reauthenticate or have logged in recently to change their password. -secure_password_change = false -# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. -max_frequency = "1s" -# Number of characters used in the email OTP. -otp_length = 6 -# Number of seconds before the email OTP expires (defaults to 1 hour). -otp_expiry = 3600 - -# Use a production-ready SMTP server -# [auth.email.smtp] -# enabled = true -# host = "smtp.sendgrid.net" -# port = 587 -# user = "apikey" -# pass = "env(SENDGRID_API_KEY)" -# admin_email = "admin@email.com" -# sender_name = "Admin" - -# 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 = false -# If enabled, users need to confirm their phone number before signing in. -enable_confirmations = false -# Template for sending OTP to users -template = "Your code is {{ .Code }}" -# Controls the minimum amount of time that must pass before sending another sms otp. -max_frequency = "5s" - -# Use pre-defined map of phone number to OTP for testing. -# [auth.sms.test_otp] -# 4152127777 = "123456" - -# Configure logged in session timeouts. -# [auth.sessions] -# Force log out after the specified duration. -# timebox = "24h" -# Force log out if the user has been inactive longer than the specified duration. -# inactivity_timeout = "8h" - -# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. -# [auth.hook.custom_access_token] -# enabled = true -# uri = "pg-functions:////" - -# 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)" - -# Multi-factor-authentication is available to Supabase Pro plan. -[auth.mfa] -# Control how many MFA factors can be enrolled at once per user. -max_enrolled_factors = 10 - -# Control MFA via App Authenticator (TOTP) -[auth.mfa.totp] -enroll_enabled = false -verify_enabled = false - -# Configure MFA via Phone Messaging -[auth.mfa.phone] -enroll_enabled = false -verify_enabled = false -otp_length = 6 -template = "Your code is {{ .Code }}" -max_frequency = "5s" - -# Configure MFA via WebAuthn -# [auth.mfa.web_authn] -# enroll_enabled = true -# verify_enabled = true - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `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 = "" -# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. -skip_nonce_check = false - -# Use Firebase Auth as a third-party provider alongside Supabase Auth. -[auth.third_party.firebase] -enabled = false -# project_id = "my-firebase-project" - -# Use Auth0 as a third-party provider alongside Supabase Auth. -[auth.third_party.auth0] -enabled = false -# tenant = "my-auth0-tenant" -# tenant_region = "us" - -# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. -[auth.third_party.aws_cognito] -enabled = false -# user_pool_id = "my-user-pool-id" -# user_pool_region = "us-east-1" - -# Use Clerk as a third-party provider alongside Supabase Auth. -[auth.third_party.clerk] -enabled = false -# Obtain from https://clerk.com/setup/supabase -# domain = "example.clerk.accounts.dev" - -[edge_runtime] -enabled = true -# Configure one of the supported request policies: `oneshot`, `per_worker`. -# Use `oneshot` for hot reload, or `per_worker` for load testing. -policy = "oneshot" -# Port to attach the Chrome inspector for debugging edge functions. -inspector_port = 8083 -# The Deno major version to use. -deno_version = 1 - -# [edge_runtime.secrets] -# secret_key = "env(SECRET_VALUE)" - -[analytics] -enabled = true -port = 54327 -# Configure one of the supported backends: `postgres`, `bigquery`. -backend = "postgres" - -# Experimental features may be deprecated any time -[experimental] -# Configures Postgres storage engine to use OrioleDB (S3) -orioledb_version = "" -# Configures S3 bucket URL, eg. .s3-.amazonaws.com -s3_host = "env(S3_HOST)" -# Configures S3 bucket region, eg. us-east-1 -s3_region = "env(S3_REGION)" -# Configures AWS_ACCESS_KEY_ID for S3 bucket -s3_access_key = "env(S3_ACCESS_KEY)" -# Configures AWS_SECRET_ACCESS_KEY for S3 bucket -s3_secret_key = "env(S3_SECRET_KEY)" - -[functions.generate-tts] -enabled = true -verify_jwt = true -import_map = "./functions/generate-tts/deno.json" -# Uncomment to specify a custom file path to the entrypoint. -# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx -entrypoint = "./functions/generate-tts/index.ts" -# Specifies static files to be bundled with the function. Supports glob patterns. -# For example, if you want to serve static HTML pages in your function: -# static_files = [ "./functions/generate-tts/*.html" ] diff --git a/supabase/functions/generate-tts/.npmrc b/supabase/functions/generate-tts/.npmrc deleted file mode 100644 index 48c6388..0000000 --- a/supabase/functions/generate-tts/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -# Configuration for private npm package dependencies -# For more information on using private registries with Edge Functions, see: -# https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries diff --git a/supabase/functions/generate-tts/deno.json b/supabase/functions/generate-tts/deno.json deleted file mode 100644 index f6ca845..0000000 --- a/supabase/functions/generate-tts/deno.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "imports": {} -} diff --git a/supabase/functions/generate-tts/deno.lock b/supabase/functions/generate-tts/deno.lock deleted file mode 100644 index 502fad9..0000000 --- a/supabase/functions/generate-tts/deno.lock +++ /dev/null @@ -1,664 +0,0 @@ -{ - "version": "4", - "specifiers": { - "jsr:@supabase/functions-js@*": "2.4.4", - "npm:@mistralai/mistralai@*": "1.6.0_zod@3.24.4", - "npm:@types/node@*": "22.12.0", - "npm:kokoro-js@*": "1.2.1", - "npm:kokoro-tts@*": "0.0.1-security", - "npm:openai@^4.52.5": "4.97.0", - "npm:p-limit@*": "6.2.0" - }, - "jsr": { - "@supabase/functions-js@2.4.4": { - "integrity": "38456509a6e22fb116b118464cbb36357256f9048d3580632b63af91f63769f7", - "dependencies": [ - "npm:openai" - ] - } - }, - "npm": { - "@emnapi/runtime@1.4.3": { - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dependencies": [ - "tslib" - ] - }, - "@huggingface/jinja@0.4.1": { - "integrity": "sha512-3WXbMFaPkk03LRCM0z0sylmn8ddDm4ubjU7X+Hg4M2GOuMklwoGAFXp9V2keq7vltoB/c7McE5aHUVVddAewsw==" - }, - "@huggingface/transformers@3.5.1": { - "integrity": "sha512-qWsPoJMBPYcrGuzRMVL//3dwcLXED9r+j+Hzw+hZpBfrMPdyq57ehNs7lew0D34Paj0DO0E0kZQjOZcIVN17hQ==", - "dependencies": [ - "@huggingface/jinja", - "onnxruntime-node", - "onnxruntime-web", - "sharp" - ] - }, - "@img/sharp-darwin-arm64@0.34.1": { - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", - "dependencies": [ - "@img/sharp-libvips-darwin-arm64" - ] - }, - "@img/sharp-darwin-x64@0.34.1": { - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", - "dependencies": [ - "@img/sharp-libvips-darwin-x64" - ] - }, - "@img/sharp-libvips-darwin-arm64@1.1.0": { - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==" - }, - "@img/sharp-libvips-darwin-x64@1.1.0": { - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==" - }, - "@img/sharp-libvips-linux-arm64@1.1.0": { - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==" - }, - "@img/sharp-libvips-linux-arm@1.1.0": { - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==" - }, - "@img/sharp-libvips-linux-ppc64@1.1.0": { - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==" - }, - "@img/sharp-libvips-linux-s390x@1.1.0": { - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==" - }, - "@img/sharp-libvips-linux-x64@1.1.0": { - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==" - }, - "@img/sharp-libvips-linuxmusl-arm64@1.1.0": { - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==" - }, - "@img/sharp-libvips-linuxmusl-x64@1.1.0": { - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==" - }, - "@img/sharp-linux-arm64@0.34.1": { - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", - "dependencies": [ - "@img/sharp-libvips-linux-arm64" - ] - }, - "@img/sharp-linux-arm@0.34.1": { - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", - "dependencies": [ - "@img/sharp-libvips-linux-arm" - ] - }, - "@img/sharp-linux-s390x@0.34.1": { - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", - "dependencies": [ - "@img/sharp-libvips-linux-s390x" - ] - }, - "@img/sharp-linux-x64@0.34.1": { - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", - "dependencies": [ - "@img/sharp-libvips-linux-x64" - ] - }, - "@img/sharp-linuxmusl-arm64@0.34.1": { - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", - "dependencies": [ - "@img/sharp-libvips-linuxmusl-arm64" - ] - }, - "@img/sharp-linuxmusl-x64@0.34.1": { - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", - "dependencies": [ - "@img/sharp-libvips-linuxmusl-x64" - ] - }, - "@img/sharp-wasm32@0.34.1": { - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", - "dependencies": [ - "@emnapi/runtime" - ] - }, - "@img/sharp-win32-ia32@0.34.1": { - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==" - }, - "@img/sharp-win32-x64@0.34.1": { - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==" - }, - "@isaacs/fs-minipass@4.0.1": { - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dependencies": [ - "minipass" - ] - }, - "@mistralai/mistralai@1.6.0_zod@3.24.4": { - "integrity": "sha512-PQwGV3+n7FbE7Dp3Vnd8DAa3ffx6WuVV966Gfmf4QvzwcO3Mvxpz0SnJ/PjaZcsCwApBCZpNyQzvarAKEQLKeQ==", - "dependencies": [ - "zod", - "zod-to-json-schema" - ] - }, - "@protobufjs/aspromise@1.1.2": { - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64@1.1.2": { - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen@2.0.4": { - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter@1.1.0": { - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch@1.1.0": { - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/inquire" - ] - }, - "@protobufjs/float@1.0.2": { - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire@1.1.0": { - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path@1.1.2": { - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool@1.1.0": { - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8@1.1.0": { - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@types/node-fetch@2.6.12": { - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "dependencies": [ - "@types/node@22.12.0", - "form-data" - ] - }, - "@types/node@18.19.87": { - "integrity": "sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A==", - "dependencies": [ - "undici-types@5.26.5" - ] - }, - "@types/node@22.12.0": { - "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", - "dependencies": [ - "undici-types@6.20.0" - ] - }, - "abort-controller@3.0.0": { - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": [ - "event-target-shim" - ] - }, - "agentkeepalive@4.6.0": { - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dependencies": [ - "humanize-ms" - ] - }, - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "boolean@3.2.0": { - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" - }, - "call-bind-apply-helpers@1.0.2": { - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": [ - "es-errors", - "function-bind" - ] - }, - "chownr@3.0.0": { - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==" - }, - "color-convert@2.0.1": { - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": [ - "color-name" - ] - }, - "color-name@1.1.4": { - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string@1.9.1": { - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": [ - "color-name", - "simple-swizzle" - ] - }, - "color@4.2.3": { - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": [ - "color-convert", - "color-string" - ] - }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": [ - "delayed-stream" - ] - }, - "define-data-property@1.1.4": { - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": [ - "es-define-property", - "es-errors", - "gopd" - ] - }, - "define-properties@1.2.1": { - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": [ - "define-data-property", - "has-property-descriptors", - "object-keys" - ] - }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "detect-libc@2.0.4": { - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==" - }, - "detect-node@2.1.0": { - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "dunder-proto@1.0.1": { - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": [ - "call-bind-apply-helpers", - "es-errors", - "gopd" - ] - }, - "es-define-property@1.0.1": { - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" - }, - "es-errors@1.3.0": { - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-object-atoms@1.1.1": { - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": [ - "es-errors" - ] - }, - "es-set-tostringtag@2.1.0": { - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dependencies": [ - "es-errors", - "get-intrinsic", - "has-tostringtag", - "hasown" - ] - }, - "es6-error@4.1.1": { - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "escape-string-regexp@4.0.0": { - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "event-target-shim@5.0.1": { - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "flatbuffers@25.2.10": { - "integrity": "sha512-7JlN9ZvLDG1McO3kbX0k4v+SUAg48L1rIwEvN6ZQl/eCtgJz9UylTMzE9wrmYrcorgxm3CX/3T/w5VAub99UUw==" - }, - "form-data-encoder@1.7.2": { - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "form-data@4.0.2": { - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dependencies": [ - "asynckit", - "combined-stream", - "es-set-tostringtag", - "mime-types" - ] - }, - "formdata-node@4.4.1": { - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": [ - "node-domexception", - "web-streams-polyfill" - ] - }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "get-intrinsic@1.3.0": { - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": [ - "call-bind-apply-helpers", - "es-define-property", - "es-errors", - "es-object-atoms", - "function-bind", - "get-proto", - "gopd", - "has-symbols", - "hasown", - "math-intrinsics" - ] - }, - "get-proto@1.0.1": { - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": [ - "dunder-proto", - "es-object-atoms" - ] - }, - "global-agent@3.0.0": { - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dependencies": [ - "boolean", - "es6-error", - "matcher", - "roarr", - "semver", - "serialize-error" - ] - }, - "globalthis@1.0.4": { - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dependencies": [ - "define-properties", - "gopd" - ] - }, - "gopd@1.2.0": { - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" - }, - "guid-typescript@1.0.9": { - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" - }, - "has-property-descriptors@1.0.2": { - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": [ - "es-define-property" - ] - }, - "has-symbols@1.1.0": { - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" - }, - "has-tostringtag@1.0.2": { - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": [ - "has-symbols" - ] - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": [ - "function-bind" - ] - }, - "humanize-ms@1.2.1": { - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": [ - "ms" - ] - }, - "is-arrayish@0.3.2": { - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "json-stringify-safe@5.0.1": { - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "kokoro-js@1.2.1": { - "integrity": "sha512-oq0HZJWis3t8lERkMJh84WLU86dpYD0EuBPtqYnLlQzyFP1OkyBRDcweAqCfhNOpltyN9j/azp1H6uuC47gShw==", - "dependencies": [ - "@huggingface/transformers", - "phonemizer" - ] - }, - "kokoro-tts@0.0.1-security": { - "integrity": "sha512-VK7ILDqP0J88kiXdFrq7ymugipw42xLGmAU3kqpyFwJKlBqcsiTXVbvdTtehwYhYPw5QKJPmN2j7tD6fhPe6qw==" - }, - "long@5.3.2": { - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, - "matcher@3.0.0": { - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dependencies": [ - "escape-string-regexp" - ] - }, - "math-intrinsics@1.1.0": { - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": [ - "mime-db" - ] - }, - "minipass@7.1.2": { - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" - }, - "minizlib@3.0.2": { - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dependencies": [ - "minipass" - ] - }, - "mkdirp@3.0.1": { - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==" - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node-domexception@1.0.0": { - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, - "node-fetch@2.7.0": { - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": [ - "whatwg-url" - ] - }, - "object-keys@1.1.1": { - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "onnxruntime-common@1.21.0": { - "integrity": "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==" - }, - "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4": { - "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==" - }, - "onnxruntime-node@1.21.0": { - "integrity": "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==", - "dependencies": [ - "global-agent", - "onnxruntime-common@1.21.0", - "tar" - ] - }, - "onnxruntime-web@1.22.0-dev.20250409-89f8206ba4": { - "integrity": "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==", - "dependencies": [ - "flatbuffers", - "guid-typescript", - "long", - "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", - "platform", - "protobufjs" - ] - }, - "openai@4.97.0": { - "integrity": "sha512-LRoiy0zvEf819ZUEJhgfV8PfsE8G5WpQi4AwA1uCV8SKvvtXQkoWUFkepD6plqyJQRghy2+AEPQ07FrJFKHZ9Q==", - "dependencies": [ - "@types/node@18.19.87", - "@types/node-fetch", - "abort-controller", - "agentkeepalive", - "form-data-encoder", - "formdata-node", - "node-fetch" - ] - }, - "p-limit@6.2.0": { - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "dependencies": [ - "yocto-queue" - ] - }, - "phonemizer@1.2.1": { - "integrity": "sha512-v0KJ4mi2T4Q7eJQ0W15Xd4G9k4kICSXE8bpDeJ8jisL4RyJhNWsweKTOi88QXFc4r4LZlz5jVL5lCHhkpdT71A==" - }, - "platform@1.3.6": { - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "protobufjs@7.5.0": { - "integrity": "sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/base64", - "@protobufjs/codegen", - "@protobufjs/eventemitter", - "@protobufjs/fetch", - "@protobufjs/float", - "@protobufjs/inquire", - "@protobufjs/path", - "@protobufjs/pool", - "@protobufjs/utf8", - "@types/node@22.12.0", - "long" - ] - }, - "roarr@2.15.4": { - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dependencies": [ - "boolean", - "detect-node", - "globalthis", - "json-stringify-safe", - "semver-compare", - "sprintf-js" - ] - }, - "semver-compare@1.0.0": { - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" - }, - "semver@7.7.1": { - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" - }, - "serialize-error@7.0.1": { - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dependencies": [ - "type-fest" - ] - }, - "sharp@0.34.1": { - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", - "dependencies": [ - "@img/sharp-darwin-arm64", - "@img/sharp-darwin-x64", - "@img/sharp-libvips-darwin-arm64", - "@img/sharp-libvips-darwin-x64", - "@img/sharp-libvips-linux-arm", - "@img/sharp-libvips-linux-arm64", - "@img/sharp-libvips-linux-ppc64", - "@img/sharp-libvips-linux-s390x", - "@img/sharp-libvips-linux-x64", - "@img/sharp-libvips-linuxmusl-arm64", - "@img/sharp-libvips-linuxmusl-x64", - "@img/sharp-linux-arm", - "@img/sharp-linux-arm64", - "@img/sharp-linux-s390x", - "@img/sharp-linux-x64", - "@img/sharp-linuxmusl-arm64", - "@img/sharp-linuxmusl-x64", - "@img/sharp-wasm32", - "@img/sharp-win32-ia32", - "@img/sharp-win32-x64", - "color", - "detect-libc", - "semver" - ] - }, - "simple-swizzle@0.2.2": { - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": [ - "is-arrayish" - ] - }, - "sprintf-js@1.1.3": { - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, - "tar@7.4.3": { - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dependencies": [ - "@isaacs/fs-minipass", - "chownr", - "minipass", - "minizlib", - "mkdirp", - "yallist" - ] - }, - "tr46@0.0.3": { - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "tslib@2.8.1": { - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "type-fest@0.13.1": { - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==" - }, - "undici-types@5.26.5": { - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "undici-types@6.20.0": { - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - }, - "web-streams-polyfill@4.0.0-beta.3": { - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" - }, - "webidl-conversions@3.0.1": { - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url@5.0.0": { - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": [ - "tr46", - "webidl-conversions" - ] - }, - "yallist@5.0.0": { - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==" - }, - "yocto-queue@1.2.1": { - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==" - }, - "zod-to-json-schema@3.24.5_zod@3.24.4": { - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dependencies": [ - "zod" - ] - }, - "zod@3.24.4": { - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==" - } - }, - "remote": { - "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.5.1/+esm": "fb0a2ba5088bbde44cddff091a0d1844147cd08c75c75985df70672c7151efd5", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/+esm": "cf3368de4ad22103fe3083600aa9a1bb764d12a0795b2994110836fb5b9091e4", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.js/+esm": "cf3368de4ad22103fe3083600aa9a1bb764d12a0795b2994110836fb5b9091e4", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.web.js": "6067712fe4c43cdb36c762f860a5ab8d96ea1d9c8882466438eb9f3d0d20be8f", - "https://cdn.jsdelivr.net/npm/onnxruntime-common/+esm": "54aea20a2aaa80823ca7c15c084008e838fe74b61f3026d7d5e0b0187b6b9fcb", - "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0-dev.20250409-89f8206ba4/+esm": "53bf11d80531b341f46d4dc695a3d143d8b4c71d30e43905f9588db9804832db", - "https://cdn.jsdelivr.net/npm/phonemizer@1.2.1/+esm": "b8b4327641797c57274be04e4c82a561dd1e63ec7d37bcbb9820f8be1e332696" - } -} diff --git a/supabase/functions/generate-tts/index.ts b/supabase/functions/generate-tts/index.ts deleted file mode 100644 index f6d7ae2..0000000 --- a/supabase/functions/generate-tts/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Import necessary modules -import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { KokoroTTS } from "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.js/+esm"; - -console.log("generate-tts function initialized"); - -Deno.serve(async (req) => { - if (req.method === "OPTIONS") { - // Handle preflight requests - return new Response(null, { - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", - }, - }); - } - - try { - console.log("Received request for TTS generation"); - // Parse the incoming request body - const { text, voice, index } = await req.json(); - console.log("Parsed request body:", { text, voice, index }); - - // Validate the input - if (!text || !voice) { - return new Response( - JSON.stringify({ error: "Missing required parameters: text or voice" }), - { status: 400, headers: { "Content-Type": "application/json" } } - ); - } - - console.log(`Generating TTS for text: "${text}" with voice: "${voice}"`); - - // Initialize KokoroTTS - const model_id = "onnx-community/Kokoro-82M-v1.0-ONNX"; - const tts = await KokoroTTS.from_pretrained(model_id, { - dtype: "fp32", - }); - - // Generate the speech audio - const audio = await tts.generate(text, { - voice, - }); - - const arrayBuffer = await audio.toWav(); - - // audioUrl should be the base64 encoded audio blob - const audioUrl = `data:audio/wav;base64,${btoa( - String.fromCharCode(...new Uint8Array(arrayBuffer)) - )}`; - - console.log(`TTS generated successfully for index: ${index}`); - - // Return the audio URL - return new Response(JSON.stringify({ audioUrl }), { - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - console.error("Error generating TTS:", error); - - // Return an error response - return new Response(JSON.stringify({ error: "Failed to generate TTS" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -}); diff --git a/supabase/functions/process-document/index.ts b/supabase/functions/process-document/index.ts deleted file mode 100644 index e43cdb9..0000000 --- a/supabase/functions/process-document/index.ts +++ /dev/null @@ -1,510 +0,0 @@ -import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { createClient } from "jsr:@supabase/supabase-js@2"; -import { Mistral } from "npm:@mistralai/mistralai"; -import pLimit from "npm:p-limit"; -export const corsHeaders = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", -}; -const apiKey = Deno.env.get("MISTRAL_API_KEY"); -const client = new Mistral({ - apiKey: apiKey, -}); -const PROCESSING_PROMPT = ` -You are a document processing AI. Your task is to process the Markdown text scanned from a document page and return it in a clean and structured format. - -The textual page data should only be returned in valid Markdown format. Use proper headings and subheadings to structure the content. **Do not add headings if they do not exist in the original text.** If there is a title to the document, it should be the first heading. -Any images should be included. -Do not return the Markdown as a code block, only as a raw string, without any new lines. - -No data or information should ever be removed, it should only be processed and formatted. - -There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. - -The Markdown should be human-readable and well-formatted. The markdown string should properly sanitized and should not break a JSON parser when returned as the final format. - -Return the final result as a text object with the following structure (without code block formatting): - -""" - - ---------- - -{ - "citations": [ - { - "number": 1, // The number as it appears in the text - "text": "Citation text 1" // Ensure any JSON-breaking characters are properly escaped - }, - { - "number": 2, - "text": "Citation text 2" - } - ] -} -""" - -Do not return the text object as a code block, only as a raw string. -`; -Deno.serve(async (req) => { - if (req.method === "OPTIONS") { - console.log("Handling OPTIONS request..."); - return new Response(null, { - headers: { - ...corsHeaders, - "Access-Control-Allow-Methods": "POST, OPTIONS", - }, - }); - } - - if (req.method === "POST") { - console.log("Processing POST request..."); - const { body, writable } = new TransformStream(); - const writer = writable.getWriter(); - - // Set up the SSE response - const headers = new Headers({ - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - ...corsHeaders, - }); - - let activeOperations = 0; // Track active operations - let streamClosed = false; // Track if the stream is closed - - const sendEvent = async (event, data) => { - if (streamClosed) { - console.warn("Attempted to write to a closed stream."); - return; - } - const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`; - console.log("Sending event:", message); - try { - activeOperations++; - await writer.write(new TextEncoder().encode(message)); - } catch (error) { - console.error("Error writing to stream:", error); - } finally { - activeOperations--; - } - }; - - // Start streaming updates - sendEvent("status", { - message: "Initializing...", - }); - - try { - const supabase = createClient( - Deno.env.get("SUPABASE_URL"), - Deno.env.get("SUPABASE_ANON_KEY") - ); - - const supabaseServer = createClient( - Deno.env.get("SUPABASE_URL"), - Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") - ); - - const formData = await req.formData(); - const accessToken = formData.get("access_token"); - const refreshToken = formData.get("refresh_token"); - var reprocessing = false; - var uuid = crypto.randomUUID(); - - const { - data: { user }, - error: sessionError, - } = await supabase.auth.setSession({ - access_token: accessToken, - refresh_token: refreshToken, - }); - - if (sessionError) { - console.error("Error setting session:", sessionError); - sendEvent("error", { - message: "Error setting session", - error: sessionError, - }); - throw new Error("Setting session failed"); - } - - if (formData.has("id")) { - console.log("Reprocessing document..."); - reprocessing = true; - console.log("File ID found in form data."); - sendEvent("status", { - message: "File ID found in form data.", - }); - const docId = formData.get("id"); - console.log("Document ID:", docId, formData); - const { data: documentData, error: documentError } = await supabase - .from("documents") - .select("*") - .eq("id", docId) - .single(); - - if (documentError) { - console.error("Error fetching document record:", documentError); - sendEvent("error", { - message: "Error fetching document record", - error: documentError, - }); - throw new Error("Document record fetch failed"); - } - - if (documentData) { - await supabase - .from("documents") - .update({ - is_processing: true, - }) - .eq("id", documentData.id); - uuid = documentData.id; - } else { - console.error("Document record not found."); - sendEvent("error", { - message: "Document record not found", - }); - throw new Error("Document record not found"); - } - - const { data: fileData, error: fileError } = await supabase.storage - .from("documents") - .download(`${user.id}/${uuid}.pdf`); - - if (fileError) { - console.error("Error downloading file from storage:", fileError); - sendEvent("error", { - message: "Error downloading file from storage", - error: fileError, - }); - throw new Error("File download failed"); - } - - console.log("File downloaded from storage:", fileData); - sendEvent("status", { - message: "File downloaded from storage", - fileData, - }); - - formData.set("file", fileData); - } - - if (!formData.has("file")) { - console.error("File not found in form data."); - sendEvent("error", { - message: "File not found in form data", - }); - throw new Error("File not found"); - } - if (!formData.has("access_token") || !formData.has("refresh_token")) { - console.error("Access token or refresh token not found in form data."); - sendEvent("error", { - message: "Access token or refresh token not found in form data", - }); - throw new Error("Tokens not found"); - } - - const file = formData.get("file") as File; - const fileName = file.name; - - console.log("Generated UUID:", uuid); - sendEvent("status", { - message: "Generated UUID", - uuid, - }); - - console.log("Authenticated user:", user); - sendEvent("status", { - message: "Authenticated user", - user, - }); - - if (!reprocessing) { - const { data: storageData, error: storageError } = - await supabase.storage - .from("documents") - .upload(`${user.id}/${uuid}.pdf`, file); - - if (storageError) { - console.error("Error uploading file to storage:", storageError); - sendEvent("error", { - message: "Error uploading file to storage", - error: storageError, - }); - throw new Error("File upload failed"); - } - - console.log("File uploaded to storage:", storageData); - sendEvent("status", { - message: "File uploaded to storage", - storageData, - }); - - const { error: docError } = await supabase.from("documents").insert({ - id: uuid, - file_name: file.name, - owner: user.id, - raw_file: storageData.id, - is_processing: true, - }); - - if (docError) { - console.error("Error inserting document record:", docError); - sendEvent("error", { - message: "Error inserting document record", - error: docError, - }); - throw new Error("Document record insertion failed"); - } - - console.log("Document record inserted successfully."); - sendEvent("status", { - message: "Document record inserted successfully", - }); - } else { - console.log("Reprocessing document..."); - sendEvent("status", { - message: "Reprocessing document", - }); - - const { error: docError } = await supabase - .from("documents") - .update({ - is_processing: true, - }) - .eq("id", uuid); - if (docError) { - console.error("Error updating document record:", docError); - sendEvent("error", { - message: "Error updating document record", - error: docError, - }); - throw new Error("Document record update failed"); - } - console.log("Document record updated successfully."); - sendEvent("status", { - message: "Document record updated successfully", - }); - } - - console.log("Uploading file to Mistral..."); - sendEvent("status", { - message: "Uploading file to Mistral...", - }); - - const uploaded_pdf = await client.files.upload({ - file: { - fileName, - content: file, - }, - purpose: "ocr", - }); - - console.log("File uploaded to Mistral:", uploaded_pdf); - sendEvent("status", { - message: "File uploaded to Mistral", - uploaded_pdf, - }); - - const signedUrl = await client.files.getSignedUrl({ - fileId: uploaded_pdf.id, - }); - - console.log("Generated signed URL:", signedUrl); - sendEvent("status", { - message: "Generated signed URL", - signedUrl, - }); - - console.log("Processing OCR..."); - sendEvent("status", { - message: "Processing OCR...", - }); - - const ocrResponse = await client.ocr.process({ - model: "mistral-ocr-latest", - document: { - type: "document_url", - documentUrl: signedUrl.url, - }, - }); - - console.log("OCR response received:", ocrResponse); - sendEvent("status", { - message: "OCR response received", - ocrResponse, - }); - - const limit = pLimit(2); - const promises = []; - - for (const page of ocrResponse.pages) { - console.log("Processing page:", page.index); - sendEvent("status", { - message: `Processing page ${page.index}`, - }); - - const pagePromise = limit(async () => { - console.log(`Processing page ${page.index} with Mistral...`); - - const response = await client.chat.complete({ - model: "mistral-small-latest", - messages: [ - { - role: "system", - content: [ - { - type: "text", - text: PROCESSING_PROMPT, - }, - ], - }, - { - role: "user", - content: [ - { - type: "text", - text: page.markdown, - }, - ], - }, - ], - }); - - if (!response.choices) { - console.error("No choices in response for page:", page.index); - sendEvent("error", { - message: `No choices in response for page ${page.index}`, - }); - return; - } - - console.log("Response received for page:", page.index); - sendEvent("status", { - message: `Response received for page ${page.index}`, - }); - - const imageData = {}; - if (page.images.length > 0) { - console.log( - `Processing ${page.images.length} images for page ${page.index}...` - ); - sendEvent("status", { - message: `Processing images for page ${page.index}`, - }); - for (const img of page.images) { - imageData[img.id] = img.imageBase64; - } - } - - if (response.choices[0].message.content) { - // remove any potential code block formatting from the content - console.log( - `[${page.index}] ${response.choices[0].message.content}` - ); - const split = - response.choices[0].message.content.split("---------"); - - const content = split[0].trim(); - const citationsStr = split[1]?.trim() || "{}"; - console.log(`[${page.index}] Citations: ${citationsStr}`); - const citations = JSON.parse(citationsStr).citations || {}; - - console.log("Generating Markdown for page:", page.index); - sendEvent("status", { - message: `Generating Markdown for page ${page.index}`, - }); - const markdown = replaceImagesInMarkdown(content, imageData); - - return { - ...page, - markdown, - citations, - }; - } else { - console.error("Message content is undefined for page:", page.index); - sendEvent("error", { - message: `Message content is undefined for page ${page.index}`, - }); - } - }); - - promises.push(pagePromise); - } - - console.log("Waiting for all pages to be processed..."); - sendEvent("status", { - message: "Waiting for all pages to be processed...", - }); - - const results = await Promise.all(promises); - - console.log("All pages processed. Results:", results); - sendEvent("status", { - message: "All pages processed", - results, - }); - - const sortedResults = results.sort((a, b) => a.index - b.index); - console.log("Sorted results:", sortedResults); - sendEvent("status", { message: "Sorted results", sortedResults }); - - const { data, error } = await supabase - .from("documents") - .update({ - ocr_data: sortedResults, - is_processing: false, - }) - .eq("id", uuid); - - if (error) { - console.error("Error updating document record:", error); - sendEvent("error", { - message: "Error updating document record", - error, - }); - throw new Error("Document record update failed"); - } - - console.log("Closing SSE stream..."); - } catch (error) { - console.error("Error during processing:", error); - sendEvent("error", { - message: "Error during processing", - error, - }); - } finally { - // Wait for all active operations to complete before closing the stream - const interval = setInterval(() => { - if (activeOperations === 0) { - clearInterval(interval); - streamClosed = true; - writer.close(); - } - }, 100); // Check every 100ms - } - - return new Response(body, { - headers, - }); - } - - console.error("Method not allowed:", req.method); - return new Response("Method not allowed", { - status: 405, - }); -}); -function replaceImagesInMarkdown(markdownStr, imagesDict) { - console.log("Replacing images in Markdown..."); - for (const [imgName, base64Str] of Object.entries(imagesDict)) { - markdownStr = markdownStr.replace( - new RegExp(`!\\[${imgName}\\]\\(${imgName}\\)`, "g"), - `![${imgName}](${base64Str})` - ); - } - console.log("Image replacement complete."); - return markdownStr; -} diff --git a/tailwind.config.ts b/tailwind.config.ts index fab6d90..1e92049 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,7 +1,5 @@ -import type { Config } from "tailwindcss"; - const config = { - darkMode: ["class"], + darkMode: "class", content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", @@ -75,6 +73,6 @@ const config = { }, }, plugins: [require("tailwindcss-animate")], -} satisfies Config; +}; export default config;