Merge pull request #14681 from supabase/master

Prod deploy dashboard
This commit is contained in:
Alaister Young authored and GitHub committed 2023-05-30 17:02:58 +10:00
commit 02f18cbb55
191 files changed
+73519 -58467

No files matched your search

+6 -5
View File
@@ -14,11 +14,12 @@ jobs:
uses: reviewdog/action-misspell@v1
with:
github_token: ${{ secrets.github_token }}
locale: "US"
locale: 'US'
reporter: github-pr-review
level: error
exclude: |
"*.css"
"**/package.json"
"**/package-lock.json"
".git/*"
*.css
**/package.json
**/package-lock.json
.git/*
*.ipynb
+1 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request:
branches: ['master']
paths:
- 'apps/docs/**/*.{ts,tsx}'
- 'apps/docs/**/*.ts*'
jobs:
build:
+3 -3
View File
@@ -17,7 +17,7 @@ const HomePageCover = (props) => {
p-5 md:p-8
"
>
<div className="col-span-full flex flex-col md:flex-row xl:flex-col justify-between gap-1 md:gap-6">
<div className="col-span-full flex flex-col md:flex-row xl:flex-col justify-between gap-1 md:gap-3">
<div className="md:max-w-xs xl:max-w-none">
<div className="flex items-center gap-3 mb-3">
<IconBackground>
@@ -29,7 +29,7 @@ const HomePageCover = (props) => {
Discover how to set up a database to an app making queries in just a few minutes.
</p>
</div>
<div className="flex flex-wrap md:grid md:grid-cols-4 gap-2 sm:gap-3">
<div className="flex flex-wrap md:grid md:grid-cols-4 2xl:grid-cols-7 gap-2 sm:gap-3">
<Link href={`/guides/getting-started/quickstarts/reactjs`} passHref>
<a className="no-underline">
<IconPanel
@@ -118,7 +118,7 @@ const HomePageCover = (props) => {
</p>
</div>
</div>
<div className="w-full xl:max-w-[365px] -mb-40">
<div className="w-full xl:max-w-[365px] 2xl:max-w-[608px] -mb-40">
<GettingStarted />
</div>
</div>
@@ -21,6 +21,7 @@ import {
IconMenuSwift,
IconMenuStatus,
IconMenuKotlin,
IconMenuAI,
} from './HomeMenuIcons'
function getMenuIcon(menuKey: string, width: number = 16, height: number = 16) {
@@ -41,6 +42,8 @@ function getMenuIcon(menuKey: string, width: number = 16, height: number = 16) {
return <IconMenuRealtime width={width} height={height} />
case 'storage':
return <IconMenuStorage width={width} height={height} />
case 'ai':
return <IconMenuAI width={width} height={height} />
case 'platform':
return <IconMenuPlatform width={width} height={height} />
case 'resources':
@@ -356,6 +356,25 @@ export function IconMenuStorage({ width = 16, height = 16 }: HomeMenuIcon) {
<path
d="M13 7.616V5.6l-3.618-3.6H3v4.03m9.964-.447L9.38 2v3.584h3.584ZM1.974 6v8h12V7.509h-7.59l-1.533-1.51H1.974Z"
stroke="currentColor"
stroke-miterlimit="10"
stroke-linejoin="bevel"
/>
</svg>
)
}
export function IconMenuAI({ width = 16, height = 16 }: HomeMenuIcon) {
return (
<svg
width={width}
height={height}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.59777 3.20772C7.25701 4.22996 6.68287 5.15901 5.92094 5.92094C5.15901 6.68288 4.23014 7.25695 3.2079 7.59771L2 8L3.20772 8.40223C4.22996 8.74299 5.15901 9.31712 5.92094 10.0791C6.68287 10.841 7.25695 11.7699 7.59771 12.7921L8 14L8.40223 12.7923C8.74299 11.77 9.31713 10.841 10.0791 10.0791C10.841 9.31712 11.7699 8.74305 12.7921 8.40229L14 8L12.7923 7.59777C11.77 7.25701 10.841 6.68288 10.0791 5.92094C9.31713 5.15901 8.74305 4.23014 8.40229 3.2079L8 2L7.59777 3.20772Z"
stroke="currentColor"
strokeMiterlimit="10"
strokeLinejoin="bevel"
/>
@@ -52,6 +52,12 @@ export const HOMEPAGE_MENU_ITEMS: HomepageMenuItems = [
href: '/guides/storage',
level: 'storage',
},
{
label: 'AI & Vectors',
icon: 'ai',
href: '/guides/ai',
level: 'ai',
},
],
[
{
@@ -280,15 +286,6 @@ export const gettingstarted: NavMenuConstant = {
},
],
},
{
name: 'AI & ML',
items: [
{
name: 'Vector Search with OpenAI',
url: '/guides/getting-started/openai/vector-search',
},
],
},
],
}
@@ -750,8 +747,8 @@ export const functions: NavMenuConstant = {
items: [
{ name: 'Dart Edge on Supabase', url: '/guides/functions/dart-edge' },
{ name: 'Browserless.io', url: '/guides/functions/examples/screenshots' },
{ name: 'Hugging Face', url: '/guides/functions/examples/huggingface-image-captioning' },
{ name: 'OpenAI API', url: '/guides/functions/examples/openai' },
{ name: 'Hugging Face', url: '/guides/ai/examples/huggingface-image-captioning' },
{ name: 'OpenAI API', url: '/guides/ai/examples/openai' },
{ name: 'Upstash Redis', url: '/guides/functions/examples/upstash-redis' },
{ name: 'Type-Safe SQL with Kysely', url: '/guides/functions/kysely-postgres' },
],
@@ -760,7 +757,7 @@ export const functions: NavMenuConstant = {
name: 'Examples',
url: '/guides/functions/examples',
items: [
{ name: 'Generating OpenAI GPT3 completions', url: '/guides/functions/examples/openai' },
{ name: 'Generating OpenAI GPT3 completions', url: '/guides/ai/examples/openai' },
{ name: 'Generating OG images ', url: '/guides/functions/examples/og-image' },
{
name: 'CAPTCHA support with Cloudflare Turnstile',
@@ -857,6 +854,83 @@ export const storage: NavMenuConstant = {
],
}
export const ai: NavMenuConstant = {
icon: 'ai',
title: 'AI & Vectors',
url: '/guides/ai',
items: [
{ name: 'Overview', url: '/guides/ai' },
{ name: 'Concepts', url: '/guides/ai/concepts' },
{
name: 'Structured & unstructured embeddings',
url: '/guides/ai/structured-unstructured-embeddings',
},
{
name: 'Quickstarts',
url: undefined,
items: [{ name: 'Python client', url: '/guides/ai/vecs-python-client' }],
},
{
name: 'Guides',
url: undefined,
items: [
{ name: 'Managing collections', url: '/guides/ai/managing-collections' },
{ name: 'Managing indexes', url: '/guides/ai/managing-indexes' },
{ name: 'Vector columns', url: '/guides/ai/vector-columns' },
{ name: 'Engineering for scale', url: '/guides/ai/engineering-for-scale' },
],
},
{
name: 'Examples',
url: undefined,
items: [
{
name: 'OpenAI completions using Edge Functions',
url: '/guides/ai/examples/openai',
},
{
name: 'Image search with OpenAI CLIP',
url: '/guides/ai/examples/image-search-openai-clip',
},
{
name: 'Generate image captions using Hugging Face',
url: '/guides/ai/examples/huggingface-image-captioning',
},
{
name: 'Building ChatGPT Plugins',
url: '/guides/ai/examples/building-chatgpt-plugins',
},
{
name: 'Adding generative Q&A to your documentation',
url: '/guides/ai/examples/headless-vector-search',
},
{
name: 'Adding generative Q&A to your Next.js site',
url: '/guides/ai/examples/nextjs-vector-search',
},
],
},
{
name: 'Third-Party Tools',
url: undefined,
items: [
{
name: 'LangChain',
url: '/guides/ai/langchain',
},
{
name: 'Hugging Face',
url: '/guides/ai/hugging-face',
},
{
name: 'Google Colab',
url: '/guides/ai/google-colab',
},
],
},
],
}
export const supabase_cli: NavMenuConstant = {
icon: 'reference-cli',
title: 'Supabase CLI',
@@ -1096,7 +1170,7 @@ export const integrations: NavMenuConstant = {
items: [
{ name: 'Cloudflare Workers', url: '/guides/integrations/cloudflare-workers' },
{ name: 'Estuary', url: '/guides/integrations/estuary' },
{ name: 'OpenAI', url: '/guides/functions/examples/openai' },
{ name: 'OpenAI', url: '/guides/ai/examples/openai' },
{ name: 'pgMustard', url: '/guides/integrations/pgmustard' },
{ name: 'Prisma', url: '/guides/integrations/prisma' },
{ name: 'Sequin', url: '/guides/integrations/sequin' },
@@ -1116,6 +1190,7 @@ export const integrations: NavMenuConstant = {
{ name: 'Directus', url: '/guides/integrations/directus' },
{ name: 'Draftbit', url: '/guides/integrations/draftbit' },
{ name: 'FlutterFlow', url: '/guides/integrations/flutterflow' },
{ name: 'Forest Admin', url: '/guides/integrations/forestadmin' },
{ name: 'Plasmic', url: '/guides/integrations/plasmic' },
{ name: 'ILLA', url: '/guides/integrations/illa' },
],
@@ -68,6 +68,11 @@ const menus: Menu[] = [
path: '/guides/storage',
type: 'guide',
},
{
id: 'ai',
path: '/guides/ai',
type: 'guide',
},
{
id: 'platform',
path: '/guides/platform',
@@ -197,7 +197,7 @@ const NavigationMenuRefListItems = ({
<HeaderLink title={menu.title} url={menu.url} id={id} />
<RevVersionDropdown />
</div>
<ul className="function-link-list flex flex-col gap-2">
<ul className="function-link-list flex flex-col gap-2 pb-5">
{filteredSections.map((section) => {
return (
<Fragment key={section.title}>
@@ -10,6 +10,7 @@ import { extractTsDocNode, generateParameters } from '~/lib/refGenerator/helpers
import RefDetailCollapse from '~/components/reference/RefDetailCollapse'
import { Fragment } from 'react'
import { IRefFunctionSection } from './Reference.types'
import components from '~/components'
const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
const item = props.spec.functions.find((x: any) => x.id === props.funcData.id)
@@ -38,16 +39,16 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
<header className={['prose'].join(' ')}>
{shortText && <ReactMarkdown className="text-sm">{shortText}</ReactMarkdown>}
</header>
{item.description && (
<div className="prose">
<ReactMarkdown className="text-sm">{item.description}</ReactMarkdown>
</div>
)}
{item.notes && (
<div className="prose">
<ReactMarkdown className="text-sm">{item.notes}</ReactMarkdown>
<ReactMarkdown className="text-sm" components={components}>
{item.notes}
</ReactMarkdown>
</div>
)}
{/* // parameters */}
+168
View File
@@ -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<Session>` (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
+4
View File
@@ -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',
+108
View File
@@ -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.
<div className="grid md:grid-cols-12 gap-4 not-prose">
{examples.map((x) => (
<div className="col-span-4" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel icon={'/docs/img/icons/github-icon'} hasLightIcon={true} title={x.name}>
{x.description}
</GlassPanel>
</a>
</Link>
</div>
))}
</div>
export const examples = [
{
name: 'Headless Vector Search',
description: 'A toolkit to perform vector similarity search on your knowledge base embeddings.',
href: '/guides/ai/examples/headless-vector-search',
},
{
name: 'Image Search with OpenAI CLIP',
description: 'Implement image search with the OpenAI CLIP Model and Supabase Vector.',
href: '/guides/ai/examples/image-search-openai-clip',
},
{
name: 'Hugging Face inference',
description: 'Generate image captions using Hugging Face.',
href: '/guides/ai/examples/huggingface-image-captioning',
},
{
name: 'OpenAI completions',
description: 'Generate GPT text completions using OpenAI in Edge Functions.',
href: '/guides/ai/examples/openai',
},
{
name: 'Building ChatGPT Plugins',
description: 'Use Supabase as a Retrieval Store for your ChatGPT plugin.',
href: '/guides/ai/examples/building-chatgpt-plugins',
},
{
name: 'Vector search with Next.js and OpenAI',
description:
'Learn how to build a ChatGPT-style doc search powered by Next.js, OpenAI, and Supabase.',
href: '/guides/ai/examples/nextjs-vector-search',
},
]
## Case studies
<div className="grid md:grid-cols-12 gap-4 not-prose">
{customers.map((x) => (
<div className="col-span-4" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel title={x.name}>{x.description}</GlassPanel>
</a>
</Link>
</div>
))}
</div>
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 }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
+67
View File
@@ -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:
<img src="/docs/img/ai/vector-similarity.png" alt="Vector similarity" width="640" height="640" />
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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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):
![Single Database](/docs/img/ai/scaling/single-database.png)
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.
![With secondaries](/docs/img/ai/scaling/with-secondaries.png)
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:
![Multi Database](/docs/img/ai/scaling/multi-database.png)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -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:
<img
className="dark:hidden !m-0"
alt="diagram reference"
src="/docs/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--light.png"
/>
<img
className="hidden dark:block !m-0"
alt="diagram reference"
src="/docs/img/ai/chatgpt-plugins/chatgpt-plugin-scheme--dark.png"
/>
### 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=<open_ai_api_key>
export DATASTORE=supabase
export SUPABASE_URL=<supabase_url>
export SUPABASE_SERVICE_ROLE_KEY=<supabase_key>
```
For Postgres datastore, you'll need to export these environment variables instead:
```bash
export OPENAI_API_KEY=<open_ai_api_key>
export DATASTORE=postgres
export PG_HOST=<postgres_host_url>
export PG_PASSWORD=<postgres_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.
![OpenAI Secret Keys](/docs/img/ai/chatgpt-plugins/openai-secret-keys.png)
### 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\\" <http://localhost:3333/upsert-file>
```
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.
![ChatGPT Plugin Store](/docs/img/ai/chatgpt-plugins/chatgpt-plugin-store.png)
![ChatGPT Local Plugin](/docs/img/ai/chatgpt-plugins/chatgpt-local-plugin.png)
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.
![Ask ChatGPT](/docs/img/ai/chatgpt-plugins/ask-chatgpt.png)
And after ChatGPT receives a response from the plugin it will answer your question with the data from the documentation.
![ChatGPT Reply](/docs/img/ai/chatgpt-plugins/chatgpt-reply.png)
## 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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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:
![headless search](/docs/img/ai/headless-search/headless.png)
## 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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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.
<div class="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/OgnYxRkxEUw"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Setup
- Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects).
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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.
<div class="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/xmfNUCjszh4"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
<Admonition type="note">
You can find the [full example on
GitHub](https://github.com/supabase-community/nextjs-openai-doc-search).
</Admonition>
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?
></iframe>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -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',
}
<div class="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/29p8kIqyU_Y"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
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
+112
View File
@@ -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.
![Google Colab new notebook](/docs/img/ai/google-colab/colab-new.png)
## 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
```
![Install vecs](/docs/img/ai/google-colab/install-vecs.png)
## 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:<password>@<host>: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:<password>@<host>: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.
![Colab documents](/docs/img/ai/google-colab/colab-documents.png)
## 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']`:
![Colab results](/docs/img/ai/google-colab/colab-results.png)
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 }) => <Layout meta={meta} children={children} />
export default Page
+168
View File
@@ -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.
<Admonition type="info">
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.
</Admonition>
## 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.
<Admonition type="info">
You will need the `supabase` CLI [installed](/docs/guides/cli) for the following commands to work.
</Admonition>
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=<your-token-here>
```
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`.
<Admonition type="info">
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.
</Admonition>
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`:
<img
src="/docs/img/ai/hugging-face/llama-sunglasses-example.png"
alt="Llama wearing sunglasses example"
width="400"
height="400"
/>
## 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 }) => <Layout meta={meta} children={children} />
export default Page
+107
View File
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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://<user>:<password>@<host>:<port>/<db_name>"
# 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()`.
<Admonition type="note">
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.
</Admonition>
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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="dashboard"
>
<TabPanel id="dashboard" label="Dashboard">
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.
</TabPanel>
<TabPanel id="sql" label="SQL">
```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`.
</TabPanel>
</Tabs>
### 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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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 }) => <Layout meta={meta} children={children} />
@@ -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.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
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
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
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
```
</TabPanel>
</Tabs>
## 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).
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
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,
})
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
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<Database>(
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).
</TabPanel>
</Tabs>
## 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])
```
</TabPanel>
@@ -471,7 +547,7 @@ useEffect(() => {
return () => {
subscription.unsubscribe()
}
}, [serverAccessToken, supabase, fetcher])
}, [serverAccessToken, supabase, revalidate])
```
</TabPanel>
@@ -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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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,
})
```
+2 -2
View File
@@ -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',
@@ -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.
![forest-admin-collections](/docs/img/guides/integrations/forestadmin/forest-admin-collections.png)
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.
![supabase-db-credentials](/docs/img/guides/integrations/forestadmin/supabase-db-credentials.png)
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.
![supabase-db-create-table](/docs/img/guides/integrations/forestadmin/supabase-db-create-table.png)
## Step 2: Connect the database to Forest Admin
First, you have to create a new project on Forest Admin:
![forestadmin-create-project](/docs/img/guides/integrations/forestadmin/forestadmin-create-project.png)
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.
![forestadmin-hosting](/docs/img/guides/integrations/forestadmin/forestadmin-hosting.png)
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.
![forestadmin-db-credentials](/docs/img/guides/integrations/forestadmin/forestadmin-db-credentials.png)
## 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 }) => <Layout meta={meta} children={children} />
export default Page
@@ -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.
@@ -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.
<Admonition type="caution">
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.
<Admonition type="info">
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).
</Admonition>
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:
@@ -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/<Function name>/index.ts`.
## Configuration
Each system can be [configured](../self-hosting#configuration) to suit your particular use-case.
+7 -2
View File
@@ -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',
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

@@ -100,7 +100,7 @@ Discover 100+ other apps in this [tweet](https://twitter.com/supabase/status/162
![Community](/images/blog/2022-june/community.jpg)
- Langchain supports our database as a vector store, using pgvector. [Doc](https://hwchase17.github.io/langchainjs/docs/modules/indexes/vector_stores/supabase/) <br/>
- LangChain supports our database as a vector store, using pgvector. [Doc](https://hwchase17.github.io/langchainjs/docs/modules/indexes/vector_stores/supabase/) <br/>
- refine week: building a CRUD app with refine & Supabase. [Full series](https://refine.dev/week-of-refine/)
@@ -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)
@@ -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.
<img
className="dark:hidden !m-0"
alt="diagram reference"
src="/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugin-scheme--light.png"
/>
<img
className="hidden dark:block !m-0"
alt="diagram reference"
src="/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugin-scheme--dark.png"
/>
### 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=<open_ai_api_key>
export DATASTORE=supabase
export SUPABASE_URL=<supabase_url>
export SUPABASE_SERVICE_ROLE_KEY=<supabase_key>
```
For Postgres datastore, you'll need to export these environment variables instead:
```bash
export OPENAI_API_KEY=<open_ai_api_key>
export DATASTORE=postgres
export PG_HOST=<postgres_host_url>
export PG_PASSWORD=<postgres_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.
![OpenAI Secret Keys](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/openai-secret-keys.png)
### 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\\" <http://localhost:3333/upsert-file>
```
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.
![ChatGPT Change Model](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-change-model.png)
![ChatGPT Plugin Store](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugin-store.png)
![ChatGPT Local Plugin](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-local-plugin.png)
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.
![Ask ChatGPT](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/ask-chatgpt.png)
![ChatGPT Query](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-query.png)
And after ChatGPT receives a response from the plugin it will answer your question with the data from the documentation.
![ChatGPT Reply](/images/blog/2023-05-25-chatgpt-plugins-support-postgres/chatgpt-reply.png)
## 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)
@@ -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:
<div className="video-container">
<iframe
className="w-full"
src="https://www.youtube-nocookie.com/embed/AazB9mQetkw"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>
</div>
## Best Overall Project
### Winner
[Syno](https://github.com/ineffablesam/Syno) - by [@samuelP09301972](https://twitter.com/samuelP09301972)
Syno changes the way you consume video content. Say goodbye to lengthy videos and say hello to concise, accurate summaries that capture the essence of each video. Experience the future of video summarization with Syno.
![Syno](/images/blog/flutter-hackathon-winners/syno.jpg)
### Runner Up
[Study Genius](https://github.com/Salomondiei08/StudyAI-App.git) - by [@salomon_diei](https://twitter.com/salomon_diei)
Study Genius is a mobile application that build a personalized learning experience for students. It provies a comprehensive platform for effective study, collaboration and personalized support for students.
![Study Genius](/images/blog/flutter-hackathon-winners/study-genius.jpg)
## Best FlutterFlow project
### Winner
[Lord Of The Memes](https://lord-of-the-memes.flutterflow.app/) - by [@mohonishc](https://twitter.com/mohonishc)
Lord of the Memes is a fun interactive game to be played with friends where you have to react to various situations with meme gifs and compete to be crowned as the ultimate Meme Lord.
![Lord Of The Memes](/images/blog/flutter-hackathon-winners/lord-of-the-memes.jpg)
### Runner Up
[Colabity](https://app.flutterflow.io/project/cocolab-2n64lp) - by [@hichamics](https://twitter.com/hichamics)
Colabity is an app that matches creative minds with each other to work on similar interest-based projects.
![Colabity](/images/blog/flutter-hackathon-winners/colabity.jpg)
## Best Dart Edge Project
### Winner
[ChatGlobe](https://chatglobe.vercel.app/) - by [@tmhk_tnht](https://twitter.com/tmhk_tnht)
ChatGlobe an application that allows you to chat with people around the world in your own language. It translates the messages into multiple languages using Dart Edge and OpenAI API as they are sent.
![ChatGlobe](/images/blog/flutter-hackathon-winners/chatglobe.jpg)
### Runner Up
[Adventure Kingdom](https://github.com/Anujeshdahiya/adventurekingdom/tree/flutterflow) - by [@anujeshd](https://twitter.com/anujeshd), [@rishikhatri47](https://twitter.com/rishikhatri47), and [@Harshit_1320](https://twitter.com/Harshit_1320)
Adventure Kingdom is an app for theme park in which you can check different park attraction informations.
![Adventure Kingdom](/images/blog/flutter-hackathon-winners/adventure-kingdom.jpg)
## Most technically impressive
### Winner
[Accident Detection](https://github.com/Guneetsinghtuli/supabase) - by [@Guneetsingh02](https://twitter.com/Guneetsingh02)
Accident Detection is an app for detecting road accidents using machine learning techniques and utilizing inputs from a variety of sensors, such as accelerometers and GPS to detect and analyze data related to accidents, such as changes in speed, direction, and impact force.
![Accident Detection](/images/blog/flutter-hackathon-winners/accident-detection.jpg)
### Runner Up
[IntelliTask](https://noga-dev.github.io/intellitask/) - by [@noga_dev](https://twitter.com/noga_dev)
IntelliTask is a simple semi-automated todo app where the AI decides how important the task is and when it should be due.
![IntelliTask](/images/blog/flutter-hackathon-winners/intellitask.jpg)
## Most visually pleasing
### Winner
[Caffeio](https://github.com/curregoz/caffeio-app) - by [@caurregoz](https://twitter.com/caurregoz)
Caffeio an app for coffee aficionados. Explore a variety of coffee brewing methods like V60, AeroPress, and Chemex, with comprehensive guides and assistance to elevate your coffee experience. Uncover the captivating stories behind each method, deepening your appreciation for your favorites.
![Caffeio](/images/blog/flutter-hackathon-winners/caffeio.jpg)
### Runner Up
[Decision Tales](https://zi30067ti310.zapp.page/) - by [@JudoUergens](https://twitter.com/JudoUergens)
Decision Tales is an app that let's you follow your own path through fascinating stories. Each story has many possible outcomes, which you untangle with your decisions.
![Decision Tales](/images/blog/flutter-hackathon-winners/decision-tales.jpg)
## The Prizes
[@samuelP09301972](https://twitter.com/samuelP09301972), the creator of Syno will receive a limited Flutter-themed Supabase keyboard.
![Flutter Keyboard](/images/blog/flutter-hackathon-winners/keyboard.png)
Each member of the winning and runner-up projects will receive a limited edition Flutter Hackathon swag pack including a t-shirt as proof of winning in the Flutter Hackathon.
![Flutter Hackathon t-shirt](/images/blog/flutter-hackathon-winners/flutter-hackathon-t-shirt.png)
### Get Started Guides
If you missed the hackathon, but still want to build awesome apps using Flutter and Supabase, here are some resources to get you started:
- [Flutter Quick Start Guide](https://supabase.com/docs/guides/with-flutter)
- [Supabase Flutter documentation](https://supabase.com/docs/reference/dart/introduction)
- [Edge Functions Guide](https://supabase.com/docs/guides/functions)
- [Setup Supabase on FlutterFlow guide](https://docs.flutterflow.io/data-and-backend/supabase/supabase-setup)
- [How to Set Up Supabase Authentication in FlutterFlow video](https://www.youtube.com/watch?v=tL-sLPfWzVE)
+1 -1
View File
@@ -28,7 +28,7 @@ misc: [{ label: 'Backed by', text: 'Y Combinator' }]
about: Mendable is Chat Powered Search for Documentation.
---
[Mendable](http://mendable.ai/) provides a chat-powered search engine for technical documentation. Their AI-powered search tool makes it easier for developers and other technical users to find relevant information in complex documentation. Users can simply ask questions in natural language, and the tool returns the most relevant answers. Mendable's search engine also provides detailed analytics, which helps teams identify knowledge gaps and areas for improvement in their documentation. Mendable has integrated with some of the largest open source projects in the space such as Langchain and LlamaIndex.
[Mendable](http://mendable.ai/) provides a chat-powered search engine for technical documentation. Their AI-powered search tool makes it easier for developers and other technical users to find relevant information in complex documentation. Users can simply ask questions in natural language, and the tool returns the most relevant answers. Mendable's search engine also provides detailed analytics, which helps teams identify knowledge gaps and areas for improvement in their documentation. Mendable has integrated with some of the largest open source projects in the space such as LangChain and LlamaIndex.
## The Challenge
+7
View File
@@ -328,5 +328,12 @@
"position": "Product Management Leader @ AWS",
"author_url": "https://github.com/daltjoh-aws",
"author_image_url": "https://github.com/daltjoh-aws.png"
},
{
"author_id": "egor_romanov",
"author": "Egor Romanov",
"position": "Engineering",
"author_url": "https://github.com/egor-romanov",
"author_image_url": "https://github.com/egor-romanov.png"
}
]
+15
View File
@@ -1981,4 +1981,19 @@ module.exports = [
source: '/docs/guides/auth/auth-helpers/nextjs-server-components',
destination: '/docs/guides/auth/auth-helpers/nextjs',
},
{
permanent: true,
source: '/docs/guides/getting-started/openai/vector-search',
destination: '/docs/guides/ai/examples/docs-search',
},
{
permanent: true,
source: '/docs/guides/functions/examples/huggingface-image-captioning',
destination: '/docs/guides/ai/examples/huggingface-image-captioning',
},
{
permanent: true,
source: '/docs/guides/functions/examples/openai',
destination: '/docs/guides/ai/examples/openai',
},
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 KiB

+3 -2
View File
@@ -82,7 +82,8 @@ IMGPROXY_ENABLE_WEBP_DETECTION=true
############
# Functions - Configuration for Functions
############
FUNCTIONS_HTTP_PORT=9002
# NOTE: VERIFY_JWT applies to all functions. Per-function VERIFY_JWT is not supported yet.
FUNCTIONS_VERIFY_JWT=false
############
# Logs - Configuration for Logflare
@@ -101,4 +102,4 @@ VECTOR_API_PORT=9001
# Google Cloud Project details
GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID
GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER
GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER
+3 -4
View File
@@ -250,10 +250,9 @@ services:
SUPABASE_URL: http://kong:8000
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}"
VERIFY_JWT: "false"
ports:
- ${FUNCTIONS_HTTP_PORT}:9000/tcp
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}
# TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786
VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}"
volumes:
- ./volumes/functions:/home/deno/functions:Z
command:
+13
View File
@@ -153,6 +153,19 @@ services:
- /storage/v1/
plugins:
- name: cors
## Edge Functions routes
- name: functions-v1
_comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
url: http://functions:9000/
routes:
- name: functions-v1-all
strip_path: true
paths:
- /functions/v1/
plugins:
- name: cors
## Analytics routes
- name: analytics-v1
_comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
+16
View File
@@ -0,0 +1,16 @@
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from "https://deno.land/std@0.177.1/http/server.ts"
serve(async () => {
return new Response(
`"Hello from Edge Functions!"`,
{ headers: { "Content-Type": "application/json" } },
)
})
// To invoke:
// curl 'http://localhost:<KONG_HTTP_PORT>/functions/v1/hello' \
// --header 'Authorization: Bearer <anon/service_role API key>'
Loaded 100 of 191 files, more files were not shown because too many files have changed in this diff. Show more