lmstudio: Propagate actual error message from server (#34538)

Discovered in this issue: #34513

Previously, we were propagating deserialization errors to users when
using LMStudio, instead of the actual error message sent from the
LMStudio server. This change will help users understand why their
request failed while streaming responses.

Release Notes:

- lmsudio: Display specific backend error messaging on failure rather
than generic ones

---------

Signed-off-by: Umesh Yadav <git@umesh.dev>
Co-authored-by: Peter Tripp <peter@zed.dev>
This commit is contained in:
Umesh Yadav 2025-07-25 19:06:43 +05:30 committed by GitHub
parent a2408f353b
commit f21ba9e2c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,4 +1,4 @@
use anyhow::{Context as _, Result};
use anyhow::{Context as _, Result, anyhow};
use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::BoxStream};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest, http};
use serde::{Deserialize, Serialize};
@ -275,11 +275,16 @@ impl Capabilities {
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LmStudioError {
pub message: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum ResponseStreamResult {
Ok(ResponseStreamEvent),
Err { error: String },
Err { error: LmStudioError },
}
#[derive(Serialize, Deserialize, Debug)]
@ -392,7 +397,6 @@ pub async fn stream_chat_completion(
let mut response = client.send(request).await?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());
Ok(reader
.lines()
.filter_map(|line| async move {
@ -402,18 +406,16 @@ pub async fn stream_chat_completion(
if line == "[DONE]" {
None
} else {
let result = serde_json::from_str(&line)
.context("Unable to parse chat completions response");
if let Err(ref e) = result {
eprintln!("Error parsing line: {e}\nLine content: '{line}'");
match serde_json::from_str(line) {
Ok(ResponseStreamResult::Ok(response)) => Some(Ok(response)),
Ok(ResponseStreamResult::Err { error, .. }) => {
Some(Err(anyhow!(error.message)))
}
Some(result)
Err(error) => Some(Err(anyhow!(error))),
}
}
Err(e) => {
eprintln!("Error reading line: {e}");
Some(Err(e.into()))
}
Err(error) => Some(Err(anyhow!(error))),
}
})
.boxed())