function isOpenAIToolCallType(type: unknown): boolean { return type === "toolCall" || type === "toolUse" || type === "functionCall";
}
/** * OpenAI can reject replayed `function_call` items with an `fc_*` id if the * matching `reasoning` item is absent in the same assistant turn. * * When that pairing is missing, strip the `|fc_*` suffix from tool call ids so * pi-ai omits `function_call.id` on replay.
*/
export function downgradeOpenAIFunctionCallReasoningPairs(
messages: AgentMessage[],
): AgentMessage[] {
let changed = false; const rewrittenMessages: AgentMessage[] = [];
let pendingRewrittenIds: Map<string, string> | null = null;
for (const msg of messages) { if (!msg || typeof msg !== "object") {
pendingRewrittenIds = null;
rewrittenMessages.push(msg); continue;
}
const role = (msg as { role?: unknown }).role; if (role === "assistant") { const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>; if (!Array.isArray(assistantMsg.content)) {
pendingRewrittenIds = null;
rewrittenMessages.push(msg); continue;
}
const localRewrittenIds = new Map<string, string>();
let seenReplayableReasoning = false;
let assistantChanged = false; const nextContent = assistantMsg.content.map((block) => { if (!block || typeof block !== "object") { return block;
}
/** * OpenAI Responses API can reject transcripts that contain a standalone `reasoning` item id * without the required following item, or stale encrypted reasoning after a model route switch. * * OpenClaw persists provider-specific reasoning metadata in `thinkingSignature`; if that metadata * is incomplete or no longer replay-safe, drop the block to keep history usable.
*/
export function downgradeOpenAIReasoningBlocks(
messages: AgentMessage[],
options: DowngradeOpenAIReasoningBlocksOptions = {},
): AgentMessage[] {
let anyChanged = false; const out: AgentMessage[] = [];
for (const msg of messages) { if (!msg || typeof msg !== "object") {
out.push(msg); continue;
}
const role = (msg as { role?: unknown }).role; if (role !== "assistant") {
out.push(msg); continue;
}
const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>; if (!Array.isArray(assistantMsg.content)) {
out.push(msg); continue;
}
let changed = false;
type AssistantContentBlock = (typeof assistantMsg.content)[number];
const nextContent: AssistantContentBlock[] = []; for (let i = 0; i < assistantMsg.content.length; i++) { const block = assistantMsg.content[i]; if (!block || typeof block !== "object") {
nextContent.push(block as AssistantContentBlock); continue;
} const record = block as OpenAIThinkingBlock; if (record.type !== "thinking") {
nextContent.push(block); continue;
} const signature = parseOpenAIReasoningSignature(record.thinkingSignature); if (!signature) {
nextContent.push(block); continue;
} if (options.dropReplayableReasoning) {
changed = true; continue;
} if (hasFollowingNonThinkingBlock(assistantMsg.content, i)) {
nextContent.push(block); continue;
}
changed = true;
}
if (!changed) {
out.push(msg); continue;
}
anyChanged = true; if (nextContent.length === 0) { continue;
}
out.push({ ...assistantMsg, content: nextContent } as AgentMessage);
}
return anyChanged ? out : messages;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-05-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.