Feature/sse (#3125)

* Base changes for ServerSide Events (instead of socket.io)

* lint fixes

* adding of interface and separate methods for streaming events

* lint

* first draft, handles both internal and external prediction end points.

* lint fixes

* additional internal end point for streaming and associated changes

* return streamresponse as true to build agent flow

* 1) JSON formatting for internal events
2) other fixes

* 1) convert internal event to metadata to maintain consistency with external response

* fix action and metadata streaming

* fix for error when agent flow is aborted

* prevent subflows from streaming and other code cleanup

* prevent streaming from enclosed tools

* add fix for preventing chaintool streaming

* update lock file

* add open when hidden to sse

* Streaming errors

* Streaming errors

* add fix for showing error message

---------

Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
Vinod Kiran
2024-09-17 12:31:25 +05:30
committed by GitHub
parent 7a4c7efcab
commit 26444ac3ae
47 changed files with 1021 additions and 327 deletions
+19
View File
@@ -400,3 +400,22 @@ export interface IStateWithMessages extends ICommonObject {
messages: BaseMessage[]
[key: string]: any
}
export interface IServerSideEventStreamer {
streamEvent(chatId: string, data: string): void
streamStartEvent(chatId: string, data: any): void
streamTokenEvent(chatId: string, data: string): void
streamCustomEvent(chatId: string, eventType: string, data: any): void
streamSourceDocumentsEvent(chatId: string, data: any): void
streamUsedToolsEvent(chatId: string, data: any): void
streamFileAnnotationsEvent(chatId: string, data: any): void
streamToolEvent(chatId: string, data: any): void
streamAgentReasoningEvent(chatId: string, data: any): void
streamNextAgentEvent(chatId: string, data: any): void
streamActionEvent(chatId: string, data: any): void
streamAbortEvent(chatId: string): void
streamEndEvent(chatId: string): void
}
+28 -17
View File
@@ -1,6 +1,5 @@
import { Logger } from 'winston'
import { v4 as uuidv4 } from 'uuid'
import { Server } from 'socket.io'
import { Client } from 'langsmith'
import CallbackHandler from 'langfuse-langchain'
import lunary from 'lunary'
@@ -15,7 +14,7 @@ import { AgentAction } from '@langchain/core/agents'
import { LunaryHandler } from '@langchain/community/callbacks/handlers/lunary'
import { getCredentialData, getCredentialParam, getEnvironmentVariable } from './utils'
import { ICommonObject, INodeData } from './Interface'
import { ICommonObject, INodeData, IServerSideEventStreamer } from './Interface'
import { LangWatch, LangWatchSpan, LangWatchTrace, autoconvertTypedValues } from 'langwatch'
interface AgentRun extends Run {
@@ -163,16 +162,16 @@ export class ConsoleCallbackHandler extends BaseTracer {
export class CustomChainHandler extends BaseCallbackHandler {
name = 'custom_chain_handler'
isLLMStarted = false
socketIO: Server
socketIOClientId = ''
skipK = 0 // Skip streaming for first K numbers of handleLLMStart
returnSourceDocuments = false
cachedResponse = true
chatId: string = ''
sseStreamer: IServerSideEventStreamer | undefined
constructor(socketIO: Server, socketIOClientId: string, skipK?: number, returnSourceDocuments?: boolean) {
constructor(sseStreamer: IServerSideEventStreamer | undefined, chatId: string, skipK?: number, returnSourceDocuments?: boolean) {
super()
this.socketIO = socketIO
this.socketIOClientId = socketIOClientId
this.sseStreamer = sseStreamer
this.chatId = chatId
this.skipK = skipK ?? this.skipK
this.returnSourceDocuments = returnSourceDocuments ?? this.returnSourceDocuments
}
@@ -186,14 +185,20 @@ export class CustomChainHandler extends BaseCallbackHandler {
if (this.skipK === 0) {
if (!this.isLLMStarted) {
this.isLLMStarted = true
this.socketIO.to(this.socketIOClientId).emit('start', token)
if (this.sseStreamer) {
this.sseStreamer.streamStartEvent(this.chatId, token)
}
}
if (this.sseStreamer) {
this.sseStreamer.streamTokenEvent(this.chatId, token)
}
this.socketIO.to(this.socketIOClientId).emit('token', token)
}
}
handleLLMEnd() {
this.socketIO.to(this.socketIOClientId).emit('end')
if (this.sseStreamer) {
this.sseStreamer.streamEndEvent(this.chatId)
}
}
handleChainEnd(outputs: ChainValues, _: string, parentRunId?: string): void | Promise<void> {
@@ -208,17 +213,23 @@ export class CustomChainHandler extends BaseCallbackHandler {
const result = cachedValue.split(/(\s+)/)
result.forEach((token: string, index: number) => {
if (index === 0) {
this.socketIO.to(this.socketIOClientId).emit('start', token)
if (this.sseStreamer) {
this.sseStreamer.streamStartEvent(this.chatId, token)
}
}
if (this.sseStreamer) {
this.sseStreamer.streamTokenEvent(this.chatId, token)
}
this.socketIO.to(this.socketIOClientId).emit('token', token)
})
if (this.returnSourceDocuments) {
this.socketIO.to(this.socketIOClientId).emit('sourceDocuments', outputs?.sourceDocuments)
if (this.returnSourceDocuments && this.sseStreamer) {
this.sseStreamer.streamSourceDocumentsEvent(this.chatId, outputs?.sourceDocuments)
}
if (this.sseStreamer) {
this.sseStreamer.streamEndEvent(this.chatId)
}
this.socketIO.to(this.socketIOClientId).emit('end')
} else {
if (this.returnSourceDocuments) {
this.socketIO.to(this.socketIOClientId).emit('sourceDocuments', outputs?.sourceDocuments)
if (this.returnSourceDocuments && this.sseStreamer) {
this.sseStreamer.streamSourceDocumentsEvent(this.chatId, outputs?.sourceDocuments)
}
}
}