Merge branch 'develop' into 'featureApollo'

# Conflicts:
#   src/router/index.js
#   src/views/pages/KsSimilaritySearch.vue
This commit is contained in:
Sumedh
2024-08-09 03:58:23 +00:00
16 changed files with 875 additions and 318 deletions

View File

@@ -1,98 +1,322 @@
<template>
<div className="card">
<div className="card">
<DataTable v-model:filters="filters" :value="ksdocuments" paginator showGridlines :rows="10" dataKey="id"
filterDisplay="menu" :loading="loading"
:globalFilterFields="['ingestionInfo.metadata.KsApplicationName', 'ingestionInfo.metadata.KsFileSources', 'ingestionInfo.metadata.KsDocSource', 'ingestionStatus', 'ingestionDateFormat']">
<template #header>
<div class="flex items-center justify-between gap-4 p-4 ">
<span class="text-xl font-bold">KS Documents</span>
<div class="flex items-center gap-2 flex-grow">
<IconField class="flex-grow">
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="Keyword Search" />
</IconField>
</div>
<Button icon="pi pi-plus" rounded raised @click="newKsDocument()" v-tooltip="'Create New Document'"
class="mr-2" />
<Button icon="pi pi-check-circle" rounded raised @click="startlngestion()"
v-tooltip="'Start All documents Ingestion'" class="mr-8" :disabled="allDocumentsIngested"
:class="{ 'p-button-danger': allDocumentsIngested }" />
</div>
</template>
<template #empty>No Records found</template>
<template #loading>Loading Data. Please wait....</template>
<!--Column field="id" header="id" sortable style="min-width: 12rem">
<template #body="slotProps">
<Tag>ksdocuments: {{ slotProps.data.id }}</Tag>
<Tag>ksingestioninfo: {{ slotProps.data.ingestionInfo.id }}</Tag>
</template>
</Column-->
<!--Column field="ingestionInfo.id" header="ksingestioninfo id" sortable style="min-width: 12rem" /-->
<Column field="ingestionInfo.metadata.KsApplicationName" header="KSApplicationName" sortable
style="min-width: 12rem">
<template #body="{ data }">
{{ data.ingestionInfo.metadata.KsApplicationName }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()" placeholder="Search by File" />
</template>
</Column>
<Column field="ingestionInfo.metadata.KsFileSource" header="KsFileSource" sortable>
<template #body="{ data }">
{{ data.ingestionInfo.metadata.KsFileSource }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File Name" />
</template>
</Column>
<Column field="ingestionInfo.metadata.KsDocSource" header="KsDocSource" sortable style="min-width: 12rem">
<template #body="{ data }">
{{ data.ingestionInfo.metadata.KsDocSource }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()" placeholder="Search by File" />
</template>
</Column>
<Column field="ingestionStatus" header="Status" sortable>
<template #body="slotProps">
<Tag :value="slotProps.data.ingestionStatus" :severity="getStatus(slotProps.data)" />
</template>
<template #filter="{ filterModel, filterCallback }">
<Select v-model="filterModel.value" @change="filterCallback()" :options="statuses" placeholder="Select One"
style="min-width: 12rem" :showClear="true">
<template #option="{ option }">
<Tag :value="option" :severity="getStatus({ ingestionStatus: option })" />
</template>
</Select>
</template>
</Column>
<Column header="Ingestion Date" filterField="ingestionDateFormat" dataType="date" style="min-width: 10rem">
<template #body="{ data }">
{{ formatDate(data.ingestionDate) }}
</template>
<template #filter="{ filterModel }">
<DatePicker v-model="filterModel.value" dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy"
@change="updateFilterModel" />
</template>
</Column>
<Column headerStyle="width: 5rem; text-align: center" bodyStyle="text-align: center; overflow: visible">
<template #body="slotProps">
<div class="flex justify-center items-center space-x-3" >
<!--Button type="button" icon="pi pi-pencil" rounded @click="editKsDocument(slotProps.data)"
v-tooltip="'Edit the information of document'" /-->
<Button type="button" icon="pi pi-play" rounded @click="startIndividualngestion(slotProps.data.id)"
v-tooltip="'Start Ingestion of document'" :disabled="slotProps.data.ingestionStatus === 'INGESTED'"
:class="{ 'p-button-danger': slotProps.data.ingestionStatus === 'INGESTED' }" />
<Button type="button" icon="pi pi-trash" rounded @click="showConfirmDialog(slotProps.data.id)"
v-tooltip="'Delete the ingested Record'" :disabled="slotProps.data.ingestionStatus === 'NEW'"
:class="{ 'p-button-danger': slotProps.data.ingestionStatus === 'NEW' }" />
<DataTable :value="ksdocuments" :paginator="true" :rows="10" dataKey="id" :rowHover="true" showGridlines>
<template #header>
<div class="flex flex-wrap items-center justify-between gap-2">
<span class="text-xl font-bold">KS Documents</span>
<Button icon="pi pi-plus" rounded raised @click="newKsDocument()" />
</div>
</template>
<Column field="name" header="Name"></Column>
<Column field="fileName" header="File Name"></Column>
<Column field="ingestionStatus" header="Status">
<template #body="slotProps">
<Tag :value="slotProps.data.ingestionStatus" :severity="getStatus(slotProps.data)" />
</template>
</Column>
<Column field="ingestionDate" header="Ingestion Date"></Column>
<Column headerStyle="width: 5rem; text-align: center" bodyStyle="text-align: center; overflow: visible">
<template #body="slotProps">
<Button type="button" icon="pi pi-pencil" rounded @click="editKsDocument(slotProps.data)" />
<Tag :value="slotProps.data.id" />
<Tag :value="slotProps.data.ingestionInfo.id" />
<Button type="button" v-if="slotProps.data.ingestionStatus === 'NEW'" icon="pi pi-play" rounded
@click="startIngestion(slotProps.data.id)" />
</template>
</Column>
</DataTable>
<Dialog header="Ingestion Result" v-model:visible="ingestionDialogVisible" :modal="true" :closable="false">
<p>{{ ingestionResult }}</p>
<Button label="OK" icon="pi pi-check" @click="ingestionDialogVisible = false" />
</Dialog>
</div>
<Dialog header="Confirm Deletion" :visible="confirmDialogVisible" modal @hide="resetConfirmDialog"
:style="{ width: '300px' }">
<p>Are you sure you want to delete this record?</p>
<template #footer>
<Button label="No" icon="pi pi-times" @click="confirmDialogVisible = false"/>
<Button label="Yes" icon="pi pi-check" @click="confirmDelete" class="p-button-danger" />
</template>
</Dialog>
</div>
</template>
</Column>
</DataTable>
<Dialog header="Ingestion Result" v-model:visible="ingestionDialogVisible" :modal="true" :closable="false">
<p>{{ ingestionResult }}</p>
<Button label="OK" icon="pi pi-check" @click="ingestionDialogVisible = false" />
</Dialog>
</div>
</template>
<script setup>
import { FilterMatchMode, FilterOperator } from '@primevue/core/api';
import axios from 'axios';
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import moment from 'moment';
import Button from 'primevue/button';
import Column from 'primevue/column';
import DataTable from 'primevue/datatable';
import DatePicker from 'primevue/datepicker'
import Dialog from 'primevue/dialog';
import InputText from 'primevue/inputtext';
import Select from 'primevue/select';
import Tag from 'primevue/tag';
import Tooltip from 'primevue/tooltip';
const router = useRouter()
const ksdocuments = ref(null);
const loading = ref(true);
const ingestionDialogVisible = ref(false);
const ingestionResult = ref('');
const filters = ref();
const confirmDialogVisible = ref(false);
const recordToDelete = ref(null);
const initFilters = () => {
filters.value = {
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
id: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
'ingestionInfo.metadata.KsApplicationName': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
'ingestionInfo.metadata.KsFileSource': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
'ingestionInfo.metadata.KsDocSource': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
ingestionDateFormat: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] },
ingestionStatus: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }
};
};
initFilters();
const statuses = ref(['NEW', 'INGESTED', 'FAILED']); // Add your statuses here
onMounted(() => {
axios.get('http://localhost:8082/fe-api/ksdocuments')
.then(response => {
console.log(response.data);
ksdocuments.value = response.data;
});
axios.get('/fe-api/ksdocuments')
.then(response => {
ksdocuments.value = getCustomDatewithAllResponse(response.data);
console.log(ksdocuments.value);
loading.value = false;
});
});
// Computed property to check if all documents are ingested
const allDocumentsIngested = computed(() => {
return ksdocuments.value && ksdocuments.value.every(doc => doc.ingestionStatus === 'INGESTED');
});
const getStatus = (data) => {
if (data.ingestionStatus === 'INGESTED') {
return 'success';
} else if (data.ingestionStatus === 'NEW') {
return 'danger';
} else {
return 'warn';
}
if (data.ingestionStatus === 'INGESTED') {
return 'success';
} else if (data.ingestionStatus === 'NEW') {
return 'danger';
} else {
return 'warn';
}
}
const getCustomDatewithAllResponse = (data) => {
return [...(data || [])].map((d) => {
d.ingestionDateFormat = new Date(d.ingestionDateFormat);
return d;
});
};
const updateFilterModel = () => {
console.log("updateFilterModel")
}
const editKsDocument = (data) => {
console.log(data);
router.push({ name: 'ks-document-edit', params: { id: data.id } });
console.log(data);
router.push({ name: 'ks-document-edit', params: { id: data.id } });
}
const startIngestion = (id) => {
axios.get(`http://localhost:8082/test/ingest_document/${id}`)
//axios.get('http://localhost:8082/test/ingestion_loop')
.then(response => {
ingestionResult.value = response.data;
if (response.data.status == "OK") {
ksdocuments.value.forEach(element => {
if (response.data.ingestedDocumentId.includes(element.id)) {
element.status = "INGESTED"
}
});
} else {
ingestionResult.value = `Error: ${response.data.message}`;
}
//delete functionality
function showConfirmDialog(id) {
recordToDelete.value = id;
confirmDialogVisible.value = true;
}
ingestionDialogVisible.value = true;
})
.catch(error => {
ingestionDialogVisible.value = true;
});
function confirmDelete() {
if (recordToDelete.value !== null) {
deleteRecordsFromVectorStore(recordToDelete.value);
recordToDelete.value = null;
}
confirmDialogVisible.value = false;
}
function resetConfirmDialog() {
recordToDelete.value = null;
}
const deleteRecordsFromVectorStore = (id) => {
const documentToDelete = ksdocuments.value.find(doc => doc.id === id);
if (!documentToDelete) {
console.error('Document not found');
return;
}
const requestPayload = {
ksDocumentId: id,
ksIngestionInfoId: documentToDelete.ingestionInfo.id,
ksDoctype: documentToDelete.ingestionInfo.metadata.KsDoctype,
ksDocSource: documentToDelete.ingestionInfo.metadata.KsDocSource,
ksFileSource: documentToDelete.ingestionInfo.metadata.KsFileSource,
ksApplicationName: documentToDelete.ingestionInfo.metadata.KsApplicationName,
};
axios.post('/fe-api/vector-store/deleteRecords', requestPayload)
.then(response => {
console.log('Delete resource:', response.data)
ksdocuments.value = ksdocuments.value.filter(doc => doc.id !== id);
})
.catch(error => {
console.error('Error deleting records: ', error)
});
}
//ingestion
const startIndividualngestion = (id) => {
axios.get(`/test/ingest_document/${id}`)
//axios.get('/test/ingestion_loop')
.then(response => {
ingestionResult.value = response.data;
if (response.data.status == "OK") {
ksdocuments.value.forEach(element => {
if (response.data.ingestedDocumentId.includes(element.id)) {
element.status = "INGESTED"
}
});
} else {
ingestionResult.value = `Error: ${response.data.message}`;
}
ingestionDialogVisible.value = true;
})
.catch(error => {
ingestionDialogVisible.value = true;
});
};
const startlngestion = () => {
axios.get('/test/ingestion_loop')
.then(response => {
ingestionResult.value = response.data;
if (response.data.status == "OK") {
ksdocuments.value.forEach(element => {
if (response.data.ingestedDocumentId.includes(element.id)) {
element.status = "INGESTED"
}
});
} else {
ingestionResult.value = `Error: ${response.data.message}`;
}
ingestionDialogVisible.value = true;
})
.catch(error => {
ingestionDialogVisible.value = true;
});
};
//new record creation
const newKsDocument = () => {
console.log('new');
router.push({ name: 'ks-document-new' });
console.log('new');
router.push({ name: 'ks-document-new' });
}
// Function to format date string
function formatDate(dateString) {
// Parse the date string using moment
return moment(dateString).format('MM/DD/YYYY');
}
</script>
<style scoped>
/* Add this to ensure buttons are spaced consistently */
.space-x-3 > * + * {
margin-left: 1rem; /* Adjust as needed for desired spacing */
}
/* Custom styling for disabled red button */
.p-button-danger {
background-color: white;
border-color: blue;
color: black;
}
.p-button-danger:disabled {
/*background-color: red;*/
border-color: red;
color: red;
cursor: not-allowed;
}
</style>

View File

@@ -6,73 +6,84 @@
<h2 class="text-3xl font-bold mb-4">Ks document</h2>
</div>
<form @submit.prevent="submitForm" class="p-fluid">
<div class="lex flex-col md:flex-row gap-4">
<div class="flex flex-wrap gap-2 w-full">
<label for="description">Description</label>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="description" v-tooltip="'A brief overview of the system purpose and functionality.'">System
Description</label>
<InputText id="description" type="text" v-model="formData.description" required class="w-full" />
</div>
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="type">Type</label>
<label for="type"
v-tooltip="'Specify the type of file here. e.g, PDF Document, DOCX, TXT, MD Document etc..'">File
Type</label>
<InputText id="type" v-model="formData.type" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksApplicationName">KS Application Name</label>
<label for="ksApplicationName" v-tooltip="'Enter the application name here.'">KS Application Name</label>
<InputText id="ksApplicationName" v-model="formData.ksApplicationName" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksDocType">KS Doc Type</label>
<InputText id="ksDocType" v-model="formData.ksDocType" required class="w-full" />
<label for="ksDocType" v-tooltip="'Specify the type of document e.g, md, pdf,'">KS Document Type</label>
<Select id="ksDocType" v-model="formData.ksDocType" :options="dropdownItems" required optionLabel="name" optionValue="value" placeholder="Select One" class="w-full"></Select>
<!--InputText id="ksDocType" v-model="formData.ksDocType" required class="w-full" /-->
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksDocSource">KS Doc Source</label>
<label for="ksDocSource"
v-tooltip="'The KS Document Source field is intended to capture the origin or source from where the document was obtained or associated. ex.. Retrieved from DevopsJ2Cloud Git Repository - CSV System Configuration '">KS
Document Source</label>
<InputText id="ksDocSource" v-model="formData.ksDocSource" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="defaultChunkSize">Default Chunk Size</label>
<label for="defaultChunkSize" v-tooltip="'Define the default size for chunks of data.'">Default Chunk
Size</label>
<InputNumber id="defaultChunkSize" v-model="formData.defaultChunkSize" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="minChunkSize">Min Chunk Size</label>
<label for="minChunkSize" v-tooltip="'Specify the minimum allowable size for chunks'">Min Chunk
Size</label>
<InputNumber id="minChunkSize" v-model="formData.minChunkSize" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="maxNumberOfChunks">Max Number of Chunks</label>
<label for="maxNumberOfChunks" v-tooltip="'Set the maximum number of chunks allowed.'">Max Number of
Chunks</label>
<InputNumber id="maxNumberOfChunks" v-model="formData.maxNumberOfChunks" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="minChunkSizeToEmbed">Min Chunk Size to Embed</label>
<label for="minChunkSizeToEmbed" v-tooltip="'Define the minimum chunk size that can be embedded.'">Min
Chunk Size to
Embed</label>
<InputNumber id="minChunkSizeToEmbed" v-model="formData.minChunkSizeToEmbed" required class="w-full" />
</span>
</div>
<div class="col-12 mb-4">
<label for="file" class="block text-lg mb-2">File</label>
<label for="file" class="block text-lg mb-2" v-tooltip="'Upload the file here.'">File</label>
<div class="flex align-items-center">
<FileUpload ref="fileUpload" mode="basic" :maxFileSize="10000000" chooseLabel="Select File"
<FileUpload ref="fileUpload" mode="basic" :maxFileSize="10000000000" chooseLabel="Select File"
class="p-button-rounded" @select="onFileSelect" />
</div>
</div>
@@ -81,81 +92,6 @@
</div>
</div>
</Fluid>
<!--div class="card-container">
<form @submit.prevent="submitForm" class="p-fluid">
<div class="grid">
<div class="col-12 mb-4">
<label for="file" class="block text-lg mb-2">File</label>
<div class="flex align-items-center">
<FileUpload
ref="fileUpload"
mode="basic"
:maxFileSize="10000000"
chooseLabel="Select File"
class="p-button-rounded"
@select="onFileSelect"
/>
</div>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="description">Description</label>
<InputText id="description" v-model="formData.description" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="type">Type</label>
<InputText id="type" v-model="formData.type" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksApplicationName">KS Application Name</label>
<InputText id="ksApplicationName" v-model="formData.ksApplicationName" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksDocType">KS Doc Type</label>
<InputText id="ksDocType" v-model="formData.ksDocType" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="ksDocSource">KS Doc Source</label>
<InputText id="ksDocSource" v-model="formData.ksDocSource" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="defaultChunkSize">Default Chunk Size</label>
<InputNumber id="defaultChunkSize" v-model="formData.defaultChunkSize" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="minChunkSize">Min Chunk Size</label>
<InputNumber id="minChunkSize" v-model="formData.minChunkSize" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="maxNumberOfChunks">Max Number of Chunks</label>
<InputNumber id="maxNumberOfChunks" v-model="formData.maxNumberOfChunks" required class="w-full" />
</span>
</div>
<div class="col-12 md:col-6 mb-4">
<span class="p-float-label">
<label for="minChunkSizeToEmbed">Min Chunk Size to Embed</label>
<InputNumber id="minChunkSizeToEmbed" v-model="formData.minChunkSizeToEmbed" required class="w-full" />
</span>
</div>
</div>
<Button type="submit" label="Submit" class="p-button-rounded p-button-lg" />
</form>
</div-->
</template>
<script setup>
@@ -163,17 +99,25 @@ import axios from 'axios';
import { useToast } from 'primevue/usetoast';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import Tooltip from 'primevue/tooltip';
const toast = useToast();
const router = useRouter();
const dropdownItems = ref([
{ name: 'PDF', value: 'pdf' },
{ name: 'MD', value: 'md' },
{ name: 'DOCX', value: 'docx' },
{ name: 'EXCEL', value: 'excel' }
]);
const formData = ref({
description: 'Test-UI-DevopsJ2CSystem',
description: '', //Jenkins DevopsJ2Cloud System CSV configuration md file
ingestionStatus: 'NEW',
type: 'MD_DOCUMENT',
ksApplicationName: 'jenkins',
ksDocType: 'setup-documentation',
ksDocSource: 'guide-for-techincal-setup',
type: '', //.md file
ksApplicationName: '', //Jenkins-DevopsJ2Cloud
ksDocType: '',
ksDocSource: '', //Git Repository - DevopsJ2Cloud CSV System Configuration
defaultChunkSize: 1000,
minChunkSize: 200,
maxNumberOfChunks: 1000,
@@ -206,7 +150,7 @@ const submitForm = async () => {
formDataToSend.append('minChunkSizeToEmbed', formData.value.minChunkSizeToEmbed);
try {
const response = await axios.post('http://localhost:8082/upload', formDataToSend, {
const response = await axios.post('/upload', formDataToSend, {
headers: {
'Content-Type': 'multipart/form-data'
}

View File

@@ -1,34 +1,24 @@
<template>
<Fluid>
<div class="flex mt-6">
<div class="card flex flex-col gap-4 w-full">
<div>
<h2 class="text-3xl font-bold mb-4">Similarity Search</h2>
</div>
<div class="flex flex-wrap">
<!--label for="address">Address</label-->
<Textarea id="query" v-model="query" rows="4" placeholder="Enter your query..." class="w-full" />
</div>
<div class="flex flex-col md:flex-row gap-4">
<div class="flex flex-wrap gap-2 w-full">
<Select id="type" v-model="dropdownItem" :options="dropdownItems" optionLabel="name"
placeholder="Select type" class="w-full"></Select>
<h2 class="text-4xl font-semibold text-center mb-4">Similarity Search</h2>
<div class="similarity-search">
<div class="card-container flex flex-col gap-6">
<div class="flex flex-col gap-4">
<Textarea id="query" v-model="query" rows="6" placeholder="Enter your query..." class="input-textarea" />
<div class="select-container">
<!--SelectButton id="type" v-model="dropdownItem" :options="dropdownItems" optionLabel="name"
class="select-button" /-->
<InputText v-model="filterQuery" type="text" placeholder="Add filterQuery" />
</div>
</div>
<div class="p-field p-col-12 p-md-2">
<Button label="Send" icon="pi pi-send" :fluid="false" @click="sendQuery" />
</div>
<Button label="Query" icon="pi pi-send" @click="sendQuery" class="send-button" />
</div>
</div>
<div class="flex mt-6">
<div class="results-container p-mt-4">
<Card v-for="(result, index) in messages" :key="index" class="p-mb-3">
<div v-if="messages.length > 0" class="results-container mt-6">
<Card v-for="(result, index) in messages" :key="index" class="result-card">
<template #content>
<ScrollPanel style="width: 100%; max-height: 200px">
<pre class="result-content">{{ result }}</pre>
<ScrollPanel style="width: 100%; max-height: 400px">
<CodeSnippet :code="dynamicCode" language="systemd" />
</ScrollPanel>
</template>
</Card>
@@ -41,13 +31,18 @@
import Button from 'primevue/button';
import Card from 'primevue/card';
import ScrollPanel from 'primevue/scrollpanel';
import SelectButton from 'primevue/selectbutton'; // Import SelectButton
import { useToast } from 'primevue/usetoast';
import { ref } from 'vue';
import { watch, ref } from 'vue';
import CodeSnippet from '@/components/CodeSnippet.vue';
import axios from 'axios';
const query = ref('');
const dropdownItem = ref(null);
const messages = ref([]);
const toast = useToast();
const dynamicCode = ref('');
const filterQuery = ref("'KsApplicationName' == 'ATF'")
const dropdownItems = [
{ name: 'Documentation', code: 'setup-documentation' },
@@ -55,12 +50,16 @@ const dropdownItems = [
{ name: 'Source code', code: 'sourcecode' }
];
const sendQuery = async () => {
if (query.value.trim() !== '' && dropdownItem.value) {
try {
const response = await fetch(`http://localhost:8082/test/query_vector?query="${query.value}"&type=${dropdownItem.value.code}`);
const data = await response.json();
const sendQuery = () => {
if (query.value.trim() !== '' && filterQuery) {
axios.get('/test/query_vector', {
params: {
query: query.value,
filterQuery: filterQuery.value,
}
})
.then(response => {
const data = response.data;
console.log('API response:', data);
if (data && Array.isArray(data) && data.length > 0) {
@@ -69,52 +68,83 @@ const sendQuery = async () => {
} else {
toast.add({ severity: 'info', summary: 'Info', detail: 'No results found', life: 3000 });
}
} catch (error) {
})
.catch(error => {
console.error('Error sending query:', error);
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to send query', life: 3000 });
}
query.value = '';
dropdownItem.value = null;
});
} else {
toast.add({ severity: 'warn', summary: 'Warning', detail: 'Please enter a query and select a type', life: 3000 });
}
};
// Function to generate dynamic code snippet
function generateDynamicCode() {
const randomValue = messages.value.join(', ');
return `[${randomValue}]`;
}
watch(messages, (newMessages) => {
dynamicCode.value = generateDynamicCode();
});
</script>
<style scoped>
.similarity-search {
max-width: 1200px;
max-width: 1000px;
margin: 0 auto;
padding: 2rem;
}
.card-container {
padding: 2rem;
border-radius: 8px;
background-color: #ffffff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.input-textarea {
width: 100%;
resize: vertical;
min-height: 150px;
/* Increased height for better readability */
border-radius: 8px;
padding: 1rem;
border: 1px solid #ccc;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
.select-container {
display: flex;
justify-content: center;
}
.select-button {
width: 100%;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.send-button {
width: 100%;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.results-container {
max-height: 600px;
overflow-y: auto;
padding: 2rem;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.result-content {
white-space: pre-wrap;
word-wrap: break-word;
font-family: monospace;
font-size: 0.9em;
padding: 1rem;
background-color: #f8f9fa;
border-radius: 4px;
.result-card {
margin-bottom: 1rem;
border-radius: 8px;
}
.p-select {
width: 100%;
}
.p-select .p-select-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.p-inputtextarea {
resize: vertical;
min-height: 100px;
.p-scrollpanel {
border-radius: 8px;
background-color: #ffffff;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<div className="card">
<DataTable v-model:filters="filters" :value="vectorDetails" dataKey="id" :loading="loading" paginator showGridlines
:rows="10" filterDisplay="menu"
:globalFilterFields="['id', 'metadata.ksApplicationName', 'metadata.ksDocSource', 'metadata.ksDoctype', 'metadata.ksFileSource']">
<template #header>
<div class="flex items-center justify-between gap-4 p-4 ">
<span class="text-xl font-bold">Vector Data</span>
<div class="flex items-center gap-2 flex-grow">
<IconField class="flex-grow">
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="Keyword Search" />
</IconField>
</div>
</div>
</template>
<template #empty>No Records found</template>
<template #loading>Loading Data. Please wait.....</template>
<Column field="id" header="Id" sortable>
<template #body="{ data }">
{{ data.id }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()" placeholder="Search By id" />
</template>
</Column>
<Column field="metadata.ksApplicationName" header="KsApplicationName" sortable>
<template #body="{ data }">
{{ data.metadata.ksApplicationName }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search By Application name" />
</template>
</Column>
<Column field="metadata.ksFileSource" header="KsFileSource" sortable>
<template #body="{ data }">
{{ data.metadata.ksFileSource }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search By Source/Path" />
</template>
</Column>
<Column field="metadata.ksDocSource" header="KsDocSource" sortable>
<template #body="{ data }">
{{ data.metadata.ksDocSource }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search By Document/Repo source" />
</template>
</Column>
<Column field="metadata.ksDoctype" header="KsDoctype" sortable>
<template #body="{ data }">
{{ data.metadata.ksDoctype }}
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search By Document/Source type" />
</template>
</Column>
</DataTable>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { FilterMatchMode, FilterOperator } from '@primevue/core/api'
import axios from 'axios';
const vectorDetails = ref(null);
const loading = ref(true);
const filters = ref();
onMounted(() => {
axios.get('/fe-api/vector-store/details')
.then(response => {
vectorDetails.value = response.data;
console.log(vectorDetails.value)
loading.value = false;
});
});
const initFilters = () => {
filters.value = {
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
id: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
'metadata.ksApplicationName': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
'metadata.ksDocSource': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
'metadata.ksDoctype': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
'metadata.ksFileSource': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }
};
};
initFilters();
</script>

View File

@@ -1,59 +1,81 @@
<script setup>
import { useLayout } from '@/layout/composables/layout';
import { computed, ref } from 'vue';
import {useAuth } from '@websanova/vue-auth/src/v3.js';
const auth = useAuth();
const { isDarkTheme } = useLayout();
const email = ref('');
const username = ref('');
const password = ref('');
const checked = ref(false);
const logoUrl = computed(() => {
return `/layout/images/${isDarkTheme ? 'logo-white' : 'logo-dark'}.svg`;
return `/layout/images/${isDarkTheme ? 'logo-white' : 'logo-dark'}.svg`;
});
const login = () => {
console.log('Username: ', username.value);
auth.login({
data: {
"username": username.value,
"password": password.value
},
redirect: '/ksdocuments',
fetchUser: true,
url: '/api/auth/login'
});
}
</script>
<template>
<div class="bg-surface-50 dark:bg-surface-950 flex items-center justify-center min-h-screen min-w-[100vw] overflow-hidden">
<div class="flex flex-col items-center justify-center">
<img :src="logoUrl" alt="Sakai logo" class="mb-8 w-24 shrink-0" />
<div style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
<div class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20" style="border-radius: 53px">
<div class="text-center mb-8">
<img src="/demo/images/login/avatar.png" alt="Image" height="50" class="mb-4" />
<div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">Welcome, Isabel!</div>
<span class="text-surface-600 dark:text-surface-200 font-medium">Sign in to continue</span>
</div>
<div
class="bg-surface-50 dark:bg-surface-950 flex items-center justify-center min-h-screen min-w-[100vw] overflow-hidden">
<div class="flex flex-col items-center justify-center">
<div>
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Email</label>
<InputText id="email1" type="text" placeholder="Email address" class="w-full md:w-[30rem] mb-8" style="padding: 1rem" v-model="email" />
<div
style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
<div class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20" style="border-radius: 53px">
<div class="text-center mb-8">
<span class="text-surface-600 dark:text-surface-200 font-medium">Welcome to Apollo- The Knowledge
Source</span>
</div>
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="w-full mb-4" inputClass="w-full" :inputStyle="{ padding: '1rem' }"></Password>
<div>
<label for="email1"
class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Username</label>
<InputText id="email1" type="text" placeholder="Username" class="w-full md:w-[30rem] mb-8"
style="padding: 1rem" v-model="username" />
<div class="flex items-center justify-between mb-8 gap-8">
<div class="flex items-center">
<Checkbox v-model="checked" id="rememberme1" binary class="mr-2"></Checkbox>
<label for="rememberme1">Remember me</label>
</div>
<a class="font-medium no-underline ml-2 text-right cursor-pointer" style="color: var(--primary-color)">Forgot password?</a>
</div>
<Button label="Sign In" class="w-full p-4 text-xl"></Button>
</div>
</div>
</div>
<label for="password1"
class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="w-full mb-4"
inputClass="w-full" :inputStyle="{ padding: '1rem' }"></Password>
<!--div class="flex items-center justify-between mb-8 gap-8">
<div class="flex items-center">
<Checkbox v-model="checked" id="rememberme1" binary class="mr-2"></Checkbox>
<label for="rememberme1">Remember me</label>
</div>
<a class="font-medium no-underline ml-2 text-right cursor-pointer"
style="color: var(--primary-color)">Forgot password?</a>
</div> -->
<Button label="Sign In" @click="login" class="w-full p-4 text-xl"></Button>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.pi-eye {
transform: scale(1.6);
margin-right: 1rem;
transform: scale(1.6);
margin-right: 1rem;
}
.pi-eye-slash {
transform: scale(1.6);
margin-right: 1rem;
transform: scale(1.6);
margin-right: 1rem;
}
</style>