feat: improve embedding sys.user_id and conversion id info usage (#18035)

This commit is contained in:
Panpan 2025-04-16 21:08:13 +08:00 committed by GitHub
parent 358fd28c28
commit 44cdb3dcea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 85 additions and 47 deletions

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 } from '../utils' import { buildChatItemTree, getProcessedSystemVariablesFromUrlParams } 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 {
@ -106,6 +106,13 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
}, [isInstalledApp, installedAppInfo, appInfo]) }, [isInstalledApp, installedAppInfo, appInfo])
const appId = useMemo(() => appData?.app_id, [appData]) const appId = useMemo(() => appData?.app_id, [appData])
const [userId, setUserId] = useState<string>()
useEffect(() => {
getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => {
setUserId(user_id)
})
}, [])
useEffect(() => { useEffect(() => {
if (appData?.site.default_language) if (appData?.site.default_language)
changeLanguage(appData.site.default_language) changeLanguage(appData.site.default_language)
@ -124,18 +131,24 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
setSidebarCollapseState(localState === 'collapsed') setSidebarCollapseState(localState === 'collapsed')
} }
}, [appId]) }, [appId])
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, { const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, {
defaultValue: {}, defaultValue: {},
}) })
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo]) const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || '', [appId, conversationIdInfo, userId])
const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { const handleConversationIdInfoChange = useCallback((changeConversationId: string) => {
if (appId) { if (appId) {
let prevValue = conversationIdInfo?.[appId || '']
if (typeof prevValue === 'string')
prevValue = {}
setConversationIdInfo({ setConversationIdInfo({
...conversationIdInfo, ...conversationIdInfo,
[appId || '']: changeConversationId, [appId || '']: {
...prevValue,
[userId || 'DEFAULT']: changeConversationId,
},
}) })
} }
}, [appId, conversationIdInfo, setConversationIdInfo]) }, [appId, conversationIdInfo, setConversationIdInfo, userId])
const [newConversationId, setNewConversationId] = useState('') const [newConversationId, setNewConversationId] = useState('')
const chatShouldReloadKey = useMemo(() => { const chatShouldReloadKey = useMemo(() => {

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 } from '../utils' import { buildChatItemTree, getProcessedInputsFromUrlParams, getProcessedSystemVariablesFromUrlParams } from '../utils'
import { getProcessedFilesFromResponse } from '../../file-uploader/utils' import { getProcessedFilesFromResponse } from '../../file-uploader/utils'
import { import {
fetchAppInfo, fetchAppInfo,
@ -72,23 +72,36 @@ export const useEmbeddedChatbot = () => {
}, [appInfo]) }, [appInfo])
const appId = useMemo(() => appData?.app_id, [appData]) const appId = useMemo(() => appData?.app_id, [appData])
const [userId, setUserId] = useState<string>()
useEffect(() => {
getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => {
setUserId(user_id)
})
}, [])
useEffect(() => { useEffect(() => {
if (appInfo?.site.default_language) if (appInfo?.site.default_language)
changeLanguage(appInfo.site.default_language) changeLanguage(appInfo.site.default_language)
}, [appInfo]) }, [appInfo])
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, { const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, {
defaultValue: {}, defaultValue: {},
}) })
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo]) const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || '', [appId, conversationIdInfo, userId])
const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { const handleConversationIdInfoChange = useCallback((changeConversationId: string) => {
if (appId) { if (appId) {
let prevValue = conversationIdInfo?.[appId || '']
if (typeof prevValue === 'string')
prevValue = {}
setConversationIdInfo({ setConversationIdInfo({
...conversationIdInfo, ...conversationIdInfo,
[appId || '']: changeConversationId, [appId || '']: {
...prevValue,
[userId || 'DEFAULT']: changeConversationId,
},
}) })
} }
}, [appId, conversationIdInfo, setConversationIdInfo]) }, [appId, conversationIdInfo, setConversationIdInfo, userId])
const [newConversationId, setNewConversationId] = useState('') const [newConversationId, setNewConversationId] = useState('')
const chatShouldReloadKey = useMemo(() => { const chatShouldReloadKey = useMemo(() => {

View File

@ -2,29 +2,44 @@ import { CONVERSATION_ID_INFO } from '../base/chat/constants'
import { fetchAccessToken } from '@/service/share' import { fetchAccessToken } from '@/service/share'
import { getProcessedSystemVariablesFromUrlParams } from '../base/chat/utils' import { getProcessedSystemVariablesFromUrlParams } from '../base/chat/utils'
export const isTokenV1 = (token: Record<string, any>) => {
return !token.version
}
export const getInitialTokenV2 = (): Record<string, any> => ({
version: 2,
})
export const checkOrSetAccessToken = async () => { export const checkOrSetAccessToken = async () => {
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) const userId = (await getProcessedSystemVariablesFromUrlParams()).user_id
let accessTokenJson = { [sharedToken]: '' } const accessToken = localStorage.getItem('token') || JSON.stringify(getInitialTokenV2())
let accessTokenJson = getInitialTokenV2()
try { try {
accessTokenJson = JSON.parse(accessToken) accessTokenJson = JSON.parse(accessToken)
if (isTokenV1(accessTokenJson))
accessTokenJson = getInitialTokenV2()
} }
catch { catch {
} }
if (!accessTokenJson[sharedToken]) { if (!accessTokenJson[sharedToken]?.[userId || 'DEFAULT']) {
const sysUserId = (await getProcessedSystemVariablesFromUrlParams()).user_id const res = await fetchAccessToken(sharedToken, userId)
const res = await fetchAccessToken(sharedToken, sysUserId) accessTokenJson[sharedToken] = {
accessTokenJson[sharedToken] = res.access_token ...accessTokenJson[sharedToken],
[userId || 'DEFAULT']: res.access_token,
}
localStorage.setItem('token', JSON.stringify(accessTokenJson)) localStorage.setItem('token', JSON.stringify(accessTokenJson))
} }
} }
export const setAccessToken = async (sharedToken: string, token: string) => { export const setAccessToken = async (sharedToken: string, token: string, user_id?: string) => {
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) const accessToken = localStorage.getItem('token') || JSON.stringify(getInitialTokenV2())
let accessTokenJson = { [sharedToken]: '' } let accessTokenJson = getInitialTokenV2()
try { try {
accessTokenJson = JSON.parse(accessToken) accessTokenJson = JSON.parse(accessToken)
if (isTokenV1(accessTokenJson))
accessTokenJson = getInitialTokenV2()
} }
catch { catch {
@ -32,17 +47,22 @@ export const setAccessToken = async (sharedToken: string, token: string) => {
localStorage.removeItem(CONVERSATION_ID_INFO) localStorage.removeItem(CONVERSATION_ID_INFO)
accessTokenJson[sharedToken] = token accessTokenJson[sharedToken] = {
...accessTokenJson[sharedToken],
[user_id || 'DEFAULT']: token,
}
localStorage.setItem('token', JSON.stringify(accessTokenJson)) localStorage.setItem('token', JSON.stringify(accessTokenJson))
} }
export const removeAccessToken = () => { export const removeAccessToken = () => {
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) const accessToken = localStorage.getItem('token') || JSON.stringify(getInitialTokenV2())
let accessTokenJson = { [sharedToken]: '' } let accessTokenJson = getInitialTokenV2()
try { try {
accessTokenJson = JSON.parse(accessToken) accessTokenJson = JSON.parse(accessToken)
if (isTokenV1(accessTokenJson))
accessTokenJson = getInitialTokenV2()
} }
catch { catch {

View File

@ -287,9 +287,9 @@ const handleStream = (
const baseFetch = base const baseFetch = base
export const upload = (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => { export const upload = async (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => {
const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
const token = getAccessToken(isPublicAPI) const token = await getAccessToken(isPublicAPI)
const defaultOptions = { const defaultOptions = {
method: 'POST', method: 'POST',
url: (url ? `${urlPrefix}${url}` : `${urlPrefix}/files/upload`) + (searchParams || ''), url: (url ? `${urlPrefix}${url}` : `${urlPrefix}/files/upload`) + (searchParams || ''),
@ -324,7 +324,7 @@ export const upload = (options: any, isPublicAPI?: boolean, url?: string, search
}) })
} }
export const ssePost = ( export const ssePost = async (
url: string, url: string,
fetchOptions: FetchOptionType, fetchOptions: FetchOptionType,
otherOptions: IOtherOptions, otherOptions: IOtherOptions,
@ -385,7 +385,7 @@ export const ssePost = (
if (body) if (body)
options.body = JSON.stringify(body) options.body = JSON.stringify(body)
const accessToken = getAccessToken(isPublicAPI) const accessToken = await getAccessToken(isPublicAPI)
; (options.headers as Headers).set('Authorization', `Bearer ${accessToken}`) ; (options.headers as Headers).set('Authorization', `Bearer ${accessToken}`)
globalThis.fetch(urlWithPrefix, options as RequestInit) globalThis.fetch(urlWithPrefix, options as RequestInit)

View File

@ -3,6 +3,8 @@ import ky from 'ky'
import type { IOtherOptions } from './base' import type { IOtherOptions } from './base'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import { API_PREFIX, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config' import { API_PREFIX, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config'
import { getInitialTokenV2, isTokenV1 } from '@/app/components/share/utils'
import { getProcessedSystemVariablesFromUrlParams } from '@/app/components/base/chat/utils'
const TIME_OUT = 100000 const TIME_OUT = 100000
@ -67,44 +69,34 @@ const beforeErrorToast = (otherOptions: IOtherOptions): BeforeErrorHook => {
} }
} }
export const getPublicToken = () => { export async function getAccessToken(isPublicAPI?: boolean) {
let token = ''
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
let accessTokenJson = { [sharedToken]: '' }
try {
accessTokenJson = JSON.parse(accessToken)
}
catch { }
token = accessTokenJson[sharedToken]
return token || ''
}
export function getAccessToken(isPublicAPI?: boolean) {
if (isPublicAPI) { if (isPublicAPI) {
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) const userId = (await getProcessedSystemVariablesFromUrlParams()).user_id
let accessTokenJson = { [sharedToken]: '' } const accessToken = localStorage.getItem('token') || JSON.stringify({ version: 2 })
let accessTokenJson: Record<string, any> = { version: 2 }
try { try {
accessTokenJson = JSON.parse(accessToken) accessTokenJson = JSON.parse(accessToken)
if (isTokenV1(accessTokenJson))
accessTokenJson = getInitialTokenV2()
} }
catch { catch {
} }
return accessTokenJson[sharedToken] return accessTokenJson[sharedToken]?.[userId || 'DEFAULT']
} }
else { else {
return localStorage.getItem('console_token') || '' return localStorage.getItem('console_token') || ''
} }
} }
const beforeRequestPublicAuthorization: BeforeRequestHook = (request) => { const beforeRequestPublicAuthorization: BeforeRequestHook = async (request) => {
const token = getAccessToken(true) const token = await getAccessToken(true)
request.headers.set('Authorization', `Bearer ${token}`) request.headers.set('Authorization', `Bearer ${token}`)
} }
const beforeRequestAuthorization: BeforeRequestHook = (request) => { const beforeRequestAuthorization: BeforeRequestHook = async (request) => {
const accessToken = getAccessToken() const accessToken = await getAccessToken()
request.headers.set('Authorization', `Bearer ${accessToken}`) request.headers.set('Authorization', `Bearer ${accessToken}`)
} }