feat: add instance config provider and map storage ids
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
AgentModelSelection,
|
||||
AgentModelSelections,
|
||||
ConfigFile,
|
||||
ModelPreference,
|
||||
@@ -107,6 +108,7 @@ export type WorkspaceFileSearchResponse = FileSystemEntry[]
|
||||
|
||||
export interface InstanceData {
|
||||
messageHistory: string[]
|
||||
agentModelSelections: AgentModelSelection
|
||||
}
|
||||
|
||||
export interface BinaryRecord {
|
||||
|
||||
@@ -13,7 +13,6 @@ const PreferencesSchema = z.object({
|
||||
lastUsedBinary: z.string().optional(),
|
||||
environmentVariables: z.record(z.string()).default({}),
|
||||
modelRecents: z.array(ModelPreferenceSchema).default([]),
|
||||
agentModelSelections: AgentModelSelectionsSchema.default({}),
|
||||
diffViewMode: z.enum(["split", "unified"]).default("split"),
|
||||
toolOutputExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
|
||||
diagnosticsExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
|
||||
|
||||
@@ -67,7 +67,11 @@ export function createHttpServer(deps: HttpServerDeps) {
|
||||
registerFilesystemRoutes(app, { fileSystemBrowser: deps.fileSystemBrowser })
|
||||
registerMetaRoutes(app, { serverMeta: deps.serverMeta })
|
||||
registerEventRoutes(app, { eventBus: deps.eventBus, registerClient: registerSseClient })
|
||||
registerStorageRoutes(app, { instanceStore: deps.instanceStore, eventBus: deps.eventBus })
|
||||
registerStorageRoutes(app, {
|
||||
instanceStore: deps.instanceStore,
|
||||
eventBus: deps.eventBus,
|
||||
workspaceManager: deps.workspaceManager,
|
||||
})
|
||||
registerInstanceProxyRoutes(app, { workspaceManager: deps.workspaceManager, logger: proxyLogger })
|
||||
|
||||
|
||||
|
||||
@@ -2,25 +2,36 @@ import { FastifyInstance } from "fastify"
|
||||
import { z } from "zod"
|
||||
import { InstanceStore } from "../../storage/instance-store"
|
||||
import { EventBus } from "../../events/bus"
|
||||
import { ModelPreferenceSchema } from "../../config/schema"
|
||||
import type { InstanceData } from "../../api-types"
|
||||
import { WorkspaceManager } from "../../workspaces/manager"
|
||||
|
||||
interface RouteDeps {
|
||||
instanceStore: InstanceStore
|
||||
eventBus: EventBus
|
||||
workspaceManager: WorkspaceManager
|
||||
}
|
||||
|
||||
const InstanceDataSchema = z.object({
|
||||
messageHistory: z.array(z.string()).default([]),
|
||||
agentModelSelections: z.record(z.string(), ModelPreferenceSchema).default({}),
|
||||
})
|
||||
|
||||
const EMPTY_INSTANCE_DATA: InstanceData = {
|
||||
messageHistory: [],
|
||||
agentModelSelections: {},
|
||||
}
|
||||
|
||||
export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
|
||||
const resolveStorageKey = (instanceId: string): string => {
|
||||
const workspace = deps.workspaceManager.get(instanceId)
|
||||
return workspace?.path ?? instanceId
|
||||
}
|
||||
|
||||
app.get<{ Params: { id: string } }>("/api/storage/instances/:id", async (request, reply) => {
|
||||
try {
|
||||
const data = await deps.instanceStore.read(request.params.id)
|
||||
const storageId = resolveStorageKey(request.params.id)
|
||||
const data = await deps.instanceStore.read(storageId)
|
||||
return data
|
||||
} catch (error) {
|
||||
reply.code(500)
|
||||
@@ -31,7 +42,8 @@ export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
|
||||
app.put<{ Params: { id: string } }>("/api/storage/instances/:id", async (request, reply) => {
|
||||
try {
|
||||
const body = InstanceDataSchema.parse(request.body ?? {})
|
||||
await deps.instanceStore.write(request.params.id, body)
|
||||
const storageId = resolveStorageKey(request.params.id)
|
||||
await deps.instanceStore.write(storageId, body)
|
||||
deps.eventBus.publish({ type: "instance.dataChanged", instanceId: request.params.id, data: body })
|
||||
reply.code(204)
|
||||
} catch (error) {
|
||||
@@ -42,7 +54,8 @@ export function registerStorageRoutes(app: FastifyInstance, deps: RouteDeps) {
|
||||
|
||||
app.delete<{ Params: { id: string } }>("/api/storage/instances/:id", async (request, reply) => {
|
||||
try {
|
||||
await deps.instanceStore.delete(request.params.id)
|
||||
const storageId = resolveStorageKey(request.params.id)
|
||||
await deps.instanceStore.delete(storageId)
|
||||
deps.eventBus.publish({ type: "instance.dataChanged", instanceId: request.params.id, data: EMPTY_INSTANCE_DATA })
|
||||
reply.code(204)
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { InstanceData } from "../api-types"
|
||||
|
||||
const DEFAULT_INSTANCE_DATA: InstanceData = {
|
||||
messageHistory: [],
|
||||
agentModelSelections: {},
|
||||
}
|
||||
|
||||
export class InstanceStore {
|
||||
|
||||
@@ -6,6 +6,7 @@ export type ConfigData = AppConfig
|
||||
|
||||
const DEFAULT_INSTANCE_DATA: InstanceData = {
|
||||
messageHistory: [],
|
||||
agentModelSelections: {},
|
||||
}
|
||||
|
||||
function isDeepEqual(a: unknown, b: unknown): boolean {
|
||||
@@ -150,9 +151,11 @@ export class ServerStorage {
|
||||
private normalizeInstanceData(data?: InstanceData | null): InstanceData {
|
||||
const source = data ?? DEFAULT_INSTANCE_DATA
|
||||
const messageHistory = Array.isArray(source.messageHistory) ? [...source.messageHistory] : []
|
||||
const agentModelSelections = { ...(source.agentModelSelections ?? {}) }
|
||||
return {
|
||||
...source,
|
||||
messageHistory,
|
||||
agentModelSelections,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { render } from "solid-js/web"
|
||||
import App from "./App"
|
||||
import { ThemeProvider } from "./lib/theme"
|
||||
import { ConfigProvider } from "./stores/preferences"
|
||||
import { InstanceConfigProvider } from "./stores/instance-config"
|
||||
import "./index.css"
|
||||
import "@git-diff-view/solid/styles/diff-view-pure.css"
|
||||
|
||||
@@ -14,9 +15,11 @@ if (!root) {
|
||||
render(
|
||||
() => (
|
||||
<ConfigProvider>
|
||||
<ThemeProvider>
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
<InstanceConfigProvider>
|
||||
<ThemeProvider>
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</InstanceConfigProvider>
|
||||
</ConfigProvider>
|
||||
),
|
||||
root,
|
||||
|
||||
138
packages/ui/src/stores/instance-config.tsx
Normal file
138
packages/ui/src/stores/instance-config.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { createContext, createMemo, createSignal, onCleanup, type Accessor, type ParentComponent, useContext } from "solid-js"
|
||||
import type { InstanceData } from "../../../cli/src/api-types"
|
||||
import { storage } from "../lib/storage"
|
||||
|
||||
const DEFAULT_INSTANCE_DATA: InstanceData = { messageHistory: [], agentModelSelections: {} }
|
||||
|
||||
const [instanceDataMap, setInstanceDataMap] = createSignal<Map<string, InstanceData>>(new Map())
|
||||
const loadPromises = new Map<string, Promise<void>>()
|
||||
const instanceSubscriptions = new Map<string, () => void>()
|
||||
|
||||
function cloneInstanceData(data?: InstanceData | null): InstanceData {
|
||||
const source = data ?? DEFAULT_INSTANCE_DATA
|
||||
return {
|
||||
...source,
|
||||
messageHistory: Array.isArray(source.messageHistory) ? [...source.messageHistory] : [],
|
||||
agentModelSelections: { ...(source.agentModelSelections ?? {}) },
|
||||
}
|
||||
}
|
||||
|
||||
function attachSubscription(instanceId: string) {
|
||||
if (instanceSubscriptions.has(instanceId)) return
|
||||
const unsubscribe = storage.onInstanceDataChanged(instanceId, (data) => {
|
||||
setInstanceData(instanceId, data)
|
||||
})
|
||||
instanceSubscriptions.set(instanceId, unsubscribe)
|
||||
}
|
||||
|
||||
function detachSubscription(instanceId: string) {
|
||||
const unsubscribe = instanceSubscriptions.get(instanceId)
|
||||
if (!unsubscribe) return
|
||||
unsubscribe()
|
||||
instanceSubscriptions.delete(instanceId)
|
||||
}
|
||||
|
||||
function setInstanceData(instanceId: string, data: InstanceData) {
|
||||
setInstanceDataMap((prev) => {
|
||||
const next = new Map(prev)
|
||||
next.set(instanceId, cloneInstanceData(data))
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
async function ensureInstanceConfig(instanceId: string): Promise<void> {
|
||||
if (!instanceId) return
|
||||
if (instanceDataMap().has(instanceId)) return
|
||||
if (loadPromises.has(instanceId)) {
|
||||
await loadPromises.get(instanceId)
|
||||
return
|
||||
}
|
||||
const promise = storage
|
||||
.loadInstanceData(instanceId)
|
||||
.then((data) => {
|
||||
setInstanceData(instanceId, data)
|
||||
attachSubscription(instanceId)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn("Failed to load instance data:", error)
|
||||
setInstanceData(instanceId, DEFAULT_INSTANCE_DATA)
|
||||
attachSubscription(instanceId)
|
||||
})
|
||||
.finally(() => {
|
||||
loadPromises.delete(instanceId)
|
||||
})
|
||||
loadPromises.set(instanceId, promise)
|
||||
await promise
|
||||
}
|
||||
|
||||
async function updateInstanceConfig(instanceId: string, mutator: (draft: InstanceData) => void): Promise<void> {
|
||||
if (!instanceId) return
|
||||
await ensureInstanceConfig(instanceId)
|
||||
const current = instanceDataMap().get(instanceId) ?? DEFAULT_INSTANCE_DATA
|
||||
const draft = cloneInstanceData(current)
|
||||
mutator(draft)
|
||||
try {
|
||||
await storage.saveInstanceData(instanceId, draft)
|
||||
} catch (error) {
|
||||
console.warn("Failed to persist instance data:", error)
|
||||
}
|
||||
setInstanceData(instanceId, draft)
|
||||
}
|
||||
|
||||
function getInstanceConfig(instanceId: string): InstanceData {
|
||||
return instanceDataMap().get(instanceId) ?? DEFAULT_INSTANCE_DATA
|
||||
}
|
||||
|
||||
function useInstanceConfig(instanceId: string): Accessor<InstanceData> {
|
||||
const context = useContext(InstanceConfigContext)
|
||||
if (!context) {
|
||||
throw new Error("useInstanceConfig must be used within InstanceConfigProvider")
|
||||
}
|
||||
return createMemo(() => instanceDataMap().get(instanceId) ?? DEFAULT_INSTANCE_DATA)
|
||||
}
|
||||
|
||||
function clearInstanceConfig(instanceId: string): void {
|
||||
setInstanceDataMap((prev) => {
|
||||
if (!prev.has(instanceId)) return prev
|
||||
const next = new Map(prev)
|
||||
next.delete(instanceId)
|
||||
return next
|
||||
})
|
||||
detachSubscription(instanceId)
|
||||
}
|
||||
|
||||
interface InstanceConfigContextValue {
|
||||
getInstanceConfig: typeof getInstanceConfig
|
||||
ensureInstanceConfig: typeof ensureInstanceConfig
|
||||
updateInstanceConfig: typeof updateInstanceConfig
|
||||
clearInstanceConfig: typeof clearInstanceConfig
|
||||
}
|
||||
|
||||
const InstanceConfigContext = createContext<InstanceConfigContextValue>()
|
||||
|
||||
const contextValue: InstanceConfigContextValue = {
|
||||
getInstanceConfig,
|
||||
ensureInstanceConfig,
|
||||
updateInstanceConfig,
|
||||
clearInstanceConfig,
|
||||
}
|
||||
|
||||
const InstanceConfigProvider: ParentComponent = (props) => {
|
||||
onCleanup(() => {
|
||||
for (const unsubscribe of instanceSubscriptions.values()) {
|
||||
unsubscribe()
|
||||
}
|
||||
instanceSubscriptions.clear()
|
||||
})
|
||||
|
||||
return <InstanceConfigContext.Provider value={contextValue}>{props.children}</InstanceConfigContext.Provider>
|
||||
}
|
||||
|
||||
export {
|
||||
InstanceConfigProvider,
|
||||
useInstanceConfig,
|
||||
ensureInstanceConfig as ensureInstanceConfigLoaded,
|
||||
getInstanceConfig,
|
||||
updateInstanceConfig,
|
||||
clearInstanceConfig,
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { sseManager } from "../lib/sse-manager"
|
||||
import { cliApi } from "../lib/api-client"
|
||||
import { cliEvents } from "../lib/cli-events"
|
||||
import type { WorkspaceDescriptor, WorkspaceEventPayload, WorkspaceLogEntry } from "../../../cli/src/api-types"
|
||||
import { ensureInstanceConfigLoaded } from "./instance-config"
|
||||
import {
|
||||
fetchSessions,
|
||||
fetchAgents,
|
||||
@@ -20,6 +21,7 @@ import { computeDisplayParts } from "./session-messages"
|
||||
import { withSession, setSessionPendingPermission } from "./session-state"
|
||||
import { setHasInstances } from "./ui"
|
||||
|
||||
|
||||
const [instances, setInstances] = createSignal<Map<string, Instance>>(new Map())
|
||||
const [activeInstanceId, setActiveInstanceId] = createSignal<string | null>(null)
|
||||
const [instanceLogs, setInstanceLogs] = createSignal<Map<string, LogEntry[]>>(new Map())
|
||||
@@ -116,6 +118,7 @@ async function hydrateInstanceData(instanceId: string) {
|
||||
await fetchSessions(instanceId)
|
||||
await fetchAgents(instanceId)
|
||||
await fetchProviders(instanceId)
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
const instance = instances().get(instanceId)
|
||||
if (!instance?.client) return
|
||||
await fetchCommands(instanceId, instance.client)
|
||||
|
||||
@@ -1,88 +1,35 @@
|
||||
import type { InstanceData } from "../../../cli/src/api-types"
|
||||
import { storage } from "../lib/storage"
|
||||
import {
|
||||
ensureInstanceConfigLoaded,
|
||||
getInstanceConfig,
|
||||
updateInstanceConfig,
|
||||
} from "./instance-config"
|
||||
|
||||
const MAX_HISTORY = 100
|
||||
|
||||
const instanceDataCache = new Map<string, InstanceData>()
|
||||
const instanceSubscriptions = new Map<string, () => void>()
|
||||
|
||||
export async function addToHistory(instanceId: string, text: string): Promise<void> {
|
||||
const data = await ensureInstanceData(instanceId)
|
||||
const nextHistory = [text, ...data.messageHistory]
|
||||
if (nextHistory.length > MAX_HISTORY) {
|
||||
nextHistory.length = MAX_HISTORY
|
||||
}
|
||||
|
||||
const nextData: InstanceData = {
|
||||
...data,
|
||||
messageHistory: nextHistory,
|
||||
}
|
||||
|
||||
instanceDataCache.set(instanceId, cloneInstanceData(nextData))
|
||||
|
||||
try {
|
||||
await storage.saveInstanceData(instanceId, nextData)
|
||||
} catch (err) {
|
||||
console.warn("Failed to persist message history:", err)
|
||||
}
|
||||
if (!instanceId || !text) return
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
await updateInstanceConfig(instanceId, (draft) => {
|
||||
const nextHistory = [text, ...(draft.messageHistory ?? [])]
|
||||
if (nextHistory.length > MAX_HISTORY) {
|
||||
nextHistory.length = MAX_HISTORY
|
||||
}
|
||||
draft.messageHistory = nextHistory
|
||||
})
|
||||
}
|
||||
|
||||
export async function getHistory(instanceId: string): Promise<string[]> {
|
||||
const data = await ensureInstanceData(instanceId)
|
||||
return [...data.messageHistory]
|
||||
if (!instanceId) return []
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
const data = getInstanceConfig(instanceId)
|
||||
return [...(data.messageHistory ?? [])]
|
||||
}
|
||||
|
||||
export async function clearHistory(instanceId: string): Promise<void> {
|
||||
const data = await ensureInstanceData(instanceId)
|
||||
const nextData: InstanceData = {
|
||||
...data,
|
||||
messageHistory: [],
|
||||
}
|
||||
|
||||
instanceDataCache.set(instanceId, cloneInstanceData(nextData))
|
||||
|
||||
try {
|
||||
await storage.saveInstanceData(instanceId, nextData)
|
||||
} catch (error) {
|
||||
console.warn("Failed to clear history:", error)
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureInstanceData(instanceId: string): Promise<InstanceData> {
|
||||
const cached = instanceDataCache.get(instanceId)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await storage.loadInstanceData(instanceId)
|
||||
const normalized = cloneInstanceData(data)
|
||||
instanceDataCache.set(instanceId, normalized)
|
||||
attachInstanceSubscription(instanceId)
|
||||
return normalized
|
||||
} catch (error) {
|
||||
console.warn("Failed to load history:", error)
|
||||
const fallback = cloneInstanceData({ messageHistory: [] })
|
||||
instanceDataCache.set(instanceId, fallback)
|
||||
attachInstanceSubscription(instanceId)
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
function attachInstanceSubscription(instanceId: string) {
|
||||
if (instanceSubscriptions.has(instanceId)) {
|
||||
return
|
||||
}
|
||||
const unsubscribe = storage.onInstanceDataChanged(instanceId, (data) => {
|
||||
instanceDataCache.set(instanceId, cloneInstanceData(data))
|
||||
if (!instanceId) return
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
await updateInstanceConfig(instanceId, (draft) => {
|
||||
draft.messageHistory = []
|
||||
})
|
||||
instanceSubscriptions.set(instanceId, unsubscribe)
|
||||
}
|
||||
|
||||
function cloneInstanceData(data?: InstanceData | null): InstanceData {
|
||||
const source: InstanceData = data ?? { messageHistory: [] }
|
||||
return {
|
||||
...source,
|
||||
messageHistory: Array.isArray(source.messageHistory) ? [...source.messageHistory] : [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { createContext, createMemo, createSignal, onMount, useContext } from "solid-js"
|
||||
import type { Accessor, ParentComponent } from "solid-js"
|
||||
import { storage, type ConfigData } from "../lib/storage"
|
||||
import {
|
||||
ensureInstanceConfigLoaded,
|
||||
getInstanceConfig,
|
||||
updateInstanceConfig as updateInstanceData,
|
||||
} from "./instance-config"
|
||||
|
||||
type DeepReadonly<T> = T extends (...args: any[]) => unknown
|
||||
? T
|
||||
@@ -27,7 +32,6 @@ export interface Preferences {
|
||||
lastUsedBinary?: string
|
||||
environmentVariables: Record<string, string>
|
||||
modelRecents: ModelPreference[]
|
||||
agentModelSelections: AgentModelSelections
|
||||
diffViewMode: DiffViewMode
|
||||
toolOutputExpansion: ExpansionPreference
|
||||
diagnosticsExpansion: ExpansionPreference
|
||||
@@ -53,7 +57,6 @@ const defaultPreferences: Preferences = {
|
||||
showThinkingBlocks: false,
|
||||
environmentVariables: {},
|
||||
modelRecents: [],
|
||||
agentModelSelections: {},
|
||||
diffViewMode: "split",
|
||||
toolOutputExpansion: "expanded",
|
||||
diagnosticsExpansion: "expanded",
|
||||
@@ -71,32 +74,24 @@ function deepEqual(a: unknown, b: unknown): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
function normalizePreferences(pref?: Partial<Preferences>): Preferences {
|
||||
function normalizePreferences(pref?: Partial<Preferences> & { agentModelSelections?: unknown }): Preferences {
|
||||
const sanitized = pref ?? {}
|
||||
const environmentVariables = {
|
||||
...defaultPreferences.environmentVariables,
|
||||
...(pref?.environmentVariables ?? {}),
|
||||
...(sanitized.environmentVariables ?? {}),
|
||||
}
|
||||
|
||||
const sourceModelRecents = pref?.modelRecents ?? defaultPreferences.modelRecents
|
||||
const sourceModelRecents = sanitized.modelRecents ?? defaultPreferences.modelRecents
|
||||
const modelRecents = sourceModelRecents.map((item) => ({ ...item }))
|
||||
|
||||
const sourceAgentSelections = pref?.agentModelSelections ?? defaultPreferences.agentModelSelections
|
||||
const agentModelSelections: AgentModelSelections = {}
|
||||
for (const [instanceId, selections] of Object.entries(sourceAgentSelections)) {
|
||||
agentModelSelections[instanceId] = Object.fromEntries(
|
||||
Object.entries(selections).map(([agentId, selection]) => [agentId, { ...selection }]),
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
showThinkingBlocks: pref?.showThinkingBlocks ?? defaultPreferences.showThinkingBlocks,
|
||||
lastUsedBinary: pref?.lastUsedBinary ?? defaultPreferences.lastUsedBinary,
|
||||
showThinkingBlocks: sanitized.showThinkingBlocks ?? defaultPreferences.showThinkingBlocks,
|
||||
lastUsedBinary: sanitized.lastUsedBinary ?? defaultPreferences.lastUsedBinary,
|
||||
environmentVariables,
|
||||
modelRecents,
|
||||
agentModelSelections,
|
||||
diffViewMode: pref?.diffViewMode ?? defaultPreferences.diffViewMode,
|
||||
toolOutputExpansion: pref?.toolOutputExpansion ?? defaultPreferences.toolOutputExpansion,
|
||||
diagnosticsExpansion: pref?.diagnosticsExpansion ?? defaultPreferences.diagnosticsExpansion,
|
||||
diffViewMode: sanitized.diffViewMode ?? defaultPreferences.diffViewMode,
|
||||
toolOutputExpansion: sanitized.toolOutputExpansion ?? defaultPreferences.toolOutputExpansion,
|
||||
diagnosticsExpansion: sanitized.diagnosticsExpansion ?? defaultPreferences.diagnosticsExpansion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +117,22 @@ function buildFallbackConfig(): ConfigData {
|
||||
return normalizeConfig()
|
||||
}
|
||||
|
||||
function removeLegacyAgentSelections(config?: ConfigData | null): { cleaned: ConfigData; migrated: boolean } {
|
||||
const migrated = Boolean((config?.preferences as { agentModelSelections?: unknown } | undefined)?.agentModelSelections)
|
||||
const cleanedConfig = normalizeConfig(config)
|
||||
return { cleaned: cleanedConfig, migrated }
|
||||
}
|
||||
|
||||
async function syncConfig(source?: ConfigData): Promise<void> {
|
||||
try {
|
||||
const configData = source ?? (await storage.loadConfig())
|
||||
applyConfig(configData)
|
||||
const loaded = source ?? (await storage.loadConfig())
|
||||
const { cleaned, migrated } = removeLegacyAgentSelections(loaded)
|
||||
applyConfig(cleaned)
|
||||
if (migrated) {
|
||||
void storage.updateConfig(cleaned).catch((error: unknown) => {
|
||||
console.error("Failed to persist legacy config cleanup:", error)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load config:", error)
|
||||
applyConfig(buildFallbackConfig())
|
||||
@@ -328,27 +335,25 @@ function addRecentModelPreference(model: ModelPreference): void {
|
||||
updatePreferences({ modelRecents: updated })
|
||||
}
|
||||
|
||||
function setAgentModelPreference(instanceId: string, agent: string, model: ModelPreference): void {
|
||||
async function setAgentModelPreference(instanceId: string, agent: string, model: ModelPreference): Promise<void> {
|
||||
if (!instanceId || !agent || !model.providerId || !model.modelId) return
|
||||
const selections = preferences().agentModelSelections ?? {}
|
||||
const instanceSelections = selections[instanceId] ?? {}
|
||||
const existing = instanceSelections[agent]
|
||||
if (existing && existing.providerId === model.providerId && existing.modelId === model.modelId) {
|
||||
return
|
||||
}
|
||||
updatePreferences({
|
||||
agentModelSelections: {
|
||||
...selections,
|
||||
[instanceId]: {
|
||||
...instanceSelections,
|
||||
[agent]: model,
|
||||
},
|
||||
},
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
await updateInstanceData(instanceId, (draft) => {
|
||||
const selections = { ...(draft.agentModelSelections ?? {}) }
|
||||
const existing = selections[agent]
|
||||
if (existing && existing.providerId === model.providerId && existing.modelId === model.modelId) {
|
||||
return
|
||||
}
|
||||
selections[agent] = model
|
||||
draft.agentModelSelections = selections
|
||||
})
|
||||
}
|
||||
|
||||
function getAgentModelPreference(instanceId: string, agent: string): ModelPreference | undefined {
|
||||
return preferences().agentModelSelections?.[instanceId]?.[agent]
|
||||
async function getAgentModelPreference(instanceId: string, agent: string): Promise<ModelPreference | undefined> {
|
||||
if (!instanceId || !agent) return undefined
|
||||
await ensureInstanceConfigLoaded(instanceId)
|
||||
const selections = getInstanceConfig(instanceId).agentModelSelections ?? {}
|
||||
return selections[agent]
|
||||
}
|
||||
|
||||
void ensureConfigLoaded().catch((error: unknown) => {
|
||||
|
||||
@@ -306,7 +306,7 @@ async function updateSessionAgent(instanceId: string, sessionId: string, agent:
|
||||
})
|
||||
|
||||
if (agent && shouldApplyModel) {
|
||||
setAgentModelPreference(instanceId, agent, nextModel)
|
||||
await setAgentModelPreference(instanceId, agent, nextModel)
|
||||
}
|
||||
|
||||
if (shouldApplyModel) {
|
||||
@@ -335,7 +335,7 @@ async function updateSessionModel(
|
||||
})
|
||||
|
||||
if (session.agent) {
|
||||
setAgentModelPreference(instanceId, session.agent, model)
|
||||
await setAgentModelPreference(instanceId, session.agent, model)
|
||||
}
|
||||
addRecentModelPreference(model)
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ async function createSession(instanceId: string, agent?: string): Promise<Sessio
|
||||
const defaultModel = await getDefaultModel(instanceId, selectedAgent)
|
||||
|
||||
if (selectedAgent && isModelValid(instanceId, defaultModel)) {
|
||||
setAgentModelPreference(instanceId, selectedAgent, defaultModel)
|
||||
await setAgentModelPreference(instanceId, selectedAgent, defaultModel)
|
||||
}
|
||||
|
||||
setLoading((prev) => {
|
||||
|
||||
@@ -32,13 +32,6 @@ async function getDefaultModel(
|
||||
const instanceProviders = providers().get(instanceId) || []
|
||||
const instanceAgents = agents().get(instanceId) || []
|
||||
|
||||
if (agentName) {
|
||||
const stored = getAgentModelPreference(instanceId, agentName)
|
||||
if (isModelValid(instanceId, stored)) {
|
||||
return stored
|
||||
}
|
||||
}
|
||||
|
||||
if (agentName) {
|
||||
const agent = instanceAgents.find((a) => a.name === agentName)
|
||||
if (agent && agent.model && isModelValid(instanceId, agent.model)) {
|
||||
@@ -47,6 +40,11 @@ async function getDefaultModel(
|
||||
modelId: agent.model.modelId,
|
||||
}
|
||||
}
|
||||
|
||||
const stored = await getAgentModelPreference(instanceId, agentName)
|
||||
if (isModelValid(instanceId, stored)) {
|
||||
return stored
|
||||
}
|
||||
}
|
||||
|
||||
const recent = getRecentModelPreferenceForInstance(instanceId)
|
||||
|
||||
Reference in New Issue
Block a user