@sema-agent/core
Version:
Stateless, task-oriented AI agent core
777 lines (776 loc) • 44.5 kB
JavaScript
import {} from "../internal/llm.js";
import { BrainError } from "./errors.js";
import { startsWithInputTooLongAnchor } from "./input-too-long.js";
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
import { STREAM_TORN_WARNING, createRepetitionPoll, createStreamEndLedger, modelOutputInvalidOf, noteResponseModel, nameMissingCause, parseStreamedToolArgs, placeTurnNotes, truncationCauseClause } from "./stream-shared.js";
import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } from "./tool-call-id.js";
import { emitBrainTelemetry, reportReasoningWireFacts } from "./status-sink.js";
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, modelSupportsVision, sendableImages } from "./media-degrade.js";
import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders, takeHeaderCasefold } from "./request-params.js";
import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
import { requestRouteOf } from "./request-route.js";
import { MIN_THINKING_TOKENS, budgetCapSkipsThinking, declaredEffortLevels, reasoningBudgetShare, reasoningRequestCarried, resolveEffort } from "./reasoning.js";
import { runStreamingBrain } from "./stream-engine.js";
import { refuseRetiredTimeoutKnobs } from "./timeout.js";
import { resolveFetch } from "./transport.js";
function thinkingBudget(maxTokens, share, fixed, hardCap = false) {
const max = hardCap ? maxTokens : Math.max(maxTokens, MIN_THINKING_TOKENS * 2);
const want = fixed ?? Math.floor(max * share);
const budget = Math.min(Math.max(MIN_THINKING_TOKENS, want), max - 1);
return { max, budget };
}
const EPHEMERAL = { type: "ephemeral" };
const DOCUMENT_OMITTED_NO_VISION = "[PDF document omitted: the serving model does not support document input; extract the text via Bash instead (e.g. `pdftotext`).]";
function textOf(content) {
return typeof content === "string"
? content
: content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
}
function toUserBlocks(content, supportsVision = true) {
if (typeof content === "string") {
return content.length > 0 ? [{ type: "text", text: content }] : [];
}
const blocks = [];
let omitted = 0;
for (const c of content) {
if (c.type === "text" && typeof c.text === "string" && c.text.length > 0) {
blocks.push({ type: "text", text: c.text });
}
else if (c.type === "image" && c.data && c.mimeType) {
if (supportsVision)
blocks.push({ type: "image", source: { type: "base64", media_type: c.mimeType, data: c.data } });
else {
omitted++;
blocks.push({ type: "text", text: IMAGE_OMITTED_NO_VISION });
}
}
}
if (omitted > 0)
emitBrainTelemetry({ kind: "vision_placeholder", count: omitted });
return blocks;
}
function toAnthropicMessages(ctx, model) {
const supportsVision = modelSupportsVision(model);
const raw = [];
let pendingDocs = [];
const flushDocs = () => {
if (pendingDocs.length === 0)
return;
raw.push({ role: "user", content: pendingDocs });
pendingDocs = [];
};
const resultWireId = providerResultIds(ctx.messages);
for (const m of ctx.messages) {
if (m.role !== "toolResult")
flushDocs();
if (m.role === "user") {
const ub = toUserBlocks(m.content, supportsVision);
raw.push({ role: "user", content: ub.length ? ub : [{ type: "text", text: NO_CONTENT }] });
}
else if (m.role === "assistant") {
const blocks = [];
for (const c of m.content) {
if (c.type === "thinking") {
const tc = c;
const sigRaw = tc.thinkingSignature;
const sig = typeof sigRaw === "string" && sigRaw.length > 0 ? sigRaw : undefined;
if (tc.redacted) {
if (sig)
blocks.push({ type: "redacted_thinking", data: sig });
}
else if (sig) {
blocks.push({ type: "thinking", thinking: c.thinking, signature: sig });
}
}
else if (c.type === "text" && c.text) {
blocks.push({ type: "text", text: c.text });
}
else if (c.type === "toolCall") {
blocks.push({ type: "tool_use", id: providerToolCallId(c), name: c.name, input: c.arguments ?? {} });
}
}
raw.push({ role: "assistant", content: blocks.length ? blocks : [{ type: "text", text: NO_CONTENT }] });
}
else if (m.role === "toolResult") {
const parts = Array.isArray(m.content) ? m.content : undefined;
const docs = parts ? parts.filter((c) => c.type === "document") : [];
const rest = docs.length > 0 && parts ? parts.filter((c) => c.type !== "document") : m.content;
const hasImage = !m.isError && Array.isArray(rest) && rest.some((c) => c.type === "image");
let inner = hasImage ? toUserBlocks(rest, supportsVision) : textOf(rest);
if (docs.length > 0 && !m.isError) {
if (supportsVision) {
for (const d of docs) {
if (d.data && d.mimeType)
pendingDocs.push({ type: "document", source: { type: "base64", media_type: d.mimeType, data: d.data } });
}
const pointer = "(PDF document attached below)";
if (typeof inner === "string")
inner = inner ? `${inner}\n${pointer}` : pointer;
else
inner.push({ type: "text", text: pointer });
}
else {
emitBrainTelemetry({ kind: "document_placeholder", count: docs.length });
const note = DOCUMENT_OMITTED_NO_VISION;
if (typeof inner === "string")
inner = inner ? `${inner}\n${note}` : note;
else
inner.push({ type: "text", text: note });
}
}
if (m.isError) {
const note = errorResultMediaNote({ images: sendableImages(Array.isArray(rest) ? rest : []).length, documents: docs.length });
if (note !== undefined) {
if (typeof inner === "string")
inner = inner ? `${inner}\n${note}` : note;
else
inner.push({ type: "text", text: note });
}
}
if (typeof inner === "string" ? inner.length === 0 : inner.length === 0)
inner = NO_CONTENT;
raw.push({
role: "user",
content: [
{
type: "tool_result",
tool_use_id: resultWireId(m.toolCallId),
content: inner,
...(m.isError ? { is_error: true } : {}),
},
],
});
}
}
flushDocs();
const merged = [];
for (const msg of raw) {
const prev = merged[merged.length - 1];
if (prev && prev.role === msg.role) {
prev.content.push(...msg.content);
}
else {
merged.push({ role: msg.role, content: [...msg.content] });
}
}
return merged;
}
function toAnthropicTools(ctx, cacheBreakpoints) {
if (!ctx.tools || ctx.tools.length === 0) {
return undefined;
}
return ctx.tools.map((t, i) => ({
name: t.name,
description: t.description,
input_schema: t.parameters,
...(cacheBreakpoints && i === ctx.tools.length - 1 ? { cache_control: EPHEMERAL } : {}),
}));
}
const NO_CONTENT = "(no content)";
function mapStopReason(reason, hadToolUse) {
if (hadToolUse || reason === "tool_use")
return "toolUse";
if (reason === "max_tokens")
return "length";
return "stop";
}
function anthropicStreamErrorCode(t) {
switch (t) {
case "rate_limit_error":
return "rate_limit";
case "authentication_error":
case "permission_error":
return "auth";
case "invalid_request_error":
return "invalid_request";
case "request_too_large":
return "invalid_request";
default:
return "server";
}
}
function computeUsage(model, u) {
const input = u.input_tokens ?? 0;
const output = u.output_tokens ?? 0;
const cacheRead = u.cache_read_input_tokens ?? 0;
const cacheWrite = u.cache_creation_input_tokens ?? 0;
const c = model.cost;
const costInput = (input / 1e6) * (c?.input ?? 0);
const costOutput = (output / 1e6) * (c?.output ?? 0);
const costCacheRead = (cacheRead / 1e6) * (c?.cacheRead ?? 0);
const costCacheWrite = (cacheWrite / 1e6) * (c?.cacheWrite ?? 0);
return {
input,
output,
cacheRead,
cacheWrite,
totalTokens: input + output + cacheRead + cacheWrite,
cost: {
input: costInput,
output: costOutput,
cacheRead: costCacheRead,
cacheWrite: costCacheWrite,
total: costInput + costOutput + costCacheRead + costCacheWrite,
},
};
}
function closeToolUseBlock(acc, responseIds, closed) {
if (acc.closedTc)
return acc.closedTc;
if (!acc.toolName)
return undefined;
const args = parseStreamedToolArgs(acc.toolJson, closed);
if (args === undefined)
return undefined;
const { engineId, wireId } = admitToolCallId(acc.toolId, "toolu_", responseIds);
acc.closedTc = { type: "toolCall", id: engineId, ...(wireId !== engineId ? { wireId } : {}), name: acc.toolName, arguments: args };
return acc.closedTc;
}
export const ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com";
const ANTHROPIC_KEY_HEADER = "x-api-key";
export function createAnthropicBrain(given = {}) {
const config = brainConfigSnapshot(given, "createAnthropicBrain config");
assertServableRequestRoot(config.baseUrl, "config.baseUrl");
refuseRetiredTimeoutKnobs(config, "construction", "createAnthropicBrain config");
const doFetch = resolveFetch(config);
const cacheBreakpoints = config.cacheBreakpoints !== false;
const stream = (model, context, options) => {
let thinkingRequested = false;
let sentMaxTokens;
let sentMaxTokensLane = "config-default";
return runStreamingBrain({
model,
doFetch,
signal: options?.signal,
config,
httpLabel: "anthropic",
maxRetries: options?.maxRetries,
stallTimeouts: options?.stallTimeouts,
buildRequest: (overrides) => {
const routed = { id: model.id, baseUrl: model.baseUrl };
const { root, origin: rootOrigin } = resolveRequestRootOf(routed.baseUrl, config.baseUrl, ANTHROPIC_DEFAULT_BASE_URL);
const modelHeaders = headerBagOnce(model.headers);
const optionsHeaders = headerBagOnce(options?.headers);
const routeCredential = resolveRouteCredential({
optionsApiKey: options?.apiKey,
optionsHeaders,
modelHeaders,
configApiKey: config.apiKey,
configHeaders: config.headers,
keyHeader: ANTHROPIC_KEY_HEADER,
});
const routeAdmission = admitRouteRequest(routed, routeCredential, { baseUrl: config.baseUrl, laneDefault: ANTHROPIC_DEFAULT_BASE_URL });
const apiKey = routeCredential.apiKey;
const nonEmptyBlocks = context.systemBlocks?.filter((b) => b.text.length > 0);
const system = nonEmptyBlocks?.length
? nonEmptyBlocks.map((b) => ({
type: "text",
text: b.text,
...(cacheBreakpoints && b.cacheControlBoundary ? { cache_control: EPHEMERAL } : {}),
}))
: context.systemPrompt
? [{ type: "text", text: context.systemPrompt, ...(cacheBreakpoints ? { cache_control: EPHEMERAL } : {}) }]
: undefined;
const body = {
model: model.id,
max_tokens: overrides?.maxOutputTokens ?? options?.maxTokens ?? model.maxTokens ?? config.defaultMaxTokens ?? 4096,
messages: toAnthropicMessages(context, model),
stream: true,
};
if (system)
body.system = system;
const tools = toAnthropicTools(context, cacheBreakpoints);
if (tools)
body.tools = tools;
const anthCompat = (model.compat ?? {});
if (options?.temperature !== undefined && anthCompat.supportsTemperature !== false) {
body.temperature = options.temperature;
}
let builtReasoningFacts;
let thinkingOn = false;
if (reasoningRequestCarried(model, options?.reasoning)) {
if (anthCompat.thinkingMode === "adaptive") {
body.thinking = { type: "adaptive" };
thinkingOn = true;
}
else {
const hardCap = overrides?.maxOutputTokens !== undefined || options?.maxTokens !== undefined;
builtReasoningFacts = { outputCapTokens: body.max_tokens, hardOutputCap: hardCap };
if (budgetCapSkipsThinking(builtReasoningFacts.outputCapTokens, builtReasoningFacts.hardOutputCap)) {
body.thinking = undefined;
delete body.thinking;
}
else {
const share = config.thinkingBudgetShare ?? reasoningBudgetShare(options.reasoning);
const { max, budget } = thinkingBudget(body.max_tokens, share, config.thinkingBudgetTokens, hardCap);
body.max_tokens = max;
body.thinking = { type: "enabled", budget_tokens: budget };
thinkingOn = true;
}
}
if (thinkingOn)
delete body.temperature;
}
else if (model.reasoning && options?.reasoning === "off" && anthCompat.thinkingMode !== "adaptive") {
body.thinking = { type: "disabled" };
}
const betas = [];
let sendEffortBeta = false;
const declaredEffort = declaredEffortLevels(anthCompat.effortLevels);
if (declaredEffort !== undefined && reasoningRequestCarried(model, options?.reasoning)) {
const { effective } = resolveEffort(options.reasoning, declaredEffort);
const wireEffort = effective === "minimal" ? "low" : effective;
body.output_config = { effort: wireEffort };
sendEffortBeta = true;
}
if (sendEffortBeta)
betas.push("effort-2025-11-24");
if (anthCompat.contextManagement && thinkingOn) {
body.context_management = { edits: [{ type: "clear_thinking_20251015", keep: "all" }] };
betas.push("context-management-2025-06-27");
}
if (anthCompat.interleavedThinking) {
betas.push("interleaved-thinking-2025-05-14");
}
const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
applyRouteCredentialHeaders(headers, routeCredential);
if (betas.length > 0) {
const existing = (takeHeaderCasefold(headers, "anthropic-beta") ?? "").split(",").map((b) => b.trim()).filter(Boolean);
for (const beta of betas) {
if (!existing.includes(beta))
existing.push(beta);
}
headers["anthropic-beta"] = existing.join(",");
}
lockHeader(headers, "content-type", "application/json");
lockHeader(headers, "anthropic-version", config.version ?? "2023-06-01");
if (apiKey)
lockHeader(headers, ANTHROPIC_KEY_HEADER, apiKey);
const builtThinkingRequested = body.thinking !== undefined;
const builtMaxTokens = body.max_tokens;
const builtMaxTokensLane = overrides?.maxOutputTokens !== undefined
? "engine"
: options?.maxTokens !== undefined
? "options"
: model.maxTokens !== undefined
? "model"
: "config-default";
const { body: wire, dropped: extraBodyDropped } = applyExtraBody(body, model.extraBody, ANTHROPIC_RESERVED);
return {
url: joinRequestUrl(root, "/v1/messages"),
headers,
body: JSON.stringify(wire),
extraBodyDropped,
route: requestRouteOf(routeCredential, rootOrigin),
outputCapTokens: effectiveOutputCap(wire, OUTPUT_CAP_KEYS.anthropic),
capDerivedPaths: [...OUTPUT_CAP_KEYS.anthropic, "thinking.budget_tokens"],
onCommitted: () => {
routeAdmission();
thinkingRequested = builtThinkingRequested;
sentMaxTokens = builtMaxTokens;
sentMaxTokensLane = builtMaxTokensLane;
if (builtReasoningFacts !== undefined)
reportReasoningWireFacts(builtReasoningFacts);
},
};
},
makeParser: (ctrl) => {
const { out, partial } = ctrl;
const blocks = new Map();
const responseIds = responseToolCallIds();
const usageRaw = {};
let stopReason = null;
let stopDetails;
let nextContentIndex = 0;
let malformedFrames = 0;
let malformedSample;
let degenerate = false;
let degenTextBlock;
const rep = createRepetitionPoll();
let lastDegenCheck = 0;
let lastDegenCheckThinking = 0;
const detectRep = config.detectRepetition !== false;
let messageStopped = false;
const afterEnd = createStreamEndLedger();
const streamEnded = () => stopReason != null || messageStopped;
const mergeDeltaUsage = (u) => {
if (!u)
return;
if (u.output_tokens !== undefined)
usageRaw.output_tokens = u.output_tokens;
if (u.input_tokens != null && u.input_tokens > 0)
usageRaw.input_tokens = u.input_tokens;
if (u.cache_read_input_tokens != null && u.cache_read_input_tokens > 0)
usageRaw.cache_read_input_tokens = u.cache_read_input_tokens;
if (u.cache_creation_input_tokens != null && u.cache_creation_input_tokens > 0) {
usageRaw.cache_creation_input_tokens = u.cache_creation_input_tokens;
}
};
const reportedUsage = () => ({
usage: computeUsage(model, usageRaw),
...(Object.keys(usageRaw).length === 0 ? { usageMissing: true } : {}),
});
const wordOf = (v) => (typeof v === "string" && v !== "" ? v : "?");
const refuseAfterEnd = (evt) => {
switch (evt.type) {
case "message_delta": {
mergeDeltaUsage(evt.usage);
const late = evt.delta?.stop_reason;
if (typeof late === "string" && late !== "" && late !== stopReason)
afterEnd.note("ending", late);
return;
}
case "content_block_start":
afterEnd.note("content", wordOf(evt.content_block?.type));
return;
case "content_block_delta":
afterEnd.note("content", wordOf(evt.delta?.type));
return;
case "content_block_stop":
case "message_start":
afterEnd.note("content", evt.type);
return;
case "error":
afterEnd.note("error", wordOf(evt.error?.type));
return;
default:
return;
}
};
const assembleTurn = (stampOn) => {
const finalContent = [];
const toolCalls = [];
const malformed = [];
const unnamed = [];
let anyText = false;
let reasoningSeen = false;
const endedCut = stopReason === "max_tokens" || stopReason == null;
for (const [, acc] of [...blocks.entries()].sort((a, b) => a[0] - b[0])) {
if (acc.type === "thinking") {
if (acc.text.length > 0 || acc.redacted === true)
reasoningSeen = true;
const block = { type: "thinking", thinking: acc.text };
const sig = acc.signature || acc.startSignature;
if (sig)
block.thinkingSignature = sig;
if (acc.redacted)
block.redacted = true;
finalContent.push(block);
}
else if (acc.type === "text") {
const text = acc === degenTextBlock && rep.cut !== undefined ? trimDegenerateTail(acc.text, rep.cut) : acc.text;
if (text) {
finalContent.push({ type: "text", text });
if (text.trim())
anyText = true;
}
}
else if (acc.type === "tool_use") {
const closed = acc.stopped === true || !endedCut;
if (!acc.toolName) {
(nameMissingCause(closed) === "truncated" ? malformed : unnamed).push(`id="${acc.toolId || "?"}"(${acc.toolJson.slice(0, 200)})`);
}
else {
const tc = closeToolUseBlock(acc, responseIds, closed);
if (tc) {
toolCalls.push(tc);
finalContent.push(tc);
}
else {
malformed.push(`${acc.toolName}(${acc.toolJson.slice(0, 200)})`);
}
}
}
}
const notes = [];
if (malformed.length > 0) {
notes.push({ cause: "truncated", count: malformed.length, text: `\n[note: ${malformed.length} tool call(s) were truncated (stop_reason="${stopReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]` });
}
if (unnamed.length > 0) {
notes.push({ cause: "unnamed", count: unnamed.length, text: `\n[note: ${unnamed.length} tool call(s) arrived with no tool name and were dropped — re-issue them next turn with an explicit tool name: ${unnamed.join("; ")}]` });
}
if (malformedFrames > 0) {
notes.push({ cause: "bad_frame", count: malformedFrames, text: `\n[note: ${malformedFrames} provider stream frame(s) were not valid JSON and were dropped — the streamed output may be missing content (first sample: ${JSON.stringify(malformedSample)})]` });
}
placeTurnNotes(stampOn, finalContent, notes);
return { content: finalContent, toolCalls, malformed, unnamed, anyText, reasoningSeen };
};
const handleEvent = (evt) => {
if (streamEnded()) {
refuseAfterEnd(evt);
return;
}
switch (evt.type) {
case "message_start": {
const msg = evt.message;
noteResponseModel(partial, msg?.model, model.id);
const u = msg?.usage;
if (u) {
usageRaw.input_tokens = u.input_tokens;
usageRaw.cache_read_input_tokens = u.cache_read_input_tokens;
usageRaw.cache_creation_input_tokens = u.cache_creation_input_tokens;
}
break;
}
case "content_block_start": {
ctrl.sawContentToken();
const idx = evt.index;
const cb = evt.content_block;
const acc = { type: "text", text: "", signature: "", toolId: "", toolName: "", toolJson: "" };
if (cb.type === "thinking" || cb.type === "redacted_thinking") {
acc.type = "thinking";
if (cb.type === "redacted_thinking") {
acc.redacted = true;
acc.signature = cb.data ?? "";
}
else {
if (typeof cb.thinking === "string" && cb.thinking !== "")
acc.text = cb.thinking;
if (typeof cb.signature === "string" && cb.signature !== "")
acc.startSignature = cb.signature;
}
acc.pb = { type: "thinking", thinking: "" };
partial.content = [acc.pb];
out.push({ type: "thinking_start", contentIndex: idx, partial: { ...partial } });
if (acc.text !== "") {
acc.pb.thinking = acc.text;
out.push({ type: "thinking_delta", contentIndex: idx, delta: acc.text, partial: { ...partial } });
}
}
else if (cb.type === "tool_use") {
acc.type = "tool_use";
acc.toolId = cb.id ?? "";
acc.toolName = cb.name ?? "";
}
else {
acc.type = "text";
acc.pb = { type: "text", text: "" };
partial.content = [acc.pb];
out.push({ type: "text_start", contentIndex: idx, partial: { ...partial } });
}
blocks.set(idx, acc);
nextContentIndex = Math.max(nextContentIndex, idx + 1);
break;
}
case "content_block_delta": {
ctrl.sawContentToken();
const idx = evt.index;
const acc = blocks.get(idx);
if (!acc)
break;
const d = evt.delta;
if (d.type === "text_delta" && d.text) {
acc.text += d.text;
if (acc.pb && acc.pb.type === "text")
acc.pb.text = acc.text;
out.push({ type: "text_delta", contentIndex: idx, delta: d.text, partial: { ...partial } });
if (detectRep && !degenerate && acc.text.length - lastDegenCheck >= 64) {
lastDegenCheck = acc.text.length;
if (rep.poll(acc.text)) {
degenerate = true;
degenTextBlock = acc;
ctrl.cancel();
}
}
}
else if (d.type === "thinking_delta" && d.thinking) {
acc.startSignature = undefined;
acc.text += d.thinking;
if (acc.pb && acc.pb.type === "thinking")
acc.pb.thinking = acc.text;
out.push({ type: "thinking_delta", contentIndex: idx, delta: d.thinking, partial: { ...partial } });
if (detectRep && !degenerate && acc.text.length - lastDegenCheckThinking >= 64) {
lastDegenCheckThinking = acc.text.length;
if (rep.poll(acc.text)) {
degenerate = true;
ctrl.cancel();
}
}
}
else if (d.type === "signature_delta" && typeof d.signature === "string" && d.signature.length > 0) {
acc.signature += d.signature;
}
else if (d.type === "input_json_delta" && d.partial_json) {
acc.toolJson += d.partial_json;
}
break;
}
case "content_block_stop": {
const idx = evt.index;
const acc = blocks.get(idx);
if (acc)
acc.stopped = true;
if (acc?.type === "thinking") {
out.push({ type: "thinking_end", contentIndex: idx, content: acc.text, partial: { ...partial } });
}
else if (acc?.type === "text") {
out.push({ type: "text_end", contentIndex: idx, content: acc.text, partial: { ...partial } });
}
else if (acc?.type === "tool_use") {
const tc = closeToolUseBlock(acc, responseIds, true);
if (tc) {
out.push({ type: "toolcall_end", contentIndex: idx, toolCall: tc, partial: { ...partial } });
}
}
break;
}
case "message_delta": {
const d = evt.delta;
if (d?.stop_reason)
stopReason = d.stop_reason;
if (d?.stop_details)
stopDetails = d.stop_details;
mergeDeltaUsage(evt.usage);
break;
}
case "message_stop":
messageStopped = true;
break;
case "error": {
const rawError = evt.error;
const e = typeof rawError === "object" && rawError !== null && !Array.isArray(rawError)
? rawError
: undefined;
const inputTooLong = e?.type === "request_too_large" ||
(e?.type === "invalid_request_error" && typeof e.message === "string" && startsWithInputTooLongAnchor(e.message));
ctrl.keepOutput({ content: assembleTurn({}).content, ...reportedUsage() });
throw new BrainError(anthropicStreamErrorCode(e?.type), `anthropic stream error (${e?.type ?? "unknown"}): ${e?.message ?? ""}`, undefined, { apiError: true, ...(e === undefined ? {} : inputTooLong ? { inputTooLong: true } : { inputTooLongRuledOut: true }) });
}
default:
break;
}
};
return {
snapshot() {
const accs = [...blocks.values()];
const hasSubstantiveText = accs.some((a) => a.type === "text" && a.text.trim() !== "");
const hasCompletedToolCall = accs.some((a) => a.closedTc !== undefined);
const hasThinking = accs.some((a) => a.type === "thinking" && (a.text.length > 0 || a.redacted === true));
return {
hasSubstantiveText,
hasCompletedToolCall,
hasOnlyThinking: hasThinking && !hasSubstantiveText && !hasCompletedToolCall,
terminalFrameSeen: streamEnded(),
};
},
sealForRetry() {
for (const [idx, acc] of [...blocks.entries()].sort((a, b) => a[0] - b[0])) {
if (acc.type === "thinking" && !acc.stopped) {
acc.stopped = true;
out.push({ type: "thinking_end", contentIndex: idx, content: acc.text, partial: { ...partial } });
}
}
},
onLine(rawLine) {
const line = rawLine.trim();
if (!line.startsWith("data:"))
return;
const data = line.slice(5).trim();
if (!data)
return;
let evt;
try {
evt = JSON.parse(data);
}
catch {
malformedFrames++;
malformedSample ??= data.slice(0, 160);
return;
}
handleEvent(evt);
},
finalize(opts) {
const { content: finalContent, toolCalls, malformed, unnamed, anyText, reasoningSeen } = assembleTurn(partial);
const doneReason = mapStopReason(stopReason, toolCalls.length > 0);
const noUsableContent = toolCalls.length === 0 && !anyText;
const toolErrorParts = [];
const capFacts = { capStop: stopReason === "max_tokens", outputTokens: usageRaw.output_tokens, capTokens: sentMaxTokens };
if (malformed.length > 0) {
toolErrorParts.push(`tool call argument(s) not valid JSON (likely truncated, stop_reason="${stopReason ?? "?"}"${truncationCauseClause({ ...capFacts, terminalFrameSeen: streamEnded() })}): ${malformed.join("; ")}`);
}
if (unnamed.length > 0) {
toolErrorParts.push(`tool call(s) arrived with no tool name and cannot be executed (stop_reason="${stopReason ?? "?"}"): ${unnamed.join("; ")}`);
}
const toolError = toolErrorParts.length > 0 ? toolErrorParts.join(" | ") : undefined;
const truncatedEmpty = stopReason === "max_tokens" && noUsableContent && !toolError;
const dynamicCut = truncatedEmpty && options?.maxTokensDynamic === true;
const staticReasoningCut = truncatedEmpty && !dynamicCut && reasoningSeen && options?.staticReasoningCutDowngrade === true;
const safetyCut = stopReason === "refusal";
const contextWindowExceeded = stopReason === "model_context_window_exceeded";
const emptyNoFinish = noUsableContent && stopReason == null && !degenerate;
const maxTokensNote = `effective max_tokens=${sentMaxTokens ?? "?"} (from ${sentMaxTokensLane === "options"
? "options.maxTokens"
: sentMaxTokensLane === "engine"
? "the engine's context-overflow recovery"
: sentMaxTokensLane === "model"
? "model.maxTokens"
: "config default"})`;
const errorMessage = degenerate
? DEGENERATE_MESSAGE
: safetyCut
? `[refusal] response refused by the model (stop_reason="refusal"${stopDetails?.category ? `, category="${stopDetails.category}"` : ""}) — ${stopDetails?.explanation ?? "the output was withheld for policy and is unreliable"}`
: contextWindowExceeded
? `[invalid_request] model context window exceeded (stop_reason="model_context_window_exceeded") — the conversation no longer fits the model's context window`
: (toolError ??
(truncatedEmpty && !dynamicCut && !staticReasoningCut
? thinkingRequested
? `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}) — extended thinking likely consumed the entire budget; raise max_tokens or lower thinkingBudgetShare`
: reasoningSeen
? `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}) — the model emitted reasoning although no thinking was requested (a budget-ignoring gateway?) and it consumed the entire output budget; raise max_tokens`
: `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}); raise max_tokens`
: emptyNoFinish
? `[stream_torn] model stream ended with no content and no stop_reason — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
: undefined));
const errored = degenerate ||
safetyCut ||
contextWindowExceeded ||
emptyNoFinish ||
(toolError !== undefined && noUsableContent) ||
(truncatedEmpty && !dynamicCut && !staticReasoningCut);
const tornWithContent = !errored && opts?.truncated !== true && !streamEnded() && !noUsableContent && !degenerate;
const errorMessageOut = tornWithContent ? (errorMessage ? `${errorMessage}\n${STREAM_TORN_WARNING}` : STREAM_TORN_WARNING) : errorMessage;
const errorKind = degenerate
? "degenerate"
: !safetyCut && contextWindowExceeded
? "input_too_long"
: !safetyCut && toolError === undefined && truncatedEmpty && !dynamicCut && !staticReasoningCut
? "length_empty"
: undefined;
const modelOutputInvalid = errored && toolError !== undefined && !degenerate && !safetyCut && !contextWindowExceeded ? modelOutputInvalidOf({ truncated: malformed.length, unnamed: unnamed.length }, capFacts) : undefined;
const finalMessage = {
...partial,
content: finalContent.length > 0 ? finalContent : [{ type: "text", text: "" }],
stopReason: errored ? "error" : doneReason,
...reportedUsage(),
timestamp: Date.now(),
...(errorMessageOut ? { errorMessage: errorMessageOut } : {}),
...(errorKind ? { errorKind } : {}),
...(modelOutputInvalid !== undefined ? { modelOutputInvalid } : {}),
...(tornWithContent ? { partialFinalized: true } : {}),
...(rep.cut !== undefined || rep.spared.length > 0
? { repetition: { ...(rep.cut !== undefined ? { cut: rep.cut } : {}), ...(rep.spared.length > 0 ? { spared: rep.spared } : {}) } }
: {}),
...(staticReasoningCut ? { staticReasoningCut: true } : {}),
...(afterEnd.ignored !== undefined ? { streamEndIgnored: afterEnd.ignored } : {}),
...(stopDetails
? {
diagnostics: [
...(partial.diagnostics ?? []),
{ type: "stop_details", timestamp: Date.now(), details: { ...stopDetails } },
],
}
: {}),
};
if (errored) {
out.push({ type: "error", reason: "error", error: finalMessage });
}
else {
out.push({ type: "done", reason: doneReason, message: finalMessage });
}
},
};
},
});
};
return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, laneDefault: ANTHROPIC_DEFAULT_BASE_URL, keyHeader: ANTHROPIC_KEY_HEADER }) };
}