fix: manage Surplus multiplier offers

This commit is contained in:
david
2026-08-27 13:21:19 +02:00
parent ed6dbf5730
commit f79a33e7c3
3 changed files with 59 additions and 19 deletions

View File

@@ -381,7 +381,10 @@ function parseOffer(value: unknown, index: number): SurplusOffer {
return {
id: offerId(item, `Surplus offer ${index}`),
model: string(item.model, `Surplus offer ${index}.model`),
sellerBaseUrl: string(item.seller_base_url, `Surplus offer ${index}.seller_base_url`),
sellerBaseUrl: string(
item.seller_base_url ?? item.base_url,
`Surplus offer ${index}.base_url`,
),
status,
capDailyUsd: optionalNonNegativeNumber(item.cap_daily_usd, `Surplus offer ${index}.cap_daily_usd`),
costMultiplierPpm: optionalMultiplierPpm(

View File

@@ -359,44 +359,40 @@ export class SurplusSellerController {
const pending = this.state;
const offers = await this.client.listAllOffers();
const active = offers.filter((offer) => offer.status === "active");
const matches = active
const activeMatches = active
.filter((offer) =>
offer.model === pending.intent.model &&
normalizeUrl(offer.sellerBaseUrl) ===
normalizeUrl(this.config.providerBaseUrl)
)
.sort((left, right) => left.id.localeCompare(right.id));
if (matches.length === 0) {
const inactiveMatch = offers.some((offer) =>
const inactiveMatches = offers
.filter((offer) =>
offer.status === "inactive" &&
offer.model === pending.intent.model &&
normalizeUrl(offer.sellerBaseUrl) ===
normalizeUrl(this.config.providerBaseUrl)
);
if (inactiveMatch && active.length === 0) {
const selecting: SurplusControllerState = {
version: 1,
phase: "selecting",
};
await this.stateStore.save(selecting);
this.state = selecting;
this.markSuccess("suspended", null, null);
return;
}
)
.sort((left, right) => left.id.localeCompare(right.id));
const primary = activeMatches[0] ?? inactiveMatches[0];
if (!primary) {
if (active.length > 0) await this.pauseOffers(active);
throw new Error(
"Create result is ambiguous and no matching offer is visible; refusing another POST",
);
}
const primary = matches[0];
await this.pauseOffers(active.filter((offer) => offer.id !== primary.id));
const confirmedOffers = await this.client.listAllOffers();
const confirmedActive = confirmedOffers.filter(
(offer) => offer.status === "active",
);
if (confirmedActive.length !== 1 || confirmedActive[0].id !== primary.id) {
const expectedActive = primary.status === "active" ? 1 : 0;
if (
confirmedActive.length !== expectedActive ||
(expectedActive === 1 && confirmedActive[0].id !== primary.id)
) {
await this.pauseOffers(confirmedActive);
throw new Error("Adopted Surplus create did not reconcile to one active offer");
throw new Error("Adopted Surplus create did not reconcile safely");
}
const adopted: SurplusControllerState = {
version: 1,
@@ -413,6 +409,10 @@ export class SurplusSellerController {
throw error;
}
this.state = adopted;
if (primary.status === "inactive") {
await this.manageActiveOffer();
return;
}
this.markSuccess("active", null, {
intent: adopted.intent,
offerId: adopted.offerId,
@@ -492,7 +492,8 @@ export class SurplusSellerController {
outputMicroUsdPer1m: quote.outputMicroUsdPer1m,
};
const priceChanged =
managed.costMultiplierPpm !== quote.costMultiplierPpm ||
managed.costMultiplierPpm === null ||
Math.abs(managed.costMultiplierPpm - quote.costMultiplierPpm) > 1 ||
managed.capDailyUsd !== Number(this.config.perOfferCapUsd);
if (this.config.live && priceChanged) {
await this.client.updateOffer(currentState.offerId, {

View File

@@ -260,6 +260,42 @@ describe("SurplusSellerController", () => {
await restarted.stop();
});
it("adopts and resumes an inactive offer after an ambiguous create", async () => {
const client = new FakeSurplusClient();
client.offer = {
id: "managed-offer",
model: "beta-model",
sellerBaseUrl: "https://openrouter.ai/api/v1",
status: "inactive",
capDailyUsd: 1,
costMultiplierPpm: 49_950,
inputMicroUsdPer1m: 3_972,
outputMicroUsdPer1m: 7_944,
};
const store = new MemoryStateStore();
store.state = {
version: 1,
phase: "create_pending",
intent: {
model: "beta-model",
providerModelId: "vendor/beta-model",
costMultiplierPpm: 49_950,
inputMicroUsdPer1m: 3_972,
outputMicroUsdPer1m: 7_944,
dailyCapUsd: 1,
idempotencyKey: "create-1",
createdAt: "2026-08-27T10:00:00.000Z",
baselineTrades24h: 0,
},
};
await controller(client, store).runOnce();
expect(client.createCalls).toBe(0);
expect(client.offer.status).toBe("active");
expect(store.state.phase).toBe("active");
});
it("never retries an ambiguous create when no matching offer is visible", async () => {
const client = new FakeSurplusClient();
client.createError = new SurplusHttpError("network lost", null);