|
| 1 | +import { |
| 2 | + BaseEvent, |
| 3 | + EventType, |
| 4 | + TextMessageChunkEvent, |
| 5 | + ToolCallArgsEvent, |
| 6 | + ToolCallEndEvent, |
| 7 | + ToolCallStartEvent, |
| 8 | +} from "@ag-ui/client"; |
| 9 | +import { randomUUID } from "crypto"; |
| 10 | + |
| 11 | +/** |
| 12 | + * Converts a TanStack AI stream into AG-UI `BaseEvent` objects. |
| 13 | + * |
| 14 | + * This is a pure converter — it does NOT emit lifecycle events |
| 15 | + * (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class) |
| 16 | + * is responsible for those. |
| 17 | + */ |
| 18 | +export async function* convertTanStackStream( |
| 19 | + stream: AsyncIterable<unknown>, |
| 20 | + abortSignal: AbortSignal, |
| 21 | +): AsyncGenerator<BaseEvent> { |
| 22 | + const messageId = randomUUID(); |
| 23 | + |
| 24 | + for await (const chunk of stream) { |
| 25 | + if (abortSignal.aborted) break; |
| 26 | + |
| 27 | + const raw = chunk as Record<string, unknown>; |
| 28 | + const type = raw.type as string; |
| 29 | + |
| 30 | + if (type === "TEXT_MESSAGE_CONTENT" && raw.delta) { |
| 31 | + const textEvent: TextMessageChunkEvent = { |
| 32 | + type: EventType.TEXT_MESSAGE_CHUNK, |
| 33 | + role: "assistant", |
| 34 | + messageId, |
| 35 | + delta: raw.delta as string, |
| 36 | + }; |
| 37 | + yield textEvent; |
| 38 | + } else if (type === "TOOL_CALL_START") { |
| 39 | + const startEvent: ToolCallStartEvent = { |
| 40 | + type: EventType.TOOL_CALL_START, |
| 41 | + parentMessageId: messageId, |
| 42 | + toolCallId: raw.toolCallId as string, |
| 43 | + toolCallName: raw.toolCallName as string, |
| 44 | + }; |
| 45 | + yield startEvent; |
| 46 | + } else if (type === "TOOL_CALL_ARGS") { |
| 47 | + const argsEvent: ToolCallArgsEvent = { |
| 48 | + type: EventType.TOOL_CALL_ARGS, |
| 49 | + toolCallId: raw.toolCallId as string, |
| 50 | + delta: raw.delta as string, |
| 51 | + }; |
| 52 | + yield argsEvent; |
| 53 | + } else if (type === "TOOL_CALL_END") { |
| 54 | + const endEvent: ToolCallEndEvent = { |
| 55 | + type: EventType.TOOL_CALL_END, |
| 56 | + toolCallId: raw.toolCallId as string, |
| 57 | + }; |
| 58 | + yield endEvent; |
| 59 | + } |
| 60 | + // Unknown chunk types are silently ignored |
| 61 | + } |
| 62 | +} |
0 commit comments