Skip to content

Commit

Permalink
inital commit
Browse files Browse the repository at this point in the history
  • Loading branch information
theshanbhag committed Dec 10, 2024
0 parents commit 9a2cf73
Show file tree
Hide file tree
Showing 8 changed files with 5,079 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Version 0.0.1
- Initial Version
26 changes: 26 additions & 0 deletions POSTINSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
This file provides your users an overview of how to use your extension after they've installed it. All content is optional, but this is the recommended format. Your users will see the contents of this file in the Firebase console after they install the extension.
Include instructions for using the extension and any important functional details. Also include **detailed descriptions** for any additional post-installation setup required by the user.
Reference values for the extension instance using the ${param:PARAMETER_NAME} or ${function:VARIABLE_NAME} syntax.
Learn more in the docs: https://firebase.google.com/docs/extensions/publishers/user-documentation#reference-in-postinstall
Learn more about writing a POSTINSTALL.md file in the docs:
https://firebase.google.com/docs/extensions/publishers/user-documentation#writing-postinstall
-->

# See it in action

You can test your Extension from the deployed Cloud Function Trigger by navgating to the Cloud Function -> Deployed service name -> Triggers.

# Using the extension

To use this extesnion you need to import the trigger/https endpoint to your Application Logic.

To learn more about HTTP functions, visit the [functions documentation](https://firebase.google.com/docs/functions/http-events).

<!-- We recommend keeping the following section to explain how to monitor extensions with Firebase -->
# Monitoring

As a best practice, you can [monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor) of your installed extension, including checks on its health, usage, and logs.
40 changes: 40 additions & 0 deletions PREINSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--
This file provides your users an overview of your extension. All content is optional, but this is the recommended format. Your users will see the contents of this file when they run the `firebase ext:info` command.
Include any important functional details as well as a brief description for any additional setup required by the user (both pre- and post-installation).
Learn more about writing a PREINSTALL.md file in the docs:
https://firebase.google.com/docs/extensions/publishers/user-documentation#writing-preinstall
-->

# MongoDB Atlas Extension

This firebase extension is a v1 for MongoDB Atlas. It can perform the following operations

1. /findOne
* requires user to pass filter in the format {"filter":{"name":"sample"}}
2. /insertOne
* requires the user to pass document in JSON format.
3. /vectorSearch
* requires the user to pass the query in the format {"query": "Tell me about Firebase extensions"}

### The parameters that can be configures at the time of installation are below:
* Connection String: The Mongodb SRV string from Atlas -> connect
* Database Name: name of the database
* Collection Name: Name of the collection you want to connect to.
* Embedding Model Name
* LLM Model Name
* Index Name
* Vector Index Name
* Vector Field Name
* Vector return Name

<!-- We recommend keeping the following section to explain how billing for Firebase Extensions works -->
# Billing

This extension uses other Firebase or Google Cloud Platform services which may have associated charges:

<!-- List all products the extension interacts with -->
- Cloud Functions

When you use Firebase Extensions, you're only charged for the underlying resources that you use. A paid-tier billing plan is only required if the extension uses a service that requires a paid-tier plan, for example calling to a Google Cloud Platform API or making outbound network requests to non-Google services. All Firebase services offer a free tier of usage. [Learn more about Firebase billing.](https://firebase.google.com/pricing)
114 changes: 114 additions & 0 deletions extension.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: mongodb-functions
version: 0.0.1
specVersion: v1beta # Firebase Extensions specification version; don't change

# Friendly display name for your extension (~3-5 words)
displayName: MongoDB Atlas

# Brief description of the task your extension performs (~1 sentence)
description: >-
Perform basic operations like findOne, InsertOne and vectorSearch on MongoDB Atlas
author:
authorName: Venkatesh Shanbhag
url: https://mongodb.com

license: Apache-2.0 # Required license

# Public URL for the source code of your extension
sourceUrl: https://github.com/your-name/your-repo

resources:
# - name: mongodbcrud
# type: ev
# properties:
# eventTrigger:
# eventType: providers/google.firebase.database/eventTypes/ref.create
# # DATABASE_INSTANCE (project's default instance) is an auto-populated
# # parameter value. You can also specify an instance.
# resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
# runtime: "nodejs18"
- name: mongodbcrud
type: firebaseextensions.v1beta.function
properties:
runtime: nodejs18
httpsTrigger: {}

params:
- param: CONNECTION_STRING
label: MongoDB Connection String of your cluster
description: >-
MongoDB connection String
type: string
default: mongodb+srv://
required: true
immutable: false
- param: DATABASE_NAME
label: Mongodb Database name
description: >-
Mongodb Database name
type: string
default: test
required: true
immutable: false
- param: COLLECTION_NAME
label: Mongodb Collection name
description: >-
Mongodb Collection name
type: string
default: test
required: true
immutable: false
- param: EMBEDDING_MODEL_NAME
label: Googel VertexAI Model name
description: >-
Mongodb Model name
type: string
default: text-embedding-004
required: true
immutable: false
- param: LLM_MODEL_NAME
label: Googel VertexAI Model name
description: >-
Mongodb Model name
type: string
default: "gemini-1.5-pro"
required: true
immutable: false
- param: INDEX_NAME
label: Mongodb Index name
description: >-
Mongodb Index name
type: string
default: vector_index
required: true
immutable: false
- param: INDEX_FIELD
label: Mongodb Index field
description: >-
Mongodb Index field
type: string
default: text
required: true
immutable: false
- param: EMBEDDING_FIELD
label: Mongodb Embedding field
description: >-
Mongodb Embedding field
type: string
default: embedding
required: true
immutable: false
- param: LLM_PROMPT
label: LLM Prompt
description: >-
LLM Prompt
type: string
default: "You are a Chatbot. respond the the user only with the context you have from the input provided to you. Don't return anything that is irrelevant to the context."
required: true
immutable: false

events:
- type: test-publisher.mongodb-functions.v1.complete
description: >-
Occurs with MongoDB function execution is complete
1 change: 1 addition & 0 deletions functions/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
126 changes: 126 additions & 0 deletions functions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { MongoClient } from 'mongodb';
import { initializeApp } from "firebase-admin/app";
import { https } from 'firebase-functions/v1';
import { VertexAIEmbeddings, ChatVertexAI } from "@langchain/google-vertexai";

import { MongoDBAtlasVectorSearch } from "@langchain/mongodb";


const app = initializeApp();


export const mongodbcrud = https.onRequest(async (req, res) => {

const route = req.path;
const payload = req.body;
const client = new MongoClient(process.env.CONNECTION_STRING);
const db = client.db(process.env.DATABASE_NAME);
const collection = db.collection(process.env.COLLECTION_NAME);

const llm = new ChatVertexAI({
model: process.env.LLM_MODEL_NAME,
temperature: 0.2,
maxRetries: 2,
// For web, authOptions.credentials
// authOptions: { ... }
// other params...
});
const embeddings = new VertexAIEmbeddings({
model: process.env.EMBEDDING_MODEL_NAME,
location: process.env.LOCATION,
// ...
});

const vectorStore = new MongoDBAtlasVectorSearch(embeddings, {
collection: collection,
indexName: process.env.INDEX_NAME, // The name of the Atlas search index. Defaults to "default"
textKey: process.env.INDEX_FIELD, // The name of the collection field containing the raw content. Defaults to "text"
embeddingKey: process.env.EMBEDDING_FIELD, // The name of the collection field containing the embedded text. Defaults to "embedding"
});

try {
console.log('Connected successfully to MongoDB');
if (route === "/findOne") {
const filter = payload.filter || {};
const projection = payload.projection || {};
try{
const result = {
document: await collection.findOne(filter, projection)
};
return res.status(200).send(result);
}
finally{
await client.close();
}
}
// else if(route === '/find'){
// const aggQuery = [];
// const filter = payload.filter || {};
// const sort = payload.sort || {};
// try {
// const result = await collection.findOne(query);
// console.log('Found document:', result);
// return res.status(200).send(result);
// }
// finally{
// await client.close();
// }

// }
else if(route === '/insertOne'){
const document = payload.document || {};
if (!document) {
return res.status(400).send('Document is required');
}
const result = await collection.insertOne(document);
console.log('Inserted document:', result.insertedId);
return res.status(200).send(result);
}
// else if(route === '/insertMany'){
// const documents = payload.documents || [];
// if (!documents.length) {
// return res.status(400).send('Documents are required');
// }

// collection.updateOne({ name: 'John Doe' }, { $set: { name: 'Jane Doe' } });
// }
else if(route === '/vectorSearch'){
const query = payload.query || {};
if (!query) {
return res.status(400).send('Query is required');
}
try{
// const singleVector = await embeddings.embedQuery(query);
// console.log(singleVector);
const similaritySearchResults = await vectorStore.similaritySearch(
query,
1
);
for (const doc of similaritySearchResults) {
console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);
}

const aiMsg = await llm.invoke([
[
"system",
process.env.LLM_PROMPT,
],
similaritySearchResults[0].pageContent,
]);

return res.status(200).send(aiMsg.content);

}
finally{
await client.close();
}

}
}
catch (error) {
// Handle the error here
console.error(error);
}


})
Loading

0 comments on commit 9a2cf73

Please sign in to comment.