mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +08:00
feat(openai): align GPT-5.6 and Codex request contracts
This commit is contained in:
@@ -463,8 +463,8 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_responses_cross_fo
|
||||
accept: String,
|
||||
authorization: String,
|
||||
x_client_request_id: String,
|
||||
session_id: String,
|
||||
conversation_id: String,
|
||||
codex_session_id: String,
|
||||
codex_thread_id: String,
|
||||
instructions: String,
|
||||
user_text: String,
|
||||
prompt_cache_key: String,
|
||||
@@ -742,15 +742,15 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_responses_cross_fo
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
session_id: payload
|
||||
codex_session_id: payload
|
||||
.get("headers")
|
||||
.and_then(|value| value.get("session_id"))
|
||||
.and_then(|value| value.get("session-id"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
conversation_id: payload
|
||||
codex_thread_id: payload
|
||||
.get("headers")
|
||||
.and_then(|value| value.get("conversation_id"))
|
||||
.and_then(|value| value.get("thread-id"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
@@ -889,19 +889,16 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_responses_cross_fo
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.x_client_request_id,
|
||||
"trace-openai-chat-cli-local-123"
|
||||
seen_execution_runtime_request.codex_thread_id
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.prompt_cache_key,
|
||||
"bc749eb7-a9e2-5793-8d14-abd659c700b0"
|
||||
seen_execution_runtime_request.codex_session_id,
|
||||
seen_execution_runtime_request.codex_thread_id
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.session_id,
|
||||
"d1e9b802644e1f52"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.conversation_id,
|
||||
"d1e9b802644e1f52"
|
||||
assert!(seen_execution_runtime_request.prompt_cache_key.is_empty());
|
||||
assert_ne!(
|
||||
seen_execution_runtime_request.codex_thread_id,
|
||||
seen_execution_runtime_request.trace_id
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.instructions,
|
||||
|
||||
@@ -2,7 +2,6 @@ use super::{
|
||||
any, build_router_with_state, build_state_with_execution_runtime_override, json, start_server,
|
||||
to_bytes, Arc, Body, Json, Mutex, Request, Router, StatusCode, TRACE_ID_HEADER,
|
||||
};
|
||||
use crate::ai_serving::CODEX_OPENAI_IMAGE_INTERNAL_MODEL;
|
||||
use aether_crypto::{encrypt_python_fernet_plaintext, DEVELOPMENT_ENCRYPTION_KEY};
|
||||
use aether_data::repository::auth::{
|
||||
InMemoryAuthApiKeySnapshotRepository, StoredAuthApiKeySnapshot,
|
||||
@@ -55,13 +54,9 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
struct SeenExecutionRuntimeStreamRequest {
|
||||
trace_id: String,
|
||||
url: String,
|
||||
model: String,
|
||||
authorization: String,
|
||||
x_client_request_id: String,
|
||||
tool_type: String,
|
||||
tool_action: String,
|
||||
tool_partial_images: Option<u64>,
|
||||
request_stream: bool,
|
||||
headers: serde_json::Value,
|
||||
body: serde_json::Value,
|
||||
plan_stream: bool,
|
||||
}
|
||||
|
||||
@@ -180,7 +175,7 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
None,
|
||||
Some(2),
|
||||
None,
|
||||
Some(serde_json::json!({"upstream_stream_policy":"force_stream"})),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
@@ -272,65 +267,26 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
model: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
authorization: payload
|
||||
.get("headers")
|
||||
.and_then(|value| value.get("authorization"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
x_client_request_id: payload
|
||||
.get("headers")
|
||||
.and_then(|value| value.get("x-client-request-id"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
tool_type: payload
|
||||
headers: payload.get("headers").cloned().unwrap_or_default(),
|
||||
body: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("tools"))
|
||||
.and_then(|value| value.get(0))
|
||||
.and_then(|value| value.get("type"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
tool_action: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("tools"))
|
||||
.and_then(|value| value.get(0))
|
||||
.and_then(|value| value.get("action"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
tool_partial_images: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("tools"))
|
||||
.and_then(|value| value.get(0))
|
||||
.and_then(|value| value.get("partial_images"))
|
||||
.and_then(|value| value.as_u64()),
|
||||
request_stream: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("stream"))
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
plan_stream: payload
|
||||
.get("stream")
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
});
|
||||
let frames = concat!(
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"text/event-stream\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"event: response.output_item.done\\ndata: {\\\"type\\\":\\\"response.output_item.done\\\",\\\"output_index\\\":0,\\\"item\\\":{\\\"id\\\":\\\"ig_123\\\",\\\"type\\\":\\\"image_generation_call\\\",\\\"result\\\":\\\"aGVsbG8=\\\"}}\\n\\n\"}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"event: response.completed\\ndata: {\\\"type\\\":\\\"response.completed\\\",\\\"response\\\":{\\\"tool_usage\\\":{\\\"image_gen\\\":{\\\"input_tokens\\\":11,\\\"output_tokens\\\":22,\\\"total_tokens\\\":33}}}}\\n\\n\"}}\n",
|
||||
"{\"type\":\"headers\",\"payload\":{\"kind\":\"headers\",\"status_code\":200,\"headers\":{\"content-type\":\"application/json\"}}}\n",
|
||||
"{\"type\":\"data\",\"payload\":{\"kind\":\"data\",\"text\":\"{\\\"created\\\":1776991097,\\\"data\\\":[{\\\"b64_json\\\":\\\"aGVsbG8=\\\",\\\"revised_prompt\\\":\\\"水墨视觉海报\\\"}],\\\"usage\\\":{\\\"input_tokens\\\":11,\\\"output_tokens\\\":22,\\\"total_tokens\\\":33}}\"}}\n",
|
||||
"{\"type\":\"telemetry\",\"payload\":{\"kind\":\"telemetry\",\"telemetry\":{\"elapsed_ms\":41}}}\n",
|
||||
"{\"type\":\"eof\",\"payload\":{\"kind\":\"eof\"}}\n"
|
||||
);
|
||||
@@ -397,26 +353,28 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
)
|
||||
.header(TRACE_ID_HEADER, "trace-codex-image-stream-local-123")
|
||||
.body(
|
||||
"{\"model\":\"gpt-image-2\",\"prompt\":\"生成一张中国历史视觉海报\",\"stream\":true,\"partial_images\":1}",
|
||||
"{\"model\":\"gpt-image-2\",\"prompt\":\"生成一张水墨视觉海报\",\"background\":\"auto\",\"quality\":\"auto\",\"size\":\"auto\",\"stream\":true,\"response_format\":\"b64_json\"}",
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some("text/event-stream")
|
||||
);
|
||||
let response_status = response.status();
|
||||
let response_content_type = response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::to_string);
|
||||
let response_text = response.text().await.expect("body should read");
|
||||
assert!(response_text.contains("event: image_generation.partial_image"));
|
||||
assert!(response_text.contains("\"type\":\"image_generation.partial_image\""));
|
||||
assert!(response_text.contains("\"b64_json\":\"aGVsbG8=\""));
|
||||
assert_eq!(response_status, StatusCode::OK, "{response_text}");
|
||||
assert_eq!(
|
||||
response_content_type.as_deref(),
|
||||
Some("text/event-stream"),
|
||||
"{response_text}"
|
||||
);
|
||||
assert!(response_text.contains("event: image_generation.completed"));
|
||||
assert!(response_text.contains("\"type\":\"image_generation.completed\""));
|
||||
assert!(response_text.contains("\"b64_json\":\"aGVsbG8=\""));
|
||||
assert!(response_text.contains("\"total_tokens\":33"));
|
||||
assert!(!response_text.contains("response.completed"));
|
||||
|
||||
@@ -444,25 +402,34 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.url,
|
||||
"https://chatgpt.com/backend-api/codex/responses"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.model,
|
||||
CODEX_OPENAI_IMAGE_INTERNAL_MODEL
|
||||
"https://chatgpt.com/backend-api/codex/images/generations"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.authorization,
|
||||
"Bearer refreshed-codex-image-stream-access-token"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.x_client_request_id,
|
||||
"trace-codex-image-stream-local-123"
|
||||
seen_execution_runtime_request.body,
|
||||
json!({
|
||||
"prompt": "生成一张水墨视觉海报",
|
||||
"background": "auto",
|
||||
"model": "gpt-image-2",
|
||||
"quality": "auto",
|
||||
"size": "auto"
|
||||
})
|
||||
);
|
||||
assert_eq!(seen_execution_runtime_request.tool_type, "image_generation");
|
||||
assert_eq!(seen_execution_runtime_request.tool_action, "generate");
|
||||
assert_eq!(seen_execution_runtime_request.tool_partial_images, Some(1));
|
||||
assert!(seen_execution_runtime_request.request_stream);
|
||||
assert!(seen_execution_runtime_request.plan_stream);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.headers["user-agent"],
|
||||
"codex_cli_rs/0.144.1"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.headers["originator"],
|
||||
"codex_cli_rs"
|
||||
);
|
||||
for header in ["x-client-request-id", "session-id", "thread-id"] {
|
||||
assert!(seen_execution_runtime_request.headers.get(header).is_none());
|
||||
}
|
||||
assert!(!seen_execution_runtime_request.plan_stream);
|
||||
|
||||
gateway_handle.abort();
|
||||
execution_runtime_handle.abort();
|
||||
@@ -594,7 +561,7 @@ async fn gateway_bridges_codex_image_sync_json_to_streaming_image_sse_impl() {
|
||||
None,
|
||||
Some(2),
|
||||
None,
|
||||
Some(serde_json::json!({"upstream_stream_policy":"force_stream"})),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
@@ -772,8 +739,8 @@ async fn gateway_bridges_codex_image_sync_json_to_streaming_image_sse_impl() {
|
||||
seen_execution_runtime_request.trace_id,
|
||||
"trace-codex-image-stream-json-123"
|
||||
);
|
||||
assert!(seen_execution_runtime_request.request_stream);
|
||||
assert!(seen_execution_runtime_request.plan_stream);
|
||||
assert!(!seen_execution_runtime_request.request_stream);
|
||||
assert!(!seen_execution_runtime_request.plan_stream);
|
||||
|
||||
gateway_handle.abort();
|
||||
execution_runtime_handle.abort();
|
||||
@@ -1152,12 +1119,14 @@ async fn gateway_routes_openai_responses_stream_image_intent_to_openai_image_pla
|
||||
seen_plan.url,
|
||||
"https://images.example.com/v1/images/generations"
|
||||
);
|
||||
assert!(seen_plan.plan_stream);
|
||||
assert!(!seen_plan.plan_stream);
|
||||
assert_eq!(seen_plan.auth_header, "Bearer sk-upstream-image-bridge");
|
||||
assert_eq!(seen_plan.body_json["stream"], true);
|
||||
assert_eq!(seen_plan.body_json["input"], "Draw a mountain observatory");
|
||||
assert_eq!(seen_plan.body_json["tools"][0]["type"], "image_generation");
|
||||
assert_eq!(seen_plan.body_json["tools"][0]["size"], "1024x1024");
|
||||
assert_eq!(seen_plan.body_json["model"], "gpt-image-2");
|
||||
assert_eq!(seen_plan.body_json["prompt"], "Draw a mountain observatory");
|
||||
assert_eq!(seen_plan.body_json["size"], "1024x1024");
|
||||
assert!(seen_plan.body_json.get("stream").is_none());
|
||||
assert!(seen_plan.body_json.get("input").is_none());
|
||||
assert!(seen_plan.body_json.get("tools").is_none());
|
||||
|
||||
gateway_handle.abort();
|
||||
execution_runtime_handle.abort();
|
||||
|
||||
Reference in New Issue
Block a user