forked from boos/BoosAPI
Fix model refresh dedupe and desktop packaging
This commit is contained in:
@@ -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'));
|
||||
|
||||
@@ -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,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 下游格式' },
|
||||
|
||||
Reference in New Issue
Block a user