Skip to content

Commit

Permalink
modularized admin page - Adithya S K
Browse files Browse the repository at this point in the history
  • Loading branch information
adithya-s-k committed Aug 27, 2024
1 parent ccf3e1f commit be0fabd
Show file tree
Hide file tree
Showing 4 changed files with 487 additions and 448 deletions.
129 changes: 129 additions & 0 deletions frontend/app/admin/DataIngestion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use client';
import React, { useState } from 'react';
import { useAuth } from '@/app/authProvider';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { LoaderIcon, Upload } from 'lucide-react';
import { FileUpload } from '@/components/ui/file-upload';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';

export default function DataIngestion() {
const { axiosInstance } = useAuth();
const [file, setFile] = useState<File | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [uploadResult, setUploadResult] = useState<string | null>(null);

const handleFileUpload = (files: File[]) => {
if (files.length > 0) {
setFile(files[0]);
}
};

const uploadFile = async () => {
if (!file) return;

setIsUploading(true);
setUploadResult(null);

const formData = new FormData();
formData.append('file', file);

try {
const response = await axiosInstance.post(
'/api/admin/upload_data',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);

setUploadResult(`File uploaded successfully. ${response.data.message}`);
} catch (error) {
console.error('Error uploading file:', error);
setUploadResult('Error uploading file. Please try again.');
} finally {
setIsUploading(false);
setFile(null);
}
};

return (
<Card>
<CardHeader>
<CardTitle>Data Ingestion</CardTitle>
<CardDescription>Manage data ingestion processes.</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="w-full mx-auto min-h-96 border border-dashed bg-white dark:bg-black border-neutral-200 dark:border-neutral-800 rounded-lg">
<FileUpload onChange={handleFileUpload} />
</div>
{file && (
<div className="flex items-center space-x-2">
<Button
onClick={uploadFile}
disabled={isUploading}
variant="default"
className="flex items-center w-full"
>
{isUploading ? (
<LoaderIcon className="mr-2 h-4 w-4 animate-spin" />
) : (
<Upload className="mr-2 h-4 w-4" />
)}
{isUploading ? 'Uploading and Ingesting...' : 'Ingest File'}
</Button>
</div>
)}
{uploadResult && (
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
{uploadResult}
</div>
)}
<div>
<Label htmlFor="ingestionStrategy">
Ingestion Strategy (Coming Soon)
</Label>
<Select disabled>
<SelectTrigger>
<SelectValue placeholder="Select a strategy" />
</SelectTrigger>
<SelectContent>
<SelectItem value="fixed">Fixed Size Chunking</SelectItem>
<SelectItem value="semantic">Semantic Chunking</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="parser">Parser (Coming Soon)</Label>
<Select disabled>
<SelectTrigger>
<SelectValue placeholder="Select a parser" />
</SelectTrigger>
<SelectContent>
<SelectItem value="llama">Llama Parse</SelectItem>
<SelectItem value="omni">Omniparse</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>
);
}
184 changes: 184 additions & 0 deletions frontend/app/admin/RAGConfiguration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
'use client';
import React, { useEffect, useState } from 'react';
import { useAuth } from '@/app/authProvider';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Separator } from '@/components/ui/separator';
import { PlusCircle, Trash2 } from 'lucide-react';

export default function RAGConfiguration() {
const { axiosInstance } = useAuth();
const [systemPrompt, setSystemPrompt] = useState<string>('');
const [suggestedQuestions, setSuggestedQuestions] = useState<string[]>([]);
const [configLoading, setConfigLoading] = useState<boolean>(true);
const [configError, setConfigError] = useState<string | null>(null);
const [systemPromptLoading, setSystemPromptLoading] = useState(false);
const [startersLoading, setStartersLoading] = useState(false);
const [initialSystemPrompt, setInitialSystemPrompt] = useState<string>('');
const [initialSuggestedQuestions, setInitialSuggestedQuestions] = useState<
string[]
>([]);

useEffect(() => {
fetchConfig();
}, [axiosInstance]);

const fetchConfig = async () => {
setConfigLoading(true);
setConfigError(null);
try {
const [configResponse, systemPromptResponse] = await Promise.all([
axiosInstance.get('/api/chat/config'),
axiosInstance.get('/api/admin/system-prompt'),
]);

const config = configResponse.data;
const systemPromptData = systemPromptResponse.data;

setSystemPrompt(systemPromptData.system_prompt || '');
setInitialSystemPrompt(systemPromptData.system_prompt || '');
setSuggestedQuestions(config.starterQuestions || []);
setInitialSuggestedQuestions(config.starterQuestions || []);
setConfigLoading(false);
} catch (err: any) {
setConfigError(err.message || 'Failed to fetch configuration');
setConfigLoading(false);
}
};

const updateSystemPrompt = async () => {
setSystemPromptLoading(true);
try {
const response = await axiosInstance.put('/api/admin/system-prompt', {
new_prompt: systemPrompt,
});
if (response.status === 200) {
console.log('System prompt updated successfully');
setInitialSystemPrompt(systemPrompt);
}
} catch (error) {
console.error('Failed to update system prompt:', error);
} finally {
setSystemPromptLoading(false);
}
};

const updateConversationStarters = async () => {
setStartersLoading(true);
try {
const newStarters = suggestedQuestions.filter((q) => q.trim() !== '');
const response = await axiosInstance.put(
'/api/admin/conversation-starters',
{ new_starters: newStarters }
);
if (response.status === 200) {
console.log('Conversation starters updated successfully');
setInitialSuggestedQuestions(newStarters);
}
} catch (error) {
console.error('Failed to update conversation starters:', error);
} finally {
setStartersLoading(false);
}
};

const addSuggestedQuestion = () => {
setSuggestedQuestions([...suggestedQuestions, '']);
};

const updateSuggestedQuestion = (index: number, value: string) => {
const updatedQuestions = [...suggestedQuestions];
updatedQuestions[index] = value;
setSuggestedQuestions(updatedQuestions);
};

const deleteSuggestedQuestion = (index: number) => {
const updatedQuestions = suggestedQuestions.filter((_, i) => i !== index);
setSuggestedQuestions(updatedQuestions);
};

const isSystemPromptChanged = systemPrompt !== initialSystemPrompt;
const areSuggestedQuestionsChanged =
JSON.stringify(suggestedQuestions) !==
JSON.stringify(initialSuggestedQuestions);

if (configLoading) return <div>Loading configuration...</div>;
if (configError) return <div>Error: {configError}</div>;

return (
<Card>
<CardHeader>
<CardTitle>RAG Configuration</CardTitle>
<CardDescription>
Configure Retrieval-Augmented Generation settings.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="systemPrompt">System Prompt</Label>
<Textarea
id="systemPrompt"
value={systemPrompt}
onChange={(e) => setSystemPrompt(e.target.value)}
placeholder="Enter system prompt here..."
/>
<Button
onClick={updateSystemPrompt}
className="mt-2 w-full"
variant={isSystemPromptChanged ? 'default' : 'secondary'}
disabled={systemPromptLoading || !isSystemPromptChanged}
>
{systemPromptLoading ? 'Updating...' : 'Update System Prompt'}
</Button>
</div>
<Separator />
<div>
<Label>Suggested Questions</Label>
{suggestedQuestions.map((question, index) => (
<div key={index} className="flex items-center space-x-2 mt-2">
<Input
value={question}
onChange={(e) => updateSuggestedQuestion(index, e.target.value)}
placeholder="Enter a suggested question..."
/>
<Button
variant="outline"
size="icon"
onClick={() => deleteSuggestedQuestion(index)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
))}
<div className="w-full flex justify-between items-center mt-2">
<Button
variant="outline"
className="mt-2"
onClick={addSuggestedQuestion}
>
<PlusCircle className="h-4 w-4 mr-2" />
Add Question
</Button>
<Button
variant={areSuggestedQuestionsChanged ? 'default' : 'secondary'}
onClick={updateConversationStarters}
className="mt-2 ml-2"
disabled={startersLoading || !areSuggestedQuestionsChanged}
>
{startersLoading ? 'Updating...' : 'Update Conversation Starters'}
</Button>
</div>
</div>
</CardContent>
</Card>
);
}
Loading

0 comments on commit be0fabd

Please sign in to comment.