diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fe66ea7fb32..b5d3188a5b3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,7 +14,7 @@ To ensure a positive and inclusive environment, please read our [code of conduc
If you find a bug, please create an Issue and we’ll triage it.
-- Please search [existing Issues](https://github.com/supabase/supabase) before creating a new one.
+- Please search [existing Issues](https://github.com/supabase/supabase/issues) before creating a new one.
- Please include a clear description of the problem along with steps to reproduce it. Exact steps with screenshots and urls really help here.
## Pull Requests
@@ -23,7 +23,7 @@ We actively welcome your Pull Requests! A couple of things to keep in mind befor
- If you’re fixing an Issue, make sure someone else hasn’t already created a PR fixing the same issue. Likewise, make sure to link your PR to the related Issue(s).
- We will always try to accept the first viable PR that resolves the Issue.
-- If you're new, we encourage you to take a look at issues tagged with [good first issue](https://github.com/supabase/supabase/pulls?q=is%3Aopen+is%3Apr+label%3A%22good+first+issue%22).
+- If you're new, we encourage you to take a look at issues tagged with [good first issue](https://github.com/supabase/supabase/labels/good%20first%20issue).
- If you’re submitting a new feature, make sure you have opened a [Discussion](https://github.com/orgs/supabase/discussions/new/choose) to discuss the new feature before opening a PR. We’d love to accept your hard work, but unfortunately if a feature hasn’t gone through a proper design process, your PR will be closed.
- Please use the PR message template and provide detailed context for quicker review. PRs without clear problem statements will be closed.
diff --git a/apps/docs/components/Feedback/Feedback.tsx b/apps/docs/components/Feedback/Feedback.tsx
index 0359b20e360..4694e338e20 100644
--- a/apps/docs/components/Feedback/Feedback.tsx
+++ b/apps/docs/components/Feedback/Feedback.tsx
@@ -16,6 +16,7 @@ import { Button, cn } from 'ui'
import { useSendFeedbackMutation } from '~/lib/fetch/feedback'
import { useSendTelemetryEvent } from '~/lib/telemetry'
import { FeedbackModal, type FeedbackFields } from './FeedbackModal'
+import { getSanitizedTabParams } from './Feedback.utils'
const FeedbackButton = forwardRef<
HTMLButtonElement,
@@ -89,7 +90,7 @@ function Feedback() {
vote: response,
page: pathname,
metadata: {
- query: Object.fromEntries(new URLSearchParams(window.location.search).entries()),
+ query: getSanitizedTabParams(),
},
})
if (error) console.error(error)
diff --git a/apps/docs/components/Feedback/Feedback.utils.ts b/apps/docs/components/Feedback/Feedback.utils.ts
new file mode 100644
index 00000000000..bc3683490f0
--- /dev/null
+++ b/apps/docs/components/Feedback/Feedback.utils.ts
@@ -0,0 +1,16 @@
+import { pick } from 'lodash'
+
+/**
+ * Gets the tab selection state from the URL search params.
+ *
+ * Sanitizes by including only those search params that are explicitly marked
+ * as query groups.
+ */
+const getSanitizedTabParams = () => {
+ const searchParams = new URLSearchParams(window.location.search)
+ const queryGroups = searchParams.getAll('queryGroups')
+
+ return pick(Object.fromEntries(searchParams.entries()), queryGroups)
+}
+
+export { getSanitizedTabParams }
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 62ed67d0656..bf1e5bda20e 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -1139,6 +1139,10 @@ export const functions: NavMenuConstant = {
name: 'Handling Routing in Functions',
url: '/guides/functions/routing',
},
+ {
+ name: 'Running AI Models',
+ url: '/guides/functions/ai-models',
+ },
{
name: 'Regional invocations',
url: '/guides/functions/regional-invocation',
@@ -1187,6 +1191,10 @@ export const functions: NavMenuConstant = {
name: 'Generating OG images ',
url: '/guides/functions/examples/og-image',
},
+ {
+ name: 'Semantic AI Search',
+ url: '/guides/functions/examples/semantic-search',
+ },
{
name: 'CAPTCHA support with Cloudflare Turnstile',
url: '/guides/functions/examples/cloudflare-turnstile',
diff --git a/apps/docs/content/guides/ai/semantic-search.mdx b/apps/docs/content/guides/ai/semantic-search.mdx
index a9a219f7213..44744441840 100644
--- a/apps/docs/content/guides/ai/semantic-search.mdx
+++ b/apps/docs/content/guides/ai/semantic-search.mdx
@@ -26,7 +26,7 @@ Embeddings are generated using a language model, and embeddings are compared to
## Embedding models
-There are many embedding models available today. Some can be accessed through third-party APIs like [OpenAI](https://platform.openai.com/docs/guides/embeddings), where you send your text in the request and receive an embedding vector in the response. Others can run locally on your own compute, such as through Transformers.js for JavaScript implementations. For more information on local implementation, see [Generate embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings).
+There are many embedding models available today. Supabase Edge Functions has [built in support](/docs/guides/functions/examples/semantic-search) for the `gte-small` model. Others can be accessed through third-party APIs like [OpenAI](https://platform.openai.com/docs/guides/embeddings), where you send your text in the request and receive an embedding vector in the response. Others can run locally on your own compute, such as through Transformers.js for JavaScript implementations. For more information on local implementation, see [Generate embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings).
It's crucial to remember that when using embedding models with semantic search, you must use the same model for all embedding comparisons. Comparing embeddings created by different models will yield meaningless results.
diff --git a/apps/docs/content/guides/auth/auth-anonymous.mdx b/apps/docs/content/guides/auth/auth-anonymous.mdx
index 30b6346f53f..4a3b4aa0ad9 100644
--- a/apps/docs/content/guides/auth/auth-anonymous.mdx
+++ b/apps/docs/content/guides/auth/auth-anonymous.mdx
@@ -237,7 +237,7 @@ where is_anonymous is true and created_at < now() - interval '30 days';
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
- [Supabase Kotlin Client](https://github.com/supabase-community/supabase-kt)
diff --git a/apps/docs/content/guides/auth/auth-email.mdx b/apps/docs/content/guides/auth/auth-email.mdx
index ab2636abfe6..6491a642830 100644
--- a/apps/docs/content/guides/auth/auth-email.mdx
+++ b/apps/docs/content/guides/auth/auth-email.mdx
@@ -207,7 +207,7 @@ suspend fun logout() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
- [Supabase Kotlin Client](https://github.com/supabase-community/supabase-kt)
diff --git a/apps/docs/content/guides/auth/concepts/redirect-urls.mdx b/apps/docs/content/guides/auth/concepts/redirect-urls.mdx
index 3f9f3aab8ad..665b94f13a0 100644
--- a/apps/docs/content/guides/auth/concepts/redirect-urls.mdx
+++ b/apps/docs/content/guides/auth/concepts/redirect-urls.mdx
@@ -76,6 +76,22 @@ const { data, error } = await supabase.auth.signInWithOAuth({
})
```
+## Email templates when using `redirectTo`
+
+When using a `redirectTo` option, you may need to replace the `{{ .SiteURL }}` with `{{ .RedirectTo }}` in your email templates. See the [Email Templates guide](/docs/guides/auth/auth-email-templates) for more information.
+
+For example, change the following:
+
+```html
+
+Confirm your mail
+
+
+Confirm your mail
+```
+
## Mobile deep linking URIs
For mobile applications you can use deep linking URIs. For example, for your `SITE_URL` you can specify something like `com.supabase://login-callback/` and for additional redirect URLs something like `com.supabase.staging://login-callback/` if needed.
diff --git a/apps/docs/content/guides/auth/passwordless-login/auth-email-otp.mdx b/apps/docs/content/guides/auth/passwordless-login/auth-email-otp.mdx
index 2605da556fc..9dc8599500a 100644
--- a/apps/docs/content/guides/auth/passwordless-login/auth-email-otp.mdx
+++ b/apps/docs/content/guides/auth/passwordless-login/auth-email-otp.mdx
@@ -157,7 +157,7 @@ If successful the user will now be logged in and you should receive a valid sess
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Phone SMS & WhatsApp OTP](/docs/guides/auth/phone-login)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
diff --git a/apps/docs/content/guides/auth/passwordless-login/auth-magic-link.mdx b/apps/docs/content/guides/auth/passwordless-login/auth-magic-link.mdx
index 001cab6c545..ed62d006f12 100644
--- a/apps/docs/content/guides/auth/passwordless-login/auth-magic-link.mdx
+++ b/apps/docs/content/guides/auth/passwordless-login/auth-magic-link.mdx
@@ -158,6 +158,6 @@ suspend fun logout() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
diff --git a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx
index 1cdde00d182..5b41ce54404 100644
--- a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx
@@ -112,6 +112,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Bitbucket Account](https://bitbucket.org)
diff --git a/apps/docs/content/guides/auth/social-login/auth-discord.mdx b/apps/docs/content/guides/auth/social-login/auth-discord.mdx
index 1c15e17249e..2154fd37eb8 100644
--- a/apps/docs/content/guides/auth/social-login/auth-discord.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-discord.mdx
@@ -113,7 +113,7 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Discord Account](https://discord.com)
- [Discord Developer Portal](https://discord.com/developers)
diff --git a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx
index b195edf22d2..f15eb331425 100644
--- a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx
@@ -131,6 +131,6 @@ You can read more about App Review [here](https://developers.facebook.com/docs/a
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Facebook Developers Dashboard](https://developers.facebook.com/)
diff --git a/apps/docs/content/guides/auth/social-login/auth-figma.mdx b/apps/docs/content/guides/auth/social-login/auth-figma.mdx
index 0db6671df23..1f982fb668c 100644
--- a/apps/docs/content/guides/auth/social-login/auth-figma.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-figma.mdx
@@ -112,6 +112,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Figma Developers page](https://www.figma.com/developers)
diff --git a/apps/docs/content/guides/auth/social-login/auth-github.mdx b/apps/docs/content/guides/auth/social-login/auth-github.mdx
index c0ffc5e8f04..0a2883ac3d8 100644
--- a/apps/docs/content/guides/auth/social-login/auth-github.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-github.mdx
@@ -123,6 +123,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitHub Developer Settings](https://github.com/settings/developers)
diff --git a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx
index 6dd71afa9be..3ffcfd43354 100644
--- a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx
@@ -109,6 +109,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitLab Account](https://gitlab.com)
diff --git a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx
index f6ed6125535..865e7ba6ee5 100644
--- a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx
@@ -135,6 +135,6 @@ Do reach out to support if you have any concerns around this change.
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [LinkedIn Developer Dashboard](https://api.LinkedIn.com/apps)
diff --git a/apps/docs/content/guides/auth/social-login/auth-notion.mdx b/apps/docs/content/guides/auth/social-login/auth-notion.mdx
index 0c414ab4fdb..9b7a079155d 100644
--- a/apps/docs/content/guides/auth/social-login/auth-notion.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-notion.mdx
@@ -112,7 +112,7 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Notion Account](https://notion.so)
- [Notion Developer Portal](https://www.notion.so/my-integrations)
diff --git a/apps/docs/content/guides/auth/social-login/auth-slack.mdx b/apps/docs/content/guides/auth/social-login/auth-slack.mdx
index 238b394b9d3..e1022ee7562 100644
--- a/apps/docs/content/guides/auth/social-login/auth-slack.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-slack.mdx
@@ -130,6 +130,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Slack Developer Dashboard](https://api.slack.com/apps)
diff --git a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx
index c74d07bb473..071cf0f8b52 100644
--- a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx
@@ -124,6 +124,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/)
diff --git a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx
index 405477b5da4..a968201c747 100644
--- a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx
@@ -127,7 +127,7 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitch Account](https://twitch.tv)
- [Twitch Developer Console](https://dev.twitch.tv/console)
diff --git a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx
index 749029d3c9f..67721da5d8b 100644
--- a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx
@@ -118,6 +118,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitter Developer Dashboard](https://developer.twitter.com/en/portal/dashboard)
diff --git a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx
index 37d6d07a8e8..ef4bf5e2d7d 100644
--- a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx
+++ b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx
@@ -125,6 +125,6 @@ suspend fun signOut() {
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Zoom App Marketplace](https://marketplace.zoom.us/)
diff --git a/apps/docs/content/guides/database/arrays.mdx b/apps/docs/content/guides/database/arrays.mdx
index eb405bd293f..a7579573c17 100644
--- a/apps/docs/content/guides/database/arrays.mdx
+++ b/apps/docs/content/guides/database/arrays.mdx
@@ -210,5 +210,5 @@ returns:
## Resources
- [Supabase JS Client](https://github.com/supabase/supabase-js)
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [PostgreSQL Arrays](https://www.postgresql.org/docs/15/arrays.html)
diff --git a/apps/docs/content/guides/database/sql-to-api.mdx b/apps/docs/content/guides/database/sql-to-api.mdx
index 65550a1c67e..76140737c95 100644
--- a/apps/docs/content/guides/database/sql-to-api.mdx
+++ b/apps/docs/content/guides/database/sql-to-api.mdx
@@ -77,7 +77,7 @@ const { count, error } = await supabase
## Resources
-- [Supabase Account - Free Plan OK](https://supabase.com)
+- [Supabase - Get started for free](https://supabase.com)
- [Postgrest Operators](https://postgrest.org/en/stable/api.html#operators)
- [Supabase API: JavaScript select](/docs/reference/javascript/select)
- [Supabase API: JavaScript modifiers](/docs/reference/javascript/using-modifiers)
diff --git a/apps/docs/content/guides/functions/ai-models.mdx b/apps/docs/content/guides/functions/ai-models.mdx
new file mode 100644
index 00000000000..e895bb5568e
--- /dev/null
+++ b/apps/docs/content/guides/functions/ai-models.mdx
@@ -0,0 +1,171 @@
+---
+id: 'function-ai-models'
+title: 'Running AI Models'
+description: 'How to run AI models in Edge Functions.'
+subtitle: 'How to run AI models in Edge Functions.'
+---
+
+[Supabase Edge Runtime](https://github.com/supabase/edge-runtime) has a built-in API for running AI models. You can use this API to generate embeddings, build conversational workflows, and do other AI related tasks in your Edge Functions.
+
+## Setup
+
+There are no external dependencies or packages to install to enable the API.
+
+You can create a new inference session by doing:
+
+```ts
+const model = new Supabase.ai.Session('model-name')
+```
+
+
+
+To get type hints and checks for the API we recommend adding this triple-slash directive to the start of the Edge Function code: `/// `
+
+
+
+## Running a model inference
+
+Once the session is instantiated, you can call it with inputs to perform inferences. Depending on the model you run, you may need to provide different options (discussed below).
+
+```ts
+const output = await model.run(input, options)
+```
+
+## How to generate text embeddings
+
+Now let's see how to write an Edge Function using the `Supabase.ai` API to generate text embeddings. Currently, `Supabase.ai` API only supports the [gte-small](https://huggingface.co/Supabase/gte-small) model.
+
+
+
+`gte-small` model exclusively caters to English texts, and any lengthy texts will be truncated to a maximum of 512 tokens. While you can provide inputs longer than 512 tokens, truncation may affect the accuracy.
+
+
+
+```ts
+const model = new Supabase.ai.Session('gte-small')
+
+Deno.serve(async (req: Request) => {
+ const params = new URL(req.url).searchParams
+ const input = params.get('input')
+ const output = await model.run(input, { mean_pool: true, normalize: true })
+ return new Response(JSON.stringify(output), {
+ headers: {
+ 'Content-Type': 'application/json',
+ Connection: 'keep-alive',
+ },
+ })
+})
+```
+
+## Using Large Language Models
+
+Inference via larger models is supported via [Ollama](https://ollama.com/). In the first iteration, the `llama2` and `mistral` models are supported and you would need to manage the Ollama server yourself.
+
+
+
+### Running locally
+
+1. Install Ollama and pull the Mistral model
+
+ ```
+ ollama pull mistral
+ ```
+
+1. Run the Ollama server locally
+
+ ```
+ ollama serve
+ ```
+
+1. Set a function secret called AI_INFERENCE_API_HOST to point to the Ollama server
+
+ ```
+ echo "AI_INFERENCE_API_HOST=http://host.docker.internal:11434/api/generate" >> supabase/functions/.env
+ ```
+
+1. Create a new function with the following code
+
+ ```
+ supabase functions new ollama-test
+ ```
+
+ ```ts
+ ///
+ const session = new Supabase.ai.Session('mistral')
+
+ Deno.serve(async (req: Request) => {
+ const params = new URL(req.url).searchParams
+ const prompt = params.get('prompt') ?? ''
+
+ // Get the output as a stream
+ const output = await session.run(prompt, { stream: true })
+
+ const headers = new Headers({
+ 'Content-Type': 'text/event-stream',
+ Connection: 'keep-alive',
+ })
+
+ // Create a stream
+ const stream = new ReadableStream({
+ async start(controller) {
+ const encoder = new TextEncoder()
+
+ try {
+ for await (const chunk of output) {
+ controller.enqueue(encoder.encode(chunk.response ?? ''))
+ }
+ } catch (err) {
+ console.error('Stream error:', err)
+ } finally {
+ controller.close()
+ }
+ },
+ })
+
+ // Return the stream to the user
+ return new Response(stream, {
+ headers,
+ })
+ })
+ ```
+
+1. Execute the function
+
+ ```
+ curl --get "http://localhost:54321/functions/v1/ollama-test" \
+ --data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
+ -H "Authorization: $ANON_KEY"
+ ```
+
+### Deploying to production
+
+Once the function is working locally, it's time to deploy to production.
+
+1. Deploy a Ollama server and set a function secret called `AI_INFERENCE_API_HOST` to point to the deployed Ollama server
+
+ ```
+ supabase secrets set AI_INFERENCE_API_HOST=https://path-to-your-ollama-server/
+ ```
+
+2. Deploy the Supabase function
+
+ ```
+ supabase functions deploy ollama-test
+ ```
+
+3. Execute the function
+
+ ```
+ curl --get "https://project-ref.supabase.co/functions/v1/ollama-test" \
+ --data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
+ -H "Authorization: $ANON_KEY"
+ ```
+
+As demonstrated in the video above, running Ollama locally is typically slower than running it in on a server with dedicated GPUs. We are collaborating with the Ollama team to improve local performance.
+
+In the future, a hosted Ollama API, will be provided as part of the Supabase platform. Supabase will scale and manage the API and GPUs for you. To sign up for early access, fill up [this form](https://forms.supabase.com/supabase.ai-llm-early-access).
diff --git a/apps/docs/content/guides/functions/examples/semantic-search.mdx b/apps/docs/content/guides/functions/examples/semantic-search.mdx
new file mode 100644
index 00000000000..b42077dd395
--- /dev/null
+++ b/apps/docs/content/guides/functions/examples/semantic-search.mdx
@@ -0,0 +1,129 @@
+---
+id: 'function-ai-models'
+title: 'Semantic Search'
+description: 'Semantic Search with pgvector and Supabase Edge Functions'
+subtitle: 'Semantic Search with pgvector and Supabase Edge Functions'
+tocVideo: 'w4Rr_1whU-U'
+---
+
+[Semantic search](/docs/guides/ai/semantic-search) interprets the meaning behind user queries rather than exact [keywords](/docs/guides/ai/keyword-search). It uses machine learning to capture the intent and context behind the query, handling language nuances like synonyms, phrasing variations, and word relationships.
+
+Since Supabase Edge Runtime [v1.36.0](https://github.com/supabase/edge-runtime/releases/tag/v1.36.0) you can run the [`gte-small` model](https://huggingface.co/Supabase/gte-small) natively within Supabase Edge Functions without any external dependencies! This allows you to easily generate text embeddings without calling any external APIs!
+
+In this tutorial you're implementing three parts:
+
+1. A [`generate-embedding`](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts) database webhook edge function which generates embeddings when a content row is added (or updated) in the [`public.embeddings`](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/migrations/20240408072601_embeddings.sql) table.
+2. A [`query_embeddings` Postgres function](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/migrations/20240410031515_vector-search.sql) which allows us to perform similarity search from an egde function via [Remote Procedure Call (RPC)](https://supabase.com/docs/guides/database/functions?language=js).
+3. A [`search` edge function](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/functions/search/index.ts) which generates the embedding for the search term, performs the similarity search via RPC function call, and returns the result.
+
+You can find the complete example code on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions)
+
+### Create the database table and webhook
+
+Given the [following table definition](https://github.com/supabase/supabase/blob/master/examples/ai/edge-functions/supabase/migrations/20240408072601_embeddings.sql):
+
+```sql
+create extension if not exists vector with schema extensions;
+
+create table embeddings (
+ id bigint primary key generated always as identity,
+ content text not null,
+ embedding vector (384)
+);
+alter table embeddings enable row level security;
+
+create index on embeddings using hnsw (embedding vector_ip_ops);
+```
+
+You can deploy the [following edge function](https://github.com/supabase/supabase/blob/master/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts) as a [database webhook](/docs/guides/database/webhooks) to generate the embeddings for any text content inserted into the table:
+
+```tsx
+const model = new Supabase.ai.Session('gte-small')
+
+Deno.serve(async (req) => {
+ const payload: WebhookPayload = await req.json()
+ const { content, id } = payload.record
+
+ // Generate embedding.
+ const embedding = await model.run(content, {
+ mean_pool: true,
+ normalize: true,
+ })
+
+ // Store in database.
+ const { error } = await supabase
+ .from('embeddings')
+ .update({ embedding: JSON.stringify(embedding) })
+ .eq('id', id)
+ if (error) console.warn(error.message)
+
+ return new Response('ok')
+})
+```
+
+## Create a Postgres Function and RPC
+
+With the embeddings now stored in your Postgres database table, you can query them from Supabase Edge Functions by utilizing [Remote Procedure Calls (RPC)](/docs/guides/database/functions?language=js).
+
+Given the [following Postgres Function](https://github.com/supabase/supabase/blob/master/examples/ai/edge-functions/supabase/migrations/20240410031515_vector-search.sql):
+
+```sql
+-- Matches document sections using vector similarity search on embeddings
+--
+-- Returns a setof embeddings so that we can use PostgREST resource embeddings (joins with other tables)
+-- Additional filtering like limits can be chained to this function call
+create or replace function query_embeddings(embedding vector(384), match_threshold float)
+returns setof embeddings
+language plpgsql
+as $$
+begin
+ return query
+ select *
+ from embeddings
+
+ -- The inner product is negative, so we negate match_threshold
+ where embeddings.embedding <#> embedding < -match_threshold
+
+ -- Our embeddings are normalized to length 1, so cosine similarity
+ -- and inner product will produce the same query results.
+ -- Using inner product which can be computed faster.
+ --
+ -- For the different distance functions, see https://github.com/pgvector/pgvector
+ order by embeddings.embedding <#> embedding;
+end;
+$$;
+```
+
+## Query vectors in Supabase Edge Functions
+
+You can use `supabase-js` to first generate the embedding for the search term and then invoke the Postgres function to find the relevant results from your stored embeddings, right from your [Supabase Edge Function](https://github.com/supabase/supabase/blob/master/examples/ai/edge-functions/supabase/functions/search/index.ts):
+
+```tsx
+const model = new Supabase.ai.Session('gte-small')
+
+Deno.serve(async (req) => {
+ const { search } = await req.json()
+ if (!search) return new Response('Please provide a search param!')
+ // Generate embedding for search term.
+ const embedding = await model.run(search, {
+ mean_pool: true,
+ normalize: true,
+ })
+
+ // Query embeddings.
+ const { data: result, error } = await supabase
+ .rpc('query_embeddings', {
+ embedding,
+ match_threshold: 0.8,
+ })
+ .select('content')
+ .limit(3)
+ if (error) {
+ return Response.json(error)
+ }
+
+ return Response.json({ search, result })
+})
+```
+
+That's it, you now have AI powered semantic search set up without any external dependencies! Just you, pgvector, and Supabase Edge Functions!
diff --git a/apps/docs/content/guides/functions/routing.mdx b/apps/docs/content/guides/functions/routing.mdx
index ab5496f0c41..193505e5c16 100644
--- a/apps/docs/content/guides/functions/routing.mdx
+++ b/apps/docs/content/guides/functions/routing.mdx
@@ -58,7 +58,7 @@ You can deploy the function to Supabase via:
supabase functions deploy hello-world
```
-Once the function is deployed, you can try to call the two ednpoints using cURL (or Postman).
+Once the function is deployed, you can try to call the two endpoints using cURL (or Postman).
```bash
# https://supabase.com/docs/guides/functions/deploy#invoking-remote-functions
diff --git a/apps/docs/hooks/useHash.ts b/apps/docs/hooks/useHash.ts
index 3c6f9567dab..29824c1742b 100644
--- a/apps/docs/hooks/useHash.ts
+++ b/apps/docs/hooks/useHash.ts
@@ -1,12 +1,17 @@
import { useState, useCallback, useEffect } from 'react'
const useHash = () => {
- const [hash, setHash] = useState(() =>
- typeof window !== 'undefined' ? window.location.hash.split('#')[1] : undefined
- )
+ const [hash, setHash] = useState(() => {
+ if (typeof window !== 'undefined') {
+ const url = new URL(window.location.href)
+ return url.hash.substring(1) // Removes the leading '#'
+ }
+ return undefined
+ })
const hashChangeHandler = useCallback(() => {
- setHash(window.location.hash.split('#')[1])
+ const url = new URL(window.location.href)
+ setHash(url.hash.substring(1))
}, [])
useEffect(() => {
@@ -18,7 +23,9 @@ const useHash = () => {
const updateHash = useCallback(
(newHash) => {
- if (newHash !== hash) window.location.hash = newHash
+ if (newHash !== hash) {
+ window.location.hash = newHash
+ }
},
[hash]
)
diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyEditor/PolicyRoles.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyEditor/PolicyRoles.tsx
index c8373a59039..4586d246c21 100644
--- a/apps/studio/components/interfaces/Auth/Policies/PolicyEditor/PolicyRoles.tsx
+++ b/apps/studio/components/interfaces/Auth/Policies/PolicyEditor/PolicyRoles.tsx
@@ -43,7 +43,7 @@ const PolicyRoles = ({ selectedRoles, onUpdateSelectedRoles }: PolicyRolesProps)
+
+## Generating Text Embeddings in Edge Functions
+
+Embeddings capture the "relatedness" of text, images, video, or other types of information. Embeddings are stored in the database as an array of floating point numbers, known as vectors. Since we [released](/openai-embeddings-postgres-vector) pgvector on the platform, Postgres has become a popular vector database.
+
+Today's release solves a few technical challenges for developers who want to generate embeddings from the content in their database, giving them the ability to offload this compute-intensive task to background workers.
+
+### Integrated pgvector experience
+
+You can now utilize [database webhooks](/docs/guides/database/webhooks) to automatically generate embeddings whenever a new row is inserted into a database table.
+
+
+
+Because embedding creation is a compute-intensive task, it makes sense to offload the work from your database. Edge Functions are the perfect “background worker”. We've created a simple example to show how you can generate embeddings in Edge Functions: [Semantic Search with pgvector and Supabase Edge Functions](/docs/guides/functions/examples/semantic-search).
+
+### Technical architecture
+
+Embedding generation uses the [ONNX runtime](https://onnxruntime.ai/) under the hood. This is a cross-platform inferencing library that supports multiple execution providers from CPU to specialized GPUs.
+
+Libraries like `transformers.js` also use ONNX runtime which, in the context of Edge Functions, runs as a WASM module, which can be slow during the instantiation process.
+
+To solve this, we built a native extension in Edge Runtime that enables using ONNX runtime via the Rust interface. This was made possible thanks to an excellent Rust wrapper called [Ort](https://github.com/pykeio/ort):
+
+
+
+Embedding generation is fairly lightweight compared to LLM workloads, so it can run on a CPU without hardware acceleration.
+
+### Availability: open source embeddings
+
+Embeddings models are available on Edge Functions today. We currently support [`gte-small`](https://huggingface.co/Supabase/gte-small) and we'll add more embeddings models based on user feedback.
+
+Embedding generation via `Supabase.ai` API is available today for all Edge Functions users in both local, hosted, and self-hosted platforms.
+
+### Lower costs
+
+Generating embeddings in an Edge Function doesn't cost anything extra: we still charge on CPU usage. A typical embedding generation request should run in less than a 1s, even from a cold start. Typically it won't use more than 100-200ms of CPU time.
+
+Proprietary LLMs like OpenAI and Claude provide [APIs](https://platform.openai.com/docs/guides/embeddings) to generate text embeddings, charging per token. For example, OpenAI's `text-embedding-3-small` cost $0.02/1M tokens at the time of writing this post.
+
+Open source text embedding models provide similar performance to OpenAI's paid models. For example, the `gte-small` model, which operates on 384 dimensions, has an average of 61.36 compared to OpenAI's `text-embedding-3-small`, which is at 62.26 on the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard), and they perform search faster with [fewer dimensions](https://supabase.com/blog/fewer-dimensions-are-better-pgvector).
+
+With Supabase Edge Functions, you can generate text embeddings 10x cheaper than OpenAI embeddings APIs.
+
+## Large Language Models in Supabase Edge Functions
+
+Embedding generation only a part of the solution. Typically you need an LLM (like OpenAI's GPT-3.5) to generate human-like interactions. We're working with Ollama to make this possible with Supabase: local development, self-hosted, and on the platform.
+
+### Open source inference models
+
+We are excited to announce experimental support for Llama & Mistral with `Supabase.ai` API.
+
+
+
+The API is simple to use, with support for streaming responses:
+
+```jsx
+const session = new Supabase.ai.Session('mistral')
+
+Deno.serve(async (req: Request) => {
+ // Get the prompt from the query string
+ const params = new URL(req.url).searchParams
+ const prompt = params.get('prompt') ?? ''
+
+ // Get the output as a stream
+ const output = await session.run(prompt, { stream: true })
+
+ // Create a stream
+ const stream = new ReadableStream({
+ async start(controller) {
+ const encoder = new TextEncoder()
+ for await (const chunk of output) {
+ controller.enqueue(encoder.encode(chunk.response ?? ''))
+ }
+ },
+ })
+
+ // Return the stream to the user
+ return new Response(stream, {
+ headers: new Headers({
+ 'Content-Type': 'text/event-stream',
+ Connection: 'keep-alive',
+ }),
+ })
+})
+```
+
+Check out the full guide [here](https://supabase.com/docs/guides/functions/ai-models#using-large-language-models).
+
+### Technical architecture
+
+LLM models are challenging to run directly via ONNX runtime on CPU. For these, we are using a GPU-accelerated [Ollama](https://ollama.com/) server under the hood:
+
+
+
+We think this is a great match: the Ollama team have worked hard to ensure that the local development experience is great, and we love development environments that can be run without internet access (for those who enjoy programming on planes).
+
+As a Supabase developer, you don't have to worry about deploying models and managing GPU instances - simply use a serverless API to get your job done.
+
+### Availability: open source embeddings
+
+Access to open-source LLMs is currently invite-only while we manage demand for the GPU instances. Please [get in touch](https://forms.supabase.com/supabase.ai-llm-early-access) if you need early access.
+
+## Extending model support
+
+We plan to extend support for more models. [Let us know](https://forms.supabase.com/supabase.ai-llm-early-access) which models you want next. We're looking to support fine-tuned models too!
+
+## Getting started
+
+Check out the Supabase docs today to get started with the AI models:
+
+- Edge Functions: [supabase.com/docs/guides/functions](https://supabase.com/docs/guides/functions)
+- Vectors: [supabase.com/docs/guides/ai](https://supabase.com/docs/guides/ai)
+- [Semantic search demo](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions)
+- [Store and query embeddings](/docs/guides/ai/vector-columns#querying-a-vector--embedding) in Postgres and use them for Retrieval Augmented Generation (RAG) and Semantic Search
diff --git a/apps/www/components/Footer/index.tsx b/apps/www/components/Footer/index.tsx
index f921bf1d4f5..2625e44407a 100644
--- a/apps/www/components/Footer/index.tsx
+++ b/apps/www/components/Footer/index.tsx
@@ -33,7 +33,7 @@ const Footer = (props: Props) => {
className={cn(
'bg-alternative',
isLaunchWeek && 'bg-[#060809]',
- isGAWeek && 'dark:bg-[#060809]',
+ isGAWeek && 'dark:bg-alternative',
props.className
)}
aria-labelledby="footerHeading"
diff --git a/apps/www/components/Hero/Hero.tsx b/apps/www/components/Hero/Hero.tsx
index 8c305464e56..8f9d8a6d1ca 100644
--- a/apps/www/components/Hero/Hero.tsx
+++ b/apps/www/components/Hero/Hero.tsx
@@ -23,8 +23,8 @@ const Hero = () => {