mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat(codex-image): 封装 GPT Image 2 图片接口并收紧错误处理
- 新增 openai:image 路由、planner 与 finalize,内部通过 Codex responses image_generation tool 执行生图 - 补充 Codex OAuth/header 兼容、图片 success report 本地处理与相关前后端/集成测试 - 禁止 chat/completions 使用 gpt-image-2,图片接口限制 n=1,并移除 Provider 模型页的图片能力开关
This commit is contained in:
@@ -37,6 +37,7 @@ pub fn build_admin_provider_model_create_record(
|
||||
supports_function_calling: Option<bool>,
|
||||
supports_streaming: Option<bool>,
|
||||
supports_extended_thinking: Option<bool>,
|
||||
supports_image_generation: Option<bool>,
|
||||
is_active: Option<bool>,
|
||||
config: Option<serde_json::Value>,
|
||||
) -> Result<UpsertAdminProviderModelRecord, String> {
|
||||
@@ -52,7 +53,7 @@ pub fn build_admin_provider_model_create_record(
|
||||
supports_function_calling,
|
||||
supports_streaming,
|
||||
supports_extended_thinking,
|
||||
None,
|
||||
supports_image_generation,
|
||||
is_active.unwrap_or(true),
|
||||
true,
|
||||
config,
|
||||
@@ -72,6 +73,7 @@ pub fn build_admin_provider_model_update_record(
|
||||
supports_function_calling: Option<bool>,
|
||||
supports_streaming: Option<bool>,
|
||||
supports_extended_thinking: Option<bool>,
|
||||
supports_image_generation: Option<bool>,
|
||||
is_active: bool,
|
||||
is_available: bool,
|
||||
config: Option<serde_json::Value>,
|
||||
@@ -88,7 +90,7 @@ pub fn build_admin_provider_model_update_record(
|
||||
supports_function_calling,
|
||||
supports_streaming,
|
||||
supports_extended_thinking,
|
||||
existing.supports_image_generation,
|
||||
supports_image_generation,
|
||||
is_active,
|
||||
is_available,
|
||||
config,
|
||||
|
||||
@@ -541,6 +541,12 @@ const ADMIN_API_FORMAT_DEFINITIONS: &[AdminApiFormatDefinition] = &[
|
||||
default_path: "/v1/responses/compact",
|
||||
aliases: &["openai_compact", "responses_compact"],
|
||||
},
|
||||
AdminApiFormatDefinition {
|
||||
value: "openai:image",
|
||||
label: "OpenAI Image",
|
||||
default_path: "/v1/images/generations",
|
||||
aliases: &["openai_image", "images"],
|
||||
},
|
||||
AdminApiFormatDefinition {
|
||||
value: "openai:video",
|
||||
label: "OpenAI Video",
|
||||
|
||||
@@ -48,10 +48,11 @@ pub use crate::contracts::{
|
||||
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND, OPENAI_CLI_SYNC_PLAN_KIND,
|
||||
OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_ERROR_REPORT_KIND, OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_IMAGE_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_SUCCESS_REPORT_KIND,
|
||||
OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND, OPENAI_VIDEO_CONTENT_PLAN_KIND,
|
||||
OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
};
|
||||
pub use crate::conversion::request::{
|
||||
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
||||
@@ -134,6 +135,7 @@ pub use crate::planner::specialized::{
|
||||
resolve_stream_spec as resolve_gemini_files_stream_spec,
|
||||
resolve_sync_spec as resolve_gemini_files_sync_spec, LocalGeminiFilesSpec,
|
||||
},
|
||||
image::{resolve_sync_spec as resolve_local_image_sync_spec, LocalOpenAiImageSpec},
|
||||
video::{
|
||||
resolve_sync_spec as resolve_local_video_sync_spec, LocalVideoCreateFamily,
|
||||
LocalVideoCreateSpec,
|
||||
|
||||
@@ -23,7 +23,7 @@ pub use plan_kinds::{
|
||||
GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
|
||||
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
};
|
||||
@@ -43,5 +43,6 @@ pub use report_kinds::{
|
||||
OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND, OPENAI_CLI_STREAM_SUCCESS_REPORT_KIND,
|
||||
OPENAI_CLI_SYNC_ERROR_REPORT_KIND, OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND, OPENAI_COMPACT_SYNC_ERROR_REPORT_KIND,
|
||||
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND, OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND, OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND,
|
||||
OPENAI_IMAGE_SYNC_SUCCESS_REPORT_KIND, OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ pub const GEMINI_FILES_UPLOAD_PLAN_KIND: &str = "gemini_files_upload";
|
||||
pub const GEMINI_FILES_LIST_PLAN_KIND: &str = "gemini_files_list";
|
||||
pub const GEMINI_FILES_DELETE_PLAN_KIND: &str = "gemini_files_delete";
|
||||
pub const GEMINI_FILES_DOWNLOAD_PLAN_KIND: &str = "gemini_files_download";
|
||||
pub const OPENAI_IMAGE_SYNC_PLAN_KIND: &str = "openai_image_sync";
|
||||
pub const OPENAI_VIDEO_CONTENT_PLAN_KIND: &str = "openai_video_content";
|
||||
pub const OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND: &str = "openai_video_cancel_sync";
|
||||
pub const OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND: &str = "openai_video_remix_sync";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::contracts::{
|
||||
CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_SYNC_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND,
|
||||
GEMINI_CLI_SYNC_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND,
|
||||
};
|
||||
|
||||
pub const OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "openai_chat_sync_finalize";
|
||||
@@ -9,6 +9,7 @@ pub const CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "claude_chat_sync_finali
|
||||
pub const GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND: &str = "gemini_chat_sync_finalize";
|
||||
pub const OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "openai_cli_sync_finalize";
|
||||
pub const OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND: &str = "openai_compact_sync_finalize";
|
||||
pub const OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND: &str = "openai_image_sync_finalize";
|
||||
pub const CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "claude_cli_sync_finalize";
|
||||
pub const GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND: &str = "gemini_cli_sync_finalize";
|
||||
pub const OPENAI_VIDEO_CREATE_SYNC_FINALIZE_REPORT_KIND: &str = "openai_video_create_sync_finalize";
|
||||
@@ -18,6 +19,7 @@ pub const OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "openai_chat_sync_success
|
||||
pub const CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "claude_chat_sync_success";
|
||||
pub const GEMINI_CHAT_SYNC_SUCCESS_REPORT_KIND: &str = "gemini_chat_sync_success";
|
||||
pub const OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "openai_cli_sync_success";
|
||||
pub const OPENAI_IMAGE_SYNC_SUCCESS_REPORT_KIND: &str = "openai_image_sync_success";
|
||||
pub const CLAUDE_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "claude_cli_sync_success";
|
||||
pub const GEMINI_CLI_SYNC_SUCCESS_REPORT_KIND: &str = "gemini_cli_sync_success";
|
||||
|
||||
@@ -43,6 +45,7 @@ pub fn implicit_sync_finalize_report_kind(plan_kind: &str) -> Option<&'static st
|
||||
GEMINI_CHAT_SYNC_PLAN_KIND => Some(GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND),
|
||||
OPENAI_CLI_SYNC_PLAN_KIND => Some(OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND),
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND => Some(OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND),
|
||||
OPENAI_IMAGE_SYNC_PLAN_KIND => Some(OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND),
|
||||
CLAUDE_CLI_SYNC_PLAN_KIND => Some(CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND),
|
||||
GEMINI_CLI_SYNC_PLAN_KIND => Some(GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND),
|
||||
_ => None,
|
||||
@@ -56,6 +59,7 @@ pub fn core_error_default_client_api_format(report_kind: &str) -> Option<&'stati
|
||||
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("gemini:chat"),
|
||||
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("openai:cli"),
|
||||
OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => Some("openai:compact"),
|
||||
OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND => Some("openai:image"),
|
||||
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some("claude:cli"),
|
||||
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("gemini:cli"),
|
||||
_ => None,
|
||||
@@ -80,6 +84,7 @@ pub fn core_success_background_report_kind(report_kind: &str) -> Option<&'static
|
||||
OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND),
|
||||
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND),
|
||||
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some(GEMINI_CHAT_SYNC_SUCCESS_REPORT_KIND),
|
||||
OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND => Some(OPENAI_IMAGE_SYNC_SUCCESS_REPORT_KIND),
|
||||
OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND | OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => {
|
||||
Some(OPENAI_CLI_SYNC_SUCCESS_REPORT_KIND)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::contracts::{
|
||||
GEMINI_FILES_UPLOAD_PLAN_KIND, GEMINI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND,
|
||||
OPENAI_CLI_STREAM_PLAN_KIND, OPENAI_CLI_SYNC_PLAN_KIND, OPENAI_COMPACT_STREAM_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
OPENAI_COMPACT_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND, OPENAI_VIDEO_CANCEL_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
};
|
||||
@@ -168,6 +168,14 @@ pub fn resolve_execution_runtime_sync_plan_kind(
|
||||
return Some(OPENAI_CHAT_SYNC_PLAN_KIND);
|
||||
}
|
||||
|
||||
if route_family == Some("openai")
|
||||
&& route_kind == Some("image")
|
||||
&& *method == Method::POST
|
||||
&& matches!(path, "/v1/images/generations" | "/v1/images/edits")
|
||||
{
|
||||
return Some(OPENAI_IMAGE_SYNC_PLAN_KIND);
|
||||
}
|
||||
|
||||
if route_family == Some("openai")
|
||||
&& route_kind == Some("cli")
|
||||
&& *method == Method::POST
|
||||
@@ -265,6 +273,7 @@ pub fn supports_sync_scheduler_decision_kind(plan_kind: &str) -> bool {
|
||||
matches!(
|
||||
plan_kind,
|
||||
OPENAI_CHAT_SYNC_PLAN_KIND
|
||||
| OPENAI_IMAGE_SYNC_PLAN_KIND
|
||||
| OPENAI_CLI_SYNC_PLAN_KIND
|
||||
| OPENAI_COMPACT_SYNC_PLAN_KIND
|
||||
| CLAUDE_CHAT_SYNC_PLAN_KIND
|
||||
@@ -308,7 +317,9 @@ mod tests {
|
||||
resolve_execution_runtime_sync_plan_kind, supports_stream_scheduler_decision_kind,
|
||||
supports_sync_scheduler_decision_kind,
|
||||
};
|
||||
use crate::contracts::{OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND};
|
||||
use crate::contracts::{
|
||||
OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn resolves_openai_chat_plan_kinds() {
|
||||
@@ -353,4 +364,31 @@ mod tests {
|
||||
OPENAI_CHAT_STREAM_PLAN_KIND
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_openai_image_sync_plan_kind() {
|
||||
assert_eq!(
|
||||
resolve_execution_runtime_sync_plan_kind(
|
||||
Some("ai_public"),
|
||||
Some("openai"),
|
||||
Some("image"),
|
||||
&Method::POST,
|
||||
"/v1/images/generations",
|
||||
),
|
||||
Some(OPENAI_IMAGE_SYNC_PLAN_KIND)
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_execution_runtime_sync_plan_kind(
|
||||
Some("ai_public"),
|
||||
Some("openai"),
|
||||
Some("image"),
|
||||
&Method::POST,
|
||||
"/v1/images/edits",
|
||||
),
|
||||
Some(OPENAI_IMAGE_SYNC_PLAN_KIND)
|
||||
);
|
||||
assert!(supports_sync_scheduler_decision_kind(
|
||||
OPENAI_IMAGE_SYNC_PLAN_KIND
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
31
crates/aether-ai-pipeline/src/planner/specialized/image.rs
Normal file
31
crates/aether-ai-pipeline/src/planner/specialized/image.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use crate::contracts::OPENAI_IMAGE_SYNC_PLAN_KIND;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct LocalOpenAiImageSpec {
|
||||
pub api_format: &'static str,
|
||||
pub decision_kind: &'static str,
|
||||
pub report_kind: &'static str,
|
||||
}
|
||||
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalOpenAiImageSpec> {
|
||||
match plan_kind {
|
||||
OPENAI_IMAGE_SYNC_PLAN_KIND => Some(LocalOpenAiImageSpec {
|
||||
api_format: "openai:image",
|
||||
decision_kind: OPENAI_IMAGE_SYNC_PLAN_KIND,
|
||||
report_kind: "openai_image_sync_finalize",
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::resolve_sync_spec;
|
||||
|
||||
#[test]
|
||||
fn resolves_openai_image_sync_spec() {
|
||||
let spec = resolve_sync_spec("openai_image_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "openai:image");
|
||||
assert_eq!(spec.report_kind, "openai_image_sync_finalize");
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod files;
|
||||
pub mod image;
|
||||
pub mod video;
|
||||
|
||||
@@ -8,6 +8,10 @@ use sha2::Sha256;
|
||||
use uuid::Uuid;
|
||||
|
||||
const CODEX_PROMPT_CACHE_NAMESPACE_VERSION: &str = "v3";
|
||||
const CODEX_DEFAULT_USER_AGENT: &str =
|
||||
"codex-tui/0.122.0 (Aether; x86_64) vscode/3.0.12 (codex-tui; 0.122.0)";
|
||||
const CODEX_DEFAULT_VERSION: &str = "0.122.0";
|
||||
const CODEX_DEFAULT_ORIGINATOR: &str = "codex_cli_rs";
|
||||
const UUID_NAMESPACE_OID_BYTES: [u8; 16] = [
|
||||
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||
];
|
||||
@@ -16,7 +20,7 @@ fn is_codex_openai_cli_request(provider_type: &str, provider_api_format: &str) -
|
||||
provider_type.trim().eq_ignore_ascii_case("codex")
|
||||
&& matches!(
|
||||
provider_api_format.trim().to_ascii_lowercase().as_str(),
|
||||
"openai:cli" | "openai:compact"
|
||||
"openai:cli" | "openai:compact" | "openai:image"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -239,6 +243,30 @@ pub fn apply_codex_openai_cli_special_headers(
|
||||
}
|
||||
}
|
||||
|
||||
if !header_map_has_non_empty_value(original_headers, "user-agent")
|
||||
&& !btree_map_has_non_empty_value(provider_request_headers, "user-agent")
|
||||
{
|
||||
provider_request_headers.insert(
|
||||
"user-agent".to_string(),
|
||||
CODEX_DEFAULT_USER_AGENT.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
if !header_map_has_non_empty_value(original_headers, "version")
|
||||
&& !btree_map_has_non_empty_value(provider_request_headers, "version")
|
||||
{
|
||||
provider_request_headers.insert("version".to_string(), CODEX_DEFAULT_VERSION.to_string());
|
||||
}
|
||||
|
||||
if !header_map_has_non_empty_value(original_headers, "originator")
|
||||
&& !btree_map_has_non_empty_value(provider_request_headers, "originator")
|
||||
{
|
||||
provider_request_headers.insert(
|
||||
"originator".to_string(),
|
||||
CODEX_DEFAULT_ORIGINATOR.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let short_session_id = prompt_cache_key.and_then(build_short_codex_header_id);
|
||||
|
||||
if !header_map_has_non_empty_value(original_headers, "session_id")
|
||||
|
||||
@@ -251,6 +251,7 @@ pub fn preset_models_for_provider(provider_type: &str) -> Option<Vec<Value>> {
|
||||
],
|
||||
"codex" => vec![
|
||||
preset_model("gpt-5", "openai", "GPT-5", "openai:cli"),
|
||||
preset_model("gpt-image-2", "openai", "GPT Image 2", "openai:image"),
|
||||
preset_model("gpt-5-codex", "openai", "GPT-5 Codex", "openai:cli"),
|
||||
preset_model("gpt-5-codex-mini", "openai", "GPT-5 Codex Mini", "openai:cli"),
|
||||
preset_model("gpt-5.1", "openai", "GPT-5.1", "openai:cli"),
|
||||
|
||||
@@ -87,6 +87,9 @@ pub fn infer_internal_finalize_signature(payload: &GatewaySyncReportRequest) ->
|
||||
if report_kind.starts_with("openai_compact_") {
|
||||
return Some("openai:compact".to_string());
|
||||
}
|
||||
if report_kind.starts_with("openai_image_") {
|
||||
return Some("openai:image".to_string());
|
||||
}
|
||||
if report_kind.starts_with("openai_cli_") {
|
||||
return Some("openai:cli".to_string());
|
||||
}
|
||||
@@ -128,6 +131,11 @@ pub fn resolve_internal_finalize_route(signature: &str) -> Option<InternalFinali
|
||||
route_family: "openai",
|
||||
route_kind: "compact",
|
||||
}),
|
||||
"openai:image" => Some(InternalFinalizeRoute {
|
||||
public_path: "/v1/images/generations",
|
||||
route_family: "openai",
|
||||
route_kind: "image",
|
||||
}),
|
||||
"openai:video" => Some(InternalFinalizeRoute {
|
||||
public_path: "/v1/videos",
|
||||
route_family: "openai",
|
||||
@@ -224,6 +232,7 @@ pub fn is_local_ai_sync_report_kind(report_kind: &str) -> bool {
|
||||
| "claude_chat_sync_error"
|
||||
| "gemini_chat_sync_error"
|
||||
| "openai_cli_sync_success"
|
||||
| "openai_image_sync_success"
|
||||
| "claude_cli_sync_success"
|
||||
| "gemini_cli_sync_success"
|
||||
| "openai_cli_sync_error"
|
||||
@@ -406,6 +415,7 @@ mod tests {
|
||||
assert!(is_local_ai_sync_report_kind(
|
||||
"openai_video_create_sync_success"
|
||||
));
|
||||
assert!(is_local_ai_sync_report_kind("openai_image_sync_success"));
|
||||
assert!(is_local_ai_sync_report_kind("gemini_files_delete_mapping"));
|
||||
assert!(!is_local_ai_sync_report_kind("unknown_sync_kind"));
|
||||
}
|
||||
@@ -466,6 +476,13 @@ mod tests {
|
||||
Some("openai:video".to_string())
|
||||
);
|
||||
|
||||
let from_image_report_kind =
|
||||
sample_sync_report_with_context("openai_image_sync_finalize", json!({}));
|
||||
assert_eq!(
|
||||
infer_internal_finalize_signature(&from_image_report_kind),
|
||||
Some("openai:image".to_string())
|
||||
);
|
||||
|
||||
let unknown = sample_sync_report("unknown_sync_finalize", 200);
|
||||
assert_eq!(infer_internal_finalize_signature(&unknown), None);
|
||||
}
|
||||
@@ -488,6 +505,14 @@ mod tests {
|
||||
route_kind: "video",
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_internal_finalize_route("openai:image"),
|
||||
Some(InternalFinalizeRoute {
|
||||
public_path: "/v1/images/generations",
|
||||
route_family: "openai",
|
||||
route_kind: "image",
|
||||
})
|
||||
);
|
||||
assert_eq!(resolve_internal_finalize_route("unknown:kind"), None);
|
||||
}
|
||||
|
||||
@@ -615,6 +640,15 @@ mod tests {
|
||||
})),
|
||||
"openai_chat_sync_success"
|
||||
));
|
||||
assert!(should_handle_local_sync_report(
|
||||
Some(&json!({
|
||||
"request_id": "req-123",
|
||||
"provider_id": "provider-123",
|
||||
"endpoint_id": "endpoint-123",
|
||||
"key_id": "key-123"
|
||||
})),
|
||||
"openai_image_sync_success"
|
||||
));
|
||||
assert!(!should_handle_local_sync_report(
|
||||
Some(&json!({"request_id": "req-123"})),
|
||||
"openai_chat_sync_success"
|
||||
|
||||
Reference in New Issue
Block a user