## 核心变更 ### 🔧 关键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移除计划
179 lines
5.7 KiB
TypeScript
179 lines
5.7 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import { mergeOverrides } from '../../../src/services/model/parameter-utils'
|
|
import type { UnifiedParameterDefinition } from '../../../src/services/model/parameter-schema'
|
|
|
|
describe('自定义参数迁移测试', () => {
|
|
const schema: UnifiedParameterDefinition[] = [
|
|
{
|
|
name: 'temperature',
|
|
type: 'number',
|
|
minValue: 0,
|
|
maxValue: 2,
|
|
defaultValue: 1
|
|
},
|
|
{
|
|
name: 'max_tokens',
|
|
type: 'integer',
|
|
minValue: 1,
|
|
maxValue: 40000
|
|
}
|
|
]
|
|
|
|
describe('向后兼容旧格式的 customParamOverrides', () => {
|
|
it('应该合并 customParamOverrides 和 paramOverrides', () => {
|
|
// 模拟旧数据格式:内置参数在 paramOverrides,自定义参数在 customParamOverrides
|
|
const paramOverrides = { temperature: 0.7 }
|
|
const customParamOverrides = { custom_flag: 'test_value', api_version: '2024-01' }
|
|
|
|
const merged = mergeOverrides({
|
|
schema,
|
|
includeDefaults: false,
|
|
customOverrides: customParamOverrides,
|
|
requestOverrides: paramOverrides
|
|
})
|
|
|
|
// 验证内置参数被正确处理
|
|
expect(merged.temperature).toBe(0.7)
|
|
|
|
// 验证自定义参数没有丢失
|
|
expect(merged.custom_flag).toBe('test_value')
|
|
expect(merged.api_version).toBe('2024-01')
|
|
})
|
|
|
|
it('requestOverrides 应该覆盖 customOverrides', () => {
|
|
const customParamOverrides = { custom_flag: 'old_value' }
|
|
const paramOverrides = { custom_flag: 'new_value', temperature: 0.8 }
|
|
|
|
const merged = mergeOverrides({
|
|
schema,
|
|
includeDefaults: false,
|
|
customOverrides: customParamOverrides,
|
|
requestOverrides: paramOverrides
|
|
})
|
|
|
|
// requestOverrides 优先级更高
|
|
expect(merged.custom_flag).toBe('new_value')
|
|
expect(merged.temperature).toBe(0.8)
|
|
})
|
|
|
|
it('应该过滤掉空值的自定义参数', () => {
|
|
const customParamOverrides = {
|
|
valid_param: 'value',
|
|
empty_string: '',
|
|
null_value: null,
|
|
undefined_value: undefined
|
|
}
|
|
|
|
const merged = mergeOverrides({
|
|
schema,
|
|
includeDefaults: false,
|
|
customOverrides: customParamOverrides as any
|
|
})
|
|
|
|
// 只有非空值应该被保留
|
|
expect(merged.valid_param).toBe('value')
|
|
expect(merged.empty_string).toBeUndefined()
|
|
expect(merged.null_value).toBeUndefined()
|
|
expect(merged.undefined_value).toBeUndefined()
|
|
})
|
|
|
|
it('应该拒绝危险的自定义参数键名', () => {
|
|
const customParamOverrides = {
|
|
'__proto__': 'dangerous',
|
|
'apiKey': 'should_reject',
|
|
'safe_param': 'ok'
|
|
}
|
|
|
|
const merged = mergeOverrides({
|
|
schema,
|
|
includeDefaults: false,
|
|
customOverrides: customParamOverrides
|
|
})
|
|
|
|
// 危险参数应该被过滤(不会作为自己的属性存在)
|
|
expect(Object.hasOwn(merged, '__proto__')).toBe(false)
|
|
expect(Object.hasOwn(merged, 'apiKey')).toBe(false)
|
|
|
|
// 安全参数应该保留
|
|
expect(merged.safe_param).toBe('ok')
|
|
})
|
|
})
|
|
|
|
describe('LLM Service 运行时配置准备', () => {
|
|
it('应该模拟 prepareRuntimeConfig 的行为', () => {
|
|
// 模拟一个旧格式的 TextModelConfig
|
|
const modelConfig = {
|
|
id: 'test',
|
|
name: 'Test Model',
|
|
enabled: true,
|
|
providerMeta: { id: 'test', name: 'Test' } as any,
|
|
modelMeta: {
|
|
id: 'test-model',
|
|
name: 'Test Model',
|
|
providerId: 'test',
|
|
capabilities: {},
|
|
parameterDefinitions: schema
|
|
} as any,
|
|
connectionConfig: {},
|
|
paramOverrides: { temperature: 0.7, max_tokens: 1000 },
|
|
customParamOverrides: {
|
|
custom_header: 'X-Custom-Value',
|
|
extra_param: 'important_value'
|
|
}
|
|
}
|
|
|
|
// 模拟 prepareRuntimeConfig 逻辑
|
|
const mergedOverrides = mergeOverrides({
|
|
schema: modelConfig.modelMeta.parameterDefinitions,
|
|
includeDefaults: false,
|
|
customOverrides: modelConfig.customParamOverrides,
|
|
requestOverrides: modelConfig.paramOverrides
|
|
})
|
|
|
|
// 验证运行时配置包含所有参数
|
|
expect(mergedOverrides.temperature).toBe(0.7)
|
|
expect(mergedOverrides.max_tokens).toBe(1000)
|
|
expect(mergedOverrides.custom_header).toBe('X-Custom-Value')
|
|
expect(mergedOverrides.extra_param).toBe('important_value')
|
|
})
|
|
|
|
it('应该处理已经迁移的新格式配置', () => {
|
|
// 模拟已经迁移的配置:所有参数都在 paramOverrides
|
|
const modelConfig = {
|
|
id: 'test',
|
|
name: 'Test Model',
|
|
enabled: true,
|
|
providerMeta: { id: 'test', name: 'Test' } as any,
|
|
modelMeta: {
|
|
id: 'test-model',
|
|
name: 'Test Model',
|
|
providerId: 'test',
|
|
capabilities: {},
|
|
parameterDefinitions: schema
|
|
} as any,
|
|
connectionConfig: {},
|
|
paramOverrides: {
|
|
temperature: 0.7,
|
|
max_tokens: 1000,
|
|
custom_header: 'X-Custom-Value',
|
|
extra_param: 'important_value'
|
|
},
|
|
customParamOverrides: undefined // 已迁移
|
|
}
|
|
|
|
const mergedOverrides = mergeOverrides({
|
|
schema: modelConfig.modelMeta.parameterDefinitions,
|
|
includeDefaults: false,
|
|
customOverrides: modelConfig.customParamOverrides,
|
|
requestOverrides: modelConfig.paramOverrides
|
|
})
|
|
|
|
// 新格式也应该正常工作
|
|
expect(mergedOverrides.temperature).toBe(0.7)
|
|
expect(mergedOverrides.max_tokens).toBe(1000)
|
|
expect(mergedOverrides.custom_header).toBe('X-Custom-Value')
|
|
expect(mergedOverrides.extra_param).toBe('important_value')
|
|
})
|
|
})
|
|
})
|