refactor(desktop): migrate provider state to zustand store
- Create ProviderStore for global provider state management - Refactor useProvider hook to use the store - Data fetched once at startup, shared across components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5ae86feb2b
commit
fc77cb89d3
2 changed files with 111 additions and 62 deletions
|
|
@ -1,14 +1,11 @@
|
|||
/**
|
||||
* Hook for managing LLM providers in the Desktop App.
|
||||
*
|
||||
* Provides functionality similar to CLI `/provider` command:
|
||||
* - List all providers with status
|
||||
* - Get current provider/model
|
||||
* - Switch provider/model
|
||||
* Uses the global ProviderStore for state management.
|
||||
* Data is fetched once at app startup and shared across all components.
|
||||
*/
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
|
||||
// Types are defined in electron-env.d.ts and available globally
|
||||
import { useCallback } from 'react'
|
||||
import { useProviderStore } from '../stores/provider'
|
||||
|
||||
interface UseProviderReturn {
|
||||
/** All providers with their status */
|
||||
|
|
@ -30,64 +27,24 @@ interface UseProviderReturn {
|
|||
}
|
||||
|
||||
export function useProvider(): UseProviderReturn {
|
||||
const [providers, setProviders] = useState<ProviderStatus[]>([])
|
||||
const [current, setCurrent] = useState<CurrentProviderInfo | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const [providerList, currentInfo] = await Promise.all([
|
||||
window.electronAPI.provider.list(),
|
||||
window.electronAPI.provider.current(),
|
||||
])
|
||||
|
||||
setProviders(providerList)
|
||||
setCurrent(currentInfo)
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
setError(message)
|
||||
console.error('[useProvider] Failed to load providers:', message)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Load providers on mount
|
||||
useEffect(() => {
|
||||
refresh()
|
||||
}, [refresh])
|
||||
|
||||
const setProvider = useCallback(async (providerId: string, modelId?: string) => {
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.provider.set(providerId, modelId)
|
||||
|
||||
if (result.ok) {
|
||||
// Refresh to update current status
|
||||
await refresh()
|
||||
return { ok: true }
|
||||
} else {
|
||||
setError(result.error ?? 'Unknown error')
|
||||
return { ok: false, error: result.error }
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
setError(message)
|
||||
return { ok: false, error: message }
|
||||
}
|
||||
}, [refresh])
|
||||
|
||||
const getProviderMeta = useCallback((providerId: string) => {
|
||||
return providers.find((p) => p.id === providerId)
|
||||
}, [providers])
|
||||
const {
|
||||
providers,
|
||||
current,
|
||||
loading,
|
||||
error,
|
||||
refresh,
|
||||
setProvider,
|
||||
} = useProviderStore()
|
||||
|
||||
const availableProviders = providers.filter((p) => p.available)
|
||||
|
||||
const getProviderMeta = useCallback(
|
||||
(providerId: string) => {
|
||||
return providers.find((p) => p.id === providerId)
|
||||
},
|
||||
[providers]
|
||||
)
|
||||
|
||||
return {
|
||||
providers,
|
||||
availableProviders,
|
||||
|
|
|
|||
92
apps/desktop/src/renderer/src/stores/provider.ts
Normal file
92
apps/desktop/src/renderer/src/stores/provider.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { create } from 'zustand'
|
||||
|
||||
interface ProviderStore {
|
||||
// State
|
||||
providers: ProviderStatus[]
|
||||
current: CurrentProviderInfo | null
|
||||
loading: boolean
|
||||
error: string | null
|
||||
initialized: boolean
|
||||
|
||||
// Actions
|
||||
fetch: () => Promise<void>
|
||||
setProvider: (providerId: string, modelId?: string) => Promise<{ ok: boolean; error?: string }>
|
||||
refresh: () => Promise<void>
|
||||
}
|
||||
|
||||
export const useProviderStore = create<ProviderStore>()((set, get) => ({
|
||||
providers: [],
|
||||
current: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
initialized: false,
|
||||
|
||||
fetch: async () => {
|
||||
// Skip if already initialized
|
||||
if (get().initialized) return
|
||||
|
||||
set({ loading: true, error: null })
|
||||
|
||||
try {
|
||||
const [providerList, currentInfo] = await Promise.all([
|
||||
window.electronAPI.provider.list(),
|
||||
window.electronAPI.provider.current(),
|
||||
])
|
||||
|
||||
set({
|
||||
providers: providerList,
|
||||
current: currentInfo,
|
||||
initialized: true,
|
||||
})
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
set({ error: message })
|
||||
console.error('[ProviderStore] Failed to load providers:', message)
|
||||
} finally {
|
||||
set({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
refresh: async () => {
|
||||
set({ loading: true, error: null })
|
||||
|
||||
try {
|
||||
const [providerList, currentInfo] = await Promise.all([
|
||||
window.electronAPI.provider.list(),
|
||||
window.electronAPI.provider.current(),
|
||||
])
|
||||
|
||||
set({
|
||||
providers: providerList,
|
||||
current: currentInfo,
|
||||
})
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
set({ error: message })
|
||||
console.error('[ProviderStore] Failed to refresh providers:', message)
|
||||
} finally {
|
||||
set({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
setProvider: async (providerId: string, modelId?: string) => {
|
||||
set({ error: null })
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.provider.set(providerId, modelId)
|
||||
|
||||
if (result.ok) {
|
||||
// Refresh to update current status
|
||||
await get().refresh()
|
||||
return { ok: true }
|
||||
} else {
|
||||
set({ error: result.error ?? 'Unknown error' })
|
||||
return { ok: false, error: result.error }
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
set({ error: message })
|
||||
return { ok: false, error: message }
|
||||
}
|
||||
},
|
||||
}))
|
||||
Loading…
Add table
Add a link
Reference in a new issue