Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request/Response streaming for Finch adapter, SSE middleware #540

Merged
merged 2 commits into from Apr 11, 2024

Conversation

teamon
Copy link
Member

@teamon teamon commented Aug 24, 2022

The implementation requires spawning a process, I'm not sure there's a way to implement streaming without it given Finch.stream/5 only returns after all data has been streamed and the idea for tesla response streaming is to return Tesla.Env with status & headers and body as a stream.

Example streaming OpenAI client:

defmodule OpenAI do
  def new(token) do
    middleware = [
      {Tesla.Middleware.BaseUrl, "https://api.openai.com/v1"},
      {Tesla.Middleware.BearerAuth, token: token},
      # decode text/event-stream response
      {Tesla.Middleware.JSON, decode_content_types: ["text/event-stream"]},
      # decode SSE and return stream of data only (to be decoded by JSON)
      {Tesla.Middleware.SSE, only: :data}
    ]
    # use Finch as adapter
    Tesla.client(middleware, {Tesla.Adapter.Finch, name: MyFinch})
  end

  def completion(client, prompt) do
    data = %{
      model: "gpt-3.5-turbo",
      messages: [
        %{role: "user", content: prompt}
      ],
      # tell OpenAI to stream the response
      stream: true
    }
    # use the new [response: :stream] adapter option
    Tesla.post(client, "/chat/completions", data, opts: [adapter: [response: :stream]])
  end
end

{:ok, pid} = Finch.start_link(name: MyFinch)

client = OpenAI.new("<token>")

{:ok, env} = OpenAI.completion(client, "Count to 5, with a comma between each number and no newlines.")

env.body
|> Stream.each(&IO.inspect(&1, label: "chunk"))
|> Stream.run()

Output:

chunk: %{
  "choices" => [
    %{"delta" => %{"role" => "assistant"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => "1"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => ","}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => " "}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => "2"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => ","}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => " "}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => "3"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => ","}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => " "}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => "4"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => ","}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => " "}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [
    %{"delta" => %{"content" => "5"}, "finish_reason" => nil, "index" => 0}
  ],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: %{
  "choices" => [%{"delta" => %{}, "finish_reason" => "stop", "index" => 0}],
  "created" => 1681734469,
  "id" => "chatcmpl-76IFRG54YaoTcpb7bPEw5SvJZT5N6",
  "model" => "gpt-3.5-turbo-0301",
  "object" => "chat.completion.chunk"
}
chunk: "[DONE]"

@teamon teamon added finch Issues related to Finch adapter feature ✨ labels Aug 24, 2022
lib/tesla/adapter/finch.ex Outdated Show resolved Hide resolved
@teamon teamon changed the title Request/Response streaming for Finch adapter Request/Response streaming for Finch adapter, SSE middleware Apr 17, 2023
@hodak
Copy link
Collaborator

hodak commented Apr 20, 2023

Love it, would like to use it, here's a PR with error handling fix: #573

@cliftonmcintosh
Copy link

Thanks for this. I'd love to be able to use the streaming. This would allow me to move away from HTTPoison for streaming a response. Are there plans for this to be merged soon?

@atomkirk
Copy link

Please merge!! 😄

@teamon teamon merged commit d488bb2 into master Apr 11, 2024
10 checks passed
@teamon teamon deleted the tt/finch-stream branch April 11, 2024 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature ✨ finch Issues related to Finch adapter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants