feat: add openai to codex translation

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
zyycn
2026-06-11 21:26:38 +08:00
co-authored by Codex
parent 0f97c5ce47
commit eff182e5e1
9 changed files with 105 additions and 6 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ Update this file before each feature commit.
| TLS headers and fingerprint | Completed | pending codex headers commit | `cargo test codex_headers_include_desktop_identity_and_turn_state`; `cargo tree \| rg 'reqwest\|rustls'`; full `fmt/test/clippy` before commit | Adds Codex Desktop fingerprint model, exact identity/request headers, and pinned reqwest/rustls client builder. |
| Cookie persistence | Completed | pending cookies commit | `cargo test cookie_jar_captures_and_replays_account_scoped_cookies`; full `fmt/test/clippy` before commit | Adds account-scoped in-memory Cookie jar and repository shell; database persistence follows storage integration. |
| Account pool and refresh | Completed | pending accounts commit | `cargo test account_pool_skips_expired_disabled_banned_and_quota_exhausted_accounts`; full `fmt/test/clippy` before commit | Adds account statuses, acquisition filter, lifecycle events, token pair, OAuth config, and refresh policy shell. |
| Translation | Planned | | | |
| Translation | Completed | pending translation commit | `cargo test chat_completion_translates_to_codex_response_request`; full `fmt/test/clippy` before commit | Adds OpenAI Chat request types, Codex Responses request type, translation, and OpenAI-compatible error body helper. |
| HTTP routes | Planned | | | |
| Upstream lifecycle | Planned | | | |
| Fingerprint updates | Planned | | | |
@@ -2310,7 +2310,7 @@ git commit -m "feat: add account pool foundation"
- Modify: `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs`
- Test: `/home/zyy/桌面/Codes/codex-proxy-rs/tests/routes_chat_test.rs`
- [ ] **Step 1: Write failing translation test**
- [x] **Step 1: Write failing translation test**
Create `/home/zyy/桌面/Codes/codex-proxy-rs/tests/routes_chat_test.rs`:
@@ -2335,7 +2335,7 @@ fn chat_completion_translates_to_codex_response_request() {
}
```
- [ ] **Step 2: Run test and verify RED**
- [x] **Step 2: Run test and verify RED**
Run:
@@ -2345,7 +2345,7 @@ cargo test chat_completion_translates_to_codex_response_request
Expected: compile failure because translation module does not exist.
- [ ] **Step 3: Implement minimal translation types**
- [x] **Step 3: Implement minimal translation types**
Modify `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs`:
@@ -2463,7 +2463,7 @@ pub enum ResponseFormat {
}
```
- [ ] **Step 4: Verify translation**
- [x] **Step 4: Verify translation**
Run:
@@ -2473,7 +2473,7 @@ cargo test chat_completion_translates_to_codex_response_request
Expected: pass.
- [ ] **Step 5: Commit translation layer**
- [x] **Step 5: Commit translation layer**
```bash
git add src/translation src/codex/types.rs src/lib.rs tests/routes_chat_test.rs
+14
View File
@@ -1 +1,15 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodexResponsesRequest {
pub model: String,
pub instructions: String,
pub input: Vec<Value>,
pub stream: bool,
pub store: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Value>>,
}
+1
View File
@@ -10,3 +10,4 @@ pub mod logs;
pub mod pagination;
pub mod state;
pub mod storage;
pub mod translation;
+12
View File
@@ -0,0 +1,12 @@
use serde_json::{json, Value};
pub fn openai_error(message: &str, code: &str) -> Value {
json!({
"error": {
"message": message,
"type": "server_error",
"param": null,
"code": code
}
})
}
+3
View File
@@ -0,0 +1,3 @@
pub mod codex_to_openai;
pub mod openai_to_codex;
pub mod schema;
+43
View File
@@ -0,0 +1,43 @@
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
codex::types::CodexResponsesRequest,
error::{AppError, AppResult},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionRequest {
pub model: String,
#[serde(default)]
pub stream: bool,
pub messages: Vec<ChatMessage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: String,
pub content: String,
}
pub fn translate_chat_to_codex(req: ChatCompletionRequest) -> AppResult<CodexResponsesRequest> {
if req.messages.is_empty() {
return Err(AppError::BadRequest(
"messages must not be empty".to_string(),
));
}
let input = req
.messages
.into_iter()
.map(|message| json!({ "role": message.role, "content": message.content }))
.collect();
Ok(CodexResponsesRequest {
model: req.model,
instructions: String::new(),
input,
stream: req.stream,
store: false,
reasoning: None,
tools: None,
})
}
+6
View File
@@ -0,0 +1,6 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponseFormat {
Text,
JsonObject,
JsonSchema,
}
+20
View File
@@ -0,0 +1,20 @@
use codex_proxy_rs::translation::openai_to_codex::{
translate_chat_to_codex, ChatCompletionRequest, ChatMessage,
};
#[test]
fn chat_completion_translates_to_codex_response_request() {
let req = ChatCompletionRequest {
model: "gpt-5.4".to_string(),
stream: true,
messages: vec![ChatMessage {
role: "user".to_string(),
content: "hello".to_string(),
}],
};
let codex = translate_chat_to_codex(req).unwrap();
assert_eq!(codex.model, "gpt-5.4");
assert!(codex.stream);
assert_eq!(codex.input[0]["role"], "user");
}