packages/blueprints/gen-ai-chatbot/static-assets/chatbot-genai-cdk/custom-resources/setup-pgvector/index.js (93 lines of code) (raw):
const { Client } = require("pg");
import { getSecret } from "@aws-lambda-powertools/parameters/secrets";
const setUp = async (dbConfig) => {
const client = new Client(dbConfig);
try {
await client.connect();
console.log("Connected to the database.");
// Create pgvector table and index
// Ref: https://github.com/pgvector/pgvector
await client.query("CREATE EXTENSION IF NOT EXISTS pgcrypto;");
await client.query("CREATE EXTENSION IF NOT EXISTS vector;");
await client.query("DROP TABLE IF EXISTS items;");
// NOTE: Cohere multi lingual embedding dimension is 1024
// Ref: https://txt.cohere.com/introducing-embed-v3/
await client.query(`CREATE TABLE IF NOT EXISTS items(
id CHAR(26) primary key,
botid CHAR(26),
content text,
source text,
embedding vector(1024));`);
// `lists` parameter controls the nubmer of clusters created during index building.
// Also it's important to choose the same index method as the one used in the query.
// Here we use L2 distance for the index method.
// See: https://txt.cohere.com/introducing-embed-v3/
await client.query(`CREATE INDEX idx_items_embedding ON items
USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);`);
await client.query(`CREATE INDEX idx_items_botid ON items (botid);`);
console.log("SQL execution successful.");
} catch (err) {
console.error("Error executing SQL: ", err.stack);
throw err;
} finally {
await client.end();
console.log("Database connection closed.");
}
};
const updateStatus = async (event, status, reason, physicalResourceId) => {
const body = JSON.stringify({
Status: status,
Reason: reason,
PhysicalResourceId: physicalResourceId,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
NoEcho: false,
Data: {},
});
const res = await fetch(event.ResponseURL, {
method: "PUT",
body,
headers: {
"Content-Type": "",
"Content-Length": body.length.toString(),
},
});
console.log(res);
console.log(await res.text());
};
exports.handler = async (event, context) => {
console.log(`Received event: ${JSON.stringify(event, null, 2)}`);
console.log(`Received context: ${JSON.stringify(context, null, 2)}`);
console.log(`DB_SECRETS_ARN: ${process.env.DB_SECRETS_ARN}`);
const secrets = await getSecret(process.env.DB_SECRETS_ARN);
const dbInfo = JSON.parse(secrets);
// const dbConfig = event.ResourceProperties.dbConfig;
const dbConfig = {
host: dbInfo["host"],
user: dbInfo["username"],
password: dbInfo["password"],
database: dbInfo["dbname"],
port: dbInfo["port"],
};
const dbClusterIdentifier = process.env.DB_CLUSTER_IDENTIFIER;
try {
switch (event.RequestType) {
case "Create":
case "Update":
await setUp(dbConfig);
await updateStatus(
event,
"SUCCESS",
"Setup succeeded",
dbClusterIdentifier
);
break;
case "Delete":
await updateStatus(event, "SUCCESS", "", dbClusterIdentifier);
}
} catch (error) {
console.log(error);
if (event.PhysicalResourceId) {
await updateStatus(
event,
"FAILED",
error.message,
event.PhysicalResourceId
);
} else {
await updateStatus(event, "FAILED", error.message, dbClusterIdentifier);
}
}
};