修复流式测试夹具与自主构建重试
兼容 stream=true 的 Responses 与 Chat Completions 测试响应 修正截断 scaffold 修复错误前缀匹配 确保最终回复失败审计夹具先完成 planning 阶段
This commit is contained in:
@@ -668,7 +668,7 @@ pub(super) const AGENT_RUNTIME_AUTONOMOUS_MANIFEST_DAG_WAIT_LIVENESS_ERROR_PREFI
|
||||
pub(super) const AGENT_RUNTIME_AUTONOMOUS_RESPONSE_PLAN_LIVENESS_ERROR_PREFIX: &str =
|
||||
"自主构建专业 Agent 已给出结论但结构化计划仍未完成";
|
||||
pub(super) const AGENT_RUNTIME_AUTONOMOUS_TRUNCATED_SCAFFOLD_ERROR_PREFIX: &str =
|
||||
"自主构建完整写入游戏入口(index.html 或 game/index.html)时必须提交闭合且可运行的 HTML";
|
||||
"自主构建完整写入 game/index.html 时必须提交闭合且可运行的 HTML";
|
||||
pub(super) const AGENT_RUNTIME_QUALITY_REVIEW_AGENT_ID: &str = "quality-review";
|
||||
pub(super) const AGENT_RUNTIME_SUPERVISOR_INITIAL_COLLABORATION_LIVENESS_ERROR_PREFIX: &str =
|
||||
"Project Supervisor 首批协作在首个 planning 窗口内未建立";
|
||||
|
||||
@@ -1557,17 +1557,35 @@ pub(crate) fn spawn_mock_llm_tool_plan_then_invalid_final_reply(
|
||||
let base_url = format!("http://{}", listener.local_addr().expect("mock llm addr"));
|
||||
std::thread::spawn(move || {
|
||||
let (mut planning_stream, _) = listener.accept().expect("mock tool plan accept");
|
||||
drop(read_mock_http_request(&mut planning_stream));
|
||||
let planning_body = serde_json::json!({
|
||||
"id": "resp_invalid_final_reply_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string();
|
||||
let planning_request = read_mock_http_request(&mut planning_stream);
|
||||
let planning_streaming = planning_request.contains("\"stream\":true");
|
||||
let (planning_body, planning_content_type) = if planning_streaming {
|
||||
(
|
||||
format!(
|
||||
"data: {}\n\ndata: {}\n\n",
|
||||
serde_json::json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": planning_response
|
||||
}),
|
||||
serde_json::json!({ "type": "response.completed" })
|
||||
),
|
||||
"text/event-stream; charset=utf-8",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
serde_json::json!({
|
||||
"id": "resp_invalid_final_reply_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string(),
|
||||
"application/json",
|
||||
)
|
||||
};
|
||||
let planning_response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {planning_content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
planning_body.len(),
|
||||
planning_body
|
||||
);
|
||||
@@ -1762,30 +1780,65 @@ fn spawn_mock_llm_server_responses_with_capture(
|
||||
if let Some(sender) = request_sender.as_ref() {
|
||||
let _ = sender.send(request_text.clone());
|
||||
}
|
||||
let body = if request_text.contains("POST /responses HTTP/1.1") {
|
||||
serde_json::json!({
|
||||
"id": "resp_game_creator_mock",
|
||||
"model": "mock-game-model",
|
||||
"output_text": response_content,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
let is_responses = request_text.contains("POST /responses HTTP/1.1");
|
||||
let is_stream = request_text.contains("\"stream\":true");
|
||||
let (body, content_type) = if is_stream && is_responses {
|
||||
(
|
||||
format!(
|
||||
"data: {}\n\ndata: {}\n\n",
|
||||
serde_json::json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": response_content
|
||||
}),
|
||||
serde_json::json!({ "type": "response.completed" })
|
||||
),
|
||||
"text/event-stream; charset=utf-8",
|
||||
)
|
||||
} else if is_stream {
|
||||
(
|
||||
format!(
|
||||
"data: {}\n\ndata: [DONE]\n\n",
|
||||
serde_json::json!({
|
||||
"id": "chatcmpl_game_creator_mock",
|
||||
"object": "chat.completion.chunk",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"delta": { "role": "assistant", "content": response_content },
|
||||
"finish_reason": "stop"
|
||||
}]
|
||||
})
|
||||
),
|
||||
"text/event-stream; charset=utf-8",
|
||||
)
|
||||
} else if is_responses {
|
||||
(
|
||||
serde_json::json!({
|
||||
"id": "resp_game_creator_mock",
|
||||
"model": "mock-game-model",
|
||||
"output_text": response_content,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string(),
|
||||
"application/json",
|
||||
)
|
||||
} else {
|
||||
serde_json::json!({
|
||||
"id": "chatcmpl_game_creator_mock",
|
||||
"model": "mock-game-model",
|
||||
"choices": [
|
||||
{
|
||||
(
|
||||
serde_json::json!({
|
||||
"id": "chatcmpl_game_creator_mock",
|
||||
"model": "mock-game-model",
|
||||
"choices": [{
|
||||
"message": { "content": response_content },
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": { "prompt_tokens": 11, "completion_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
}
|
||||
.to_string();
|
||||
}],
|
||||
"usage": { "prompt_tokens": 11, "completion_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string(),
|
||||
"application/json",
|
||||
)
|
||||
};
|
||||
let response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
body.len(),
|
||||
body
|
||||
);
|
||||
@@ -2159,18 +2212,34 @@ fn spawn_mock_llm_tool_plan_then_transient_final_reply(
|
||||
.try_into()
|
||||
.unwrap_or(u64::MAX);
|
||||
let planning_request = read_mock_http_request(&mut planning_stream);
|
||||
let _ = request_capture_sender.send((planning_accepted_at_ms, planning_request));
|
||||
let _ = request_capture_sender.send((planning_accepted_at_ms, planning_request.clone()));
|
||||
let _ = request_notice_sender.send(());
|
||||
let planning_body = serde_json::json!({
|
||||
"id": "resp_transient_final_reply_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string();
|
||||
let planning_body = if planning_request.contains("\"stream\":true") {
|
||||
format!(
|
||||
"data: {}\n\ndata: {}\n\n",
|
||||
serde_json::json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": planning_response
|
||||
}),
|
||||
serde_json::json!({ "type": "response.completed" })
|
||||
)
|
||||
} else {
|
||||
serde_json::json!({
|
||||
"id": "resp_transient_final_reply_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string()
|
||||
};
|
||||
let planning_content_type = if planning_request.contains("\"stream\":true") {
|
||||
"text/event-stream; charset=utf-8"
|
||||
} else {
|
||||
"application/json"
|
||||
};
|
||||
let planning_http_response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {planning_content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
planning_body.len(),
|
||||
planning_body
|
||||
);
|
||||
@@ -2250,7 +2319,7 @@ fn spawn_mock_llm_tool_plan_then_transient_final_compaction(
|
||||
let (mut planning_stream, _) = listener.accept().expect("mock tool plan accept");
|
||||
let planning_accepted_at_ms = accepted_at_ms();
|
||||
let planning_request = read_mock_http_request(&mut planning_stream);
|
||||
let _ = request_capture_sender.send((planning_accepted_at_ms, planning_request));
|
||||
let _ = request_capture_sender.send((planning_accepted_at_ms, planning_request.clone()));
|
||||
replace_test_local_config(
|
||||
&config_path,
|
||||
format!(
|
||||
@@ -2274,16 +2343,32 @@ fn spawn_mock_llm_tool_plan_then_transient_final_compaction(
|
||||
),
|
||||
);
|
||||
let _ = request_notice_sender.send(());
|
||||
let planning_body = serde_json::json!({
|
||||
"id": "resp_final_compaction_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string();
|
||||
let planning_body = if planning_request.contains("\"stream\":true") {
|
||||
format!(
|
||||
"data: {}\n\ndata: {}\n\n",
|
||||
serde_json::json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": planning_response
|
||||
}),
|
||||
serde_json::json!({ "type": "response.completed" })
|
||||
)
|
||||
} else {
|
||||
serde_json::json!({
|
||||
"id": "resp_final_compaction_planning",
|
||||
"model": "mock-game-model",
|
||||
"output_text": planning_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 11, "output_tokens": 22, "total_tokens": 33 }
|
||||
})
|
||||
.to_string()
|
||||
};
|
||||
let planning_content_type = if planning_request.contains("\"stream\":true") {
|
||||
"text/event-stream; charset=utf-8"
|
||||
} else {
|
||||
"application/json"
|
||||
};
|
||||
let planning_http_response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {planning_content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
planning_body.len(),
|
||||
planning_body
|
||||
);
|
||||
@@ -2308,19 +2393,35 @@ fn spawn_mock_llm_tool_plan_then_transient_final_compaction(
|
||||
let recovered_compaction_request = read_mock_http_request(&mut recovered_compaction_stream);
|
||||
let _ = request_capture_sender.send((
|
||||
recovered_compaction_accepted_at_ms,
|
||||
recovered_compaction_request,
|
||||
recovered_compaction_request.clone(),
|
||||
));
|
||||
let _ = request_notice_sender.send(());
|
||||
let compaction_body = serde_json::json!({
|
||||
"id": "resp_final_compaction_recovered",
|
||||
"model": "mock-game-model",
|
||||
"output_text": compaction_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 44, "output_tokens": 12, "total_tokens": 56 }
|
||||
})
|
||||
.to_string();
|
||||
let compaction_body = if recovered_compaction_request.contains("\"stream\":true") {
|
||||
format!(
|
||||
"data: {}\n\ndata: {}\n\n",
|
||||
serde_json::json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": compaction_response
|
||||
}),
|
||||
serde_json::json!({ "type": "response.completed" })
|
||||
)
|
||||
} else {
|
||||
serde_json::json!({
|
||||
"id": "resp_final_compaction_recovered",
|
||||
"model": "mock-game-model",
|
||||
"output_text": compaction_response,
|
||||
"status": "completed",
|
||||
"usage": { "input_tokens": 44, "output_tokens": 12, "total_tokens": 56 }
|
||||
})
|
||||
.to_string()
|
||||
};
|
||||
let compaction_content_type = if recovered_compaction_request.contains("\"stream\":true") {
|
||||
"text/event-stream; charset=utf-8"
|
||||
} else {
|
||||
"application/json"
|
||||
};
|
||||
let compaction_http_response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {compaction_content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
||||
compaction_body.len(),
|
||||
compaction_body
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user