mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-08-17 02:10:31 +03:00
feat: 🎸 local
all local in 60 seconds
This commit is contained in:
parent
b40631a0ac
commit
8382dde575
@ -6,6 +6,9 @@ CREATE_FIRST_USER=true # set to false if you want to create the first user manua
|
||||
# login: admin@quivr.app
|
||||
# password: admin
|
||||
|
||||
#LOCAL
|
||||
#OLLAMA_API_BASE_URL=http://host.docker.internal:11434 # Uncomment to activate ollama. This is the local url for the ollama api
|
||||
|
||||
|
||||
|
||||
|
||||
@ -39,9 +42,6 @@ GOOGLE_CLOUD_PROJECT=<ignore-me>
|
||||
CELERY_BROKER_URL=redis://redis:6379/0
|
||||
CELEBRY_BROKER_QUEUE_NAME=quivr-preview.fifo
|
||||
|
||||
#LOCAL
|
||||
#OLLAMA_API_BASE_URL=http://host.docker.internal:11434 # local all in one remove comment to use local llm with Ollama
|
||||
|
||||
|
||||
#RESEND
|
||||
RESEND_API_KEY=<change-me>
|
||||
|
12
README.md
12
README.md
@ -70,16 +70,18 @@ Ensure you have the following installed:
|
||||
|
||||
|
||||
> Want to use [Ollama.ai](https://ollama.ai) instead?
|
||||
> Finish installing Quivr, put a randon open_api_key in the `.env` file and
|
||||
> follow the instructions [here](https://brain.quivr.app/docs/Developers/selfHosted/run_fully_local) to update Quivr to use Ollama AFTER you have finished installing Quivr.
|
||||
> Uncomment the following lines in the `.env` file:
|
||||
> OLLAMA_API_BASE_URL
|
||||
> Run the following command to start Ollama: `ollama run llama2`
|
||||
> You can find more information about Ollama [here](https://ollama.ai/).
|
||||
|
||||
- **Step 4**: Launch the project
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
docker compose up --build # if OPENAI
|
||||
# docker compose -f docker-compose-ollama.yml up --build # Only if using Ollama. You need to run `ollama run llama2` first.
|
||||
```
|
||||
|
||||
- **Step 5**: Login to the app
|
||||
|
||||
|
505
docker-compose-ollama.yml
Normal file
505
docker-compose-ollama.yml
Normal file
@ -0,0 +1,505 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
frontend:
|
||||
pull_policy: never
|
||||
build:
|
||||
context: frontend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
- NEXT_PUBLIC_ENV=local
|
||||
- NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL}
|
||||
- NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}
|
||||
- NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY}
|
||||
- NEXT_PUBLIC_CMS_URL=${NEXT_PUBLIC_CMS_URL}
|
||||
- NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL}
|
||||
container_name: web
|
||||
depends_on:
|
||||
- backend-core
|
||||
restart: always
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
||||
|
||||
backend-core:
|
||||
image: backend-base
|
||||
pull_policy: never
|
||||
env_file:
|
||||
- .env
|
||||
build:
|
||||
context: backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: backend-core
|
||||
command:
|
||||
- "uvicorn"
|
||||
- "main:app"
|
||||
- "--host"
|
||||
- "0.0.0.0"
|
||||
- "--port"
|
||||
- "5050"
|
||||
- "--workers"
|
||||
- "1"
|
||||
restart: always
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
kong:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 5050:5050
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
restart: always
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
worker:
|
||||
pull_policy: never
|
||||
image: backend-base
|
||||
env_file:
|
||||
- .env
|
||||
build:
|
||||
context: backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: worker
|
||||
command: celery -A celery_worker worker -l info
|
||||
restart: always
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
beat:
|
||||
image: backend-base
|
||||
pull_policy: never
|
||||
env_file:
|
||||
- .env
|
||||
build:
|
||||
context: backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: beat
|
||||
command: celery -A celery_worker beat -l info
|
||||
restart: always
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
flower:
|
||||
image: backend-base
|
||||
pull_policy: never
|
||||
env_file:
|
||||
- .env
|
||||
build:
|
||||
context: backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: flower
|
||||
command: celery -A celery_worker flower -l info --port=5555
|
||||
restart: always
|
||||
depends_on:
|
||||
- redis
|
||||
- worker
|
||||
- beat
|
||||
ports:
|
||||
- 5555:5555
|
||||
|
||||
studio:
|
||||
container_name: supabase-studio
|
||||
image: supabase/studio:20231123-64a766a
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"node",
|
||||
"-e",
|
||||
"require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
STUDIO_PG_META_URL: http://meta:8080
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
|
||||
DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
|
||||
|
||||
SUPABASE_URL: http://kong:8000
|
||||
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
|
||||
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
|
||||
LOGFLARE_URL: http://analytics:4000
|
||||
NEXT_PUBLIC_ENABLE_LOGS: true
|
||||
# Comment to use Big Query backend for analytics
|
||||
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
|
||||
# Uncomment to use Big Query backend for analytics
|
||||
# NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery
|
||||
|
||||
kong:
|
||||
container_name: supabase-kong
|
||||
image: kong:2.8.1
|
||||
restart: unless-stopped
|
||||
# https://unix.stackexchange.com/a/294837
|
||||
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
|
||||
ports:
|
||||
- ${KONG_HTTP_PORT}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT}:8443/tcp
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
KONG_DATABASE: "off"
|
||||
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
|
||||
# https://github.com/supabase/cli/issues/14
|
||||
KONG_DNS_ORDER: LAST,A,CNAME
|
||||
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
|
||||
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
|
||||
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||
volumes:
|
||||
# https://github.com/supabase/supabase/issues/12661
|
||||
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro
|
||||
|
||||
auth:
|
||||
container_name: supabase-auth
|
||||
image: supabase/gotrue:v2.99.0
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--no-verbose",
|
||||
"--tries=1",
|
||||
"--spider",
|
||||
"http://localhost:9999/health"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
GOTRUE_API_HOST: 0.0.0.0
|
||||
GOTRUE_API_PORT: 9999
|
||||
API_EXTERNAL_URL: ${API_EXTERNAL_URL}
|
||||
|
||||
GOTRUE_DB_DRIVER: postgres
|
||||
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
|
||||
GOTRUE_SITE_URL: ${SITE_URL}
|
||||
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
|
||||
GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
|
||||
|
||||
GOTRUE_JWT_ADMIN_ROLES: service_role
|
||||
GOTRUE_JWT_AUD: authenticated
|
||||
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
|
||||
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
|
||||
GOTRUE_JWT_SECRET: ${JWT_SECRET}
|
||||
|
||||
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
|
||||
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
|
||||
# GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true
|
||||
# GOTRUE_SMTP_MAX_FREQUENCY: 1s
|
||||
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
|
||||
GOTRUE_SMTP_HOST: ${SMTP_HOST}
|
||||
GOTRUE_SMTP_PORT: ${SMTP_PORT}
|
||||
GOTRUE_SMTP_USER: ${SMTP_USER}
|
||||
GOTRUE_SMTP_PASS: ${SMTP_PASS}
|
||||
GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}
|
||||
GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE}
|
||||
GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION}
|
||||
GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY}
|
||||
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE}
|
||||
|
||||
GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
|
||||
GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
|
||||
|
||||
rest:
|
||||
container_name: supabase-rest
|
||||
image: postgrest/postgrest:v11.2.2
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
|
||||
PGRST_DB_ANON_ROLE: anon
|
||||
PGRST_JWT_SECRET: ${JWT_SECRET}
|
||||
PGRST_DB_USE_LEGACY_GUCS: "false"
|
||||
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
|
||||
PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}
|
||||
command: "postgrest"
|
||||
|
||||
realtime:
|
||||
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
|
||||
container_name: realtime-dev.supabase-realtime
|
||||
image: supabase/realtime:v2.25.35
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"bash",
|
||||
"-c",
|
||||
"printf \\0 > /dev/tcp/localhost/4000"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PORT: 4000
|
||||
DB_HOST: ${POSTGRES_HOST}
|
||||
DB_PORT: ${POSTGRES_PORT}
|
||||
DB_USER: supabase_admin
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
DB_NAME: ${POSTGRES_DB}
|
||||
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
|
||||
DB_ENC_KEY: supabaserealtime
|
||||
API_JWT_SECRET: ${JWT_SECRET}
|
||||
FLY_ALLOC_ID: fly123
|
||||
FLY_APP_NAME: realtime
|
||||
SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
|
||||
ERL_AFLAGS: -proto_dist inet_tcp
|
||||
ENABLE_TAILSCALE: "false"
|
||||
DNS_NODES: "''"
|
||||
command: >
|
||||
sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"
|
||||
|
||||
storage:
|
||||
container_name: supabase-storage
|
||||
image: supabase/storage-api:v0.43.11
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
rest:
|
||||
condition: service_started
|
||||
imgproxy:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--no-verbose",
|
||||
"--tries=1",
|
||||
"--spider",
|
||||
"http://localhost:5000/status"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ANON_KEY: ${ANON_KEY}
|
||||
SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
POSTGREST_URL: http://rest:3000
|
||||
PGRST_JWT_SECRET: ${JWT_SECRET}
|
||||
DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
FILE_SIZE_LIMIT: 52428800
|
||||
STORAGE_BACKEND: file
|
||||
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
|
||||
TENANT_ID: stub
|
||||
# TODO: https://github.com/supabase/storage-api/issues/55
|
||||
REGION: stub
|
||||
GLOBAL_S3_BUCKET: stub
|
||||
ENABLE_IMAGE_TRANSFORMATION: "true"
|
||||
IMGPROXY_URL: http://imgproxy:5001
|
||||
volumes:
|
||||
- ./volumes/storage:/var/lib/storage:z
|
||||
|
||||
imgproxy:
|
||||
container_name: supabase-imgproxy
|
||||
image: darthsim/imgproxy:v3.8.0
|
||||
healthcheck:
|
||||
test: [ "CMD", "imgproxy", "health" ]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
environment:
|
||||
IMGPROXY_BIND: ":5001"
|
||||
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
|
||||
IMGPROXY_USE_ETAG: "true"
|
||||
IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION}
|
||||
volumes:
|
||||
- ./volumes/storage:/var/lib/storage:z
|
||||
|
||||
meta:
|
||||
container_name: supabase-meta
|
||||
image: supabase/postgres-meta:v0.68.0
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PG_META_PORT: 8080
|
||||
PG_META_DB_HOST: ${POSTGRES_HOST}
|
||||
PG_META_DB_PORT: ${POSTGRES_PORT}
|
||||
PG_META_DB_NAME: ${POSTGRES_DB}
|
||||
PG_META_DB_USER: supabase_admin
|
||||
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
functions:
|
||||
container_name: supabase-edge-functions
|
||||
image: supabase/edge-runtime:v1.22.4
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
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_HOST}:${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:
|
||||
- start
|
||||
- --main-service
|
||||
- /home/deno/functions/main
|
||||
|
||||
analytics:
|
||||
container_name: supabase-analytics
|
||||
image: supabase/logflare:1.4.0
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://localhost:4000/health" ]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 10
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
# Disable this if you are using an external Postgres database
|
||||
condition: service_healthy
|
||||
# Uncomment to use Big Query backend for analytics
|
||||
# volumes:
|
||||
# - type: bind
|
||||
# source: ${PWD}/gcloud.json
|
||||
# target: /opt/app/rel/logflare/bin/gcloud.json
|
||||
# read_only: true
|
||||
environment:
|
||||
LOGFLARE_NODE_HOST: 127.0.0.1
|
||||
DB_USERNAME: supabase_admin
|
||||
DB_DATABASE: ${POSTGRES_DB}
|
||||
DB_HOSTNAME: ${POSTGRES_HOST}
|
||||
DB_PORT: ${POSTGRES_PORT}
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
DB_SCHEMA: _analytics
|
||||
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
|
||||
LOGFLARE_SINGLE_TENANT: true
|
||||
LOGFLARE_SUPABASE_MODE: true
|
||||
LOGFLARE_MIN_CLUSTER_SIZE: 1
|
||||
RELEASE_COOKIE: cookie
|
||||
|
||||
# Comment variables to use Big Query backend for analytics
|
||||
POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
POSTGRES_BACKEND_SCHEMA: _analytics
|
||||
LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
|
||||
|
||||
# Uncomment to use Big Query backend for analytics
|
||||
# GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
|
||||
# GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER}
|
||||
ports:
|
||||
- 4000:4000
|
||||
entrypoint: |
|
||||
sh -c `cat <<'EOF' > run.sh && sh run.sh
|
||||
./logflare eval Logflare.Release.migrate
|
||||
./logflare start --sname logflare
|
||||
EOF
|
||||
`
|
||||
|
||||
# Comment out everything below this point if you are using an external Postgres database
|
||||
db:
|
||||
container_name: supabase-db
|
||||
image: supabase/postgres:15.1.0.117
|
||||
healthcheck:
|
||||
test: pg_isready -U postgres -h localhost
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
depends_on:
|
||||
vector:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- postgres
|
||||
- -c
|
||||
- config_file=/etc/postgresql/postgresql.conf
|
||||
- -c
|
||||
- log_min_messages=fatal # prevents Realtime polling queries from appearing in logs
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Pass down internal port because it's set dynamically by other services
|
||||
- ${POSTGRES_PORT}:${POSTGRES_PORT}
|
||||
environment:
|
||||
POSTGRES_HOST: /var/run/postgresql
|
||||
PGPORT: ${POSTGRES_PORT}
|
||||
POSTGRES_PORT: ${POSTGRES_PORT}
|
||||
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
PGDATABASE: ${POSTGRES_DB}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
JWT_EXP: ${JWT_EXPIRY}
|
||||
volumes:
|
||||
- ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
|
||||
# Must be superuser to create event trigger
|
||||
- ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
|
||||
# Must be superuser to alter reserved role
|
||||
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
|
||||
# Initialize the database settings with JWT_SECRET and JWT_EXP
|
||||
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
|
||||
# PGDATA directory is persisted between restarts
|
||||
- ./volumes/db/data:/var/lib/postgresql/data:Z
|
||||
# Changes required for Analytics support
|
||||
- ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
|
||||
- ./scripts/tables-ollama.sql:/docker-entrypoint-initdb.d/seed.sql
|
||||
|
||||
vector:
|
||||
container_name: supabase-vector
|
||||
image: timberio/vector:0.28.1-alpine
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"wget",
|
||||
"--no-verbose",
|
||||
"--tries=1",
|
||||
"--spider",
|
||||
"http://vector:9001/health"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
volumes:
|
||||
- ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
|
||||
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro
|
||||
|
||||
command: [ "--config", "etc/vector/vector.yml" ]
|
@ -31,73 +31,19 @@ Then run the following command to run Ollama in the background:
|
||||
ollama run llama2
|
||||
```
|
||||
|
||||
### Update Quivr to use Ollama
|
||||
|
||||
In order to have Quivr use Ollama we need to update the tables in Supabase to support the embedding format that Ollama uses. Ollama uses by default llama 2 that produces 4096 dimensional embeddings while OpenAI API produces 1536 dimensional embeddings.
|
||||
|
||||
|
||||
Go to your supabase localhost instance [http://localhost:8000](http://localhost:8000) (default password admin/admin) and delete your table `vectors` and create a new table vectors with the following schema:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS vectors;
|
||||
CREATE TABLE IF NOT EXISTS vectors (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
content TEXT,
|
||||
file_sha1 TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096)
|
||||
);
|
||||
```
|
||||
|
||||
Then run the following command to update the table:
|
||||
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION match_vectors(query_embedding VECTOR(4096), match_count INT, p_brain_id UUID)
|
||||
RETURNS TABLE(
|
||||
id UUID,
|
||||
brain_id UUID,
|
||||
content TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096),
|
||||
similarity FLOAT
|
||||
) LANGUAGE plpgsql AS $$
|
||||
#variable_conflict use_column
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
vectors.id,
|
||||
brains_vectors.brain_id,
|
||||
vectors.content,
|
||||
vectors.metadata,
|
||||
vectors.embedding,
|
||||
1 - (vectors.embedding <=> query_embedding) AS similarity
|
||||
FROM
|
||||
vectors
|
||||
INNER JOIN
|
||||
brains_vectors ON vectors.id = brains_vectors.vector_id
|
||||
WHERE brains_vectors.brain_id = p_brain_id
|
||||
ORDER BY
|
||||
vectors.embedding <=> query_embedding
|
||||
LIMIT match_count;
|
||||
END;
|
||||
$$;
|
||||
```
|
||||
|
||||
This will update the match_vectors function to use the new embedding format.
|
||||
|
||||
|
||||
## Add Ollama Model to Quivr
|
||||
|
||||
Now that you have your model running locally, you need to add it to Quivr.
|
||||
|
||||
In order to allow the user to choose between the OpenAI API and Ollama, we need to add a new model to the Quivr backend.
|
||||
In order to allow the user to choose between the Ollama, we need to add a new model to the Quivr backend.
|
||||
|
||||
Go to supabase and in the table `user_settings` either add by default or to your user the following value to the `models` column:
|
||||
|
||||
```json
|
||||
[
|
||||
"gpt-3.5-turbo-1106",
|
||||
"ollama/llama2"
|
||||
"ollama/llama2",
|
||||
"ollama/mistral",
|
||||
]
|
||||
```
|
||||
|
||||
@ -108,17 +54,3 @@ By adding this as default, it means that all new users will have this model by d
|
||||
```sql
|
||||
DELETE TABLE user_settings;
|
||||
```
|
||||
|
||||
|
||||
## Env Variables
|
||||
|
||||
|
||||
In order to have Quivr use Ollama we need to update the env variables.
|
||||
|
||||
Go to `.env` and un comment the following env variables:
|
||||
|
||||
```bash
|
||||
OLLAMA_API_BASE_URL=http://host.docker.internal:11434
|
||||
```
|
||||
|
||||
Then go to the Quivr and you are good to go.
|
449
scripts/tables-ollama.sql
Normal file
449
scripts/tables-ollama.sql
Normal file
@ -0,0 +1,449 @@
|
||||
-- Create users table
|
||||
CREATE TABLE IF NOT EXISTS user_daily_usage(
|
||||
user_id UUID REFERENCES auth.users (id),
|
||||
email TEXT,
|
||||
date TEXT,
|
||||
daily_requests_count INT,
|
||||
PRIMARY KEY (user_id, date)
|
||||
);
|
||||
|
||||
-- Create chats table
|
||||
CREATE TABLE IF NOT EXISTS chats(
|
||||
chat_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users (id),
|
||||
creation_time TIMESTAMP DEFAULT current_timestamp,
|
||||
history JSONB,
|
||||
chat_name TEXT
|
||||
);
|
||||
|
||||
|
||||
-- Create vector extension
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
|
||||
-- Create vectors table
|
||||
CREATE TABLE IF NOT EXISTS vectors (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
content TEXT,
|
||||
file_sha1 TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096)
|
||||
);
|
||||
|
||||
-- Create function to match vectors
|
||||
CREATE OR REPLACE FUNCTION match_vectors(query_embedding VECTOR(4096), match_count INT, p_brain_id UUID)
|
||||
RETURNS TABLE(
|
||||
id UUID,
|
||||
brain_id UUID,
|
||||
content TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096),
|
||||
similarity FLOAT
|
||||
) LANGUAGE plpgsql AS $$
|
||||
#variable_conflict use_column
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
vectors.id,
|
||||
brains_vectors.brain_id,
|
||||
vectors.content,
|
||||
vectors.metadata,
|
||||
vectors.embedding,
|
||||
1 - (vectors.embedding <=> query_embedding) AS similarity
|
||||
FROM
|
||||
vectors
|
||||
INNER JOIN
|
||||
brains_vectors ON vectors.id = brains_vectors.vector_id
|
||||
WHERE brains_vectors.brain_id = p_brain_id
|
||||
ORDER BY
|
||||
vectors.embedding <=> query_embedding
|
||||
LIMIT match_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create stats table
|
||||
CREATE TABLE IF NOT EXISTS stats (
|
||||
time TIMESTAMP,
|
||||
chat BOOLEAN,
|
||||
embedding BOOLEAN,
|
||||
details TEXT,
|
||||
metadata JSONB,
|
||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY
|
||||
);
|
||||
|
||||
-- Create summaries table
|
||||
CREATE TABLE IF NOT EXISTS summaries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
document_id UUID REFERENCES vectors(id),
|
||||
content TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096)
|
||||
);
|
||||
|
||||
-- Create function to match summaries
|
||||
CREATE OR REPLACE FUNCTION match_summaries(query_embedding VECTOR(4096), match_count INT, match_threshold FLOAT)
|
||||
RETURNS TABLE(
|
||||
id BIGINT,
|
||||
document_id UUID,
|
||||
content TEXT,
|
||||
metadata JSONB,
|
||||
embedding VECTOR(4096),
|
||||
similarity FLOAT
|
||||
) LANGUAGE plpgsql AS $$
|
||||
#variable_conflict use_column
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
id,
|
||||
document_id,
|
||||
content,
|
||||
metadata,
|
||||
embedding,
|
||||
1 - (summaries.embedding <=> query_embedding) AS similarity
|
||||
FROM
|
||||
summaries
|
||||
WHERE 1 - (summaries.embedding <=> query_embedding) > match_threshold
|
||||
ORDER BY
|
||||
summaries.embedding <=> query_embedding
|
||||
LIMIT match_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create api_keys table
|
||||
CREATE TABLE IF NOT EXISTS api_keys(
|
||||
key_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users (id),
|
||||
api_key TEXT UNIQUE,
|
||||
creation_time TIMESTAMP DEFAULT current_timestamp,
|
||||
deleted_time TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
--- Create prompts table
|
||||
CREATE TABLE IF NOT EXISTS prompts (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
title VARCHAR(255),
|
||||
content TEXT,
|
||||
status VARCHAR(255) DEFAULT 'private'
|
||||
);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'brain_type_enum') THEN
|
||||
-- Create the ENUM type 'brain_type' if it doesn't exist
|
||||
CREATE TYPE brain_type_enum AS ENUM ('doc', 'api');
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
--- Create brains table
|
||||
CREATE TABLE IF NOT EXISTS brains (
|
||||
brain_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
status TEXT,
|
||||
description TEXT,
|
||||
model TEXT,
|
||||
max_tokens INT,
|
||||
temperature FLOAT,
|
||||
prompt_id UUID REFERENCES prompts(id),
|
||||
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
brain_type brain_type_enum DEFAULT 'doc'
|
||||
);
|
||||
|
||||
|
||||
-- Create chat_history table
|
||||
CREATE TABLE IF NOT EXISTS chat_history (
|
||||
message_id UUID DEFAULT uuid_generate_v4(),
|
||||
chat_id UUID REFERENCES chats(chat_id),
|
||||
user_message TEXT,
|
||||
assistant TEXT,
|
||||
message_time TIMESTAMP DEFAULT current_timestamp,
|
||||
PRIMARY KEY (chat_id, message_id),
|
||||
prompt_id UUID REFERENCES prompts(id),
|
||||
brain_id UUID REFERENCES brains(brain_id)
|
||||
);
|
||||
|
||||
-- Create notification table
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
datetime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
chat_id UUID REFERENCES chats(chat_id),
|
||||
message TEXT,
|
||||
action VARCHAR(255) NOT NULL,
|
||||
status VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
-- Create brains X users table
|
||||
CREATE TABLE IF NOT EXISTS brains_users (
|
||||
brain_id UUID,
|
||||
user_id UUID,
|
||||
rights VARCHAR(255),
|
||||
default_brain BOOLEAN DEFAULT false,
|
||||
PRIMARY KEY (brain_id, user_id),
|
||||
FOREIGN KEY (user_id) REFERENCES auth.users (id),
|
||||
FOREIGN KEY (brain_id) REFERENCES brains (brain_id)
|
||||
);
|
||||
|
||||
-- Create brains X vectors table
|
||||
CREATE TABLE IF NOT EXISTS brains_vectors (
|
||||
brain_id UUID,
|
||||
vector_id UUID,
|
||||
file_sha1 TEXT,
|
||||
PRIMARY KEY (brain_id, vector_id),
|
||||
FOREIGN KEY (vector_id) REFERENCES vectors (id),
|
||||
FOREIGN KEY (brain_id) REFERENCES brains (brain_id)
|
||||
);
|
||||
|
||||
-- Create brains X vectors table
|
||||
CREATE TABLE IF NOT EXISTS brain_subscription_invitations (
|
||||
brain_id UUID,
|
||||
email VARCHAR(255),
|
||||
rights VARCHAR(255),
|
||||
PRIMARY KEY (brain_id, email),
|
||||
FOREIGN KEY (brain_id) REFERENCES brains (brain_id)
|
||||
);
|
||||
|
||||
--- Create user_identity table
|
||||
CREATE TABLE IF NOT EXISTS user_identity (
|
||||
user_id UUID PRIMARY KEY,
|
||||
openai_api_key VARCHAR(255)
|
||||
);
|
||||
|
||||
-- Create the new table with 6 columns
|
||||
CREATE TABLE IF NOT EXISTS api_brain_definition (
|
||||
brain_id UUID REFERENCES brains(brain_id),
|
||||
method VARCHAR(255) CHECK (method IN ('GET', 'POST', 'PUT', 'DELETE')),
|
||||
url VARCHAR(255),
|
||||
params JSON,
|
||||
search_params JSON,
|
||||
secrets JSON
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.get_user_email_by_user_id(user_id uuid)
|
||||
RETURNS TABLE (email text)
|
||||
SECURITY definer
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY SELECT au.email::text FROM auth.users au WHERE au.id = user_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.get_user_id_by_user_email(user_email text)
|
||||
RETURNS TABLE (user_id uuid)
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY SELECT au.id::uuid FROM auth.users au WHERE au.email = user_email;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
name VARCHAR(255) PRIMARY KEY,
|
||||
executed_at TIMESTAMPTZ DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
user_id UUID PRIMARY KEY,
|
||||
models JSONB DEFAULT '["ollama/llama2"]'::jsonb,
|
||||
daily_chat_credit INT DEFAULT 300,
|
||||
max_brains INT DEFAULT 30,
|
||||
max_brain_size INT DEFAULT 100000000
|
||||
);
|
||||
|
||||
-- knowledge table
|
||||
CREATE TABLE IF NOT EXISTS knowledge (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
file_name TEXT,
|
||||
url TEXT,
|
||||
brain_id UUID NOT NULL REFERENCES brains(brain_id),
|
||||
extension TEXT NOT NULL,
|
||||
CHECK ((file_name IS NOT NULL AND url IS NULL) OR (file_name IS NULL AND url IS NOT NULL))
|
||||
);
|
||||
|
||||
|
||||
-- knowledge_vectors table
|
||||
CREATE TABLE IF NOT EXISTS knowledge_vectors (
|
||||
knowledge_id UUID NOT NULL REFERENCES knowledge(id),
|
||||
vector_id UUID NOT NULL REFERENCES vectors(id),
|
||||
embedding_model TEXT NOT NULL,
|
||||
PRIMARY KEY (knowledge_id, vector_id, embedding_model)
|
||||
);
|
||||
|
||||
-- Create the function to add user_id to the onboardings table
|
||||
CREATE OR REPLACE FUNCTION public.create_user_onboarding() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.onboardings (user_id)
|
||||
VALUES (NEW.id);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY definer;
|
||||
|
||||
-- Revoke all on function handle_new_user_onboarding() from PUBLIC;
|
||||
REVOKE ALL ON FUNCTION create_user_onboarding() FROM PUBLIC;
|
||||
|
||||
-- Drop the trigger if it exists
|
||||
DROP TRIGGER IF EXISTS create_user_onboarding_trigger ON auth.users;
|
||||
|
||||
-- Create the trigger on the insert into the auth.users table
|
||||
CREATE TRIGGER create_user_onboarding_trigger
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.create_user_onboarding();
|
||||
|
||||
-- Create the onboarding table
|
||||
CREATE TABLE IF NOT EXISTS onboardings (
|
||||
user_id UUID NOT NULL REFERENCES auth.users (id),
|
||||
onboarding_a BOOLEAN NOT NULL DEFAULT true,
|
||||
onboarding_b1 BOOLEAN NOT NULL DEFAULT true,
|
||||
onboarding_b2 BOOLEAN NOT NULL DEFAULT true,
|
||||
onboarding_b3 BOOLEAN NOT NULL DEFAULT true,
|
||||
creation_time TIMESTAMP DEFAULT current_timestamp,
|
||||
PRIMARY KEY (user_id)
|
||||
);
|
||||
|
||||
|
||||
-- Stripe settings --
|
||||
-- Create extension 'wrappers' if it doesn't exist
|
||||
CREATE EXTENSION IF NOT EXISTS wrappers;
|
||||
|
||||
-- Create foreign data wrapper 'stripe_wrapper' if it doesn't exist
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.foreign_data_wrappers
|
||||
WHERE foreign_data_wrapper_name = 'stripe_wrapper'
|
||||
) THEN
|
||||
CREATE FOREIGN DATA WRAPPER stripe_wrapper
|
||||
HANDLER stripe_fdw_handler;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Check if the server 'stripe_server' exists before creating it
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_foreign_server WHERE srvname = 'stripe_server') THEN
|
||||
CREATE SERVER stripe_server
|
||||
FOREIGN DATA WRAPPER stripe_wrapper
|
||||
OPTIONS (
|
||||
api_key 'your_stripe_api_key' -- Replace with your Stripe API key
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Create foreign table 'public.customers' if it doesn't exist
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = 'customers'
|
||||
) THEN
|
||||
CREATE FOREIGN TABLE public.customers (
|
||||
id text,
|
||||
email text,
|
||||
name text,
|
||||
description text,
|
||||
created timestamp,
|
||||
attrs jsonb
|
||||
)
|
||||
SERVER stripe_server
|
||||
OPTIONS (
|
||||
OBJECT 'customers',
|
||||
ROWID_COLUMN 'id'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Create table 'users' if it doesn't exist
|
||||
CREATE TABLE IF NOT EXISTS public.users (
|
||||
id uuid REFERENCES auth.users NOT NULL PRIMARY KEY,
|
||||
email text
|
||||
);
|
||||
|
||||
-- Create or replace function 'public.handle_new_user'
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.users (id, email)
|
||||
VALUES (NEW.id, NEW.email);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Check if the trigger 'on_auth_user_created' exists before creating it
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'on_auth_user_created') THEN
|
||||
CREATE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
insert into
|
||||
storage.buckets (id, name)
|
||||
values
|
||||
('quivr', 'quivr');
|
||||
|
||||
CREATE POLICY "Access Quivr Storage 1jccrwz_0" ON storage.objects FOR INSERT TO anon WITH CHECK (bucket_id = 'quivr');
|
||||
|
||||
CREATE POLICY "Access Quivr Storage 1jccrwz_1" ON storage.objects FOR SELECT TO anon USING (bucket_id = 'quivr');
|
||||
|
||||
CREATE POLICY "Access Quivr Storage 1jccrwz_2" ON storage.objects FOR UPDATE TO anon USING (bucket_id = 'quivr');
|
||||
|
||||
CREATE POLICY "Access Quivr Storage 1jccrwz_3" ON storage.objects FOR DELETE TO anon USING (bucket_id = 'quivr');
|
||||
|
||||
-- Create functions for secrets in vault
|
||||
CREATE OR REPLACE FUNCTION insert_secret(name text, secret text)
|
||||
returns uuid
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
begin
|
||||
return vault.create_secret(secret, name);
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
create or replace function read_secret(secret_name text)
|
||||
returns text
|
||||
language plpgsql
|
||||
security definer set search_path = public
|
||||
as $$
|
||||
declare
|
||||
secret text;
|
||||
begin
|
||||
select decrypted_secret from vault.decrypted_secrets where name =
|
||||
secret_name into secret;
|
||||
return secret;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace function delete_secret(secret_name text)
|
||||
returns text
|
||||
language plpgsql
|
||||
security definer set search_path = public
|
||||
as $$
|
||||
declare
|
||||
deleted_rows int;
|
||||
begin
|
||||
delete from vault.decrypted_secrets where name = secret_name;
|
||||
get diagnostics deleted_rows = row_count;
|
||||
if deleted_rows = 0 then
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
|
||||
INSERT INTO migrations (name)
|
||||
SELECT '20231128173900_remove_openai_api_key'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM migrations WHERE name = '20231128173900_remove_openai_api_key'
|
||||
);
|
Loading…
Reference in New Issue
Block a user