api: proper error messages for connection failures and HTTP errors
- Connection errors now show cause (refused/timeout/request error), URL, and the underlying error without redundant URL repetition - HTTP errors show status code, URL, and up to 1000 chars of body - Unparseable SSE events logged with content preview instead of silently dropped — may contain error info from vllm/server - Stream errors already had good context (kept as-is) You can't debug what you can't see. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b1d83b55c0
commit
b28b7def19
2 changed files with 26 additions and 6 deletions
|
|
@ -138,7 +138,18 @@ pub(crate) async fn send_and_check(
|
|||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to send request to API: {}", e))?;
|
||||
.map_err(|e| {
|
||||
let cause = if e.is_connect() {
|
||||
"connection refused"
|
||||
} else if e.is_timeout() {
|
||||
"request timed out"
|
||||
} else if e.is_request() {
|
||||
"request error"
|
||||
} else {
|
||||
"unknown"
|
||||
};
|
||||
anyhow::anyhow!("{} ({}): {}", cause, url, e.without_url())
|
||||
})?;
|
||||
|
||||
let status = response.status();
|
||||
let elapsed = start.elapsed();
|
||||
|
|
@ -164,12 +175,13 @@ pub(crate) async fn send_and_check(
|
|||
if !status.is_success() {
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
let _ = ui_tx.send(UiMessage::Debug(format!(
|
||||
"API error {} after {:.1}s: {}",
|
||||
"HTTP {} after {:.1}s ({}): {}",
|
||||
status,
|
||||
elapsed.as_secs_f64(),
|
||||
&body[..body.len().min(300)]
|
||||
url,
|
||||
&body[..body.len().min(500)]
|
||||
)));
|
||||
anyhow::bail!("API error {}: {}", status, &body[..body.len().min(500)]);
|
||||
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.len().min(1000)]);
|
||||
}
|
||||
|
||||
if debug {
|
||||
|
|
|
|||
|
|
@ -79,9 +79,17 @@ pub async fn stream(
|
|||
anyhow::bail!("API error in stream: {} {}", err_msg, raw);
|
||||
}
|
||||
|
||||
let chunk: ChatCompletionChunk = match serde_json::from_value(event) {
|
||||
let chunk: ChatCompletionChunk = match serde_json::from_value(event.clone()) {
|
||||
Ok(c) => c,
|
||||
Err(_) => continue,
|
||||
Err(e) => {
|
||||
// Log unparseable events — they may contain error info
|
||||
let preview = event.to_string();
|
||||
let _ = ui_tx.send(UiMessage::Debug(format!(
|
||||
"unparseable SSE event ({}): {}",
|
||||
e, &preview[..preview.len().min(300)]
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if chunk.usage.is_some() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue