Each session sends its first two model calls to gpt-5.6-terra. Guest sessions are in that count. The plan does not enter the decision, and neither does the id the picker sent. What matters is how many forced slots this session has already used.
The client still receives the id it asked for. The UI stores it as requestedModel. The suggestion-end event carries the same field. Read only those two and a forced call looks like any other call.
The slot counter moves before the stream starts, so a failed call still spends a slot. If the first two calls both fail, the third goes to the model the client requested.
A retry on that requested model happens only when nothing has reached the client yet and the error is not a timeout. Timeout on the forced model stops there. So does a failure after the client has already seen output. A retry that does run does not give the slot back.
Daily spend has a kill switch, and it fails closed. If that check cannot be read, the call is not sent. Forced slots left on the session do not change that.
const FORCED_MODEL = "gpt-5.6-terra";
const FORCED_CALLS = 2;
async function runCall(ctx, deps) {
const spend = await deps.readDailySpend().catch(() => null);
if (spend === null || spend.exceeded) {
throw new Error("spend blocked");
}
const forced = await deps.claimSlot(ctx.sessionId, FORCED_CALLS);
const model = forced ? FORCED_MODEL : ctx.requestedModel;
let visible = false;
try {
await deps.stream(model, () => {
visible = true;
});
} catch (err) {
if (!(forced && !visible && !deps.isTimeout(err))) throw err;
await deps.stream(ctx.requestedModel, () => {});
}
deps.emitSuggestionEnd({ requestedModel: ctx.requestedModel });
}
The sketch is not the production code. claimSlot runs before stream. emitSuggestionEnd always reports ctx.requestedModel. Spent slots and successful calls are different counts.
This path is how LiveSuggest picks the model for a suggestion.
Top comments (1)
the detail that a failed call still burns a forced slot is the part i'd have missed. makes sense to claim it before the stream starts, but it's easy for users to think the picker is being ignored when it's really the counter. might be worth surfacing that in the ui somewhere.