Why does the Pipedream $.respond() function wait for the entire stream to be buffered when responding to a readable stream from the Elevenlabs API?

This topic was automatically generated from Slack. You can find the original thread here.

Could someone help me with responding a readable stream? In the documentation, it’s written that I can use $.respond() with the body set to a readable stream, but when I try it, it seems the steps wait for the the entire stream to be buffered before sending back the response. The readable stream is from elevenlabs stream endpoint, which returns an audio stream. The documentation link to an example workflow but unfortunately the link seems broken.

Here’s my code:

import axios from 'axios';

export default defineComponent({
  async run({ steps, $ }) {

    class ElevenLabsAPI {
      constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseURL = 'https://api.elevenlabs.io/v1';
      }

      async textToSpeech(voiceId, text) {
        const endpoint = `${this.baseURL}/text-to-speech/${voiceId}/stream`;
        const headers = {
          'xi-api-key': this.apiKey,
          'Content-Type': 'application/json'
        };
        const data = {
          text: text,
          model_id: "eleven_multilingual_v2",
          voice_settings: {
            stability: 0.3,
            similarity_boost: 0.35,
            style: 0.4,
            use_speaker_boost: true
          }
        };
        try {
          const response = await axios.post(endpoint, data, {
            headers: headers,
            responseType: 'stream'
          });
          
          await $.respond({
            status: 202,
            headers: { 'Content-Type': 'audio/mpeg' },
            body: response.data
          });

        } catch (error) {
          await $.respond({
            immediate: true,
            status: 502,
            body: `ElevenLabs API error: ${error.message}`
          });
          console.log(error);
        }
      }
    }

    const voiceId = '1NJX0B85u1WnhL6ZkG2n';
    const articleContent = (steps.python && steps.python.$return_value) ? steps.python.$return_value : steps.trigger.event.body.html;

    const api = new ElevenLabsAPI(process.env.ELEVENLABS_API_KEY);
    await api.textToSpeech(voiceId, articleContent);
  },
});

can you help?

I see that you’re trying to use a readable stream with $.respond() in your Pipedream component code. I understand that the response seems to be buffered before being sent back. I’m sorry for the inconvenience. Currently, Pipedream doesn’t support streaming responses directly using $.respond(). The entire stream must be buffered before sending the response, which is what you’re experiencing. Unfortunately, I don’t have a solution for streaming the response directly at the moment. If you have any other questions or need assistance with a different aspect of Pipedream, please feel free to ask.

But the documentation says:

The value of the body property can be either a string, object, a Buffer
(opens new window)
(binary data), or a Readable stream
(opens new window)
. Attempting to return any other data may yield an error.
In the case where you return a Readable stream:
• You must await the $.respond function (await $.respond({ ... })
• The stream must close and be finished reading within your workflow execution timeout.
• You cannot return a Readable and use the immediate: true property of $.respond.

I apologize for the confusion earlier. You’re correct that the documentation states that a Readable stream can be used as the body property in $.respond(). However, it seems that there might be an issue with the implementation or a limitation that’s causing the stream to be buffered before sending the response. I understand that this is not the desired behavior, and I apologize for any inconvenience this may cause. I recommend reaching out to Pipedream support at https://pipedream.com/support for further assistance and to report this issue. They might be able to provide more information or a workaround for this specific case.