Packages
llm_composer
0.19.5
0.20.2
0.20.1
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.3
0.12.2
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.8.0
0.7.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
LlmComposer is an Elixir library that facilitates chat interactions with language models, providing tools to handle user messages, generate responses, and execute functions automatically based on model outputs.
Current section
50 Versions
Jump to
Current section
50 Versions
Compare versions
5
files changed
+99
additions
-60
deletions
| @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | |
| 6 6 | |
| 7 7 | ## [Unreleased] |
| 8 8 | |
| 9 | + ## [0.19.5] - 2026-06-01 |
| 10 | + |
| 11 | + ### Added |
| 12 | + - Bedrock HTTP receive timeout is now configurable via `config :llm_composer, :bedrock, receive_timeout: <ms>`. Falls back to the global `config :llm_composer, :timeout` and then to the previous hardcoded default of 30 000 ms. Applies to all Mint-based paths (streaming and non-streaming) as well as the shared `handle_stream_response` helpers used by Finch streaming. |
| 13 | + |
| 14 | + ## [0.19.4] - 2026-05-05 |
| 15 | + |
| 16 | + ### Fixed |
| 17 | + - Fixed Google cached token tracking: `cachedContentTokenCount` from `usageMetadata` is now extracted and populated in both `LlmResponse.cached_tokens` (non-streaming) and `StreamChunk.usage.cached_tokens` (streaming) instead of always being `nil`. |
| 18 | + |
| 9 19 | ## [0.19.3] - 2026-04-30 |
| 10 20 | |
| 11 21 | ### Fixed |
| @@ -306,7 +316,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | |
| 306 316 | - Initial release with support for basic message handling, interaction with OpenAI and Ollama models, and a foundational structure for model settings and function execution. |
| 307 317 | |
| 308 318 | --- |
| 309 | - [Unreleased]: https://github.com/doofinder/llm_composer/compare/0.19.3...HEAD |
| 319 | + [Unreleased]: https://github.com/doofinder/llm_composer/compare/0.19.5...HEAD |
| 320 | + [0.19.5]: https://github.com/doofinder/llm_composer/compare/0.19.4...0.19.5 |
| 321 | + [0.19.4]: https://github.com/doofinder/llm_composer/compare/0.19.3...0.19.4 |
| 310 322 | [0.19.3]: https://github.com/doofinder/llm_composer/compare/0.19.2...0.19.3 |
| 311 323 | [0.19.2]: https://github.com/doofinder/llm_composer/compare/0.19.1...0.19.2 |
| 312 324 | [0.19.1]: https://github.com/doofinder/llm_composer/compare/0.19.0...0.19.1 |
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/doofinder/llm_composer">>}]}. |
| 2 2 | {<<"name">>,<<"llm_composer">>}. |
| 3 | - {<<"version">>,<<"0.19.4">>}. |
| 3 | + {<<"version">>,<<"0.19.5">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<"LlmComposer is an Elixir library that facilitates chat interactions with language models, providing tools to handle user messages, generate responses, and execute functions automatically based on model outputs.">>}. |
| 6 6 | {<<"elixir">>,<<"~> 1.18">>}. |
| @@ -49,68 +49,69 @@ | |
| 49 49 | {<<"repository">>,<<"hexpm">>}]]}. |
| 50 50 | {<<"files">>, |
| 51 51 | [<<"lib">>,<<"lib/llm_composer.ex">>,<<"lib/llm_composer">>, |
| 52 | - <<"lib/llm_composer/cache">>,<<"lib/llm_composer/cache/behaviour.ex">>, |
| 53 | - <<"lib/llm_composer/cache/ets.ex">>,<<"lib/llm_composer/cost">>, |
| 52 | + <<"lib/llm_composer/function_call_extractors.ex">>, |
| 53 | + <<"lib/llm_composer/function_call_helpers.ex">>, |
| 54 | + <<"lib/llm_composer/provider_router.ex">>, |
| 55 | + <<"lib/llm_composer/provider_stream_chunk">>, |
| 56 | + <<"lib/llm_composer/provider_stream_chunk/parser">>, |
| 57 | + <<"lib/llm_composer/provider_stream_chunk/parser/open_ai.ex">>, |
| 58 | + <<"lib/llm_composer/provider_stream_chunk/parser/open_ai_responses.ex">>, |
| 59 | + <<"lib/llm_composer/provider_stream_chunk/parser/bedrock.ex">>, |
| 60 | + <<"lib/llm_composer/provider_stream_chunk/parser/google.ex">>, |
| 61 | + <<"lib/llm_composer/provider_stream_chunk/parser/ollama.ex">>, |
| 62 | + <<"lib/llm_composer/provider_stream_chunk/open_ai.ex">>, |
| 63 | + <<"lib/llm_composer/provider_stream_chunk/open_ai_responses.ex">>, |
| 64 | + <<"lib/llm_composer/provider_stream_chunk/bedrock.ex">>, |
| 65 | + <<"lib/llm_composer/provider_stream_chunk/struct.ex">>, |
| 66 | + <<"lib/llm_composer/provider_stream_chunk/google.ex">>, |
| 67 | + <<"lib/llm_composer/provider_stream_chunk/open_router.ex">>, |
| 68 | + <<"lib/llm_composer/provider_stream_chunk/ollama.ex">>, |
| 69 | + <<"lib/llm_composer/errors.ex">>,<<"lib/llm_composer/helpers.ex">>, |
| 70 | + <<"lib/llm_composer/cost">>,<<"lib/llm_composer/cost/pricing.ex">>, |
| 54 71 | <<"lib/llm_composer/cost/fetchers">>, |
| 55 72 | <<"lib/llm_composer/cost/fetchers/models_dev.ex">>, |
| 56 73 | <<"lib/llm_composer/cost/fetchers/open_router.ex">>, |
| 57 | - <<"lib/llm_composer/cost/pricing.ex">>, |
| 58 74 | <<"lib/llm_composer/cost/cost_assembler.ex">>, |
| 59 | - <<"lib/llm_composer/cost_info.ex">>,<<"lib/llm_composer/errors.ex">>, |
| 60 | - <<"lib/llm_composer/function.ex">>,<<"lib/llm_composer/function_call.ex">>, |
| 61 | - <<"lib/llm_composer/function_call_extractors.ex">>, |
| 62 | - <<"lib/llm_composer/function_call_helpers.ex">>, |
| 75 | + <<"lib/llm_composer/cost_info.ex">>, |
| 63 76 | <<"lib/llm_composer/function_executor.ex">>, |
| 64 | - <<"lib/llm_composer/helpers.ex">>,<<"lib/llm_composer/http_client.ex">>, |
| 65 | - <<"lib/llm_composer/provider_response.ex">>, |
| 66 | - <<"lib/llm_composer/provider_response">>, |
| 67 | - <<"lib/llm_composer/provider_response/bedrock.ex">>, |
| 68 | - <<"lib/llm_composer/provider_response/google.ex">>, |
| 69 | - <<"lib/llm_composer/provider_response/ollama.ex">>, |
| 70 | - <<"lib/llm_composer/provider_response/open_ai.ex">>, |
| 71 | - <<"lib/llm_composer/provider_response/open_ai_responses.ex">>, |
| 72 | - <<"lib/llm_composer/provider_response/open_router.ex">>, |
| 73 | - <<"lib/llm_composer/provider_response/parser">>, |
| 74 | - <<"lib/llm_composer/provider_response/parser/bedrock.ex">>, |
| 75 | - <<"lib/llm_composer/provider_response/parser/ollama.ex">>, |
| 76 | - <<"lib/llm_composer/provider_response/parser/open_ai.ex">>, |
| 77 | - <<"lib/llm_composer/provider_response/parser/google.ex">>, |
| 78 | - <<"lib/llm_composer/provider_response/struct.ex">>, |
| 79 | - <<"lib/llm_composer/provider_router.ex">>, |
| 80 77 | <<"lib/llm_composer/provider_router">>, |
| 81 78 | <<"lib/llm_composer/provider_router/simple.ex">>, |
| 79 | + <<"lib/llm_composer/cache">>,<<"lib/llm_composer/cache/ets.ex">>, |
| 80 | + <<"lib/llm_composer/cache/behaviour.ex">>, |
| 81 | + <<"lib/llm_composer/provider_response.ex">>, |
| 82 | + <<"lib/llm_composer/llm_response.ex">>, |
| 83 | + <<"lib/llm_composer/providers_runner.ex">>, |
| 84 | + <<"lib/llm_composer/provider.ex">>, |
| 82 85 | <<"lib/llm_composer/provider_stream_chunk.ex">>, |
| 83 | - <<"lib/llm_composer/provider_stream_chunk">>, |
| 84 | - <<"lib/llm_composer/provider_stream_chunk/bedrock.ex">>, |
| 85 | - <<"lib/llm_composer/provider_stream_chunk/google.ex">>, |
| 86 | - <<"lib/llm_composer/provider_stream_chunk/ollama.ex">>, |
| 87 | - <<"lib/llm_composer/provider_stream_chunk/open_ai.ex">>, |
| 88 | - <<"lib/llm_composer/provider_stream_chunk/open_ai_responses.ex">>, |
| 89 | - <<"lib/llm_composer/provider_stream_chunk/open_router.ex">>, |
| 90 | - <<"lib/llm_composer/provider_stream_chunk/parser">>, |
| 91 | - <<"lib/llm_composer/provider_stream_chunk/parser/bedrock.ex">>, |
| 92 | - <<"lib/llm_composer/provider_stream_chunk/parser/ollama.ex">>, |
| 93 | - <<"lib/llm_composer/provider_stream_chunk/parser/open_ai.ex">>, |
| 94 | - <<"lib/llm_composer/provider_stream_chunk/parser/open_ai_responses.ex">>, |
| 95 | - <<"lib/llm_composer/provider_stream_chunk/parser/google.ex">>, |
| 96 | - <<"lib/llm_composer/provider_stream_chunk/struct.ex">>, |
| 86 | + <<"lib/llm_composer/settings.ex">>,<<"lib/llm_composer/function.ex">>, |
| 87 | + <<"lib/llm_composer/stream_chunk.ex">>, |
| 88 | + <<"lib/llm_composer/provider_response">>, |
| 89 | + <<"lib/llm_composer/provider_response/parser">>, |
| 90 | + <<"lib/llm_composer/provider_response/parser/open_ai.ex">>, |
| 91 | + <<"lib/llm_composer/provider_response/parser/bedrock.ex">>, |
| 92 | + <<"lib/llm_composer/provider_response/parser/google.ex">>, |
| 93 | + <<"lib/llm_composer/provider_response/parser/ollama.ex">>, |
| 94 | + <<"lib/llm_composer/provider_response/open_ai.ex">>, |
| 95 | + <<"lib/llm_composer/provider_response/open_ai_responses.ex">>, |
| 96 | + <<"lib/llm_composer/provider_response/bedrock.ex">>, |
| 97 | + <<"lib/llm_composer/provider_response/struct.ex">>, |
| 98 | + <<"lib/llm_composer/provider_response/google.ex">>, |
| 99 | + <<"lib/llm_composer/provider_response/open_router.ex">>, |
| 100 | + <<"lib/llm_composer/provider_response/ollama.ex">>, |
| 97 101 | <<"lib/llm_composer/providers">>, |
| 98 | - <<"lib/llm_composer/providers/bedrock.ex">>, |
| 99 | - <<"lib/llm_composer/providers/bedrock">>, |
| 100 | - <<"lib/llm_composer/providers/bedrock/http_client.ex">>, |
| 101 | - <<"lib/llm_composer/providers/bedrock/stream_operation.ex">>, |
| 102 | - <<"lib/llm_composer/providers/google.ex">>, |
| 103 | - <<"lib/llm_composer/providers/ollama.ex">>, |
| 104 102 | <<"lib/llm_composer/providers/open_ai.ex">>, |
| 103 | + <<"lib/llm_composer/providers/utils.ex">>, |
| 105 104 | <<"lib/llm_composer/providers/open_ai_responses.ex">>, |
| 106 105 | <<"lib/llm_composer/providers/open_ai_responses">>, |
| 107 106 | <<"lib/llm_composer/providers/open_ai_responses/reasoning.ex">>, |
| 107 | + <<"lib/llm_composer/providers/bedrock.ex">>, |
| 108 | + <<"lib/llm_composer/providers/bedrock">>, |
| 109 | + <<"lib/llm_composer/providers/bedrock/stream_operation.ex">>, |
| 110 | + <<"lib/llm_composer/providers/bedrock/http_client.ex">>, |
| 111 | + <<"lib/llm_composer/providers/google.ex">>, |
| 108 112 | <<"lib/llm_composer/providers/open_router.ex">>, |
| 109 | - <<"lib/llm_composer/providers/utils.ex">>, |
| 110 | - <<"lib/llm_composer/providers_runner.ex">>, |
| 111 | - <<"lib/llm_composer/stream_chunk.ex">>, |
| 112 | - <<"lib/llm_composer/llm_response.ex">>,<<"lib/llm_composer/message.ex">>, |
| 113 | - <<"lib/llm_composer/provider.ex">>,<<"lib/llm_composer/settings.ex">>, |
| 114 | - <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>, |
| 115 | - <<"CHANGELOG.md">>]}. |
| 113 | + <<"lib/llm_composer/providers/ollama.ex">>, |
| 114 | + <<"lib/llm_composer/message.ex">>,<<"lib/llm_composer/function_call.ex">>, |
| 115 | + <<"lib/llm_composer/http_client.ex">>,<<".formatter.exs">>,<<"mix.exs">>, |
| 116 | + <<"README.md">>,<<"LICENSE">>,<<"CHANGELOG.md">>]}. |
| 116 117 | {<<"build_tools">>,[<<"mix">>]}. |
| @@ -7,6 +7,22 @@ if Code.ensure_loaded?(ExAws) do | |
| 7 7 | Bedrock compatible model can be used. To specify any model-specific options for |
| 8 8 | the request, you can pass them in the `request_params` option and they will |
| 9 9 | be merged into the base request that is prepared. |
| 10 | + |
| 11 | + ## Timeout configuration |
| 12 | + |
| 13 | + The HTTP receive timeout (how long to wait for data from Bedrock before giving |
| 14 | + up) can be tuned via application config. The lookup order is: |
| 15 | + |
| 16 | + 1. `config :llm_composer, :bedrock, receive_timeout: <ms>` — Bedrock-specific override |
| 17 | + 2. `config :llm_composer, :timeout, <ms>` — global llm_composer timeout |
| 18 | + 3. Default: `30_000` ms (30 s) |
| 19 | + |
| 20 | + Example: |
| 21 | + |
| 22 | + config :llm_composer, :bedrock, receive_timeout: 60_000 |
| 23 | + |
| 24 | + The timeout applies to all Mint-based requests (streaming and non-streaming). |
| 25 | + Finch regular (non-streaming) requests use Finch's own timeout configuration. |
| 10 26 | """ |
| 11 27 | @behaviour LlmComposer.Provider |
| @@ -31,7 +31,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 31 31 | |
| 32 32 | require Logger |
| 33 33 | |
| 34 | - @stream_timeout 30_000 |
| 34 | + @default_receive_timeout 30_000 |
| 35 35 | |
| 36 36 | @impl ExAws.Request.HttpClient |
| 37 37 | @spec request( |
| @@ -159,7 +159,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 159 159 | stream_mint_loop(conn, ref, caller) |
| 160 160 | end |
| 161 161 | after |
| 162 | - @stream_timeout -> |
| 162 | + receive_timeout() -> |
| 163 163 | send(caller, {:bedrock_stream, :done}) |
| 164 164 | end |
| 165 165 | end |
| @@ -232,7 +232,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 232 232 | collect_mint_response(conn, ref, acc) |
| 233 233 | end |
| 234 234 | after |
| 235 | - @stream_timeout -> {:error, %{reason: :timeout}} |
| 235 | + receive_timeout() -> {:error, %{reason: :timeout}} |
| 236 236 | end |
| 237 237 | end |
| 238 238 | |
| @@ -290,7 +290,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 290 290 | {:DOWN, ^ref, :process, _pid, reason} -> |
| 291 291 | {:error, {:task_crashed, reason}} |
| 292 292 | after |
| 293 | - @stream_timeout -> {:error, :timeout_waiting_for_headers} |
| 293 | + receive_timeout() -> {:error, :timeout_waiting_for_headers} |
| 294 294 | end |
| 295 295 | |
| 296 296 | {:bedrock_stream, {:error, reason}} -> |
| @@ -299,7 +299,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 299 299 | {:DOWN, ^ref, :process, _pid, reason} -> |
| 300 300 | {:error, {:task_crashed, reason}} |
| 301 301 | after |
| 302 | - @stream_timeout -> {:error, :timeout_waiting_for_status} |
| 302 | + receive_timeout() -> {:error, :timeout_waiting_for_status} |
| 303 303 | end |
| 304 304 | end |
| 305 305 | |
| @@ -311,7 +311,7 @@ if Code.ensure_loaded?(ExAws) do | |
| 311 311 | {:bedrock_stream, {:error, _reason}} -> acc |
| 312 312 | {:DOWN, ^ref, :process, _pid, _reason} -> acc |
| 313 313 | after |
| 314 | - @stream_timeout -> acc |
| 314 | + receive_timeout() -> acc |
| 315 315 | end |
| 316 316 | end |
| 317 317 | |
| @@ -326,13 +326,23 @@ if Code.ensure_loaded?(ExAws) do | |
| 326 326 | {:bedrock_stream, {:error, _reason}} -> {:halt, state} |
| 327 327 | {:DOWN, ^ref, :process, _pid, _reason} -> {:halt, state} |
| 328 328 | after |
| 329 | - @stream_timeout -> {:halt, state} |
| 329 | + receive_timeout() -> {:halt, state} |
| 330 330 | end |
| 331 331 | end, |
| 332 332 | fn _state -> :ok end |
| 333 333 | ) |
| 334 334 | end |
| 335 335 | |
| 336 | + @spec receive_timeout() :: non_neg_integer() |
| 337 | + defp receive_timeout do |
| 338 | + bedrock_timeout = |
| 339 | + :llm_composer |
| 340 | + |> Application.get_env(:bedrock, []) |
| 341 | + |> Keyword.get(:receive_timeout) |
| 342 | + |
| 343 | + bedrock_timeout || Application.get_env(:llm_composer, :timeout, @default_receive_timeout) |
| 344 | + end |
| 345 | + |
| 336 346 | @spec finch_name() :: {:ok, atom()} | :error |
| 337 347 | defp finch_name do |
| 338 348 | case Application.get_env(:llm_composer, :tesla_adapter) do |
| @@ -4,7 +4,7 @@ defmodule LlmComposer.MixProject do | |
| 4 4 | def project do |
| 5 5 | [ |
| 6 6 | app: :llm_composer, |
| 7 | - version: "0.19.4", |
| 7 | + version: "0.19.5", |
| 8 8 | elixir: "~> 1.18", |
| 9 9 | start_permanent: Mix.env() == :prod, |
| 10 10 | deps: deps(), |