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

Test #79

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Test #79

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
10 changes: 6 additions & 4 deletions api/controllers/embed.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,21 @@ export class EmbeddingController {
}
const file = req.file;
const { buffer } = file;
const embeddingHandler: CreateDocumentEmbeddingHandler = new CreateDocumentEmbeddingHandler(buffer);
try {
const { title, documentType, domain } = documentRequestSchema.parse(req.body);
const metaData = JSON.parse(req.body.other);
const embeddingHandler: CreateDocumentEmbeddingHandler = new CreateDocumentEmbeddingHandler(buffer);
const { title, documentTypeId, domainId } = documentRequestSchema.parse(metaData);
const result = await embeddingHandler.handle({
title,
documentType,
domain,
documentTypeId,
domainId,
});
if (!result?.isSuccess) {
return res.json(Result.fail("unable to create embeddings", 400));
}
return res.json(result);
} catch (error) {
next(error);
generateErrorResponse(error, res, next);
}
}
Expand Down
4 changes: 2 additions & 2 deletions api/handlers/create-document-embed.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export class CreateDocumentEmbeddingHandler implements IRequestHandler<ICreateEm
async handle(request: ICreateEmbeddingRequestDTO): Promise<Result<boolean>> {
const embeddingService: EmbeddingService = new EmbeddingService(this.apiKey, this.pdf);
try {
const { title, documentType, domain } = request;
const result = await embeddingService.createDocumentsEmbeddings(title, documentType, domain);
const { title, documentTypeId, domainId } = request;
const result = await embeddingService.createDocumentsEmbeddings(title, documentTypeId, domainId);
if (!result) {
throw new HttpException(HTTP_RESPONSE_CODE.BAD_REQUEST, "An error occured, could not create embeddings");
}
Expand Down
6 changes: 1 addition & 5 deletions api/interfaces/embedding-service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ export interface IEmbeddingService {
}>;
cosineSimilarity(vecA: number[], vecB: number[]): number;
euclideanDistance(vecA: number[], vecB: number[]): number;
createDocumentsEmbeddings(
title: string,
documentType: DocumentTypeEnum,
domain: DomainEnum
): Promise<Result<boolean>>;
createDocumentsEmbeddings(title: string, documentType: number, domain: number): Promise<Result<boolean>>;
getQueryMatches(
query: string,
matchCount: number,
Expand Down
6 changes: 3 additions & 3 deletions api/lib/validation-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { z } from "zod";
import { DocumentTypeEnum, DomainEnum } from "./constants";

const title = z.string();
const documentType = z.nativeEnum(DocumentTypeEnum);
const domain = z.nativeEnum(DomainEnum);
export const documentRequestSchema = z.object({ title, documentType, domain });
const documentTypeId = z.number();
const domainId = z.number();
export const documentRequestSchema = z.object({ title, documentTypeId, domainId });

const name = z.nativeEnum(DomainEnum);
export const domainRequestSchema = z.object({ name });
Expand Down
14 changes: 9 additions & 5 deletions api/repositories/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import { DefaultArgs } from "@prisma/client/runtime/library";

export class Database {
private static instance: Database;
protected prisma: PrismaClient<
Prisma.PrismaClientOptions,
{ log: "info" },
DefaultArgs
>;
protected prisma: PrismaClient<Prisma.PrismaClientOptions, { log: "info" }, DefaultArgs>;
constructor() {
this.prisma = new PrismaClient();
this.createIvfflatIndex();
Expand Down Expand Up @@ -45,4 +41,12 @@ export class Database {
}
return Database.instance;
}

static getPrisma(): PrismaClient<Prisma.PrismaClientOptions, { log: "info" }, DefaultArgs> {
try {
return this.getInstance().prisma;
} catch (error) {
throw Error(error);
}
}
}
1 change: 0 additions & 1 deletion api/repositories/document-type.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { DocumentTypeEnum, HTTP_RESPONSE_CODE } from "../lib/constants";
import { HttpException } from "../exceptions/exception";

export class DocumentTypeRepository extends Database {
instance: DocumentTypeRepository;
constructor() {
super();
}
Expand Down
4 changes: 2 additions & 2 deletions api/repositories/dtos/dtos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export interface ICreateEmbeddingDTO {

export interface ICreateEmbeddingRequestDTO {
title: string;
documentType: DocumentTypeEnum;
domain: DomainEnum;
documentTypeId: number;
domainId: number;
}

export interface ICreateDomainRequestDTO {
Expand Down
1 change: 0 additions & 1 deletion api/repositories/embedding.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ export class EmbeddingRepository extends Database {
matchThreshold: number,
documentId: number
): Promise<IQueryMatch[]> {
console.log({ documentId });
//change text to document_embedding
//check how to select textembedding from DB
const matches = await this.prisma.$queryRaw`
Expand Down
18 changes: 5 additions & 13 deletions api/services/embed.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DocumentTypeService } from "./document-type.service";
import { DocumentService } from "./document.service";
import { DomainService } from "./domain.service";
import { oneLine } from "common-tags";
import { Database } from "../repositories/database";

/**The `role` parameter in the `ContentPart` object is used to specify the role of the text content in relation to the task being performed.
* the following roles are commonly used:
Expand Down Expand Up @@ -154,20 +155,11 @@ export class EmbeddingService extends GenerativeAIService implements IEmbeddingS
* @returns {Promise<boolean>} - A promise that resolves to true if the document and embeddings are created successfully, false otherwise.
* @throws {Error} - If the document type or domain doesn't exist, or if unable to create document embeddings.
*/
async createDocumentsEmbeddings(
title: string,
documentType: DocumentTypeEnum,
domain: DomainEnum
): Promise<Result<boolean>> {
//Check why there are errors in this trnascation
//put this in a prisma transaction
async createDocumentsEmbeddings(title: string, documentTypeId: number, domainId: number): Promise<Result<boolean>> {
try {
const documentRepository: DocumentRepository = new DocumentRepository();
const domainService: DomainService = new DomainService();
const documentTypeService: DocumentTypeService = new DocumentTypeService();
const docType: IDocumentTypeModel | undefined = await documentTypeService.getDocumentType(documentType);
const documentTypeId: number = docType.id;

const docDomain: IDomainModel | undefined = await domainService.getDomain(domain);
const domainId: number = docDomain.id;
const document: IDocumentModel = await documentRepository.create(title);
let documentId: number;

Expand All @@ -190,6 +182,7 @@ export class EmbeddingService extends GenerativeAIService implements IEmbeddingS
}
} catch (error) {
console.error(error);
return Result.fail("Failed to create document embeddings", 500);
}
}

Expand Down Expand Up @@ -232,7 +225,6 @@ export class EmbeddingService extends GenerativeAIService implements IEmbeddingS
const result: GenerateContentResult = await aiModel.generateContent(prompt);
const response: EnhancedGenerateContentResponse = result.response;
const text: string = response.text();
console.log(text);
return text;
}

Expand Down
9 changes: 3 additions & 6 deletions presentation/src/components/ChatForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { Button, Card, Container, Form, ListGroup, Row, Stack } from "react-boot
import useAxiosPrivate from "../hooks/useAxiosPrivate";
import { IDataItem } from "../interfaces/document.interface";
import { FileUploader } from "./DragAndDrop";
import Books from "./DropDown";
import { DropDown } from "./DropDown";
import NavBar from "./NavBar";
import Example from "./Modal";

interface IHistory {
role: string;
Expand All @@ -24,6 +23,7 @@ export function Thread() {

const handleBookSelect = (bookData: IDataItem) => {
setSelectedBook(bookData);
console.log(selectedBook);
};

const formAction = async () => {
Expand Down Expand Up @@ -86,7 +86,7 @@ export function Thread() {
<div className="p-2"></div>
<div className="p-2 ms-auto">
<div>
<Books onDataItemSelect={handleBookSelect} model="document" />
<DropDown onDataItemSelect={handleBookSelect} model="document" />
</div>
</div>
<div className="p-2">
Expand Down Expand Up @@ -180,9 +180,6 @@ export function Thread() {
</div>
</div>
<div style={{ width: "18rem" }} className="col-lg-3 col-md-4 col-sm-6">
<div>
<Example />
</div>
<div style={{ marginTop: "10px" }}>
<Card style={{ backgroundColor: "#000", borderColor: "#fff", color: "#fff" }}>
<Card.Body>
Expand Down
70 changes: 31 additions & 39 deletions presentation/src/components/DragAndDrop.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,33 @@
import React from "react";
import { useDropzone, DropzoneOptions } from "react-dropzone";
import useAxiosPrivate from "../hooks/useAxiosPrivate";
import React, { useState } from "react";
import { DropzoneOptions, useDropzone } from "react-dropzone";
import { AppModal } from "./Modal";

export const FileUploader: React.FC = () => {
const axiosPrivate = useAxiosPrivate();

const handleFileUpload = async (file: File) => {
try {
const formData = createFormData(file);
const response = await uploadFileToServer(formData);
console.log("File uploaded successfully:", response.data.message);
} catch (error) {
console.error("Error uploading file:", error);
}
const [showModal, setShowModal] = useState<boolean>(false);

const [pdf, setPdf] = useState<File>();

const handleShowModal = () => {
setShowModal(true);
};

const onHideModal = () => {
setShowModal(false);
};

const onDrop = (acceptedFiles: File[]) => {
const onDrop = async (acceptedFiles: File[]) => {
if (acceptedFiles.length > 0) {
const file = acceptedFiles[0];
if (file.type !== "application/pdf") {
throw new Error("Only PDF files are allowed");
if (file) {
if (file.type !== "application/pdf") {
throw new Error("Only PDF files are allowed");
}
setPdf(file);
handleShowModal();
}
console.log("File uploaded:", file);
handleFileUpload(file);
}
};

const createFormData = (file: File) => {
const formData = new FormData();
formData.append("pdf", file);
return formData;
};

const uploadFileToServer = async (formData: FormData) => {
const url = "/embed/documents";
const headers = { "Content-Type": "multipart/form-data" };
const response = await axiosPrivate.post(url, formData, { headers });
return response;
};

const options: DropzoneOptions = {
onDrop,
maxSize: 5 * 1024 * 1024,
Expand All @@ -48,13 +37,16 @@ export const FileUploader: React.FC = () => {
const { getRootProps, getInputProps, isDragActive } = useDropzone(options);

return (
<div {...getRootProps({ className: `dropzone ${isDragActive ? "active" : ""}` })}>
<input {...getInputProps()} />
{isDragActive ? (
<p>Drop your PDF document here...</p>
) : (
<p>Drag and drop a PDF doc here, or click to select a PDF doc</p>
)}
</div>
<>
<AppModal show={showModal} onHide={onHideModal} pdfData={pdf} />
<div {...getRootProps({ className: `dropzone ${isDragActive ? "active" : ""}` })}>
<input {...getInputProps()} />
{isDragActive ? (
<p>Drop your PDF document here...</p>
) : (
<p>Drag and drop a PDF doc here, or click to select a PDF doc</p>
)}
</div>
</>
);
};
35 changes: 23 additions & 12 deletions presentation/src/components/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import { useEffect, useState } from "react";
import Dropdown from "react-bootstrap/Dropdown";
import useAxiosPrivate from "../hooks/useAxiosPrivate";
import { IDataItem } from "../interfaces/document.interface";
import { getLocalStorageData, setLocalStorageData } from "../utils";
import { capitalizeFirstLetter, getLocalStorageData, setLocalStorageData } from "../utils";
import { MODEL_URLS, MODELS } from "../constants";

interface IBookProps {
onDataItemSelect: (data: IDataItem) => void;
model: string;
}

function Books({ onDataItemSelect, model }: Readonly<IBookProps>) {
export const DropDown = ({ onDataItemSelect, model }: Readonly<IBookProps>) => {
const axiosPrivate = useAxiosPrivate();
const [selectedDataItems, setSelectedDataItems] = useState<string>("Select Book");
const prompt = model === MODELS.document ? "Select Book" : "Select";
const [selectedDataItems, setSelectedDataItems] = useState<string>(prompt);
const [dataItems, setDataItems] = useState([]);

useEffect(() => {
Expand All @@ -33,6 +34,7 @@ function Books({ onDataItemSelect, model }: Readonly<IBookProps>) {
fetchData();
}, []);

// Todo To truly decouple this drop down, pass in the modelUrls as props
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getDataItems = async (model: string): Promise<any> => {
let url = "";
Expand All @@ -55,7 +57,12 @@ function Books({ onDataItemSelect, model }: Readonly<IBookProps>) {
const handleSelect = async (eventKey: string | null) => {
if (eventKey) {
setSelectedDataItems(eventKey);
const selectedItemData = dataItems.find((item: { title: string }) => item.title === eventKey);
let selectedItemData;
if (model === MODELS.document) {
selectedItemData = dataItems.find((item: { title: string }) => item.title === eventKey);
} else {
selectedItemData = dataItems.find((item: { name: string }) => item.name === eventKey);
}
if (selectedItemData) {
onDataItemSelect(selectedItemData);
}
Expand All @@ -68,17 +75,21 @@ function Books({ onDataItemSelect, model }: Readonly<IBookProps>) {
{selectedDataItems}
</Dropdown.Toggle>
<Dropdown.Menu>
{dataItems?.map((document: { title: string; id: number }) => (
<Dropdown.Item eventKey={document.title} key={document.id}>
{document.title}
</Dropdown.Item>
))}
{dataItems?.map((document: { title: string; name: string; id: number }) =>
model === MODELS.document ? (
<Dropdown.Item eventKey={document.title} key={document.id}>
{capitalizeFirstLetter(document.title.toLocaleUpperCase())}
</Dropdown.Item>
) : (
<Dropdown.Item eventKey={document.name} key={document.id}>
{capitalizeFirstLetter(document.name)}
</Dropdown.Item>
)
)}
</Dropdown.Menu>
</Dropdown>
);
} catch (error) {
console.error(error);
}
}

export default Books;
};
Loading
Loading