Fix model refresh dedupe and desktop packaging

This commit is contained in:
ciat-777
2026-03-16 15:49:15 +08:00
parent 60df68b712
commit b0e16fd3ea
5 changed files with 94 additions and 28 deletions
@@ -100,6 +100,42 @@ describe('refreshModelsForAccount credential discovery', () => {
expect(tokenRows).toHaveLength(0);
});
it('deduplicates discovered model names before writing availability rows', async () => {
getApiTokenMock.mockResolvedValue(null);
getModelsMock.mockResolvedValue(['? ', '?', 'GPT-4.1', 'gpt-4.1']);
const site = await db.insert(schema.sites).values({
name: 'site-dedupe',
url: 'https://site-dedupe.example.com',
platform: 'new-api',
status: 'active',
}).returning().get();
const account = await db.insert(schema.accounts).values({
siteId: site.id,
username: 'dedupe-user',
accessToken: 'session-token',
apiToken: null,
status: 'active',
}).returning().get();
const result = await refreshModelsForAccount(account.id);
expect(result).toMatchObject({
accountId: account.id,
refreshed: true,
status: 'success',
modelCount: 2,
modelsPreview: ['?', 'GPT-4.1'],
});
const rows = await db.select().from(schema.modelAvailability)
.where(eq(schema.modelAvailability.accountId, account.id))
.all();
expect(rows.map((row) => row.modelName).sort()).toEqual(['?', 'GPT-4.1']);
});
it('marks runtime health unhealthy when model discovery fails', async () => {
getApiTokenMock.mockResolvedValue(null);
getModelsMock.mockRejectedValue(new Error('HTTP 401: invalid token'));
+17 -1
View File
@@ -95,7 +95,23 @@ function isApiKeyConnection(account: typeof schema.accounts.$inferSelect): boole
}
function normalizeModels(models: string[]): string[] {
return Array.from(new Set(models.filter((model) => typeof model === 'string' && model.trim().length > 0)));
const normalizedModels: string[] = [];
const seen = new Set<string>();
for (const rawModel of models) {
if (typeof rawModel !== 'string') continue;
const modelName = rawModel.trim();
if (!modelName) continue;
// Keep app/database behavior stable across SQLite/MySQL by deduping with a
// case-insensitive key after trimming whitespace.
const dedupeKey = modelName.toLowerCase();
if (seen.has(dedupeKey)) continue;
seen.add(dedupeKey);
normalizedModels.push(modelName);
}
return normalizedModels;
}
function isExactModelPattern(modelPattern: string): boolean {
+1 -1
View File
@@ -1,7 +1,7 @@
import { tr } from '../i18n.js';
import { SITE_DOCS_URL } from '../docsLink.js';
const VERSION = '1.2.2';
const VERSION = '1.2.3';
const FEATURES = [
{ icon: '🌐', title: '统一代理网关', desc: '一个 Key、一个入口,兼容 OpenAI / Claude 下游格式' },