mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-07 00:05:49 +00:00
feat: add openai to codex translation
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>>,
|
||||
}
|
||||
|
||||
@@ -10,3 +10,4 @@ pub mod logs;
|
||||
pub mod pagination;
|
||||
pub mod state;
|
||||
pub mod storage;
|
||||
pub mod translation;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod codex_to_openai;
|
||||
pub mod openai_to_codex;
|
||||
pub mod schema;
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ResponseFormat {
|
||||
Text,
|
||||
JsonObject,
|
||||
JsonSchema,
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user