fix(runtime): SSE行缓冲 — 修复glm tool call参数截断丢失
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
根因: OpenAI driver的SSE解析直接按TCP chunk分行,
当glm的JSON响应被拆成多个TCP包时,SSE data行被截断,
导致tool call arguments丢失(input={})。
修复:
1. 添加pending_line缓冲区,跨chunk累积不完整的SSE行
2. 只处理完整的行(\n结尾),未完成的保留到下次
3. researcher.infer_action()增加更多字段推断(search/keyword/q等)
验证: 99 tests PASS, 160 hands tests PASS
This commit is contained in:
@@ -163,6 +163,7 @@ impl LlmDriver for OpenAiDriver {
|
||||
let mut current_tool_id: Option<String> = None;
|
||||
let mut sse_event_count: usize = 0;
|
||||
let mut raw_bytes_total: usize = 0;
|
||||
let mut pending_line = String::new(); // Buffer for incomplete SSE lines
|
||||
|
||||
while let Some(chunk_result) = byte_stream.next().await {
|
||||
let chunk = match chunk_result {
|
||||
@@ -180,13 +181,21 @@ impl LlmDriver for OpenAiDriver {
|
||||
if raw_bytes_total <= 600 {
|
||||
tracing::debug!("[OpenAI:stream] RAW chunk ({} bytes): {:?}", text.len(), &text[..text.len().min(500)]);
|
||||
}
|
||||
for line in text.lines() {
|
||||
// Accumulate text and split by lines, handling incomplete last line
|
||||
pending_line.push_str(&text);
|
||||
// Extract complete lines (ending with \n), keep the rest pending
|
||||
let mut complete_lines: Vec<String> = Vec::new();
|
||||
while let Some(pos) = pending_line.find('\n') {
|
||||
complete_lines.push(pending_line[..pos].to_string());
|
||||
pending_line = pending_line[pos + 1..].to_string();
|
||||
}
|
||||
for line in complete_lines {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() || trimmed.starts_with(':') {
|
||||
continue; // Skip empty lines and SSE comments
|
||||
}
|
||||
// Handle both "data: " (standard) and "data:" (no space)
|
||||
let data = if let Some(d) = trimmed.strip_prefix("data: ") {
|
||||
let data: Option<&str> = if let Some(d) = trimmed.strip_prefix("data: ") {
|
||||
Some(d)
|
||||
} else if let Some(d) = trimmed.strip_prefix("data:") {
|
||||
Some(d.trim_start())
|
||||
|
||||
Reference in New Issue
Block a user