@heungtae/codex-chat-bridge 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.lock CHANGED
@@ -231,7 +231,7 @@ checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
231
231
 
232
232
  [[package]]
233
233
  name = "codex-chat-bridge"
234
- version = "0.1.3"
234
+ version = "0.1.4"
235
235
  dependencies = [
236
236
  "anyhow",
237
237
  "async-stream",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "codex-chat-bridge"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  edition = "2024"
5
5
  license = "Apache-2.0"
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heungtae/codex-chat-bridge",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Responses-to-chat/completions bridge for Codex workflows",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
package/src/main.rs CHANGED
@@ -25,6 +25,7 @@ use serde::Serialize;
25
25
  use serde_json::Value;
26
26
  use serde_json::json;
27
27
  use std::collections::BTreeMap;
28
+ use std::collections::HashSet;
28
29
  use std::fs::File;
29
30
  use std::fs::{self};
30
31
  use std::io::Write;
@@ -66,6 +67,14 @@ struct Args {
66
67
 
67
68
  #[arg(long)]
68
69
  http_shutdown: bool,
70
+
71
+ #[arg(
72
+ long = "drop-tool-type",
73
+ value_name = "TYPE",
74
+ action = clap::ArgAction::Append,
75
+ help = "drop tool entries whose `type` matches this value; can be repeated"
76
+ )]
77
+ drop_tool_types: Vec<String>,
69
78
  }
70
79
 
71
80
  #[derive(Debug, Clone, Default, Deserialize)]
@@ -76,6 +85,7 @@ struct FileConfig {
76
85
  api_key_env: Option<String>,
77
86
  server_info: Option<PathBuf>,
78
87
  http_shutdown: Option<bool>,
88
+ drop_tool_types: Option<Vec<String>>,
79
89
  }
80
90
 
81
91
  #[derive(Debug, Clone)]
@@ -86,6 +96,7 @@ struct ResolvedConfig {
86
96
  api_key_env: String,
87
97
  server_info: Option<PathBuf>,
88
98
  http_shutdown: bool,
99
+ drop_tool_types: Vec<String>,
89
100
  }
90
101
 
91
102
  const DEFAULT_CONFIG_TEMPLATE: &str = r#"# codex-chat-bridge runtime configuration
@@ -98,6 +109,7 @@ const DEFAULT_CONFIG_TEMPLATE: &str = r#"# codex-chat-bridge runtime configurati
98
109
  # api_key_env = "OPENAI_API_KEY"
99
110
  # server_info = "/tmp/codex-chat-bridge-info.json"
100
111
  # http_shutdown = false
112
+ # drop_tool_types = ["web_search", "web_search_preview"]
101
113
  "#;
102
114
 
103
115
  #[derive(Clone)]
@@ -106,6 +118,7 @@ struct AppState {
106
118
  upstream_url: String,
107
119
  api_key: String,
108
120
  http_shutdown: bool,
121
+ drop_tool_types: HashSet<String>,
109
122
  }
110
123
 
111
124
  #[derive(Serialize)]
@@ -224,6 +237,7 @@ async fn main() -> Result<()> {
224
237
  upstream_url: config.upstream_url.clone(),
225
238
  api_key,
226
239
  http_shutdown: config.http_shutdown,
240
+ drop_tool_types: config.drop_tool_types.into_iter().collect(),
227
241
  });
228
242
 
229
243
  let app = Router::new()
@@ -293,6 +307,9 @@ fn ensure_default_config_file(path: &Path) -> Result<()> {
293
307
 
294
308
  fn resolve_config(args: Args, file_config: Option<FileConfig>) -> ResolvedConfig {
295
309
  let file_config = file_config.unwrap_or_default();
310
+ let mut drop_tool_types = file_config.drop_tool_types.unwrap_or_default();
311
+ drop_tool_types.extend(args.drop_tool_types);
312
+ drop_tool_types.retain(|v| !v.trim().is_empty());
296
313
 
297
314
  ResolvedConfig {
298
315
  host: args
@@ -310,6 +327,7 @@ fn resolve_config(args: Args, file_config: Option<FileConfig>) -> ResolvedConfig
310
327
  .unwrap_or_else(|| "OPENAI_API_KEY".to_string()),
311
328
  server_info: args.server_info.or(file_config.server_info),
312
329
  http_shutdown: args.http_shutdown || file_config.http_shutdown.unwrap_or(false),
330
+ drop_tool_types,
313
331
  }
314
332
  }
315
333
 
@@ -363,7 +381,8 @@ async fn handle_responses(
363
381
  }
364
382
  };
365
383
 
366
- let bridge_request = match map_responses_to_chat_request(&request_value) {
384
+ let bridge_request = match map_responses_to_chat_request(&request_value, &state.drop_tool_types)
385
+ {
367
386
  Ok(v) => v,
368
387
  Err(err) => return sse_error_response("invalid_request", &err.to_string()),
369
388
  };
@@ -660,7 +679,10 @@ fn sse_error_response(code: &str, message: &str) -> Response {
660
679
  .into_response()
661
680
  }
662
681
 
663
- fn map_responses_to_chat_request(request: &Value) -> Result<BridgeRequest> {
682
+ fn map_responses_to_chat_request(
683
+ request: &Value,
684
+ drop_tool_types: &HashSet<String>,
685
+ ) -> Result<BridgeRequest> {
664
686
  let model = request
665
687
  .get("model")
666
688
  .and_then(Value::as_str)
@@ -773,7 +795,7 @@ fn map_responses_to_chat_request(request: &Value) -> Result<BridgeRequest> {
773
795
  }
774
796
  }
775
797
 
776
- let chat_tools = normalize_chat_tools(tools);
798
+ let chat_tools = normalize_chat_tools(tools, drop_tool_types);
777
799
  let chat_tool_choice = normalize_tool_choice(tool_choice);
778
800
 
779
801
  let response_id = format!("resp_bridge_{}", Uuid::now_v7());
@@ -828,11 +850,16 @@ fn function_output_to_text(value: &Value) -> String {
828
850
  }
829
851
  }
830
852
 
831
- fn normalize_chat_tools(tools: Vec<Value>) -> Vec<Value> {
853
+ fn normalize_chat_tools(tools: Vec<Value>, drop_tool_types: &HashSet<String>) -> Vec<Value> {
832
854
  tools
833
855
  .into_iter()
834
856
  .filter_map(|tool| {
835
- if tool.get("type").and_then(Value::as_str) != Some("function") {
857
+ let tool_type = tool.get("type").and_then(Value::as_str);
858
+ if tool_type.is_some_and(|t| drop_tool_types.contains(t)) {
859
+ return None;
860
+ }
861
+
862
+ if tool_type != Some("function") {
836
863
  return Some(tool);
837
864
  }
838
865
 
@@ -958,7 +985,7 @@ mod tests {
958
985
  "parallel_tool_calls": true
959
986
  });
960
987
 
961
- let req = map_responses_to_chat_request(&input).expect("should map");
988
+ let req = map_responses_to_chat_request(&input, &HashSet::new()).expect("should map");
962
989
  let messages = req
963
990
  .chat_request
964
991
  .get("messages")
@@ -1011,7 +1038,7 @@ mod tests {
1011
1038
  #[test]
1012
1039
  fn normalize_chat_tools_passes_non_function_tool() {
1013
1040
  let tools = vec![json!({"type": "web_search_preview"})];
1014
- let out = normalize_chat_tools(tools);
1041
+ let out = normalize_chat_tools(tools, &HashSet::new());
1015
1042
  assert_eq!(out, vec![json!({"type": "web_search_preview"})]);
1016
1043
  }
1017
1044
 
@@ -1039,7 +1066,7 @@ mod tests {
1039
1066
  "tools": []
1040
1067
  });
1041
1068
 
1042
- let req = map_responses_to_chat_request(&input).expect("should map");
1069
+ let req = map_responses_to_chat_request(&input, &HashSet::new()).expect("should map");
1043
1070
  let messages = req
1044
1071
  .chat_request
1045
1072
  .get("messages")
@@ -1059,14 +1086,14 @@ mod tests {
1059
1086
  "tool_choice": 123
1060
1087
  });
1061
1088
 
1062
- let req = map_responses_to_chat_request(&input).expect("should map");
1089
+ let req = map_responses_to_chat_request(&input, &HashSet::new()).expect("should map");
1063
1090
  assert_eq!(req.chat_request["tool_choice"], "auto");
1064
1091
  }
1065
1092
 
1066
1093
  #[test]
1067
1094
  fn map_requires_input_array() {
1068
1095
  let input = json!({"model":"gpt-4.1"});
1069
- let err = map_responses_to_chat_request(&input).expect_err("must fail");
1096
+ let err = map_responses_to_chat_request(&input, &HashSet::new()).expect_err("must fail");
1070
1097
  assert!(err.to_string().contains("missing `input` array"));
1071
1098
  }
1072
1099
 
@@ -1086,7 +1113,7 @@ mod tests {
1086
1113
  "input": [{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}],
1087
1114
  "tools": []
1088
1115
  });
1089
- let req = map_responses_to_chat_request(&input).expect("ok");
1116
+ let req = map_responses_to_chat_request(&input, &HashSet::new()).expect("ok");
1090
1117
  let obj = req.chat_request.as_object().expect("object");
1091
1118
  assert!(!obj.contains_key("tools"));
1092
1119
  assert!(!obj.contains_key("tool_choice"));
@@ -1111,7 +1138,7 @@ mod tests {
1111
1138
  "type": "function",
1112
1139
  "function": {"name":"f", "parameters": {"type":"object"}}
1113
1140
  })];
1114
- let out = normalize_chat_tools(tools.clone());
1141
+ let out = normalize_chat_tools(tools.clone(), &HashSet::new());
1115
1142
  assert_eq!(out, tools);
1116
1143
  }
1117
1144
 
@@ -1135,11 +1162,24 @@ mod tests {
1135
1162
  "input": [{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}],
1136
1163
  "tools": []
1137
1164
  });
1138
- let req = map_responses_to_chat_request(&input).expect("ok");
1165
+ let req = map_responses_to_chat_request(&input, &HashSet::new()).expect("ok");
1139
1166
  let messages = req.chat_request["messages"].as_array().expect("array");
1140
1167
  assert_eq!(messages[0]["role"], "system");
1141
1168
  }
1142
1169
 
1170
+ #[test]
1171
+ fn normalize_chat_tools_drops_configured_tool_types() {
1172
+ let tools = vec![
1173
+ json!({"type": "web_search_preview"}),
1174
+ json!({"type": "function", "name": "f", "parameters": {"type":"object"}}),
1175
+ ];
1176
+ let mut drop = HashSet::new();
1177
+ drop.insert("web_search_preview".to_string());
1178
+ let out = normalize_chat_tools(tools, &drop);
1179
+ assert_eq!(out.len(), 1);
1180
+ assert_eq!(out[0]["type"], "function");
1181
+ }
1182
+
1143
1183
  #[tokio::test]
1144
1184
  async fn stream_emits_output_item_added_before_text_delta() {
1145
1185
  let upstream = stream::iter(vec![Ok::<Bytes, reqwest::Error>(Bytes::from(
@@ -1172,6 +1212,7 @@ mod tests {
1172
1212
  api_key_env: Some("CLI_API_KEY".to_string()),
1173
1213
  server_info: None,
1174
1214
  http_shutdown: true,
1215
+ drop_tool_types: vec![],
1175
1216
  };
1176
1217
  let file = FileConfig {
1177
1218
  host: Some("127.0.0.1".to_string()),
@@ -1180,6 +1221,7 @@ mod tests {
1180
1221
  api_key_env: Some("FILE_API_KEY".to_string()),
1181
1222
  server_info: Some(PathBuf::from("/tmp/server.json")),
1182
1223
  http_shutdown: Some(false),
1224
+ drop_tool_types: None,
1183
1225
  };
1184
1226
 
1185
1227
  let resolved = resolve_config(args, Some(file));
@@ -1204,6 +1246,7 @@ mod tests {
1204
1246
  api_key_env: None,
1205
1247
  server_info: None,
1206
1248
  http_shutdown: false,
1249
+ drop_tool_types: vec![],
1207
1250
  };
1208
1251
 
1209
1252
  let resolved = resolve_config(args, None);