{"version":3,"file":"agent/runner/stream-adapter.mjs","sources":["webpack://@multimodal/agent/./src/agent/runner/stream-adapter.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { AgentEventStream } from '@multimodal/agent-interface';\nimport { getLogger } from '../../utils/logger';\n\n/**\n * StreamAdapter - Adapts between standard events and streaming iterations\n *\n * This class handles the conversion between the event stream and\n * AsyncIterable for streaming responses.\n */\nexport class StreamAdapter {\n  private logger = getLogger('StreamAdapter');\n\n  constructor(private eventStream: AgentEventStream.Processor) {}\n\n  /**\n   * Create an AsyncIterable from the event stream for streaming back to the client\n   *\n   * @param abortSignal Optional abort signal to stop streaming\n   * @returns An AsyncIterable of events\n   */\n  createStreamFromEvents(abortSignal?: AbortSignal): AsyncIterable<AgentEventStream.Event> {\n    // Create an event stream controller to expose events as an AsyncIterable\n    const controller = new AbortController();\n    const { signal } = controller;\n\n    // Link external abort signal if provided\n    if (abortSignal) {\n      if (abortSignal.aborted) {\n        controller.abort();\n      } else {\n        abortSignal.addEventListener('abort', () => {\n          controller.abort();\n        });\n      }\n    }\n\n    // Create a queue to buffer events\n    const queue: AgentEventStream.Event[] = [];\n    let resolveNext: ((value: IteratorResult<AgentEventStream.Event, any>) => void) | null = null;\n    let isComplete = false;\n\n    // Subscribe to all events instead of specific types\n    const unsubscribe = this.eventStream.subscribe((event) => {\n      // If stream is already complete, ignore new events\n      if (isComplete) {\n        return;\n      }\n\n      // Mark stream is closed when agent is aborted\n      if (signal.aborted) {\n        isComplete = true;\n        // The latest message accompanying abort should be emitted,\n        // but all new events after the stream is closed are ignored.\n        queue.push(event);\n        unsubscribe();\n        this.logger.info(`[Stream] Signal aborted, marking stream as complete.`);\n      }\n      // For agent_run_end, mark the stream as complete after adding this final event\n      else if (event.type === 'agent_run_end') {\n        queue.push(event);\n        isComplete = true;\n        unsubscribe();\n        this.logger.info(`[Stream] \"agent_run_end\" event received, marking stream as complete.`);\n      }\n      // Regular event processing\n      else {\n        // Add event to queue\n        queue.push(event);\n      }\n\n      // If someone is waiting for the next item, resolve their promise\n      if (resolveNext) {\n        const next = resolveNext;\n        resolveNext = null;\n\n        if (queue.length > 0) {\n          next({ done: false, value: queue.shift()! });\n        } else if (isComplete) {\n          next({ done: true, value: undefined });\n        }\n      }\n    });\n\n    // Return an AsyncIterable that yields events as they arrive\n    return {\n      [Symbol.asyncIterator]() {\n        return {\n          async next(): Promise<IteratorResult<AgentEventStream.Event, any>> {\n            // Check if aborted\n            if (signal.aborted) {\n              return { done: true, value: undefined };\n            }\n\n            // If items are in queue, return the next one\n            if (queue.length > 0) {\n              return { done: false, value: queue.shift()! };\n            }\n\n            // If stream is complete and queue is empty, we're done\n            if (isComplete) {\n              return { done: true, value: undefined };\n            }\n\n            // Otherwise wait for the next item\n            return new Promise<IteratorResult<AgentEventStream.Event, any>>((resolve) => {\n              resolveNext = resolve;\n\n              // Also handle abort while waiting\n              if (signal.aborted) {\n                resolve({ done: true, value: undefined });\n              }\n            });\n          },\n\n          async return() {\n            // Cancel the execution if consumer stops iterating\n            controller.abort();\n            unsubscribe();\n            return { done: true, value: undefined };\n          },\n        };\n      },\n    };\n  }\n\n  /**\n   * Create a stream that's already aborted\n   * Used when a request is aborted before streaming starts\n   */\n  createAbortedStream(): AsyncIterable<AgentEventStream.Event> {\n    const abortEvent = this.eventStream.createEvent('system', {\n      level: 'warning',\n      message: 'Request was aborted',\n    });\n\n    // Create a single-event stream that completes immediately\n    return {\n      [Symbol.asyncIterator]() {\n        let sent = false;\n        return {\n          async next(): Promise<IteratorResult<AgentEventStream.Event, any>> {\n            if (!sent) {\n              sent = true;\n              return { done: false, value: abortEvent };\n            }\n            return { done: true, value: undefined };\n          },\n          async return() {\n            return { done: true, value: undefined };\n          },\n        };\n      },\n    };\n  }\n\n  /**\n   * Mark the stream as complete with a final event\n   *\n   * @param finalEvent The event that signals completion\n   */\n  completeStream(finalEvent: AgentEventStream.AssistantMessageEvent): void {\n    this.logger.info(`[Stream] Marking stream as complete with final event`);\n  }\n\n  /**\n   * Mark the stream as aborted\n   */\n  abortStream(): void {\n    this.logger.info(`[Stream] Marking stream as aborted`);\n\n    // Create an abort system event\n    const abortEvent = this.eventStream.createEvent('system', {\n      level: 'warning',\n      message: 'Request was aborted',\n    });\n\n    // Add it to the event stream\n    this.eventStream.sendEvent(abortEvent);\n  }\n}\n"],"names":["StreamAdapter","abortSignal","controller","AbortController","signal","queue","resolveNext","isComplete","unsubscribe","event","next","undefined","Symbol","Promise","resolve","abortEvent","sent","finalEvent","eventStream","getLogger"],"mappings":";;;;;AAIC;;;;;;;;;;AAWM,MAAMA;IAWX,uBAAuBC,WAAyB,EAAyC;QAEvF,MAAMC,aAAa,IAAIC;QACvB,MAAM,EAAEC,MAAM,EAAE,GAAGF;QAGnB,IAAID,aACF,IAAIA,YAAY,OAAO,EACrBC,WAAW,KAAK;aAEhBD,YAAY,gBAAgB,CAAC,SAAS;YACpCC,WAAW,KAAK;QAClB;QAKJ,MAAMG,QAAkC,EAAE;QAC1C,IAAIC,cAAqF;QACzF,IAAIC,aAAa;QAGjB,MAAMC,cAAc,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAACC;YAE9C,IAAIF,YACF;YAIF,IAAIH,OAAO,OAAO,EAAE;gBAClBG,aAAa;gBAGbF,MAAM,IAAI,CAACI;gBACXD;gBACA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACnB,OAEK,IAAIC,AAAe,oBAAfA,MAAM,IAAI,EAAsB;gBACvCJ,MAAM,IAAI,CAACI;gBACXF,aAAa;gBACbC;gBACA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACnB,OAIEH,MAAM,IAAI,CAACI;YAIb,IAAIH,aAAa;gBACf,MAAMI,OAAOJ;gBACbA,cAAc;gBAEd,IAAID,MAAM,MAAM,GAAG,GACjBK,KAAK;oBAAE,MAAM;oBAAO,OAAOL,MAAM,KAAK;gBAAI;qBACrC,IAAIE,YACTG,KAAK;oBAAE,MAAM;oBAAM,OAAOC;gBAAU;YAExC;QACF;QAGA,OAAO;YACL,CAACC,OAAO,aAAa,CAAC;gBACpB,OAAO;oBACL,MAAM;wBAEJ,IAAIR,OAAO,OAAO,EAChB,OAAO;4BAAE,MAAM;4BAAM,OAAOO;wBAAU;wBAIxC,IAAIN,MAAM,MAAM,GAAG,GACjB,OAAO;4BAAE,MAAM;4BAAO,OAAOA,MAAM,KAAK;wBAAI;wBAI9C,IAAIE,YACF,OAAO;4BAAE,MAAM;4BAAM,OAAOI;wBAAU;wBAIxC,OAAO,IAAIE,QAAqD,CAACC;4BAC/DR,cAAcQ;4BAGd,IAAIV,OAAO,OAAO,EAChBU,QAAQ;gCAAE,MAAM;gCAAM,OAAOH;4BAAU;wBAE3C;oBACF;oBAEA,MAAM;wBAEJT,WAAW,KAAK;wBAChBM;wBACA,OAAO;4BAAE,MAAM;4BAAM,OAAOG;wBAAU;oBACxC;gBACF;YACF;QACF;IACF;IAMA,sBAA6D;QAC3D,MAAMI,aAAa,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU;YACxD,OAAO;YACP,SAAS;QACX;QAGA,OAAO;YACL,CAACH,OAAO,aAAa,CAAC;gBACpB,IAAII,OAAO;gBACX,OAAO;oBACL,MAAM;wBACJ,IAAI,CAACA,MAAM;4BACTA,OAAO;4BACP,OAAO;gCAAE,MAAM;gCAAO,OAAOD;4BAAW;wBAC1C;wBACA,OAAO;4BAAE,MAAM;4BAAM,OAAOJ;wBAAU;oBACxC;oBACA,MAAM;wBACJ,OAAO;4BAAE,MAAM;4BAAM,OAAOA;wBAAU;oBACxC;gBACF;YACF;QACF;IACF;IAOA,eAAeM,UAAkD,EAAQ;QACvE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IACnB;IAKA,cAAoB;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAGjB,MAAMF,aAAa,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU;YACxD,OAAO;YACP,SAAS;QACX;QAGA,IAAI,CAAC,WAAW,CAAC,SAAS,CAACA;IAC7B;IAtKA,YAAoBG,WAAuC,CAAE;;QAF7D,uBAAQ,UAAR;aAEoBA,WAAW,GAAXA;aAFZ,MAAM,GAAGC,UAAU;IAEmC;AAuKhE"}