)}
{/* // parameters */}
diff --git a/apps/docs/docs/ref/csharp/release-notes.mdx b/apps/docs/docs/ref/csharp/release-notes.mdx
index 777621307dd..291636cfb08 100644
--- a/apps/docs/docs/ref/csharp/release-notes.mdx
+++ b/apps/docs/docs/ref/csharp/release-notes.mdx
@@ -3,6 +3,174 @@ id: release-notes
title: Release Notes
---
+## 0.11.0 - 2023-05-24
+
+- Update dependency: postgrest-csharp@3.2.0
+ - General codebase and QOL improvements. Exceptions are generally thrown through `PostgrestException` now instead
+ of `Exception`. A `FailureHint.Reason` is provided with failures if possible to parse.
+ - `AddDebugListener` is now available on the client to help with debugging
+ - Merges [#65](https://github.com/supabase-community/postgrest-csharp/pull/65) Cleanup + Add better exception handling
+ - Merges [#66](https://github.com/supabase-community/postgrest-csharp/pull/66) Local test Fixes
+ - Fixes [#67](https://github.com/supabase-community/postgrest-csharp/issues/67) Postgrest Reference attribute is
+ producing StackOverflow for circular references
+- Update dependency: gotrue-csharp@4.0.2
+ - [#58](https://github.com/supabase-community/gotrue-csharp/issues/58) - Add support for the `reauthentication` endpoint which allows for secure password changes.
+- Update dependency: realtime-csharp@6.0.1
+ - Updates publishing action for future packages, includes README and icon.
+ - Merges [#28](https://github.com/supabase-community/realtime-csharp/pull/28) and [#30](https://github.com/supabase-community/realtime-csharp/pull/30)
+ - The realtime client now takes a "fail-fast" approach. On establishing an initial connection, client will throw
+ a `RealtimeException` in `ConnectAsync()` if the socket server is unreachable. After an initial connection has been
+ established, the **client will continue attempting reconnections indefinitely until disconnected.**
+ - [Major, New] C# `EventHandlers` have been changed to `delegates`. This should allow for cleaner event data access over
+ the previous subclassed `EventArgs` setup. Events are scoped accordingly. For example, the `RealtimeSocket` error
+ handlers will receive events regarding socket connectivity; whereas the `RealtimeChannel` error handlers will receive
+ events according to `Channel` joining/leaving/etc. This is implemented with the following methods prefixed by (
+ Add/Remove/Clear):
+ - `RealtimeBroadcast.AddBroadcastEventHandler`
+ - `RealtimePresence.AddPresenceEventHandler`
+ - `RealtimeSocket.AddStateChangedHandler`
+ - `RealtimeSocket.AddMessageReceivedHandler`
+ - `RealtimeSocket.AddHeartbeatHandler`
+ - `RealtimeSocket.AddErrorHandler`
+ - `RealtimeClient.AddDebugHandler`
+ - `RealtimeClient.AddStateChangedHandler`
+ - `RealtimeChannel.AddPostgresChangeHandler`
+ - `RealtimeChannel.AddMessageReceivedHandler`
+ - `RealtimeChannel.AddErrorHandler`
+ - `Push.AddMessageReceivedHandler`
+ - [Major, new] `ClientOptions.Logger` has been removed in favor of `Client.AddDebugHandler()` which allows for
+ implementing custom logging solutions if desired.
+ - A simple logger can be set up with the following:
+ ```c#
+ client.AddDebugHandler((sender, message, exception) => Debug.WriteLine(message));
+ ```
+ - [Major] `Connect()` has been marked `Obsolete` in favor of `ConnectAsync()`
+ - Custom reconnection logic has been removed in favor of using the built-in logic from `Websocket.Client@4.6.1`.
+ - Exceptions that are handled within this library have been marked as `RealtimeException`s.
+ - The local, docker-composed test suite has been brought back (as opposed to remotely testing on live supabase servers)
+ to test against.
+ - Comments have been added throughout the entire codebase and an `XML` file is now generated on build.
+
+## 0.10.0 - 2023-05-14
+
+- Changes options to require `Supabase.SupabaseOptions.SessionPersistor` from using `ISupabaseSessionHandler`
+ to `IGotrueSessionPersistance` (these are now synchronous operations).
+- Update dependency: gotrue-csharp@4.0.1
+ - [#60](https://github.com/supabase-community/gotrue-csharp/pull/60) - Add interfaces, bug fixes, additional error
+ reason detection. Thanks [@wiverson](https://github.com/wiverson)!
+ - [#57](https://github.com/supabase-community/gotrue-csharp/pull/57) Refactor exceptions, code cleanup, and move to
+ delegate auth state changes
+ - Huge thank you to [@wiverson](https://github.com/wiverson) for his help on this refactor and release!
+ - Changes
+ - Exceptions have been simplified to a single `GotrueException`. A `Reason` field has been added
+ to `GotrueException` to clarify what happened. This should also be easier to manage as the Gotrue
+ server API & messages evolve.
+ - The session delegates for `Save`/`Load`/`Destroy` have been simplified to no longer require `async`.
+ - Console logging in a few places (most notable the background refresh thread) has been removed
+ in favor of a notification method. See `Client.AddDebugListener()` and the test cases for examples.
+ This will allow you to implement your own logging strategy (write to temp file, console, user visible
+ err console, etc).
+ - The client now more reliably emits AuthState changes.
+ - There is now a single source of truth for headers in the stateful Client - the `Options` headers.
+ - New feature:
+ - Added a `Settings` request to the stateless API only - you can now query the server instance to
+ determine if it's got the settings you need. This might allow for things like a visual
+ component in a tool to verify the GoTrue settings are working correctly, or tests that run differently
+ depending on the server configuration.
+ - Implementation notes:
+ - Test cases have been added to help ensure reliability of auth state change notifications
+ and persistence.
+ - Persistence is now managed via the same notifications as auth state change
+
+## 0.9.1 - 2023-04-28
+
+- Update dependency: gotrue-csharp@3.1.1
+ - Implements `SignInWithIdToken` for Apple/Google signing from LW7. A HUGE thank you
+ to [@wiverson](https://github.com/wiverson)!
+- Update dependency: realtime-csharp@5.0.5
+ - Re: [#27](https://github.com/supabase-community/realtime-csharp/issues/27) `PostgresChangesOptions` was not
+ setting `listenType` in constructor. Thanks [@Kuffs2205](https://github.com/Kuffs2205)
+- Update dependency: supabase-storage-csharp@1.2.10
+ - Re: [#7](https://github.com/supabase-community/storage-csharp/issues/7) Implements a `DownloadPublicFile` method.
+
+## 0.9.0 - 2023-04-12
+
+- Update dependency: gotrue-csharp@3.1.0
+
+ - [Minor] Implements PKCE auth flow. SignIn using a provider now returns an instance of `ProviderAuthState` rather
+ than a `string`.
+
+- Update dependency: supabase-storage-csharp@1.2.9
+ - Implements storage features from LW7:
+ - feat: custom file size limit and mime types at bucket
+ level [supabase/storage-js#151](https://github.com/supabase/storage-js/pull/151) file size and mime type
+ limits per bucket
+ - feat: quality option, image
+ transformation [supabase/storage-js#145](https://github.com/supabase/storage-js/pull/152) quality option for
+ image transformations
+ - feat: format option for webp
+ support [supabase/storage-js#142](https://github.com/supabase/storage-js/pull/142) format option for image
+ transformation
+
+## 0.8.8 - 2023-03-29
+
+- Update dependency: gotrue-csharp@3.0.6
+ - Supports adding `SignInOptions` (i.e. `RedirectTo`) on `OAuth Provider` SignIn requests.
+
+## 0.8.7 - 2023-03-23
+
+- Update dependency: realtime-csharp@5.0.4
+ - Re: [#26](https://github.com/supabase-community/realtime-csharp/pull/26) - Fixes Connect() not returning callback
+ result when the socket isn't null. Thanks [@BlueWaterCrystal](https://github.com/BlueWaterCrystal)!
+
+## 0.8.6 - 2023-03-23
+
+- Update dependency: supabase-storage-csharp@1.2.8
+ - [Merge #5](https://github.com/supabase-community/storage-csharp/pull/5) Added search string as an optional search
+ parameter. Thanks [@ElectroKnight22](https://github.com/ElectroKnight22)!
+
+## 0.8.5 - 2023-03-10
+
+- Update dependency: realtime-csharp@5.0.3
+ - Re: [#25](https://github.com/supabase-community/realtime-csharp/issues/25) - Support Channel being resubscribed
+ after having been unsubscribed, fixes rejoin timer being erroneously called on channel `Unsubscribe`.
+ Thanks [@Kuffs2205](https://github.com/Kuffs2205)!
+
+## 0.8.4 - 2023-03-03
+
+- Update dependency: supabase-storage-csharp@1.2.7
+ - Re: [#4](https://github.com/supabase-community/storage-csharp/issues/4) Implementation for `ClientOptions` which
+ supports specifying Upload, Download, and Request timeouts.
+- Update dependency: realtime-csharp@5.0.2
+ - Re: [#24](https://github.com/supabase-community/realtime-csharp/issues/24) - Fixes join failing until reconnect
+ happened + adds access token push on channel join. Big thank you to [@Honeyhead](https://github.com/honeyhead) for
+ the help debugging and identifying!
+
+## 0.8.3 - 2023-02-26
+
+- Update dependency: supabase-storage-csharp@1.2.5
+ - Provides fix
+ for [supabase-community/supabase-csharp#54](https://github.com/supabase-community/supabase-csharp/issues/54) -
+ Dynamic headers were always being overwritten by initialized token headers, so the storage client would not
+ receive user's access token as expected.
+ - Provides fix for upload progress not reporting
+ in [supabase-community/storage-csharp#3](https://github.com/supabase-community/storage-csharp/issues/3)
+- Update dependency: gotrue-csharp@3.0.5
+ - Fixes [#44](https://github.com/supabase-community/gotrue-csharp/issues/44) - refresh timer should automatically
+ reattempt (interval of 5s) for HTTP exceptions - gracefully exits on invalid refresh and triggers
+ an `AuthState.Changed` event
+
+## 0.8.2 - 2023-02-26
+
+- Update dependency: supabase-storage-csharp@1.2.4
+ - `UploadOrUpdate` now appropriately throws request exceptions
+
+## 0.8.1 - 2023-02-06
+
+- Update dependency: realtime-csharp@5.0.1
+ - Re: [#22](https://github.com/supabase-community/realtime-csharp/issues/22) - `SerializerSettings` were not being
+ passed to `PostgresChangesResponse` - Thanks [@Shenrak](https://github.com/Shenrak) for the help debugging!
+
## 0.8.0 - 2023-01-31
- Update dependency: realtime-csharp@5.0.0
diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx
index ff35c1af441..adc866288b0 100644
--- a/apps/docs/layouts/SiteLayout.tsx
+++ b/apps/docs/layouts/SiteLayout.tsx
@@ -46,6 +46,10 @@ const levelsData = {
icon: '/docs/img/icons/menu/storage',
name: 'Storage',
},
+ ai: {
+ icon: '/docs/img/icons/menu/ai',
+ name: 'AI & Vectors',
+ },
supabase_cli: {
icon: '/docs/img/icons/menu/reference-cli',
name: 'Supabase CLI',
diff --git a/apps/docs/pages/guides/ai.mdx b/apps/docs/pages/guides/ai.mdx
new file mode 100644
index 00000000000..950464008f4
--- /dev/null
+++ b/apps/docs/pages/guides/ai.mdx
@@ -0,0 +1,108 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai',
+ title: 'AI & Vectors',
+ description: 'Use Supabase to store and search embedding vectors.',
+ subtitle: 'Use Supabase to store and search embedding vectors.',
+ sidebar_label: 'Overview',
+}
+
+Supabase provides a number of tools to get started with Vectors and Embeddings. These tools include:
+
+- An embedding store using the popular [pgvector](https://github.com/pgvector/pgvector/)
+- A Python Library for managing unstructured collections called [Vecs](https://supabase.github.io/vecs/api)
+
+## Features
+
+Supabase provides a full toolkit for developing AI applications.
+
+- A [vector store](/docs/guides/ai/vector-columns) and embeddings support using pgvector.
+- A [Python client](/docs/guides/ai/vecs-python-client) for managing unstructured embeddings.
+- [Database migrations](/docs/guides/ai/examples/headless-vector-search#prepare-your-database) for managing structured embeddings.
+- Integrations with all popular AI providers, such as [OpenAI](/docs/guides/ai/examples/openai), [Hugging Face](/docs/guides/ai/hugging-face), [LangChain](/docs/guides/ai/langchain), and more.
+
+## Examples
+
+Check out all of the AI [templates and examples](https://github.com/supabase/supabase/tree/master/examples/ai) in our GitHub repository.
+
+
+
+export const customers = [
+ {
+ name: 'Mendable switches from Pinecone to Supabase for PostgreSQL vector embeddings',
+ description:
+ 'How Mendable boosts efficiency and accuracy of chat powered search for documentation using Supabase with pgvector',
+ href: 'https://supabase.com/customers/mendableai',
+ },
+ {
+ name: 'Markprompt: GDPR-Compliant AI Chatbots for Docs and Websites',
+ description:
+ "AI-powered chatbot platform, Markprompt, empowers developers to deliver efficient and GDPR-compliant prompt experiences on top of their content, by leveraging Supabase's secure and privacy-focused database and authentication solutions",
+ href: 'https://supabase.com/customers/markprompt',
+ },
+]
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/concepts.mdx b/apps/docs/pages/guides/ai/concepts.mdx
new file mode 100644
index 00000000000..7a62aa7e8cf
--- /dev/null
+++ b/apps/docs/pages/guides/ai/concepts.mdx
@@ -0,0 +1,67 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-concepts',
+ title: 'Concepts',
+ description: 'Learn about embeddings within AI and vector applications.',
+ sidebar_label: 'Concepts',
+}
+
+Embeddings are a core concept when building AI and vector applications.
+
+## What are embeddings?
+
+Embeddings capture the "relatedness" of text, images, video, or other types of information. This relatedness is most commonly used for:
+
+- **Search:** how similar is a search term to a body of text?
+- **Recommendations:** how similar are two products?
+- **Classifications:** how do we categorize a body of text?
+- **Clustering:** how do we identify trends?
+
+Let's explore an example of text embeddings. Say we have three phrases:
+
+1. "The cat chases the mouse"
+2. "The kitten hunts rodents"
+3. "I like ham sandwiches"
+
+Your job is to group phrases with similar meaning. If you are a human, this should be obvious. Phrases 1 and 2 are almost identical, while phrase 3 has a completely different meaning.
+
+Although phrases 1 and 2 are similar, they share no common vocabulary (besides "the"). Yet their meanings are nearly identical. How can we teach a computer that these are the same?
+
+## Human language
+
+Humans use words and symbols to communicate language. But words in isolation are mostly meaningless - we need to draw from shared knowledge & experience in order to make sense of them. The phrase “You should Google it” only makes sense if you know that Google is a search engine and that people have been using it as a verb.
+
+In the same way, we need to train a neural network model to understand human language. An effective model should be trained on millions of different examples to understand what each word, phrase, sentence, or paragraph could mean in different contexts.
+
+So how does this relate to embeddings?
+
+## How do embeddings work?
+
+Embeddings compress discrete information (words & symbols) into distributed continuous-valued data (vectors). If we took our phrases from before and plot them on a chart, it might look something like this:
+
+
+
+Phrases 1 and 2 would be plotted close to each other, since their meanings are similar. We would expect phrase 3 to live somewhere far away since it isn't related. If we had a fourth phrase, “Sally ate Swiss cheese”, this might exist somewhere between phrase 3 (cheese can go on sandwiches) and phrase 1 (mice like Swiss cheese).
+
+In this example we only have 2 dimensions: the X and Y axis. In reality, we would need many more dimensions to effectively capture the complexities of human language.
+
+## Using embeddings
+
+Compared to our 2-dimensional example above, most embedding models will output many more dimensions. For example OpenAI's `text-embedding-ada-002` model outputs 1536 dimensions.
+
+Why is this useful? Once we have generated embeddings on multiple texts, it is trivial to calculate how similar they are using vector math operations like cosine distance. A common use case for this is search. Your process might look something like this:
+
+1. Pre-process your knowledge base and generate embeddings for each page
+2. Store your embeddings to be referenced later (more on this)
+3. Build a search page that prompts your user for input
+4. Take user's input, generate a one-time embedding, then perform a similarity search against your pre-processed embeddings.
+5. Return the most similar pages to the user
+
+## See also
+
+- [Structured and Unstructured embeddings](/docs/guides/ai/structured-unstructured-embeddings)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/engineering-for-scale.mdx b/apps/docs/pages/guides/ai/engineering-for-scale.mdx
new file mode 100644
index 00000000000..a74bfe39d50
--- /dev/null
+++ b/apps/docs/pages/guides/ai/engineering-for-scale.mdx
@@ -0,0 +1,127 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-engineering-for-scale',
+ title: 'Engineering for Scale',
+ description: 'Building an enterprise-grade vector architecture',
+ subtitle: 'Building an enterprise-grade vector architecture.',
+ sidebar_label: 'Engineering for Scale',
+}
+
+Content sources for vectors can be extremely large. As you grow you should run your Vector workloads across several secondary databases (sometimes called "pods"), which allows each collection to scale independently.
+
+## Simple workloads
+
+For small workloads it's typical to store your data in a single database.
+
+If you've used [Vecs](/docs/guides/ai/vecs-python-client) to create 3 different collections, you can expose collections to your web or mobile application using [views](/docs/guides/database/tables#views):
+
+
+
+For example, with 3 collections, called `docs`, `posts`, and `images`, we could expose the "docs" inside the public schema like this:
+
+```sql
+create view public.docs as
+select
+ id,
+ embedding,
+ metadata, # Expose the metadata as JSON
+ (metadata->>'url')::text as url # Extract the URL as a string
+from vector
+```
+
+You can then use any of the client libraries to access your collections within your applications:
+
+{/* prettier-ignore */}
+```js
+const { data, error } = await supabase
+ .from('docs')
+ .select('id, embedding, metadata')
+ .eq('url', '/hello-world')
+```
+
+## Enterprise workloads
+
+As you move into production, we recommend running splitting your collections into separate projects. This is because it allows your vector stores to scale independently of your production data. Vectors typically grow faster than operational data, and they have different resource requirements. Running them on separate databases removes the single-point-of-failure.
+
+
+
+You can use as many secondary databases as you need to manage your collections. With this architecture, you have 2 options for accessing collections within your application:
+
+1. Query the collections directly using Vecs.
+2. Access the collections from your Primary database through a Wrapper.
+
+You can use both of these in tandem to suit your use-case. We recommend option `1` wherever possible, as it offers the most scalability.
+
+### Query collections using Vecs
+
+Vecs provides methods for querying collections, either using a [cosine similarity function](https://supabase.github.io/vecs/api/#basic) or with [metadata filtering](https://supabase.github.io/vecs/api/#metadata-filtering).
+
+```python
+# cosine similarity
+docs.query(query_vector=[0.4,0.5,0.6], limit=5)
+
+# metadata filtering
+docs.query(
+ query_vector=[0.4,0.5,0.6],
+ limit=5,
+ filters={"year": {"$eq": 2012}}, # metadata filters
+)
+```
+
+### Accessing external collections using Wrappers
+
+Supabase supports [Foreign Data Wrappers](/blog/postgres-foreign-data-wrappers-rust). Wrappers allow you connect two databases together so that you can query them over the network.
+
+This involves 2 steps: connecting to your remote database from the primary, and creating a Foreign Table.
+
+#### Connecting your remote database
+
+Inside your Primary database we need to provide the credentials to access the secondary database:
+
+```sql
+create extension postgres_fdw;
+
+create server docs_server
+foreign data wrapper postgres_fdw
+options (host 'db.xxx.supabase.co', port '5432', dbname 'postgres');
+
+create user mapping for docs_user
+server docs_server
+options (user 'postgres', password 'password');
+```
+
+#### Create a foreign table
+
+We can now create a foreign table to access the data in our secondary project.
+
+```sql
+create foreign table docs (
+ id text not null,
+ embedding vector(1536),
+ metadata jsonb,
+ url text
+)
+server docs_server
+options (schema_name 'public', table_name 'docs');
+```
+
+This looks very similar to our View example above, and you can continue to use the client libraries to access your collections through the foreign table:
+
+{/* prettier-ignore */}
+```js
+const { data, error } = await supabase
+ .from('docs')
+ .select('id, embedding, metadata')
+ .eq('url', '/hello-world')
+```
+
+### Enterprise architecture
+
+This diagram provides an example architecture, allowing you to access the collections either with our client libraries or using Vecs. You can add as many secondary databases as you need, in this example we show one only:
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/examples/building-chatgpt-plugins.mdx b/apps/docs/pages/guides/ai/examples/building-chatgpt-plugins.mdx
new file mode 100644
index 00000000000..1f34a54c413
--- /dev/null
+++ b/apps/docs/pages/guides/ai/examples/building-chatgpt-plugins.mdx
@@ -0,0 +1,159 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ title: 'Building ChatGPT plugins',
+ subtitle: 'Use Supabase as a Retrieval Store for your ChatGPT plugin.',
+ breadcrumb: 'AI Examples',
+}
+
+ChatGPT recently released [Plugins](https://openai.com/blog/chatgpt-plugins) which help ChatGPT access up-to-date information, run computations, or use third-party services.
+If you're building a plugin for ChatGPT, you'll probably want to answer questions from a specific source. We can solve this with “retrieval plugins”, which allow ChatGPT to access information from a database.
+
+## What is ChatGPT Retrieval Plugin?
+
+A [Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin) is a Python project designed to inject external data into a ChatGPT conversation. It does a few things:
+
+1. Turn documents into smaller chunks.
+2. Converts chunks into embeddings using OpenAI's `text-embedding-ada-002` model.
+3. Stores the embeddings into a vector database.
+4. Queries the vector database for relevant documents when a question is asked.
+
+It allows ChatGPT to dynamically pull relevant information into conversations from your data sources. This could be PDF documents, Confluence, or Notion knowledge bases.
+
+## Example: Chat with Postgres Docs
+
+Let’s build an example where we can “ask ChatGPT questions” about the Postgres documentation. Although ChatGPT already knows about the Postgres documentation because it is publicly available, this is a simple example which demonstrates how to work with PDF files.
+
+This plugin requires several steps:
+
+1. Download all the [Postgres docs as a PDF](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf)
+2. Convert the docs into chunks of embedded text and store them in Supabase
+3. Run our plugin locally so that we can ask questions about the Postgres docs.
+
+We'll be saving the Postgres documentation in Postgres, and ChatGPT will be retrieving the documentation whenever a user asks a question:
+
+
+
+
+
+### Step 1: Fork the ChatGPT Retrieval Plugin repository
+
+Fork the ChatGPT Retrieval Plugin repository to your GitHub account and clone it to your local machine. Read through the `README.md` file to understand the project structure.
+
+### Step 2: Install dependencies
+
+Choose your desired datastore provider and remove unused dependencies from `pyproject.toml`. For this example, we'll use Supabase. And install dependencies with Poetry:
+
+```bash
+poetry install
+```
+
+### Step 3: Create a Supabase project
+
+Create a [Supabase project](https://supabase.com/dashboard) and database by following the instructions [here](https://supabase.com/docs/guides/platform). Export the environment variables required for the retrieval plugin to work:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=supabase
+export SUPABASE_URL=
+export SUPABASE_SERVICE_ROLE_KEY=
+```
+
+For Postgres datastore, you'll need to export these environment variables instead:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=postgres
+export PG_HOST=
+export PG_PASSWORD=
+```
+
+### Step 4: Run Postgres Locally
+
+To start quicker you may use Supabase CLI to spin everything up locally as it already includes pgvector from the start. Install `supabase-cli`, go to the `examples/providers` folder in the repo and run:
+
+```bash
+supabase start
+```
+
+This will pull all docker images and run supabase stack in docker on your local machine. It will also apply all the necessary migrations to set the whole thing up. You can then use your local setup the same way, just export the environment variables and follow to the next steps.
+
+Using `supabase-cli` is not required and you can use any other docker image or hosted version of PostgresDB that includes `pgvector`. Just make sure you run migrations from `examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql`.
+
+### Step 5: Obtain OpenAI API key
+
+To create embeddings Plugin uses OpenAI API and `text-embedding-ada-002` model. Each time we add some data to our datastore, or try to query relevant information from it, embedding will be created either for inserted data chunk, or for the query itself. To make it work we need to export `OPENAI_API_KEY`. If you already have an account in OpenAI, you just need to go to [User Settings - API keys](https://platform.openai.com/account/api-keys) and Create new secret key.
+
+
+
+### Step 6: Run the plugin
+
+Execute the following command to run the plugin:
+
+```bash
+poetry run dev
+# output
+INFO: Will watch for changes in these directories: ['./chatgpt-retrieval-plugin']
+INFO: Uvicorn running on http://localhost:3333 (Press CTRL+C to quit)
+INFO: Started reloader process [87843] using WatchFiles
+INFO: Started server process [87849]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+The plugin will start on your localhost - port `:3333` by default.
+
+### Step 6: Populating data in the datastore
+
+For this example, we'll upload Postgres documentation to the datastore. Download the [Postgres documentation](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf) and use the `/upsert-file` endpoint to upload it:
+
+```bash
+curl -X POST -F \\"file=@./postgresql-15-US.pdf\\"
+```
+
+The plugin will split your data and documents into smaller chunks automatically. You can view the chunks using the Supabase dashboard or any other SQL client you prefer. For the whole Postgres Documentation I got 7,904 records in my documents table, which is not a lot, but we can try to add index for `embedding` column to speed things up by a little. To do so, you should run the following SQL command:
+
+```sql
+create index on documents
+using ivfflat (embedding vector_ip_ops)
+with (lists = 10);
+```
+
+This will create an index for the inner product distance function. Important to note that it is an approximate index. It will change the logic from performing the exact nearest neighbor search to the approximate nearest neighbor search.
+
+We are using `lists = 10`, because as a general guideline, you should start looking for optimal lists constant value with the formula: `rows / 1000` when you have less than 1 million records in your table.
+
+### Step 7: Using our plugin within ChatGPT
+
+To integrate our plugin with ChatGPT, register it in the ChatGPT dashboard. Assuming you have access to ChatGPT Plugins and plugin development, select the Plugins model in a new chat, then choose "Plugin store" and "Develop your own plugin." Enter `localhost:3333` into the domain input, and your plugin is now part of ChatGPT.
+
+
+
+
+
+You can now ask questions about Postgres and receive answers derived from the documentation.
+
+Let's try it out: ask ChatGPT to find out when to use `check` and when to use `using`. You will be able to see what queries were sent to our plugin and what it responded to.
+
+
+
+And after ChatGPT receives a response from the plugin it will answer your question with the data from the documentation.
+
+
+
+## Resources
+
+- ChatGPT Retrieval Plugin: [github.com/openai/chatgpt-retrieval-plugin](https://github.com/openai/chatgpt-retrieval-plugin)
+- ChatGTP Plugins: [official documentation](https://platform.openai.com/docs/plugins/introduction)
+
+export const Page = ({ children }) =>
+export default Page
diff --git a/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx b/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx
new file mode 100644
index 00000000000..467d3788a38
--- /dev/null
+++ b/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx
@@ -0,0 +1,124 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ title: 'Adding generative Q&A for your documentation',
+ subtitle:
+ 'Learn how to build a ChatGPT-style doc search powered using our headless search toolkit.',
+ breadcrumb: 'AI Examples',
+}
+
+Supabase provides a [Headless Search Toolkit](https://github.com/supabase/headless-vector-search) for adding "Generative Q&A" to your documentation. The toolkit is "headless", so that you can integrate it into your existing website and style it to match your website theme.
+
+You can see how this works with the Supabase docs. Just him `cmd+k` and "ask" for something like "what are the features of supabase?". You will see that the response is streamed back, using the information provided in the docs:
+
+
+
+## Tech stack
+
+- Supabase: Database & Edge Functions.
+- OpenAI: Embeddings and completions.
+- GitHub Actions: for ingesting your markdown docs.
+
+## Toolkit
+
+This toolkit consists of 2 parts:
+
+- The [Headless Vector Search](https://github.com/supabase/headless-vector-search) template which you can deploy in your own organization.
+- A [GitHub Action](https://github.com/supabase/embeddings-generator) which will ingest your markdown files, convert them to embeddings, and store them in your database.
+
+## Usage
+
+There are 3 steps to build similarity search inside your documentation:
+
+1. Prepare your database.
+2. Ingest your documentation.
+3. Add a search interface.
+
+### Prepare your database
+
+To prepare, create a [new Supabase project](https://database.new) and store the database and API credentials, which you can find in the project [settings](https://app.supabase.com/_/settings).
+
+Now we can use the [Headless Vector Search](https://github.com/supabase/headless-vector-search#set-up) instructions to set up the database:
+
+1. Clone the repo to your local machine: `git clone git@github.com:supabase/headless-vector-search.git`
+2. Link the repo to your remote project: `supabase link --project-ref XXX`
+3. Apply the database migrations: `supabase db push`
+4. Set your OpenAI key as a secret: `supabase secrets set OPENAI_KEY=sk-xxx`
+5. Deploy the Edge Functions: `supabase functions deploy --no-verify-jwt`
+6. Expose `docs` schema via API in Supabase Dashboard [settings](https://app.supabase.com/project/_/settings/api) > `API Settings` > `Exposed schemas`
+
+### Ingest your documentation
+
+Now we need to push your documentation into the database as embeddings. You can do this manually, but to make it easier we've created a [GitHub Action](https://github.com/marketplace/actions/supabase-embeddings-generator) which can update your database every time there is a Pull Request.
+
+In your knowledge base repository, create a new action called `.github/workflows/generate_embeddings.yml` with the following content:
+
+```yml
+name: 'generate_embeddings'
+on: # run on main branch changes
+ push:
+ branches:
+ - main
+
+jobs:
+ generate:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: supabase/supabase-embeddings-generator@v0.0.x # Update this to the latest version.
+ with:
+ supabase-url: 'https://your-project-ref.supabase.co' # Update this to your project URL.
+ supabase-service-role-key: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
+ openai-key: ${{ secrets.OPENAI_KEY }}
+ docs-root-path: 'docs' # the path to the root of your md(x) files
+```
+
+Make sure to choose the latest version, and set your `SUPABASE_SERVICE_ROLE_KEY` and `OPENAI_KEY` as repository secrets in your repo settings (settings > secrets > actions).
+
+### Add a search interface
+
+Now inside your docs, you need to create a search interface. Because this is a headless interface, you can use it with any language. The only requirement is that you send the user query to the `query` Edge Function, which will stream an answer back from OpenAI. It might look something like this:
+
+```js
+const onSubmit = (e: Event) => {
+ e.preventDefault()
+ answer.value = ""
+ isLoading.value = true
+
+ const query = new URLSearchParams({ query: inputRef.current!.value })
+ const projectUrl = `https://your-project-ref.functions.supabase.co`
+ const queryURL = `${projectURL}/${query}`
+ const eventSource = new EventSource(queryURL)
+
+ eventSource.addEventListener("error", (err) => {
+ isLoading.value = false
+ console.error(err)
+ })
+
+ eventSource.addEventListener("message", (e: MessageEvent) => {
+ isLoading.value = false
+
+ if (e.data === "[DONE]") {
+ eventSource.close()
+ return
+ }
+
+ const completionResponse: CreateCompletionResponse = JSON.parse(e.data)
+ const text = completionResponse.choices[0].text
+
+ answer.value += text
+ });
+
+ isLoading.value = true
+}
+```
+
+## Resources
+
+- Read about how we built [ChatGPT for the Supabase Docs](https://supabase.com/blog/chatgpt-supabase-docs).
+- Read the pgvector Docs for [Embeddings and vector similarity](/docs/guides/database/extensions/pgvector)
+- See how to build something like this from scratch [using Next.js](/docs/guides/ai/examples/nextjs-vector-search).
+
+export const Page = ({ children }) =>
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx b/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx
similarity index 86%
rename from apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx
rename to apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx
index 087567f9650..1f891655d85 100644
--- a/apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx
+++ b/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx
@@ -1,28 +1,23 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- title: 'Hugging Face',
+ title: 'Generate image captions using Hugging Face',
description:
'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
+ subtitle:
+ 'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
video: 'https://www.youtube.com/v/OgnYxRkxEUw',
tocVideo: 'OgnYxRkxEUw',
}
+We can combine Hugging Face with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) to automatically caption for any image we upload to a storage bucket.
+
+## About Hugging Face
+
[Hugging Face](https://huggingface.co/) is the collaboration platform for the machine learning community.
[Huggingface.js](https://huggingface.co/docs/huggingface.js/index) provides a convenient way to make calls to 100,000+ Machine Learning models, making it easy to incorporate AI functionality into your [Supabase Edge Functions](https://supabase.com/edge-functions).
-Putting this together with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) we can easily put together a service that automatically generates captions for any image we upload to a storage bucket.
-
-
-
-
-
## Setup
- Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects).
diff --git a/apps/docs/pages/guides/ai/examples/image-search-openai-clip.mdx b/apps/docs/pages/guides/ai/examples/image-search-openai-clip.mdx
new file mode 100644
index 00000000000..0ca6fcd19d2
--- /dev/null
+++ b/apps/docs/pages/guides/ai/examples/image-search-openai-clip.mdx
@@ -0,0 +1,177 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-image-search-python',
+ title: 'Image Search with OpenAI CLIP',
+ description: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.',
+ subtitle: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.',
+}
+
+The [OpenAI CLIP Model](https://github.com/openai/CLIP) was trained on a variety of (image, text)-pairs. You can use the CLIP model for:
+
+- Text-to-Image / Image-To-Text / Image-to-Image / Text-to-Text Search
+- You can fine-tune it on your own image and text data with the regular SentenceTransformers training code.
+
+[SentenceTransformers](https://www.sbert.net/examples/applications/image-search/README.html) provides models that allow you to embed images and text into the same vector space. You can use this to find similar images as well as to implement image search.
+
+You can find the full application code as a Python Poetry project on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/image_search#image-search-with-supabase-vector).
+
+## Create a new Python Project with Poetry
+
+[Poetry](https://python-poetry.org/) provides packaging and dependency management for Python. If you haven't already, install poetry via pip:
+
+```shell
+pip install poetry
+```
+
+Then initialize a new project:
+
+```shell
+poetry new image-search
+```
+
+## Setup Supabase project
+
+If you haven't already, [install the Supabase CLI](/docs/guides/cli), then initialize Supabase in the root of your newly created poetry project:
+
+```shell
+supabase init
+```
+
+Next, start your local Supabase stack:
+
+```shell
+supabase start
+```
+
+This will start up the Supabase stack locally and print out a bunch of environtment details, including your local `DB URL`. Make a note of that for later user.
+
+## Install the Dependencies
+
+We will need to add the following dependencies to our project:
+
+- [`vecs`](https://github.com/supabase/vecs#vecs): Supabase Vector Python Client.
+- [`sentence-transformers`](https://huggingface.co/sentence-transformers/clip-ViT-B-32): a framework for sentence, text and image embeddings (used with OpenAI CLIP model)
+- [`matplotlib`](https://matplotlib.org/): for displaying our image result
+
+```shell
+poetry add vecs sentence-transformers matplotlib
+```
+
+## Import the necessary dependencies
+
+At the top of your main python script, import the dependencies and store your `DB URL` from above in a variable:
+
+```python
+from PIL import Image
+from sentence_transformers import SentenceTransformer
+import vecs
+from matplotlib import pyplot as plt
+from matplotlib import image as mpimg
+
+DB_CONNECTION = "postgresql://postgres:postgres@localhost:54322/postgres"
+```
+
+## Create embeddings for your images
+
+In the root of your project, create a new folder called `images` and add some images. You can use the images from the example project on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/image_search/images) or you can find license free images on [unsplash](https://unsplash.com).
+
+Next, create a `seed` method, which will create a new Supabase Vector Collection, generate embeddings for your images, and upsert the embeddings into your database:
+
+```python
+def seed():
+ # create vector store client
+ vx = vecs.create_client(DB_CONNECTION)
+
+ # create a collection of vectors with 3 dimensions
+ images = vx.create_collection(name="image_vectors", dimension=512)
+
+ # Load CLIP model
+ model = SentenceTransformer('clip-ViT-B-32')
+
+ # Encode an image:
+ img_emb1 = model.encode(Image.open('./images/one.jpg'))
+ img_emb2 = model.encode(Image.open('./images/two.jpg'))
+ img_emb3 = model.encode(Image.open('./images/three.jpg'))
+ img_emb4 = model.encode(Image.open('./images/four.jpg'))
+
+ # add records to the *images* collection
+ images.upsert(
+ vectors=[
+ (
+ "one.jpg", # the vector's identifier
+ img_emb1, # the vector. list or np.array
+ {"type": "jpg"} # associated metadata
+ ), (
+ "two.jpg",
+ img_emb2,
+ {"type": "jpg"}
+ ), (
+ "three.jpg",
+ img_emb3,
+ {"type": "jpg"}
+ ), (
+ "four.jpg",
+ img_emb4,
+ {"type": "jpg"}
+ )
+ ]
+ )
+ print("Inserted images")
+
+ # index the collection for fast search performance
+ images.create_index()
+ print("Created index")
+```
+
+Add this method as a script in your `pyproject.toml` file:
+
+```toml
+[tool.poetry.scripts]
+seed = "image_search.main:seed"
+search = "image_search.main:search"
+```
+
+After activating the virtual environtment with `poetry shell` you can now run your seed script via `poetry run seed`. You can inspect the generated embeddings in your local database by visiting the local Supabase dashboard at [localhost:54323](http://localhost:54323/project/default/editor), selecting the `vecs` schema, and the `image_vectors` database.
+
+## Perform an Image Search from a Text Query
+
+With Supabase Vector we can easily query our embeddings. We can use either an image as search input or alternative we can generate an embedding from a string input and use that as the query input:
+
+```python
+def search():
+ # create vector store client
+ vx = vecs.create_client(DB_CONNECTION)
+ images = vx.get_collection(name="image_vectors")
+
+ # Load CLIP model
+ model = SentenceTransformer('clip-ViT-B-32')
+ # Encode text query
+ query_string = "a bike in front of a red brick wall"
+ text_emb = model.encode(query_string)
+
+ # query the collection filtering metadata for "type" = "jpg"
+ results = images.query(
+ query_vector=text_emb, # required
+ limit=1, # number of records to return
+ filters={"type": {"$eq": "jpg"}}, # metadata filters
+ )
+ result = results[0]
+ print(result)
+ plt.title(result)
+ image = mpimg.imread('./images/' + result)
+ plt.imshow(image)
+ plt.show()
+```
+
+By limiting the query to one result, we can show the most relevant image to the user. Finally we use `matplotlib` to show the image result to the user.
+
+That's it, go ahead and test it out by running `poetry run search` and you will be presented with an image of a "bike in front of a red brick wall".
+
+## Conclusion
+
+With just a couple of lines of Python you are able to implement image search as well as reverse image search using OpenAI's CLIP model and Supabase Vector.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started/openai/vector-search.mdx b/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx
similarity index 95%
rename from apps/docs/pages/guides/getting-started/openai/vector-search.mdx
rename to apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx
index d1f5023d7f1..01bef30379e 100644
--- a/apps/docs/pages/guides/getting-started/openai/vector-search.mdx
+++ b/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx
@@ -2,33 +2,23 @@ import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
- title: 'OpenAI Embeddings & Vector Search',
+ title: 'Vector search with Next.js and OpenAI',
subtitle:
'Learn how to build a ChatGPT-style doc search powered by Next.js, OpenAI, and Supabase.',
- breadcrumb: 'OpenAI',
+ breadcrumb: 'AI Examples',
video: 'https://www.youtube.com/v/xmfNUCjszh4',
tocVideo: 'xmfNUCjszh4',
}
-In this tutorial we'll look at how you can build a custom ChatGPT-like search experience for your own knowledge base. See our [Supabase Clippy](https://supabase.com/blog/chatgpt-supabase-docs) blog post for an example of how this will look.
+While our [Headless Vector search](/docs/guides/ai/examples/headless-vector-search) provides a toolkit for generative Q&A, in this tutorial we'll go more in-depth, build a custom ChatGPT-like search experience from the ground-up using Next.js. You will:
-We assume that you have a Next.js project with a collection of `.mdx` files nested inside your `pages` directory. We will start developing locally with the Supabase CLI and then push our local database changes to our hosted Supabase project.
+1. Convert your markdown into embeddings using OpenAI.
+2. Store you embeddings in Postgres using pgvector.
+3. Deploy a function for answering your users' questions.
-## Video Guide
+You can read our [Supabase Clippy](https://supabase.com/blog/chatgpt-supabase-docs) blog post for a full example.
-
-
-
-
-
- You can find the [full example on
- GitHub](https://github.com/supabase-community/nextjs-openai-doc-search).
-
+We assume that you have a Next.js project with a collection of `.mdx` files nested inside your `pages` directory. We will start developing locally with the Supabase CLI and then push our local database changes to our hosted Supabase project. You can find the [full Next.js example on GitHub](https://github.com/supabase-community/nextjs-openai-doc-search).
## Create a project
@@ -553,5 +543,5 @@ Want to learn more about the awesome tech that is powering this?
>
-export const Page = ({ children }) =>
+export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/functions/examples/openai.mdx b/apps/docs/pages/guides/ai/examples/openai.mdx
similarity index 50%
rename from apps/docs/pages/guides/functions/examples/openai.mdx
rename to apps/docs/pages/guides/ai/examples/openai.mdx
index 236ad59354f..7094a8fc936 100644
--- a/apps/docs/pages/guides/functions/examples/openai.mdx
+++ b/apps/docs/pages/guides/ai/examples/openai.mdx
@@ -3,20 +3,35 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'examples-openai',
title: 'Generating OpenAI GPT3 completions',
- description: 'Using OpenAI in Edge Functions.',
+ description: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.',
+ subtitle: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.',
video: 'https://www.youtube.com/v/29p8kIqyU_Y',
+ tocVideo: '29p8kIqyU_Y',
}
-
-
-
+OpenAI provides a [completions API](https://platform.openai.com/docs/api-reference/completions) that allows you to use their generative GPT models in your own applications.
-Use the [OpenAI completions API](https://platform.openai.com/docs/api-reference/completions) in Supabase Edge Functions.
+OpenAI's API is intended to be used from the server-side. Supabase offers Edge Functions to make it easy to interact with third party APIs like OpenAI.
+
+## Setup Supabase project
+
+If you haven't already, [install the Supabase CLI](/docs/guides/cli) and initialize your project:
+
+```shell
+supabase init
+```
+
+## Create edge function
+
+Scaffold a new edge function called `openai` by running:
+
+```shell
+supabase functions new openai
+```
+
+A new edge function will now exist under `./supabase/functions/openai/index.ts`.
+
+We'll design the function to take your user's query (via POST request) and forward it to OpenAI's API.
```ts index.ts
import 'xhr_polyfill'
@@ -31,7 +46,7 @@ serve(async (req) => {
prompt: query,
max_tokens: 256,
temperature: 0,
- stream: true,
+ stream: false,
}
return fetch('https://api.openai.com/v1/completions', {
@@ -45,12 +60,28 @@ serve(async (req) => {
})
```
+Note that we are setting `stream` to `false` which will wait until the entire response is complete before returning. If you wish to stream GPT's response word-by-word back to your client, set `stream` to `true`.
+
+## Create OpenAI key
+
+You may have noticed we were passing `OPENAI_API_KEY` in the Authorization header to OpenAI. To generate this key, go to https://platform.openai.com/account/api-keys and create a new secret key.
+
+After getting the key, copy it into a new file called `.env.local` in your `./supabase` folder:
+
+```
+OPENAI_API_KEY=your-key-here
+```
+
## Run locally
+Serve the edge function locally by running:
+
```bash
supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt
```
+Notice how we are passing in the `.env.local` file.
+
Use cURL or Postman to make a POST request to http://localhost:54321/functions/v1/openai.
```bash
@@ -59,8 +90,12 @@ curl -i --location --request POST http://localhost:54321/functions/v1/openai \
--data '{"query":"What is Supabase?"}'
```
+You should see a GPT response come back from OpenAI!
+
## Deploy
+Deploy your function to the cloud by runnning:
+
```bash
supabase functions deploy --no-verify-jwt openai
supabase secrets set --env-file ./supabase/.env.local
diff --git a/apps/docs/pages/guides/ai/google-colab.mdx b/apps/docs/pages/guides/ai/google-colab.mdx
new file mode 100644
index 00000000000..a3438246727
--- /dev/null
+++ b/apps/docs/pages/guides/ai/google-colab.mdx
@@ -0,0 +1,112 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-google-colab',
+ title: 'Google Colab',
+ description: 'Use Google Colab to manage your Supabase Vector store.',
+ subtitle: 'Use Google Colab to manage your Supabase Vector store.',
+ sidebar_label: 'Google Colab',
+}
+
+Google Colab is a hosted Jupyter Notebook service. It provides free access to computing resources, including GPUs and TPUs, and is well-suited to machine learning, data science, and education. We can use Colab to manage collections using [Supabase Vecs](/docs/ai/vecs-python-client).
+
+In this tutorial we'll connect to a database running on the Supabase [platform](https://app.supabase.com/). If you don't already have a database, you can create one here: [database.new](https://database.new).
+
+## Create a new notebook
+
+Start by visiting [colab.research.google.com](https://colab.research.google.com/). There you can create a new notebook.
+
+
+
+## Install Vecs
+
+We'll use the Supabase Vector client, [Vecs](/docs/guides/ai/vecs-python-client), to manage our collections.
+
+At the top of the notebook add the notebook paste the following code and hit the "execute" button (`ctrl+enter`):
+
+```py
+pip install vecs
+```
+
+
+
+## Connect to your database
+
+Find the Postgres connection string for your Supabase project in the [database settings](https://app.supabase.com/_/settings/database) of the dashboard. Copy the "URI" format, which should look something like `postgresql:/postgres:@:5432/postgres`
+
+Create a new code block below the install block (`ctrl+m b`) and add the following code using the Postgres URI you copied above:
+
+```py
+import vecs
+
+DB_CONNECTION = "postgresql://postgres:@:5432/postgres"
+
+# create vector store client
+vx = vecs.create_client(DB_CONNECTION)
+```
+
+Execute the code block (`ctrl+enter`). If no errors were returned then your connection was successful.
+
+## Create a collection
+
+Now we're going to create a new collection and insert some documents.
+
+Create a new code block below the install block (`ctrl+m b`). Add the following code to the code block and execute it (`ctrl+enter`):
+
+```py
+collection = vx.create_collection(name="colab_collection", dimension=3)
+
+collection.upsert(
+ vectors=[
+ (
+ "vec0", # the vector's identifier
+ [0.1, 0.2, 0.3], # the vector. list or np.array
+ {"year": 1973} # associated metadata
+ ),
+ (
+ "vec1",
+ [0.7, 0.8, 0.9],
+ {"year": 2012}
+ )
+ ]
+)
+```
+
+This will create a table inside your database within the `vecs` schema, called `colab_collection`. You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown.
+
+
+
+## Query your documents
+
+Now we can search for documents based on their similarity. Create a new code block and execute the following code:
+
+```py
+collection.query(
+ query_vector=[0.4,0.5,0.6], # required
+ limit=5, # number of records to return
+ filters={}, # metadata filters
+ measure="cosine_distance", # distance measure to use
+ include_value=False, # should distance measure values be returned?
+ include_metadata=False, # should record metadata be returned?
+)
+```
+
+You will see that this returns two documents in an array `['vec1', 'vec0']`:
+
+
+
+It also returns a warning:
+
+```
+Query does not have a covering index for cosine_distance.
+```
+
+You can lean more about creating indexes in the [Vecs documentation](https://supabase.github.io/vecs/api/#create-an-index).
+
+## Resources
+
+- Vecs API: [supabase.github.io/vecs/api](https://supabase.github.io/vecs/api)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/hugging-face.mdx b/apps/docs/pages/guides/ai/hugging-face.mdx
new file mode 100644
index 00000000000..b87c724a55b
--- /dev/null
+++ b/apps/docs/pages/guides/ai/hugging-face.mdx
@@ -0,0 +1,168 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-hugging-face',
+ title: 'Hugging Face',
+ description: 'Learn how to integrate hugging face models with Supabase',
+ sidebar_label: 'Hugging Face',
+}
+
+[Hugging Face](https://huggingface.co) is an open source hub for AI/ML models and tools. With over 100,000 machine learning models available, Hugging Face provides a great way to integrate specialized AI & ML tasks into your application.
+
+## AI Tasks
+
+Below are some of the types of tasks you can perform with Hugging Face:
+
+### Natural language
+
+- [Summarization](https://huggingface.co/tasks/summarization)
+- [Text classification](https://huggingface.co/tasks/text-classification)
+- [Text generation](https://huggingface.co/tasks/text-generation)
+- [Translation](https://huggingface.co/tasks/translation)
+- [Fill in the blank](https://huggingface.co/tasks/fill-mask)
+
+### Computer Vision
+
+- [Image to text](https://huggingface.co/tasks/image-to-text)
+- [Text to image](https://huggingface.co/tasks/text-to-image)
+- [Image classification](https://huggingface.co/tasks/image-classification)
+- [Video classification](https://huggingface.co/tasks/video-classification)
+- [Object detection](https://huggingface.co/tasks/object-detection)
+- [Image segmentation](https://huggingface.co/tasks/image-segmentation)
+
+### Audio
+
+- [Text to speech](https://huggingface.co/tasks/text-to-speech)
+- [Speech to text](https://huggingface.co/tasks/automatic-speech-recognition)
+- [Audio classification](https://huggingface.co/tasks/audio-classification)
+
+See a [full list of tasks](https://huggingface.co/tasks).
+
+## Access token
+
+You should first generate a Hugging Face access token for your app:
+
+https://huggingface.co/settings/tokens
+
+Name your token based on the app its being used for and the environment. For example, if you are building an image generation app you might create 2 tokens:
+
+- "My Image Generator (Dev)"
+- "My Image Generator (Prod)"
+
+Since we will be using this token for the inference API, choose the `read` role.
+
+
+
+Though it is possible to use the Hugging Face inference API today without an access token, [you may be rate limited](https://huggingface.co/docs/huggingface.js/inference/README#usage).
+
+To ensure you don't experience any unexpected downtime or errors, we recommend creating an access token.
+
+
+
+## Integrate with Supabase
+
+Supabase aims to make it as easy as possible to integrate with third-party tools like Hugging Face.
+
+### Edge functions
+
+When building any application, you should never trust the client frontend with secrets or credentials. Here this means you should never use your Hugging Face access token directly from your frontend. Supabase solves this using [Edge Functions](/docs/guides/functions), server side TypeScript functions that spin up on-demand only as you need them. Since Edge Functions run on a server, you can safely give them access to your Hugging Face access token.
+
+
+
+You will need the `supabase` CLI [installed](/docs/guides/cli) for the following commands to work.
+
+
+
+To create a new edge function, navigate to your local project and initialize Supabase if you haven't already:
+
+```shell
+supabase init
+```
+
+Then create an edge function:
+
+```shell
+supabase functions new text-to-image
+```
+
+Create a file called `.env.local` to store your Hugging Face access token:
+
+```shell
+HUGGING_FACE_ACCESS_TOKEN=
+```
+
+Let's modify the edge function to import Hugging Face's inference client and perform a text-to-image request:
+
+```ts
+import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
+import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
+
+const hf = new HfInference(Deno.env.get('HUGGING_FACE_ACCESS_TOKEN'))
+
+serve(async (req) => {
+ const { prompt } = await req.json()
+
+ const image = await hf.textToImage(
+ {
+ inputs: prompt,
+ model: 'stabilityai/stable-diffusion-2',
+ },
+ {
+ use_cache: false,
+ }
+ )
+
+ return new Response(image)
+})
+```
+
+This function creates a new instance of `HfInference` using the `HUGGING_FACE_ACCESS_TOKEN` environment variable.
+
+It expects a POST request that includes a JSON request body. The JSON body should include a parameter called `prompt` that represents the text-to-image prompt that we will pass to Hugging Face's inference API.
+
+Next we call `textToImage()`, passing in the user's prompt along with the model that we would like to use for the image generation. Today Hugging Face recommends `stabilityai/stable-diffusion-2`, but you can change this to any other text-to-image model. You can see a list of which models are supported for each task by navigating to their [models page](https://huggingface.co/models?pipeline_tag=text-to-image) and filtering by task.
+
+We set `use_cache` to `false` so that repeat queries with the same prompt will produce new images. If the task and model you are using is deterministic (will always produce the same result based on the same input), consider setting `use_cache` to `true` for faster responses.
+
+The `image` result returned from the API will be a `Blob`. We can pass the `Blob` directly into a `new Response()` which will automatically set the content type and body of the response from the `image`.
+
+Finally let's serve the edge function locally to test it:
+
+```shell
+supabase functions serve --env-file .env.local --no-verify-jwt
+```
+
+Remember to pass in the `.env.local` file using the `--env-file` parameter so that the edge function can access the `HUGGING_FACE_ACCESS_TOKEN`.
+
+
+
+For demo purposes we set `--no-verify-jwt` to make it easy to test the edge function without passing in a JWT token. In a real application you will need to pass the JWT as a `Bearer` token in the `Authorization` header.
+
+
+
+At this point, you can make an API request to your edge function using your preferred frontend framework (Next.js, React, Expo, etc). We can also test from the terminal using `curl`:
+
+```shell
+curl --output result.jpg --location --request POST 'http://localhost:54321/functions/v1/text-to-image' \
+ --header 'Content-Type: application/json' \
+ --data '{"query":"Llama wearing sunglasses"}'
+```
+
+Your generated image will save to `result.jpg`:
+
+
+
+## Resources
+
+- Official [Hugging Face site](https://huggingface.co/).
+- Official [Hugging Face JS docs](https://huggingface.co/docs/huggingface.js).
+- [Generate image captions](/docs/guides/ai/examples/huggingface-image-captioning) using Hugging Face.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/langchain.mdx b/apps/docs/pages/guides/ai/langchain.mdx
new file mode 100644
index 00000000000..7009bca2d03
--- /dev/null
+++ b/apps/docs/pages/guides/ai/langchain.mdx
@@ -0,0 +1,107 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-lang-chain',
+ title: 'LangChain',
+ description:
+ 'Learn how to integrate Supabase with LangChain, a popular framework for composing AI, Vectors, and embeddings',
+ sidebar_label: 'LangChain',
+}
+
+[LangChain](langchain.com) is a popular framework for working with AI, Vectors, and embeddings. LangChain supports using Supabase as a [vector store](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase), using the `pgvector` extension.
+
+## Initializing your database
+
+Prepare you database with the relevant tables:
+
+```sql
+-- Enable the pgvector extension to work with embedding vectors
+create extension vector;
+
+-- Create a table to store your documents
+create table documents (
+ id bigserial primary key,
+ content text, -- corresponds to Document.pageContent
+ metadata jsonb, -- corresponds to Document.metadata
+ embedding vector(1536) -- 1536 works for OpenAI embeddings, change if needed
+);
+
+-- Create a function to search for documents
+create function match_documents (
+ query_embedding vector(1536),
+ match_count int,
+ filter jsonb DEFAULT '{}'
+) returns table (
+ id bigint,
+ content text,
+ metadata jsonb,
+ similarity float
+)
+language plpgsql
+as $$
+#variable_conflict use_column
+begin
+ return query
+ select
+ id,
+ content,
+ metadata,
+ 1 - (documents.embedding <=> query_embedding) as similarity
+ from documents
+ where metadata @> filter
+ order by documents.embedding <=> query_embedding
+ limit match_count;
+end;
+$$;
+```
+
+## Usage
+
+You can now search your documents using any Node.js application. This is intended to be run on a secure server route.
+
+```js
+import { SupabaseVectorStore } from 'langchain/vectorstores/supabase'
+import { OpenAIEmbeddings } from 'langchain/embeddings/openai'
+import { createClient } from '@supabase/supabase-js'
+
+const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY
+if (!supabaseKey) throw new Error(`Expected SUPABASE_SERVICE_ROLE_KEY`)
+
+const url = process.env.SUPABASE_URL
+if (!url) throw new Error(`Expected env var SUPABASE_URL`)
+
+export const run = async () => {
+ const client = createClient(url, supabaseKey)
+
+ const vectorStore = await SupabaseVectorStore.fromTexts(
+ ['Hello world', 'Bye bye', "What's this?"],
+ [{ id: 2 }, { id: 1 }, { id: 3 }],
+ new OpenAIEmbeddings(),
+ {
+ client,
+ tableName: 'documents',
+ queryName: 'match_documents',
+ }
+ )
+
+ const resultOne = await vectorStore.similaritySearch('Hello world', 1)
+
+ console.log(resultOne)
+}
+```
+
+## Hybrid search
+
+LangChain supports the concept of a hybrid search, which combines Similarity Search with Full Text Search. Read the official docs to get started: [Supabase Hybrid Search](https://js.langchain.com/docs/modules/indexes/retrievers/supabase-hybrid).
+
+You can install the LangChain Hybrid Search function though our [database.dev package manager](https://database.dev/langchain/hybrid_search).
+
+## Resources
+
+- Official [LangChain site](https://langchain.com/).
+- Official [LangChain docs](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase).
+- Supabase [Hybrid Search](https://js.langchain.com/docs/modules/indexes/retrievers/supabase-hybrid).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/managing-collections.mdx b/apps/docs/pages/guides/ai/managing-collections.mdx
new file mode 100644
index 00000000000..5849f6aa526
--- /dev/null
+++ b/apps/docs/pages/guides/ai/managing-collections.mdx
@@ -0,0 +1,156 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-collections',
+ title: 'Managing collections',
+ description: 'Learn how to manage groups of vector records using the vecs Python library',
+ sidebar_label: 'Managing collections',
+}
+
+A collection is an group of vector records managed by the `vecs` Python library. Records can be added to or updated in a collection. Collections can be queried at any time, but should be indexed for scalable query performance.
+
+Supabase provides a [Python client](/docs/guides/ai/vecs-python-client) called `vecs` for managing unstructured vector stores in Postgres. If you come from a data science background, this unstructured data approach will feel familiar. If you are more interested in a structured data approach, see [Vector columns](/docs/guides/ai/vector-columns) or read our guide on [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured-embeddings).
+
+Under the hood `vecs` will manage the necessary Postgres tables and columns to store and query your collections.
+
+## API
+
+Find the full API in the [official API docs](https://supabase.github.io/vecs/api).
+
+### Connecting
+
+Before you can interact with vecs, create the client to communicate with Postgres.
+
+```python
+import vecs
+
+DB_CONNECTION = "postgresql://:@:/"
+
+# create vector store client
+vx = vecs.create_client(DB_CONNECTION)
+```
+
+### Create collection
+
+You can create a collection to store vectors specifying the collections name and the number of dimensions in the vectors you intend to store.
+
+```python
+docs = vx.create_collection(name="docs", dimension=3)
+```
+
+If another collection exists with the same name,
+
+### Get an existing collection
+
+To access a previously created collection, use `get_collection` to retrieve it by name
+
+```python
+docs = vx.get_collection(name="docs")
+```
+
+### Upserting vectors
+
+`vecs` combines the concepts of "insert" and "update" into "upsert". Upserting records adds them to the collection if the `id` is not present, or updates the existing record if the `id` does exist.
+
+```python
+# add records to the collection
+docs.upsert(
+ vectors=[
+ (
+ "vec0", # the vector's identifier
+ [0.1, 0.2, 0.3], # the vector. list or np.array
+ {"year": 1973} # associated metadata
+ ),
+ (
+ "vec1",
+ [0.7, 0.8, 0.9],
+ {"year": 2012}
+ )
+ ]
+)
+```
+
+### Create an index
+
+Collections can be queried immediately after being created.
+However, for good performance, the collection should be indexed after records have been upserted.
+
+Indexes should be created **after** the collection has been populated with records. Building an index on an empty collection will result in significantly reduced recall. Once the index has been created you can still upsert new documents into the collection but you should rebuild the index if the size of the collection more than doubles.
+
+Only one index may exist per-collection. By default, creating an index will replace any existing index.
+
+To create an index:
+
+```python
+##
+# INSERT RECORDS HERE
+##
+
+# index the collection to be queried by cosine distance
+docs.create_index(measure=vecs.IndexMeasure.cosine_distance)
+```
+
+Available options for query `measure` are:
+
+- `vecs.IndexMeasure.cosine_distance`
+- `vecs.IndexMeasure.l2_distance`
+- `vecs.IndexMeasure.max_inner_product`
+
+which correspond to different methods for comparing query vectors to the vectors in the database.
+
+If you aren't sure which to use, stick with the default (cosine_distance) by omitting the parameter i.e.: `docs.create_index()`.
+
+
+
+The time required to create an index grows with the number of records and size of vectors. For a few thousand records expect sub-minute a response in under a minute. It may take a few minutes for larger collections.
+
+
+
+For an in-depth guide on vector indexes, see [Managing indexes](/docs/guides/ai/managing-indexes).
+
+### Query
+
+Be aware that indexes are essential for good performance. If you do not create an index, every query will return a warning that includes the `IndexMeasure` you should index.
+
+#### Basic
+
+The simplest form of search is to provide a query vector.
+
+```python
+docs.query(
+ query_vector=[0.4,0.5,0.6], # required
+ limit=5, # number of records to return
+ filters={}, # metadata filters
+ measure="cosine_distance", # distance measure to use
+ include_value=False, # should distance measure values be returned?
+ include_metadata=False, # should record metadata be returned?
+)
+```
+
+Which returns a list of vector record `ids`.
+
+#### Metadata Filtering
+
+The metadata that is associated with each record can also be filtered during a query.
+
+As an example, `{"year": {"$eq": 2005}}` filters a `year` metadata key to be equal to 2005
+
+In context:
+
+```python
+docs.query(
+ query_vector=[0.4,0.5,0.6],
+ filters={"year": {"$eq": 2012}}, # metadata filters
+)
+```
+
+For a complete reference, see the [metadata guide](https://supabase.github.io/vecs/concepts_metadata/).
+
+## Resources
+
+- Official Vecs Documentation: https://supabase.github.io/vecs/api
+- Source Code: https://github.com/supabase/vecs
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/managing-indexes.mdx b/apps/docs/pages/guides/ai/managing-indexes.mdx
new file mode 100644
index 00000000000..6269919badf
--- /dev/null
+++ b/apps/docs/pages/guides/ai/managing-indexes.mdx
@@ -0,0 +1,95 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-managing-indexes',
+ title: 'Managing indexes',
+ description: 'Understanding vector indexes',
+ sidebar_label: 'Managing indexes',
+}
+
+Once your vector table starts to grow, you will likely want to add an index to speed up queries. Without indexes, you'll be performing a sequential scan which can be a resource-intensive operation when you have many records.
+
+## IVFFlat indexes
+
+Today `pgvector` indexes use an algorithm called IVFFlat. IVF stands for 'inverted file indexes'. It works by clustering your vectors in order to reduce the similarity search scope. Rather than comparing a vector to every other vector, the vector is only compared against vectors within the same cell cluster (or nearby clusters, depending on your configuration).
+
+### Inverted lists (cell clusters)
+
+When you create the index, you choose the number of inverted lists (cell clusters). Increase this number to speed up queries, but at the expense of recall.
+
+For example, to create an index with 100 lists on a column that uses the cosine operator:
+
+```sql
+create index on items using ivfflat (column_name vector_cosine_ops) with (lists = 100);
+```
+
+For more info on the different operators, see [Distance operations](#distance-operators).
+
+For every query, you can set the number of probes (1 by default). The number of probes corresponds to the number of nearby cells to probe for a match. Increase this for better recall at the expense of speed.
+
+To set the number of probes for the duration of the session run:
+
+```sql
+set ivfflat.probes = 10;
+```
+
+To set the number of probes only for the current transaction run:
+
+```sql
+begin;
+set local ivfflat.probes = 10;
+select ...
+commit;
+```
+
+If the number of probes is the same as the number of lists, exact nearest neighbor search will be performed and the planner won't use the index.
+
+### Approximate nearest neighbor
+
+One important note with IVF indexes is that nearest neighbor search is approximate, since exact search on high dimensional data can't be indexed efficiently. This means that similarity results will change (slightly) after you add an index (trading recall for speed).
+
+## Distance operators
+
+The type of index required depends on the distance operator you are using. `pgvector` includes 3 distance operators:
+
+| Operator | Description | [**Operator class**](https://www.postgresql.org/docs/current/sql-createopclass.html) |
+| -------- | ---------------------- | ------------------------------------------------------------------------------------ |
+| `<->` | Euclidean distance | `vector_l2_ops` |
+| `<#>` | negative inner product | `vector_ip_ops` |
+| `<=>` | cosine distance | `vector_cosine_ops` |
+
+Use the following SQL commands to create an index for the operator(s) used in your queries.
+
+### Euclidean L2 distance (`vector_l2_ops`)
+
+```sql
+create index on items using ivfflat (column_name vector_l2_ops) with (lists = 100);
+```
+
+### Inner product (`vector_ip_ops`)
+
+```sql
+create index on items using ivfflat (column_name vector_ip_ops) with (lists = 100);
+```
+
+### Cosine distance (`vector_cosine_ops`)
+
+```sql
+create index on items using ivfflat (column_name vector_cosine_ops) with (lists = 100);
+```
+
+Currently vectors with up to 2,000 dimensions can be indexed.
+
+If you are using the `vecs` Python library, follow the instructions in [Managing collections](/docs/guides/ai/managing-collections#create-an-index) to create indexes.
+
+## When should you add indexes?
+
+`pgvector` recommends adding indexes only after the table has sufficient data, so that the internal IVFFlat cell clusters are based on your data's distribution. Anytime the distribution changes significantly, consider recreating indexes.
+
+## Resources
+
+Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/structured-unstructured-embeddings.mdx b/apps/docs/pages/guides/ai/structured-unstructured-embeddings.mdx
new file mode 100644
index 00000000000..e1a18bc4126
--- /dev/null
+++ b/apps/docs/pages/guides/ai/structured-unstructured-embeddings.mdx
@@ -0,0 +1,61 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'structured-unstructured-embeddings',
+ title: 'Structured and unstructured embeddings',
+ description:
+ 'Supabase is flexible enough to provide structured and unstructured embeddings using pgvector.',
+ subtitle:
+ 'Supabase is flexible enough to provide structured and unstructured embeddings using pgvector.',
+ sidebar_label: 'Structured and unstructured embeddings',
+}
+
+Most vector stores treat embeddings like NoSQL, unstructured data. Supabase is flexible enough to fit either a structured or an unstructured approach.
+
+Compare these code snippets:
+
+## Structured
+
+```sql
+create table docs (
+ id uuid primary key,
+ content text,
+ url string,
+ embedding vector(1536)
+);
+
+insert into docs
+ (id, content, url, embedding)
+values
+ ('79409372-7556-4ccc-ab8f-5786a6cfa4f7', 'Hello world', '/hello-world', [100, 200, 300]);
+```
+
+A structured approach is usually defined in SQL, and managed via database [migrations](/docs/guides/getting-started/local-development#database-migrations).
+
+## Unstructured
+
+```py
+import vecs
+
+docs = vx.create_collection(name="docs", dimension=1536)
+
+docs.upsert(vectors=[
+ ('79409372-7556-4ccc-ab8f-5786a6cfa4f7', [100, 200, 300], { url: '/hello-world' })
+])
+
+```
+
+An unstructured approach is usually defined in Python and has a looser table definition, storing metadata as a json document along with the embedding.
+
+## Choosing the right model
+
+Both approaches create a table where you can store your embeddings and some metadata. You should choose the best approach for your use-case.
+
+- Structured embeddings are typically co-located with some content that is already stored in your database.
+- Unstructured embeddings are typically defined at runtime, better-suited for a large body of external content.
+
+Both approaches are fine, and the one you should choose depends on your use-case.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/vecs-python-client.mdx b/apps/docs/pages/guides/ai/vecs-python-client.mdx
new file mode 100644
index 00000000000..0be65c7c744
--- /dev/null
+++ b/apps/docs/pages/guides/ai/vecs-python-client.mdx
@@ -0,0 +1,91 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ id: 'ai-vecs-python-client',
+ title: 'Python client',
+ subtitle: 'Manage unstructured vector stores in PostgreSQL.',
+ breadcrumb: 'AI Quickstarts',
+}
+
+Supabase provides a Python client called [`vecs`](https://github.com/supabase/vecs) for managing unstructured vector stores. This client provides a set of useful tools for creating and querying collections in PostgreSQL using the [pgvector](/docs/guides/database/extensions/pgvector) extension.
+
+## Quick start
+
+Let's see how Vecs works using a local database. Make sure you have the Supabase CLI [installed](/docs/guides/cli#installation) on your machine.
+
+### Initialize your project
+
+Start a local Postgres instance in any folder using the `init` and `start` commands. Make sure you have Docker running!
+
+```bash
+# Initialize your project
+supabase init
+
+# Start Postgres
+supabase start
+```
+
+### Create a collection
+
+Inside a Python shell, run the following commands to create a new collection called "docs", with 3 dimensions.
+
+```py
+import vecs
+
+# create vector store client
+vx = vecs.create_client("postgresql://postgres:postgres@localhost:54322/postgres")
+
+# create a collection of vectors with 3 dimensions
+docs = vx.create_collection(name="docs", dimension=3)
+```
+
+### Add embeddings
+
+Now we can insert some embeddings into our "docs" collection using the `usert()` command:
+
+```py
+import vecs
+
+# create vector store client
+docs = vecs.get_collection(name="docs")
+
+# a collection of vectors with 3 dimensions
+vectors=[
+ ("vec0", [0.1, 0.2, 0.3], {"year": 1973}),
+ ("vec1", [0.7, 0.8, 0.9], {"year": 2012})
+]
+
+# insert our vectors
+docs.upsert(vectors=vectors)
+```
+
+### Query the collection
+
+You can now query the collection to retrieve a relevant match:
+
+```py
+import vecs
+
+docs = vecs.get_collection(name="docs")
+
+# query the collection filtering metadata for "year" = 2012
+docs.query(
+ query_vector=[0.4,0.5,0.6], # required
+ limit=1, # number of records to return
+ filters={"year": {"$eq": 2012}}, # metadata filters
+)
+```
+
+## Deep Dive
+
+For a more in-depth guide on `vecs` collections, see [Managing collections](/docs/guides/ai/managing-collections).
+
+## Resources
+
+- Official Vecs Documentation: https://supabase.github.io/vecs/api
+- Source Code: https://github.com/supabase/vecs
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/ai/vector-columns.mdx b/apps/docs/pages/guides/ai/vector-columns.mdx
new file mode 100644
index 00000000000..d941c164302
--- /dev/null
+++ b/apps/docs/pages/guides/ai/vector-columns.mdx
@@ -0,0 +1,153 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-vector-columns',
+ title: 'Vector columns',
+ description: 'Learn how to use vectors within your own Postgres tables',
+ sidebar_label: 'Vector columns',
+}
+
+Supabase offers a number of different ways to store and query vectors within Postgres. If you prefer to use Python to store and query your vectors using collections, see [Managing collections](/docs/guides/ai/managing-collections). If you want more control over vectors within your own Postgres tables or would like to interact with them using a different language like JavaScript, keep reading.
+
+Vectors in Supabase are enabled via [pgvector](https://github.com/pgvector/pgvector/), a PostgreSQL extension for storing and querying vectors in Postgres. It can be used to store [embeddings](/docs/guides/ai/concepts#what-are-embeddings).
+
+## Usage
+
+### Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "vector" and enable the extension.
+
+
+
+
+```sql
+ -- Example: enable the "vector" extension.
+create extension vector
+with
+ schema extensions;
+
+-- Example: disable the "vector" extension
+drop
+ extension if exists vector;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension, call `drop extension`.
+
+
+
+
+### Create a table to store vectors
+
+After enabling the `vector` extension, you will get access to a new data type called `vector`. The size of the vector (indicated in parenthesis) represents the number of dimensions stored in that vector.
+
+```sql
+create table documents (
+ id serial primary key,
+ title text not null,
+ body text not null,
+ embedding vector(1536)
+);
+```
+
+In the above SQL snippet, we create a `documents` table with a column called `embedding` (note this is just a regular Postgres column - you can name it whatever you like). We give the `embedding` column a `vector` data type with 1536 dimensions. Change this to the number of dimensions used in your vector application. For example, if you are generating embeddings using OpenAI's `text-embeddings-ada-002` model, you would set this number as 1536 since that model produces 1536 dimensions.
+
+### Storing a vector / embedding
+
+In this example we'll generate a vector using the OpenAI API client, then store it in the database using the Supabase JavaScript client.
+
+```js
+const title = 'First post!'
+const body = 'Hello world!'
+
+// Generate a vector using OpenAI
+const embeddingResponse = await openai.createEmbedding({
+ model: 'text-embedding-ada-002',
+ input: body,
+})
+
+const [{ embedding }] = embeddingResponse.data.data
+
+// Store the vector in Postgres
+const { data, error } = await supabase.from('documents').insert({
+ title,
+ body,
+ embedding,
+})
+```
+
+This example uses the JavaScript Supabase client, but you can modify it to work with any [supported language library](/docs#client-libraries).
+
+### Querying a vector / embedding
+
+Similarity search is the most common use case for vectors. `pgvector` support 3 new operators for performing similarity search:
+
+| Operator | Description |
+| -------- | ---------------------- |
+| `<->` | Euclidean distance |
+| `<#>` | negative inner product |
+| `<=>` | cosine distance |
+
+Choosing the right operator depends on your needs. If you are searching over OpenAI embeddings, OpenAI recommends using cosine similarity. For more information on how embeddings work and how they relate to each other, see [What are Embeddings?](/docs/guides/ai/concepts#what-are-embeddings).
+
+Supabase client libraries like `supabase-js` connect to your Postgres instance via [PostgREST](docs/guides/getting-started/architecture#postgrest-api). PostgREST does not currently support `pgvector` similarity operators, so we'll need to wrap our query in a Postgres function and call it via the `rpc()` method:
+
+```sql
+create or replace function match_documents (
+ query_embedding vector(1536),
+ match_threshold float,
+ match_count int
+)
+returns table (
+ id bigint,
+ content text,
+ similarity float
+)
+language sql stable
+as $$
+ select
+ documents.id,
+ documents.content,
+ 1 - (documents.embedding <=> query_embedding) as similarity
+ from documents
+ where 1 - (documents.embedding <=> query_embedding) > match_threshold
+ order by similarity desc
+ limit match_count;
+$$;
+```
+
+This function takes a `query_embedding` argument and compares it to all other embeddings in the `documents` table. Each comparison returns a similarity score. If the similarity is greater than the `match_threshold` argument, it is returned. The number of rows returned is limited by the `match_count` argument.
+
+Feel free to modify this method to fit the needs of your application. The `match_threshold` ensures that only documents that have a minimum similarity to the `query_embedding` are returned. Without this, you may end up returning documents that subjectively don't match. This value will vary for each application - you will need to perform your own testing to determine the threshold that makes sense for your app.
+
+To execute the function from your client library, call `rpc()` with the name of your Postgres function:
+
+```ts
+const { data: documents } = await supabaseClient.rpc('match_documents', {
+ query_embedding: embedding, // Pass the embedding you want to compare
+ match_threshold: 0.78, // Choose an appropriate threshold for your data
+ match_count: 10, // Choose the number of matches
+})
+```
+
+In this example `embedding` would be another embedding you wish to compare against your table of pre-generated embedding documents. For example if you were building a search engine, every time the user submits their query you would first generate an embedding on the search query itself (using `openai.createEmbedding()`), then pass it into the above `rpc()` function to match.
+
+Vectors and embedding can be used for much more than search. Learn more about embeddings at [What are Embeddings?](/docs/guides/ai/concepts#what-are-embeddings).
+
+### Indexes
+
+Once your vector table starts to grow, you will likely want to add an index to speed up queries. See [Managing indexes](/docs/guides/ai/managing-indexes) to learn how vector indexes work and how to create them.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/api/rest/client-libs.mdx b/apps/docs/pages/guides/api/rest/client-libs.mdx
index 4282c41abf9..aa967a4b9eb 100644
--- a/apps/docs/pages/guides/api/rest/client-libs.mdx
+++ b/apps/docs/pages/guides/api/rest/client-libs.mdx
@@ -22,10 +22,10 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie
| ----------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| C# | [supabase-csharp](https://github.com/supabase-community/supabase-csharp) | [Docs](https://supabase.com/docs/reference/csharp/introduction) |
| Go | [supabase-go](https://github.com/supabase-community/supabase-go) | |
-| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | |
+| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | [Docs](https://supabase.com/docs/reference/kotlin/introduction) |
| Python | [supabase-py](https://github.com/supabase-community/supabase-py) | [Docs](https://supabase.com/docs/reference/python/initializing) |
| Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | |
-| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | |
+| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | [Docs](https://supabase.com/docs/reference/swift/introduction) |
| Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | |
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx
index dd47ce758bd..1063118b065 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx
@@ -134,6 +134,65 @@ function MyApp({
You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined.
+### Code Exchange API Route
+
+The `Code Exchange` API route is required for the [server-side auth flow](https://supabase.com/docs/guides/auth/server-side-rendering) implemented by the Next.js Auth Helpers. It exchanges an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
+
+
+
+
+Create a new file at `pages/api/auth/callback.js` and populate with the following:
+
+```jsx title="pages/api/auth/callback.js"
+import { NextApiHandler } from 'next'
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+const handler = async (req, res) => {
+ const { code } = req.query
+
+ if (code) {
+ const supabase = createPagesServerClient({ req, res })
+ await supabase.auth.exchangeCodeForSession(String(code))
+ }
+
+ res.redirect('/')
+}
+
+export default handler
+```
+
+
+
+
+
+Create a new file at `pages/api/auth/callback.ts` and populate with the following:
+
+```tsx title="pages/api/auth/callback.ts"
+import { NextApiHandler } from 'next'
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+const handler: NextApiHandler = async (req, res) => {
+ const { code } = req.query
+
+ if (code) {
+ const supabase = createPagesServerClient({ req, res })
+ await supabase.auth.exchangeCodeForSession(String(code))
+ }
+
+ res.redirect('/')
+}
+
+export default handler
+```
+
+
+
+
## Usage with TypeScript
You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
@@ -590,6 +649,35 @@ export const config = {
## Migration Guide
+### Migrating to v0.7.X
+
+#### PKCE Auth Flow
+
+PKCE is the new server-side auth flow implemented by the Next.js Auth Helpers. It requires a new API route for `/api/auth/callback` that exchanges an auth `code` for the user's `session`.
+
+Check the [Code Exchange API Route steps](/docs/guides/auth/auth-helpers/nextjs-pages#code-exchange-api-route) above to implement this route.
+
+#### Authentication
+
+For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange API Route - `/api/auth/callback`. This is an example with the `signUp` function:
+
+```jsx
+supabase.auth.signUp({
+ email: 'jon@example.com',
+ password: 'sup3rs3cur3',
+ options: {
+ emailRedirectTo: 'http://localhost:3000/auth/callback',
+ },
+})
+```
+
+#### Deprecated Functions
+
+With v0.7.x of the Next.js Auth Helpers a new naming convention has been implemented for createClient functions. The `createBrowserSupabaseClient` and `createServerSupabaseClient` functions have been marked as deprecated, and will be removed in a future version of the Auth Helpers.
+
+- `createBrowserSupabaseClient` has been replaced with `createPagesBrowserClient`
+- `createServerSupabaseClient` has been replaced with `createPagesServerClient`
+
### Migrating to v0.5.X
To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
index 8131d278ab3..f7ad292e39d 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
@@ -170,7 +170,7 @@ export async function GET(request: NextRequest) {
## Authentication
-Authentication can be initiated [client](/docs/guides/auth/auth-helpers/nextjs#client-side) or [server-side](/docs/guides/auth/auth-helpers/nextjs#server-side). All of the [supabase-js authentication strategies](http://localhost:3001/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.
+Authentication can be initiated [client](/docs/guides/auth/auth-helpers/nextjs#client-side) or [server-side](/docs/guides/auth/auth-helpers/nextjs#server-side). All of the [supabase-js authentication strategies](/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.
> Note: The authentication flow requires the [Code Exchange Route](/docs/guides/auth/auth-helpers/nextjs#code-exchange-route) to exchange a `code` for the user's `session`.
@@ -701,6 +701,54 @@ See [refreshing session example](/docs/guides/auth/auth-helpers/nextjs#refresh-s
- [Protected Routes](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/[id]/page.tsx)
- [Conditional Rendering in Client Components with SSR](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login-form.tsx)
+## Migration Guide
+
+### Migrating to v0.7.X
+
+#### PKCE Auth Flow
+
+PKCE is the new server-side auth flow implemented by the Next.js Auth Helpers. It requires a new Route Handler for `/auth/callback` that exchanges an auth `code` for the user's `session`.
+
+Check the [Code Exchange Route steps](/docs/guides/auth/auth-helpers/nextjs#code-exchange-route) above to implement this Route Handler.
+
+#### Authentication
+
+For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange route handler - `/auth/callback`. This is an example with the `signUp` function:
+
+```jsx
+supabase.auth.signUp({
+ email: 'jon@example.com',
+ password: 'sup3rs3cur3',
+ options: {
+ emailRedirectTo: 'http://localhost:3000/auth/callback',
+ },
+})
+```
+
+#### Deprecated Functions
+
+With v0.7.x of the Next.js Auth Helpers a new naming convention has been implemented for createClient functions. The `createMiddlewareSupabaseClient`, `createBrowserSupabaseClient`, `createServerComponentSupabaseClient` and `createRouteHandlerSupabaseClient` functions have been marked as deprecated, and will be removed in a future version of the Auth Helpers.
+
+- `createMiddlewareSupabaseClient` has been replaced with `createMiddlewareClient`
+- `createBrowserSupabaseClient` has been replaced with `createClientComponentClient`
+- `createServerComponentSupabaseClient` has been replaced with `createServerComponentClient`
+- `createRouteHandlerSupabaseClient` has been replaced with `createRouteHandlerClient`
+
+#### createClientComponentClient returns singleton
+
+You no longer need to implement logic to ensure there is only a single instance of the Supabase Client shared across all Client Components - this is now handled by the `createClientComponentClient` function. Call it as many times as you want!
+
+```jsx
+"use client";
+
+import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
+
+export default function() {
+ const supabase = createClientComponentClient();
+ return ...
+}
+```
+
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/auth/auth-helpers/remix.mdx b/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
index 7ff90a03904..98ff6f57c94 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
@@ -61,6 +61,82 @@ SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
+### Code Exchange Route
+
+The `Code Exchange` route is required for the [server-side auth flow](https://supabase.com/docs/guides/auth/server-side-rendering) implemented by the Remix Auth Helpers. It exchanges an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
+
+
+
+
+Create a new file at `app/routes/auth.callback.jsx` and populate with the following:
+
+```jsx title="app/routes/auth.callback.jsx"
+import { redirect } from '@remix-run/node'
+import { createServerClient } from '@supabase/auth-helpers-remix'
+
+export const loader = async ({ request }) => {
+ const response = new Response()
+ const url = new URL(request.url)
+ const code = url.searchParams.get('code')
+
+ if (code) {
+ const supabaseClient = createServerClient(
+ process.env.SUPABASE_URL,
+ process.env.SUPABASE_ANON_KEY,
+ { request, response }
+ )
+ await supabaseClient.auth.exchangeCodeForSession(code)
+ }
+
+ return redirect('/', {
+ headers: response.headers,
+ })
+}
+```
+
+
+
+
+
+Create a new file at `app/routes/auth.callback.tsx` and populate with the following:
+
+```tsx title="app/routes/auth.callback.tsx"
+import { redirect } from '@remix-run/node'
+import { createServerClient } from '@supabase/auth-helpers-remix'
+
+import type { Database } from 'db_types'
+import type { LoaderArgs } from '@remix-run/node'
+
+export const loader = async ({ request }: LoaderArgs) => {
+ const response = new Response()
+ const url = new URL(request.url)
+ const code = url.searchParams.get('code')
+
+ if (code) {
+ const supabaseClient = createServerClient(
+ process.env.SUPABASE_URL!,
+ process.env.SUPABASE_ANON_KEY!,
+ { request, response }
+ )
+ await supabaseClient.auth.exchangeCodeForSession(code)
+ }
+
+ return redirect('/', {
+ headers: response.headers,
+ })
+}
+```
+
+> `Database` is a TypeScript definitions file [generated by the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types).
+
+
+
+
## Server-side
The Supabase client can now be used server-side - in loaders and actions - by calling the `createServerClient` function.
@@ -441,7 +517,7 @@ useEffect(() => {
return () => {
subscription.unsubscribe()
}
-}, [serverAccessToken, supabase, fetcher])
+}, [serverAccessToken, supabase, revalidate])
```
@@ -471,7 +547,7 @@ useEffect(() => {
return () => {
subscription.unsubscribe()
}
-}, [serverAccessToken, supabase, fetcher])
+}, [serverAccessToken, supabase, revalidate])
```
@@ -507,6 +583,9 @@ export default function Login() {
const handleGitHubLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'github',
+ options: {
+ redirectTo: 'http://localhost:3000/auth/callback',
+ },
})
}
@@ -542,6 +621,9 @@ export default function Login() {
const handleGitHubLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'github',
+ options: {
+ redirectTo: 'http://localhost:3000/auth/callback',
+ },
})
}
@@ -685,6 +767,30 @@ export default function Index() {
> Ensure you have [enabled replication](https://app.supabase.com/project/_/database/replication) on the table you are subscribing to.
+## Migration Guide
+
+### Migrating to v0.2.0
+
+#### PKCE Auth Flow
+
+PKCE is the new server-side auth flow implemented by the Remix Auth Helpers. It requires a new `loader` route for `/auth/callback` that exchanges an auth `code` for the user's `session`.
+
+Check the [Code Exchange Route steps](/docs/guides/auth/auth-helpers/remix#code-exchange-route) above to implement this route.
+
+#### Authentication
+
+For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange API Route - `/api/auth/callback`. This is an example with the `signUp` function:
+
+```jsx
+supabaseClient.auth.signUp({
+ email: 'jon@example.com',
+ password: 'sup3rs3cur3',
+ options: {
+ emailRedirectTo: 'http://localhost:3000/auth/callback',
+ },
+})
+```
+
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgvector.mdx b/apps/docs/pages/guides/database/extensions/pgvector.mdx
index 3b47baccca2..e9ce3274f8d 100644
--- a/apps/docs/pages/guides/database/extensions/pgvector.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgvector.mdx
@@ -9,6 +9,8 @@ export const meta = {
[pgvector](https://github.com/pgvector/pgvector/) is a PostgreSQL extension for vector similarity search. It can also be used for storing [embeddings](https://supabase.com/blog/openai-embeddings-postgres-vector).
+Learn more about Supabase's [AI & Vector](/docs/guides/ai) offering.
+
## Concepts
### Vector similarity
@@ -81,13 +83,14 @@ const embeddingResponse = await openai.createEmbedding({
model: 'text-embedding-ada-002',
input: body,
})
-const [responseData] = embeddingResponse.data.data.
+
+const [{ embedding }] = embeddingResponse.data.data
// Store the vector in Postgres
const { data, error } = await supabase.from('posts').insert({
title,
body,
- embedding: responseData.embedding,
+ embedding,
})
```
diff --git a/apps/docs/pages/guides/functions.mdx b/apps/docs/pages/guides/functions.mdx
index bb8157e871b..1263a7e47ad 100644
--- a/apps/docs/pages/guides/functions.mdx
+++ b/apps/docs/pages/guides/functions.mdx
@@ -116,12 +116,12 @@ export const examples = [
{
name: 'Hugging Face',
description: `Access 100,000+ Machine Learning models.`,
- href: '/guides/functions/examples/huggingface-image-captioning',
+ href: '/guides/ai/examples/huggingface-image-captioning',
},
{
name: 'OpenAI',
description: `Using OpenAI in Edge Functions.`,
- href: '/guides/functions/examples/openai',
+ href: '/guides/ai/examples/openai',
},
{
name: 'Stripe Webhooks',
diff --git a/apps/docs/pages/guides/integrations/forestadmin.mdx b/apps/docs/pages/guides/integrations/forestadmin.mdx
new file mode 100644
index 00000000000..910a42f3473
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/forestadmin.mdx
@@ -0,0 +1,53 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'forestadmin',
+ title: 'ForestAdmin',
+ description:
+ 'Get started with Supabase and Forest Admin, a tool for automatically generating an Admin Panel without having to build it.',
+}
+
+This guide outlines how to instantly generate an Admin Panel on top of your Supabase backend.
+
+[Forest Admin](https://www.forestadmin.com/) offers an off-the-shelf Admin Panel system that can reduce the amount of time and effort needed to create, maintain, and manage internal tools.
+It automatically builds a backend API and provides a user-friendly interface to Create, Read, Update and Delete, Search, Segment your data, trigger custom actions, control permissions, and set up workflows on top of your app's data.
+
+
+
+If you don’t have a Forest Admin account, you can create one in a few minutes [here](https://app.forestadmin.com/signup).
+
+Let’s get started!
+
+## Step 1: Configure your Supabase Backend
+If you already have a Supabase project set up, simply go to the Project Settings / Database tab to access the Database Settings and retrieve the connection string (URI tab). This is the only information you will need to connect your Supabase account to Forest Admin.
+
+
+
+If you don't have anything set up on Supabase yet, you can [create a Project](https://app.supabase.com/new/_) in just a few seconds. Once done, just go to the Database tab and create your first table.
+
+
+
+## Step 2: Connect the database to Forest Admin
+
+First, you have to create a new project on Forest Admin:
+
+
+Then, you can use Forest Admin's Instant Setup for the Cloud mode to quickly get started. Alternatively, you can host the generated backend admin API on your own (Advanced setup), giving you full control of the backend code. In this guide, we will use the Cloud mode and the Instant Setup.
+
+
+Finally, you can enter the database credentials you obtained in Step 1 and set them in Forest Admin by using the Connection URI mode. Don't forget to replace the password in the connection string with the database password you set in Supabase. Note that if you forget it, you can always go to your Supabase Database settings and reset your database password.
+
+
+## You're all done!
+
+There it is, the configuration of Forest Admin is now complete and your admin panel is now ready-to-use with all the features of an admin panel provided out of the box. You can now browse or manipulate all your data in a structured way, use search with support for complex filters, build dashboards, invite your team mates and start collaborating around your business operations and much more.
+
+## Resources
+
+- [Forest Admin](https://www.forestadmin.com/) official website.
+- [Forest Admin GitHub](https://github.com/ForestAdmin).
+- [Forest Admin](https://docs.forestadmin.com/documentation-portal/) documentation.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/platform/custom-domains.mdx b/apps/docs/pages/guides/platform/custom-domains.mdx
index 6bf9f41472a..0f9df7630d2 100644
--- a/apps/docs/pages/guides/platform/custom-domains.mdx
+++ b/apps/docs/pages/guides/platform/custom-domains.mdx
@@ -141,7 +141,6 @@ As with the final activation stage of the process for setting up a vanity subdom
## Limitations
-- Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
- A Supabase project can—at this time—use either a Custom Domain or a Vanity Subdomain, but not both.
- Some authentication flows like Sign-in with Twitter set cookies to track the progress of the flow. Make sure you use only one domain in your frontend application for this reason. Mixing calls to the Supabase domain `foobarbaz.supabase.co` and your custom domain could cause those flows to stop working due to the [Same Origin Policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) enforced on cookies by the browser.
diff --git a/apps/docs/pages/guides/platform/database-size.mdx b/apps/docs/pages/guides/platform/database-size.mdx
index ec25d73509e..5d92884eb5f 100644
--- a/apps/docs/pages/guides/platform/database-size.mdx
+++ b/apps/docs/pages/guides/platform/database-size.mdx
@@ -9,7 +9,7 @@ export const meta = {
Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily. As you read this document we will refer to "database size" and "disk size":
- "Database size" is the total size of used storage from your database.
-- "Disk size" describes the size of the underlying available storage.
+- "Disk Storage size" describes the size of the underlying available storage.
## Database space management
@@ -23,7 +23,7 @@ select
from pg_database;
```
-This value is reported in the [database settings page](https://app.supabase.com/project/_/settings/database).
+This value is reported in the [database report page](https://app.supabase.com/project/_/reports/database).
Database Space is consumed primarily by your data, indexes, and materialized views. You can reduce your disk size by removing any of these and running a Vacuum operation.
@@ -61,16 +61,18 @@ Supabase uses network-attached storage to balance performance with scalability.
Pro and Enterprise projects have auto-scaling Disk Storage.
-Disk storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode.
+Disk Storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode.
-
+The maximum Disk Storage Size for Pro Tier is 64TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan.
-If you intend to import a lot of data into your database which requires multiple disk expansions then [reach out to our team](https://app.supabase.com/support/new). For example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode).
+The Disk Storage Size can also be manually expanded in [Database settings page](https://app.supabase.com/project/_/settings/database). The maximum default you can expand the Disk Storage to is 200GB. If you wish to manually expand it any more than 200B, please [contact us](https://app.supabase.com/support/new) to discuss expanding the 200GB limit.
+
+
+
+You may want to import a lot of data into your database which requires multiple disk expansions; for example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode). If so, it is highly recommended you increase the Disk Storage Size manually on the [Database settings page](https://app.supabase.com/project/_/settings/database).
-The maximum Disk Size for Pro Tier is 64TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan.
-
### Free Tier Behavior
Free Tier projects enter [read-only](#read-only-mode) mode when you exceed the 500mb limit. Once in read-only mode, you have several options:
diff --git a/apps/docs/pages/guides/self-hosting/docker.mdx b/apps/docs/pages/guides/self-hosting/docker.mdx
index d789a0e9ee1..d1382bdf9f9 100644
--- a/apps/docs/pages/guides/self-hosting/docker.mdx
+++ b/apps/docs/pages/guides/self-hosting/docker.mdx
@@ -68,6 +68,10 @@ Update the `.env` file with your own secrets. In particular, these are required:
The Docker setup doesn't include a management database for managing users and logins. If you plan to deploy the Studio to the web we suggest you put it behind a web proxy with Basic Auth or hide it behind a VPN.
+## Setting up Edge Functions
+
+Your Functions are stored in `volumes/functions`. The default setup has a `hello` Function that you can invoke on `http://localhost:8000/functions/v1/hello`. You can add new Functions as `volumes/functions//index.ts`.
+
## Configuration
Each system can be [configured](../self-hosting#configuration) to suit your particular use-case.
diff --git a/apps/docs/pages/index.mdx b/apps/docs/pages/index.mdx
index e7459d7e7e6..31c0a5a03fe 100644
--- a/apps/docs/pages/index.mdx
+++ b/apps/docs/pages/index.mdx
@@ -198,7 +198,6 @@ export const products = [
href: '/guides/database',
description:
'Supabase provides a full Postgres database for every project with Realtime functionality, database backups, extensions, and more.',
- span: 'col-span-12 md:col-span-6',
},
{
title: 'Auth',
@@ -207,7 +206,6 @@ export const products = [
href: '/guides/auth',
description:
'Add and manage email and password, passwordless, OAuth, and mobile logins to your project through a suite of identity providers and APIs.',
- span: 'col-span-12 md:col-span-6',
},
{
title: 'Storage',
@@ -217,6 +215,13 @@ export const products = [
description:
'Store, organize, transform, and serve large files—fully integrated with your Postgres database with Row Level Security access policies.',
},
+ {
+ title: 'AI & Vectors',
+ icon: 'ai',
+ hasLightIcon: true,
+ href: '/guides/ai',
+ description: 'Use Supabase to store and search embedding vectors.',
+ },
{
title: 'Realtime',
icon: 'realtime',
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/ask-chatgpt.png b/apps/docs/public/img/ai/chatgpt-plugins/ask-chatgpt.png
new file mode 100644
index 00000000000..c936d71b68a
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/ask-chatgpt.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-change-model.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-change-model.png
new file mode 100644
index 00000000000..5151367c291
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-change-model.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-local-plugin.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-local-plugin.png
new file mode 100644
index 00000000000..7b9a3158130
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-local-plugin.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--dark.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--dark.png
new file mode 100644
index 00000000000..261b95b3d28
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--dark.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--light.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--light.png
new file mode 100644
index 00000000000..a6e7490ffeb
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--light.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-store.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-store.png
new file mode 100644
index 00000000000..ecff2df51d4
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugin-store.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugins-support-postgres.jpeg b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugins-support-postgres.jpeg
new file mode 100644
index 00000000000..535a886b22b
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-plugins-support-postgres.jpeg differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-query.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-query.png
new file mode 100644
index 00000000000..08b8553d6d9
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-query.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-reply.png b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-reply.png
new file mode 100644
index 00000000000..d3b34a61028
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/chatgpt-reply.png differ
diff --git a/apps/docs/public/img/ai/chatgpt-plugins/openai-secret-keys.png b/apps/docs/public/img/ai/chatgpt-plugins/openai-secret-keys.png
new file mode 100644
index 00000000000..e0f2e6e0247
Binary files /dev/null and b/apps/docs/public/img/ai/chatgpt-plugins/openai-secret-keys.png differ
diff --git a/apps/docs/public/img/ai/google-colab/colab-documents.png b/apps/docs/public/img/ai/google-colab/colab-documents.png
new file mode 100644
index 00000000000..95f995ea971
Binary files /dev/null and b/apps/docs/public/img/ai/google-colab/colab-documents.png differ
diff --git a/apps/docs/public/img/ai/google-colab/colab-new.png b/apps/docs/public/img/ai/google-colab/colab-new.png
new file mode 100644
index 00000000000..19d7c09ecda
Binary files /dev/null and b/apps/docs/public/img/ai/google-colab/colab-new.png differ
diff --git a/apps/docs/public/img/ai/google-colab/colab-results.png b/apps/docs/public/img/ai/google-colab/colab-results.png
new file mode 100644
index 00000000000..6fc50dc5818
Binary files /dev/null and b/apps/docs/public/img/ai/google-colab/colab-results.png differ
diff --git a/apps/docs/public/img/ai/google-colab/install-vecs.png b/apps/docs/public/img/ai/google-colab/install-vecs.png
new file mode 100644
index 00000000000..0126e0e2fa0
Binary files /dev/null and b/apps/docs/public/img/ai/google-colab/install-vecs.png differ
diff --git a/apps/docs/public/img/ai/headless-search/headless.png b/apps/docs/public/img/ai/headless-search/headless.png
new file mode 100644
index 00000000000..f9cbdc0457d
Binary files /dev/null and b/apps/docs/public/img/ai/headless-search/headless.png differ
diff --git a/apps/docs/public/img/ai/hugging-face/llama-sunglasses-example.png b/apps/docs/public/img/ai/hugging-face/llama-sunglasses-example.png
new file mode 100644
index 00000000000..6fd4ff55638
Binary files /dev/null and b/apps/docs/public/img/ai/hugging-face/llama-sunglasses-example.png differ
diff --git a/apps/docs/public/img/ai/scaling/multi-database.png b/apps/docs/public/img/ai/scaling/multi-database.png
new file mode 100644
index 00000000000..7593bc443fd
Binary files /dev/null and b/apps/docs/public/img/ai/scaling/multi-database.png differ
diff --git a/apps/docs/public/img/ai/scaling/single-database.png b/apps/docs/public/img/ai/scaling/single-database.png
new file mode 100644
index 00000000000..d48fed75d9c
Binary files /dev/null and b/apps/docs/public/img/ai/scaling/single-database.png differ
diff --git a/apps/docs/public/img/ai/scaling/with-secondaries.png b/apps/docs/public/img/ai/scaling/with-secondaries.png
new file mode 100644
index 00000000000..50c3798f7c6
Binary files /dev/null and b/apps/docs/public/img/ai/scaling/with-secondaries.png differ
diff --git a/apps/docs/public/img/ai/vector-similarity.png b/apps/docs/public/img/ai/vector-similarity.png
new file mode 100644
index 00000000000..b2282e76f1e
Binary files /dev/null and b/apps/docs/public/img/ai/vector-similarity.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png b/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png
new file mode 100644
index 00000000000..62bf401b5d0
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png
new file mode 100644
index 00000000000..5e21934d1db
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png
new file mode 100644
index 00000000000..48492f5b041
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png
new file mode 100644
index 00000000000..7087ea22f74
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png
new file mode 100644
index 00000000000..20f2a470c3b
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png
new file mode 100644
index 00000000000..cf13d0fa951
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png differ
diff --git a/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx b/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx
index 29553ffa88e..6b43011f606 100644
--- a/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx
+++ b/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx
@@ -100,7 +100,7 @@ Discover 100+ other apps in this [tweet](https://twitter.com/supabase/status/162

-- Langchain supports our database as a vector store, using pgvector. [Doc](https://hwchase17.github.io/langchainjs/docs/modules/indexes/vector_stores/supabase/)
+- LangChain supports our database as a vector store, using pgvector. [Doc](https://hwchase17.github.io/langchainjs/docs/modules/indexes/vector_stores/supabase/)
- refine week: building a CRUD app with refine & Supabase. [Full series](https://refine.dev/week-of-refine/)
diff --git a/apps/www/_blog/2023-04-14-launch-week-7-community-highlights.mdx b/apps/www/_blog/2023-04-14-launch-week-7-community-highlights.mdx
index 5f74875cf0d..78c164718e0 100644
--- a/apps/www/_blog/2023-04-14-launch-week-7-community-highlights.mdx
+++ b/apps/www/_blog/2023-04-14-launch-week-7-community-highlights.mdx
@@ -38,7 +38,7 @@ Along with our new Clickhouse [Foreign Data Wrapper](https://supabase.github.io/
### LangChain
-It’s always fun when two open source communities join forces. LangChain is an open source framework for developing applications powered by language models. This week, to support the Supabase AI Hackathon, they released a [Supabase x Langchain template](https://github.com/langchain-ai/langchain-template-supabase), one of the fastest ways to build AI applications. Their community recently made two contributions for Supabase: adding Supabase as a [Vector Store](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase), and a neat [Hybrid Search](https://js.langchain.com/docs/modules/indexes/retrievers/supabase-hybrid) algorithm using Postgres’ Full Text Search. The also
+It’s always fun when two open source communities join forces. LangChain is an open source framework for developing applications powered by language models. This week, to support the Supabase AI Hackathon, they released a [Supabase x LangChain template](https://github.com/langchain-ai/langchain-template-supabase), one of the fastest ways to build AI applications. Their community recently made two contributions for Supabase: adding Supabase as a [Vector Store](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase), and a neat [Hybrid Search](https://js.langchain.com/docs/modules/indexes/retrievers/supabase-hybrid) algorithm using Postgres’ Full Text Search. The also
[Read the integration docs.](https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase)
diff --git a/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx b/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx
new file mode 100644
index 00000000000..c094a0f0826
--- /dev/null
+++ b/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx
@@ -0,0 +1,268 @@
+---
+title: 'ChatGPT plugins now support Postgres & Supabase'
+description: Supabase recently contributed to the OpenAI Retrieval Plugin repo with a Postgres and a Supabase implementation to help developers build ChatGPT plugins using pgvector.
+tags:
+ - AI
+ - tutorial
+ - postgres
+date: '2023-05-25'
+toc_depth: 2
+author: egor_romanov,paul_copplestone
+image: 2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugins-support-postgres.jpeg
+thumb: 2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugins-support-postgres.jpeg
+---
+
+One of the challenges that ChatGPT faces is being able to answer questions from a private dataset. We can solve this with “retrieval plugins”, which allow ChatGPT to access information from a database.
+
+Supabase recently contributed to the OpenAI repo with a [Postgres](https://github.com/openai/chatgpt-retrieval-plugin#postgres) and a [Supabase](https://github.com/openai/chatgpt-retrieval-plugin#supabase) implementation to help developers build plugins using pgvector.
+
+Let’s dig into the specifics of Retrieval plugins, then we can implement an example - we’ll ingest all of the Postgres docs into a Supabase database, then get ChatGPT to answer questions. It’s a contrived example since ChatGPT already knows about Postgres, but what other data source would Supabase want to use?
+
+## What is ChatGPT Retrieval Plugin?
+
+ChatGPT recently released [Plugins](https://openai.com/blog/chatgpt-plugins) which help ChatGPT access up-to-date information, run computations, or use third-party services.
+
+A [Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin) is a Python project designed to inject external data into the ChatGPT. It allows ChatGPT to dynamically pull relevant information into conversations from your data sources. This could be PDF documents, Confluence, or Notion knowledge bases.
+
+A retrieval plugin does a few things:
+
+1. Turn documents into smaller chunks.
+2. Converts chunks into embeddings using OpenAI's `text-embedding-ada-002` model.
+3. Stores the embeddings into a vector database.
+4. Queries the vector database for relevant documents when a question is asked.
+
+You can choose your preferred vector database provider from a list of [supported options](https://github.com/openai/chatgpt-retrieval-plugin#choosing-a-vector-database).
+
+## Adding Supabase and Postgres as Datastore options for ChatGPT Retrieval Plugin
+
+We've implemented two vector provider options: one for Postgres and one for Supabase. The main differences are:
+
+- The Postgres version uses the `psycopg2` python library to directly connect to the database.
+- The Supabase version interacts with the database via PostgREST. This is helpful if you want to use [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security) or if you are planning to use the data in the Retrieval store beyond ChatGPT.
+
+The Postgres implementation is great to start with because there are now a [large number of providers](https://github.com/pgvector/pgvector/issues/54) supporting pgvector.
+
+Both have the same schema so you can easily switch between them:
+
+```sql
+create table if not exists documents (
+ id text primary key default gen_random_uuid()::text,
+ source text,
+ source_id text,
+ content text,
+ document_id text,
+ author text,
+ url text,
+ created_at timestamptz default now(),
+ embedding vector(1536)
+);
+```
+
+When you create the retrieval store inside your database, a stored function is implemented to query and find relevant information for your question to ChatGPT:
+
+```sql
+create or replace function match_page_sections(
+ in_embedding vector(1536),
+ in_match_count int default 3,
+ in_document_id text default '%%',
+ in_source_id text default '%%',
+ in_source text default '%%',
+ in_author text default '%%',
+ in_start_date timestamptz default '-infinity',
+ in_end_date timestamptz default 'infinity'
+)
+returns table (
+ id text,
+ source text,
+ source_id text,
+ document_id text,
+ url text,
+ created_at timestamptz,
+ author text,
+ content text,
+ embedding vector(1536),
+ similarity float
+)
+language plpgsql
+as $$
+#variable_conflict use_variable
+begin
+return query
+
+select
+ documents.id,
+ documents.source,
+ documents.source_id,
+ documents.document_id,
+ documents.url,
+ documents.created_at,
+ documents.author,
+ documents.content,
+ documents.embedding,
+ (documents.embedding <#> in_embedding) * -1 as similarity
+from
+ documents
+where
+ in_start_date <= documents.created_at and
+ documents.created_at <= in_end_date and
+ (documents.source_id like in_source_id or documents.source_id is null) and
+ (documents.source like in_source or documents.source is null) and
+ (documents.author like in_author or documents.author is null) and
+ (documents.document_id like in_document_id or documents.document_id is null)
+order by
+ documents.embedding <#> in_embedding
+limit
+ in_match_count;
+end;
+$$;
+```
+
+We apply filters based on the source, author, document, and date, and find the closest embeddings using the inner product distance function. This function offers the best performance when the embeddings are normalized, which is the case for OpenAI embeddings. The similarity is calculated as: `(documents.embedding <#> in_embedding) * -1 as similarity`. And that’s it, you can now seamlessly use the Retrieval Plugin with a Postgres Database underneath, eliminating the need for any manual implementation on your end.
+
+## Example: Chat with Postgres Docs
+
+Let’s build an example where we can “ask ChatGPT questions” about the Postgres documentation.
+
+This will require several steps:
+
+1. Download all the [Postgres docs as a PDF](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf)
+2. Convert the docs into chunks of embedded text and store them in Supabase
+3. Run our plugin locally so that we can ask questions about the Postgres docs.
+
+
+
+
+
+### Step 1: Fork the ChatGPT Retrieval Plugin repository
+
+Fork the ChatGPT Retrieval Plugin repository to your GitHub account and clone it to your local machine. Read through the `README.md` file to understand the project structure.
+
+### Step 2: Install dependencies
+
+Choose your desired datastore provider and remove unused dependencies from `pyproject.toml`. For this example, we'll use Supabase. And install dependencies with Poetry:
+
+```bash
+poetry install
+```
+
+### Step 3: Create a Supabase project
+
+Create a [Supabase project](https://supabase.com/dashboard) and database by following the instructions [here](https://supabase.com/docs/guides/platform). Export the environment variables required for the retrieval plugin to work:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=supabase
+export SUPABASE_URL=
+export SUPABASE_SERVICE_ROLE_KEY=
+```
+
+For Postgres datastore, you'll need to export these environment variables instead:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=postgres
+export PG_HOST=
+export PG_PASSWORD=
+```
+
+### Step 4: Run Postgres Locally
+
+To start quicker you may use Supabase CLI to spin everything up locally as it already includes pgvector from the start. Install `supabase-cli`, go to the `examples/providers` folder in the repo and run:
+
+```bash
+supabase start
+```
+
+This will pull all docker images and run supabase stack in docker on your local machine. It will also apply all the necessary migrations to set the whole thing up. You can then use your local setup the same way, just export the environment variables and follow to the next steps.
+
+Using `supabase-cli` is not required and you can use any other docker image or hosted version of PostgresDB that includes `pgvector`. Just make sure you run migrations from `examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql`.
+
+### Step 5: Obtain OpenAI API key
+
+To create embeddings Plugin uses OpenAI API and `text-embedding-ada-002` model. Each time we add some data to our datastore, or try to query relevant information from it, embedding will be created either for inserted data chunk, or for the query itself. To make it work we need to export `OPENAI_API_KEY`. If you already have an account in OpenAI, you just need to go to [User Settings - API keys](https://platform.openai.com/account/api-keys) and Create new secret key.
+
+
+
+### Step 6: Run the plugin!
+
+Execute the following command to run the plugin:
+
+```bash
+poetry run dev
+# output
+INFO: Will watch for changes in these directories: ['./chatgpt-retrieval-plugin']
+INFO: Uvicorn running on http://localhost:3333 (Press CTRL+C to quit)
+INFO: Started reloader process [87843] using WatchFiles
+INFO: Started server process [87849]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+The plugin will start on your localhost - port :3333 by default.
+
+### Step 6: Populating data in the datastore
+
+For this example, we'll upload Postgres documentation to the datastore. Download the [Postgres documentation](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf) and use the `/upsert-file` endpoint to upload it:
+
+```bash
+curl -X POST -F \\"file=@./postgresql-15-US.pdf\\"
+```
+
+The plugin will split your data and documents into smaller chunks automatically. You can view the chunks using the Supabase dashboard or any other SQL client you prefer. For the whole Postgres Documentation I got 7,904 records in my documents table, which is not a lot, but we can try to add index for `embedding` column to speed things up by a little. To do so, you should run the following SQL command:
+
+```sql
+create index on documents
+using ivfflat (embedding vector_ip_ops)
+with (lists = 10);
+```
+
+This will create an index for the inner product distance function. Important to note that it is an approximate index. It will change the logic from performing the exact nearest neighbor search to the approximate nearest neighbor search.
+
+We are using `lists = 10`, because as a general guideline, you should start looking for optimal lists constant value with the formula: `rows / 1000` when you have less than 1 million records in your table.
+
+Now, it is time to add our plugin to ChatGPT.
+
+### Empowering ChatGPT with Postgres knowledge
+
+To integrate our plugin with ChatGPT, register it in the ChatGPT dashboard. Assuming you have access to ChatGPT Plugins and plugin development, select the Plugins model in a new chat, then choose "Plugin store" and "Develop your own plugin." Enter `localhost:3333` into the domain input, and your plugin is now part of ChatGPT.
+
+
+
+
+
+
+
+You can now ask questions about Postgres and receive answers derived from the documentation!
+
+Let's try it out: ask ChatGPT to find out when to use `check` and when to use `using`. You will be able to see what queries were sent to our plugin and what it responded to.
+
+
+
+
+
+And after ChatGPT receives a response from the plugin it will answer your question with the data from the documentation.
+
+
+
+## Wrap up
+
+It's easy to bring any context into the datastore and utilize it with ChatGPT. Simply export your knowledge base from platforms like Notion or Confluence, upload it to the datastore, and you're good to go. You can also use any other datastore provider you prefer.
+
+And the good news is that you’re not limited by using it with ChatGPT, you can embed it in your website or documentation, and build a Slack bot or telegram bot to answer questions about your company or product. For that, you will only need to add a single call to OpenAI API to create a summary of data retrieved from the Plugin. You can find some inspiration on how to do that in our blog post about building [Supabase Clippy assistant](https://supabase.com/blog/chatgpt-supabase-docs).
+
+Let us know on [Twitter](https://twitter.com/Supabase) if you are building ChatGPT Plugins. We can’t wait to see what you will build!
+
+## More AI resources
+
+- [OpenAI ChatGPT Plugin docs](https://platform.openai.com/docs/plugins/introduction)
+- [ChatGPT Retrieval Plugin Repo](https://github.com/openai/chatgpt-retrieval-plugin)
+- [How to build ChatGPT Plugin from scratch with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template).
+- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
diff --git a/apps/www/_blog/2023-05-29-flutter-hackathon-winners.mdx b/apps/www/_blog/2023-05-29-flutter-hackathon-winners.mdx
new file mode 100644
index 00000000000..48ab9a0bf74
--- /dev/null
+++ b/apps/www/_blog/2023-05-29-flutter-hackathon-winners.mdx
@@ -0,0 +1,137 @@
+---
+title: 'Flutter Hackathon Winners'
+description: Announcing the winners of the Flutter Hackathon!
+author: tyler_shukert
+image: flutter-hackathon-winners/flutter-hackathon-winners.png
+thumb: flutter-hackathon-winners/flutter-hackathon-winners.png
+tags:
+ - hackathon
+ - community
+date: '2023-05-29'
+toc_depth: 2
+---
+
+Last week we wrapped up the Flutter Hackathon, and we were blown away by the results. Today, I have the honor of announcing the winners, along with our good friends [Majid](https://twitter.com/mhadaily) from [Invertase](https://invertase.io/) and [Pooja](https://twitter.com/pooja_bhaumik) from [FlutterFlow](https://flutterflow.io/).
+
+Now, without further ado, let's look at some of our favorites in more detail:
+
+
\""
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Look at an example review\n",
+ "data[\"text\"][5]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "955b2700-8242-40eb-ac3f-d479a0312693",
+ "metadata": {},
+ "source": [
+ "## Embedding Model\n",
+ "\n",
+ "Next, we can use the `sentence-transformers/all-MiniLM-L6-v2` model to create a 384 dimensional text embedding that represents the\n",
+ "semantic meaning of each review. These embeddings are what we'll use for near-duplicate detection."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "ce9bc85b-e844-407c-a0ad-ccf6af3c8866",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sentence_transformers import SentenceTransformer\n",
+ "\n",
+ "model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9e82862d-440a-4f66-9ed7-0eaa6a0f4062",
+ "metadata": {},
+ "source": [
+ "## Initialize the Vecs Collection\n",
+ "\n",
+ "The [`vecs`](https://supabase.github.io/vecs/api/) library wraps a pythonic interface around PostgreSQL and pgvector.\n",
+ "A collection in `vecs` maps 1:1 with a PostgreSQL table."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "d2771545-d209-4ceb-a222-ed139a4620f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import vecs\n",
+ "\n",
+ "# Substitute your connection string here\n",
+ "DB_CONNECTION = \"postgresql://postgres:password@localhost:5431/db\"\n",
+ "\n",
+ "# create vector store client\n",
+ "vx = vecs.create_client(DB_CONNECTION)\n",
+ "\n",
+ "# create a PostgreSQL/pgvector table named \"reviews\" to contain the review embeddings\n",
+ "reviews = vx.create_collection(name=\"reviews\", dimension=384)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9ae030b4-cfd2-43bc-802f-e7ac4007d2ad",
+ "metadata": {},
+ "source": [
+ "## Create Embeddings for Each Review\n",
+ "\n",
+ "Now we can iterate over the dataset, producing embeddings for the reviews"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "bd134310-9da1-4448-8358-9fc491c98e1e",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "500it [07:06, 1.17it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "from typing import List, Dict, Tuple\n",
+ "from flupy import flu\n",
+ "import numpy as np\n",
+ "from tqdm import tqdm\n",
+ "\n",
+ "\n",
+ "batch_size = 50\n",
+ "\n",
+ "records: List[Tuple[str, np.ndarray, Dict]] = []\n",
+ "\n",
+ "# Iterate over the dataset in chunks\n",
+ "for chunk_ix, chunk in tqdm(flu(data['text']).chunk(batch_size).enumerate()):\n",
+ "\n",
+ " # Create embeddings for current chunk\n",
+ " embedding_chunk = model.encode(chunk)\n",
+ "\n",
+ " # Enumerate the embeddings and create a record to insert into the database\n",
+ " for row_ix, (text, embedding) in enumerate(zip(chunk, embedding_chunk)):\n",
+ " record_id = chunk_ix * batch_size + row_ix\n",
+ " records.append((f\"{record_id}\", embedding, {\"text\": text}))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6ef285d7-dc7b-4576-ab2b-bf0a4ba06de4",
+ "metadata": {},
+ "source": [
+ "## Insert the Embeddings into Postgres"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "6156e7f9-b78a-4ab9-8002-b09ec8716be2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "reviews.upsert(records)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "aa095a53-dd9c-4a3c-93bf-c54708c67765",
+ "metadata": {},
+ "source": [
+ "## Index the Collection\n",
+ "\n",
+ "Indexing the collection creates an index on the vector column in Postgres that significantly improves performance\n",
+ "of similarity queries."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "6b96d3af-5592-4fbc-81ab-f77b4228ccaa",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "reviews.create_index()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "371631e6-4995-484f-9d76-40ab5e7b2e16",
+ "metadata": {},
+ "source": [
+ "## Search for Near Duplicates\n",
+ "\n",
+ "Finally we can enumerate each review, searching for the most similar\n",
+ "reviews and displaying them if the results are near duplicates. We could then prune out the near-duplicate reviews\n",
+ "to make sure our viewers see a new and interesting opinion with each review they choose to read."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "d2a5c699-7c04-48ad-9033-a310bbb0dffc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "824it [00:05, 187.11it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "query_id: 817 \t result_id: 823 \t distance 0.1672 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " This has to be, by far, the absolute worst movie I have seen in the last 20 years. When I saw that Michael Madsen was in it I figured it couldn't be too bad a movie since he has been in some pretty decent films, and he was a pretty fair actor. WRONG! No one should waste their time on this film. I fast forwarded through 80 percent of it and I don't feel that I missed a thing. \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " I agree totally with the last commenter this could be the worst movie ever made .I too had to fast forward through most of this movie. Michael Madsen must have done this movie as a favor to someone.The picture quality is grainy all the way through .And what little plot there is,is just plain stupid .I give this movie a 1 out of 10 if I could give it a lower score I would .Don't waste your time on this movie or you'll regret it. \n",
+ " --------------------------------------------------------------------------------\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "8675it [00:47, 166.43it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "query_id: 8663 \t result_id: 8669 \t distance 0.1532 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " Rodney Dangerfield is a great. He has done a lot of great works. But this one....is awful. The whole plot is whack. It could have been much better. The jokes in the movie aren't funny....their stupid. This was very not so hilarious. He can do much better than this. \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " As much as I love Rodney Dangerfield, this was a terrible movie. The plot was kind of a holistic rip off of various movies, but unfortunately they forgot to rip off any good jokes. In addition it was annoying and boring and that's being kind. If you're looking for a good laugh, rent a copy of Private Parts. \n",
+ " --------------------------------------------------------------------------------\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "10522it [00:58, 164.98it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "query_id: 10486 \t result_id: 5437 \t distance 0.1634 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " Aside for being classic in the aspect of its cheesy lines and terrible acting, this film should never be watched unless you are looking for a good cure for your insomnia. I can't imagine anyone actually thinking this was a \"good movie.\" \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " Aside from the horrendous acting and the ridiculous and ludicrous plot, this movie wasn't too bad. Unfortunately, that doesn't leave much movie not to suck. Do not waste your time on this film, even if you find yourself suffering from insomnia, as I did. Watch an infomercial instead. \n",
+ " --------------------------------------------------------------------------------\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "16405it [01:36, 97.07it/s] "
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "query_id: 16401 \t result_id: 16424 \t distance 0.1583 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " This has to be the funniest stand up comedy I have ever seen. Eddie Izzard is a genius, he picks in Brits, Americans and everyone in between. His style is completely natural and completely hilarious. I doubt that anyone could sit through this and not laugh their a** off. Watch, enjoy, it's funny. \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " Until I saw this special on HBO, I had never heard of Eddie Izzard. I sure am glad that I have now! He is one of the funniest comedians I have ever seen! Rarely has a comedian immersed himself so completely in his craft then Eddie. I could not stop laughing for the entire show. If you like to laugh you HAVE to see this special! \n",
+ " --------------------------------------------------------------------------------\n",
+ "query_id: 16401 \t result_id: 16408 \t distance 0.1663 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " This has to be the funniest stand up comedy I have ever seen. Eddie Izzard is a genius, he picks in Brits, Americans and everyone in between. His style is completely natural and completely hilarious. I doubt that anyone could sit through this and not laugh their a** off. Watch, enjoy, it's funny. \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " This is another gem of a stand up show from Eddie Izzard . You cannot fail to laugh at the wide range of topics he talks about. He even takes the piss out of his American audiance at times and most of them didnt even realise it! A must see for anybody who likes comedians. 9 out of 10. \n",
+ " --------------------------------------------------------------------------------\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "18953it [01:54, 101.67it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "query_id: 18936 \t result_id: 18942 \t distance 0.1325 \n",
+ "\n",
+ " Query Text \n",
+ "\n",
+ " World At War is perhaps the greatest documentary series of all time. The historical research is virtually flawless. Even after a quarter century, it is the most accurate and definitive documentary about WW2. An invaluable historical work that includes interviews with some of the most important and fascinating figures from the war. I highly recommend it as a learning experience. \n",
+ "\n",
+ " Result Text \n",
+ "\n",
+ " The world at war is one of the best documentaries about world war 2.
The 24 episodes cover the war and what it was like in the countries involved in it. The first episode tells us how the Hitler came to power, and how he was able to build up one of the strongest armies in the world. They also fucus on the military actions taken during the war, and the holocaust. One of the strongest and best documentaries ever made. All of you must watch this. Perfection! 10/10
) : null}
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/AddNewPaymentMethodModal.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/AddNewPaymentMethodModal.tsx
index 15b5b74d20c..b00322e8279 100644
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/AddNewPaymentMethodModal.tsx
+++ b/studio/components/interfaces/BillingV2/Subscription/Tier/AddNewPaymentMethodModal.tsx
@@ -9,6 +9,7 @@ import { useCallback, useEffect, useState } from 'react'
import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store'
import { Modal } from 'ui'
import AddNewPaymentMethodForm from './AddNewPaymentMethodForm'
+import { useTheme } from 'common'
// [Joshen] Directly brought over from old Billing folder, so we can deprecate that folder easily next time
@@ -84,9 +85,11 @@ const AddNewPaymentMethodModal = ({
}
}
+ const { isDarkMode } = useTheme()
+
const options = {
clientSecret: intent ? intent.client_secret : '',
- appearance: { theme: 'night', labels: 'floating' },
+ appearance: { theme: isDarkMode ? 'night' : 'flat', labels: 'floating' },
} as any
const onLocalCancel = () => {
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/PaymentMethodSelection.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/PaymentMethodSelection.tsx
index 495f2b4ed72..f0008936b6a 100644
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/PaymentMethodSelection.tsx
+++ b/studio/components/interfaces/BillingV2/Subscription/Tier/PaymentMethodSelection.tsx
@@ -147,7 +147,10 @@ const PaymentMethodSelection = ({
category: 'success',
message: 'Successfully added new payment method',
})
- await refetchPaymentMethods()
+ const { data } = await refetchPaymentMethods()
+ if (!selectedPaymentMethod && data?.length) {
+ onSelectPaymentMethod(data[0].id)
+ }
}}
/>
>
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx
index 4fcdf9405d2..e61b6193a97 100644
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx
+++ b/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx
@@ -89,7 +89,7 @@ const SubscriptionTier = ({}: SubscriptionTierProps) => {
When this project exceeds its included usage quotas, it may become unresponsive.
{currentPlan?.id === 'free'
- ? 'If you wish to exceed the included usage, it is advised you upgrade to a paid plan.'
+ ? 'If you wish to exceed the included usage, you should upgrade to a paid plan.'
: 'You can change the Cost Control settings if you plan on exceeding the included usage quotas.'}
@@ -98,7 +98,7 @@ const SubscriptionTier = ({}: SubscriptionTierProps) => {
type="horizontal"
value={daysWithinCycle - daysToCycleEnd}
max={daysWithinCycle}
- barClass="bg-scale-1100"
+ barClass="bg-scale-1200"
labelBottom={`Current billing cycle (${billingCycleStart.format(
'MMM DD'
)} - ${billingCycleEnd.format('MMM DD')})`}
diff --git a/studio/components/interfaces/BillingV2/Usage/Activity.tsx b/studio/components/interfaces/BillingV2/Usage/Activity.tsx
index c56c895096e..a5e7ef7af2a 100644
--- a/studio/components/interfaces/BillingV2/Usage/Activity.tsx
+++ b/studio/components/interfaces/BillingV2/Usage/Activity.tsx
@@ -16,9 +16,10 @@ import UsageBarChart from './UsageBarChart'
import SectionContent from './SectionContent'
import SectionHeader from './SectionHeader'
import { USAGE_CATEGORIES } from './Usage.constants'
-import { getUpgradeUrl } from './Usage.utils'
+import { ChartYFormatterCompactNumber, getUpgradeUrl } from './Usage.utils'
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import { DataPoint } from 'data/analytics/constants'
+import Panel from 'components/ui/Panel'
export interface ActivityProps {
projectRef: string
@@ -32,14 +33,32 @@ const Activity = ({ projectRef }: ActivityProps) => {
current_period_start !== undefined
? new Date(current_period_start * 1000).toISOString()
: undefined
- const endDate =
+ let endDate =
current_period_end !== undefined ? new Date(current_period_end * 1000).toISOString() : undefined
+
+ // If end date is in future, set end date to yesterday/now
+ if (endDate && dayjs(endDate).isAfter(dayjs())) {
+ const yesterday = dayjs(new Date()).subtract(1, 'day')
+
+ /**
+ * Currently, daily-stats data is only available a day later, so we'll use yesterday as end date, as otherwise the current day would just show up with "0" values
+ *
+ * We are actively working on removing this restriction on the data-eng/LF side and can remove this workaround once that's done
+ */
+ let newEndDate = yesterday.isAfter(dayjs(startDate)) ? yesterday : new Date()
+
+ // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
+ endDate = newEndDate.toISOString().slice(0, -5) + 'Z'
+ }
+
const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === 'activity')
const upgradeUrl = getUpgradeUrl(projectRef, subscription)
+
const isFreeTier = subscription?.tier.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.FREE
const isProTier = subscription?.tier.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.PRO
- const exceededLimitStyle = isFreeTier || isProTier ? 'text-red-900' : 'text-amber-900'
+ const usageBasedBilling = !isFreeTier && !isProTier
+ const exceededLimitStyle = !usageBasedBilling ? 'text-red-900' : 'text-amber-900'
const { data: mauData, isLoading: isLoadingMauData } = useDailyStatsQuery({
projectRef,
@@ -149,27 +168,16 @@ const Activity = ({ projectRef }: ActivityProps) => {
const chartData = chartMeta[attribute.key]?.data ?? []
- // [Joshen] Ideally this should come from the API imo, foresee some discrepancies
- const lastZeroValue = chartData.find(
- (x: any) => x.loopId > 0 && x[attribute.attribute] === 0
- )
- const lastKnownValue =
- lastZeroValue !== undefined && !chartMeta[attribute.key]?.hasNoData
- ? dayjs(lastZeroValue.period_start)
- .subtract(1, 'day')
- .format('DD MMM YYYY, HH:mma (ZZ)')
- : undefined
-
return (
- {/*
- [Joshen] Could potentially run a map here based on USAGE_CATEGORIES, rather than defining each section
+ {/*
+ [Joshen] Could potentially run a map here based on USAGE_CATEGORIES, rather than defining each section
but thinking it's gonna "cover up" too much details and make it harder to add attribute specific components
e.g for database size, we also need to show disk volume size. Not to mention that are little nuances across
each attribute RE formatting (bytes vs locale string)
diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.utils.ts b/studio/components/interfaces/BillingV2/Usage/Usage.utils.ts
index d4286819f02..99f6092748f 100644
--- a/studio/components/interfaces/BillingV2/Usage/Usage.utils.ts
+++ b/studio/components/interfaces/BillingV2/Usage/Usage.utils.ts
@@ -4,6 +4,7 @@ import { USAGE_APPROACHING_THRESHOLD } from '../Billing.constants'
import { CategoryAttribute, USAGE_STATUS } from './Usage.constants'
import { StripeSubscription } from 'components/interfaces/Billing'
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
+import { formatBytes } from 'lib/helpers'
// [Joshen] This is just for development to generate some test data for chart rendering
export const generateUsageData = (attribute: string, days: number): DataPoint[] => {
@@ -46,3 +47,20 @@ export const getUpgradeUrl = (projectRef: string, subscription?: StripeSubscript
? `/project/${projectRef}/settings/billing/update`
: `/project/${projectRef}/settings/billing/update/pro`
}
+
+const compactNumberFormatter = new Intl.NumberFormat('en-US', {
+ notation: 'compact',
+ compactDisplay: 'short',
+})
+
+export const ChartYFormatterCompactNumber = (number: number | string, unit: string) => {
+ if (typeof number === 'string') return number
+
+ if (unit === 'bytes') {
+ const formattedBytes = formatBytes(number, 0).replace(/\s/g, '')
+
+ return formattedBytes === '0bytes' ? '0' : formattedBytes
+ } else {
+ return compactNumberFormatter.format(number)
+ }
+}
diff --git a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx
index 2e2dbddae43..efe0289794d 100644
--- a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx
+++ b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx
@@ -58,7 +58,7 @@ const UsageBarChart = ({
= ({
<>
+ This is heavy Work-In-Progress and not customer facing yet, use with caution!
+ This organization will use the new org level billing, instead of having
+ individual subscriptions per project. There are still a lot of open ends that
+ may be restrictive for you, follow #team-billing for updates.
+
+
+ }
+ />
+
+
+
+
This is your organization within Supabase.
+
+ For example, you can use the name of your company or department.
+
+
+ You need additional permissions to increase the disk size
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ We auto-scale your disk as you need more storage, but can only do this every 6 hours.
+ If you upload more than 1.5x the current size of your storage, your database will go
+ into read-only mode. If you know how big your database is going to be, you can
+ manually increase the size here.
+
+ This is heavy Work-In-Progress and not customer facing yet, use with
+ caution! This organization uses the new org level billing, instead of having
+ individual subscriptions per project.
+