@sema-agent/core
Version:
Stateless, task-oriented AI agent core
41 lines (40 loc) • 2.21 kB
JavaScript
import { deliverEngineNotice } from "../engine-notice.js";
import { readMember } from "../thrown-value.js";
export function createFinishReasonSeat(onNotice, ids) {
const announced = new Set();
const once = (key) => {
if (announced.has(key))
return false;
announced.add(key);
return true;
};
return {
observe(message) {
const word = readMember(message, "finishReasonUnknown");
if (typeof word === "string" && once(`unknown\u0000${word}`)) {
deliverEngineNotice(onNotice, {
code: "route.finish_reason_unknown",
message: `The provider ended a response with "${word}", a finish reason this engine does not read. The turn was kept ` +
`as the model left it, but the provider may have cut it short — the text above may be incomplete.`,
detail: { finishReason: word, sessionId: ids.sessionId, runId: ids.runId },
});
}
const late = readMember(message, "streamEndIgnored");
const statement = readMember(late, "statement");
const lateWord = readMember(late, "word");
const frames = readMember(late, "frames");
if ((statement === "ending" || statement === "error" || statement === "content") &&
typeof lateWord === "string" &&
typeof frames === "number" &&
once(`late\u0000${statement}\u0000${lateWord}`)) {
const what = statement === "error" ? `an error "${lateWord}"` : statement === "ending" ? `the ending "${lateWord}"` : `${lateWord} content`;
deliverEngineNotice(onNotice, {
code: "route.stream_end_ignored",
message: `After the provider had already ended a response, its stream sent more (first: ${what}; ${frames} frame${frames === 1 ? "" : "s"} in all). ` +
`The first ending stands; nothing after it was added to the turn.`,
detail: { statement, word: lateWord, frames, sessionId: ids.sessionId, runId: ids.runId },
});
}
},
};
}