Skip to content

Commit 339ecb7

Browse files
fix(web): a model change declares each offering once so the redeploy is accepted (CL-8591) (#948)
* test(web): a model swap never declares the new offering twice (CL-8591) * fix(web): a model change declares each offering once so the redeploy is accepted (CL-8591)
1 parent 6a99d16 commit 339ecb7

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

‎apps/web/src/settings/myra-model-redeploy.test.ts‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,33 @@ describe("swapDeclaredOffering", () => {
4545
expect(result?.sourceOfferingIds).toEqual(["off_new", "off_b", "off_c"]);
4646
});
4747

48+
test("drops the duplicate when the new offering is already declared", () => {
49+
// The new offering is minted before the current list is read, so the
50+
// swap would otherwise declare it twice and the hub rejects that.
51+
const before = {
52+
sourceOfferingIds: ["off_old", "off_new"],
53+
defaultSourceOfferingId: "off_old",
54+
declaredSources: [
55+
{ provider: "openai-compatible" as const, model: "qwen2.5:7b" },
56+
{ provider: "openai-compatible" as const, model: "llama3.2:1b" },
57+
],
58+
};
59+
60+
const result = swapDeclaredOffering(
61+
before,
62+
"off_old",
63+
"off_new",
64+
"openai-compatible",
65+
"llama3.2:1b",
66+
);
67+
68+
expect(result).toEqual({
69+
sourceOfferingIds: ["off_new"],
70+
defaultSourceOfferingId: "off_new",
71+
declaredSources: [{ provider: "openai-compatible", model: "llama3.2:1b" }],
72+
});
73+
});
74+
4875
test("returns null when the old offering id isn't declared at all", () => {
4976
const result = swapDeclaredOffering(
5077
BEFORE,

‎apps/web/src/settings/myra-model-redeploy.ts‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,23 @@ export function swapDeclaredOffering(
6767
} | null {
6868
const index = before.sourceOfferingIds.indexOf(oldOfferingId);
6969
if (index === -1) return null;
70+
const swappedIds = before.sourceOfferingIds.map((id) =>
71+
id === oldOfferingId ? newOfferingId : id,
72+
);
73+
const swappedSources = before.declaredSources.map((source, position) =>
74+
position === index ? { provider, model: newCanonicalName } : source,
75+
);
76+
// The new offering is minted before the current list is read, so it can
77+
// already be present; the hub rejects a chain that names an id twice.
78+
const firstSeen = (id: string, position: number) => swappedIds.indexOf(id) === position;
7079
return {
71-
sourceOfferingIds: before.sourceOfferingIds.map((id) =>
72-
id === oldOfferingId ? newOfferingId : id,
73-
),
80+
sourceOfferingIds: swappedIds.filter(firstSeen),
7481
defaultSourceOfferingId:
7582
before.defaultSourceOfferingId === oldOfferingId
7683
? newOfferingId
7784
: before.defaultSourceOfferingId,
78-
declaredSources: before.declaredSources.map((source, position) =>
79-
position === index ? { provider, model: newCanonicalName } : source,
85+
declaredSources: swappedSources.filter((_, position) =>
86+
firstSeen(swappedIds[position] ?? "", position),
8087
),
8188
};
8289
}

0 commit comments

Comments
 (0)