How to set timeout option for Req?

This Post request takes some time, and fails in Req with a mint timeout error, but works fine with Httpoison. How can I configure the timeout with Req?

body =
  %{
    prompt: prompt,
    model: "claude-2",
    max_tokens_to_sample: 5000,
    stream: false
  }
  |> Jason.encode!()

{:ok, %HTTPoison.Response{status_code: 200, body: body}} =
  HTTPoison.post(
    "https://api.anthropic.com/v1/complete",
    body,
    headers,
    recv_timeout: 120_000
  )

completion =
  body
  |> Jason.decode!()
  |> Map.get("completion")

With Req this fails and times out:

{:ok, %Req.Response{body: %{"completion" => completion}}} =
  Req.post("https://api.anthropic.com/v1/complete",
    json: %{
      prompt: prompt,
      model: "claude-2",
      max_tokens_to_sample: 5000,
      stream: false
    },
    headers: headers,
    connect_options: [timeout: 120_000]
  )

Appreciate the help!

1 Like

It seems like the :receive_timeout option is what you actually want.

  • :receive_timeout - socket receive timeout in milliseconds, defaults to 15_000.

source: Req.Steps — req v0.4.8

So try something like this: Req.post(url, json: json, headers: headers, receive_timeout: 120_000)

7 Likes

I recently had trouble sending a multipart audio file using a file stream. I tried to make a custom API call to OpenAI to send an audio file. I heavily relied on this article to create it. I was able to send the audio, but when the audio is too big, it fails exactly at 15 seconds. No matter the receive_timeout option, it responds with %Req.TransportError{reason: :closed}. However, when the timeout is short, like 50, it responds back with %Req.TransportError{reason: :timeout}, meaning that it is considering the timeout.

@behaviour ProjectX.OpenAI.OpenAIAPI

  @spec new([Keyword.t()]) :: Req.t()
  def new(options \\ []) when is_list(options) do
    Req.new(
      base_url: "https://api.openai.com/v1",
      auth: {:bearer, System.get_env("OPENAPI_KEY")}
    )
    |> Req.merge(options)
  end

  @spec request(String.t(), list()) :: {:ok, Req.Response.t()} | {:error, Exception.t()}
  def request(url, options \\ []), do: Req.request(new(url: parse_url(url)), options)

  @spec request!(String.t(), list()) :: Req.Response.t()
  def request!(url, options \\ []), do: Req.request!(new(url: parse_url(url)), options)

  defp parse_url(url) when is_binary(url), do: url

  @impl true
  @spec transcribe_audio(String.t()) :: {:ok, Req.Response.t()} | {:error, String.t()}
  def transcribe_audio(audio_path) do
    with audio <- File.stream!(audio_path),
         {:ok, response} <-
           request("/audio/transcriptions",
             method: :post,
             receive_timeout: 500_000,
             form_multipart: [file: audio, model: "whisper-1", response_format: "text"]
             #  retry: [max_attempts: 3, delay: 2000]
           ) do
      {:ok, response}
    else
      error ->
        {:error, "Unexpected error: #{inspect(error)}"}
    end
  end