n8n
Version:
n8n Workflow Automation Tool
194 lines • 6.77 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initSseStream = initSseStream;
exports.pumpChunks = pumpChunks;
const agents_1 = require("@n8n/agents");
function initSseStream(res) {
res.setHeader('Content-Type', 'text/event-stream; charset=UTF-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');
res.flushHeaders();
res.socket?.setNoDelay?.(true);
const send = (event) => {
res.write(`data: ${JSON.stringify(event)}\n\n`);
res.flush?.();
};
return { send };
}
function toAgentSseMessage(message) {
if (!('content' in message) || !Array.isArray(message.content))
return undefined;
const content = [];
for (const part of message.content) {
if (part.type === 'text' && 'text' in part) {
content.push({ type: 'text', text: part.text });
}
else if (part.type === 'reasoning' && 'text' in part) {
content.push({ type: 'reasoning', text: part.text });
}
}
if (content.length === 0)
return undefined;
return { role: message.role, content };
}
function emitTextLikeChunk(chunk, send) {
switch (chunk.type) {
case 'text-start':
send({ type: 'text-start', id: chunk.id });
break;
case 'text-delta':
if (chunk.delta)
send({ type: 'text-delta', id: chunk.id, delta: chunk.delta });
break;
case 'text-end':
send({ type: 'text-end', id: chunk.id });
break;
case 'reasoning-start':
send({ type: 'reasoning-start', id: chunk.id });
break;
case 'reasoning-delta':
if (chunk.delta)
send({ type: 'reasoning-delta', id: chunk.id, delta: chunk.delta });
break;
case 'reasoning-end':
send({ type: 'reasoning-end', id: chunk.id });
break;
}
}
function handleWorkingMemoryChunk(chunk, ctx) {
const { send, workingMemoryToolCallIds } = ctx;
const isWmName = 'toolName' in chunk && chunk.toolName === agents_1.UPDATE_WORKING_MEMORY_TOOL_NAME;
if (chunk.type === 'tool-input-delta') {
return workingMemoryToolCallIds.has(chunk.toolCallId);
}
if (!isWmName)
return false;
if (chunk.type === 'tool-input-start' || chunk.type === 'tool-call') {
workingMemoryToolCallIds.add(chunk.toolCallId);
return true;
}
if (chunk.type === 'tool-execution-start')
return true;
if (chunk.type === 'tool-result') {
if (chunk.isError) {
const errMsg = chunk.output instanceof Error ? chunk.output.message : String(chunk.output);
send({ type: 'error', message: `Working memory update failed: ${errMsg}` });
}
else {
send({ type: 'working-memory-update', toolName: chunk.toolName });
}
return true;
}
return false;
}
function emitToolChunk(chunk, ctx) {
const { send, onToolEvent } = ctx;
if (chunk.type !== 'tool-call-suspended' && handleWorkingMemoryChunk(chunk, ctx)) {
return { suspended: false };
}
switch (chunk.type) {
case 'tool-input-start':
send({
type: 'tool-input-start',
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
});
onToolEvent?.toolInputStart?.(chunk.toolName);
break;
case 'tool-input-delta':
if (chunk.delta) {
send({ type: 'tool-input-delta', toolCallId: chunk.toolCallId, delta: chunk.delta });
onToolEvent?.toolInputDelta?.(chunk.toolCallId, chunk.delta);
}
break;
case 'tool-call':
send({
type: 'tool-call',
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
input: chunk.input,
});
break;
case 'tool-execution-start':
send({
type: 'tool-execution-start',
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
});
break;
case 'tool-result':
send({
type: 'tool-result',
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
output: chunk.output,
...(chunk.isError !== undefined && { isError: chunk.isError }),
});
onToolEvent?.toolResult?.(chunk.toolName);
break;
case 'tool-call-suspended': {
const payload = {
toolCallId: chunk.toolCallId,
runId: chunk.runId,
toolName: chunk.toolName,
input: chunk.suspendPayload,
};
send({ type: 'tool-call-suspended', payload });
return { suspended: true };
}
}
return { suspended: false };
}
function emitChunkEvents(chunk, ctx) {
switch (chunk.type) {
case 'start-step':
ctx.send({ type: 'start-step' });
return { suspended: false };
case 'finish-step':
ctx.send({ type: 'finish-step' });
return { suspended: false };
case 'text-start':
case 'text-delta':
case 'text-end':
case 'reasoning-start':
case 'reasoning-delta':
case 'reasoning-end':
emitTextLikeChunk(chunk, ctx.send);
return { suspended: false };
case 'tool-input-start':
case 'tool-input-delta':
case 'tool-call':
case 'tool-execution-start':
case 'tool-result':
case 'tool-call-suspended':
return emitToolChunk(chunk, ctx);
case 'message': {
const sseMessage = toAgentSseMessage(chunk.message);
if (sseMessage)
ctx.send({ type: 'message', message: sseMessage });
return { suspended: false };
}
case 'error': {
const errMsg = chunk.error instanceof Error ? chunk.error.message : String(chunk.error);
ctx.send({ type: 'error', message: errMsg });
return { suspended: false };
}
default:
return { suspended: false };
}
}
async function pumpChunks(chunks, send, onToolEvent) {
const ctx = {
send,
onToolEvent,
workingMemoryToolCallIds: new Set(),
};
for await (const chunk of chunks) {
const { suspended } = emitChunkEvents(chunk, ctx);
if (suspended)
return true;
}
return false;
}
//# sourceMappingURL=agent-sse-stream.js.map