Azure / azure-functions-nodejs-library

The Node.js framework for Azure Functions
https://www.npmjs.com/package/@azure/functions
MIT License
58 stars 15 forks source link

Support streams (http) #97

Closed haukepribnow closed 6 months ago

haukepribnow commented 1 year ago

Repro steps

  1. Using programming model v4, create an HTTP-triggered function where:

    • The handler function provides a ReadableStream in the return object's body field.
    • The ReadableStream enqueues data with a time delay.
    • The ReadableStream continues to enqueue data after the handler function has returned.
    • While enqueuing data, context.log() gets called.
  2. Call the function via HTTP.

(I recommend to check the source code below; I guess it's more telling than my description here.)

Expected behavior

When the function is called:

Actual behavior

Known workarounds

n/a

Related information

Source ```typescript import { ReadableStream } from "stream/web"; import { HttpRequest, HttpResponse, InvocationContext, app } from "@azure/functions"; // Test function that returns a ReadableStream as body. export async function streamingTest(request: HttpRequest, context: InvocationContext): Promise { context.log(`Http function processed request for url "${request.url}"`); const encoder = new TextEncoder(); let times = 0; const stream = new ReadableStream({ type: 'bytes', async start(controller) { function onTimer() { if (times >= 1000) { controller.close(); return; } context.log(`${times}`); // This raises a warning: // Unexpected call to 'log' on the context object after function execution // has completed. Please check for asynchronous calls that are not awaited. // Function name: streamingTest. Invocation Id: [removed]. times++; const data = encoder.encode(`${times}\r\n`); controller.enqueue(data); setTimeout(onTimer, 10); } onTimer(); } }, { highWaterMark: 1 }); return new HttpResponse({ status: 200, headers: { "Content-Type": "text/plain" }, body: stream }); }; app.http('streamingTest', { methods: ['GET'], authLevel: 'anonymous', handler: streamingTest }); ```
Screenshot of Wireshark showing information about the HTTP request & response ![image](https://github.com/Azure/azure-functions-nodejs-library/assets/811850/1de6d21c-ef78-4e75-af07-fa8730a1e8cb)
ejizba commented 1 year ago

Hi @haukepribnow we don't fully support streams right now. This is the expected behavior:

Instead, the completion of the stream is awaited before any data is sent. All data is then sent in a single chunk.

I'm cleaning up the stream issues and will use this to track support for http streams. This was finally unblocked for us in the host: https://github.com/Azure/azure-functions-dotnet-worker/issues/1387. The .NET Isolated worker was the first one to use it, but we should be able to benefit in Node.js as well.

Unfortunately I don't have any ETAs. For now we're focused on GA-ing the v4 programming model, but we will keep the roadmap updated with any stream plans: https://github.com/Azure/azure-functions-nodejs-library/wiki/Roadmap

Streaming Azure resources will be done separately, tracked by https://github.com/Azure/azure-functions-nodejs-library/issues/99

m14t commented 1 year ago

Given the slow responses from OpenAI, but their support for streaming, having support for streaming here would be critical.

Azure's private OpenAI offering is have me, a decades long AWS customer, strongly considering switching to Azure for AI based projects, but this issue will likely prevent me from using this serverless approach.

AlexPshul commented 10 months ago

Thanks @ejizba for keeping this issue alive and referencing this in other issues.

I am not sure how much this is requested by others, but I wanted to leave a +1 on this. Especially with all the Azure OpenAI related stuff that is going on right now, having an Azure Function that can stream back responses would be super valuable!

Do you have any updates on this or ETAs? Are you open to contributions? :)

ejizba commented 10 months ago

@AlexPshul We're open to contributions, but not for this feature. This has been in-progress for a while and involves a lot of moving parts. In the next week or so I should actually be able to post unofficial instructions to try this out, so the best way you could help would be testing.

As for the official preview announcement, that ETA is listed on the roadmap. I will update the roadmap as we get closer to an exact date - it depends how the testing goes.

AlexPshul commented 10 months ago

Thanks @ejizba for the quick reply and the details. I'd love to try the feature when it's available, even if it's unofficial. Once you have it, please let me know where I can access it.

ejizba commented 9 months ago

Hi folks, I was waiting to post instructions until core tools released and it just did today! Here are steps if you want to try out http streams before we announce preview: https://github.com/Azure/azure-functions-nodejs-library/wiki/Http-Stream-Support Edit: we announced preview, see here instead: https://aka.ms/AzFuncNodeHttpStreams

AlexPshul commented 9 months ago

Works great! Just what I need! image Had to play around a bit to understand how to return a stream from a function. Ended up implementing my own generator and sending back a Readable.from(generator) in the response.

@ejizba Thanks for providing us with an early preview. I will be happy to contribute an example to the docs if you need one.

ejizba commented 9 months ago

We just announced preview! 🎉 Check out our blog post here: https://aka.ms/AzFuncNodeHttpStreams

ilyachenko commented 7 months ago

Hi folks! HTTP streams work like a charm. I tested them using the @azure/openai. The stream buffer should be returned in the body of the function:

app.setup({ enableHttpStream: true });

// ...

const chunks = await client.streamChatCompletions(deploymentId, messages);

// ...

function createStream(chunks, callback) {
  const buffer = new Readable({
    read() {},
  });

  const stream = async () => {
    for await (const chunk of chunks) {
      buffer.push(content);
    }
    buffer.push(null);
  };

  stream();

  return buffer;
}

return {
    headers: { "Content-Type": "text/plain" },
    body: createStream(chunks)
}
eric-gonzalez-tfs commented 6 months ago

Hi @ejizba, Thank you for the update!

After calling the setup method with enableHttpStream set to true, will await request.formData() take advantage of the streaming request functionality?

The blog post suggests using request.body but is request.formData just as good? Thanks!

image

georgal commented 6 months ago

hey everyone,

as @ilyachenko indicates, the streaming works like a charm, but in my scenario only on localhost. Once running from app service it doesn't stream, instead returns response once the stream is completed.

Is this something anyone witnessed? Is there any way to address the issue? azure configs, specific tier etc?

Thanks in advance!

ejizba commented 6 months ago

Hi folks, http stream support is GA as of today! 🎉 Read more in our blog post here: https://aka.ms/azfuncnodehttpstreams

@eric-gonzalez-tfs @georgal I'm going to close this issue, but I've moved your comments to a discussion here: https://github.com/Azure/azure-functions-nodejs-library/discussions/261