feat: add model selector, skills/memory pages, and config management

- Add model selector in sidebar that discovers models from auth.json credential pool
- Add per-session model display (badge in chat header and session list)
- Add skills browser page and memory editor page
- Add BFF routes for skills, memory, and config model management
- Model switching updates config.yaml provider field to bypass env auto-detection
- Refactor Settings page, simplify ChatInput with file upload
- Add attachment upload support via BFF /upload endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-04-12 23:23:50 +08:00
parent ee9f56dfbd
commit 5887462f7d
21 changed files with 1941 additions and 106 deletions
+21 -11
View File
@@ -1,19 +1,18 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { checkHealth, fetchModels } from '@/api/system'
import type { Model } from '@/api/system'
import { checkHealth, fetchAvailableModels, updateDefaultModel, type AvailableModelGroup } from '@/api/system'
export const useAppStore = defineStore('app', () => {
const connected = ref(false)
const serverVersion = ref('')
const models = ref<Model[]>([])
const modelGroups = ref<AvailableModelGroup[]>([])
const selectedModel = ref('')
const healthPollTimer = ref<ReturnType<typeof setInterval>>()
// Settings
const streamEnabled = ref(true)
const sessionPersistence = ref(true)
const maxTokens = ref(4096)
const selectedModel = ref('hermes-agent')
async function checkConnection() {
try {
@@ -27,16 +26,26 @@ export const useAppStore = defineStore('app', () => {
async function loadModels() {
try {
const res = await fetchModels()
models.value = res.data || []
if (models.value.length > 0 && !models.value.find(m => m.id === selectedModel.value)) {
selectedModel.value = models.value[0].id
}
const res = await fetchAvailableModels()
modelGroups.value = res.groups
selectedModel.value = res.default
} catch {
// ignore
}
}
async function switchModel(modelId: string, providerOverride?: string) {
try {
// Find the group containing this model to get provider info
const group = modelGroups.value.find(g => g.models.includes(modelId))
const provider = providerOverride || group?.provider || ''
await updateDefaultModel({ default: modelId, provider })
selectedModel.value = modelId
} catch (err: any) {
console.error('Failed to switch model:', err)
}
}
function startHealthPolling(interval = 30000) {
stopHealthPolling()
checkConnection()
@@ -53,13 +62,14 @@ export const useAppStore = defineStore('app', () => {
return {
connected,
serverVersion,
models,
modelGroups,
selectedModel,
streamEnabled,
sessionPersistence,
maxTokens,
selectedModel,
checkConnection,
loadModels,
switchModel,
startHealthPolling,
stopHealthPolling,
}