feat: passing the inputs values using difyChatbotConfig (#6376)

This commit is contained in:
yoyocircle 2024-07-18 21:54:16 +08:00 committed by GitHub
parent e493ce9981
commit 284ef52bba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 42 deletions

View File

@ -31,6 +31,7 @@ import type {
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils' import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
import { useToastContext } from '@/app/components/base/toast' import { useToastContext } from '@/app/components/base/toast'
import { changeLanguage } from '@/i18n/i18next-config' import { changeLanguage } from '@/i18n/i18next-config'
import { getProcessedInputsFromUrlParams } from '@/app/components/base/chat/utils'
export const useEmbeddedChatbot = () => { export const useEmbeddedChatbot = () => {
const isInstalledApp = false const isInstalledApp = false
@ -109,6 +110,7 @@ export const useEmbeddedChatbot = () => {
const { t } = useTranslation() const { t } = useTranslation()
const newConversationInputsRef = useRef<Record<string, any>>({}) const newConversationInputsRef = useRef<Record<string, any>>({})
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any>>({}) const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any>>({})
const [initInputs, setInitInputs] = useState<Record<string, any>>({})
const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => { const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => {
newConversationInputsRef.current = newInputs newConversationInputsRef.current = newInputs
setNewConversationInputs(newInputs) setNewConversationInputs(newInputs)
@ -116,30 +118,49 @@ export const useEmbeddedChatbot = () => {
const inputsForms = useMemo(() => { const inputsForms = useMemo(() => {
return (appParams?.user_input_form || []).filter((item: any) => item.paragraph || item.select || item['text-input'] || item.number).map((item: any) => { return (appParams?.user_input_form || []).filter((item: any) => item.paragraph || item.select || item['text-input'] || item.number).map((item: any) => {
if (item.paragraph) { if (item.paragraph) {
let value = initInputs[item.paragraph.variable]
if (value && item.paragraph.max_length && value.length > item.paragraph.max_length)
value = value.slice(0, item.paragraph.max_length)
return { return {
...item.paragraph, ...item.paragraph,
default: value || item.default,
type: 'paragraph', type: 'paragraph',
} }
} }
if (item.number) { if (item.number) {
const convertedNumber = Number(initInputs[item.number.variable]) ?? undefined
return { return {
...item.number, ...item.number,
default: convertedNumber || item.default,
type: 'number', type: 'number',
} }
} }
if (item.select) { if (item.select) {
const isInputInOptions = item.select.options.includes(initInputs[item.select.variable])
return { return {
...item.select, ...item.select,
default: (isInputInOptions ? initInputs[item.select.variable] : undefined) || item.default,
type: 'select', type: 'select',
} }
} }
let value = initInputs[item['text-input'].variable]
if (value && item['text-input'].max_length && value.length > item['text-input'].max_length)
value = value.slice(0, item['text-input'].max_length)
return { return {
...item['text-input'], ...item['text-input'],
default: value || item.default,
type: 'text-input', type: 'text-input',
} }
}) })
}, [appParams]) }, [appParams])
useEffect(() => {
// init inputs from url params
setInitInputs(getProcessedInputsFromUrlParams())
}, [])
useEffect(() => { useEffect(() => {
const conversationInputs: Record<string, any> = {} const conversationInputs: Record<string, any> = {}

View File

@ -0,0 +1,20 @@
async function decodeBase64AndDecompress(base64String: string) {
const binaryString = atob(base64String)
const compressedUint8Array = Uint8Array.from(binaryString, char => char.charCodeAt(0))
const decompressedStream = new Response(compressedUint8Array).body.pipeThrough(new DecompressionStream('gzip'))
const decompressedArrayBuffer = await new Response(decompressedStream).arrayBuffer()
return new TextDecoder().decode(decompressedArrayBuffer)
}
function getProcessedInputsFromUrlParams(): Record<string, any> {
const urlParams = new URLSearchParams(window.location.search)
const inputs: Record<string, any> = {}
urlParams.forEach(async (value, key) => {
inputs[key] = await decodeBase64AndDecompress(decodeURIComponent(value))
})
return inputs
}
export {
getProcessedInputsFromUrlParams,
}

View File

@ -24,27 +24,55 @@
}; };
// Main function to embed the chatbot // Main function to embed the chatbot
function embedChatbot() { async function embedChatbot() {
if (!config || !config.token) { if (!config || !config.token) {
console.error(`${configKey} is empty or token is not provided`); console.error(`${configKey} is empty or token is not provided`);
return; return;
} }
async function compressAndEncodeBase64(input) {
const uint8Array = new TextEncoder().encode(input);
const compressedStream = new Response(
new Blob([uint8Array]).stream().pipeThrough(new CompressionStream('gzip'))
).arrayBuffer();
const compressedUint8Array = new Uint8Array(await compressedStream);
return btoa(String.fromCharCode(...compressedUint8Array));
}
async function getCompressedInputsFromConfig() {
const inputs = config?.inputs || {};
const compressedInputs = {};
await Promise.all(
Object.entries(inputs).map(async ([key, value]) => {
compressedInputs[key] = await compressAndEncodeBase64(value);
})
);
return compressedInputs;
}
const params = new URLSearchParams(await getCompressedInputsFromConfig());
const baseUrl = const baseUrl =
config.baseUrl || `https://${config.isDev ? "dev." : ""}udify.app`; config.baseUrl || `https://${config.isDev ? "dev." : ""}udify.app`;
// pre-check the length of the URL
const iframeUrl = `${baseUrl}/chatbot/${config.token}?${params}`;
if(iframeUrl.length > 2048) {
console.error("The URL is too long, please reduce the number of inputs to prevent the bot from failing to load");
}
// Function to create the iframe for the chatbot // Function to create the iframe for the chatbot
function createIframe() { function createIframe() {
const iframe = document.createElement("iframe"); const iframe = document.createElement("iframe");
iframe.allow = "fullscreen;microphone"; iframe.allow = "fullscreen;microphone";
iframe.title = "dify chatbot bubble window"; iframe.title = "dify chatbot bubble window";
iframe.id = iframeId; iframe.id = iframeId;
iframe.src = `${baseUrl}/chatbot/${config.token}`; iframe.src = iframeUrl;
iframe.style.cssText = ` iframe.style.cssText = `
border: none; position: fixed; flex-direction: column; justify-content: space-between; border: none; position: fixed; flex-direction: column; justify-content: space-between;
box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px;
bottom: 5rem; right: 1rem; width: 24rem; max-width: calc(100vw - 2rem); height: 40rem; bottom: 5rem; right: 1rem; width: 24rem; max-width: calc(100vw - 2rem); height: 40rem;
max-height: calc(100vh - 6rem); border-radius: 0.75rem; display: flex; z-index: 2147483647; max-height: calc(100vh - 6rem); border-radius: 0.75rem; display: flex; z-index: 2147483647;
overflow: hidden; left: unset; background-color: #F3F4F6; overflow: hidden; left: unset; background-color: #F3F4F6;
`; `;
@ -106,19 +134,19 @@
document.head.appendChild(styleSheet); document.head.appendChild(styleSheet);
styleSheet.sheet.insertRule(` styleSheet.sheet.insertRule(`
#${containerDiv.id} { #${containerDiv.id} {
position: fixed; position: fixed;
bottom: var(--${containerDiv.id}-bottom, 1rem); bottom: var(--${containerDiv.id}-bottom, 1rem);
right: var(--${containerDiv.id}-right, 1rem); right: var(--${containerDiv.id}-right, 1rem);
left: var(--${containerDiv.id}-left, unset); left: var(--${containerDiv.id}-left, unset);
top: var(--${containerDiv.id}-top, unset); top: var(--${containerDiv.id}-top, unset);
width: var(--${containerDiv.id}-width, 50px); width: var(--${containerDiv.id}-width, 50px);
height: var(--${containerDiv.id}-height, 50px); height: var(--${containerDiv.id}-height, 50px);
border-radius: var(--${containerDiv.id}-border-radius, 25px); border-radius: var(--${containerDiv.id}-border-radius, 25px);
background-color: var(--${containerDiv.id}-bg-color, #155EEF); background-color: var(--${containerDiv.id}-bg-color, #155EEF);
box-shadow: var(--${containerDiv.id}-box-shadow, rgba(0, 0, 0, 0.2) 0px 4px 8px 0px); box-shadow: var(--${containerDiv.id}-box-shadow, rgba(0, 0, 0, 0.2) 0px 4px 8px 0px);
cursor: pointer; cursor: pointer;
z-index: 2147483647; z-index: 2147483647;
transition: all 0.2s ease-in-out 0s; transition: all 0.2s ease-in-out 0s;
} }
`); `);
styleSheet.sheet.insertRule(` styleSheet.sheet.insertRule(`
@ -154,7 +182,8 @@
} else { } else {
document.addEventListener('keydown', handleEscKey); document.addEventListener('keydown', handleEscKey);
} }
resetIframePosition(); resetIframePosition();
}); });

File diff suppressed because one or more lines are too long