mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 12:09:50 +08:00
fix(ws): harden Responses continuation state
This commit is contained in:
@@ -50,6 +50,69 @@ pub(crate) fn build_local_openai_responses_request_body_with_codex_model_capabil
|
||||
request_headers: &http::HeaderMap,
|
||||
model_capabilities: Option<&crate::ai_serving::CodexResponsesModelCapabilities>,
|
||||
enable_model_directives: bool,
|
||||
) -> Option<Value> {
|
||||
build_local_openai_responses_request_body_with_codex_model_capabilities_and_websocket_mode(
|
||||
body_json,
|
||||
mapped_model,
|
||||
require_streaming,
|
||||
force_body_stream_field,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
body_rules,
|
||||
request_headers,
|
||||
model_capabilities,
|
||||
enable_model_directives,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds a Responses body for a pinned WebSocket continuation.
|
||||
///
|
||||
/// This is intentionally an additive variant of the ordinary HTTP builder.
|
||||
/// The WebSocket framing layer, rather than a JSON-body heuristic, tells the
|
||||
/// Codex compatibility pass that `previous_response_id` is transport state and
|
||||
/// that Responses Lite `tools`/`instructions` must not be materialized into a
|
||||
/// second historical input prefix.
|
||||
pub(crate) fn build_local_openai_responses_request_body_with_codex_model_capabilities_for_websocket_continuation(
|
||||
body_json: &Value,
|
||||
mapped_model: &str,
|
||||
require_streaming: bool,
|
||||
force_body_stream_field: bool,
|
||||
provider_type: &str,
|
||||
provider_api_format: &str,
|
||||
body_rules: Option<&Value>,
|
||||
request_headers: &http::HeaderMap,
|
||||
model_capabilities: Option<&crate::ai_serving::CodexResponsesModelCapabilities>,
|
||||
enable_model_directives: bool,
|
||||
) -> Option<Value> {
|
||||
build_local_openai_responses_request_body_with_codex_model_capabilities_and_websocket_mode(
|
||||
body_json,
|
||||
mapped_model,
|
||||
require_streaming,
|
||||
force_body_stream_field,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
body_rules,
|
||||
request_headers,
|
||||
model_capabilities,
|
||||
enable_model_directives,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_local_openai_responses_request_body_with_codex_model_capabilities_and_websocket_mode(
|
||||
body_json: &Value,
|
||||
mapped_model: &str,
|
||||
require_streaming: bool,
|
||||
force_body_stream_field: bool,
|
||||
provider_type: &str,
|
||||
provider_api_format: &str,
|
||||
body_rules: Option<&Value>,
|
||||
request_headers: &http::HeaderMap,
|
||||
model_capabilities: Option<&crate::ai_serving::CodexResponsesModelCapabilities>,
|
||||
enable_model_directives: bool,
|
||||
websocket_continuation: bool,
|
||||
) -> Option<Value> {
|
||||
let provider_request_body = surface_build_local_openai_responses_request_body(
|
||||
body_json,
|
||||
@@ -68,15 +131,27 @@ pub(crate) fn build_local_openai_responses_request_body_with_codex_model_capabil
|
||||
.get("model")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or(mapped_model);
|
||||
crate::ai_serving::apply_codex_openai_responses_special_body_edits_with_source_model_and_capabilities(
|
||||
&mut provider_request_body,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
mapped_model,
|
||||
source_model,
|
||||
model_capabilities,
|
||||
body_rules,
|
||||
);
|
||||
if websocket_continuation {
|
||||
crate::ai_serving::apply_codex_openai_responses_websocket_continuation_body_edits_with_source_model_and_capabilities(
|
||||
&mut provider_request_body,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
mapped_model,
|
||||
source_model,
|
||||
model_capabilities,
|
||||
body_rules,
|
||||
);
|
||||
} else {
|
||||
crate::ai_serving::apply_codex_openai_responses_special_body_edits_with_source_model_and_capabilities(
|
||||
&mut provider_request_body,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
mapped_model,
|
||||
source_model,
|
||||
model_capabilities,
|
||||
body_rules,
|
||||
);
|
||||
}
|
||||
apply_openai_responses_compact_special_body_edits(
|
||||
&mut provider_request_body,
|
||||
provider_api_format,
|
||||
|
||||
@@ -153,7 +153,7 @@ fn local_openai_responses_wrapper_preserves_body_order_after_edits() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_openai_responses_wrapper_strips_foreign_reasoning_item_ids() {
|
||||
fn local_openai_responses_wrapper_defers_reasoning_replay_filtering() {
|
||||
let body_json = json!({
|
||||
"model": "gpt-5.4",
|
||||
"input": [
|
||||
@@ -184,9 +184,14 @@ fn local_openai_responses_wrapper_strips_foreign_reasoning_item_ids() {
|
||||
let input = provider_request_body["input"]
|
||||
.as_array()
|
||||
.expect("input array");
|
||||
assert_eq!(input.len(), 2);
|
||||
// This provider-agnostic normalization layer cannot decide whether an
|
||||
// id-less/foreign reasoning item is opaque state required by DeepSeek.
|
||||
// The provider-aware finalization pass applies the strict or DeepSeek
|
||||
// replay policy once the selected upstream base URL is known.
|
||||
assert_eq!(input.len(), 3);
|
||||
assert_eq!(input[0]["id"], "rs_provider_123");
|
||||
assert_eq!(input[1]["type"], "message");
|
||||
assert_eq!(input[1]["id"], "item_72d3bd8d367d01977ace23f1");
|
||||
assert_eq!(input[2]["type"], "message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user