When using a client that has a malformed api_base is used in a streaming request, the library panics.
This is because in line 413 of the src/client.rs file, the eventsource function is called, which tries to construct the EventSource from a failed RequestBuilder.
The inner value is of type Result::Err , because in the RequestBuilder pipeline, the .post fails (because into_url is called on the faulty String in the reqwest library), leaving the inner request as an error variant. The .query, .headers and .json functions pass it along, and the try_clone function bubbles it up.
Because this is then unwrapped in line 414, the entire thread panics.
Minimal reproduction
use async_openai::{Client, config::OpenAIConfig, types::CreateCompletionRequestArgs};
use futures::StreamExt;
#[tokio::main]
async fn main() {
let client = Client::with_config(
OpenAIConfig::new()
.with_api_key("ollama")
.with_api_base("localhost:11434/v1"),
);
let request = CreateCompletionRequestArgs::default()
.model("llama3.2")
.prompt("What is the capital of France?")
.build()
.unwrap();
let mut response = client.completions().create_stream(request).await.unwrap();
while let Some(chunk) = response.next().await {
println!("Received chunk: {:?}", chunk);
}
}
Backtrace:
thread 'main' panicked at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-openai-0.28.1/src/client.rs:414:14:
called `Result::unwrap()` on an `Err` value: CannotCloneRequestError
stack backtrace:
0: rust_begin_unwind
at /rustc/4eb161250e340c8f48f66e2b929ef4a5bed7c181/library/std/src/panicking.rs:692:5
1: core::panicking::panic_fmt
at /rustc/4eb161250e340c8f48f66e2b929ef4a5bed7c181/library/core/src/panicking.rs:75:14
2: core::result::unwrap_failed
at /rustc/4eb161250e340c8f48f66e2b929ef4a5bed7c181/library/core/src/result.rs:1704:5
3: core::result::Result::unwrap
at /Users/USERNAME/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:1109:23
4: async_openai::client::Client::post_stream::{{closure}}
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-openai-0.28.1/src/client.rs:407:28
5: async_openai::completion::Completions::create_stream::{{closure}}
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-openai-0.28.1/src/completion.rs:75:61
6: async_openai_bug_minimal::main::{{closure}}
at ./src/main.rs:18:68
7: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/park.rs:284:60
8: tokio::task::coop::with_budget
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/task/coop/mod.rs:167:5
9: tokio::task::coop::budget
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/task/coop/mod.rs:133:5
10: tokio::runtime::park::CachedParkThread::block_on
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/park.rs:284:31
11: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/context/blocking.rs:66:9
12: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/scheduler/multi_thread/mod.rs:87:13
13: tokio::runtime::context::runtime::enter_runtime
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/context/runtime.rs:65:16
14: tokio::runtime::scheduler::multi_thread::MultiThread::block_on
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/scheduler/multi_thread/mod.rs:86:9
15: tokio::runtime::runtime::Runtime::block_on_inner
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/runtime.rs:358:45
16: tokio::runtime::runtime::Runtime::block_on
at /Users/USERNAME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/runtime/runtime.rs:330:13
17: async_openai_bug_minimal::main
at ./src/main.rs:20:5
18: core::ops::function::FnOnce::call_once
at /Users/USERNAME/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Note that when not streaming, this caught correctly. However, both completions as well as chats do trigger this.
A solution that doesn't change the type design would be to add a check to the create_stream function that the into_url function doesn't fail on the given api base.
Alternatively the URL could be checked when the client is constructed.