Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code explanations #1

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/app/api/search/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { RagPipelineResponse, RagResult, RagAnswer, RagDocument } from '@/lib/ra
const workspace = process.env.DEEPSET_CLOUD_WORKSPACE || 'default';
const pipeline = process.env.DEEPSET_CLOUD_PIPELINE || 'my-pipeline';
const apiToken = process.env.DEEPSET_CLOUD_API_KEY;

// Construct the API URL for the deepset Cloud Search endpoint
const apiUrl = `https://api.cloud.deepset.ai/api/v1/workspaces/${workspace}/pipelines/${pipeline}/search`;

export async function POST(request) {
// Extract the query from the incoming request
const { query } = await request.json();

console.log(`${__filename}: Received text:`, query);

try {
// Prepare the options for the API request to deepset Cloud
const requestOptions = {
method: 'POST',
headers: {
Expand All @@ -30,24 +32,25 @@ export async function POST(request) {

console.log(`${__filename}: apiUrl:`, apiUrl);
console.log(`${__filename}: Request Options:\n`, requestOptions);

// Send the request to the deepset Cloud API
const res = await fetch(apiUrl, requestOptions);

// Parse the JSON response
const data = await res.json();

console.log(`${__filename}: Response Data:\n`, data);

// Create a RagPipelineResponse object from the API response
const ragResponse = new RagPipelineResponse(data);

ragResponse.processResults();

console.log(`${__filename}: Processed and returning:\n`, ragResponse);

// Return the processed response to the client
return new Response(JSON.stringify({ response: ragResponse }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
// If any error occurs, log it and return an error response
console.error(`${__filename}: Error:`, error);
return new Response(JSON.stringify({ error: 'An error occurred' }), {
status: 500,
Expand Down
14 changes: 10 additions & 4 deletions src/app/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import ReactMarkdown from 'react-markdown';
import { TextField, Button, Box, Typography, CircularProgress } from '@mui/material';
import { addReferences } from '@/utils/ragUtils';

// This component represents the main page of the UI
const HomePage = () => {
// State variables to manage user input, answer display, and loading status
const [inputText, setInputText] = useState('');
const [answerText, setAnswerText] = useState('');
const [referenceText, setReferenceText] = useState('');
const [isLoading, setIsLoading] = useState(false);

// Function to handle form submission
const handleSubmit = async () => {
setIsLoading(true);
try {
Expand All @@ -24,11 +27,13 @@ const HomePage = () => {
},
body: JSON.stringify({ query: inputText }),
});

const data = await res.json();
console.log('Response Data:', data);
// Extract relevant information from the API response
const { answer, file, meta } = data.response.results[0].answers[0];

// Process the answer to include references
if (meta && typeof meta === 'object' && !Array.isArray(meta) && Object.keys(meta).length > 0) {
const { referenceList, answerWithReferences } = addReferences(answer, meta);
setAnswerText(answerWithReferences);
Expand All @@ -37,22 +42,21 @@ const HomePage = () => {
setAnswerText(answer);
setReferenceText("No references from reference_predictor found.")
}

} catch (error) {
console.error('Error:', error);
setAnswerText('An error occurred while processing your request.');
} finally {
setIsLoading(false);
}
};

// Component to display a loading message
const LoadingMessage = () => (
<Box display="flex" alignItems="center">
<CircularProgress size={20} style={{ marginRight: '10px' }} />
<Typography>Generating an answer...</Typography>
</Box>
);

// The main render function for the component
return (
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start', mt: 4, p: 4, width: '100%' }}>
<Box sx={{ display: 'flex', flexDirection: 'column', mr: 4, width: '30%' }}>
Expand All @@ -70,6 +74,7 @@ const HomePage = () => {
{isLoading ? '...' : 'Submit'}
</Button>
</Box>
{/* Middle column: answer display area*/}
<Box
sx={{
width: '50%',
Expand All @@ -83,6 +88,7 @@ const HomePage = () => {
>
{isLoading ? <LoadingMessage /> : <ReactMarkdown>{answerText}</ReactMarkdown>}
</Box>
{/* Right column: references display area*/}
<Box
sx={{
width: '25%',
Expand Down
31 changes: 21 additions & 10 deletions src/lib/ragPipelineResponse.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,77 @@
// src/lib/ragPipelineResponse.js

// This class represents the overall response from the RAG pipeline
export class RagPipelineResponse {
constructor(data) {
// validate the input data
if (!data || typeof data !== 'object') {
throw new Error('Invalid data provided to RagPipelineResponse');
}
// Initialize properties from the input data
this.queryId = data.query_id || '';
this.results = Array.isArray(data.results)
// Create an array of RagResult objects from the results data
this.results = Array.isArray(data.results)
? data.results.map(resultData => new RagResult(resultData))
: [];
}

// Process all results in this response
processResults() {
this.results.forEach(result => result.processAnswers());
}
}

// This class represents a single result from the RAG pipeline
export class RagResult {
constructor(data) {
// Validate input data
if (!data || typeof data !== 'object') {
throw new Error('Invalid data provided to RagResult');
}
// Initialize properties from the input data
this.queryId = data.query_id || '';
this.query = data.query || '';
this.answers = Array.isArray(data.answers)
// Create arrays of RagAnswer and RagDocument objects
this.answers = Array.isArray(data.answers)
? data.answers.map(answerData => new RagAnswer(answerData))
: [];
this.documents = Array.isArray(data.documents)
this.documents = Array.isArray(data.documents)
? data.documents.map(documentData => new RagDocument(documentData))
: [];
}

// Process all answers in this result
processAnswers() {
this.answers.forEach(answer => answer.processReferences(this.documents));
}
}

// This class represents an individual answer from the RAG pipeline
export class RagAnswer {
constructor(data) {
// Validate input data
if (!data || typeof data !== 'object') {
throw new Error('Invalid data provided to RagAnswer');
}
// Initialize properties from the input data
this.answer = data.answer || '';
this.type = data.type || '';
this.meta = data.meta || {};
this.file = data.file || null;
}

// Process references for this answer
processReferences(documents) {
// If there are references and documents, link them together
if (Array.isArray(this.meta?._references) && Array.isArray(documents)) {
this.meta._references.forEach(reference => {
reference.document = documents.find(doc => doc.id === reference.document_id) || null;
});
}
}
}

// This class represents a document referenced in the RAG pipeline response
export class RagDocument {
constructor(data) {
// Validate input data
if (!data || typeof data !== 'object') {
throw new Error('Invalid data provided to RagDocument');
}
// Initialize properties from the input data
this.id = data.id || '';
this.content = data.content || '';
this.contentType = data.content_type || '';
Expand Down
7 changes: 4 additions & 3 deletions src/utils/ragUtils.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// src/app/utils/ragUtils.js

// This function adds reference notes to the answer and creates a reference list
// It filters for grounded references only, creates reference notes (like [1], [2], etc.) and extracts the relevant context from the referenced document
export const addReferences = (answer, meta) => {
const references = meta._references.filter(ref => ref.label === 'grounded');
const { referenceList, answerWithReferences } = references.reduce((acc, ref, index) => {
const referenceNote = `[${index + 1}]`;
const context = ref.document.content.slice(ref.doc_start_idx, ref.doc_end_idx);

// Update reference list
// Update the reference list
acc.referenceList += `${referenceNote} ${context}\n\n`;

// Insert reference note into answer
const answerEndIndex = ref.answer_end_idx + acc.endIndexIncrement;
acc.answerWithReferences =
acc.answerWithReferences =
acc.answerWithReferences.slice(0, answerEndIndex) +
referenceNote +
acc.answerWithReferences.slice(answerEndIndex);
Expand Down