99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
import { defineStore } from 'pinia';
|
|
|
|
export type ModelKey = string;
|
|
|
|
export interface ModelOption {
|
|
key: ModelKey;
|
|
label: string;
|
|
description: string;
|
|
visible?: boolean;
|
|
multimodal?: string;
|
|
supportsImage?: boolean;
|
|
supportsVideo?: boolean;
|
|
contextWindow?: number | null;
|
|
maxOutputTokens?: number | null;
|
|
fastOnly: boolean;
|
|
supportsThinking: boolean;
|
|
deepOnly?: boolean;
|
|
}
|
|
|
|
interface ModelState {
|
|
currentModelKey: ModelKey;
|
|
models: ModelOption[];
|
|
}
|
|
|
|
export const useModelStore = defineStore('model', {
|
|
state: (): ModelState => ({
|
|
currentModelKey: '',
|
|
models: []
|
|
}),
|
|
getters: {
|
|
currentModel(state): ModelOption | null {
|
|
return state.models.find((m) => m.key === state.currentModelKey) || state.models[0] || null;
|
|
}
|
|
},
|
|
actions: {
|
|
setModels(models: ModelOption[]) {
|
|
if (!Array.isArray(models)) return;
|
|
this.models = models;
|
|
const exists = this.currentModelKey && this.models.some((m) => m.key === this.currentModelKey);
|
|
if (!exists) {
|
|
this.currentModelKey = this.models[0]?.key || '';
|
|
}
|
|
},
|
|
setModel(key: ModelKey) {
|
|
if (this.currentModelKey === key) return;
|
|
const exists = this.models.some((m) => m.key === key);
|
|
if (exists) {
|
|
this.currentModelKey = key;
|
|
return;
|
|
}
|
|
// 如果列表中还没有该模型(例如 fetchModels 失败),
|
|
// 临时插入一个 fallback 项,避免首屏显示“未选择模型”
|
|
if (key) {
|
|
this.models = [
|
|
...this.models,
|
|
{
|
|
key,
|
|
label: key,
|
|
description: '',
|
|
fastOnly: false,
|
|
supportsThinking: true
|
|
}
|
|
];
|
|
this.currentModelKey = key;
|
|
}
|
|
},
|
|
async fetchModels() {
|
|
const resp = await fetch('/api/v1/models', { cache: 'no-store' });
|
|
const data = await resp.json();
|
|
if (!resp.ok || !data?.success || !Array.isArray(data.items)) {
|
|
throw new Error(data?.error || '加载模型列表失败');
|
|
}
|
|
const mapped: ModelOption[] = data.items
|
|
.filter((item: any) => item && item.model_key)
|
|
.map((item: any) => {
|
|
const multimodal = String(item.multimodal || 'none');
|
|
const supportsImage = multimodal === 'image' || multimodal === 'image,video';
|
|
const supportsVideo = multimodal === 'video' || multimodal === 'image,video';
|
|
return {
|
|
key: String(item.model_key),
|
|
label: String(item.name || item.model_key),
|
|
description: String(item.description || ''),
|
|
visible: item.visible !== false,
|
|
multimodal,
|
|
supportsImage,
|
|
supportsVideo,
|
|
contextWindow: typeof item.context_window === 'number' ? item.context_window : null,
|
|
maxOutputTokens:
|
|
typeof item.max_output_tokens === 'number' ? item.max_output_tokens : null,
|
|
fastOnly: !!item.fast_only,
|
|
supportsThinking: !!item.supports_thinking,
|
|
deepOnly: !!item.deep_only
|
|
};
|
|
});
|
|
this.setModels(mapped);
|
|
}
|
|
}
|
|
});
|