Merge remote-tracking branch 'origin/main' into feat/queue-based-graph-engine

This commit is contained in:
-LAN-
2025-08-27 18:05:35 +08:00
24 changed files with 859 additions and 685 deletions

View File

@ -27,10 +27,11 @@ const ChunkDetailModal: FC<Props> = ({
}) => {
const { t } = useTranslation()
const { segment, score, child_chunks } = payload
const { position, content, sign_content, keywords, document } = segment
const { position, content, sign_content, keywords, document, answer } = segment
const isParentChildRetrieval = !!(child_chunks && child_chunks.length > 0)
const extension = document.name.split('.').slice(-1)[0] as FileAppearanceTypeEnum
const heighClassName = isParentChildRetrieval ? 'h-[min(627px,_80vh)] overflow-y-auto' : 'h-[min(539px,_80vh)] overflow-y-auto'
const labelPrefix = isParentChildRetrieval ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
return (
<Modal
title={t(`${i18nPrefix}.chunkDetail`)}
@ -45,7 +46,7 @@ const ChunkDetailModal: FC<Props> = ({
<div className='flex items-center justify-between'>
<div className='flex grow items-center space-x-2'>
<SegmentIndexTag
labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`}
labelPrefix={labelPrefix}
positionId={position}
className={cn('w-fit group-hover:opacity-100')}
/>
@ -57,11 +58,29 @@ const ChunkDetailModal: FC<Props> = ({
</div>
<Score value={score} />
</div>
<Markdown
className={cn('!mt-2 !text-text-secondary', heighClassName)}
content={sign_content || content}
customDisallowedElements={['input']}
/>
{!answer && (
<Markdown
className={cn('!mt-2 !text-text-secondary', heighClassName)}
content={sign_content || content}
customDisallowedElements={['input']}
/>
)}
{answer && (
<div>
<div className='flex gap-x-1'>
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>Q</div>
<div className={cn('body-md-regular text-text-secondary line-clamp-20')}>
{content}
</div>
</div>
<div className='flex gap-x-1'>
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>A</div>
<div className={cn('body-md-regular text-text-secondary line-clamp-20')}>
{answer}
</div>
</div>
</div>
)}
{!isParentChildRetrieval && keywords && keywords.length > 0 && (
<div className='mt-6'>
<div className='text-xs font-medium uppercase text-text-tertiary'>{t(`${i18nPrefix}.keyword`)}</div>

View File

@ -32,6 +32,7 @@ export const checkOrSetAccessToken = async (appCode?: string | null) => {
[userId || 'DEFAULT']: res.access_token,
}
localStorage.setItem('token', JSON.stringify(accessTokenJson))
localStorage.removeItem(CONVERSATION_ID_INFO)
}
}

View File

@ -11,6 +11,7 @@ import type { FC, PropsWithChildren } from 'react'
import { useEffect } from 'react'
import { useState } from 'react'
import { create } from 'zustand'
import { useGlobalPublicStore } from './global-public-context'
type WebAppStore = {
shareCode: string | null
@ -56,6 +57,7 @@ const getShareCodeFromPathname = (pathname: string): string | null => {
}
const WebAppStoreProvider: FC<PropsWithChildren> = ({ children }) => {
const isGlobalPending = useGlobalPublicStore(s => s.isGlobalPending)
const updateWebAppAccessMode = useWebAppStore(state => state.updateWebAppAccessMode)
const updateShareCode = useWebAppStore(state => state.updateShareCode)
const pathname = usePathname()
@ -69,7 +71,7 @@ const WebAppStoreProvider: FC<PropsWithChildren> = ({ children }) => {
}, [shareCode, updateShareCode])
const { isFetching, data: accessModeResult } = useGetWebAppAccessModeByCode(shareCode)
const [isFetchingAccessToken, setIsFetchingAccessToken] = useState(false)
const [isFetchingAccessToken, setIsFetchingAccessToken] = useState(true)
useEffect(() => {
if (accessModeResult?.accessMode) {
@ -86,7 +88,7 @@ const WebAppStoreProvider: FC<PropsWithChildren> = ({ children }) => {
}
}, [accessModeResult, updateWebAppAccessMode, shareCode])
if (isFetching || isFetchingAccessToken) {
if (isGlobalPending || isFetching || isFetchingAccessToken) {
return <div className='flex h-full w-full items-center justify-center'>
<Loading />
</div>

View File

@ -9,6 +9,7 @@ import tailwind from 'eslint-plugin-tailwindcss'
import reactHooks from 'eslint-plugin-react-hooks'
import sonar from 'eslint-plugin-sonarjs'
import oxlint from 'eslint-plugin-oxlint'
import next from '@next/eslint-plugin-next'
// import reactRefresh from 'eslint-plugin-react-refresh'
@ -63,12 +64,14 @@ export default combine(
}),
unicorn(),
node(),
// use nextjs config will break @eslint/config-inspector
// use `ESLINT_CONFIG_INSPECTOR=true pnpx @eslint/config-inspector` to check the config
// ...process.env.ESLINT_CONFIG_INSPECTOR
// ? []
// Next.js configuration
{
plugins: {
'@next/next': next,
},
rules: {
...next.configs.recommended.rules,
...next.configs['core-web-vitals'].rules,
// performance issue, and not used.
'@next/next/no-html-link-for-pages': 'off',
},

View File

@ -545,6 +545,7 @@ export type Segment = {
keywords: string[]
hit_count: number
index_node_hash: string
answer: string
}
export type Document = {

View File

@ -25,7 +25,7 @@
"start": "cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public && cross-env PORT=$npm_config_port HOSTNAME=$npm_config_host node .next/standalone/server.js",
"lint": "pnpx oxlint && pnpm eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache",
"lint-only-show-error": "pnpx oxlint && pnpm eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --quiet",
"fix": "next lint --fix",
"fix": "eslint --fix .",
"eslint-fix": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --fix",
"eslint-fix-only-show-error": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --fix --quiet",
"eslint-complexity": "eslint --rule 'complexity: [error, {max: 15}]' --quiet",
@ -103,14 +103,14 @@
"mime": "^4.0.4",
"mitt": "^3.0.1",
"negotiator": "^0.6.3",
"next": "~15.3.5",
"next": "15.5.0",
"next-themes": "^0.4.3",
"pinyin-pro": "^3.25.0",
"qrcode.react": "^4.2.0",
"qs": "^6.13.0",
"react": "~19.1.0",
"react": "19.1.1",
"react-18-input-autosize": "^3.0.0",
"react-dom": "~19.1.0",
"react-dom": "19.1.1",
"react-easy-crop": "^5.1.0",
"react-error-boundary": "^4.1.2",
"react-headless-pagination": "^1.1.6",
@ -161,9 +161,9 @@
"@happy-dom/jest-environment": "^17.4.4",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/bundle-analyzer": "^15.4.1",
"@next/eslint-plugin-next": "~15.4.5",
"@next/mdx": "~15.3.5",
"@next/bundle-analyzer": "15.5.0",
"@next/eslint-plugin-next": "15.5.0",
"@next/mdx": "15.5.0",
"@rgrove/parse-xml": "^4.1.0",
"@storybook/addon-essentials": "8.5.0",
"@storybook/addon-interactions": "8.5.0",
@ -185,8 +185,8 @@
"@types/negotiator": "^0.6.3",
"@types/node": "18.15.0",
"@types/qs": "^6.9.16",
"@types/react": "~19.1.8",
"@types/react-dom": "~19.1.6",
"@types/react": "19.1.11",
"@types/react-dom": "19.1.7",
"@types/react-slider": "^1.3.6",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/react-window": "^1.8.8",
@ -200,7 +200,7 @@
"code-inspector-plugin": "^0.18.1",
"cross-env": "^7.0.3",
"eslint": "^9.32.0",
"eslint-config-next": "~15.4.5",
"eslint-config-next": "15.5.0",
"eslint-plugin-oxlint": "^1.6.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
@ -223,8 +223,8 @@
"uglify-js": "^3.19.3"
},
"resolutions": {
"@types/react": "~19.1.8",
"@types/react-dom": "~19.1.6",
"@types/react": "19.1.11",
"@types/react-dom": "19.1.7",
"string-width": "4.2.3"
},
"lint-staged": {

1121
web/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -398,9 +398,7 @@ export const ssePost = async (
.then((res) => {
if (!/^[23]\d{2}$/.test(String(res.status))) {
if (res.status === 401) {
refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
ssePost(url, fetchOptions, otherOptions)
}).catch(() => {
if (isPublicAPI) {
res.json().then((data: any) => {
if (isPublicAPI) {
if (data.code === 'web_app_access_denied')
@ -417,7 +415,14 @@ export const ssePost = async (
}
}
})
})
}
else {
refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
ssePost(url, fetchOptions, otherOptions)
}).catch((err) => {
console.error(err)
})
}
}
else {
res.json().then((data) => {

View File

@ -1,20 +1,12 @@
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode } from '@/models/access-control'
import { useQuery } from '@tanstack/react-query'
import { fetchAppInfo, fetchAppMeta, fetchAppParams, getAppAccessModeByAppCode } from './share'
const NAME_SPACE = 'webapp'
export const useGetWebAppAccessModeByCode = (code: string | null) => {
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
return useQuery({
queryKey: [NAME_SPACE, 'appAccessMode', code],
queryFn: () => {
if (systemFeatures.webapp_auth.enabled === false) {
return {
accessMode: AccessMode.PUBLIC,
}
}
if (!code || code.length === 0)
return Promise.reject(new Error('App code is required to get access mode'))