Skip to content

Commit

Permalink
Chat History microservice for chat data persistence (#286)
Browse files Browse the repository at this point in the history
* Initial Implementation

* Added Updates to the mongoConnection

Signed-off-by: Yogesh Pandey <yogesh.pandey@intel.com>
  • Loading branch information
yogeshmpandey authored Jul 24, 2024
1 parent f5a5489 commit 30d95b7
Show file tree
Hide file tree
Showing 10 changed files with 569 additions and 0 deletions.
105 changes: 105 additions & 0 deletions comps/chathistory/mongo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Chat History Microservice

The Chat History Microservice allows you to store, retrieve and manage chat conversations with a MongoDB database. This microservice can be used for data persistence in OPEA chat applications, enabling you to save and access chat histories.

It can be integrated into any application by making HTTP requests to the provided API endpoints as shown in the flow diagram below.

![Flow Chart](./assets/img/chathistory_flow.png)

## Setup Environment Variables

```bash
export http_proxy=${your_http_proxy}
export https_proxy=${your_http_proxy}
export MONGO_HOST=${MONGO_HOST}
export MONGO_PORT=27017
export DB_NAME=${DB_NAME}
export COLLECTION_NAME=${COLLECTION_NAME}
```

# 🚀Start Microservice with Docker

## Build Docker Image

```bash
cd ../../../../
docker build -t opea/chathistory-mongo-server:latest --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy -f comps/chathistory/mongo/docker/Dockerfile .
```

## Run Docker with CLI

- Run mongoDB image

```bash
docker run -d -p 27017:27017 --name=mongo mongo:latest
```

- Run the chathistory Service

```bash
docker run -d --name="chathistory-mongo-server" -p 6013:6013 -p 6012:6012 -p 6014:6014 -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e no_proxy=$no_proxy -e MONGO_HOST=${MONGO_HOST} -e MONGO_PORT=${MONGO_PORT} -e DB_NAME=${DB_NAME} -e COLLECTION_NAME=${COLLECTION_NAME} opea/chathistory-mongo-server:latest
```

# Invoke Microservice

Once chathistory service is up and running, users can update the database by using the below API endpoint. The API returns a unique UUID for the saved conversation.

```bash
curl -X 'POST' \
http://${host_ip}:6012/v1/chathistory/create \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"messages": "test Messages", "user": "test"
}
}'
```

- Get all the Conversations for a user

```bash
curl -X 'POST' \
http://${host_ip}:6013/v1/chathistory/get \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"user": "test"}'
```

- Get specific conversation by specifying the id.

```bash
curl -X 'POST' \
http://${host_ip}:6013/v1/chathistory/get \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"user": "test", "id":"668620173180b591e1e0cd74"}'
```

- Update the conversation by specifying the id.

```bash
curl -X 'POST' \
http://${host_ip}:6012/v1/chathistory/create \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"messages": "test Messages Update", "user": "test"
},
"id":"668620173180b591e1e0cd74"
}'
```

- Delete a stored conversation by specifying the id.

```bash
curl -X 'POST' \
http://${host_ip}:6014/v1/chathistory/delete \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"user": "test", "id":"668620173180b591e1e0cd74"}'
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
135 changes: 135 additions & 0 deletions comps/chathistory/mongo/chathistory_mongo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from typing import Optional

from fastapi import HTTPException
from mongo_store import DocumentStore
from pydantic import BaseModel

from comps.cores.mega.micro_service import opea_microservices, register_microservice
from comps.cores.proto.api_protocol import ChatCompletionRequest


class ChatMessage(BaseModel):
data: ChatCompletionRequest
first_query: Optional[str] = None
id: Optional[str] = None


class ChatId(BaseModel):
user: str
id: Optional[str] = None


def get_first_string(value):
if isinstance(value, str):
return value
elif isinstance(value, list):
# Assuming we want the first string from the first dictionary
if value and isinstance(value[0], dict):
first_dict = value[0]
if first_dict:
# Get the first value from the dictionary
first_key = next(iter(first_dict))
return first_dict[first_key]


@register_microservice(
name="opea_service@chathistory_mongo_create",
endpoint="/v1/chathistory/create",
host="0.0.0.0",
input_datatype=ChatMessage,
port=6012,
)
async def create_documents(document: ChatMessage):
"""Creates or updates a document in the document store.
Args:
document (ChatMessage): The ChatMessage object containing the data to be stored.
Returns:
The result of the operation if successful, None otherwise.
"""

try:
if document.data.user is None:
raise HTTPException(status_code=500, detail="Please provide the user information")
store = DocumentStore(document.data.user)
store.initialize_storage()
if document.first_query is None:
document.first_query = get_first_string(document.data.messages)
if document.id:
res = await store.update_document(document.id, document.data, document.first_query)
else:
res = await store.save_document(document)
return res
except Exception as e:
# Handle the exception here
print(f"An error occurred: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))


@register_microservice(
name="opea_service@chathistory_mongo_get",
endpoint="/v1/chathistory/get",
host="0.0.0.0",
input_datatype=ChatId,
port=6013,
)
async def get_documents(document: ChatId):
"""Retrieves documents from the document store based on the provided ChatId.
Args:
document (ChatId): The ChatId object containing the user and optional document id.
Returns:
The retrieved documents if successful, None otherwise.
"""
try:
store = DocumentStore(document.user)
store.initialize_storage()
if document.id is None:
res = await store.get_all_documents_of_user()
else:
res = await store.get_user_documents_by_id(document.id)
return res
except Exception as e:
# Handle the exception here
print(f"An error occurred: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))


@register_microservice(
name="opea_service@chathistory_mongo_delete",
endpoint="/v1/chathistory/delete",
host="0.0.0.0",
input_datatype=ChatId,
port=6014,
)
async def delete_documents(document: ChatId):
"""Deletes a document from the document store based on the provided ChatId.
Args:
document (ChatId): The ChatId object containing the user and document id.
Returns:
The result of the deletion if successful, None otherwise.
"""
try:
store = DocumentStore(document.user)
store.initialize_storage()
if document.id is None:
raise Exception("Document id is required.")
else:
res = await store.delete_document(document.id)
return res
except Exception as e:
# Handle the exception here
print(f"An error occurred: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))


if __name__ == "__main__":
opea_microservices["opea_service@chathistory_mongo_get"].start()
opea_microservices["opea_service@chathistory_mongo_create"].start()
opea_microservices["opea_service@chathistory_mongo_delete"].start()
10 changes: 10 additions & 0 deletions comps/chathistory/mongo/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

import os

# MONGO configuration
MONGO_HOST = os.getenv("MONGO_HOST", "localhost")
MONGO_PORT = os.getenv("MONGO_PORT", 27017)
DB_NAME = os.getenv("DB_NAME", "OPEA")
COLLECTION_NAME = os.getenv("COLLECTION_NAME", "ChatHistory")
31 changes: 31 additions & 0 deletions comps/chathistory/mongo/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

FROM python:3.11-slim

ENV LANG C.UTF-8

RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \
build-essential \
libgl1-mesa-glx \
libjemalloc-dev \
vim

RUN useradd -m -s /bin/bash user && \
mkdir -p /home/user && \
chown -R user /home/user/

USER user

COPY comps /home/user/comps
COPY requirements.txt /home/user/

RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r /home/user/comps/chathistory/mongo/requirements.txt && \
pip install --no-cache-dir -r /home/user/requirements.txt

ENV PYTHONPATH=$PYTHONPATH:/home/user

WORKDIR /home/user/comps/chathistory/mongo

ENTRYPOINT ["python", "chathistory_mongo.py"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

version: "3"
services:
mongo:
image: mongo:7.0.11
container_name: mongodb
ports:
- 27017:27017
environment:
http_proxy: ${http_proxy}
https_proxy: ${https_proxy}
no_proxy: ${no_proxy}
command: mongod --quiet --logpath /dev/null

chathistory-mongo:
image: opea/chathistory-mongo-server:latest
container_name: chathistory-mongo-server
ports:
- "6012:6012"
- "6013:6013"
- "6014:6014"
ipc: host
environment:
http_proxy: ${http_proxy}
no_proxy: ${no_proxy}
https_proxy: ${https_proxy}
MONGO_HOST: ${MONGO_HOST}
MONGO_PORT: ${MONGO_PORT}
COLLECTION_NAME: ${COLLECTION_NAME}
restart: unless-stopped

networks:
default:
driver: bridge
22 changes: 22 additions & 0 deletions comps/chathistory/mongo/mongo_conn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

from typing import Any

import motor.motor_asyncio as motor
from config import DB_NAME, MONGO_HOST, MONGO_PORT


class MongoClient:
conn_url = f"mongodb://{MONGO_HOST}:{MONGO_PORT}/"

@staticmethod
def get_db_client() -> Any:
try:
client = motor.AsyncIOMotorClient(MongoClient.conn_url)
db = client[DB_NAME]
return db

except Exception as e:
print(e)
raise Exception(e)
Loading

0 comments on commit 30d95b7

Please sign in to comment.