forked from boos/BoosAPI
[codex] add vendor code entry presets follow-up (#363)
* address coding plan review comments * add more vendor code entry presets
This commit is contained in:
@@ -471,6 +471,66 @@ describe('sites proxy settings', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('canonicalizes create payload url and platform before persistence and conflict checks', async () => {
|
||||
const created = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/sites',
|
||||
payload: {
|
||||
name: 'Aliyun Generic OpenAI',
|
||||
url: ' coding.dashscope.aliyuncs.com/v1/ ',
|
||||
platform: ' OPENAI ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(created.statusCode).toBe(200);
|
||||
expect(created.json()).toMatchObject({
|
||||
name: 'Aliyun Generic OpenAI',
|
||||
url: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
platform: 'openai',
|
||||
});
|
||||
|
||||
const duplicate = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/sites',
|
||||
payload: {
|
||||
name: 'Aliyun Generic OpenAI Duplicate',
|
||||
url: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
platform: 'openai',
|
||||
},
|
||||
});
|
||||
|
||||
expect(duplicate.statusCode).toBe(409);
|
||||
expect((duplicate.json() as { error?: string }).error).toContain('already exists');
|
||||
});
|
||||
|
||||
it('canonicalizes update payload url and platform before saving', async () => {
|
||||
const created = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/sites',
|
||||
payload: {
|
||||
name: 'Update Canonicalization',
|
||||
url: 'https://update-canonicalization.example.com',
|
||||
platform: 'new-api',
|
||||
},
|
||||
});
|
||||
expect(created.statusCode).toBe(200);
|
||||
|
||||
const response = await app.inject({
|
||||
method: 'PUT',
|
||||
url: `/api/sites/${(created.json() as { id: number }).id}`,
|
||||
payload: {
|
||||
url: ' coding.dashscope.aliyuncs.com/v1/ ',
|
||||
platform: ' OPENAI ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toMatchObject({
|
||||
url: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
platform: 'openai',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects Zhipu Coding Plan OpenAI endpoint with initialization preset metadata', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
@@ -486,4 +546,50 @@ describe('sites proxy settings', () => {
|
||||
initializationPresetId: 'zhipu-coding-plan-openai',
|
||||
});
|
||||
});
|
||||
|
||||
it('detects additional vendor-specific code endpoints with initialization preset metadata', async () => {
|
||||
const cases = [
|
||||
{ url: 'https://api.deepseek.com/v1', platform: 'openai', initializationPresetId: 'deepseek-openai' },
|
||||
{ url: 'https://api.deepseek.com/anthropic', platform: 'claude', initializationPresetId: 'deepseek-claude' },
|
||||
{ url: 'https://api.moonshot.cn/v1', platform: 'openai', initializationPresetId: 'moonshot-openai' },
|
||||
{ url: 'https://api.moonshot.cn/anthropic', platform: 'claude', initializationPresetId: 'moonshot-claude' },
|
||||
{ url: 'https://api.minimaxi.com/v1', platform: 'openai', initializationPresetId: 'minimax-openai' },
|
||||
{ url: 'https://api.minimaxi.com/anthropic', platform: 'claude', initializationPresetId: 'minimax-claude' },
|
||||
{ url: 'https://api-inference.modelscope.cn/v1', platform: 'openai', initializationPresetId: 'modelscope-openai' },
|
||||
{ url: 'https://api-inference.modelscope.cn', platform: 'claude', initializationPresetId: 'modelscope-claude' },
|
||||
{ url: 'https://ark.cn-beijing.volces.com/api/coding/v3', platform: 'openai', initializationPresetId: 'doubao-coding-openai' },
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/sites/detect',
|
||||
payload: { url: testCase.url },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toMatchObject({
|
||||
platform: testCase.platform,
|
||||
initializationPresetId: testCase.initializationPresetId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('creates Doubao Coding Plan sites without explicit platform by using preset-backed detection', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/sites',
|
||||
payload: {
|
||||
name: 'Doubao Coding Plan',
|
||||
url: 'https://ark.cn-beijing.volces.com/api/coding/v3',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toMatchObject({
|
||||
name: 'Doubao Coding Plan',
|
||||
platform: 'openai',
|
||||
initializationPresetId: 'doubao-coding-openai',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,9 +90,15 @@ type ErrorLike = {
|
||||
cause?: unknown;
|
||||
};
|
||||
|
||||
function normalizeOptionalPlatform(value: string | undefined): string | null {
|
||||
function normalizeCanonicalSiteUrl(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
const withScheme = trimmed.includes('://') ? trimmed : `https://${trimmed}`;
|
||||
return normalizeSiteUrl(withScheme);
|
||||
}
|
||||
|
||||
function normalizeSitePlatform(value: string | undefined): string | null {
|
||||
if (value === undefined) return null;
|
||||
const normalized = value.trim();
|
||||
const normalized = value.trim().toLowerCase();
|
||||
return normalized || null;
|
||||
}
|
||||
|
||||
@@ -347,15 +353,15 @@ export async function sitesRoutes(app: FastifyInstance) {
|
||||
|
||||
const existingSites = await db.select().from(schema.sites).all();
|
||||
const maxSortOrder = existingSites.reduce((max, site) => Math.max(max, site.sortOrder || 0), -1);
|
||||
const normalizedUrl = normalizeSiteUrl(url);
|
||||
|
||||
let detectedPlatform = normalizeOptionalPlatform(platform);
|
||||
const canonicalUrl = normalizeCanonicalSiteUrl(url);
|
||||
const canonicalPlatform = normalizeSitePlatform(platform);
|
||||
let detectedPlatform = canonicalPlatform;
|
||||
let responseInitializationPresetId: string | null = explicitInitializationPreset?.id || null;
|
||||
if (!detectedPlatform) {
|
||||
if (explicitInitializationPreset) {
|
||||
detectedPlatform = explicitInitializationPreset.platform;
|
||||
} else {
|
||||
const detected = await detectSite(url);
|
||||
const detected = await detectSite(canonicalUrl);
|
||||
detectedPlatform = detected?.platform ?? null;
|
||||
responseInitializationPresetId = detected?.initializationPresetId || null;
|
||||
}
|
||||
@@ -366,16 +372,16 @@ export async function sitesRoutes(app: FastifyInstance) {
|
||||
if (!detectedPlatform) {
|
||||
return { error: 'Could not detect platform. Please specify manually.' };
|
||||
}
|
||||
const conflictingSite = findExistingSiteBinding(existingSites, detectedPlatform, normalizedUrl);
|
||||
const conflictingSite = findExistingSiteBinding(existingSites, detectedPlatform, canonicalUrl);
|
||||
if (conflictingSite) {
|
||||
return sendSiteBindingConflict(reply, detectedPlatform, normalizedUrl);
|
||||
return sendSiteBindingConflict(reply, detectedPlatform, canonicalUrl);
|
||||
}
|
||||
|
||||
let inserted;
|
||||
try {
|
||||
inserted = await db.insert(schema.sites).values({
|
||||
name,
|
||||
url: normalizedUrl,
|
||||
url: canonicalUrl,
|
||||
platform: detectedPlatform,
|
||||
proxyUrl: normalizedProxyUrl.proxyUrl,
|
||||
useSystemProxy: normalizedUseSystemProxy ?? false,
|
||||
@@ -388,7 +394,7 @@ export async function sitesRoutes(app: FastifyInstance) {
|
||||
}).run();
|
||||
} catch (error) {
|
||||
if (isSitesPlatformUrlConflict(error)) {
|
||||
return sendSiteBindingConflict(reply, detectedPlatform, normalizedUrl);
|
||||
return sendSiteBindingConflict(reply, detectedPlatform, canonicalUrl);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -459,9 +465,10 @@ export async function sitesRoutes(app: FastifyInstance) {
|
||||
return reply.code(400).send({ error: normalizedCustomHeaders.error || 'Invalid customHeaders.' });
|
||||
}
|
||||
|
||||
const nextUrl = body.url !== undefined ? normalizeSiteUrl(body.url) : existingSite.url;
|
||||
const canonicalPlatform = normalizeSitePlatform(body.platform);
|
||||
const nextUrl = body.url !== undefined ? normalizeCanonicalSiteUrl(body.url) : existingSite.url;
|
||||
const nextPlatform = body.platform !== undefined
|
||||
? normalizeOptionalPlatform(body.platform)
|
||||
? canonicalPlatform
|
||||
: existingSite.platform;
|
||||
if (body.platform !== undefined && !nextPlatform) {
|
||||
return reply.code(400).send({ error: 'Invalid platform. Expected non-empty string.' });
|
||||
|
||||
Reference in New Issue
Block a user