Merge branch 'feat/deep-research' into master-deep-research

This commit is contained in:
ItzCrazyKns
2025-04-07 10:21:22 +05:30
committed by GitHub
2 changed files with 239 additions and 280 deletions

View File

@ -363,7 +363,6 @@ const ChatWindow = ({ id }: { id?: string }) => {
if (data.type === 'sources') { if (data.type === 'sources') {
sources = data.data; sources = data.data;
if (!added) {
setMessages((prevMessages) => [ setMessages((prevMessages) => [
...prevMessages, ...prevMessages,
{ {
@ -376,7 +375,6 @@ const ChatWindow = ({ id }: { id?: string }) => {
}, },
]); ]);
added = true; added = true;
}
setMessageAppeared(true); setMessageAppeared(true);
} }
@ -394,8 +392,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
}, },
]); ]);
added = true; added = true;
} setMessageAppeared(true);
} else {
setMessages((prev) => setMessages((prev) =>
prev.map((message) => { prev.map((message) => {
if (message.messageId === data.messageId) { if (message.messageId === data.messageId) {
@ -405,9 +403,9 @@ const ChatWindow = ({ id }: { id?: string }) => {
return message; return message;
}), }),
); );
}
recievedMessage += data.data; recievedMessage += data.data;
setMessageAppeared(true);
} }
if (data.type === 'messageEnd') { if (data.type === 'messageEnd') {

View File

@ -6,11 +6,6 @@ import {
MessagesPlaceholder, MessagesPlaceholder,
PromptTemplate, PromptTemplate,
} from '@langchain/core/prompts'; } from '@langchain/core/prompts';
import {
RunnableLambda,
RunnableMap,
RunnableSequence,
} from '@langchain/core/runnables';
import { BaseMessage } from '@langchain/core/messages'; import { BaseMessage } from '@langchain/core/messages';
import { StringOutputParser } from '@langchain/core/output_parsers'; import { StringOutputParser } from '@langchain/core/output_parsers';
import LineListOutputParser from '../outputParsers/listLineOutputParser'; import LineListOutputParser from '../outputParsers/listLineOutputParser';
@ -24,6 +19,7 @@ import computeSimilarity from '../utils/computeSimilarity';
import formatChatHistoryAsString from '../utils/formatHistory'; import formatChatHistoryAsString from '../utils/formatHistory';
import eventEmitter from 'events'; import eventEmitter from 'events';
import { StreamEvent } from '@langchain/core/tracers/log_stream'; import { StreamEvent } from '@langchain/core/tracers/log_stream';
import { EventEmitter } from 'node:stream';
export interface MetaSearchAgentType { export interface MetaSearchAgentType {
searchAndAnswer: ( searchAndAnswer: (
@ -47,7 +43,7 @@ interface Config {
activeEngines: string[]; activeEngines: string[];
} }
type BasicChainInput = { type SearchInput = {
chat_history: BaseMessage[]; chat_history: BaseMessage[];
query: string; query: string;
}; };
@ -60,14 +56,25 @@ class MetaSearchAgent implements MetaSearchAgentType {
this.config = config; this.config = config;
} }
private async createSearchRetrieverChain(llm: BaseChatModel) { private async searchSources(
llm: BaseChatModel,
input: SearchInput,
emitter: EventEmitter,
) {
(llm as unknown as ChatOpenAI).temperature = 0; (llm as unknown as ChatOpenAI).temperature = 0;
return RunnableSequence.from([ const chatPrompt = PromptTemplate.fromTemplate(
PromptTemplate.fromTemplate(this.config.queryGeneratorPrompt), this.config.queryGeneratorPrompt,
llm, );
this.strParser,
RunnableLambda.from(async (input: string) => { const processedChatPrompt = await chatPrompt.invoke({
chat_history: formatChatHistoryAsString(input.chat_history),
query: input.query,
});
const llmRes = await llm.invoke(processedChatPrompt);
const messageStr = await this.strParser.invoke(llmRes);
const linksOutputParser = new LineListOutputParser({ const linksOutputParser = new LineListOutputParser({
key: 'links', key: 'links',
}); });
@ -76,10 +83,10 @@ class MetaSearchAgent implements MetaSearchAgentType {
key: 'question', key: 'question',
}); });
const links = await linksOutputParser.parse(input); const links = await linksOutputParser.parse(messageStr);
let question = this.config.summarizer let question = this.config.summarizer
? await questionOutputParser.parse(input) ? await questionOutputParser.parse(messageStr)
: input; : messageStr;
if (question === 'not_needed') { if (question === 'not_needed') {
return { query: '', docs: [] }; return { query: '', docs: [] };
@ -99,8 +106,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
linkDocs.map((doc) => { linkDocs.map((doc) => {
const URLDocExists = docGroups.find( const URLDocExists = docGroups.find(
(d) => (d) =>
d.metadata.url === doc.metadata.url && d.metadata.url === doc.metadata.url && d.metadata.totalDocs < 10,
d.metadata.totalDocs < 10,
); );
if (!URLDocExists) { if (!URLDocExists) {
@ -115,8 +121,7 @@ class MetaSearchAgent implements MetaSearchAgentType {
const docIndex = docGroups.findIndex( const docIndex = docGroups.findIndex(
(d) => (d) =>
d.metadata.url === doc.metadata.url && d.metadata.url === doc.metadata.url && d.metadata.totalDocs < 10,
d.metadata.totalDocs < 10,
); );
if (docIndex !== -1) { if (docIndex !== -1) {
@ -228,42 +233,31 @@ class MetaSearchAgent implements MetaSearchAgentType {
return { query: question, docs: documents }; return { query: question, docs: documents };
} }
}),
]);
} }
private async createAnsweringChain( private async streamAnswer(
llm: BaseChatModel, llm: BaseChatModel,
fileIds: string[], fileIds: string[],
embeddings: Embeddings, embeddings: Embeddings,
optimizationMode: 'speed' | 'balanced' | 'quality', optimizationMode: 'speed' | 'balanced' | 'quality',
systemInstructions: string, systemInstructions: string,
input: SearchInput,
emitter: EventEmitter,
) { ) {
return RunnableSequence.from([ const chatPrompt = ChatPromptTemplate.fromMessages([
RunnableMap.from({ ['system', this.config.responsePrompt],
systemInstructions: () => systemInstructions, new MessagesPlaceholder('chat_history'),
query: (input: BasicChainInput) => input.query, ['user', '{query}'],
chat_history: (input: BasicChainInput) => input.chat_history, ]);
date: () => new Date().toISOString(),
context: RunnableLambda.from(async (input: BasicChainInput) => {
const processedHistory = formatChatHistoryAsString(
input.chat_history,
);
let docs: Document[] | null = null; let docs: Document[] | null = null;
let query = input.query; let query = input.query;
if (this.config.searchWeb) { if (this.config.searchWeb) {
const searchRetrieverChain = const searchResults = await this.searchSources(llm, input, emitter);
await this.createSearchRetrieverChain(llm);
const searchRetrieverResult = await searchRetrieverChain.invoke({ query = searchResults.query;
chat_history: processedHistory, docs = searchResults.docs;
query,
});
query = searchRetrieverResult.query;
docs = searchRetrieverResult.docs;
} }
const sortedDocs = await this.rerankDocs( const sortedDocs = await this.rerankDocs(
@ -274,23 +268,30 @@ class MetaSearchAgent implements MetaSearchAgentType {
optimizationMode, optimizationMode,
); );
return sortedDocs; emitter.emit('data', JSON.stringify({ type: 'sources', data: sortedDocs }));
})
.withConfig({ const context = this.processDocs(sortedDocs);
runName: 'FinalSourceRetriever',
}) const formattedChatPrompt = await chatPrompt.invoke({
.pipe(this.processDocs), query: input.query,
}), chat_history: input.chat_history,
ChatPromptTemplate.fromMessages([ date: new Date().toISOString(),
['system', this.config.responsePrompt], context: context,
new MessagesPlaceholder('chat_history'), systemInstructions: systemInstructions,
['user', '{query}'],
]),
llm,
this.strParser,
]).withConfig({
runName: 'FinalResponseGenerator',
}); });
const llmRes = await llm.stream(formattedChatPrompt);
for await (const data of llmRes) {
const messageStr = await this.strParser.invoke(data);
emitter.emit(
'data',
JSON.stringify({ type: 'response', data: messageStr }),
);
}
emitter.emit('end');
} }
private async rerankDocs( private async rerankDocs(
@ -431,39 +432,6 @@ class MetaSearchAgent implements MetaSearchAgentType {
.join('\n'); .join('\n');
} }
private async handleStream(
stream: AsyncGenerator<StreamEvent, any, any>,
emitter: eventEmitter,
) {
for await (const event of stream) {
if (
event.event === 'on_chain_end' &&
event.name === 'FinalSourceRetriever'
) {
``;
emitter.emit(
'data',
JSON.stringify({ type: 'sources', data: event.data.output }),
);
}
if (
event.event === 'on_chain_stream' &&
event.name === 'FinalResponseGenerator'
) {
emitter.emit(
'data',
JSON.stringify({ type: 'response', data: event.data.chunk }),
);
}
if (
event.event === 'on_chain_end' &&
event.name === 'FinalResponseGenerator'
) {
emitter.emit('end');
}
}
}
async searchAndAnswer( async searchAndAnswer(
message: string, message: string,
history: BaseMessage[], history: BaseMessage[],
@ -475,26 +443,19 @@ class MetaSearchAgent implements MetaSearchAgentType {
) { ) {
const emitter = new eventEmitter(); const emitter = new eventEmitter();
const answeringChain = await this.createAnsweringChain( this.streamAnswer(
llm, llm,
fileIds, fileIds,
embeddings, embeddings,
optimizationMode, optimizationMode,
systemInstructions, systemInstructions,
);
const stream = answeringChain.streamEvents(
{ {
chat_history: history, chat_history: history,
query: message, query: message,
}, },
{ emitter,
version: 'v1',
},
); );
this.handleStream(stream, emitter);
return emitter; return emitter;
} }
} }