initial layout
This commit is contained in:
parent
b309d34274
commit
a72c74dbf9
@ -10,6 +10,7 @@ export const route = {
|
|||||||
model_providers: '/settings/providers',
|
model_providers: '/settings/providers',
|
||||||
providers: '/settings/providers/$providerName',
|
providers: '/settings/providers/$providerName',
|
||||||
general: '/settings/general',
|
general: '/settings/general',
|
||||||
|
attachments: '/settings/attachments',
|
||||||
appearance: '/settings/appearance',
|
appearance: '/settings/appearance',
|
||||||
privacy: '/settings/privacy',
|
privacy: '/settings/privacy',
|
||||||
shortcuts: '/settings/shortcuts',
|
shortcuts: '/settings/shortcuts',
|
||||||
|
|||||||
@ -73,6 +73,12 @@ const SettingsMenu = () => {
|
|||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'common:attachments',
|
||||||
|
route: route.settings.attachments,
|
||||||
|
hasSubMenu: false,
|
||||||
|
isEnabled: PlatformFeatures[PlatformFeature.ATTACHMENTS],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'common:appearance',
|
title: 'common:appearance',
|
||||||
route: route.settings.appearance,
|
route: route.settings.appearance,
|
||||||
|
|||||||
45
web-app/src/hooks/useAttachments.ts
Normal file
45
web-app/src/hooks/useAttachments.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { create } from 'zustand'
|
||||||
|
import { persist, createJSONStorage } from 'zustand/middleware'
|
||||||
|
import { localStorageKey } from '@/constants/localStorage'
|
||||||
|
|
||||||
|
export type AttachmentsSettings = {
|
||||||
|
enabled: boolean
|
||||||
|
maxFileSizeMB: number
|
||||||
|
retrievalLimit: number
|
||||||
|
retrievalThreshold: number
|
||||||
|
chunkSizeTokens: number
|
||||||
|
overlapTokens: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttachmentsStore = AttachmentsSettings & {
|
||||||
|
setEnabled: (v: boolean) => void
|
||||||
|
setMaxFileSizeMB: (v: number) => void
|
||||||
|
setRetrievalLimit: (v: number) => void
|
||||||
|
setRetrievalThreshold: (v: number) => void
|
||||||
|
setChunkSizeTokens: (v: number) => void
|
||||||
|
setOverlapTokens: (v: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAttachments = create<AttachmentsStore>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
enabled: true,
|
||||||
|
maxFileSizeMB: 20,
|
||||||
|
retrievalLimit: 3,
|
||||||
|
retrievalThreshold: 0.5,
|
||||||
|
chunkSizeTokens: 512,
|
||||||
|
overlapTokens: 64,
|
||||||
|
setEnabled: (v) => set({ enabled: v }),
|
||||||
|
setMaxFileSizeMB: (v) => set({ maxFileSizeMB: Math.max(1, Math.min(200, Math.floor(v))) }),
|
||||||
|
setRetrievalLimit: (v) => set({ retrievalLimit: Math.max(1, Math.min(20, Math.floor(v))) }),
|
||||||
|
setRetrievalThreshold: (v) => set({ retrievalThreshold: Math.max(0, Math.min(1, v)) }),
|
||||||
|
setChunkSizeTokens: (v) => set({ chunkSizeTokens: Math.max(64, Math.min(8192, Math.floor(v))) }),
|
||||||
|
setOverlapTokens: (v) => set({ overlapTokens: Math.max(0, Math.min(1024, Math.floor(v))) }),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: `${localStorageKey.settingGeneral}-attachments`,
|
||||||
|
storage: createJSONStorage(() => localStorage),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@ -5,10 +5,14 @@ import { useAppState } from './useAppState'
|
|||||||
import { useToolAvailable } from './useToolAvailable'
|
import { useToolAvailable } from './useToolAvailable'
|
||||||
import { ExtensionManager } from '@/lib/extension'
|
import { ExtensionManager } from '@/lib/extension'
|
||||||
import { ExtensionTypeEnum, MCPExtension } from '@janhq/core'
|
import { ExtensionTypeEnum, MCPExtension } from '@janhq/core'
|
||||||
|
import { useAttachments } from './useAttachments'
|
||||||
|
import { PlatformFeatures } from '@/lib/platform/const'
|
||||||
|
import { PlatformFeature } from '@/lib/platform/types'
|
||||||
|
|
||||||
export const useTools = () => {
|
export const useTools = () => {
|
||||||
const updateTools = useAppState((state) => state.updateTools)
|
const updateTools = useAppState((state) => state.updateTools)
|
||||||
const { isDefaultsInitialized, setDefaultDisabledTools, markDefaultsAsInitialized } = useToolAvailable()
|
const { isDefaultsInitialized, setDefaultDisabledTools, markDefaultsAsInitialized } = useToolAvailable()
|
||||||
|
const attachmentsEnabled = useAttachments((s) => s.enabled)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function setTools() {
|
async function setTools() {
|
||||||
@ -19,11 +23,19 @@ export const useTools = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Fetch tools
|
// Fetch tools
|
||||||
const data = await getServiceHub().mcp().getTools()
|
const [mcpTools, ragTools] = await Promise.all([
|
||||||
updateTools(data)
|
getServiceHub().mcp().getTools(),
|
||||||
|
// Only include RAG tools when attachments feature is enabled
|
||||||
|
useAttachments.getState().enabled && PlatformFeatures[PlatformFeature.ATTACHMENTS]
|
||||||
|
? getServiceHub().rag().getTools()
|
||||||
|
: Promise.resolve([]),
|
||||||
|
])
|
||||||
|
|
||||||
|
const combined = [...mcpTools, ...ragTools]
|
||||||
|
updateTools(combined)
|
||||||
|
|
||||||
// Initialize default disabled tools for new users (only once)
|
// Initialize default disabled tools for new users (only once)
|
||||||
if (!isDefaultsInitialized() && data.length > 0 && mcpExtension?.getDefaultDisabledTools) {
|
if (!isDefaultsInitialized() && combined.length > 0 && mcpExtension?.getDefaultDisabledTools) {
|
||||||
const defaultDisabled = await mcpExtension.getDefaultDisabledTools()
|
const defaultDisabled = await mcpExtension.getDefaultDisabledTools()
|
||||||
if (defaultDisabled.length > 0) {
|
if (defaultDisabled.length > 0) {
|
||||||
setDefaultDisabledTools(defaultDisabled)
|
setDefaultDisabledTools(defaultDisabled)
|
||||||
@ -31,7 +43,7 @@ export const useTools = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch MCP tools:', error)
|
console.error('Failed to fetch tools:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTools()
|
setTools()
|
||||||
@ -41,9 +53,14 @@ export const useTools = () => {
|
|||||||
// Unsubscribe from the event when the component unmounts
|
// Unsubscribe from the event when the component unmounts
|
||||||
unsubscribe = unsub
|
unsubscribe = unsub
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('Failed to set up MCP update listener:', error)
|
console.error('Failed to set up tools update listener:', error)
|
||||||
})
|
})
|
||||||
return unsubscribe
|
return unsubscribe
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// Refresh tools when attachments feature toggles
|
||||||
|
useEffect(() => {
|
||||||
|
getServiceHub().events().emit(SystemEvent.MCP_UPDATE)
|
||||||
|
}, [attachmentsEnabled])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,6 +78,10 @@ export const PlatformFeatures: Record<PlatformFeature, boolean> = {
|
|||||||
// First message persisted thread - enabled for web only
|
// First message persisted thread - enabled for web only
|
||||||
[PlatformFeature.FIRST_MESSAGE_PERSISTED_THREAD]: !isPlatformTauri(),
|
[PlatformFeature.FIRST_MESSAGE_PERSISTED_THREAD]: !isPlatformTauri(),
|
||||||
|
|
||||||
// Temporary chat mode - enabled for web only
|
// Temporary chat mode - enabled for web only
|
||||||
[PlatformFeature.TEMPORARY_CHAT]: !isPlatformTauri(),
|
[PlatformFeature.TEMPORARY_CHAT]: !isPlatformTauri(),
|
||||||
|
|
||||||
|
// Attachments/RAG UI and tooling - desktop only for now
|
||||||
|
[PlatformFeature.ATTACHMENTS]:
|
||||||
|
isPlatformTauri() && !isPlatformIOS() && !isPlatformAndroid(),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,4 +71,7 @@ export enum PlatformFeature {
|
|||||||
|
|
||||||
// Temporary chat mode - web-only feature for ephemeral conversations like ChatGPT
|
// Temporary chat mode - web-only feature for ephemeral conversations like ChatGPT
|
||||||
TEMPORARY_CHAT = 'temporaryChat',
|
TEMPORARY_CHAT = 'temporaryChat',
|
||||||
|
|
||||||
|
// Attachments/RAG UI and tooling (desktop-only for now)
|
||||||
|
ATTACHMENTS = 'attachments',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
"local_api_server": "Local API Server",
|
"local_api_server": "Local API Server",
|
||||||
"https_proxy": "HTTPS Proxy",
|
"https_proxy": "HTTPS Proxy",
|
||||||
"extensions": "Extensions",
|
"extensions": "Extensions",
|
||||||
|
"attachments": "Attachments",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"modelProviders": "Model Providers",
|
"modelProviders": "Model Providers",
|
||||||
@ -369,4 +370,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -253,6 +253,25 @@
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"title": "Extensions"
|
"title": "Extensions"
|
||||||
},
|
},
|
||||||
|
"attachments": {
|
||||||
|
"subtitle": "Configure document attachments, size limits, and retrieval behavior.",
|
||||||
|
"featureTitle": "Feature",
|
||||||
|
"enable": "Enable Attachments",
|
||||||
|
"enableDesc": "Allow uploading and indexing documents for retrieval.",
|
||||||
|
"limitsTitle": "Limits",
|
||||||
|
"maxFile": "Max File Size (MB)",
|
||||||
|
"maxFileDesc": "Maximum size per file. Enforced at upload and processing time.",
|
||||||
|
"retrievalTitle": "Retrieval",
|
||||||
|
"topK": "Top-K",
|
||||||
|
"topKDesc": "Maximum citations to return.",
|
||||||
|
"threshold": "Affinity Threshold",
|
||||||
|
"thresholdDesc": "Minimum similarity score to consider relevant (0-1).",
|
||||||
|
"chunkingTitle": "Chunking",
|
||||||
|
"chunkSize": "Chunk Size (tokens)",
|
||||||
|
"chunkSizeDesc": "Approximate max tokens per chunk for embeddings.",
|
||||||
|
"chunkOverlap": "Overlap (tokens)",
|
||||||
|
"chunkOverlapDesc": "Token overlap between consecutive chunks."
|
||||||
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
"changeDataFolder": {
|
"changeDataFolder": {
|
||||||
"title": "Change Data Folder Location",
|
"title": "Change Data Folder Location",
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { Route as SettingsHttpsProxyImport } from './routes/settings/https-proxy
|
|||||||
import { Route as SettingsHardwareImport } from './routes/settings/hardware'
|
import { Route as SettingsHardwareImport } from './routes/settings/hardware'
|
||||||
import { Route as SettingsGeneralImport } from './routes/settings/general'
|
import { Route as SettingsGeneralImport } from './routes/settings/general'
|
||||||
import { Route as SettingsExtensionsImport } from './routes/settings/extensions'
|
import { Route as SettingsExtensionsImport } from './routes/settings/extensions'
|
||||||
|
import { Route as SettingsAttachmentsImport } from './routes/settings/attachments'
|
||||||
import { Route as SettingsAppearanceImport } from './routes/settings/appearance'
|
import { Route as SettingsAppearanceImport } from './routes/settings/appearance'
|
||||||
import { Route as ProjectProjectIdImport } from './routes/project/$projectId'
|
import { Route as ProjectProjectIdImport } from './routes/project/$projectId'
|
||||||
import { Route as LocalApiServerLogsImport } from './routes/local-api-server/logs'
|
import { Route as LocalApiServerLogsImport } from './routes/local-api-server/logs'
|
||||||
@ -126,6 +127,12 @@ const SettingsExtensionsRoute = SettingsExtensionsImport.update({
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const SettingsAttachmentsRoute = SettingsAttachmentsImport.update({
|
||||||
|
id: '/settings/attachments',
|
||||||
|
path: '/settings/attachments',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const SettingsAppearanceRoute = SettingsAppearanceImport.update({
|
const SettingsAppearanceRoute = SettingsAppearanceImport.update({
|
||||||
id: '/settings/appearance',
|
id: '/settings/appearance',
|
||||||
path: '/settings/appearance',
|
path: '/settings/appearance',
|
||||||
@ -229,6 +236,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof SettingsAppearanceImport
|
preLoaderRoute: typeof SettingsAppearanceImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/settings/attachments': {
|
||||||
|
id: '/settings/attachments'
|
||||||
|
path: '/settings/attachments'
|
||||||
|
fullPath: '/settings/attachments'
|
||||||
|
preLoaderRoute: typeof SettingsAttachmentsImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/settings/extensions': {
|
'/settings/extensions': {
|
||||||
id: '/settings/extensions'
|
id: '/settings/extensions'
|
||||||
path: '/settings/extensions'
|
path: '/settings/extensions'
|
||||||
@ -341,6 +355,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
|
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
'/settings/general': typeof SettingsGeneralRoute
|
||||||
'/settings/hardware': typeof SettingsHardwareRoute
|
'/settings/hardware': typeof SettingsHardwareRoute
|
||||||
@ -366,6 +381,7 @@ export interface FileRoutesByTo {
|
|||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
|
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
'/settings/general': typeof SettingsGeneralRoute
|
||||||
'/settings/hardware': typeof SettingsHardwareRoute
|
'/settings/hardware': typeof SettingsHardwareRoute
|
||||||
@ -392,6 +408,7 @@ export interface FileRoutesById {
|
|||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
|
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
'/settings/general': typeof SettingsGeneralRoute
|
||||||
'/settings/hardware': typeof SettingsHardwareRoute
|
'/settings/hardware': typeof SettingsHardwareRoute
|
||||||
@ -419,6 +436,7 @@ export interface FileRouteTypes {
|
|||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/project/$projectId'
|
| '/project/$projectId'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
|
| '/settings/attachments'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
| '/settings/general'
|
| '/settings/general'
|
||||||
| '/settings/hardware'
|
| '/settings/hardware'
|
||||||
@ -443,6 +461,7 @@ export interface FileRouteTypes {
|
|||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/project/$projectId'
|
| '/project/$projectId'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
|
| '/settings/attachments'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
| '/settings/general'
|
| '/settings/general'
|
||||||
| '/settings/hardware'
|
| '/settings/hardware'
|
||||||
@ -467,6 +486,7 @@ export interface FileRouteTypes {
|
|||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/project/$projectId'
|
| '/project/$projectId'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
|
| '/settings/attachments'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
| '/settings/general'
|
| '/settings/general'
|
||||||
| '/settings/hardware'
|
| '/settings/hardware'
|
||||||
@ -493,6 +513,7 @@ export interface RootRouteChildren {
|
|||||||
LocalApiServerLogsRoute: typeof LocalApiServerLogsRoute
|
LocalApiServerLogsRoute: typeof LocalApiServerLogsRoute
|
||||||
ProjectProjectIdRoute: typeof ProjectProjectIdRoute
|
ProjectProjectIdRoute: typeof ProjectProjectIdRoute
|
||||||
SettingsAppearanceRoute: typeof SettingsAppearanceRoute
|
SettingsAppearanceRoute: typeof SettingsAppearanceRoute
|
||||||
|
SettingsAttachmentsRoute: typeof SettingsAttachmentsRoute
|
||||||
SettingsExtensionsRoute: typeof SettingsExtensionsRoute
|
SettingsExtensionsRoute: typeof SettingsExtensionsRoute
|
||||||
SettingsGeneralRoute: typeof SettingsGeneralRoute
|
SettingsGeneralRoute: typeof SettingsGeneralRoute
|
||||||
SettingsHardwareRoute: typeof SettingsHardwareRoute
|
SettingsHardwareRoute: typeof SettingsHardwareRoute
|
||||||
@ -518,6 +539,7 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
LocalApiServerLogsRoute: LocalApiServerLogsRoute,
|
LocalApiServerLogsRoute: LocalApiServerLogsRoute,
|
||||||
ProjectProjectIdRoute: ProjectProjectIdRoute,
|
ProjectProjectIdRoute: ProjectProjectIdRoute,
|
||||||
SettingsAppearanceRoute: SettingsAppearanceRoute,
|
SettingsAppearanceRoute: SettingsAppearanceRoute,
|
||||||
|
SettingsAttachmentsRoute: SettingsAttachmentsRoute,
|
||||||
SettingsExtensionsRoute: SettingsExtensionsRoute,
|
SettingsExtensionsRoute: SettingsExtensionsRoute,
|
||||||
SettingsGeneralRoute: SettingsGeneralRoute,
|
SettingsGeneralRoute: SettingsGeneralRoute,
|
||||||
SettingsHardwareRoute: SettingsHardwareRoute,
|
SettingsHardwareRoute: SettingsHardwareRoute,
|
||||||
@ -552,6 +574,7 @@ export const routeTree = rootRoute
|
|||||||
"/local-api-server/logs",
|
"/local-api-server/logs",
|
||||||
"/project/$projectId",
|
"/project/$projectId",
|
||||||
"/settings/appearance",
|
"/settings/appearance",
|
||||||
|
"/settings/attachments",
|
||||||
"/settings/extensions",
|
"/settings/extensions",
|
||||||
"/settings/general",
|
"/settings/general",
|
||||||
"/settings/hardware",
|
"/settings/hardware",
|
||||||
@ -592,6 +615,9 @@ export const routeTree = rootRoute
|
|||||||
"/settings/appearance": {
|
"/settings/appearance": {
|
||||||
"filePath": "settings/appearance.tsx"
|
"filePath": "settings/appearance.tsx"
|
||||||
},
|
},
|
||||||
|
"/settings/attachments": {
|
||||||
|
"filePath": "settings/attachments.tsx"
|
||||||
|
},
|
||||||
"/settings/extensions": {
|
"/settings/extensions": {
|
||||||
"filePath": "settings/extensions.tsx"
|
"filePath": "settings/extensions.tsx"
|
||||||
},
|
},
|
||||||
|
|||||||
185
web-app/src/routes/settings/attachments.tsx
Normal file
185
web-app/src/routes/settings/attachments.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import { route } from '@/constants/routes'
|
||||||
|
import SettingsMenu from '@/containers/SettingsMenu'
|
||||||
|
import HeaderPage from '@/containers/HeaderPage'
|
||||||
|
import { Card, CardItem } from '@/containers/Card'
|
||||||
|
import { Switch } from '@/components/ui/switch'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { useAttachments } from '@/hooks/useAttachments'
|
||||||
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||||
|
import { PlatformGuard } from '@/lib/platform/PlatformGuard'
|
||||||
|
import { PlatformFeature } from '@/lib/platform/types'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const Route = createFileRoute(route.settings.attachments as any)({
|
||||||
|
component: AttachmentsSettings,
|
||||||
|
})
|
||||||
|
|
||||||
|
function NumberInput({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step = 1,
|
||||||
|
}: {
|
||||||
|
value: number
|
||||||
|
onChange: (v: number) => void
|
||||||
|
min?: number
|
||||||
|
max?: number
|
||||||
|
step?: number
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={value}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
onChange={(e) => onChange(Number(e.target.value))}
|
||||||
|
className="w-28"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function AttachmentsSettings() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
enabled,
|
||||||
|
maxFileSizeMB,
|
||||||
|
retrievalLimit,
|
||||||
|
retrievalThreshold,
|
||||||
|
chunkSizeTokens,
|
||||||
|
overlapTokens,
|
||||||
|
setEnabled,
|
||||||
|
setMaxFileSizeMB,
|
||||||
|
setRetrievalLimit,
|
||||||
|
setRetrievalThreshold,
|
||||||
|
setChunkSizeTokens,
|
||||||
|
setOverlapTokens,
|
||||||
|
} = useAttachments()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlatformGuard feature={PlatformFeature.ATTACHMENTS}>
|
||||||
|
<div className="flex h-full w-full">
|
||||||
|
<div className="hidden sm:flex">
|
||||||
|
<SettingsMenu />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<HeaderPage
|
||||||
|
title={t('common:attachments') || 'Attachments'}
|
||||||
|
subtitle={
|
||||||
|
t('settings:attachments.subtitle') ||
|
||||||
|
'Configure document attachments, size limits, and retrieval behavior.'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className="px-6 py-3 space-y-4">
|
||||||
|
<Card title={t('settings:attachments.featureTitle') || 'Feature'}>
|
||||||
|
<CardItem
|
||||||
|
title={t('settings:attachments.enable') || 'Enable Attachments'}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.enableDesc') ||
|
||||||
|
'Allow uploading and indexing documents for retrieval.'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title={t('settings:attachments.limitsTitle') || 'Limits'}>
|
||||||
|
<CardItem
|
||||||
|
title={t('settings:attachments.maxFile') || 'Max File Size (MB)'}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.maxFileDesc') ||
|
||||||
|
'Maximum size per file. Enforced at upload and processing time.'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<NumberInput
|
||||||
|
value={maxFileSizeMB}
|
||||||
|
onChange={setMaxFileSizeMB}
|
||||||
|
min={1}
|
||||||
|
max={200}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title={t('settings:attachments.retrievalTitle') || 'Retrieval'}>
|
||||||
|
<CardItem
|
||||||
|
title={t('settings:attachments.topK') || 'Top-K'}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.topKDesc') || 'Maximum citations to return.'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<NumberInput
|
||||||
|
value={retrievalLimit}
|
||||||
|
onChange={setRetrievalLimit}
|
||||||
|
min={1}
|
||||||
|
max={10}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<CardItem
|
||||||
|
title={
|
||||||
|
t('settings:attachments.threshold') || 'Affinity Threshold'
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.thresholdDesc') ||
|
||||||
|
'Minimum similarity score to consider relevant (0-1).'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={retrievalThreshold}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
onChange={(e) => setRetrievalThreshold(Number(e.target.value))}
|
||||||
|
className="w-28"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title={t('settings:attachments.chunkingTitle') || 'Chunking'}>
|
||||||
|
<CardItem
|
||||||
|
title={
|
||||||
|
t('settings:attachments.chunkSize') || 'Chunk Size (tokens)'
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.chunkSizeDesc') ||
|
||||||
|
'Approximate max tokens per chunk for embeddings.'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<NumberInput
|
||||||
|
value={chunkSizeTokens}
|
||||||
|
onChange={setChunkSizeTokens}
|
||||||
|
min={64}
|
||||||
|
max={8192}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<CardItem
|
||||||
|
title={
|
||||||
|
t('settings:attachments.chunkOverlap') || 'Overlap (tokens)'
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
t('settings:attachments.chunkOverlapDesc') ||
|
||||||
|
'Token overlap between consecutive chunks.'
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<NumberInput
|
||||||
|
value={overlapTokens}
|
||||||
|
onChange={setOverlapTokens}
|
||||||
|
min={0}
|
||||||
|
max={1024}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PlatformGuard>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -27,6 +27,8 @@ import { DefaultPathService } from './path/default'
|
|||||||
import { DefaultCoreService } from './core/default'
|
import { DefaultCoreService } from './core/default'
|
||||||
import { DefaultDeepLinkService } from './deeplink/default'
|
import { DefaultDeepLinkService } from './deeplink/default'
|
||||||
import { DefaultProjectsService } from './projects/default'
|
import { DefaultProjectsService } from './projects/default'
|
||||||
|
import { DefaultRAGService } from './rag/default'
|
||||||
|
import type { RAGService } from './rag/types'
|
||||||
|
|
||||||
// Import service types
|
// Import service types
|
||||||
import type { ThemeService } from './theme/types'
|
import type { ThemeService } from './theme/types'
|
||||||
@ -70,6 +72,7 @@ export interface ServiceHub {
|
|||||||
core(): CoreService
|
core(): CoreService
|
||||||
deeplink(): DeepLinkService
|
deeplink(): DeepLinkService
|
||||||
projects(): ProjectsService
|
projects(): ProjectsService
|
||||||
|
rag(): RAGService
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlatformServiceHub implements ServiceHub {
|
class PlatformServiceHub implements ServiceHub {
|
||||||
@ -92,6 +95,7 @@ class PlatformServiceHub implements ServiceHub {
|
|||||||
private coreService: CoreService = new DefaultCoreService()
|
private coreService: CoreService = new DefaultCoreService()
|
||||||
private deepLinkService: DeepLinkService = new DefaultDeepLinkService()
|
private deepLinkService: DeepLinkService = new DefaultDeepLinkService()
|
||||||
private projectsService: ProjectsService = new DefaultProjectsService()
|
private projectsService: ProjectsService = new DefaultProjectsService()
|
||||||
|
private ragService: RAGService = new DefaultRAGService()
|
||||||
private initialized = false
|
private initialized = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,6 +306,11 @@ class PlatformServiceHub implements ServiceHub {
|
|||||||
this.ensureInitialized()
|
this.ensureInitialized()
|
||||||
return this.projectsService
|
return this.projectsService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rag(): RAGService {
|
||||||
|
this.ensureInitialized()
|
||||||
|
return this.ragService
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initializeServiceHub(): Promise<ServiceHub> {
|
export async function initializeServiceHub(): Promise<ServiceHub> {
|
||||||
|
|||||||
9
web-app/src/services/rag/default.ts
Normal file
9
web-app/src/services/rag/default.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { RAGService } from './types'
|
||||||
|
import type { MCPTool } from '@janhq/core'
|
||||||
|
|
||||||
|
export class DefaultRAGService implements RAGService {
|
||||||
|
async getTools(): Promise<MCPTool[]> {
|
||||||
|
// Temporarily return no tools; real tools will be provided by rag-extension later
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
7
web-app/src/services/rag/types.ts
Normal file
7
web-app/src/services/rag/types.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { MCPTool } from '@janhq/core'
|
||||||
|
|
||||||
|
export interface RAGService {
|
||||||
|
// Return tools exposed by RAG-related extensions (e.g., retrieval, list_attachments)
|
||||||
|
getTools(): Promise<MCPTool[]>
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user