dify自带聊天页实现uid参数参数

This commit is contained in:
2025-11-13 15:40:57 +08:00
parent 88f7076630
commit a19dc232c1
16 changed files with 80 additions and 18 deletions

View File

@@ -1,5 +1,5 @@
# base image # base image
FROM node:22-alpine3.21 AS base FROM fiof1uwu2mjtuh.xuanyuan.run/node:22-alpine3.21 AS base
LABEL maintainer="takatost@gmail.com" LABEL maintainer="takatost@gmail.com"
# if you located in China, you can use aliyun mirror to speed up # if you located in China, you can use aliyun mirror to speed up

View File

@@ -74,6 +74,7 @@ const ChatItem: FC<ChatItemProps> = ({
handleRestart, handleRestart,
} = useChat( } = useChat(
config, config,
userProfile.id,
{ {
inputs, inputs,
inputsForm, inputsForm,

View File

@@ -80,6 +80,7 @@ const DebugWithSingleModel = (
handleAnnotationRemoved, handleAnnotationRemoved,
} = useChat( } = useChat(
config, config,
userProfile.id,
{ {
inputs, inputs,
inputsForm, inputsForm,

View File

@@ -51,6 +51,7 @@ const ChatWrapper = () => {
setIsResponding, setIsResponding,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
} = useChatWithHistoryContext() } = useChatWithHistoryContext()
// Semantic variable for better code readability // Semantic variable for better code readability
@@ -76,17 +77,24 @@ const ChatWrapper = () => {
handleStop, handleStop,
isResponding: respondingState, isResponding: respondingState,
suggestedQuestions, suggestedQuestions,
} = useChat( } = useChat({
appConfig, appData: appData!,
{ appParams: appData?.site.default_language ? { ...appParams, language: appData?.site.default_language } : appParams,
inputs: (isHistoryConversation ? currentConversationInputs : newConversationInputs) as any, isInstalledApp,
inputsForm: inputsForms, appId,
conversationId: currentConversationId,
inputs: currentConversationInputs,
promptVariables: allInputsHidden ? currentConversationInputs : {},
prevChatList: appPrevChatTree,
onNewConversation: handleNewConversationCompleted,
onConversationIdChange: (id) => {
handleNewConversationCompleted(id)
}, },
appPrevChatTree, onRespondingChanged: setIsResponding,
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId), onClearChatList: clearChatList,
clearChatList, onClearChatListFinished: () => setClearChatList(false),
setClearChatList, user: userIdFromQuery || initUserVariables?.user_id,
) })
const inputsFormValue = isHistoryConversation ? currentConversationInputs : newConversationInputsRef?.current const inputsFormValue = isHistoryConversation ? currentConversationInputs : newConversationInputsRef?.current
const inputDisabled = useMemo(() => { const inputDisabled = useMemo(() => {
if (allInputsHidden) if (allInputsHidden)

View File

@@ -60,6 +60,7 @@ export type ChatWithHistoryContextValue = {
name?: string name?: string
avatar_url?: string avatar_url?: string
} }
userIdFromQuery?: string
} }
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({ export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
@@ -95,5 +96,6 @@ export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>
setCurrentConversationInputs: noop, setCurrentConversationInputs: noop,
allInputsHidden: false, allInputsHidden: false,
initUserVariables: {}, initUserVariables: {},
userIdFromQuery: '',
}) })
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext) export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)

View File

@@ -16,7 +16,7 @@ import type {
Feedback, Feedback,
} from '../types' } from '../types'
import { CONVERSATION_ID_INFO } from '../constants' import { CONVERSATION_ID_INFO } from '../constants'
import { buildChatItemTree, getProcessedSystemVariablesFromUrlParams, getRawInputsFromUrlParams, getRawUserVariablesFromUrlParams } from '../utils' import { buildChatItemTree, getProcessedSystemVariablesFromUrlParams, getRawInputsFromUrlParams, getRawUserVariablesFromUrlParams, getUserIdFromUrlParams } from '../utils'
import { addFileInfos, sortAgentSorts } from '../../../tools/utils' import { addFileInfos, sortAgentSorts } from '../../../tools/utils'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
import { import {
@@ -68,7 +68,7 @@ function getFormattedChatList(messages: any[]) {
return newChatList return newChatList
} }
export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { export const useChatWithHistory = (installedAppInfo?: InstalledApp, user_id?: string) => {
const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]) const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo])
const appInfo = useWebAppStore(s => s.appInfo) const appInfo = useWebAppStore(s => s.appInfo)
const appParams = useWebAppStore(s => s.appParams) const appParams = useWebAppStore(s => s.appParams)
@@ -108,11 +108,18 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
const appId = useMemo(() => appData?.app_id, [appData]) const appId = useMemo(() => appData?.app_id, [appData])
const [userId, setUserId] = useState<string>() const [userId, setUserId] = useState<string>()
const [userIdFromQuery, setUserIdFromQuery] = useState<string>('')
useEffect(() => { useEffect(() => {
getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => { if (user_id)
setUserId(user_id) setUserId(user_id)
}) else
}, []) getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => {
setUserId(user_id)
})
// Get userid from URL query parameter
const queryUserId = getUserIdFromUrlParams()
setUserIdFromQuery(queryUserId)
}, [user_id])
useEffect(() => { useEffect(() => {
const setLocaleFromProps = async () => { const setLocaleFromProps = async () => {
@@ -570,5 +577,6 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
} }
} }

View File

@@ -22,6 +22,7 @@ import { checkOrSetAccessToken } from '@/app/components/share/utils'
import AppUnavailable from '@/app/components/base/app-unavailable' import AppUnavailable from '@/app/components/base/app-unavailable'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import useDocumentTitle from '@/hooks/use-document-title' import useDocumentTitle from '@/hooks/use-document-title'
import { useSearchParams } from 'next/navigation'
type ChatWithHistoryProps = { type ChatWithHistoryProps = {
className?: string className?: string
@@ -40,6 +41,8 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
const isSidebarCollapsed = sidebarCollapseState const isSidebarCollapsed = sidebarCollapseState
const customConfig = appData?.custom_config const customConfig = appData?.custom_config
const site = appData?.site const site = appData?.site
const searchParams = useSearchParams()
const userid = searchParams.get('userid')
const [showSidePanel, setShowSidePanel] = useState(false) const [showSidePanel, setShowSidePanel] = useState(false)
@@ -109,6 +112,8 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
const media = useBreakpoints() const media = useBreakpoints()
const isMobile = media === MediaType.mobile const isMobile = media === MediaType.mobile
const themeBuilder = useThemeContext() const themeBuilder = useThemeContext()
const searchParams = useSearchParams()
const userid = searchParams.get('userid')
const { const {
appData, appData,
@@ -148,7 +153,8 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
} = useChatWithHistory(installedAppInfo) userIdFromQuery,
} = useChatWithHistory(installedAppInfo, userid)
return ( return (
<ChatWithHistoryContext.Provider value={{ <ChatWithHistoryContext.Provider value={{
@@ -191,6 +197,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
}}> }}>
<ChatWithHistory className={className} /> <ChatWithHistory className={className} />
</ChatWithHistoryContext.Provider> </ChatWithHistoryContext.Provider>

View File

@@ -46,6 +46,7 @@ type SendCallback = {
export const useChat = ( export const useChat = (
config?: ChatConfig, config?: ChatConfig,
user?: string,
formSettings?: { formSettings?: {
inputs: Inputs inputs: Inputs
inputsForm: InputForm[] inputsForm: InputForm[]
@@ -276,6 +277,7 @@ export const useChat = (
const bodyParams = { const bodyParams = {
response_mode: 'streaming', response_mode: 'streaming',
conversation_id: conversationId.current, conversation_id: conversationId.current,
user: user || '',
files: getProcessedFiles(files || []), files: getProcessedFiles(files || []),
query, query,
inputs: getProcessedInputs(inputs || {}, formSettings?.inputsForm || []), inputs: getProcessedInputs(inputs || {}, formSettings?.inputsForm || []),

View File

@@ -51,6 +51,7 @@ const ChatWrapper = () => {
setIsResponding, setIsResponding,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
} = useEmbeddedChatbotContext() } = useEmbeddedChatbotContext()
const appConfig = useMemo(() => { const appConfig = useMemo(() => {
const config = appParams || {} const config = appParams || {}
@@ -74,6 +75,7 @@ const ChatWrapper = () => {
suggestedQuestions, suggestedQuestions,
} = useChat( } = useChat(
appConfig, appConfig,
userIdFromQuery || initUserVariables?.user_id,
{ {
inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any, inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any,
inputsForm: inputsForms, inputsForm: inputsForms,

View File

@@ -56,6 +56,7 @@ export type EmbeddedChatbotContextValue = {
name?: string name?: string
avatar_url?: string avatar_url?: string
} }
userIdFromQuery?: string
} }
export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({
@@ -86,5 +87,6 @@ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>
setCurrentConversationInputs: noop, setCurrentConversationInputs: noop,
allInputsHidden: false, allInputsHidden: false,
initUserVariables: {}, initUserVariables: {},
userIdFromQuery: '',
}) })
export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext) export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext)

View File

@@ -15,7 +15,7 @@ import type {
Feedback, Feedback,
} from '../types' } from '../types'
import { CONVERSATION_ID_INFO } from '../constants' import { CONVERSATION_ID_INFO } from '../constants'
import { buildChatItemTree, getProcessedInputsFromUrlParams, getProcessedSystemVariablesFromUrlParams, getProcessedUserVariablesFromUrlParams } from '../utils' import { buildChatItemTree, getProcessedInputsFromUrlParams, getProcessedSystemVariablesFromUrlParams, getProcessedUserVariablesFromUrlParams, getUserIdFromUrlParams } from '../utils'
import { getProcessedFilesFromResponse } from '../../file-uploader/utils' import { getProcessedFilesFromResponse } from '../../file-uploader/utils'
import { import {
fetchAppInfo, fetchAppInfo,
@@ -82,11 +82,15 @@ export const useEmbeddedChatbot = () => {
const [userId, setUserId] = useState<string>() const [userId, setUserId] = useState<string>()
const [conversationId, setConversationId] = useState<string>() const [conversationId, setConversationId] = useState<string>()
const [userIdFromQuery, setUserIdFromQuery] = useState<string>('')
useEffect(() => { useEffect(() => {
getProcessedSystemVariablesFromUrlParams().then(({ user_id, conversation_id }) => { getProcessedSystemVariablesFromUrlParams().then(({ user_id, conversation_id }) => {
setUserId(user_id) setUserId(user_id)
setConversationId(conversation_id) setConversationId(conversation_id)
}) })
// Get userid from URL query parameter
const queryUserId = getUserIdFromUrlParams()
setUserIdFromQuery(queryUserId)
}, []) }, [])
useEffect(() => { useEffect(() => {
@@ -436,5 +440,6 @@ export const useEmbeddedChatbot = () => {
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
} }
} }

View File

@@ -132,6 +132,7 @@ const EmbeddedChatbotWrapper = () => {
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
} = useEmbeddedChatbot() } = useEmbeddedChatbot()
return <EmbeddedChatbotContext.Provider value={{ return <EmbeddedChatbotContext.Provider value={{
@@ -169,6 +170,7 @@ const EmbeddedChatbotWrapper = () => {
setCurrentConversationInputs, setCurrentConversationInputs,
allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
userIdFromQuery,
}}> }}>
<Chatbot /> <Chatbot />
</EmbeddedChatbotContext.Provider> </EmbeddedChatbotContext.Provider>

View File

@@ -88,6 +88,12 @@ async function getRawUserVariablesFromUrlParams(): Promise<Record<string, any>>
return userVariables return userVariables
} }
function getUserIdFromUrlParams(): string {
const urlParams = new URLSearchParams(window.location.search)
const userId = urlParams.get('userid')
return userId ? decodeURIComponent(userId) : ''
}
function isValidGeneratedAnswer(item?: ChatItem | ChatItemInTree): boolean { function isValidGeneratedAnswer(item?: ChatItem | ChatItemInTree): boolean {
return !!item && item.isAnswer && !item.id.startsWith('answer-placeholder-') && !item.isOpeningStatement return !!item && item.isAnswer && !item.id.startsWith('answer-placeholder-') && !item.isOpeningStatement
} }
@@ -236,6 +242,7 @@ export {
getProcessedSystemVariablesFromUrlParams, getProcessedSystemVariablesFromUrlParams,
getProcessedUserVariablesFromUrlParams, getProcessedUserVariablesFromUrlParams,
getRawUserVariablesFromUrlParams, getRawUserVariablesFromUrlParams,
getUserIdFromUrlParams,
isValidGeneratedAnswer, isValidGeneratedAnswer,
getLastAnswer, getLastAnswer,
buildChatItemTree, buildChatItemTree,

View File

@@ -41,6 +41,7 @@ import { AccessMode } from '@/models/access-control'
import { useGlobalPublicStore } from '@/context/global-public-context' import { useGlobalPublicStore } from '@/context/global-public-context'
import useDocumentTitle from '@/hooks/use-document-title' import useDocumentTitle from '@/hooks/use-document-title'
import { useWebAppStore } from '@/context/web-app-context' import { useWebAppStore } from '@/context/web-app-context'
import { getUserIdFromUrlParams } from '@/app/components/base/chat/utils'
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group. const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
enum TaskStatus { enum TaskStatus {
@@ -125,6 +126,13 @@ const TextGeneration: FC<IMainProps> = ({
transfer_methods: [TransferMethod.local_file], transfer_methods: [TransferMethod.local_file],
}) })
const [completionFiles, setCompletionFiles] = useState<VisionFile[]>([]) const [completionFiles, setCompletionFiles] = useState<VisionFile[]>([])
const [userIdFromQuery, setUserIdFromQuery] = useState<string>('')
useEffect(() => {
// Get userid from URL query parameter
const queryUserId = getUserIdFromUrlParams()
setUserIdFromQuery(queryUserId)
}, [])
const handleSend = () => { const handleSend = () => {
setIsCallBatchAPI(false) setIsCallBatchAPI(false)
@@ -433,6 +441,7 @@ const TextGeneration: FC<IMainProps> = ({
isShowTextToSpeech={!!textToSpeechConfig?.enabled} isShowTextToSpeech={!!textToSpeechConfig?.enabled}
siteInfo={siteInfo} siteInfo={siteInfo}
onRunStart={() => setResultExisted(true)} onRunStart={() => setResultExisted(true)}
userIdFromQuery={userIdFromQuery}
/>) />)
const renderBatchRes = () => { const renderBatchRes = () => {

View File

@@ -46,6 +46,7 @@ export type IResultProps = {
completionFiles: VisionFile[] completionFiles: VisionFile[]
siteInfo: SiteInfo | null siteInfo: SiteInfo | null
onRunStart: () => void onRunStart: () => void
userIdFromQuery?: string
} }
const Result: FC<IResultProps> = ({ const Result: FC<IResultProps> = ({
@@ -71,6 +72,7 @@ const Result: FC<IResultProps> = ({
completionFiles, completionFiles,
siteInfo, siteInfo,
onRunStart, onRunStart,
userIdFromQuery,
}) => { }) => {
const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false) const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
useEffect(() => { useEffect(() => {
@@ -162,6 +164,7 @@ const Result: FC<IResultProps> = ({
const data: Record<string, any> = { const data: Record<string, any> = {
inputs: formatBooleanInputs(promptConfig?.prompt_variables, inputs), inputs: formatBooleanInputs(promptConfig?.prompt_variables, inputs),
user: userIdFromQuery || '',
} }
if (visionConfig.enabled && completionFiles && completionFiles?.length > 0) { if (visionConfig.enabled && completionFiles && completionFiles?.length > 0) {
data.files = completionFiles.map((item) => { data.files = completionFiles.map((item) => {

View File

@@ -23,6 +23,7 @@ import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/cha
import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { useEventEmitterContextContext } from '@/context/event-emitter' import { useEventEmitterContextContext } from '@/context/event-emitter'
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
import { useAppContext } from '@/context/app-context'
type ChatWrapperProps = { type ChatWrapperProps = {
showConversationVariableModal: boolean showConversationVariableModal: boolean
@@ -42,6 +43,7 @@ const ChatWrapper = (
ref: React.RefObject<ChatWrapperRefType>; ref: React.RefObject<ChatWrapperRefType>;
}, },
) => { ) => {
const { userProfile } = useAppContext()
const nodes = useNodes<StartNodeType>() const nodes = useNodes<StartNodeType>()
const startNode = nodes.find(node => node.data.type === BlockEnum.Start) const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
const startVariables = startNode?.data.variables const startVariables = startNode?.data.variables
@@ -89,6 +91,7 @@ const ChatWrapper = (
setTargetMessageId, setTargetMessageId,
} = useChat( } = useChat(
config, config,
userProfile.id,
{ {
inputs, inputs,
inputsForm: (startVariables || []) as any, inputsForm: (startVariables || []) as any,