🐛 Fix streaming text selection (#1444)
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/js",
|
||||
"version": "0.2.70",
|
||||
"version": "0.2.71",
|
||||
"description": "Javascript library to display typebots on your website",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
@ -154,6 +154,7 @@ export const ConversationContainer = (props: Props) => {
|
||||
const longRequest = setTimeout(() => {
|
||||
setIsSending(true)
|
||||
}, 1000)
|
||||
autoScrollToBottom()
|
||||
const { data, error } = await continueChatQuery({
|
||||
apiHost: props.context.apiHost,
|
||||
sessionId: props.initialChatReply.sessionId,
|
||||
@ -205,6 +206,11 @@ export const ConversationContainer = (props: Props) => {
|
||||
isNotDefined(action.lastBubbleBlockId)
|
||||
)
|
||||
await processClientSideActions(actionsBeforeFirstBubble)
|
||||
if (
|
||||
data.clientSideActions.length === 1 &&
|
||||
data.clientSideActions[0].type === 'stream'
|
||||
)
|
||||
return
|
||||
}
|
||||
setChatChunks((displayedChunks) => [
|
||||
...displayedChunks,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { streamingMessage } from '@/utils/streamingMessageSignal'
|
||||
import { createEffect, createSignal } from 'solid-js'
|
||||
import { For, createEffect, createSignal } from 'solid-js'
|
||||
import { marked } from 'marked'
|
||||
import domPurify from 'dompurify'
|
||||
|
||||
@ -8,7 +8,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export const StreamingBubble = (props: Props) => {
|
||||
const [content, setContent] = createSignal<string>('')
|
||||
const [content, setContent] = createSignal<string[]>([])
|
||||
|
||||
marked.use({
|
||||
renderer: {
|
||||
@ -19,12 +19,16 @@ export const StreamingBubble = (props: Props) => {
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (streamingMessage()?.id === props.streamingMessageId)
|
||||
setContent(
|
||||
domPurify.sanitize(marked.parse(streamingMessage()?.content ?? ''), {
|
||||
ADD_ATTR: ['target'],
|
||||
})
|
||||
)
|
||||
if (streamingMessage()?.id !== props.streamingMessageId) return []
|
||||
setContent(
|
||||
streamingMessage()
|
||||
?.content.split('\n\n')
|
||||
.map((line) =>
|
||||
domPurify.sanitize(marked.parse(line), {
|
||||
ADD_ATTR: ['target'],
|
||||
})
|
||||
) ?? []
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
@ -43,8 +47,9 @@ export const StreamingBubble = (props: Props) => {
|
||||
class={
|
||||
'flex flex-col overflow-hidden text-fade-in mx-4 my-2 relative text-ellipsis h-full gap-6'
|
||||
}
|
||||
innerHTML={content()}
|
||||
/>
|
||||
>
|
||||
<For each={content()}>{(line) => <span innerHTML={line} />}</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,16 +7,22 @@ let abortController: AbortController | null = null
|
||||
const secondsToWaitBeforeRetries = 3
|
||||
const maxRetryAttempts = 3
|
||||
|
||||
const edgeRuntimePath = '/api/integrations/openai/streamer'
|
||||
const nodejsRuntimePath = (sessionId: string) =>
|
||||
`/api/v1/sessions/${sessionId}/streamMessage`
|
||||
|
||||
export const streamChat =
|
||||
(context: ClientSideActionContext & { retryAttempt?: number }) =>
|
||||
async ({
|
||||
messages,
|
||||
runtime,
|
||||
onMessageStream,
|
||||
}: {
|
||||
messages?: {
|
||||
content?: string | undefined
|
||||
role?: 'system' | 'user' | 'assistant' | undefined
|
||||
}[]
|
||||
runtime: 'edge' | 'nodejs'
|
||||
onMessageStream?: (props: { id: string; message: string }) => void
|
||||
}): Promise<{ message?: string; error?: object }> => {
|
||||
try {
|
||||
@ -25,9 +31,12 @@ export const streamChat =
|
||||
const apiHost = context.apiHost
|
||||
|
||||
const res = await fetch(
|
||||
`${
|
||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||
}/api/integrations/openai/streamer`,
|
||||
isNotEmpty(apiHost)
|
||||
? apiHost
|
||||
: guessApiHost() +
|
||||
(runtime === 'edge'
|
||||
? edgeRuntimePath
|
||||
: nodejsRuntimePath(context.sessionId)),
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -35,7 +44,7 @@ export const streamChat =
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages,
|
||||
sessionId: context.sessionId,
|
||||
sessionId: runtime === 'edge' ? context.sessionId : undefined,
|
||||
}),
|
||||
signal: abortController.signal,
|
||||
}
|
||||
@ -52,7 +61,7 @@ export const streamChat =
|
||||
return streamChat({
|
||||
...context,
|
||||
retryAttempt: (context.retryAttempt ?? 0) + 1,
|
||||
})({ messages, onMessageStream })
|
||||
})({ messages, onMessageStream, runtime })
|
||||
}
|
||||
return {
|
||||
error: (await res.json()) || 'Failed to fetch the chat response.',
|
||||
|
@ -54,12 +54,17 @@ export const executeClientSideAction = async ({
|
||||
'streamOpenAiChatCompletion' in clientSideAction ||
|
||||
'stream' in clientSideAction
|
||||
) {
|
||||
const runtime =
|
||||
'streamOpenAiChatCompletion' in clientSideAction
|
||||
? clientSideAction.streamOpenAiChatCompletion.runtime
|
||||
: clientSideAction.runtime
|
||||
const { error, message } = await streamChat(context)({
|
||||
messages:
|
||||
'streamOpenAiChatCompletion' in clientSideAction
|
||||
? clientSideAction.streamOpenAiChatCompletion?.messages
|
||||
: undefined,
|
||||
onMessageStream,
|
||||
runtime,
|
||||
})
|
||||
if (error)
|
||||
return {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/nextjs",
|
||||
"version": "0.2.70",
|
||||
"version": "0.2.71",
|
||||
"description": "Convenient library to display typebots on your Next.js website",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/react",
|
||||
"version": "0.2.70",
|
||||
"version": "0.2.71",
|
||||
"description": "Convenient library to display typebots on your React app",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
Reference in New Issue
Block a user