mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +08:00
merge(main): 解决 usage 展示与生命周期同步冲突
This commit is contained in:
@@ -782,7 +782,7 @@
|
||||
:models="providerModels"
|
||||
:endpoints="endpoints"
|
||||
:provider-keys="providerKeys"
|
||||
:loading="loadingProviderModels || loadingProviderKeys"
|
||||
:loading="loadingProviderModels"
|
||||
@edit-model="handleEditModel"
|
||||
@batch-assign="handleBatchAssign"
|
||||
@refresh="loadEndpoints"
|
||||
@@ -798,7 +798,7 @@
|
||||
:provider-keys="providerKeys"
|
||||
:models="providerModels"
|
||||
:mapping-preview="providerMappingPreview"
|
||||
:loading="loadingProviderEndpoints || loadingProviderKeys || loadingProviderModels || loadingProviderMappingPreview"
|
||||
:loading="loadingProviderMappingPreview"
|
||||
@refresh="handleModelMappingChanged"
|
||||
/>
|
||||
</div>
|
||||
@@ -1303,8 +1303,6 @@ watch(
|
||||
loading.value = false
|
||||
}
|
||||
void loadSystemFormatConversionConfig()
|
||||
// mapping-preview 较慢,不阻塞首屏渲染
|
||||
void loadMappingPreview()
|
||||
if (!hasInitialProvider) {
|
||||
await loadProvider()
|
||||
}
|
||||
@@ -1313,7 +1311,13 @@ watch(
|
||||
if (newOpen && !oldOpen) {
|
||||
startCountdownTimer()
|
||||
}
|
||||
void endpointsPromise.then(() => autoRefreshQuotaInBackground())
|
||||
// 优先完成端点、密钥和模型的首屏数据,再请求计算量较大的映射预览。
|
||||
// 同时校验抽屉状态,避免关闭或切换 Provider 后启动无用请求。
|
||||
void endpointsPromise.then(() => {
|
||||
if (!props.open || props.providerId !== newId) return
|
||||
void loadMappingPreview()
|
||||
void autoRefreshQuotaInBackground()
|
||||
})
|
||||
} else if (!newOpen && oldOpen) {
|
||||
// 使在途请求失效,避免关闭后旧响应回写
|
||||
providerLoadRequestId += 1
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createSSRApp, h } from 'vue'
|
||||
import { renderToString } from '@vue/server-renderer'
|
||||
|
||||
import type { ProviderWithEndpointsSummary } from '@/api/endpoints'
|
||||
import ModelMappingTab from '../provider-tabs/ModelMappingTab.vue'
|
||||
|
||||
const provider = {
|
||||
id: 'provider-demo',
|
||||
name: 'Demo Provider',
|
||||
provider_type: 'custom',
|
||||
is_active: true,
|
||||
active_keys: 0,
|
||||
api_formats: [],
|
||||
} as ProviderWithEndpointsSummary
|
||||
|
||||
describe('ModelMappingTab response contracts', () => {
|
||||
it('keeps the module visible when a legacy or malformed preview reaches the component', async () => {
|
||||
const app = createSSRApp({
|
||||
render: () => h(ModelMappingTab, {
|
||||
provider,
|
||||
models: [],
|
||||
endpoints: [],
|
||||
providerKeys: [],
|
||||
mappingPreview: {
|
||||
message: '演示模式:该接口暂未模拟',
|
||||
demo_mode: true,
|
||||
},
|
||||
loading: false,
|
||||
}),
|
||||
})
|
||||
|
||||
const html = await renderToString(app)
|
||||
|
||||
expect(html).toContain('模型映射')
|
||||
expect(html).toContain('暂无模型映射')
|
||||
})
|
||||
})
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
resolve(process.cwd(), 'src/features/providers/components/ProviderDetailDrawer.vue'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('ProviderDetailDrawer loading priorities', () => {
|
||||
it('loads mapping preview after first-screen provider data', () => {
|
||||
const openWatcher = source
|
||||
.split('// 合并监听 providerId 和 open')[1]
|
||||
?.split('} else if (!newOpen && oldOpen)')[0]
|
||||
|
||||
expect(openWatcher).toBeTruthy()
|
||||
expect(openWatcher).toContain('const endpointsPromise = loadEndpoints()')
|
||||
expect(openWatcher).toContain('endpointsPromise.then(() => {')
|
||||
expect(openWatcher).toContain('if (!props.open || props.providerId !== newId) return')
|
||||
expect(openWatcher).toContain('void loadMappingPreview()')
|
||||
|
||||
const beforeEndpoints = openWatcher?.split('const endpointsPromise = loadEndpoints()')[0]
|
||||
expect(beforeEndpoints).not.toContain('loadMappingPreview()')
|
||||
})
|
||||
|
||||
it('keeps model and mapping loading states independent', () => {
|
||||
expect(source).toContain(':loading="loadingProviderModels"')
|
||||
expect(source).toContain(':loading="loadingProviderMappingPreview"')
|
||||
expect(source).not.toContain(':loading="loadingProviderModels || loadingProviderKeys"')
|
||||
expect(source).not.toContain(':loading="loadingProviderEndpoints || loadingProviderKeys || loadingProviderModels || loadingProviderMappingPreview"')
|
||||
})
|
||||
})
|
||||
@@ -599,13 +599,20 @@ const regexMappings = computed<CombinedMapping[]>(() => {
|
||||
const result: CombinedMapping[] = []
|
||||
const modelMap = new Map<string, CombinedMapping>()
|
||||
|
||||
for (const keyInfo of aliasMappingPreview.value.keys) {
|
||||
for (const gm of keyInfo.matching_global_models) {
|
||||
const previewKeys = Array.isArray(aliasMappingPreview.value.keys)
|
||||
? aliasMappingPreview.value.keys
|
||||
: []
|
||||
for (const keyInfo of previewKeys) {
|
||||
const matchingGlobalModels = Array.isArray(keyInfo.matching_global_models)
|
||||
? keyInfo.matching_global_models
|
||||
: []
|
||||
for (const gm of matchingGlobalModels) {
|
||||
const matchedModels = Array.isArray(gm.matched_models) ? gm.matched_models : []
|
||||
if (!modelMap.has(gm.global_model_id)) {
|
||||
modelMap.set(gm.global_model_id, {
|
||||
key: `regex-${gm.global_model_id}`,
|
||||
type: 'regex',
|
||||
targetModelName: gm.display_name,
|
||||
targetModelName: gm.display_name || gm.global_model_name || gm.global_model_id,
|
||||
targetModelId: gm.global_model_id,
|
||||
globalModelName: gm.global_model_name,
|
||||
mappings: [],
|
||||
@@ -618,7 +625,7 @@ const regexMappings = computed<CombinedMapping[]>(() => {
|
||||
if (!mapping) continue
|
||||
|
||||
// 添加 Key 信息
|
||||
const keyMatches: MappingItem[] = gm.matched_models.map(m => ({
|
||||
const keyMatches: MappingItem[] = matchedModels.map(m => ({
|
||||
name: m.allowed_model,
|
||||
pattern: m.mapping_pattern
|
||||
}))
|
||||
@@ -631,7 +638,7 @@ const regexMappings = computed<CombinedMapping[]>(() => {
|
||||
})
|
||||
|
||||
// 收集所有映射(去重)
|
||||
for (const match of gm.matched_models) {
|
||||
for (const match of matchedModels) {
|
||||
if (!mapping.mappings.some(m => m.name === match.allowed_model)) {
|
||||
mapping.mappings.push({
|
||||
name: match.allowed_model,
|
||||
|
||||
Reference in New Issue
Block a user