Skip to content

Commit a093369

Browse files
committed
fix: handle aliased upgrade targets and RFC3339 variants
1 parent e49cb27 commit a093369

4 files changed

Lines changed: 31 additions & 5 deletions

File tree

‎packages/kap-server/src/routes/modelCatalog.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,12 @@ export function registerModelCatalogRoutes(app: ModelCatalogRouteHost, core: Sco
430430
const previous = previousByModel.get(entry.model);
431431
let upgrade = previous?.upgrade;
432432
if (upgrade?.model.startsWith(`${provider_id}/`) === true) {
433-
const target = `${newId}/${upgrade.model.slice(provider_id.length + 1)}`;
434-
upgrade = newAliasKeys.has(target) ? { ...upgrade, model: target } : undefined;
433+
const targetModel = models[upgrade.model]?.model;
434+
const target = targetModel === undefined ? undefined : `${newId}/${targetModel}`;
435+
upgrade =
436+
target !== undefined && newAliasKeys.has(target)
437+
? { ...upgrade, model: target }
438+
: undefined;
435439
}
436440
const alias: ModelRecord = {
437441
...previous,

‎packages/kap-server/test/modelCatalogProviderWrite.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ describe('server-v2 /api/v1 provider write endpoints', () => {
753753

754754
it('rewrites model upgrade targets when renaming a provider', async () => {
755755
await boot(
756-
`${DEFAULTED_TOML}\n[models.gpt4o.upgrade]\nmodel = "openai/gpt-4.1"\nmigration_markdown = "Use next"\n`,
756+
`${DEFAULTED_TOML}\n[models."openai/replacement-key"]\nprovider = "openai"\nmodel = "gpt-4.1"\nmax_context_size = 1047576\n\n[models.gpt4o.upgrade]\nmodel = "openai/replacement-key"\nmigration_markdown = "Use next"\n`,
757757
);
758758
const { status } = await putJson('/api/v1/providers/openai', {
759759
...REPLACE_BODY,
@@ -782,7 +782,7 @@ describe('server-v2 /api/v1 provider write endpoints', () => {
782782

783783
it('drops model upgrades whose target disappears on provider edit', async () => {
784784
await boot(
785-
`${DEFAULTED_TOML}\n[models.gpt4o.upgrade]\nmodel = "openai/gpt-4.1"\nmigration_markdown = "Use next"\n`,
785+
`${DEFAULTED_TOML}\n[models."openai/replacement-key"]\nprovider = "openai"\nmodel = "gpt-4.1"\nmax_context_size = 1047576\n\n[models.gpt4o.upgrade]\nmodel = "openai/replacement-key"\nmigration_markdown = "Use next"\n`,
786786
);
787787
const { status } = await putJson('/api/v1/providers/openai', {
788788
...REPLACE_BODY,

‎packages/oauth/src/custom-registry.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function toStringArrayOrUndefined(value: unknown): readonly string[] | undefined
170170
return out;
171171
}
172172

173-
const RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
173+
const RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
174174

175175
function isValidRetirementAt(value: unknown): value is string | null | undefined {
176176
return (

‎packages/oauth/test/custom-registry.test.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,28 @@ describe('fetchCustomRegistry', () => {
184184
});
185185
});
186186

187+
it('accepts lowercase RFC3339 separators', async () => {
188+
const body = makeKokubResponseBody();
189+
body['registry_chat-completions']!.models['gpt-5.5'] = {
190+
id: 'gpt-5.5',
191+
upgrade: {
192+
model: 'gpt-6',
193+
migration_markdown: 'Use GPT-6',
194+
retirement_at: '2030-01-01t00:00:00z',
195+
},
196+
};
197+
const fetchMock = vi.fn(async () => makeJsonResponse(body));
198+
199+
const result = await fetchCustomRegistry(
200+
KOKUB_SOURCE,
201+
{ fetchImpl: fetchMock as unknown as typeof fetch },
202+
);
203+
204+
expect(result['registry_chat-completions']?.models['gpt-5.5']?.upgrade?.retirement_at).toBe(
205+
'2030-01-01t00:00:00z',
206+
);
207+
});
208+
187209
it('rejects malformed upgrade timestamps', async () => {
188210
const body = makeKokubResponseBody();
189211
body['registry_chat-completions']!.models['gpt-5.5'] = {

0 commit comments

Comments
 (0)