Files
agents-marketplace/supplier/src/surplus/selection.ts
2026-08-27 13:07:16 +02:00

112 lines
3.2 KiB
TypeScript

import type {
SurplusDiscoveredModel,
SurplusMarketSummary,
SurplusOrderBookOffer,
} from "./client.js";
import {
quoteSurplusPrice,
usdPer1mToMicroUsd,
type SurplusCompetitor,
type SurplusPriceQuote,
} from "./policy.js";
export interface SurplusModelCandidate {
model: string;
providerModelId: string;
upstreamInputMicroUsdPer1m: number;
upstreamOutputMicroUsdPer1m: number;
requests24h: number;
volume24h: number;
summaryQuote: SurplusPriceQuote;
}
export function selectSurplusModelCandidates(
discovered: SurplusDiscoveredModel[],
markets: SurplusMarketSummary[],
recoveryBps: number,
undercutBps: number,
): SurplusModelCandidate[] {
const marketByModel = new Map(markets.map((market) => [market.model, market]));
const candidates: SurplusModelCandidate[] = [];
for (const model of discovered) {
const market = marketByModel.get(model.model);
if (
!market ||
!model.supported ||
model.providerModelId === "" ||
model.availabilityStatus !== "available" ||
model.modelType !== "text" ||
model.priceUnit !== "M" ||
model.priceVariable ||
model.inputUsdPer1m <= 0 ||
model.outputUsdPer1m <= 0 ||
market.healthySellerCount <= 0 ||
market.bestInputMicroUsdPer1m <= 0 ||
market.bestOutputMicroUsdPer1m <= 0
) {
continue;
}
const inputPrice = usdPer1mToMicroUsd(
model.inputUsdPer1m,
`${model.model} discovery input price`,
);
const outputPrice = usdPer1mToMicroUsd(
model.outputUsdPer1m,
`${model.model} discovery output price`,
);
const quote = quoteSurplusPrice({
upstreamInputMicroUsdPer1m: inputPrice,
upstreamOutputMicroUsdPer1m: outputPrice,
recoveryBps,
undercutBps,
competitors: [{
id: `summary:${model.model}`,
inputMicroUsdPer1m: market.bestInputMicroUsdPer1m,
outputMicroUsdPer1m: market.bestOutputMicroUsdPer1m,
}],
});
if (!quote.competitive) continue;
candidates.push({
model: model.model,
providerModelId: model.providerModelId,
upstreamInputMicroUsdPer1m: inputPrice,
upstreamOutputMicroUsdPer1m: outputPrice,
requests24h: market.requests24h,
volume24h: market.volume24h,
summaryQuote: quote,
});
}
return candidates.sort(compareCandidates);
}
export function eligibleCompetitors(
offers: SurplusOrderBookOffer[],
sellerWallet: string,
ownOfferIds: ReadonlySet<string>,
): SurplusCompetitor[] {
const normalizedWallet = sellerWallet.toLowerCase();
return offers
.filter((offer) =>
offer.available &&
offer.healthy &&
offer.trusted &&
offer.seller !== normalizedWallet &&
!ownOfferIds.has(offer.id)
)
.map((offer) => ({
id: offer.id,
inputMicroUsdPer1m: offer.inputMicroUsdPer1m,
outputMicroUsdPer1m: offer.outputMicroUsdPer1m,
}));
}
function compareCandidates(
left: SurplusModelCandidate,
right: SurplusModelCandidate,
): number {
return right.requests24h - left.requests24h ||
right.volume24h - left.volume24h ||
left.model.localeCompare(right.model) ||
left.providerModelId.localeCompare(right.providerModelId);
}