Skip to content

Commit

Permalink
chore: some small organizing of the ai related code
Browse files Browse the repository at this point in the history
  • Loading branch information
lholmquist committed Sep 9, 2024
1 parent 8d6ae76 commit 15623b5
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 138 deletions.
81 changes: 0 additions & 81 deletions ai/ai.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
const { ChatOpenAI } = await import("@langchain/openai");
import { RunnableWithMessageHistory } from '@langchain/core/runnables';
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { ChatMessageHistory } from 'langchain/stores/message/in_memory';

let sessions = {};
let chainWithHistory;

export function getModel(options = {}) {
return new ChatOpenAI({
Expand All @@ -15,78 +9,3 @@ export function getModel(options = {}) {
baseURL: options.baseURL || process.env.AI_BASE_URL || 'http://localhost:8000/v1'
});
}


export function createChain(model) {
////////////////////////////////
// CREATE CHAIN
const prompt = ChatPromptTemplate.fromMessages([
[ 'system',
'You are a helpful, respectful and honest assistant named "Parasol Assistant".' +
'You will be given a claim summary, references to provide you with information, and a question. You must answer the question based as much as possible on this claim with the help of the references.' +
'Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.' +
'If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don\'t know the answer to a question, please don\'t share false information.' +
'Don\'t make up policy term limits by yourself'
],
new MessagesPlaceholder('history'),
[ 'human', '{input}' ]
]);

const chain = prompt.pipe(model);

chainWithHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: (sessionId) => {
if (sessions[sessionId] === undefined) {
sessions[sessionId] = new ChatMessageHistory();
}
return sessions[sessionId];
},
inputMessagesKey: 'input',
historyMessagesKey: 'history',
});

}

export async function answerQuestion(question, sessionId) {
const result = await chainWithHistory.stream(
{ input: createQuestion(question) },
{ configurable: { sessionId: sessionId } }
);

return result;
}

export function resetSessions(sessionId) {
delete sessions[sessionId];
}

function createQuestion(rawQuestion) {
return `Claim ID: ${rawQuestion.claimId}
Claim Inception Date: ${rawQuestion.inceptionDate}
Claim Summary:
${rawQuestion.claim}
Question: ${rawQuestion.query}
`
}



// @SystemMessage("""
// You are a helpful, respectful and honest assistant named "Parasol Assistant".
// You will be given a claim summary, references to provide you with information, and a question. You must answer the question based as much as possible on this claim with the help of the references.
// Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.

// If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.
// """
// )
// @UserMessage("""
// Claim Summary:
// {{query.claim}}

// Question: {{query.query}}
// """)
63 changes: 63 additions & 0 deletions ai/chatbot.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { RunnableWithMessageHistory } from '@langchain/core/runnables';
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { ChatMessageHistory } from 'langchain/stores/message/in_memory';

let sessions = {};
let chainWithHistory;

export function createChain(model) {
////////////////////////////////
// CREATE CHAIN
const prompt = ChatPromptTemplate.fromMessages([
[ 'system',
'You are a helpful, respectful and honest assistant named "Parasol Assistant".' +
'You will be given a claim summary, references to provide you with information, and a question. You must answer the question based as much as possible on this claim with the help of the references.' +
'Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.' +
'If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don\'t know the answer to a question, please don\'t share false information.' +
'Don\'t make up policy term limits by yourself'
],
new MessagesPlaceholder('history'),
[ 'human', '{input}' ]
]);

const chain = prompt.pipe(model);

chainWithHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: (sessionId) => {
if (sessions[sessionId] === undefined) {
sessions[sessionId] = new ChatMessageHistory();
}
return sessions[sessionId];
},
inputMessagesKey: 'input',
historyMessagesKey: 'history',
});

}

export async function answerQuestion(question, sessionId) {
const result = await chainWithHistory.stream(
{ input: createQuestion(question) },
{ configurable: { sessionId: sessionId } }
);

return result;
}

export function resetSessions(sessionId) {
delete sessions[sessionId];
}

function createQuestion(rawQuestion) {
return `Claim ID: ${rawQuestion.claimId}
Claim Inception Date: ${rawQuestion.inceptionDate}
Claim Summary:
${rawQuestion.claim}
Question: ${rawQuestion.query}
`
}
59 changes: 59 additions & 0 deletions routes/chatbot-ws-route.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { getModel } from '../ai/ai.mjs';
import { createChain, answerQuestion, resetSessions } from '../ai/chatbot.mjs';

async function chatbotWSRoute (fastify, options) {
fastify.get('/ws/query', { websocket: true }, (ws, req) => {
const controller = new AbortController();

ws.on('close', () => {
resetSessions(ws);
controller.abort();
console.log('connection closed');
});

ws.on('error', console.error);

ws.on('message', async (data) => {
const stringData = data.toString();

// This should be JSON
let JSONmessage;
try {
JSONmessage = JSON.parse(stringData);
} catch(err) {
console.log(err);
}

console.log('Query from the Client', JSONmessage);

console.log('Starting to Ask', new Date());

try {
const answerStream = await answerQuestion(JSONmessage, ws);

for await (const chunk of answerStream) {
console.log(`Got Chat Response: ${chunk.content}`);

//'{"type":"token","token":" Hello","source":""}'
const formattedAnswer = {
type: 'token',
token: chunk.content,
source: ''
};

ws.send(JSON.stringify(formattedAnswer));
}
} catch (err) {
console.log(err);
}

console.log('Done Asking', new Date());
});

// AI Related Setup
const model = getModel().bind({ signal: controller.signal });
createChain(model);
});
}

export default chatbotWSRoute;
65 changes: 8 additions & 57 deletions server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

import claimsRoute from './routes/claims-route.mjs';
import chatbotWSRoute from './routes/chatbot-ws-route.mjs';
import sqliteConnector from './plugins/db/sqlite-connector.mjs';

import { getModel, createChain, answerQuestion, resetSessions } from './ai/ai.mjs';

// Setup Logging
const fastify = Fastify({
logger: true
});

// Register the Fastify ENV plugin for reading the .env files
await fastify.register(fastifyEnv, {
schema: {
type: 'object'
},
dotenv: true
});

// WebUI related setup and serving
const webuiLocation = '../parasol-insurance/app/src/main/webui/dist';

fastify.register(fastifyStatic, {
Expand All @@ -33,65 +35,14 @@ fastify.register(fastifyStatic, {

fastify.get('/*', (req, res) => {
res.send(fs.createReadStream(path.join(__dirname, webuiLocation, 'index.html')));
})
});

// Register plugins and Routes
fastify.register(sqliteConnector);
fastify.register(claimsRoute);
fastify.register(fastifyWebsocket);
fastify.register(async function (fastify) {
fastify.get('/ws/query', { websocket: true }, (ws, req) => {
const controller = new AbortController();

ws.on('close', () => {
resetSessions(ws);
controller.abort();
console.log('connection closed');
});

ws.on('error', console.error);

ws.on('message', async (data) => {
const stringData = data.toString();

// This should be JSON
let JSONmessage;
try {
JSONmessage = JSON.parse(stringData);
} catch(err) {
console.log(err);
}

console.log('Query from the Client', JSONmessage);

console.log('Starting to Ask', new Date());

try {
const answerStream = await answerQuestion(JSONmessage, ws);

for await (const chunk of answerStream) {
console.log(`Got Chat Response: ${chunk.content}`);

//'{"type":"token","token":" Hello","source":""}'
const formattedAnswer = {
type: 'token',
token: chunk.content,
source: ''
};

ws.send(JSON.stringify(formattedAnswer));
}
} catch (err) {
console.log(err);
}

console.log('Done Asking', new Date());
});

// AI Related Setup
const model = getModel().bind({ signal: controller.signal });
createChain(model);
});
});
fastify.register(claimsRoute);
fastify.register(chatbotWSRoute);

/**
* Run the server!
Expand Down

0 comments on commit 15623b5

Please sign in to comment.