Files
NetMesh/infrastructure/ai/shared/streamStateErrors.ts

51 lines
2.1 KiB
TypeScript
Raw Permalink Normal View History

/**
* Helpers for detecting Vercel AI SDK internal stream-state errors.
*
* Background issue #1101 follow-up:
*
* When a third-party Anthropic-compat backend (DeepSeek's
* `deepseek-v4-flash` is the canonical offender) streams thinking
* deltas without first emitting a `reasoning-start` content-block
* signal, the Vercel AI SDK's reasoning state machine has nothing
* registered for the incoming `part.id` and enqueues an
* `error` chunk on `stream` with the text
* `reasoning part <id> not found` once per orphan delta. The
* analogous error exists for text parts.
*
* These chunks are *internal SDK bookkeeping noise*, not user-facing
* errors. Worse, treating them as real errors (adding a placeholder
* assistant message for each) breaks tool_use/tool_result contiguity
* on the next turn: the Anthropic message grouper splits the
* tool-result `role: 'tool'` messages from their parent tool_use
* `role: 'assistant'`, and the backend responds with
* `400 messages.N: tool_use ids were found without tool_result blocks
* immediately after`.
*
* Filtering these specific errors at the chunk-handler boundary
* stops the cascade: the orphan deltas are dropped silently (the SDK
* continues processing other chunks), no fake assistant messages
* land in history, and the next turn's request stays well-formed.
*/
const STATE_ERROR_PATTERN = /^(?:reasoning|text)\s+part\s+\S+\s+not\s+found$/i;
/**
* Return true if `error` is one of the SDK's internal stream-state
* tracking errors (e.g. an out-of-order reasoning delta). Accepts
* the loose `unknown` shape that comes off the chunk so callers
* don't need to narrow upstream.
*/
export function isSdkStreamStateError(error: unknown): boolean {
if (typeof error === 'string') {
return STATE_ERROR_PATTERN.test(error.trim());
}
if (error instanceof Error) {
return STATE_ERROR_PATTERN.test(error.message.trim());
}
if (error && typeof error === 'object' && 'message' in error) {
const msg = (error as { message?: unknown }).message;
if (typeof msg === 'string') return STATE_ERROR_PATTERN.test(msg.trim());
}
return false;
}