Files
X-Agents/web/src/views/settings/useModelSettings.ts
DESKTOP-72TV0V4\caoxiaozhu 5c435ab21e Add streaming support and refactor Chat UI
- Add run_stream method to AgentCore for streaming output
- Add base_url parameter to LLM clients for OpenRouter support
- Add xbot module for new agent implementation
- Refactor Chat.vue into composable + components (ChatHeader, ChatMessage, ChatInput, ChatSidebar, ChatAgentSelector)
- Add ChatStream handler for SSE streaming in Go server
- Add UseXBot field to chat request

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:49:44 +08:00

387 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import type { ModelInfo, ModelForm, ModelTypeOption, ProviderOption } from './types'
// API 基础 URL
const API_BASE = 'http://localhost:8082'
export function useModelSettings() {
// Model 列表
const models = ref<ModelInfo[]>([])
const modelsLoading = ref(false)
// Model Type 选项
const modelTypeOptions: ModelTypeOption[] = [
{ value: 'chat', label: 'Chat' },
{ value: 'embedding', label: 'Embedding' },
{ value: 'rerank', label: 'Rerank' },
{ value: 'vlm', label: 'VLM' },
]
// Provider 选项
const providerOptions: ProviderOption[] = [
{ value: 'OpenAI', label: 'OpenAI' },
{ value: 'Ollama', label: 'Ollama' },
{ value: 'ali', label: 'Alibaba Cloud' },
]
// 默认 Base URL 映射
const defaultBaseUrls: Record<string, string> = {
OpenAI: 'http://localhost:1234/v1',
Ollama: 'http://localhost:11434',
ali: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
}
// 是否显示新增模型表单
const showAddModelForm = ref(false)
// 是否显示编辑模型表单
const showEditModelForm = ref(false)
// 新增模型表单
const newModelForm = ref<ModelForm>({
name: '',
apiKey: '',
apiEndpoint: '',
baseUrl: '',
provider: '',
model: '',
modelType: 'chat',
})
// 监听 provider 变化,自动填充默认 Base URL
watch(() => newModelForm.value.provider, (newProvider) => {
if (newProvider && defaultBaseUrls[newProvider]) {
newModelForm.value.baseUrl = defaultBaseUrls[newProvider]
}
})
// 监听新增弹窗打开,重置连接状态
watch(showAddModelForm, (newVal) => {
if (newVal) {
connectionStatus.value = 'idle'
}
})
// 编辑模型表单
const editForm = ref<ModelForm>({
name: '',
apiKey: '',
apiEndpoint: '',
baseUrl: '',
provider: '',
model: '',
modelType: '',
})
// 当前编辑的模型ID
const editingModelId = ref<string>('')
// 测试连接状态
const testingConnection = ref(false)
const connectionStatus = ref<'idle' | 'success' | 'error'>('idle')
// 编辑弹窗测试连接状态
const testingEditConnection = ref(false)
const editConnectionStatus = ref<'idle' | 'success' | 'error'>('idle')
// 编辑前模型的状态
const originalStatus = ref<string>('')
// 获取模型列表
const fetchModels = async () => {
modelsLoading.value = true
try {
const response = await fetch(`${API_BASE}/model/list`)
const result = await response.json()
models.value = result.list || []
} catch (error) {
console.error('Failed to fetch models:', error)
ElMessage.error('Failed to load models')
} finally {
modelsLoading.value = false
}
}
// 测试连接
const testConnection = async () => {
if (!newModelForm.value.apiKey || !newModelForm.value.baseUrl) {
ElMessage.warning('Please enter API Key and Base URL')
return
}
testingConnection.value = true
connectionStatus.value = 'idle'
try {
const response = await fetch(`${API_BASE}/model/test`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: newModelForm.value.provider,
model: newModelForm.value.model,
model_type: newModelForm.value.modelType || 'chat',
api_key: newModelForm.value.apiKey,
base_url: newModelForm.value.baseUrl,
api_endpoint: newModelForm.value.apiEndpoint,
}),
})
const result = await response.json()
if (result.success) {
connectionStatus.value = 'success'
ElMessage.success(result.message || 'Connection successful!')
} else {
connectionStatus.value = 'error'
ElMessage.error(result.message || 'Connection failed')
}
} catch (error: any) {
connectionStatus.value = 'error'
ElMessage.error('Connection failed: ' + error.message)
} finally {
testingConnection.value = false
}
}
// 编辑弹窗测试连接
const testConnectionEdit = async () => {
if (!editForm.value.apiKey || !editForm.value.baseUrl) {
ElMessage.warning('Please enter API Key and Base URL')
return
}
testingEditConnection.value = true
editConnectionStatus.value = 'idle'
try {
const response = await fetch(`${API_BASE}/model/test`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: editForm.value.provider,
model: editForm.value.model,
model_type: editForm.value.modelType || 'chat',
api_key: editForm.value.apiKey,
base_url: editForm.value.baseUrl,
api_endpoint: editForm.value.apiEndpoint,
}),
})
const result = await response.json()
if (result.success) {
editConnectionStatus.value = 'success'
ElMessage.success(result.message || 'Connection successful!')
} else {
editConnectionStatus.value = 'error'
ElMessage.error(result.message || 'Connection failed')
}
} catch (error: any) {
editConnectionStatus.value = 'error'
ElMessage.error('Connection failed: ' + error.message)
} finally {
testingEditConnection.value = false
}
}
// 添加新模型(初始状态为 inactive需要测试连接后才能激活
const addModel = async () => {
if (!newModelForm.value.name || !newModelForm.value.provider || !newModelForm.value.model) {
ElMessage.warning('Please fill in required fields')
return
}
try {
const response = await fetch(`${API_BASE}/model/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: newModelForm.value.name,
model_type: newModelForm.value.modelType || 'chat',
provider: newModelForm.value.provider,
model: newModelForm.value.model,
api_key: newModelForm.value.apiKey,
base_url: newModelForm.value.baseUrl,
api_endpoint: newModelForm.value.apiEndpoint,
status: connectionStatus.value === 'success' ? 'active' : 'inactive',
}),
})
if (response.ok) {
const result = await response.json()
const wasConnected = connectionStatus.value === 'success'
models.value.push(result)
newModelForm.value = {
name: '',
apiKey: '',
apiEndpoint: '',
baseUrl: '',
provider: '',
model: '',
modelType: 'chat',
}
connectionStatus.value = 'idle'
showAddModelForm.value = false
ElMessage.success(wasConnected ? 'Model added and activated' : 'Model added successfully')
} else {
const error = await response.json()
ElMessage.error(error.message || 'Failed to add model')
}
} catch (error: any) {
ElMessage.error('Failed to add model: ' + error.message)
}
}
// 取消添加
const cancelAddModel = () => {
newModelForm.value = {
name: '',
apiKey: '',
apiEndpoint: '',
baseUrl: '',
provider: '',
model: '',
modelType: '',
}
connectionStatus.value = 'idle'
showAddModelForm.value = false
}
// 删除模型
const deleteModel = async (id: string) => {
try {
const response = await fetch(`${API_BASE}/model/${id}`, {
method: 'DELETE',
})
if (response.ok) {
models.value = models.value.filter(m => m.id !== id)
ElMessage.success('Model deleted successfully')
} else {
ElMessage.error('Failed to delete model')
}
} catch (error: any) {
ElMessage.error('Failed to delete model: ' + error.message)
}
}
// 打开编辑弹窗
const openEditDialog = (model: ModelInfo) => {
editingModelId.value = model.id
editForm.value = {
name: model.name,
apiKey: model.api_key,
apiEndpoint: model.api_endpoint,
baseUrl: model.base_url,
provider: model.provider,
model: model.model,
modelType: model.model_type,
}
originalStatus.value = model.status
editConnectionStatus.value = 'idle'
showEditModelForm.value = true
}
// 更新模型
const updateModel = async () => {
if (!editingModelId.value || !editForm.value.name || !editForm.value.provider || !editForm.value.model) {
ElMessage.warning('Please fill in required fields')
return
}
// 根据测试连接结果设置状态
// 用户测试通过 -> active
// 用户测试失败 -> error
// 用户没有测试 -> inactive
let newStatus = 'inactive'
if (editConnectionStatus.value === 'success') {
newStatus = 'active'
} else if (editConnectionStatus.value === 'error') {
newStatus = 'error'
}
try {
const response = await fetch(`${API_BASE}/model/${editingModelId.value}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: editForm.value.name,
model_type: editForm.value.modelType || 'chat',
provider: editForm.value.provider,
model: editForm.value.model,
api_key: editForm.value.apiKey,
base_url: editForm.value.baseUrl,
api_endpoint: editForm.value.apiEndpoint,
status: newStatus,
}),
})
if (response.ok) {
const result = await response.json()
const index = models.value.findIndex(m => m.id === editingModelId.value)
if (index !== -1) {
models.value[index] = result
}
showEditModelForm.value = false
ElMessage.success('Model updated successfully')
} else {
const error = await response.json()
ElMessage.error(error.message || 'Failed to update model')
}
} catch (error: any) {
ElMessage.error('Failed to update model: ' + error.message)
}
}
// 取消编辑
const cancelEditModel = () => {
editForm.value = {
name: '',
apiKey: '',
apiEndpoint: '',
baseUrl: '',
provider: '',
model: '',
modelType: '',
}
editingModelId.value = ''
showEditModelForm.value = false
}
return {
// State
models,
modelsLoading,
modelTypeOptions,
providerOptions,
showAddModelForm,
showEditModelForm,
newModelForm,
editForm,
testingConnection,
connectionStatus,
testingEditConnection,
editConnectionStatus,
// Methods
fetchModels,
testConnection,
testConnectionEdit,
addModel,
cancelAddModel,
deleteModel,
openEditDialog,
updateModel,
cancelEditModel,
}
}