2
0

🐛 (openai) Fix 2 openai streaming back to back

This commit is contained in:
Baptiste Arnaud
2023-10-12 13:59:03 +02:00
parent a48026c707
commit 42fd6037f7
8 changed files with 30 additions and 16 deletions

View File

@ -39,6 +39,7 @@ export const continueBotFlow = async (
reply: string | undefined, reply: string | undefined,
{ state, version }: Params { state, version }: Params
): Promise<ChatReply & { newSessionState: SessionState }> => { ): Promise<ChatReply & { newSessionState: SessionState }> => {
let firstBubbleWasStreamed = false
let newSessionState = { ...state } let newSessionState = { ...state }
if (!newSessionState.currentBlock) return startBotFlow({ state, version }) if (!newSessionState.currentBlock) return startBotFlow({ state, version })
@ -81,6 +82,7 @@ export const continueBotFlow = async (
block.type === IntegrationBlockType.OPEN_AI && block.type === IntegrationBlockType.OPEN_AI &&
block.options.task === 'Create chat completion' block.options.task === 'Create chat completion'
) { ) {
firstBubbleWasStreamed = true
if (reply) { if (reply) {
const result = await resumeChatCompletion(state, { const result = await resumeChatCompletion(state, {
options: block.options, options: block.options,
@ -125,7 +127,7 @@ export const continueBotFlow = async (
...group, ...group,
blocks: group.blocks.slice(blockIndex + 1), blocks: group.blocks.slice(blockIndex + 1),
}, },
{ version, state: newSessionState } { version, state: newSessionState, firstBubbleWasStreamed }
) )
return { return {
...chatReply, ...chatReply,
@ -157,6 +159,7 @@ export const continueBotFlow = async (
const chatReply = await executeGroup(nextGroup.group, { const chatReply = await executeGroup(nextGroup.group, {
version, version,
state: newSessionState, state: newSessionState,
firstBubbleWasStreamed,
}) })
return { return {

View File

@ -29,11 +29,18 @@ type ContextProps = {
state: SessionState state: SessionState
currentReply?: ChatReply currentReply?: ChatReply
currentLastBubbleId?: string currentLastBubbleId?: string
firstBubbleWasStreamed?: boolean
} }
export const executeGroup = async ( export const executeGroup = async (
group: Group, group: Group,
{ version, state, currentReply, currentLastBubbleId }: ContextProps {
version,
state,
currentReply,
currentLastBubbleId,
firstBubbleWasStreamed,
}: ContextProps
): Promise<ChatReply & { newSessionState: SessionState }> => { ): Promise<ChatReply & { newSessionState: SessionState }> => {
const messages: ChatReply['messages'] = currentReply?.messages ?? [] const messages: ChatReply['messages'] = currentReply?.messages ?? []
let clientSideActions: ChatReply['clientSideActions'] = let clientSideActions: ChatReply['clientSideActions'] =
@ -44,10 +51,13 @@ export const executeGroup = async (
let newSessionState = state let newSessionState = state
let index = -1
for (const block of group.blocks) { for (const block of group.blocks) {
index++
nextEdgeId = block.outgoingEdgeId nextEdgeId = block.outgoingEdgeId
if (isBubbleBlock(block)) { if (isBubbleBlock(block)) {
if (firstBubbleWasStreamed && index === 0) continue
messages.push( messages.push(
parseBubbleBlock(block, { parseBubbleBlock(block, {
version, version,

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/js", "name": "@typebot.io/js",
"version": "0.2.5", "version": "0.2.6",
"description": "Javascript library to display typebots on your website", "description": "Javascript library to display typebots on your website",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@ -3,7 +3,6 @@ import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/enums
import { import {
createEffect, createEffect,
createSignal, createSignal,
createUniqueId,
For, For,
onCleanup, onCleanup,
onMount, onMount,
@ -109,12 +108,11 @@ export const ConversationContainer = (props: Props) => {
})() })()
}) })
const streamMessage = (content: string) => { const streamMessage = ({ id, message }: { id: string; message: string }) => {
setIsSending(false) setIsSending(false)
const lastChunk = [...chatChunks()].pop() const lastChunk = [...chatChunks()].pop()
if (!lastChunk) return if (!lastChunk) return
const id = lastChunk.streamingMessageId ?? createUniqueId() if (lastChunk.streamingMessageId !== id)
if (!lastChunk.streamingMessageId)
setChatChunks((displayedChunks) => [ setChatChunks((displayedChunks) => [
...displayedChunks, ...displayedChunks,
{ {
@ -122,7 +120,7 @@ export const ConversationContainer = (props: Props) => {
streamingMessageId: id, streamingMessageId: id,
}, },
]) ])
setStreamingMessage({ id, content }) setStreamingMessage({ id, content: message })
} }
createEffect(() => { createEffect(() => {
@ -216,9 +214,7 @@ export const ConversationContainer = (props: Props) => {
...displayedChunks, ...displayedChunks,
{ {
input: data.input, input: data.input,
messages: [...chatChunks()].pop()?.streamingMessageId messages: data.messages,
? data.messages.slice(1)
: data.messages,
clientSideActions: data.clientSideActions, clientSideActions: data.clientSideActions,
}, },
]) ])

View File

@ -1,6 +1,7 @@
import { ClientSideActionContext } from '@/types' import { ClientSideActionContext } from '@/types'
import { guessApiHost } from '@/utils/guessApiHost' import { guessApiHost } from '@/utils/guessApiHost'
import { isNotEmpty } from '@typebot.io/lib/utils' import { isNotEmpty } from '@typebot.io/lib/utils'
import { createUniqueId } from 'solid-js'
let abortController: AbortController | null = null let abortController: AbortController | null = null
const secondsToWaitBeforeRetries = 3 const secondsToWaitBeforeRetries = 3
@ -13,7 +14,9 @@ export const streamChat =
content?: string | undefined content?: string | undefined
role?: 'system' | 'user' | 'assistant' | undefined role?: 'system' | 'user' | 'assistant' | undefined
}[], }[],
{ onMessageStream }: { onMessageStream?: (message: string) => void } {
onMessageStream,
}: { onMessageStream?: (props: { id: string; message: string }) => void }
): Promise<{ message?: string; error?: object }> => { ): Promise<{ message?: string; error?: object }> => {
try { try {
abortController = new AbortController() abortController = new AbortController()
@ -64,6 +67,8 @@ export const streamChat =
const reader = res.body.getReader() const reader = res.body.getReader()
const decoder = new TextDecoder() const decoder = new TextDecoder()
const id = createUniqueId()
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
const { done, value } = await reader.read() const { done, value } = await reader.read()
@ -72,7 +77,7 @@ export const streamChat =
} }
const chunk = decoder.decode(value) const chunk = decoder.decode(value)
message += chunk message += chunk
if (onMessageStream) onMessageStream(message) if (onMessageStream) onMessageStream({ id, message })
if (abortController === null) { if (abortController === null) {
reader.cancel() reader.cancel()
break break

View File

@ -14,7 +14,7 @@ import { injectStartProps } from './injectStartProps'
type Props = { type Props = {
clientSideAction: NonNullable<ChatReply['clientSideActions']>[0] clientSideAction: NonNullable<ChatReply['clientSideActions']>[0]
context: ClientSideActionContext context: ClientSideActionContext
onMessageStream?: (message: string) => void onMessageStream?: (props: { id: string; message: string }) => void
} }
export const executeClientSideAction = async ({ export const executeClientSideAction = async ({

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/nextjs", "name": "@typebot.io/nextjs",
"version": "0.2.5", "version": "0.2.6",
"description": "Convenient library to display typebots on your Next.js website", "description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/react", "name": "@typebot.io/react",
"version": "0.2.5", "version": "0.2.6",
"description": "Convenient library to display typebots on your React app", "description": "Convenient library to display typebots on your React app",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",