Skip to content

Commit 1ed676e

Browse files
authored
Merge pull request #26 from aguung/fix/anthropic-stream-completion
fix: detect incomplete Anthropic SSE stream and deduplicate system prompt
2 parents 693bf3d + f33d6b7 commit 1ed676e

2 files changed

Lines changed: 16 additions & 7 deletions

File tree

src-tauri/src/services/chat_service.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -669,8 +669,9 @@ async fn stream_anthropic_sse(
669669
// subsequent `data:` line. Some gateways omit the `"type"` field from
670670
// the JSON payload, so we fall back to the SSE event name.
671671
let mut current_event = String::new();
672+
let mut message_stop_received = false;
672673

673-
loop {
674+
'outer: loop {
674675
tokio::select! {
675676
_ = cancel_token.cancelled() => {
676677
return Err(AppError::Cancelled);
@@ -688,7 +689,8 @@ async fn stream_anthropic_sse(
688689
}
689690

690691
if parse_anthropic_sse_line(&line, &mut current_event, on_token, &mut output)? {
691-
return Ok(output);
692+
message_stop_received = true;
693+
break 'outer;
692694
}
693695
}
694696
}
@@ -699,6 +701,12 @@ async fn stream_anthropic_sse(
699701
}
700702
}
701703

704+
if !message_stop_received {
705+
return Err(AppError::Http(
706+
"Stream ended without completion signal — connection may have been interrupted. Please retry.".to_string(),
707+
));
708+
}
709+
702710
Ok(output)
703711
}
704712

@@ -734,10 +742,7 @@ fn parse_anthropic_sse_line(
734742
};
735743
let payload = payload.trim();
736744

737-
let value: Value = match serde_json::from_str(payload) {
738-
Ok(v) => v,
739-
Err(_) => return Ok(false),
740-
};
745+
let value: Value = serde_json::from_str(payload)?;
741746

742747
// Prefer `"type"` from the JSON payload; fall back to the preceding
743748
// `event:` line when the gateway strips it.

src-tauri/src/tools/executor.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::time::Duration;
66
use globset::GlobSet;
77
use regex::Regex;
88
use serde::{Deserialize, Serialize};
9+
use serde_json::Value;
910
use tokio::process::Command;
1011
use walkdir::WalkDir;
1112

@@ -34,7 +35,10 @@ fn sensitive_globset() -> &'static GlobSet {
3435
}
3536
builder.build().unwrap_or_else(|error| {
3637
log::error!("sensitive_globset build failed: {error} — all file access will require permission");
37-
GlobSetBuilder::new().build().expect("empty GlobSet always builds")
38+
match GlobSetBuilder::new().build() {
39+
Ok(globset) => globset,
40+
Err(inner_error) => panic!("empty GlobSet build failed: {inner_error}"),
41+
}
3842
})
3943
})
4044
}

0 commit comments

Comments
 (0)