Provider (if applicable)
aws_bedrock
Model (if applicable)
amazon.nova-2-lite-v1:0
Bug Description
req_http_options: [plugins: [...]] can be used to customize outgoing request bodies for non-streaming generation, because those provider paths go through Req.
Amazon Bedrock streaming does not go through Req. ReqLLM.Providers.AmazonBedrock.attach_stream/4 builds a Finch.Request directly:
- Builds the body with
formatter.format_request(model_id, context, translated_opts).
- Encodes it with
Jason.encode!.
- Calls
Finch.build(:post, url, headers, json_body).
- Signs the resulting Finch request.
Because this path bypasses Req, request plugins passed through req_http_options never run. This makes request body customization inconsistent between generate_text/3 and stream_text/3.
This matters for applications that need to inject provider-specific fields that are not modeled by ReqLLM yet, such as Bedrock model-specific generation/thinking configuration, routing hints, gateway compatibility fields, or temporary provider extensions.
Reproduction Code
defmodule ExtraBodyPlugin do
def attach(%Req.Request{} = req, opts) do
extra = Keyword.fetch!(opts, :extra)
Req.Request.prepend_request_steps(req, extra_body: &inject(&1, extra))
end
defp inject(%Req.Request{body: body} = req, extra) when is_binary(body) do
decoded = Jason.decode!(body)
%{req | body: Jason.encode!(Map.merge(decoded, extra))}
end
defp inject(req, _extra), do: req
end
extra_body = %{
"generationConfig" => %{
"thinkingConfig" => %{"thinkingLevel" => "medium"}
}
}
opts = [
req_http_options: [
plugins: [
fn req -> ExtraBodyPlugin.attach(req, extra: extra_body) end
]
]
]
# Non-streaming paths that use Req can run the plugin.
ReqLLM.Generation.generate_text(
%{provider: :amazon_bedrock, id: "model-id", provider_model_id: "provider-model-id"},
"hello",
opts
)
# Bedrock streaming bypasses Req and builds a Finch.Request directly,
# so the plugin never runs and the extra body is not injected.
ReqLLM.Generation.stream_text(
%{provider: :amazon_bedrock, id: "model-id", provider_model_id: "provider-model-id"},
"hello",
opts
)
The relevant Bedrock streaming code path is:
body = formatter.format_request(model_id, context, translated_opts)
json_body = body |> ReqLLM.Schema.apply_property_ordering() |> Jason.encode!()
finch_request = Finch.build(:post, url, headers, json_body)
Expected Behavior
A supported request body customization mechanism should work consistently for both:
ReqLLM.Generation.generate_text/3
ReqLLM.Generation.stream_text/3
Callers should not need separate provider-specific workarounds depending on whether streaming is enabled.
Actual Behavior
For Bedrock non-streaming, request customization can work because the request path uses Req.
For Bedrock streaming, customization does not apply because attach_stream/4 builds and signs a Finch.Request directly. req_http_options headers can still be extracted, but Req plugins never run and cannot modify the body.
Environment
- ReqLLM: 1.10.0
- Elixir: 1.19.5-otp-28
- Erlang/OTP: 28.4.2
- Provider:
:amazon_bedrock
- Streaming:
ReqLLM.Generation.stream_text/3
Provider (if applicable)
aws_bedrock
Model (if applicable)
amazon.nova-2-lite-v1:0
Bug Description
req_http_options: [plugins: [...]]can be used to customize outgoing request bodies for non-streaming generation, because those provider paths go throughReq.Amazon Bedrock streaming does not go through
Req.ReqLLM.Providers.AmazonBedrock.attach_stream/4builds aFinch.Requestdirectly:formatter.format_request(model_id, context, translated_opts).Jason.encode!.Finch.build(:post, url, headers, json_body).Because this path bypasses
Req, request plugins passed throughreq_http_optionsnever run. This makes request body customization inconsistent betweengenerate_text/3andstream_text/3.This matters for applications that need to inject provider-specific fields that are not modeled by ReqLLM yet, such as Bedrock model-specific generation/thinking configuration, routing hints, gateway compatibility fields, or temporary provider extensions.
Reproduction Code
Expected Behavior
A supported request body customization mechanism should work consistently for both:
ReqLLM.Generation.generate_text/3ReqLLM.Generation.stream_text/3Callers should not need separate provider-specific workarounds depending on whether streaming is enabled.
Actual Behavior
For Bedrock non-streaming, request customization can work because the request path uses
Req.For Bedrock streaming, customization does not apply because
attach_stream/4builds and signs aFinch.Requestdirectly.req_http_optionsheaders can still be extracted, butReqplugins never run and cannot modify the body.Environment
:amazon_bedrockReqLLM.Generation.stream_text/3