Files
linshen 006565c599 refactor(core): 实现统一参数架构并修复运行时参数丢失问题
## 核心变更

### 🔧 关键Bug修复
- 修复LLM/Image服务运行时自定义参数丢失问题
- prepareRuntimeConfig()现在支持customParamOverrides向后兼容
- 确保用户配置的自定义API头、私有开关等参数不会在运行时丢失

### 🏗️ 统一参数架构
- 新增parameter-schema.ts: 统一参数定义与安全验证
- 新增parameter-utils.ts: 参数合并、验证、拆分工具
- 实现优先级参数合并: requestOverrides > customOverrides > builtInOverrides > defaults
- 安全过滤危险参数键(__proto__, apiKey, secret等)

### 📦 服务层改进
- llm/service.ts: 添加prepareRuntimeConfig()方法支持参数迁移
- image/service.ts: 同步实现参数迁移逻辑
- model/manager.ts: 添加读时迁移和弃用警告
- 所有Adapter统一使用UnifiedParameterDefinition

### 🎨 UI组件更新
- 新增ModelParameterEditor.vue: 统一参数编辑器
- 新增ModelAdvancedSection.vue: 高级参数折叠面板
- 简化TextModelEditModal和ImageModelEditModal逻辑
- 支持类型安全的参数编辑(number, boolean, string, select等)

### ✅ 测试覆盖
- 新增parameter-schema.test.ts: 参数定义验证测试
- 新增parameter-utils.test.ts: 参数工具函数测试
- 新增custom-params-migration.test.ts: 参数迁移全流程测试
- 修复anthropic-adapter和gemini-adapter测试以匹配新架构
- 所有582个测试通过(100%通过率)

## 向后兼容性
- 完全兼容旧格式配置(customParamOverrides独立字段)
- 读时自动迁移,写时清理已弃用字段
- 保留customParamOverrides以防版本回退

## 破坏性变更
- 无(完全向后兼容)

## 文档更新
- advancedParameterDefinitions.ts添加弃用说明
- 标注v3.0弃用,v4.0移除计划
2025-10-12 22:26:58 +08:00

66 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
DANGEROUS_PARAM_KEY_PATTERNS,
isSafeCustomKey,
isValueEmpty,
normalizeDefaultValue,
type UnifiedParameterDefinition
} from '../../src/services/model/parameter-schema'
describe('parameter-schema helpers', () => {
it('checks empty values correctly', () => {
expect(isValueEmpty(undefined)).toBe(true)
expect(isValueEmpty(null)).toBe(true)
expect(isValueEmpty('')).toBe(true)
expect(isValueEmpty(' ')).toBe(true)
expect(isValueEmpty([])).toBe(true)
expect(isValueEmpty(['value'])).toBe(false)
expect(isValueEmpty(0)).toBe(false)
expect(isValueEmpty(false)).toBe(false)
})
it('normalizes default values', () => {
expect(normalizeDefaultValue('')).toBeUndefined()
expect(normalizeDefaultValue('value')).toBe('value')
expect(normalizeDefaultValue(null)).toBeUndefined()
expect(normalizeDefaultValue(undefined)).toBeUndefined()
expect(normalizeDefaultValue([])).toBeUndefined()
expect(normalizeDefaultValue(['foo'])).toEqual(['foo'])
})
it('validates safe custom keys', () => {
expect(isSafeCustomKey('temperature')).toBe(true)
expect(isSafeCustomKey('cfg-scale')).toBe(true)
expect(isSafeCustomKey('tool.call/limit')).toBe(true)
expect(isSafeCustomKey('')).toBe(false)
expect(isSafeCustomKey(' ')).toBe(false)
expect(isSafeCustomKey('max tokens')).toBe(false)
expect(isSafeCustomKey('bad$key')).toBe(false)
expect(isSafeCustomKey('apiKey')).toBe(false)
})
it('blocks dangerous key patterns', () => {
for (const pattern of DANGEROUS_PARAM_KEY_PATTERNS) {
expect(isSafeCustomKey(pattern)).toBe(false)
expect(isSafeCustomKey(`prefix.${pattern}`)).toBe(false)
}
})
it('allows interface shape compilation', () => {
const definition: UnifiedParameterDefinition = {
name: 'temperature',
type: 'number',
defaultValue: 1,
minValue: 0,
maxValue: 2,
step: 0.1,
labelKey: 'params.temperature.label',
descriptionKey: 'params.temperature.description',
required: false,
unitKey: 'params.temperature.unit',
tags: ['llm', 'sampling']
}
expect(definition.name).toBe('temperature')
})
})