init
This commit is contained in:
23
src/contexts/AppContext.tsx
Normal file
23
src/contexts/AppContext.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { App } from 'obsidian'
|
||||
import React from 'react'
|
||||
|
||||
// App context
|
||||
const AppContext = React.createContext<App | undefined>(undefined)
|
||||
|
||||
export const AppProvider = ({
|
||||
children,
|
||||
app,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
app: App
|
||||
}) => {
|
||||
return <AppContext.Provider value={app}>{children}</AppContext.Provider>
|
||||
}
|
||||
|
||||
export const useApp = () => {
|
||||
const app = React.useContext(AppContext)
|
||||
if (!app) {
|
||||
throw new Error('useApp must be used within an AppProvider')
|
||||
}
|
||||
return app
|
||||
}
|
||||
46
src/contexts/DarkModeContext.tsx
Normal file
46
src/contexts/DarkModeContext.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import { useApp } from './AppContext'
|
||||
|
||||
type DarkModeContextType = {
|
||||
isDarkMode: boolean
|
||||
}
|
||||
|
||||
const DarkModeContext = createContext<DarkModeContextType | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
export function DarkModeProvider({ children }: { children: ReactNode }) {
|
||||
const [isDarkMode, setIsDarkMode] = useState(false)
|
||||
const app = useApp()
|
||||
|
||||
useEffect(() => {
|
||||
const handleDarkMode = () => {
|
||||
setIsDarkMode(document.body.classList.contains('theme-dark'))
|
||||
}
|
||||
handleDarkMode()
|
||||
app.workspace.on('css-change', handleDarkMode)
|
||||
return () => app.workspace.off('css-change', handleDarkMode)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<DarkModeContext.Provider value={{ isDarkMode }}>
|
||||
{children}
|
||||
</DarkModeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useDarkModeContext() {
|
||||
const context = useContext(DarkModeContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useDarkModeContext must be used within a DarkModeProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
58
src/contexts/DatabaseContext.tsx
Normal file
58
src/contexts/DatabaseContext.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
|
||||
import { DBManager } from '../database/database-manager'
|
||||
import { TemplateManager } from '../database/modules/template/template-manager'
|
||||
import { VectorManager } from '../database/modules/vector/vector-manager'
|
||||
|
||||
type DatabaseContextType = {
|
||||
getDatabaseManager: () => Promise<DBManager>
|
||||
getVectorManager: () => Promise<VectorManager>
|
||||
getTemplateManager: () => Promise<TemplateManager>
|
||||
}
|
||||
|
||||
const DatabaseContext = createContext<DatabaseContextType | null>(null)
|
||||
|
||||
export function DatabaseProvider({
|
||||
children,
|
||||
getDatabaseManager,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
getDatabaseManager: () => Promise<DBManager>
|
||||
}) {
|
||||
const getVectorManager = useCallback(async () => {
|
||||
return (await getDatabaseManager()).getVectorManager()
|
||||
}, [getDatabaseManager])
|
||||
|
||||
const getTemplateManager = useCallback(async () => {
|
||||
return (await getDatabaseManager()).getTemplateManager()
|
||||
}, [getDatabaseManager])
|
||||
|
||||
useEffect(() => {
|
||||
// start initialization of dbManager in the background
|
||||
void getDatabaseManager()
|
||||
}, [getDatabaseManager])
|
||||
|
||||
const value = useMemo(() => {
|
||||
return { getDatabaseManager, getVectorManager, getTemplateManager }
|
||||
}, [getDatabaseManager, getVectorManager, getTemplateManager])
|
||||
|
||||
return (
|
||||
<DatabaseContext.Provider value={value}>
|
||||
{children}
|
||||
</DatabaseContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useDatabase(): DatabaseContextType {
|
||||
const context = useContext(DatabaseContext)
|
||||
if (!context) {
|
||||
throw new Error('useDatabase must be used within a DatabaseProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
27
src/contexts/DialogContext.tsx
Normal file
27
src/contexts/DialogContext.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { createContext, useContext } from 'react'
|
||||
|
||||
const DialogContext = createContext<HTMLElement | null>(null)
|
||||
|
||||
export function DialogProvider({
|
||||
children,
|
||||
container,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
container: HTMLElement | null
|
||||
}) {
|
||||
return (
|
||||
<DialogContext.Provider value={container}>
|
||||
{children}
|
||||
</DialogContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useDialogContainer() {
|
||||
const context = useContext(DialogContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useDialogContainer must be used within a DialogContainerProvider',
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
135
src/contexts/LLMContext.tsx
Normal file
135
src/contexts/LLMContext.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import LLMManager from '../core/llm/manager'
|
||||
import { CustomLLMModel } from '../types/llm/model'
|
||||
import {
|
||||
LLMOptions,
|
||||
LLMRequestNonStreaming,
|
||||
LLMRequestStreaming,
|
||||
} from '../types/llm/request'
|
||||
import {
|
||||
LLMResponseNonStreaming,
|
||||
LLMResponseStreaming,
|
||||
} from '../types/llm/response'
|
||||
|
||||
import { useSettings } from './SettingsContext'
|
||||
|
||||
export type LLMContextType = {
|
||||
generateResponse: (
|
||||
model: CustomLLMModel,
|
||||
request: LLMRequestNonStreaming,
|
||||
options?: LLMOptions,
|
||||
) => Promise<LLMResponseNonStreaming>
|
||||
streamResponse: (
|
||||
model: CustomLLMModel,
|
||||
request: LLMRequestStreaming,
|
||||
options?: LLMOptions,
|
||||
) => Promise<AsyncIterable<LLMResponseStreaming>>
|
||||
chatModel: CustomLLMModel
|
||||
applyModel: CustomLLMModel
|
||||
}
|
||||
|
||||
const LLMContext = createContext<LLMContextType | null>(null)
|
||||
|
||||
export function LLMProvider({ children }: PropsWithChildren) {
|
||||
const [llmManager, setLLMManager] = useState<LLMManager | null>(null)
|
||||
const { settings } = useSettings()
|
||||
|
||||
const chatModel = useMemo((): CustomLLMModel => {
|
||||
const model = settings.activeModels.find(
|
||||
(option) => option.name === settings.chatModelId,
|
||||
)
|
||||
if (!model) {
|
||||
throw new Error('Invalid chat model ID')
|
||||
}
|
||||
return model
|
||||
}, [settings])
|
||||
|
||||
const applyModel = useMemo((): CustomLLMModel => {
|
||||
const model = settings.activeModels.find(
|
||||
(option) => option.name === settings.applyModelId,
|
||||
)
|
||||
if (!model) {
|
||||
throw new Error('Invalid apply model ID')
|
||||
}
|
||||
if (model.provider === 'ollama') {
|
||||
return {
|
||||
provider: 'ollama',
|
||||
baseURL: settings.ollamaApplyModel.baseUrl,
|
||||
model: settings.ollamaApplyModel.model,
|
||||
}
|
||||
}
|
||||
return model
|
||||
}, [settings])
|
||||
|
||||
useEffect(() => {
|
||||
const manager = new LLMManager({
|
||||
deepseek: settings.deepseekApiKey,
|
||||
openai: settings.openAIApiKey,
|
||||
anthropic: settings.anthropicApiKey,
|
||||
gemini: settings.geminiApiKey,
|
||||
groq: settings.groqApiKey,
|
||||
infio: settings.infioApiKey,
|
||||
})
|
||||
setLLMManager(manager)
|
||||
}, [
|
||||
settings.deepseekApiKey,
|
||||
settings.openAIApiKey,
|
||||
settings.anthropicApiKey,
|
||||
settings.geminiApiKey,
|
||||
settings.groqApiKey,
|
||||
settings.infioApiKey,
|
||||
])
|
||||
|
||||
const generateResponse = useCallback(
|
||||
async (
|
||||
model: CustomLLMModel,
|
||||
request: LLMRequestNonStreaming,
|
||||
options?: LLMOptions,
|
||||
) => {
|
||||
if (!llmManager) {
|
||||
throw new Error('LLMManager is not initialized')
|
||||
}
|
||||
return await llmManager.generateResponse(model, request, options)
|
||||
},
|
||||
[llmManager],
|
||||
)
|
||||
|
||||
const streamResponse = useCallback(
|
||||
async (
|
||||
model: CustomLLMModel,
|
||||
request: LLMRequestStreaming,
|
||||
options?: LLMOptions,
|
||||
) => {
|
||||
if (!llmManager) {
|
||||
throw new Error('LLMManager is not initialized')
|
||||
}
|
||||
return await llmManager.streamResponse(model, request, options)
|
||||
},
|
||||
[llmManager],
|
||||
)
|
||||
|
||||
return (
|
||||
<LLMContext.Provider
|
||||
value={{ generateResponse, streamResponse, chatModel, applyModel }}
|
||||
>
|
||||
{children}
|
||||
</LLMContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useLLM() {
|
||||
const context = useContext(LLMContext)
|
||||
if (!context) {
|
||||
throw new Error('useLLM must be used within an LLMProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
39
src/contexts/RAGContext.tsx
Normal file
39
src/contexts/RAGContext.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
|
||||
import { RAGEngine } from '../core/rag/rag-engine'
|
||||
|
||||
export type RAGContextType = {
|
||||
getRAGEngine: () => Promise<RAGEngine>
|
||||
}
|
||||
|
||||
const RAGContext = createContext<RAGContextType | null>(null)
|
||||
|
||||
export function RAGProvider({
|
||||
getRAGEngine,
|
||||
children,
|
||||
}: PropsWithChildren<{ getRAGEngine: () => Promise<RAGEngine> }>) {
|
||||
useEffect(() => {
|
||||
// start initialization of ragEngine in the background
|
||||
void getRAGEngine()
|
||||
}, [getRAGEngine])
|
||||
|
||||
const value = useMemo(() => {
|
||||
return { getRAGEngine }
|
||||
}, [getRAGEngine])
|
||||
|
||||
return <RAGContext.Provider value={value}>{children}</RAGContext.Provider>
|
||||
}
|
||||
|
||||
export function useRAG() {
|
||||
const context = useContext(RAGContext)
|
||||
if (!context) {
|
||||
throw new Error('useRAG must be used within a RAGProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
58
src/contexts/SettingsContext.tsx
Normal file
58
src/contexts/SettingsContext.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { InfioSettings } from '../types/settings'
|
||||
|
||||
type SettingsContextType = {
|
||||
settings: InfioSettings
|
||||
setSettings: (newSettings: InfioSettings) => void
|
||||
}
|
||||
|
||||
// Settings context
|
||||
const SettingsContext = React.createContext<SettingsContextType | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
export const SettingsProvider = ({
|
||||
children,
|
||||
settings: initialSettings,
|
||||
setSettings,
|
||||
addSettingsChangeListener,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
settings: InfioSettings
|
||||
setSettings: (newSettings: InfioSettings) => void
|
||||
addSettingsChangeListener: (
|
||||
listener: (newSettings: InfioSettings) => void,
|
||||
) => () => void
|
||||
}) => {
|
||||
const [settingsCached, setSettingsCached] = useState(initialSettings)
|
||||
|
||||
useEffect(() => {
|
||||
const removeListener = addSettingsChangeListener((newSettings) => {
|
||||
setSettingsCached(newSettings)
|
||||
})
|
||||
|
||||
return () => {
|
||||
removeListener()
|
||||
}
|
||||
}, [addSettingsChangeListener, setSettings])
|
||||
|
||||
const value = useMemo(
|
||||
() => ({ settings: settingsCached, setSettings }),
|
||||
[settingsCached, setSettings],
|
||||
)
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={value}>
|
||||
{children}
|
||||
</SettingsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSettings = () => {
|
||||
const settings = React.useContext(SettingsContext)
|
||||
if (!settings) {
|
||||
throw new Error('useSettings must be used within a SettingsProvider')
|
||||
}
|
||||
return settings
|
||||
}
|
||||
Reference in New Issue
Block a user