Task Reference: https://answersai.notion.site/AnswersAi-Backend-Technical-Assessment-22342a93b79c438badf7018d3284eb22
Deployed API link: https://llm-service-q1p2.onrender.com
Initial requests might timeout as new instance starts which might take some time (~1-2 min).
You can first register user, then login and then ask questions for user. You can additionally fetch user and question details with the api.
This is a simple express backend to answer user questions using langchain and anthropic. The source code is modular which makes it scalable as needed. I have implemented it as below:
- I have integrated session based authentication using JWTs and
x-access-tokenheader. - I have defined two data models for User and Question as it seemed sufficient to me as per the problem statement. Service routes and controllers are also defined as per models for separation of concerns and modularity of the source code. There is a separated controller for
auth. - Clients module contains third party integrations which for now is just langchain for the service.
- I have created a middleware
verify-tokenfor JWT based session management which is in middlewares module. - Lib module contains other general purpose functions which comprises here of a Responder which is for handling responses.
-
Usermodelid (pk, autogenerated UUID) name (user name, notnull) email (user email, unique and notnull) encryptedPassword (hashed password, notnull) -
Questionmodelid (pk, autogenerated UUID) que (user question, notnull) generatedAnswer (generated answer, notnull) userId (foreign key for user id)
First you need to clone the repo to get source on your local machine. Then follow either of the below steps to get the service up:
-
Using npm
- Define following env vars in a .env at the rootDir of source
JWT_SECRET ANTHROPIC_API_KEY PORT (optional, defaults to 3000) DB_CONN_STRING (valid DSN url for a postgreSQL db) - Run
npm installto install dependency packages - Run
npm run start:devto run the express server Service would be live athttp://localhost:$PORT
- Define following env vars in a .env at the rootDir of source
-
Using docker
- Define following env vars in a .env at the rootDir of source
Or, you can define these variables in context of your shell and read these through your dockerfile using
JWT_SECRET ANTHROPIC_API_KEY PORT (optional, defaults to 3000) DB_CONN_STRING (valid DSN url for a postgreSQL db)ENVwith some modifications - Run
docker build -f Dockerfile -t 'image_name:tag' .to build the container image - Run
docker run -it -p HPORT:CPORT 'image_name:tag'to spin up a running container for the image with provided tag HPORT: host port (your local machine port to be mapped to service) CPORT: container port (port at which your service is exposed in container, defaults to 3000 in this case without$PORT) Service would be live athttp://localhost:HPORT
- Define following env vars in a .env at the rootDir of source
You can generate OPENAPI definitions for API routes of service using npm run gen-swagger.
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "REST API",
"description": ""
},
"host": "localhost:3000",
"basePath": "/",
"schemes": [
"http"
],
"paths": {
"/api/questions/": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"question": {
"example": "any"
}
}
}
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/questions/{questionId}": {
"get": {
"description": "",
"parameters": [
{
"name": "questionId",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/users/": {
"post": {
"description": "",
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/users/{userId}": {
"get": {
"description": "",
"parameters": [
{
"name": "userId",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/users/{userId}/questions": {
"get": {
"description": "",
"parameters": [
{
"name": "userId",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/auth/login": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"email": {
"example": "any"
},
"password": {
"example": "any"
}
}
}
}
],
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/auth/logout": {
"post": {
"description": "",
"responses": {
"default": {
"description": ""
}
}
}
},
"/api/auth/refresh": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"email": {
"example": "any"
},
"password": {
"example": "any"
}
}
}
}
],
"responses": {
"default": {
"description": ""
}
}
}
}
}
}
This service can be deployed as a dockerized container anywhere. Further, I have deployed it on render using blueprint render.yaml which is similar to kubernetes resource definitions which encloses both the service and database definitions.
Deployed API link: https://llm-service-q1p2.onrender.com
For production scenarios, cloud native deployment is a very good and scalable choice. Using kubernetes and resource definitions, one can deploy service and db along with a load balancer. Two possible ways can be used for this approach:
-
k3s based: With a compute engine such as on-prem machine or cloud compute (for example, AWS ECS), one can setup k3s which is a very lightweight version of k8s and then deploy the resource definitions in a spinned up cluster.
-
Cloud kubernetes engines: Using a cloud kubernetes engine such as GKE, one can deploy resource definitions directly and cluster can be configured manually or programmatically through other IAC(Infra as code) approaches
We can have a call to elaborate better and discuss the possible production-ready architectures with their pros and cons.
Link: https://bit.ly/resvats
