Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Add Citations To Chat Response #42

Merged
merged 1 commit into from
Apr 16, 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
5 changes: 1 addition & 4 deletions packages/ocular-ui/components/chat/chat-helpers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const handleChat = async (
setChatMessages
)


return await processResponse(
response,
// true,
Expand Down Expand Up @@ -73,10 +74,6 @@ export const fetchChatResponse = async (
return response.data
}





// lastChatMessage: ChatMessage,
export const processResponse = async (
response: Response,
Expand Down
5 changes: 2 additions & 3 deletions packages/ocular/src/api/routes/member/chat/create-message.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { IsString, IsNotEmpty, IsBoolean } from "class-validator"
import { validator } from "@ocular/utils"
import { ChatService } from "../../../../services"
// import {ChatContext} from "@ocular/types";

export default async (req, res) => {
const { id } = req.params
const validated = await validator(PostMessageReq, req.body)
try {
const { message } = validated;
const chatService = req.scope.resolve("chatService") as ChatService
const assistantMessage = await chatService.chat(id, message, {})
return res.status(200).send({message : assistantMessage})
const chatResponse = await chatService.chat(id, message, {})
return res.status(200).send(chatResponse)
} catch (_error: unknown) {
console.error(_error)
return res.status(500).send("Error: Failed to execute Chat");
Expand Down
63 changes: 21 additions & 42 deletions packages/ocular/src/approaches/chat-retrieve-read-retrieve.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EntityManager } from "typeorm";
import { AutoflowContainer , ApproachDefinitions, SearchResultChunk, ISearchService, ILLMInterface, SearchResult, SearchContext, Message, IChatApproach } from "@ocular/types";
import { AutoflowContainer , ApproachDefinitions, SearchResultChunk, ISearchService, ILLMInterface, SearchResult, SearchContext, Message, IChatApproach, ChatContext, ChatResponse } from "@ocular/types";
import { MessageBuilder } from "../utils/message";
import { IndexableDocChunk } from "@ocular/types";

Expand Down Expand Up @@ -67,52 +67,31 @@ export default class ChatReadRetrieveRead implements IChatApproach {
// object: 'chat.completion';
// }

async run(messages: Message[], context?: SearchContext): Promise<SearchResult> {
async run(messages: Message[], context?: ChatContext): Promise<ChatResponse> {
const { completionRequest, thoughts, hits } = await this.baseRun(messages, context);
const chatCompletion = await this.openaiService_.completeChat(completionRequest.messages);
return {
choices: [
{
index: 0,
message: {
content: chatCompletion,
role: 'assistant',
context: {
data_points: hits,
thoughts: thoughts,
},
},
},
],
hits:hits,
object: 'chat.completion',
// choices: [
// {
// index: 0,
// message: {
// content: chatCompletion,
// role: 'assistant',
// context: {
// data_points: hits,
// thoughts: thoughts,
// },
// },
// },
// ],
message: {
role: 'assistant',
content: chatCompletion,
},
data_points: hits,
};
}

// return {
// completionRequest: {
// messages: finalMessages,

// },
// hits: hits as IndexableDocChunk[],
// };

// choices: [
// {
// index: 0,
// message: {
// role: 'assistant' as const,
// content: chatCompletion,
// context: {
// data_points: hits,
// thoughts: `Question:<br>${userQuery}<br><br>Prompt:<br>${messageToDisplay.replace('\n', '<br>')}`,
// },
// },
// },
// ],
// hits:hits,
// object: 'chat.completion',

async *runWithStreaming(messages: Message[], context?: SearchContext): AsyncGenerator<SearchResultChunk, void> {
// const { completionRequest, dataPoints, thoughts } = await this.baseRun(messages, context);
// const openAiChat = await this.openai.calculateTokens();
Expand Down Expand Up @@ -168,7 +147,7 @@ export default class ChatReadRetrieveRead implements IChatApproach {
// -----------------------------------------------------------------------


let hits = await this.searchService_.search(null, userQuery, context);
let hits = await this.searchService_.search(null, queryText, context);

hits = hits.filter(doc => doc !== null);
console.log("Found Docs", hits)
Expand Down
36 changes: 19 additions & 17 deletions packages/ocular/src/services/chat.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { EntityManager} from "typeorm"
import { TransactionBaseService, ChatContext, ILLMInterface } from "@ocular/types"
import { TransactionBaseService, ChatContext, ILLMInterface, IChatApproach, ChatResponse } from "@ocular/types"
import {Chat, Message, MessageRoles, User} from "../models"
import { ChatRepository, MessageRepository } from "../repositories"
import { CreateChatInput } from "../types/chat"
import { FindConfig, Selector } from "../types/common"
import { buildQuery } from "../utils/build-query"
import { AutoflowAiError } from "@ocular/utils"


type InjectedDependencies = {
manager: EntityManager
chatRepository: typeof ChatRepository
// chatApproach: ChatApproach
chatRetrieveReadRetrieveApproache: IChatApproach
openAiService: ILLMInterface
messageRepository: typeof MessageRepository
loggedInUser: User
}

class ChatService extends TransactionBaseService {
protected readonly chatRepository_: typeof ChatRepository
// protected readonly chatApproach_: ChatApproach
protected readonly chatApproach_: IChatApproach
protected readonly openAiService_: ILLMInterface
protected readonly messageRepository_: typeof MessageRepository
protected readonly loggedInUser_: User
Expand All @@ -29,12 +28,11 @@ class ChatService extends TransactionBaseService {
super(arguments[0])
this.chatRepository_ = container.chatRepository
this.openAiService_ = container.openAiService
// this.chatApproach_ = container.chatApproach
this.chatApproach_ = container.chatRetrieveReadRetrieveApproache
this.messageRepository_ = container.messageRepository
this.loggedInUser_ = container.loggedInUser
}


async create(chat: CreateChatInput): Promise<Chat> {
return await this.atomicPhase_(
async (transactionManager: EntityManager) => {
Expand All @@ -49,17 +47,14 @@ class ChatService extends TransactionBaseService {
}

async retrieve(chatId: string , config: FindConfig<Chat> = {}): Promise<Chat> {
console.log("Retrieve ChatId",chatId)
if (!chatId) {
throw new AutoflowAiError(
AutoflowAiError.Types.NOT_FOUND,
`ChatId is not definded`
)
}
const chatRepo = this.activeManager_.withRepository(this.chatRepository_)

const query = buildQuery({ id: chatId, organisation_id:this.loggedInUser_.organisation_id, user_id: this.loggedInUser_.id }, config)

const chat = await chatRepo.findOne(query)

if (!chat) {
Expand All @@ -68,11 +63,10 @@ class ChatService extends TransactionBaseService {
`Chat with id: ${chatId} was not found`
)
}

return chat
}

async chat(chatId: string, message:string, context: ChatContext): Promise<Message> {
async chat(chatId: string, message:string, context: ChatContext): Promise<ChatResponse> {
return await this.atomicPhase_(
async (transactionManager: EntityManager) => {
const chatRepository = transactionManager.withRepository(
Expand All @@ -83,7 +77,8 @@ class ChatService extends TransactionBaseService {
id: chatId,
organisation_id: this.loggedInUser_.organisation_id,
user_id: this.loggedInUser_.id
}
},
relations: ["messages"]
});
if (!chat) {
throw new AutoflowAiError(
Expand All @@ -96,10 +91,18 @@ class ChatService extends TransactionBaseService {
)
const userMessage = messageRepository.create({chat_id:chatId, user_id:this.loggedInUser_.id, content: message, role: MessageRoles.USER})
await messageRepository.save(userMessage)
const responseMessage = await this.openAiService_.completeChat([{content: message, role: MessageRoles.USER}])
const assistantMessage = messageRepository.create({chat_id:chatId, user_id: this.loggedInUser_.id, content: responseMessage, role: MessageRoles.ASSISTANT})
let messages = chat?.messages.map(message => ({
role: message.role,
content: message.content
}));
messages.push({role: MessageRoles.USER, content: message})
const chatResponse = await this.chatApproach_.run(messages, context)
const assistantMessage = messageRepository.create({chat_id:chatId, user_id: this.loggedInUser_.id, content: chatResponse.message.content, role: MessageRoles.ASSISTANT})
const savedAssistantMessage = await messageRepository.save(assistantMessage)
return savedAssistantMessage
return {
message: savedAssistantMessage,
data_points: chatResponse.data_points
}
}
)
}
Expand All @@ -110,7 +113,6 @@ class ChatService extends TransactionBaseService {
selector.user_id = this.loggedInUser_.id
return await chatRepo.find(buildQuery(selector, config))
}

a
}

export default ChatService;
22 changes: 3 additions & 19 deletions packages/ocular/src/services/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ class SearchService extends AbstractSearchService {
protected readonly logger_: Logger
protected readonly defaultIndexName_: string
protected readonly vectorDBService_ : IVectorDB
// protected readonly searchIndexService_ : ISearchService

constructor(container, config) {
super(container, config)
this.logger_ = container.logger
this.openAiService_ = container.openAiService
this.vectorDBService_ = container.vectorDBService
// this.searchIndexService_ = container.searchIndexService
this.defaultIndexName_ = container.indexName

}
Expand All @@ -47,23 +45,9 @@ class SearchService extends AbstractSearchService {
// If retrieval mode includes vectors, compute an embedding for the query
let allDocs = []
let queryVector;
// if (hasVectors) {
console.log("Vector Search", query, indexName)
queryVector = await this.openAiService_.createEmbeddings(query!);
const hits = await this.vectorDBService_.searchDocuments(indexName,queryVector )
allDocs.push(...hits);
// }


// Retrieve Search Index Results
// if (hasText){
// const textSearch = await this.searchIndexService_.search(indexName,query)
// console.log("textSearch Docs ")
// console.log(textSearch)
// allDocs.push(...textSearch.docs);
// }


queryVector = await this.openAiService_.createEmbeddings(query!);
const hits = await this.vectorDBService_.searchDocuments(indexName,queryVector )
allDocs.push(...hits);
return allDocs
}

Expand Down
9 changes: 9 additions & 0 deletions packages/types/src/common/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { IndexableDocChunk } from './document';
import { Message } from './message';

export type ChatContext = {
stream?: boolean;
suggest_followup_questions?: boolean;
};

export type ChatResponse = {
message: Message;
data_points?: IndexableDocChunk[]
thoughts?: string;
};
1 change: 1 addition & 0 deletions packages/types/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./autoflow-container"
export * from "./chat"
export * from "./common"
export * from "./document"
export * from "./message"
export * from "./search"
6 changes: 6 additions & 0 deletions packages/types/src/common/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type MessageRole = 'system' | 'user' | 'assistant';

export interface Message {
role: MessageRole;
content: string;
}
10 changes: 2 additions & 8 deletions packages/types/src/common/search.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { IndexableDocChunk } from "./document";

export type MessageRole = 'system' | 'user' | 'assistant';

export interface Message {
role: MessageRole;
content: string;
}
import { Message } from "./message";

export type SearchContext = {
retrieval_mode?: 'hybrid' | 'text' | 'vectors';
Expand Down Expand Up @@ -38,7 +32,7 @@ export interface SearchResultChunk {
object: 'chat.completion.chunk';
}

export type SearchResultMessage = Message & {
export type SearchResultMessage = Message & {
context?: Record<string, any> & {
data_points?: IndexableDocChunk[]
thoughts?: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/types/src/interfaces/approach-interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IndexableDocChunk, Message, SearchResult, SearchResultChunk } from '../common';
import { IndexableDocChunk, Message, SearchResult, SearchResultChunk, ChatContext, ChatResponse} from '../common';
import { SearchContext } from "../common";

export enum ApproachDefinitions {
Expand All @@ -12,6 +12,6 @@ export interface IAskApproach {
}

export interface IChatApproach {
run(messages: Message[], context?: SearchContext): Promise<SearchResult>;
runWithStreaming(messages: Message[], context?: SearchContext): AsyncGenerator<SearchResultChunk, void>;
run(messages: Message[], context?: ChatContext): Promise<ChatResponse>;
runWithStreaming(messages: Message[], context?: ChatContext): AsyncGenerator<SearchResultChunk, void>;
}