initial layout
This commit is contained in:
parent
b309d34274
commit
a72c74dbf9
@ -10,6 +10,7 @@ export const route = {
|
||||
model_providers: '/settings/providers',
|
||||
providers: '/settings/providers/$providerName',
|
||||
general: '/settings/general',
|
||||
attachments: '/settings/attachments',
|
||||
appearance: '/settings/appearance',
|
||||
privacy: '/settings/privacy',
|
||||
shortcuts: '/settings/shortcuts',
|
||||
|
||||
@ -73,6 +73,12 @@ const SettingsMenu = () => {
|
||||
hasSubMenu: false,
|
||||
isEnabled: true,
|
||||
},
|
||||
{
|
||||
title: 'common:attachments',
|
||||
route: route.settings.attachments,
|
||||
hasSubMenu: false,
|
||||
isEnabled: PlatformFeatures[PlatformFeature.ATTACHMENTS],
|
||||
},
|
||||
{
|
||||
title: 'common: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 { ExtensionManager } from '@/lib/extension'
|
||||
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 = () => {
|
||||
const updateTools = useAppState((state) => state.updateTools)
|
||||
const { isDefaultsInitialized, setDefaultDisabledTools, markDefaultsAsInitialized } = useToolAvailable()
|
||||
const attachmentsEnabled = useAttachments((s) => s.enabled)
|
||||
|
||||
useEffect(() => {
|
||||
async function setTools() {
|
||||
@ -19,11 +23,19 @@ export const useTools = () => {
|
||||
)
|
||||
|
||||
// Fetch tools
|
||||
const data = await getServiceHub().mcp().getTools()
|
||||
updateTools(data)
|
||||
const [mcpTools, ragTools] = await Promise.all([
|
||||
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)
|
||||
if (!isDefaultsInitialized() && data.length > 0 && mcpExtension?.getDefaultDisabledTools) {
|
||||
if (!isDefaultsInitialized() && combined.length > 0 && mcpExtension?.getDefaultDisabledTools) {
|
||||
const defaultDisabled = await mcpExtension.getDefaultDisabledTools()
|
||||
if (defaultDisabled.length > 0) {
|
||||
setDefaultDisabledTools(defaultDisabled)
|
||||
@ -31,7 +43,7 @@ export const useTools = () => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch MCP tools:', error)
|
||||
console.error('Failed to fetch tools:', error)
|
||||
}
|
||||
}
|
||||
setTools()
|
||||
@ -41,9 +53,14 @@ export const useTools = () => {
|
||||
// Unsubscribe from the event when the component unmounts
|
||||
unsubscribe = unsub
|
||||
}).catch((error) => {
|
||||
console.error('Failed to set up MCP update listener:', error)
|
||||
console.error('Failed to set up tools update listener:', error)
|
||||
})
|
||||
return unsubscribe
|
||||
// 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
|
||||
[PlatformFeature.FIRST_MESSAGE_PERSISTED_THREAD]: !isPlatformTauri(),
|
||||
|
||||
// Temporary chat mode - enabled for web only
|
||||
// Temporary chat mode - enabled for web only
|
||||
[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 = 'temporaryChat',
|
||||
|
||||
// Attachments/RAG UI and tooling (desktop-only for now)
|
||||
ATTACHMENTS = 'attachments',
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"local_api_server": "Local API Server",
|
||||
"https_proxy": "HTTPS Proxy",
|
||||
"extensions": "Extensions",
|
||||
"attachments": "Attachments",
|
||||
"general": "General",
|
||||
"settings": "Settings",
|
||||
"modelProviders": "Model Providers",
|
||||
@ -369,4 +370,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -253,6 +253,25 @@
|
||||
"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": {
|
||||
"changeDataFolder": {
|
||||
"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 SettingsGeneralImport } from './routes/settings/general'
|
||||
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 ProjectProjectIdImport } from './routes/project/$projectId'
|
||||
import { Route as LocalApiServerLogsImport } from './routes/local-api-server/logs'
|
||||
@ -126,6 +127,12 @@ const SettingsExtensionsRoute = SettingsExtensionsImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SettingsAttachmentsRoute = SettingsAttachmentsImport.update({
|
||||
id: '/settings/attachments',
|
||||
path: '/settings/attachments',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SettingsAppearanceRoute = SettingsAppearanceImport.update({
|
||||
id: '/settings/appearance',
|
||||
path: '/settings/appearance',
|
||||
@ -229,6 +236,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof SettingsAppearanceImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/settings/attachments': {
|
||||
id: '/settings/attachments'
|
||||
path: '/settings/attachments'
|
||||
fullPath: '/settings/attachments'
|
||||
preLoaderRoute: typeof SettingsAttachmentsImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/settings/extensions': {
|
||||
id: '/settings/extensions'
|
||||
path: '/settings/extensions'
|
||||
@ -341,6 +355,7 @@ export interface FileRoutesByFullPath {
|
||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||
'/settings/general': typeof SettingsGeneralRoute
|
||||
'/settings/hardware': typeof SettingsHardwareRoute
|
||||
@ -366,6 +381,7 @@ export interface FileRoutesByTo {
|
||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||
'/settings/general': typeof SettingsGeneralRoute
|
||||
'/settings/hardware': typeof SettingsHardwareRoute
|
||||
@ -392,6 +408,7 @@ export interface FileRoutesById {
|
||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||
'/project/$projectId': typeof ProjectProjectIdRoute
|
||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||
'/settings/attachments': typeof SettingsAttachmentsRoute
|
||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||
'/settings/general': typeof SettingsGeneralRoute
|
||||
'/settings/hardware': typeof SettingsHardwareRoute
|
||||
@ -419,6 +436,7 @@ export interface FileRouteTypes {
|
||||
| '/local-api-server/logs'
|
||||
| '/project/$projectId'
|
||||
| '/settings/appearance'
|
||||
| '/settings/attachments'
|
||||
| '/settings/extensions'
|
||||
| '/settings/general'
|
||||
| '/settings/hardware'
|
||||
@ -443,6 +461,7 @@ export interface FileRouteTypes {
|
||||
| '/local-api-server/logs'
|
||||
| '/project/$projectId'
|
||||
| '/settings/appearance'
|
||||
| '/settings/attachments'
|
||||
| '/settings/extensions'
|
||||
| '/settings/general'
|
||||
| '/settings/hardware'
|
||||
@ -467,6 +486,7 @@ export interface FileRouteTypes {
|
||||
| '/local-api-server/logs'
|
||||
| '/project/$projectId'
|
||||
| '/settings/appearance'
|
||||
| '/settings/attachments'
|
||||
| '/settings/extensions'
|
||||
| '/settings/general'
|
||||
| '/settings/hardware'
|
||||
@ -493,6 +513,7 @@ export interface RootRouteChildren {
|
||||
LocalApiServerLogsRoute: typeof LocalApiServerLogsRoute
|
||||
ProjectProjectIdRoute: typeof ProjectProjectIdRoute
|
||||
SettingsAppearanceRoute: typeof SettingsAppearanceRoute
|
||||
SettingsAttachmentsRoute: typeof SettingsAttachmentsRoute
|
||||
SettingsExtensionsRoute: typeof SettingsExtensionsRoute
|
||||
SettingsGeneralRoute: typeof SettingsGeneralRoute
|
||||
SettingsHardwareRoute: typeof SettingsHardwareRoute
|
||||
@ -518,6 +539,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
LocalApiServerLogsRoute: LocalApiServerLogsRoute,
|
||||
ProjectProjectIdRoute: ProjectProjectIdRoute,
|
||||
SettingsAppearanceRoute: SettingsAppearanceRoute,
|
||||
SettingsAttachmentsRoute: SettingsAttachmentsRoute,
|
||||
SettingsExtensionsRoute: SettingsExtensionsRoute,
|
||||
SettingsGeneralRoute: SettingsGeneralRoute,
|
||||
SettingsHardwareRoute: SettingsHardwareRoute,
|
||||
@ -552,6 +574,7 @@ export const routeTree = rootRoute
|
||||
"/local-api-server/logs",
|
||||
"/project/$projectId",
|
||||
"/settings/appearance",
|
||||
"/settings/attachments",
|
||||
"/settings/extensions",
|
||||
"/settings/general",
|
||||
"/settings/hardware",
|
||||
@ -592,6 +615,9 @@ export const routeTree = rootRoute
|
||||
"/settings/appearance": {
|
||||
"filePath": "settings/appearance.tsx"
|
||||
},
|
||||
"/settings/attachments": {
|
||||
"filePath": "settings/attachments.tsx"
|
||||
},
|
||||
"/settings/extensions": {
|
||||
"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 { DefaultDeepLinkService } from './deeplink/default'
|
||||
import { DefaultProjectsService } from './projects/default'
|
||||
import { DefaultRAGService } from './rag/default'
|
||||
import type { RAGService } from './rag/types'
|
||||
|
||||
// Import service types
|
||||
import type { ThemeService } from './theme/types'
|
||||
@ -70,6 +72,7 @@ export interface ServiceHub {
|
||||
core(): CoreService
|
||||
deeplink(): DeepLinkService
|
||||
projects(): ProjectsService
|
||||
rag(): RAGService
|
||||
}
|
||||
|
||||
class PlatformServiceHub implements ServiceHub {
|
||||
@ -92,6 +95,7 @@ class PlatformServiceHub implements ServiceHub {
|
||||
private coreService: CoreService = new DefaultCoreService()
|
||||
private deepLinkService: DeepLinkService = new DefaultDeepLinkService()
|
||||
private projectsService: ProjectsService = new DefaultProjectsService()
|
||||
private ragService: RAGService = new DefaultRAGService()
|
||||
private initialized = false
|
||||
|
||||
/**
|
||||
@ -302,6 +306,11 @@ class PlatformServiceHub implements ServiceHub {
|
||||
this.ensureInitialized()
|
||||
return this.projectsService
|
||||
}
|
||||
|
||||
rag(): RAGService {
|
||||
this.ensureInitialized()
|
||||
return this.ragService
|
||||
}
|
||||
}
|
||||
|
||||
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