Skip to content

Panic on streaming response if the api_base is malformed #376

@SeseMueller

Description

@SeseMueller

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions