LLM and Hugging Face: Table Question Answering
Table Question Answering (Table QA) is the answering a question about an information on a given table.7
In this project upload a '.csv' file and ask a question about the data on a given table to get answers based on the uploaded table.
Setup Backend code
Install multer
and csv-parser
to upload a file and parse .csv files. Also fs
library to process the file at backend.
npm install multer csv-parser
Continue from initial backend setup as shown here and then process file and route hugging face API as shown below.
const multer = require('multer')
const csv = require('csv-parser')
const fs = require('fs')
const upload = multer({dest: 'uploads/')
app.post('api/table-question-answer', upload.single("file"), async(req, res) => {
const { ques } = req.body
const file = req.file
console.log(file)
if (!file || !fs.existsSync(file.path)) {
return res.status(400).send('File not found or inaccessible.');
}
// parse csv file
if(file.mimetype === 'text/csv') {
const table = []
fs.createReadStream(file.path)
.on('error', () => {
console.log('Error in reaind a file ', error)
res.status(500).json({message: `Error in reading file ${error.message}`})
})
.pipe(csv())
.on('data', (row) => table.push(row))
.on('end', async() => {
try {
const response = await hf.tableQuestionAnswering({
model: "google/tapas-large-finetuned-wtq",
inputs: {
query: ques,
table: table
}
})
fs.unlink(file.path, (err) => {
if (err) console.error('Error deleting file:', err);
});
res.status(200).json(response);
}catch(error) {
console.error('Error processing query:', error);
res.status(500).json({ message: `Error processing query: ${error.message}` });
}
}) // end on
} // end if
})
The output format for file and hf.tableQuestionAnswering is:
// for file:
// {
// fieldname: 'file',
// originalname: 'example.csv',
// encoding: '7bit',
// mimetype: 'text/csv',
// destination: 'C:\\Users\\...\\llm_backend\\uploads',
// filename: '7dfa8072d3829a4851992e8a0455dthw2',
// path: 'C:\\Users\...\\llm_backend\\uploads\\7dfa8072d3829a4851992e8a0455dthw2',
// size: 12473
}
// for hf table QA output
// {
// answer: '...',
// coordinates: [ [.., .. ] ],
// cells: [ '...' ],
// aggregator: 'NONE'
// }
Setup frontend UI [ REACT ]
For frontend used FormData
to process a file and sent to backend server. In headers use multipart/form-data
to process file at backend or else you will encounter error.
import React, { useState } from 'react'
import axios from 'axios'
const TableQA = () => {
const [ques, setQues] = useState('')
const [file, setFile] = useState(null)
const [answer, setAnswer] = useState('')
const handleFileChange = (e) => {
setFile(e.target.files[0])
}
const handleTableQA = async (e) => {
e.preventDefault()
// const tableData = JSON.parse(table)
if(!file) {
alert("Please upload a file")
return
}
const formData = new FormData()
formData.append('file', file)
formData.append('ques', ques)
try {
const response = await axios.post('http://localhost:5000/api/table-question-answer', formData, {
headers: {'Content-Type': 'multipart/form-data' },
})
setAnswer(response.data.answer)
} catch(err) {
console.error(`Error: ${err.message}`)
}
}
return (
<>
<form onSubmit={handleTableQA}>
<h2>Table Question Answer</h2>
<p>Your question</p>
<textarea
className='qa'
rows='3'
cols='50'
value={ques}
onChange={(e) => setQues(e.target.value)}
>
</textarea>
<input
type='file'
onChange={handleFileChange}
/>
<button>Get Answer</button>
</form>
<div className='result'>
<h3>Answer ?</h3>
<p>{answer}</p>
</div>
</>
)
}
export default TableQA
Run the application
- Backend
// if nodemon installed
// in package.json
{
....,
"scripts": {
"start": "nodemon server.js",
----------,
}
}
// in commage prompt
npm start
// if nodemon not installed
node server.js
- Frontend
npm start
The output of Table Question Answering renders as below