mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
refactor(ai-formats): group formats by provider
Move protocol/request/response format modules under provider-oriented formats modules and update registry, transport, and architecture paths.
This commit is contained in:
@@ -701,13 +701,13 @@ fn gateway_ai_serving_api_module_delegates_pure_ownership_to_format_crate() {
|
|||||||
"pub use crate::provider_compat::kiro_stream::{",
|
"pub use crate::provider_compat::kiro_stream::{",
|
||||||
"pub use crate::provider_compat::private_envelope::{",
|
"pub use crate::provider_compat::private_envelope::{",
|
||||||
"pub use crate::provider_compat::surfaces::{",
|
"pub use crate::provider_compat::surfaces::{",
|
||||||
"pub use crate::request::common::{",
|
"pub use crate::formats::shared::request::{",
|
||||||
"pub use crate::request::route::{",
|
"pub use crate::formats::shared::routing::{",
|
||||||
"pub use crate::response::common::{",
|
"pub use crate::formats::shared::response::{",
|
||||||
"pub use crate::response::error_body::{",
|
"pub use crate::formats::shared::error_body::{",
|
||||||
"pub use aether_ai_formats::{",
|
"pub use aether_ai_formats::{",
|
||||||
"pub use aether_ai_formats::protocol::conversion::request::{",
|
"pub use aether_ai_formats::formats::conversion::request::{",
|
||||||
"pub use aether_ai_formats::protocol::conversion::response::{",
|
"pub use aether_ai_formats::formats::conversion::response::{",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
format_crate_api.contains(pattern),
|
format_crate_api.contains(pattern),
|
||||||
|
|||||||
@@ -2153,7 +2153,7 @@ fn ai_serving_image_routes_split_surface_normalization_and_transport_policy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let surface_image =
|
let surface_image =
|
||||||
read_workspace_file("crates/aether-ai-formats/src/request/specialized/image.rs");
|
read_workspace_file("crates/aether-ai-formats/src/formats/openai/image/request.rs");
|
||||||
for pattern in [
|
for pattern in [
|
||||||
"pub enum OpenAiImageOperation",
|
"pub enum OpenAiImageOperation",
|
||||||
"pub fn is_openai_image_stream_request(",
|
"pub fn is_openai_image_stream_request(",
|
||||||
@@ -3220,7 +3220,7 @@ fn ai_serving_leaf_planner_owners_route_contract_specs_through_gateway_seams() {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!specialized_files_decision
|
!specialized_files_decision
|
||||||
.contains("aether_ai_formats::request::specialized::files::LocalGeminiFilesSpec"),
|
.contains("aether_ai_formats::formats::gemini::files::spec::LocalGeminiFilesSpec"),
|
||||||
"planner/specialized/files/decision.rs should consume LocalGeminiFilesSpec through the local specialized seam"
|
"planner/specialized/files/decision.rs should consume LocalGeminiFilesSpec through the local specialized seam"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -3243,7 +3243,7 @@ fn ai_serving_m5_moves_contracts_and_route_logic_into_format_crate() {
|
|||||||
"crates/aether-ai-formats/src/contracts/actions.rs",
|
"crates/aether-ai-formats/src/contracts/actions.rs",
|
||||||
"crates/aether-ai-formats/src/contracts/plan_kinds.rs",
|
"crates/aether-ai-formats/src/contracts/plan_kinds.rs",
|
||||||
"crates/aether-ai-formats/src/contracts/report_kinds.rs",
|
"crates/aether-ai-formats/src/contracts/report_kinds.rs",
|
||||||
"crates/aether-ai-formats/src/request/route.rs",
|
"crates/aether-ai-formats/src/formats/shared/routing.rs",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists(path),
|
workspace_file_exists(path),
|
||||||
@@ -3304,7 +3304,8 @@ fn ai_serving_m5_moves_contracts_and_route_logic_into_format_crate() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let surface_route = read_workspace_file("crates/aether-ai-formats/src/request/route.rs");
|
let surface_route =
|
||||||
|
read_workspace_file("crates/aether-ai-formats/src/formats/shared/routing.rs");
|
||||||
for pattern in [
|
for pattern in [
|
||||||
"pub fn is_matching_stream_http_request(",
|
"pub fn is_matching_stream_http_request(",
|
||||||
"is_openai_image_stream_request(parts, body_json, body_base64)",
|
"is_openai_image_stream_request(parts, body_json, body_base64)",
|
||||||
@@ -3553,11 +3554,11 @@ fn ai_serving_error_body_is_owned_by_format_finalize_module() {
|
|||||||
"ai_serving/conversion/error.rs should stay removed"
|
"ai_serving/conversion/error.rs should stay removed"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/response/error_body.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/error_body.rs"),
|
||||||
"format error response-body helpers should live under finalize/error_body.rs"
|
"format error response-body helpers should live under finalize/error_body.rs"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!workspace_file_exists("crates/aether-ai-formats/src/protocol/conversion/error.rs"),
|
!workspace_file_exists("crates/aether-ai-formats/src/formats/conversion/error.rs"),
|
||||||
"aether-ai-formats should not keep error response-body helpers under conversion"
|
"aether-ai-formats should not keep error response-body helpers under conversion"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3582,7 +3583,7 @@ fn ai_serving_error_body_is_owned_by_format_finalize_module() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_conversion_request_is_owned_by_format_crate() {
|
fn ai_serving_conversion_request_is_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/protocol/conversion/request/mod.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/conversion/request.rs"),
|
||||||
"request conversion should live in aether-ai-formats"
|
"request conversion should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -3609,7 +3610,7 @@ fn ai_serving_conversion_request_is_owned_by_format_crate() {
|
|||||||
|
|
||||||
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
||||||
assert!(
|
assert!(
|
||||||
surface_api.contains("pub use aether_ai_formats::protocol::conversion::request::{"),
|
surface_api.contains("pub use aether_ai_formats::formats::conversion::request::{"),
|
||||||
"format API facade should re-export request conversion directly from aether-ai-formats"
|
"format API facade should re-export request conversion directly from aether-ai-formats"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3617,7 +3618,7 @@ fn ai_serving_conversion_request_is_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_conversion_response_is_owned_by_format_crate() {
|
fn ai_serving_conversion_response_is_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/protocol/conversion/response/mod.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/conversion/response.rs"),
|
||||||
"response conversion should live in aether-ai-formats"
|
"response conversion should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -3644,7 +3645,7 @@ fn ai_serving_conversion_response_is_owned_by_format_crate() {
|
|||||||
|
|
||||||
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
||||||
assert!(
|
assert!(
|
||||||
surface_api.contains("pub use aether_ai_formats::protocol::conversion::response::{"),
|
surface_api.contains("pub use aether_ai_formats::formats::conversion::response::{"),
|
||||||
"format API facade should re-export response conversion directly from aether-ai-formats"
|
"format API facade should re-export response conversion directly from aether-ai-formats"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3652,7 +3653,7 @@ fn ai_serving_conversion_response_is_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_format_crate_owns_conversion_and_surface_facade() {
|
fn ai_format_crate_owns_conversion_and_surface_facade() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/protocol/conversion"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/conversion"),
|
||||||
"aether-ai-formats should own the conversion directory"
|
"aether-ai-formats should own the conversion directory"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3665,9 +3666,9 @@ fn ai_format_crate_owns_conversion_and_surface_facade() {
|
|||||||
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
let surface_api = read_workspace_file("crates/aether-ai-formats/src/api.rs");
|
||||||
for pattern in [
|
for pattern in [
|
||||||
"pub use aether_ai_formats::{",
|
"pub use aether_ai_formats::{",
|
||||||
"pub use aether_ai_formats::protocol::conversion::request::{",
|
"pub use aether_ai_formats::formats::conversion::request::{",
|
||||||
"pub use aether_ai_formats::protocol::conversion::response::{",
|
"pub use aether_ai_formats::formats::conversion::response::{",
|
||||||
"pub use crate::response::error_body::{",
|
"pub use crate::formats::shared::error_body::{",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
surface_api.contains(pattern),
|
surface_api.contains(pattern),
|
||||||
@@ -3746,12 +3747,12 @@ fn ai_serving_finalize_standard_sync_response_converters_are_owned_by_format_cra
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_finalize_stream_engine_is_owned_by_format_crate() {
|
fn ai_serving_finalize_stream_engine_is_owned_by_format_crate() {
|
||||||
for path in [
|
for path in [
|
||||||
"crates/aether-ai-formats/src/response/sse.rs",
|
"crates/aether-ai-formats/src/formats/shared/sse.rs",
|
||||||
"crates/aether-ai-formats/src/response/standard/stream_core/common.rs",
|
"crates/aether-ai-formats/src/formats/shared/stream_core/common.rs",
|
||||||
"crates/aether-ai-formats/src/response/standard/stream_core/format_matrix.rs",
|
"crates/aether-ai-formats/src/formats/shared/stream_core/format_matrix.rs",
|
||||||
"crates/aether-ai-formats/src/response/standard/openai/stream.rs",
|
"crates/aether-ai-formats/src/formats/openai/chat/stream.rs",
|
||||||
"crates/aether-ai-formats/src/response/standard/claude/stream.rs",
|
"crates/aether-ai-formats/src/formats/claude/messages/stream.rs",
|
||||||
"crates/aether-ai-formats/src/response/standard/gemini/stream.rs",
|
"crates/aether-ai-formats/src/formats/gemini/generate_content/stream.rs",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists(path),
|
workspace_file_exists(path),
|
||||||
@@ -3787,7 +3788,7 @@ fn ai_serving_finalize_stream_engine_is_owned_by_format_crate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let surface_format_matrix = read_workspace_file(
|
let surface_format_matrix = read_workspace_file(
|
||||||
"crates/aether-ai-formats/src/response/standard/stream_core/format_matrix.rs",
|
"crates/aether-ai-formats/src/formats/shared/stream_core/format_matrix.rs",
|
||||||
);
|
);
|
||||||
for pattern in [
|
for pattern in [
|
||||||
"pub struct StreamingStandardFormatMatrix",
|
"pub struct StreamingStandardFormatMatrix",
|
||||||
@@ -3811,16 +3812,16 @@ fn ai_serving_finalize_stream_engine_is_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_finalize_standard_sync_products_are_owned_by_format_crate() {
|
fn ai_serving_finalize_standard_sync_products_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/response/sync_products.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/sync_products.rs"),
|
||||||
"finalize sync_products should live in aether-ai-formats"
|
"finalize sync_products should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/response/sync_to_stream.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/sync_to_stream.rs"),
|
||||||
"finalize sync-to-stream bridge should live in aether-ai-formats"
|
"finalize sync-to-stream bridge should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
let surface_sync_products =
|
let surface_sync_products =
|
||||||
read_workspace_file("crates/aether-ai-formats/src/response/sync_products.rs");
|
read_workspace_file("crates/aether-ai-formats/src/formats/shared/sync_products.rs");
|
||||||
for expected in [
|
for expected in [
|
||||||
"pub fn maybe_build_standard_cross_format_sync_product_from_normalized_payload(",
|
"pub fn maybe_build_standard_cross_format_sync_product_from_normalized_payload(",
|
||||||
"pub fn maybe_build_standard_same_format_sync_body_from_normalized_payload(",
|
"pub fn maybe_build_standard_same_format_sync_body_from_normalized_payload(",
|
||||||
@@ -3947,7 +3948,7 @@ fn ai_serving_finalize_standard_sync_products_are_owned_by_format_crate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let surface_openai_image_stream =
|
let surface_openai_image_stream =
|
||||||
read_workspace_file("crates/aether-ai-formats/src/response/openai_image_stream.rs");
|
read_workspace_file("crates/aether-ai-formats/src/formats/openai/image/stream.rs");
|
||||||
for expected in [
|
for expected in [
|
||||||
"pub fn maybe_build_openai_image_sync_finalize_product(",
|
"pub fn maybe_build_openai_image_sync_finalize_product(",
|
||||||
"pub struct OpenAiImageSyncFinalizeProduct",
|
"pub struct OpenAiImageSyncFinalizeProduct",
|
||||||
@@ -3962,7 +3963,7 @@ fn ai_serving_finalize_standard_sync_products_are_owned_by_format_crate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let surface_sync_to_stream =
|
let surface_sync_to_stream =
|
||||||
read_workspace_file("crates/aether-ai-formats/src/response/sync_to_stream.rs");
|
read_workspace_file("crates/aether-ai-formats/src/formats/shared/sync_to_stream.rs");
|
||||||
for expected in [
|
for expected in [
|
||||||
"pub fn maybe_bridge_standard_sync_json_to_stream(",
|
"pub fn maybe_bridge_standard_sync_json_to_stream(",
|
||||||
"pub struct SyncToStreamBridgeOutcome",
|
"pub struct SyncToStreamBridgeOutcome",
|
||||||
@@ -3999,11 +4000,11 @@ fn ai_serving_finalize_standard_sync_products_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_finalize_stream_rewrite_matrix_is_owned_by_format_crate() {
|
fn ai_serving_finalize_stream_rewrite_matrix_is_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/response/stream_rewrite.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/stream_rewrite.rs"),
|
||||||
"finalize stream rewrite matrix should live in aether-ai-formats"
|
"finalize stream rewrite matrix should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/response/openai_image_stream.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/openai/image/stream.rs"),
|
||||||
"OpenAI image stream rewrite state should live in aether-ai-formats"
|
"OpenAI image stream rewrite state should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4052,7 +4053,7 @@ fn ai_serving_finalize_stream_rewrite_matrix_is_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_planner_common_parser_is_owned_by_format_crate() {
|
fn ai_serving_planner_common_parser_is_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/common.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/request.rs"),
|
||||||
"planner/common pure parser should exist in aether-ai-formats"
|
"planner/common pure parser should exist in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4195,7 +4196,7 @@ fn ai_serving_root_owns_shared_gemini_request_path_parser() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_planner_standard_normalize_is_owned_by_format_crate() {
|
fn ai_serving_planner_standard_normalize_is_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/normalize.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/standard_normalize.rs"),
|
||||||
"planner/standard/normalize should live in aether-ai-formats"
|
"planner/standard/normalize should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4258,7 +4259,7 @@ fn ai_serving_planner_standard_normalize_is_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_openai_helpers_are_owned_by_format_crate() {
|
fn ai_serving_openai_helpers_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/openai.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/openai/shared.rs"),
|
||||||
"planner/openai helper owner should exist in aether-ai-formats"
|
"planner/openai helper owner should exist in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4291,17 +4292,17 @@ fn ai_serving_openai_helpers_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_standard_matrix_delegates_format_conversion_to_format_crate() {
|
fn ai_serving_standard_matrix_delegates_format_conversion_to_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/matrix.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/request_matrix.rs"),
|
||||||
"planner/matrix facade should live in aether-ai-formats"
|
"planner/matrix facade should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/matrix.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/standard_matrix.rs"),
|
||||||
"format standard request-body planner should live in aether-ai-formats"
|
"format standard request-body planner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
for path in [
|
for path in [
|
||||||
"crates/aether-ai-formats/src/protocol/canonical.rs",
|
"crates/aether-ai-formats/src/protocol/canonical.rs",
|
||||||
"crates/aether-ai-formats/src/protocol/matrix.rs",
|
"crates/aether-ai-formats/src/formats/matrix.rs",
|
||||||
"crates/aether-ai-formats/src/protocol/registry.rs",
|
"crates/aether-ai-formats/src/formats/registry.rs",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists(path),
|
workspace_file_exists(path),
|
||||||
@@ -4309,12 +4310,12 @@ fn ai_serving_standard_matrix_delegates_format_conversion_to_format_crate() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let surface_matrix =
|
let surface_matrix =
|
||||||
read_workspace_file("crates/aether-ai-formats/src/request/standard/matrix.rs");
|
read_workspace_file("crates/aether-ai-formats/src/formats/shared/standard_matrix.rs");
|
||||||
assert!(
|
assert!(
|
||||||
surface_matrix.contains("use aether_ai_formats::protocol::registry::{")
|
surface_matrix.contains("use aether_ai_formats::formats::registry::{")
|
||||||
&& surface_matrix.contains("convert_request")
|
&& surface_matrix.contains("convert_request")
|
||||||
&& surface_matrix.contains("FormatContext")
|
&& surface_matrix.contains("FormatContext")
|
||||||
&& surface_matrix.contains("aether_ai_formats::protocol::conversion::request::{"),
|
&& surface_matrix.contains("aether_ai_formats::formats::conversion::request::{"),
|
||||||
"format standard matrix should delegate format conversion to aether-ai-formats"
|
"format standard matrix should delegate format conversion to aether-ai-formats"
|
||||||
);
|
);
|
||||||
for forbidden in [
|
for forbidden in [
|
||||||
@@ -4361,23 +4362,27 @@ fn ai_serving_standard_matrix_delegates_format_conversion_to_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_standard_family_specs_are_owned_by_format_crate() {
|
fn ai_serving_standard_family_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/family.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/family.rs"),
|
||||||
"planner/standard/family pure spec owner should live in aether-ai-formats"
|
"planner/standard/family pure spec owner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/claude/chat.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/claude/messages/chat_spec.rs"),
|
||||||
"planner/standard/claude/chat pure spec resolver should live in aether-ai-formats"
|
"planner/standard/claude/chat pure spec resolver should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/claude/cli.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/claude/messages/cli_spec.rs"),
|
||||||
"planner/standard/claude/cli pure spec resolver should live in aether-ai-formats"
|
"planner/standard/claude/cli pure spec resolver should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/gemini/chat.rs"),
|
workspace_file_exists(
|
||||||
|
"crates/aether-ai-formats/src/formats/gemini/generate_content/chat_spec.rs"
|
||||||
|
),
|
||||||
"planner/standard/gemini/chat pure spec resolver should live in aether-ai-formats"
|
"planner/standard/gemini/chat pure spec resolver should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/gemini/cli.rs"),
|
workspace_file_exists(
|
||||||
|
"crates/aether-ai-formats/src/formats/gemini/generate_content/cli_spec.rs"
|
||||||
|
),
|
||||||
"planner/standard/gemini/cli pure spec resolver should live in aether-ai-formats"
|
"planner/standard/gemini/cli pure spec resolver should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4444,7 +4449,7 @@ fn ai_serving_standard_family_specs_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_same_format_provider_specs_are_owned_by_format_crate() {
|
fn ai_serving_same_format_provider_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/passthrough/provider.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/passthrough.rs"),
|
||||||
"planner/passthrough/provider pure spec owner should live in aether-ai-formats"
|
"planner/passthrough/provider pure spec owner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4495,7 +4500,7 @@ fn ai_serving_same_format_provider_specs_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_passthrough_provider_specs_are_owned_by_format_crate() {
|
fn ai_serving_passthrough_provider_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/passthrough/provider.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/passthrough.rs"),
|
||||||
"planner/passthrough/provider pure spec owner should live in aether-ai-formats"
|
"planner/passthrough/provider pure spec owner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4540,7 +4545,7 @@ fn ai_serving_passthrough_provider_specs_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_specialized_files_specs_are_owned_by_format_crate() {
|
fn ai_serving_specialized_files_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/specialized/files.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/gemini/files/spec.rs"),
|
||||||
"planner/specialized/files pure spec owner should live in aether-ai-formats"
|
"planner/specialized/files pure spec owner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4570,9 +4575,18 @@ fn ai_serving_specialized_files_specs_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_specialized_video_specs_are_owned_by_format_crate() {
|
fn ai_serving_specialized_video_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/specialized/video.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/shared/video.rs"),
|
||||||
"planner/specialized/video pure spec owner should live in aether-ai-formats"
|
"planner/specialized/video shared spec seam should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
for path in [
|
||||||
|
"crates/aether-ai-formats/src/formats/openai/video/spec.rs",
|
||||||
|
"crates/aether-ai-formats/src/formats/gemini/video/spec.rs",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
workspace_file_exists(path),
|
||||||
|
"{path} should own provider-specific video create spec resolution"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let video =
|
let video =
|
||||||
read_workspace_file("apps/aether-gateway/src/ai_serving/planner/specialized/video.rs");
|
read_workspace_file("apps/aether-gateway/src/ai_serving/planner/specialized/video.rs");
|
||||||
@@ -4598,7 +4612,7 @@ fn ai_serving_specialized_video_specs_are_owned_by_format_crate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ai_serving_openai_responses_specs_are_owned_by_format_crate() {
|
fn ai_serving_openai_responses_specs_are_owned_by_format_crate() {
|
||||||
assert!(
|
assert!(
|
||||||
workspace_file_exists("crates/aether-ai-formats/src/request/standard/openai_responses.rs"),
|
workspace_file_exists("crates/aether-ai-formats/src/formats/openai/responses/spec.rs"),
|
||||||
"planner/standard/openai_responses pure spec owner should live in aether-ai-formats"
|
"planner/standard/openai_responses pure spec owner should live in aether-ai-formats"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -4639,8 +4653,8 @@ fn ai_serving_openai_responses_specs_are_owned_by_format_crate() {
|
|||||||
fn ai_serving_legacy_api_format_names_stay_out_of_primary_paths() {
|
fn ai_serving_legacy_api_format_names_stay_out_of_primary_paths() {
|
||||||
for path in [
|
for path in [
|
||||||
"crates/aether-ai-formats/src/contracts/plan_kinds.rs",
|
"crates/aether-ai-formats/src/contracts/plan_kinds.rs",
|
||||||
"crates/aether-ai-formats/src/request/route.rs",
|
"crates/aether-ai-formats/src/formats/shared/routing.rs",
|
||||||
"crates/aether-ai-formats/src/request/standard/openai_responses.rs",
|
"crates/aether-ai-formats/src/formats/openai/responses/spec.rs",
|
||||||
"apps/aether-gateway/src/ai_serving/planner/decision/control_plan.rs",
|
"apps/aether-gateway/src/ai_serving/planner/decision/control_plan.rs",
|
||||||
"apps/aether-gateway/src/execution_runtime/fallback.rs",
|
"apps/aether-gateway/src/execution_runtime/fallback.rs",
|
||||||
] {
|
] {
|
||||||
@@ -4664,7 +4678,7 @@ fn ai_serving_legacy_api_format_names_stay_out_of_primary_paths() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let registry = read_workspace_file("crates/aether-ai-formats/src/protocol/registry.rs");
|
let registry = read_workspace_file("crates/aether-ai-formats/src/formats/registry.rs");
|
||||||
let implementation = registry
|
let implementation = registry
|
||||||
.split("#[cfg(test)]")
|
.split("#[cfg(test)]")
|
||||||
.next()
|
.next()
|
||||||
@@ -4697,9 +4711,9 @@ fn retired_api_format_occurrences_are_whitelisted() {
|
|||||||
|
|
||||||
let allowed_paths = [
|
let allowed_paths = [
|
||||||
"apps/aether-gateway/src/handlers/admin/provider/write/normalize.rs",
|
"apps/aether-gateway/src/handlers/admin/provider/write/normalize.rs",
|
||||||
"crates/aether-ai-formats/src/protocol/formats.rs",
|
"crates/aether-ai-formats/src/formats/id.rs",
|
||||||
"crates/aether-ai-formats/src/protocol/matrix.rs",
|
"crates/aether-ai-formats/src/formats/matrix.rs",
|
||||||
"crates/aether-ai-formats/src/protocol/registry.rs",
|
"crates/aether-ai-formats/src/formats/registry.rs",
|
||||||
"crates/aether-data/src/migrate.rs",
|
"crates/aether-data/src/migrate.rs",
|
||||||
"crates/aether-data/src/lifecycle/migrate/tests.rs",
|
"crates/aether-data/src/lifecycle/migrate/tests.rs",
|
||||||
"crates/aether-usage-runtime/src/report.rs",
|
"crates/aether-usage-runtime/src/report.rs",
|
||||||
|
|||||||
@@ -38,6 +38,146 @@ pub use crate::contracts::{
|
|||||||
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND,
|
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND,
|
||||||
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||||
};
|
};
|
||||||
|
pub use crate::formats::claude::messages::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
||||||
|
pub use crate::formats::gemini::generate_content::stream::{
|
||||||
|
GeminiClientEmitter, GeminiProviderState,
|
||||||
|
};
|
||||||
|
pub use crate::formats::openai::chat::stream::{
|
||||||
|
OpenAIChatClientEmitter, OpenAIChatProviderState, OpenAIResponsesClientEmitter,
|
||||||
|
OpenAIResponsesProviderState,
|
||||||
|
};
|
||||||
|
pub use crate::formats::openai::image::stream::{
|
||||||
|
maybe_build_openai_image_sync_finalize_product, OpenAiImageStreamState,
|
||||||
|
OpenAiImageSyncFinalizeProduct,
|
||||||
|
};
|
||||||
|
pub use crate::formats::openai::shared::{
|
||||||
|
copy_request_number_field, copy_request_number_field_as,
|
||||||
|
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
|
||||||
|
parse_openai_stop_sequences, resolve_openai_chat_max_tokens, value_as_u64,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::error_body::{
|
||||||
|
build_core_error_body_for_client_format, is_core_error_finalize_kind, LocalCoreSyncErrorKind,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::model_directives::{
|
||||||
|
apply_model_directive_mapping_patch, apply_model_directive_overrides_from_model,
|
||||||
|
apply_model_directive_overrides_from_request, claude_model_uses_adaptive_effort,
|
||||||
|
extract_gemini_model_from_path, gemini_model_uses_thinking_level, model_directive_base_model,
|
||||||
|
normalize_model_directive_model, parse_model_directive, ModelDirective, ModelOverride,
|
||||||
|
ReasoningEffort,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::passthrough::{
|
||||||
|
resolve_stream_spec as resolve_local_same_format_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_local_same_format_sync_spec, LocalSameFormatProviderFamily,
|
||||||
|
LocalSameFormatProviderSpec,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::request::{
|
||||||
|
force_upstream_streaming_for_provider, parse_direct_request_body,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::request_matrix::{
|
||||||
|
build_standard_request_body_from_canonical,
|
||||||
|
build_standard_request_body_from_canonical_with_model_directives,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::response::{
|
||||||
|
build_generated_tool_call_id, build_local_success_background_report,
|
||||||
|
build_local_success_conversion_background_report, canonicalize_tool_arguments,
|
||||||
|
prepare_local_success_response_parts, prepare_local_success_response_parts_owned,
|
||||||
|
LocalSyncReportParts,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::routing::{
|
||||||
|
is_matching_stream_http_request, is_matching_stream_request,
|
||||||
|
resolve_execution_runtime_stream_plan_kind, resolve_execution_runtime_sync_plan_kind,
|
||||||
|
supports_stream_execution_decision_kind, supports_sync_execution_decision_kind,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::sse::{encode_done_sse, encode_json_sse, map_claude_stop_reason};
|
||||||
|
pub use crate::formats::shared::standard_matrix::normalize_standard_request_to_openai_chat_request;
|
||||||
|
pub use crate::formats::shared::stream_core::common::*;
|
||||||
|
pub use crate::formats::shared::stream_core::{
|
||||||
|
CanonicalStreamFrame, StreamingStandardFormatMatrix, StreamingStandardTerminalObserver,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::sync_products::{
|
||||||
|
aggregate_claude_stream_sync_response, aggregate_gemini_stream_sync_response,
|
||||||
|
aggregate_openai_chat_stream_sync_response, aggregate_openai_responses_stream_sync_response,
|
||||||
|
aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response,
|
||||||
|
convert_standard_chat_response, convert_standard_cli_response,
|
||||||
|
maybe_build_openai_chat_cross_format_sync_product_from_normalized_payload,
|
||||||
|
maybe_build_openai_responses_cross_format_sync_product_from_normalized_payload,
|
||||||
|
maybe_build_openai_responses_same_family_sync_body_from_normalized_payload,
|
||||||
|
maybe_build_standard_cross_format_sync_product,
|
||||||
|
maybe_build_standard_cross_format_sync_product_from_normalized_payload,
|
||||||
|
maybe_build_standard_same_format_sync_body_from_normalized_payload,
|
||||||
|
maybe_build_standard_sync_finalize_product_from_normalized_payload,
|
||||||
|
StandardCrossFormatSyncProduct, StandardSyncFinalizeNormalizedProduct,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::sync_to_stream::{
|
||||||
|
maybe_bridge_standard_sync_json_to_stream, SyncToStreamBridgeOutcome,
|
||||||
|
};
|
||||||
|
pub use crate::formats::shared::{
|
||||||
|
maybe_build_ai_surface_stream_rewriter, resolve_finalize_stream_rewrite_mode,
|
||||||
|
AiSurfaceFinalizeError, AiSurfaceStreamRewriter, FinalizeStreamRewriteMode,
|
||||||
|
};
|
||||||
|
pub use crate::formats::{
|
||||||
|
claude::messages::{
|
||||||
|
resolve_stream_spec as resolve_claude_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_claude_sync_spec,
|
||||||
|
},
|
||||||
|
gemini::generate_content::{
|
||||||
|
resolve_stream_spec as resolve_gemini_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_gemini_sync_spec,
|
||||||
|
},
|
||||||
|
openai::responses::{
|
||||||
|
codex::{
|
||||||
|
apply_codex_openai_responses_special_body_edits,
|
||||||
|
apply_codex_openai_responses_special_headers,
|
||||||
|
apply_openai_responses_compact_special_body_edits, CODEX_OPENAI_IMAGE_DEFAULT_MODEL,
|
||||||
|
CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL,
|
||||||
|
CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_PROMPT, CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
||||||
|
},
|
||||||
|
spec::{
|
||||||
|
resolve_stream_spec as resolve_openai_responses_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_openai_responses_sync_spec, LocalOpenAiResponsesSpec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shared::{
|
||||||
|
family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec},
|
||||||
|
standard_matrix::{
|
||||||
|
build_standard_request_body, build_standard_request_body_with_model_directives,
|
||||||
|
build_standard_request_body_with_model_directives_and_request_headers,
|
||||||
|
},
|
||||||
|
standard_normalize::{
|
||||||
|
build_cross_format_openai_chat_request_body,
|
||||||
|
build_cross_format_openai_chat_request_body_with_model_directives,
|
||||||
|
build_cross_format_openai_responses_request_body,
|
||||||
|
build_cross_format_openai_responses_request_body_with_model_directives,
|
||||||
|
build_local_openai_chat_request_body,
|
||||||
|
build_local_openai_chat_request_body_with_model_directives,
|
||||||
|
build_local_openai_responses_request_body,
|
||||||
|
build_local_openai_responses_request_body_with_model_directives,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
pub use crate::formats::{
|
||||||
|
gemini::files::spec::{
|
||||||
|
resolve_stream_spec as resolve_gemini_files_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_gemini_files_sync_spec, LocalGeminiFilesSpec,
|
||||||
|
},
|
||||||
|
openai::image::{
|
||||||
|
request::{
|
||||||
|
build_chatgpt_web_image_request_body, build_openai_image_provider_request_body,
|
||||||
|
default_model_for_openai_image_operation, is_openai_image_stream_request,
|
||||||
|
normalize_openai_image_request, openai_image_operation_from_path,
|
||||||
|
resolve_requested_openai_image_model_for_request, ChatGptWebImageRequestError,
|
||||||
|
NormalizedOpenAiImageRequest, OpenAiImageOperation, OpenAiImageResponseFormat,
|
||||||
|
},
|
||||||
|
spec::{
|
||||||
|
resolve_stream_spec as resolve_local_image_stream_spec,
|
||||||
|
resolve_sync_spec as resolve_local_image_sync_spec, LocalOpenAiImageSpec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shared::video::{
|
||||||
|
resolve_sync_spec as resolve_local_video_sync_spec, LocalVideoCreateFamily,
|
||||||
|
LocalVideoCreateSpec,
|
||||||
|
},
|
||||||
|
};
|
||||||
pub use crate::provider_compat::kiro_stream::{
|
pub use crate::provider_compat::kiro_stream::{
|
||||||
build_kiro_final_message_sse_events, build_kiro_initial_sse_events,
|
build_kiro_final_message_sse_events, build_kiro_initial_sse_events,
|
||||||
build_kiro_stream_error_sse_events, calculate_kiro_context_input_tokens,
|
build_kiro_stream_error_sse_events, calculate_kiro_context_input_tokens,
|
||||||
@@ -59,138 +199,14 @@ pub use crate::provider_compat::surfaces::{
|
|||||||
ProviderAdaptationSurface, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
ProviderAdaptationSurface, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, KIRO_ENVELOPE_NAME,
|
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, KIRO_ENVELOPE_NAME,
|
||||||
};
|
};
|
||||||
pub use crate::request::common::{
|
pub use aether_ai_formats::formats::conversion::request::{
|
||||||
force_upstream_streaming_for_provider, parse_direct_request_body,
|
|
||||||
};
|
|
||||||
pub use crate::request::matrix::{
|
|
||||||
build_standard_request_body_from_canonical,
|
|
||||||
build_standard_request_body_from_canonical_with_model_directives,
|
|
||||||
};
|
|
||||||
pub use crate::request::model_directives::{
|
|
||||||
apply_model_directive_mapping_patch, apply_model_directive_overrides_from_model,
|
|
||||||
apply_model_directive_overrides_from_request, claude_model_uses_adaptive_effort,
|
|
||||||
extract_gemini_model_from_path, gemini_model_uses_thinking_level, model_directive_base_model,
|
|
||||||
normalize_model_directive_model, parse_model_directive, ModelDirective, ModelOverride,
|
|
||||||
ReasoningEffort,
|
|
||||||
};
|
|
||||||
pub use crate::request::openai::{
|
|
||||||
copy_request_number_field, copy_request_number_field_as,
|
|
||||||
map_openai_reasoning_effort_to_claude_output, map_openai_reasoning_effort_to_gemini_budget,
|
|
||||||
parse_openai_stop_sequences, resolve_openai_chat_max_tokens, value_as_u64,
|
|
||||||
};
|
|
||||||
pub use crate::request::passthrough::provider::{
|
|
||||||
resolve_stream_spec as resolve_local_same_format_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_local_same_format_sync_spec, LocalSameFormatProviderFamily,
|
|
||||||
LocalSameFormatProviderSpec,
|
|
||||||
};
|
|
||||||
pub use crate::request::route::{
|
|
||||||
is_matching_stream_http_request, is_matching_stream_request,
|
|
||||||
resolve_execution_runtime_stream_plan_kind, resolve_execution_runtime_sync_plan_kind,
|
|
||||||
supports_stream_execution_decision_kind, supports_sync_execution_decision_kind,
|
|
||||||
};
|
|
||||||
pub use crate::request::specialized::{
|
|
||||||
files::{
|
|
||||||
resolve_stream_spec as resolve_gemini_files_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_gemini_files_sync_spec, LocalGeminiFilesSpec,
|
|
||||||
},
|
|
||||||
image::{
|
|
||||||
build_chatgpt_web_image_request_body, build_openai_image_provider_request_body,
|
|
||||||
default_model_for_openai_image_operation, is_openai_image_stream_request,
|
|
||||||
normalize_openai_image_request, openai_image_operation_from_path,
|
|
||||||
resolve_requested_openai_image_model_for_request,
|
|
||||||
resolve_stream_spec as resolve_local_image_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_local_image_sync_spec, ChatGptWebImageRequestError,
|
|
||||||
LocalOpenAiImageSpec, NormalizedOpenAiImageRequest, OpenAiImageOperation,
|
|
||||||
OpenAiImageResponseFormat,
|
|
||||||
},
|
|
||||||
video::{
|
|
||||||
resolve_sync_spec as resolve_local_video_sync_spec, LocalVideoCreateFamily,
|
|
||||||
LocalVideoCreateSpec,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
pub use crate::request::standard::{
|
|
||||||
apply_codex_openai_responses_special_body_edits, apply_codex_openai_responses_special_headers,
|
|
||||||
apply_openai_responses_compact_special_body_edits, build_cross_format_openai_chat_request_body,
|
|
||||||
build_cross_format_openai_chat_request_body_with_model_directives,
|
|
||||||
build_cross_format_openai_responses_request_body,
|
|
||||||
build_cross_format_openai_responses_request_body_with_model_directives,
|
|
||||||
build_local_openai_chat_request_body,
|
|
||||||
build_local_openai_chat_request_body_with_model_directives,
|
|
||||||
build_local_openai_responses_request_body,
|
|
||||||
build_local_openai_responses_request_body_with_model_directives, build_standard_request_body,
|
|
||||||
build_standard_request_body_with_model_directives,
|
|
||||||
build_standard_request_body_with_model_directives_and_request_headers,
|
|
||||||
claude::{
|
|
||||||
resolve_stream_spec as resolve_claude_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_claude_sync_spec,
|
|
||||||
},
|
|
||||||
gemini::{
|
|
||||||
resolve_stream_spec as resolve_gemini_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_gemini_sync_spec,
|
|
||||||
},
|
|
||||||
normalize_standard_request_to_openai_chat_request,
|
|
||||||
openai_responses::{
|
|
||||||
resolve_stream_spec as resolve_openai_responses_stream_spec,
|
|
||||||
resolve_sync_spec as resolve_openai_responses_sync_spec, LocalOpenAiResponsesSpec,
|
|
||||||
},
|
|
||||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
|
||||||
CODEX_OPENAI_IMAGE_DEFAULT_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT,
|
|
||||||
CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_PROMPT,
|
|
||||||
CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
|
||||||
};
|
|
||||||
pub use crate::response::common::{
|
|
||||||
build_generated_tool_call_id, build_local_success_background_report,
|
|
||||||
build_local_success_conversion_background_report, canonicalize_tool_arguments,
|
|
||||||
prepare_local_success_response_parts, prepare_local_success_response_parts_owned,
|
|
||||||
LocalSyncReportParts,
|
|
||||||
};
|
|
||||||
pub use crate::response::error_body::{
|
|
||||||
build_core_error_body_for_client_format, is_core_error_finalize_kind, LocalCoreSyncErrorKind,
|
|
||||||
};
|
|
||||||
pub use crate::response::openai_image_stream::{
|
|
||||||
maybe_build_openai_image_sync_finalize_product, OpenAiImageStreamState,
|
|
||||||
OpenAiImageSyncFinalizeProduct,
|
|
||||||
};
|
|
||||||
pub use crate::response::sse::{encode_done_sse, encode_json_sse, map_claude_stop_reason};
|
|
||||||
pub use crate::response::standard::claude::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
|
||||||
pub use crate::response::standard::gemini::stream::{GeminiClientEmitter, GeminiProviderState};
|
|
||||||
pub use crate::response::standard::openai::stream::{
|
|
||||||
OpenAIChatClientEmitter, OpenAIChatProviderState, OpenAIResponsesClientEmitter,
|
|
||||||
OpenAIResponsesProviderState,
|
|
||||||
};
|
|
||||||
pub use crate::response::standard::stream_core::common::*;
|
|
||||||
pub use crate::response::standard::stream_core::{
|
|
||||||
CanonicalStreamFrame, StreamingStandardFormatMatrix, StreamingStandardTerminalObserver,
|
|
||||||
};
|
|
||||||
pub use crate::response::sync_products::{
|
|
||||||
aggregate_claude_stream_sync_response, aggregate_gemini_stream_sync_response,
|
|
||||||
aggregate_openai_chat_stream_sync_response, aggregate_openai_responses_stream_sync_response,
|
|
||||||
aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response,
|
|
||||||
convert_standard_chat_response, convert_standard_cli_response,
|
|
||||||
maybe_build_openai_chat_cross_format_sync_product_from_normalized_payload,
|
|
||||||
maybe_build_openai_responses_cross_format_sync_product_from_normalized_payload,
|
|
||||||
maybe_build_openai_responses_same_family_sync_body_from_normalized_payload,
|
|
||||||
maybe_build_standard_cross_format_sync_product,
|
|
||||||
maybe_build_standard_cross_format_sync_product_from_normalized_payload,
|
|
||||||
maybe_build_standard_same_format_sync_body_from_normalized_payload,
|
|
||||||
maybe_build_standard_sync_finalize_product_from_normalized_payload,
|
|
||||||
StandardCrossFormatSyncProduct, StandardSyncFinalizeNormalizedProduct,
|
|
||||||
};
|
|
||||||
pub use crate::response::sync_to_stream::{
|
|
||||||
maybe_bridge_standard_sync_json_to_stream, SyncToStreamBridgeOutcome,
|
|
||||||
};
|
|
||||||
pub use crate::response::{
|
|
||||||
maybe_build_ai_surface_stream_rewriter, resolve_finalize_stream_rewrite_mode,
|
|
||||||
AiSurfaceFinalizeError, AiSurfaceStreamRewriter, FinalizeStreamRewriteMode,
|
|
||||||
};
|
|
||||||
pub use aether_ai_formats::protocol::conversion::request::{
|
|
||||||
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
||||||
convert_openai_chat_request_to_openai_responses_request, extract_openai_text_content,
|
convert_openai_chat_request_to_openai_responses_request, extract_openai_text_content,
|
||||||
normalize_claude_request_to_openai_chat_request,
|
normalize_claude_request_to_openai_chat_request,
|
||||||
normalize_gemini_request_to_openai_chat_request,
|
normalize_gemini_request_to_openai_chat_request,
|
||||||
normalize_openai_responses_request_to_openai_chat_request, parse_openai_tool_result_content,
|
normalize_openai_responses_request_to_openai_chat_request, parse_openai_tool_result_content,
|
||||||
};
|
};
|
||||||
pub use aether_ai_formats::protocol::conversion::response::{
|
pub use aether_ai_formats::formats::conversion::response::{
|
||||||
build_openai_responses_response, build_openai_responses_response_with_content,
|
build_openai_responses_response, build_openai_responses_response_with_content,
|
||||||
build_openai_responses_response_with_reasoning, convert_claude_chat_response_to_openai_chat,
|
build_openai_responses_response_with_reasoning, convert_claude_chat_response_to_openai_chat,
|
||||||
convert_claude_response_to_openai_responses, convert_gemini_chat_response_to_openai_chat,
|
convert_claude_response_to_openai_responses, convert_gemini_chat_response_to_openai_chat,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::contracts::{CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND};
|
use crate::contracts::{CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND};
|
||||||
use crate::request::standard::family::{
|
use crate::formats::shared::family::{
|
||||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::contracts::{CLAUDE_CLI_STREAM_PLAN_KIND, CLAUDE_CLI_SYNC_PLAN_KIND};
|
use crate::contracts::{CLAUDE_CLI_STREAM_PLAN_KIND, CLAUDE_CLI_SYNC_PLAN_KIND};
|
||||||
use crate::request::standard::family::{
|
use crate::formats::shared::family::{
|
||||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||||
};
|
};
|
||||||
|
|
||||||
16
crates/aether-ai-formats/src/formats/claude/messages/mod.rs
Normal file
16
crates/aether-ai-formats/src/formats/claude/messages/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod chat_spec;
|
||||||
|
pub mod cli_spec;
|
||||||
|
pub mod request;
|
||||||
|
pub mod response;
|
||||||
|
pub mod spec;
|
||||||
|
pub mod stream;
|
||||||
|
|
||||||
|
use crate::formats::shared::family::LocalStandardSpec;
|
||||||
|
|
||||||
|
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||||
|
chat_spec::resolve_sync_spec(plan_kind).or_else(|| cli_spec::resolve_sync_spec(plan_kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||||
|
chat_spec::resolve_stream_spec(plan_kind).or_else(|| cli_spec::resolve_stream_spec(plan_kind))
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::{
|
||||||
|
context::FormatContext,
|
||||||
|
openai::shared::{
|
||||||
|
map_openai_reasoning_effort_to_claude_output,
|
||||||
|
map_openai_reasoning_effort_to_thinking_budget,
|
||||||
|
},
|
||||||
|
shared::model_directives::claude_model_uses_adaptive_effort,
|
||||||
|
},
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_extension_object_mut, canonical_instructions_to_claude_system,
|
canonical_extension_object_mut, canonical_instructions_to_claude_system,
|
||||||
canonical_messages_to_claude, canonical_openai_reasoning_effort,
|
canonical_messages_to_claude, canonical_openai_reasoning_effort,
|
||||||
@@ -11,14 +19,6 @@ use crate::{
|
|||||||
compact_canonical_claude_messages, insert_f64, namespace_extension_object,
|
compact_canonical_claude_messages, insert_f64, namespace_extension_object,
|
||||||
CanonicalRequest,
|
CanonicalRequest,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
request::{
|
|
||||||
model_directives::claude_model_uses_adaptive_effort,
|
|
||||||
openai::{
|
|
||||||
map_openai_reasoning_effort_to_claude_output,
|
|
||||||
map_openai_reasoning_effort_to_thinking_budget,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
@@ -3,13 +3,13 @@ use std::collections::BTreeMap;
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_blocks_to_claude, canonical_stop_reason_to_claude, canonical_usage_to_claude,
|
canonical_blocks_to_claude, canonical_stop_reason_to_claude, canonical_usage_to_claude,
|
||||||
claude_content_to_canonical_blocks, claude_extensions, claude_stop_reason_to_canonical,
|
claude_content_to_canonical_blocks, claude_extensions, claude_stop_reason_to_canonical,
|
||||||
claude_usage_to_canonical, namespace_extension_object, CanonicalResponse,
|
claude_usage_to_canonical, namespace_extension_object, CanonicalResponse,
|
||||||
CanonicalResponseOutput, CanonicalRole,
|
CanonicalResponseOutput, CanonicalRole,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub use super::{chat_spec, cli_spec, resolve_stream_spec, resolve_sync_spec};
|
||||||
@@ -2,13 +2,13 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::response::common::{
|
use crate::formats::shared::response::{
|
||||||
build_generated_tool_call_id, canonicalize_tool_arguments,
|
build_generated_tool_call_id, canonicalize_tool_arguments,
|
||||||
remove_empty_pages_from_tool_arguments,
|
remove_empty_pages_from_tool_arguments,
|
||||||
};
|
};
|
||||||
use crate::response::sse::{encode_json_sse, map_claude_stop_reason};
|
use crate::formats::shared::sse::{encode_json_sse, map_claude_stop_reason};
|
||||||
use crate::response::standard::stream_core::common::*;
|
use crate::formats::shared::stream_core::common::*;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ClaudeProviderToolState {
|
struct ClaudeProviderToolState {
|
||||||
1
crates/aether-ai-formats/src/formats/claude/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/claude/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod messages;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::protocol::{context::FormatContext, registry};
|
use crate::formats::{context::FormatContext, registry};
|
||||||
|
|
||||||
pub fn convert_openai_chat_request_to_claude_request(
|
pub fn convert_openai_chat_request_to_claude_request(
|
||||||
body_json: &Value,
|
body_json: &Value,
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::protocol::{context::FormatContext, registry};
|
use crate::formats::{context::FormatContext, registry};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct OpenAiResponsesResponseUsage {
|
pub struct OpenAiResponsesResponseUsage {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod request;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
use serde_json::{json, Map};
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::formats::openai::embedding::request::mapped_embedding_model;
|
||||||
|
use crate::protocol::canonical::{namespace_extension_object, CanonicalRequest};
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
let embedding = request.embedding.as_ref()?;
|
||||||
|
let items = embedding.input.as_string_items()?;
|
||||||
|
if items.is_empty() || items.iter().any(|value| value.trim().is_empty()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut output = Map::new();
|
||||||
|
output.insert(
|
||||||
|
"model".to_string(),
|
||||||
|
Value::String(mapped_embedding_model(
|
||||||
|
request,
|
||||||
|
ctx.mapped_model_or(request.model.as_str()),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
output.insert(
|
||||||
|
"input".to_string(),
|
||||||
|
Value::Array(
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|text| json!({"type": "text", "text": text}))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let Some(dimensions) = embedding.dimensions {
|
||||||
|
output.insert("dimensions".to_string(), Value::from(dimensions));
|
||||||
|
}
|
||||||
|
output.extend(namespace_extension_object(
|
||||||
|
&embedding.extensions,
|
||||||
|
"doubao",
|
||||||
|
&output,
|
||||||
|
));
|
||||||
|
Some(Value::Object(output))
|
||||||
|
}
|
||||||
1
crates/aether-ai-formats/src/formats/doubao/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/doubao/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod embedding;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod request;
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use serde_json::json;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::formats::openai::embedding::request::mapped_embedding_model;
|
||||||
|
use crate::protocol::canonical::CanonicalRequest;
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
let embedding = request.embedding.as_ref()?;
|
||||||
|
let items = embedding.input.as_string_items()?;
|
||||||
|
if items.is_empty() || items.iter().any(|value| value.trim().is_empty()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let model = mapped_embedding_model(request, ctx.mapped_model_or(request.model.as_str()));
|
||||||
|
if items.len() == 1 {
|
||||||
|
return Some(json!({
|
||||||
|
"model": model,
|
||||||
|
"content": {
|
||||||
|
"parts": [{"text": items[0]}]
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Some(json!({
|
||||||
|
"model": model,
|
||||||
|
"requests": items.into_iter().map(|text| {
|
||||||
|
json!({
|
||||||
|
"model": model,
|
||||||
|
"content": {
|
||||||
|
"parts": [{"text": text}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}))
|
||||||
|
}
|
||||||
1
crates/aether-ai-formats/src/formats/gemini/files/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/gemini/files/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod spec;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::contracts::{GEMINI_CHAT_STREAM_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND};
|
use crate::contracts::{GEMINI_CHAT_STREAM_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND};
|
||||||
use crate::request::standard::family::{
|
use crate::formats::shared::family::{
|
||||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::contracts::{GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND};
|
use crate::contracts::{GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND};
|
||||||
use crate::request::standard::family::{
|
use crate::formats::shared::family::{
|
||||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod chat_spec;
|
||||||
|
pub mod cli_spec;
|
||||||
|
pub mod request;
|
||||||
|
pub mod response;
|
||||||
|
pub mod spec;
|
||||||
|
pub mod stream;
|
||||||
|
|
||||||
|
use crate::formats::shared::family::LocalStandardSpec;
|
||||||
|
|
||||||
|
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||||
|
chat_spec::resolve_sync_spec(plan_kind).or_else(|| cli_spec::resolve_sync_spec(plan_kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||||
|
chat_spec::resolve_stream_spec(plan_kind).or_else(|| cli_spec::resolve_stream_spec(plan_kind))
|
||||||
|
}
|
||||||
@@ -3,6 +3,14 @@ use std::collections::BTreeMap;
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::{
|
||||||
|
context::FormatContext,
|
||||||
|
openai::shared::{
|
||||||
|
map_openai_reasoning_effort_to_gemini_budget,
|
||||||
|
map_thinking_budget_to_openai_reasoning_effort,
|
||||||
|
},
|
||||||
|
shared::model_directives::{gemini_model_uses_thinking_level, ReasoningEffort},
|
||||||
|
},
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
apply_gemini_request_extensions, canonical_extension_object_mut,
|
apply_gemini_request_extensions, canonical_extension_object_mut,
|
||||||
canonical_openai_reasoning_effort, extract_gemini_model_from_path,
|
canonical_openai_reasoning_effort, extract_gemini_model_from_path,
|
||||||
@@ -14,14 +22,6 @@ use crate::{
|
|||||||
CanonicalResponseFormat, CanonicalRole, CanonicalToolChoice, CanonicalToolDefinition,
|
CanonicalResponseFormat, CanonicalRole, CanonicalToolChoice, CanonicalToolDefinition,
|
||||||
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
request::{
|
|
||||||
model_directives::{gemini_model_uses_thinking_level, ReasoningEffort},
|
|
||||||
openai::{
|
|
||||||
map_openai_reasoning_effort_to_gemini_budget,
|
|
||||||
map_thinking_budget_to_openai_reasoning_effort,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, ctx: &FormatContext) -> Option<CanonicalRequest> {
|
pub fn from(body: &Value, ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_extension_object_mut, gemini_extensions, gemini_part_to_canonical_block,
|
canonical_extension_object_mut, gemini_extensions, gemini_part_to_canonical_block,
|
||||||
gemini_stop_reason_to_canonical, gemini_usage_to_canonical, CanonicalContentBlock,
|
gemini_stop_reason_to_canonical, gemini_usage_to_canonical, CanonicalContentBlock,
|
||||||
CanonicalResponse, CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
CanonicalResponse, CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
||||||
CanonicalUsage,
|
CanonicalUsage,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub use super::{chat_spec, cli_spec, resolve_stream_spec, resolve_sync_spec};
|
||||||
@@ -2,10 +2,10 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::response::common::{build_generated_tool_call_id, canonicalize_tool_arguments};
|
use crate::formats::shared::response::{build_generated_tool_call_id, canonicalize_tool_arguments};
|
||||||
use crate::response::sse::encode_json_sse;
|
use crate::formats::shared::sse::encode_json_sse;
|
||||||
use crate::response::standard::stream_core::common::*;
|
use crate::formats::shared::stream_core::common::*;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GeminiProviderToolState {
|
struct GeminiProviderToolState {
|
||||||
4
crates/aether-ai-formats/src/formats/gemini/mod.rs
Normal file
4
crates/aether-ai-formats/src/formats/gemini/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod embedding;
|
||||||
|
pub mod files;
|
||||||
|
pub mod generate_content;
|
||||||
|
pub mod video;
|
||||||
1
crates/aether-ai-formats/src/formats/gemini/video/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/gemini/video/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod spec;
|
||||||
27
crates/aether-ai-formats/src/formats/gemini/video/spec.rs
Normal file
27
crates/aether-ai-formats/src/formats/gemini/video/spec.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::contracts::GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND;
|
||||||
|
use crate::formats::shared::video::{LocalVideoCreateFamily, LocalVideoCreateSpec};
|
||||||
|
|
||||||
|
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalVideoCreateSpec> {
|
||||||
|
match plan_kind {
|
||||||
|
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND => Some(LocalVideoCreateSpec {
|
||||||
|
api_format: "gemini:video",
|
||||||
|
decision_kind: GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
||||||
|
report_kind: "gemini_video_create_sync_finalize",
|
||||||
|
family: LocalVideoCreateFamily::Gemini,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{resolve_sync_spec, LocalVideoCreateFamily};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_gemini_video_create_spec() {
|
||||||
|
let spec = resolve_sync_spec("gemini_video_create_sync").expect("spec");
|
||||||
|
assert_eq!(spec.api_format, "gemini:video");
|
||||||
|
assert_eq!(spec.family, LocalVideoCreateFamily::Gemini);
|
||||||
|
assert_eq!(spec.report_kind, "gemini_video_create_sync_finalize");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
//! Format identity and per-wire-format adapters.
|
//! Format identity and aliases.
|
||||||
//!
|
|
||||||
//! Each child module owns the boundary between one external wire shape and
|
|
||||||
//! the canonical IR. Registry conversion is intentionally constrained to:
|
|
||||||
//! source format -> canonical -> target format.
|
|
||||||
|
|
||||||
use std::{fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
pub mod claude_messages;
|
|
||||||
pub mod gemini_generate_content;
|
|
||||||
pub mod openai_chat;
|
|
||||||
pub mod openai_responses;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum FormatFamily {
|
pub enum FormatFamily {
|
||||||
OpenAi,
|
OpenAi,
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::protocol::canonical::CanonicalRequest;
|
||||||
|
|
||||||
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
|
crate::formats::openai::embedding::request::from_namespace(body, "jina")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
crate::formats::openai::embedding::request::to_openai_like(
|
||||||
|
request,
|
||||||
|
ctx.mapped_model_or(request.model.as_str()),
|
||||||
|
"jina",
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::protocol::canonical::CanonicalEmbeddingResponse;
|
||||||
|
|
||||||
|
pub fn from(body: &Value) -> Option<CanonicalEmbeddingResponse> {
|
||||||
|
crate::formats::openai::embedding::response::from_namespace(body, "jina")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(response: &CanonicalEmbeddingResponse) -> Option<Value> {
|
||||||
|
Some(crate::formats::openai::embedding::response::to_openai_like(
|
||||||
|
response, "jina",
|
||||||
|
))
|
||||||
|
}
|
||||||
2
crates/aether-ai-formats/src/formats/jina/mod.rs
Normal file
2
crates/aether-ai-formats/src/formats/jina/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod embedding;
|
||||||
|
pub mod rerank;
|
||||||
1
crates/aether-ai-formats/src/formats/jina/rerank/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/jina/rerank/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod request;
|
||||||
16
crates/aether-ai-formats/src/formats/jina/rerank/request.rs
Normal file
16
crates/aether-ai-formats/src/formats/jina/rerank/request.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::protocol::canonical::CanonicalRequest;
|
||||||
|
|
||||||
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
|
crate::formats::openai::rerank::request::from_namespace(body, "jina")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
crate::formats::openai::rerank::request::to_openai_like(
|
||||||
|
request,
|
||||||
|
ctx.mapped_model_or(request.model.as_str()),
|
||||||
|
"jina",
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
api_format_alias_matches,
|
api_format_alias_matches,
|
||||||
protocol::formats::{is_openai_responses_compact_format, normalize_api_format_alias},
|
formats::id::{is_openai_responses_compact_format, normalize_api_format_alias},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
18
crates/aether-ai-formats/src/formats/mod.rs
Normal file
18
crates/aether-ai-formats/src/formats/mod.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pub mod claude;
|
||||||
|
pub mod context;
|
||||||
|
pub mod conversion;
|
||||||
|
pub mod doubao;
|
||||||
|
pub mod gemini;
|
||||||
|
pub mod id;
|
||||||
|
pub mod jina;
|
||||||
|
pub mod matrix;
|
||||||
|
pub mod openai;
|
||||||
|
pub mod registry;
|
||||||
|
pub mod shared;
|
||||||
|
|
||||||
|
pub use context::{FormatContext, FormatError};
|
||||||
|
pub use id::{
|
||||||
|
api_format_alias_matches, api_format_storage_aliases, is_openai_responses_compact_format,
|
||||||
|
is_openai_responses_family_format, is_openai_responses_format, normalize_api_format_alias,
|
||||||
|
FormatFamily, FormatId, FormatProfile,
|
||||||
|
};
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
pub mod stream;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_extension_object_mut, canonical_message_to_openai_chat,
|
canonical_extension_object_mut, canonical_message_to_openai_chat,
|
||||||
canonical_response_format_to_openai, canonical_tool_choice_to_openai,
|
canonical_response_format_to_openai, canonical_tool_choice_to_openai,
|
||||||
@@ -11,7 +12,6 @@ use crate::{
|
|||||||
CanonicalInstruction, CanonicalRequest, CanonicalRole, CanonicalThinkingConfig,
|
CanonicalInstruction, CanonicalRequest, CanonicalRole, CanonicalThinkingConfig,
|
||||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_blocks_to_openai_chat_message, canonical_stop_reason_to_openai,
|
canonical_blocks_to_openai_chat_message, canonical_stop_reason_to_openai,
|
||||||
canonical_usage_to_openai, openai_extensions, openai_finish_reason_to_canonical,
|
canonical_usage_to_openai, openai_extensions, openai_finish_reason_to_canonical,
|
||||||
@@ -10,7 +11,6 @@ use crate::{
|
|||||||
CanonicalResponse, CanonicalResponseOutput, CanonicalRole,
|
CanonicalResponse, CanonicalResponseOutput, CanonicalRole,
|
||||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||||
@@ -2,10 +2,10 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::response::common::build_generated_tool_call_id;
|
use crate::formats::shared::response::build_generated_tool_call_id;
|
||||||
use crate::response::sse::{encode_done_sse, encode_json_sse};
|
use crate::formats::shared::sse::{encode_done_sse, encode_json_sse};
|
||||||
use crate::response::standard::stream_core::common::*;
|
use crate::formats::shared::stream_core::common::*;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct OpenAIChatProviderToolState {
|
struct OpenAIChatProviderToolState {
|
||||||
150
crates/aether-ai-formats/src/formats/openai/embedding/request.rs
Normal file
150
crates/aether-ai-formats/src/formats/openai/embedding/request.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
use serde_json::Map;
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::protocol::canonical::{
|
||||||
|
namespace_extension_object, CanonicalEmbeddingInput, CanonicalEmbeddingRequest,
|
||||||
|
CanonicalRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
|
from_namespace(body, "openai")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
to_openai_like(
|
||||||
|
request,
|
||||||
|
ctx.mapped_model_or(request.model.as_str()),
|
||||||
|
"openai",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_namespace(body_json: &Value, namespace: &str) -> Option<CanonicalRequest> {
|
||||||
|
let request = body_json.as_object()?;
|
||||||
|
let model = request
|
||||||
|
.get("model")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())?
|
||||||
|
.to_string();
|
||||||
|
let input =
|
||||||
|
serde_json::from_value::<CanonicalEmbeddingInput>(request.get("input")?.clone()).ok()?;
|
||||||
|
if input.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let embedding = CanonicalEmbeddingRequest {
|
||||||
|
input,
|
||||||
|
encoding_format: request
|
||||||
|
.get("encoding_format")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(ToOwned::to_owned),
|
||||||
|
dimensions: request.get("dimensions").and_then(Value::as_u64),
|
||||||
|
task: request
|
||||||
|
.get("task")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(ToOwned::to_owned),
|
||||||
|
user: request
|
||||||
|
.get("user")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(ToOwned::to_owned),
|
||||||
|
extensions: namespace_extensions(
|
||||||
|
namespace,
|
||||||
|
request,
|
||||||
|
&[
|
||||||
|
"model",
|
||||||
|
"input",
|
||||||
|
"encoding_format",
|
||||||
|
"dimensions",
|
||||||
|
"task",
|
||||||
|
"user",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(CanonicalRequest {
|
||||||
|
model,
|
||||||
|
embedding: Some(embedding),
|
||||||
|
..CanonicalRequest::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_openai_like(
|
||||||
|
canonical: &CanonicalRequest,
|
||||||
|
mapped_model: &str,
|
||||||
|
namespace: &str,
|
||||||
|
default_task: bool,
|
||||||
|
) -> Option<Value> {
|
||||||
|
let embedding = canonical.embedding.as_ref()?;
|
||||||
|
if embedding.input.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut output = Map::new();
|
||||||
|
output.insert(
|
||||||
|
"model".to_string(),
|
||||||
|
Value::String(mapped_embedding_model(canonical, mapped_model)),
|
||||||
|
);
|
||||||
|
output.insert(
|
||||||
|
"input".to_string(),
|
||||||
|
serde_json::to_value(&embedding.input).ok()?,
|
||||||
|
);
|
||||||
|
if let Some(value) = &embedding.encoding_format {
|
||||||
|
output.insert("encoding_format".to_string(), Value::String(value.clone()));
|
||||||
|
}
|
||||||
|
if let Some(value) = embedding.dimensions {
|
||||||
|
output.insert("dimensions".to_string(), Value::from(value));
|
||||||
|
}
|
||||||
|
if let Some(value) = &embedding.user {
|
||||||
|
output.insert("user".to_string(), Value::String(value.clone()));
|
||||||
|
}
|
||||||
|
if let Some(task) = embedding
|
||||||
|
.task
|
||||||
|
.as_ref()
|
||||||
|
.filter(|value| !value.trim().is_empty())
|
||||||
|
{
|
||||||
|
output.insert("task".to_string(), Value::String(task.clone()));
|
||||||
|
} else if default_task {
|
||||||
|
output.insert(
|
||||||
|
"task".to_string(),
|
||||||
|
Value::String("text-matching".to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
output.extend(namespace_extension_object(
|
||||||
|
&embedding.extensions,
|
||||||
|
namespace,
|
||||||
|
&output,
|
||||||
|
));
|
||||||
|
Some(Value::Object(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mapped_embedding_model(canonical: &CanonicalRequest, mapped_model: &str) -> String {
|
||||||
|
let mapped_model = mapped_model.trim();
|
||||||
|
if mapped_model.is_empty() {
|
||||||
|
canonical.model.clone()
|
||||||
|
} else {
|
||||||
|
mapped_model.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn namespace_extensions(
|
||||||
|
namespace: &str,
|
||||||
|
object: &Map<String, Value>,
|
||||||
|
handled_keys: &[&str],
|
||||||
|
) -> BTreeMap<String, Value> {
|
||||||
|
let handled = handled_keys
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect::<std::collections::BTreeSet<_>>();
|
||||||
|
let raw = object
|
||||||
|
.iter()
|
||||||
|
.filter(|(key, _)| !handled.contains(key.as_str()))
|
||||||
|
.map(|(key, value)| (key.clone(), value.clone()))
|
||||||
|
.collect::<Map<String, Value>>();
|
||||||
|
if raw.is_empty() {
|
||||||
|
BTreeMap::new()
|
||||||
|
} else {
|
||||||
|
BTreeMap::from([(namespace.to_string(), Value::Object(raw))])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
use serde_json::{json, Map};
|
||||||
|
|
||||||
|
use crate::formats::openai::embedding::request::namespace_extensions;
|
||||||
|
use crate::protocol::canonical::{
|
||||||
|
canonical_usage_to_openai, namespace_extension_object, openai_usage_to_canonical,
|
||||||
|
CanonicalEmbedding, CanonicalEmbeddingResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn from(body: &Value) -> Option<CanonicalEmbeddingResponse> {
|
||||||
|
from_namespace(body, "openai")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(response: &CanonicalEmbeddingResponse) -> Option<Value> {
|
||||||
|
Some(to_openai_like(response, "openai"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_namespace(
|
||||||
|
body_json: &Value,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Option<CanonicalEmbeddingResponse> {
|
||||||
|
let body = body_json.as_object()?;
|
||||||
|
if body.contains_key("error") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let data = body.get("data")?.as_array()?;
|
||||||
|
let mut embeddings = Vec::new();
|
||||||
|
for (fallback_index, item) in data.iter().enumerate() {
|
||||||
|
let item_object = item.as_object()?;
|
||||||
|
let values = item_object.get("embedding")?.as_array()?;
|
||||||
|
let embedding = values
|
||||||
|
.iter()
|
||||||
|
.map(Value::as_f64)
|
||||||
|
.collect::<Option<Vec<_>>>()?;
|
||||||
|
embeddings.push(CanonicalEmbedding {
|
||||||
|
index: item_object
|
||||||
|
.get("index")
|
||||||
|
.and_then(Value::as_u64)
|
||||||
|
.and_then(|value| usize::try_from(value).ok())
|
||||||
|
.unwrap_or(fallback_index),
|
||||||
|
embedding,
|
||||||
|
extensions: namespace_extensions(
|
||||||
|
namespace,
|
||||||
|
item_object,
|
||||||
|
&["object", "index", "embedding"],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(CanonicalEmbeddingResponse {
|
||||||
|
id: body
|
||||||
|
.get("id")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.unwrap_or("embd-unknown")
|
||||||
|
.to_string(),
|
||||||
|
model: body
|
||||||
|
.get("model")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.unwrap_or("unknown")
|
||||||
|
.to_string(),
|
||||||
|
embeddings,
|
||||||
|
usage: openai_usage_to_canonical(body.get("usage")),
|
||||||
|
extensions: namespace_extensions(
|
||||||
|
namespace,
|
||||||
|
body,
|
||||||
|
&["id", "object", "model", "data", "usage"],
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_openai_like(canonical: &CanonicalEmbeddingResponse, namespace: &str) -> Value {
|
||||||
|
let mut response = Map::new();
|
||||||
|
response.insert("object".to_string(), Value::String("list".to_string()));
|
||||||
|
if !canonical.model.trim().is_empty() && canonical.model != "unknown" {
|
||||||
|
response.insert("model".to_string(), Value::String(canonical.model.clone()));
|
||||||
|
}
|
||||||
|
response.insert(
|
||||||
|
"data".to_string(),
|
||||||
|
Value::Array(
|
||||||
|
canonical
|
||||||
|
.embeddings
|
||||||
|
.iter()
|
||||||
|
.map(|embedding| {
|
||||||
|
let mut item = Map::new();
|
||||||
|
item.insert("object".to_string(), Value::String("embedding".to_string()));
|
||||||
|
item.insert("index".to_string(), Value::from(embedding.index as u64));
|
||||||
|
item.insert("embedding".to_string(), json!(embedding.embedding));
|
||||||
|
item.extend(namespace_extension_object(
|
||||||
|
&embedding.extensions,
|
||||||
|
namespace,
|
||||||
|
&item,
|
||||||
|
));
|
||||||
|
Value::Object(item)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let Some(usage) = &canonical.usage {
|
||||||
|
response.insert("usage".to_string(), canonical_usage_to_openai(usage));
|
||||||
|
}
|
||||||
|
response.extend(namespace_extension_object(
|
||||||
|
&canonical.extensions,
|
||||||
|
namespace,
|
||||||
|
&response,
|
||||||
|
));
|
||||||
|
Value::Object(response)
|
||||||
|
}
|
||||||
3
crates/aether-ai-formats/src/formats/openai/image/mod.rs
Normal file
3
crates/aether-ai-formats/src/formats/openai/image/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod request;
|
||||||
|
pub mod spec;
|
||||||
|
pub mod stream;
|
||||||
@@ -3,43 +3,10 @@ use std::collections::BTreeMap;
|
|||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
use serde_json::{json, Map, Number, Value};
|
use serde_json::{json, Map, Number, Value};
|
||||||
|
|
||||||
use crate::contracts::{OPENAI_IMAGE_STREAM_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND};
|
use crate::formats::openai::responses::codex::{
|
||||||
use crate::request::standard::{
|
|
||||||
CODEX_OPENAI_IMAGE_DEFAULT_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL,
|
CODEX_OPENAI_IMAGE_DEFAULT_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct LocalOpenAiImageSpec {
|
|
||||||
pub api_format: &'static str,
|
|
||||||
pub decision_kind: &'static str,
|
|
||||||
pub report_kind: &'static str,
|
|
||||||
pub require_streaming: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
require_streaming: false,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalOpenAiImageSpec> {
|
|
||||||
match plan_kind {
|
|
||||||
OPENAI_IMAGE_STREAM_PLAN_KIND => Some(LocalOpenAiImageSpec {
|
|
||||||
api_format: "openai:image",
|
|
||||||
decision_kind: OPENAI_IMAGE_STREAM_PLAN_KIND,
|
|
||||||
report_kind: "openai_image_stream_success",
|
|
||||||
require_streaming: true,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum OpenAiImageOperation {
|
pub enum OpenAiImageOperation {
|
||||||
Generate,
|
Generate,
|
||||||
@@ -1134,10 +1101,10 @@ mod tests {
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
build_chatgpt_web_image_request_body, build_openai_image_provider_request_body,
|
build_chatgpt_web_image_request_body, build_openai_image_provider_request_body,
|
||||||
is_openai_image_stream_request, normalize_openai_image_request, resolve_stream_spec,
|
is_openai_image_stream_request, normalize_openai_image_request, OpenAiImageOperation,
|
||||||
resolve_sync_spec, OpenAiImageOperation,
|
|
||||||
};
|
};
|
||||||
use crate::request::standard::{
|
use crate::formats::openai::image::spec::{resolve_stream_spec, resolve_sync_spec};
|
||||||
|
use crate::formats::openai::responses::codex::{
|
||||||
apply_codex_openai_responses_special_body_edits, CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
apply_codex_openai_responses_special_body_edits, CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
54
crates/aether-ai-formats/src/formats/openai/image/spec.rs
Normal file
54
crates/aether-ai-formats/src/formats/openai/image/spec.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use crate::contracts::{OPENAI_IMAGE_STREAM_PLAN_KIND, 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 require_streaming: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
require_streaming: false,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalOpenAiImageSpec> {
|
||||||
|
match plan_kind {
|
||||||
|
OPENAI_IMAGE_STREAM_PLAN_KIND => Some(LocalOpenAiImageSpec {
|
||||||
|
api_format: "openai:image",
|
||||||
|
decision_kind: OPENAI_IMAGE_STREAM_PLAN_KIND,
|
||||||
|
report_kind: "openai_image_stream_success",
|
||||||
|
require_streaming: true,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{resolve_stream_spec, 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");
|
||||||
|
assert!(!spec.require_streaming);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_openai_image_stream_spec() {
|
||||||
|
let spec = resolve_stream_spec("openai_image_stream").expect("spec");
|
||||||
|
assert_eq!(spec.api_format, "openai:image");
|
||||||
|
assert_eq!(spec.report_kind, "openai_image_stream_success");
|
||||||
|
assert!(spec.require_streaming);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@ use base64::Engine as _;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::contracts::OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND;
|
use crate::contracts::OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND;
|
||||||
use crate::request::standard::CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT;
|
use crate::formats::openai::responses::codex::CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT;
|
||||||
use crate::response::sse::encode_json_sse;
|
use crate::formats::shared::sse::encode_json_sse;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct OpenAiImageStreamState {
|
pub struct OpenAiImageStreamState {
|
||||||
7
crates/aether-ai-formats/src/formats/openai/mod.rs
Normal file
7
crates/aether-ai-formats/src/formats/openai/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod chat;
|
||||||
|
pub mod embedding;
|
||||||
|
pub mod image;
|
||||||
|
pub mod rerank;
|
||||||
|
pub mod responses;
|
||||||
|
pub mod shared;
|
||||||
|
pub mod video;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod request;
|
||||||
113
crates/aether-ai-formats/src/formats/openai/rerank/request.rs
Normal file
113
crates/aether-ai-formats/src/formats/openai/rerank/request.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
use serde_json::Map;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::context::FormatContext;
|
||||||
|
use crate::formats::openai::embedding::request::namespace_extensions;
|
||||||
|
use crate::protocol::canonical::{
|
||||||
|
namespace_extension_object, CanonicalRequest, CanonicalRerankRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
|
from_namespace(body, "openai")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to(request: &CanonicalRequest, ctx: &FormatContext) -> Option<Value> {
|
||||||
|
to_openai_like(
|
||||||
|
request,
|
||||||
|
ctx.mapped_model_or(request.model.as_str()),
|
||||||
|
"openai",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_namespace(body_json: &Value, namespace: &str) -> Option<CanonicalRequest> {
|
||||||
|
let request = body_json.as_object()?;
|
||||||
|
let model = request
|
||||||
|
.get("model")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())?
|
||||||
|
.to_string();
|
||||||
|
let query = request
|
||||||
|
.get("query")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())?
|
||||||
|
.to_string();
|
||||||
|
let documents = request.get("documents").and_then(Value::as_array)?.to_vec();
|
||||||
|
let rerank = CanonicalRerankRequest {
|
||||||
|
query,
|
||||||
|
documents,
|
||||||
|
top_n: request
|
||||||
|
.get("top_n")
|
||||||
|
.or_else(|| request.get("topN"))
|
||||||
|
.and_then(Value::as_u64),
|
||||||
|
return_documents: request
|
||||||
|
.get("return_documents")
|
||||||
|
.or_else(|| request.get("returnDocuments"))
|
||||||
|
.and_then(Value::as_bool),
|
||||||
|
extensions: namespace_extensions(
|
||||||
|
namespace,
|
||||||
|
request,
|
||||||
|
&[
|
||||||
|
"model",
|
||||||
|
"query",
|
||||||
|
"documents",
|
||||||
|
"top_n",
|
||||||
|
"topN",
|
||||||
|
"return_documents",
|
||||||
|
"returnDocuments",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if rerank.is_empty() || rerank.top_n == Some(0) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(CanonicalRequest {
|
||||||
|
model,
|
||||||
|
rerank: Some(rerank),
|
||||||
|
..CanonicalRequest::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_openai_like(
|
||||||
|
canonical: &CanonicalRequest,
|
||||||
|
mapped_model: &str,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Option<Value> {
|
||||||
|
let rerank = canonical.rerank.as_ref()?;
|
||||||
|
if rerank.is_empty() || rerank.top_n == Some(0) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut output = Map::new();
|
||||||
|
output.insert(
|
||||||
|
"model".to_string(),
|
||||||
|
Value::String(mapped_rerank_model(canonical, mapped_model)),
|
||||||
|
);
|
||||||
|
output.insert("query".to_string(), Value::String(rerank.query.clone()));
|
||||||
|
output.insert(
|
||||||
|
"documents".to_string(),
|
||||||
|
Value::Array(rerank.documents.clone()),
|
||||||
|
);
|
||||||
|
if let Some(value) = rerank.top_n {
|
||||||
|
output.insert("top_n".to_string(), Value::from(value));
|
||||||
|
}
|
||||||
|
if let Some(value) = rerank.return_documents {
|
||||||
|
output.insert("return_documents".to_string(), Value::Bool(value));
|
||||||
|
}
|
||||||
|
output.extend(namespace_extension_object(
|
||||||
|
&rerank.extensions,
|
||||||
|
namespace,
|
||||||
|
&output,
|
||||||
|
));
|
||||||
|
Some(Value::Object(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mapped_rerank_model(canonical: &CanonicalRequest, mapped_model: &str) -> String {
|
||||||
|
let mapped_model = mapped_model.trim();
|
||||||
|
if mapped_model.is_empty() {
|
||||||
|
canonical.model.clone()
|
||||||
|
} else {
|
||||||
|
mapped_model.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod codex;
|
||||||
|
pub mod request;
|
||||||
|
pub mod response;
|
||||||
|
pub mod spec;
|
||||||
|
pub mod stream;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
|
formats::openai::shared::map_thinking_budget_to_openai_reasoning_effort,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_response_format_to_openai, canonicalize_tool_arguments, media_data_or_url,
|
canonical_response_format_to_openai, canonicalize_tool_arguments, media_data_or_url,
|
||||||
namespace_extension_object, openai_content_text, openai_extensions,
|
namespace_extension_object, openai_content_text, openai_extensions,
|
||||||
@@ -11,8 +13,6 @@ use crate::{
|
|||||||
CanonicalThinkingConfig, CanonicalToolChoice, CanonicalToolDefinition,
|
CanonicalThinkingConfig, CanonicalToolChoice, CanonicalToolDefinition,
|
||||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
request::openai::map_thinking_budget_to_openai_reasoning_effort,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
formats::context::FormatContext,
|
||||||
protocol::canonical::{
|
protocol::canonical::{
|
||||||
canonical_content_block_to_openai_responses_part,
|
canonical_content_block_to_openai_responses_part,
|
||||||
canonical_usage_to_openai_responses_usage, canonicalize_tool_arguments,
|
canonical_usage_to_openai_responses_usage, canonicalize_tool_arguments,
|
||||||
@@ -12,7 +13,6 @@ use crate::{
|
|||||||
CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
||||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
},
|
},
|
||||||
protocol::context::FormatContext,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub use crate::formats::openai::chat::stream::{
|
||||||
|
OpenAIResponsesClientEmitter, OpenAIResponsesProviderState,
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
use super::model_directives::ReasoningEffort;
|
use crate::formats::shared::model_directives::ReasoningEffort;
|
||||||
|
|
||||||
pub fn parse_openai_stop_sequences(stop: Option<&Value>) -> Option<Vec<Value>> {
|
pub fn parse_openai_stop_sequences(stop: Option<&Value>) -> Option<Vec<Value>> {
|
||||||
match stop {
|
match stop {
|
||||||
1
crates/aether-ai-formats/src/formats/openai/video/mod.rs
Normal file
1
crates/aether-ai-formats/src/formats/openai/video/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod spec;
|
||||||
27
crates/aether-ai-formats/src/formats/openai/video/spec.rs
Normal file
27
crates/aether-ai-formats/src/formats/openai/video/spec.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::contracts::OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND;
|
||||||
|
use crate::formats::shared::video::{LocalVideoCreateFamily, LocalVideoCreateSpec};
|
||||||
|
|
||||||
|
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalVideoCreateSpec> {
|
||||||
|
match plan_kind {
|
||||||
|
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND => Some(LocalVideoCreateSpec {
|
||||||
|
api_format: "openai:video",
|
||||||
|
decision_kind: OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
||||||
|
report_kind: "openai_video_create_sync_finalize",
|
||||||
|
family: LocalVideoCreateFamily::OpenAi,
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{resolve_sync_spec, LocalVideoCreateFamily};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_openai_video_create_spec() {
|
||||||
|
let spec = resolve_sync_spec("openai_video_create_sync").expect("spec");
|
||||||
|
assert_eq!(spec.api_format, "openai:video");
|
||||||
|
assert_eq!(spec.family, LocalVideoCreateFamily::OpenAi);
|
||||||
|
assert_eq!(spec.report_kind, "openai_video_create_sync_finalize");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::formats::{
|
||||||
protocol::canonical::{
|
claude::messages as claude_messages,
|
||||||
canonical_to_embedding_request, canonical_to_rerank_request,
|
doubao,
|
||||||
from_embedding_to_canonical_request, from_rerank_to_canonical_request, CanonicalRequest,
|
gemini::{self, generate_content as gemini_generate_content},
|
||||||
CanonicalResponse,
|
id::FormatId,
|
||||||
},
|
jina,
|
||||||
protocol::formats::{
|
openai::{self, chat as openai_chat, responses as openai_responses},
|
||||||
claude_messages, gemini_generate_content, openai_chat, openai_responses, FormatId,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
use crate::protocol::canonical::{CanonicalRequest, CanonicalResponse};
|
||||||
|
|
||||||
pub use crate::protocol::context::{FormatContext, FormatError};
|
pub use crate::formats::context::{FormatContext, FormatError};
|
||||||
|
|
||||||
pub fn parse_request(
|
pub fn parse_request(
|
||||||
source_format: &str,
|
source_format: &str,
|
||||||
@@ -26,10 +25,10 @@ pub fn parse_request(
|
|||||||
}
|
}
|
||||||
FormatId::ClaudeMessages => claude_messages::request::from(body, ctx),
|
FormatId::ClaudeMessages => claude_messages::request::from(body, ctx),
|
||||||
FormatId::GeminiGenerateContent => gemini_generate_content::request::from(body, ctx),
|
FormatId::GeminiGenerateContent => gemini_generate_content::request::from(body, ctx),
|
||||||
FormatId::OpenAiEmbedding => from_embedding_to_canonical_request(body, "openai"),
|
FormatId::OpenAiEmbedding => openai::embedding::request::from(body, ctx),
|
||||||
FormatId::JinaEmbedding => from_embedding_to_canonical_request(body, "jina"),
|
FormatId::JinaEmbedding => jina::embedding::request::from(body, ctx),
|
||||||
FormatId::OpenAiRerank => from_rerank_to_canonical_request(body, "openai"),
|
FormatId::OpenAiRerank => openai::rerank::request::from(body, ctx),
|
||||||
FormatId::JinaRerank => from_rerank_to_canonical_request(body, "jina"),
|
FormatId::JinaRerank => jina::rerank::request::from(body, ctx),
|
||||||
FormatId::GeminiEmbedding | FormatId::DoubaoEmbedding => None,
|
FormatId::GeminiEmbedding | FormatId::DoubaoEmbedding => None,
|
||||||
}
|
}
|
||||||
.ok_or_else(|| FormatError::RequestParseFailed {
|
.ok_or_else(|| FormatError::RequestParseFailed {
|
||||||
@@ -57,36 +56,12 @@ pub fn emit_request(
|
|||||||
FormatId::OpenAiResponsesCompact => openai_responses::request::to_compact(&request, ctx),
|
FormatId::OpenAiResponsesCompact => openai_responses::request::to_compact(&request, ctx),
|
||||||
FormatId::ClaudeMessages => claude_messages::request::to(&request, ctx),
|
FormatId::ClaudeMessages => claude_messages::request::to(&request, ctx),
|
||||||
FormatId::GeminiGenerateContent => gemini_generate_content::request::to(&request, ctx),
|
FormatId::GeminiGenerateContent => gemini_generate_content::request::to(&request, ctx),
|
||||||
FormatId::OpenAiEmbedding => canonical_to_embedding_request(
|
FormatId::OpenAiEmbedding => openai::embedding::request::to(&request, ctx),
|
||||||
&request,
|
FormatId::JinaEmbedding => jina::embedding::request::to(&request, ctx),
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
FormatId::OpenAiRerank => openai::rerank::request::to(&request, ctx),
|
||||||
"openai",
|
FormatId::JinaRerank => jina::rerank::request::to(&request, ctx),
|
||||||
),
|
FormatId::GeminiEmbedding => gemini::embedding::request::to(&request, ctx),
|
||||||
FormatId::JinaEmbedding => canonical_to_embedding_request(
|
FormatId::DoubaoEmbedding => doubao::embedding::request::to(&request, ctx),
|
||||||
&request,
|
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
|
||||||
"jina",
|
|
||||||
),
|
|
||||||
FormatId::OpenAiRerank => canonical_to_rerank_request(
|
|
||||||
&request,
|
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
|
||||||
"openai",
|
|
||||||
),
|
|
||||||
FormatId::JinaRerank => canonical_to_rerank_request(
|
|
||||||
&request,
|
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
|
||||||
"jina",
|
|
||||||
),
|
|
||||||
FormatId::GeminiEmbedding => canonical_to_embedding_request(
|
|
||||||
&request,
|
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
|
||||||
"gemini",
|
|
||||||
),
|
|
||||||
FormatId::DoubaoEmbedding => canonical_to_embedding_request(
|
|
||||||
&request,
|
|
||||||
ctx.mapped_model_or(request.model.as_str()),
|
|
||||||
"doubao",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
.ok_or_else(|| FormatError::RequestEmitFailed {
|
.ok_or_else(|| FormatError::RequestEmitFailed {
|
||||||
format: target.as_str().to_string(),
|
format: target.as_str().to_string(),
|
||||||
@@ -197,7 +172,7 @@ mod tests {
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use super::{convert_request, FormatContext};
|
use super::{convert_request, FormatContext};
|
||||||
use crate::protocol::formats::FormatId;
|
use crate::formats::id::FormatId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn openai_cli_alias_is_not_a_primary_format() {
|
fn openai_cli_alias_is_not_a_primary_format() {
|
||||||
@@ -347,7 +322,7 @@ mod tests {
|
|||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
!implementation.contains(forbidden),
|
!implementation.contains(forbidden),
|
||||||
"registry should dispatch through protocol::formats::<format> adapters, found {forbidden}"
|
"registry should dispatch through formats::<provider>::<surface> adapters, found {forbidden}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,29 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub mod error_body;
|
||||||
|
pub mod family;
|
||||||
|
pub mod model_directives;
|
||||||
|
pub mod passthrough;
|
||||||
|
pub mod request;
|
||||||
|
pub mod request_matrix;
|
||||||
|
pub mod response;
|
||||||
|
pub mod routing;
|
||||||
|
pub mod sse;
|
||||||
|
pub mod standard_matrix;
|
||||||
|
pub mod standard_normalize;
|
||||||
|
pub mod stream_core;
|
||||||
|
pub mod stream_rewrite;
|
||||||
|
pub mod sync_products;
|
||||||
|
pub mod sync_to_stream;
|
||||||
|
pub mod video;
|
||||||
|
|
||||||
pub use self::sse::{encode_done_sse, encode_json_sse, map_claude_stop_reason};
|
pub use self::sse::{encode_done_sse, encode_json_sse, map_claude_stop_reason};
|
||||||
pub use self::standard::stream_core::CanonicalStreamEvent;
|
pub use self::stream_core::{CanonicalStreamEvent, CanonicalStreamFrame};
|
||||||
pub use self::standard::stream_core::CanonicalStreamFrame;
|
|
||||||
pub use self::stream_rewrite::{
|
pub use self::stream_rewrite::{
|
||||||
maybe_build_ai_surface_stream_rewriter, resolve_finalize_stream_rewrite_mode,
|
maybe_build_ai_surface_stream_rewriter, resolve_finalize_stream_rewrite_mode,
|
||||||
AiSurfaceStreamRewriter, FinalizeStreamRewriteMode,
|
AiSurfaceStreamRewriter, FinalizeStreamRewriteMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod common;
|
|
||||||
pub mod error_body;
|
|
||||||
pub mod openai_image_stream;
|
|
||||||
pub mod sse;
|
|
||||||
pub mod standard;
|
|
||||||
pub mod stream_rewrite;
|
|
||||||
pub mod sync_products;
|
|
||||||
pub mod sync_to_stream;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AiSurfaceFinalizeError(pub String);
|
pub struct AiSurfaceFinalizeError(pub String);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
pub use crate::request::standard::matrix::{
|
pub use crate::formats::shared::standard_matrix::{
|
||||||
build_standard_request_body_from_canonical,
|
build_standard_request_body_from_canonical,
|
||||||
build_standard_request_body_from_canonical_with_model_directives,
|
build_standard_request_body_from_canonical_with_model_directives,
|
||||||
};
|
};
|
||||||
@@ -14,7 +14,7 @@ use crate::contracts::{
|
|||||||
OPENAI_VIDEO_CONTENT_PLAN_KIND, OPENAI_VIDEO_CREATE_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,
|
OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND, OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||||
};
|
};
|
||||||
use crate::request::specialized::image::is_openai_image_stream_request;
|
use crate::formats::openai::image::request::is_openai_image_stream_request;
|
||||||
|
|
||||||
pub fn resolve_execution_runtime_stream_plan_kind(
|
pub fn resolve_execution_runtime_stream_plan_kind(
|
||||||
route_class: Option<&str>,
|
route_class: Option<&str>,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
pub fn map_claude_stop_reason(
|
pub fn map_claude_stop_reason(
|
||||||
stop_reason: Option<&str>,
|
stop_reason: Option<&str>,
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use aether_ai_formats::protocol::conversion::request::{
|
use aether_ai_formats::formats::conversion::request::{
|
||||||
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
||||||
convert_openai_chat_request_to_openai_responses_request,
|
convert_openai_chat_request_to_openai_responses_request,
|
||||||
normalize_claude_request_to_openai_chat_request,
|
normalize_claude_request_to_openai_chat_request,
|
||||||
normalize_gemini_request_to_openai_chat_request,
|
normalize_gemini_request_to_openai_chat_request,
|
||||||
normalize_openai_responses_request_to_openai_chat_request,
|
normalize_openai_responses_request_to_openai_chat_request,
|
||||||
};
|
};
|
||||||
use aether_ai_formats::protocol::registry::{convert_request, FormatContext};
|
use aether_ai_formats::formats::registry::{convert_request, FormatContext};
|
||||||
use aether_ai_formats::provider_compat::proxy::rules::apply_local_body_rules_with_request_headers;
|
use aether_ai_formats::provider_compat::proxy::rules::apply_local_body_rules_with_request_headers;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::request::model_directives::apply_model_directive_overrides_from_request;
|
use crate::formats::shared::model_directives::apply_model_directive_overrides_from_request;
|
||||||
|
|
||||||
use super::{
|
use crate::formats::openai::responses::codex::{
|
||||||
|
apply_codex_openai_responses_special_body_edits,
|
||||||
apply_openai_responses_compact_special_body_edits,
|
apply_openai_responses_compact_special_body_edits,
|
||||||
codex::apply_codex_openai_responses_special_body_edits,
|
|
||||||
normalize::build_local_openai_chat_request_body_with_model_directives,
|
|
||||||
};
|
};
|
||||||
|
use crate::formats::shared::standard_normalize::build_local_openai_chat_request_body_with_model_directives;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn build_standard_request_body(
|
pub fn build_standard_request_body(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use aether_ai_formats::protocol::conversion::request::{
|
use aether_ai_formats::formats::conversion::request::{
|
||||||
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,
|
||||||
convert_openai_chat_request_to_openai_responses_request,
|
convert_openai_chat_request_to_openai_responses_request,
|
||||||
normalize_openai_responses_request_to_openai_chat_request,
|
normalize_openai_responses_request_to_openai_chat_request,
|
||||||
@@ -6,7 +6,7 @@ use aether_ai_formats::protocol::conversion::request::{
|
|||||||
use aether_ai_formats::{request_conversion_kind, RequestConversionKind};
|
use aether_ai_formats::{request_conversion_kind, RequestConversionKind};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::request::model_directives::apply_model_directive_overrides_from_request;
|
use crate::formats::shared::model_directives::apply_model_directive_overrides_from_request;
|
||||||
|
|
||||||
pub fn build_local_openai_chat_request_body(
|
pub fn build_local_openai_chat_request_body(
|
||||||
body_json: &Value,
|
body_json: &Value,
|
||||||
@@ -2,20 +2,20 @@ use aether_ai_formats::FormatId;
|
|||||||
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::response::error_body::{
|
use crate::formats::claude::messages::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
||||||
build_core_error_body_for_client_format, LocalCoreSyncErrorKind,
|
use crate::formats::gemini::generate_content::stream::{GeminiClientEmitter, GeminiProviderState};
|
||||||
};
|
use crate::formats::openai::chat::stream::{
|
||||||
use crate::response::sse::encode_json_sse;
|
|
||||||
use crate::response::standard::claude::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
|
||||||
use crate::response::standard::gemini::stream::{GeminiClientEmitter, GeminiProviderState};
|
|
||||||
use crate::response::standard::openai::stream::{
|
|
||||||
OpenAIChatClientEmitter, OpenAIChatProviderState, OpenAIResponsesClientEmitter,
|
OpenAIChatClientEmitter, OpenAIChatProviderState, OpenAIResponsesClientEmitter,
|
||||||
OpenAIResponsesProviderState,
|
OpenAIResponsesProviderState,
|
||||||
};
|
};
|
||||||
use crate::response::standard::stream_core::common::{
|
use crate::formats::shared::error_body::{
|
||||||
|
build_core_error_body_for_client_format, LocalCoreSyncErrorKind,
|
||||||
|
};
|
||||||
|
use crate::formats::shared::sse::encode_json_sse;
|
||||||
|
use crate::formats::shared::stream_core::common::{
|
||||||
decode_json_data_line, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalUsage,
|
decode_json_data_line, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalUsage,
|
||||||
};
|
};
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StreamingStandardFormatMatrix {
|
pub struct StreamingStandardFormatMatrix {
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::openai::image::stream::OpenAiImageStreamState;
|
||||||
|
use crate::formats::shared::stream_core::StreamingStandardFormatMatrix;
|
||||||
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
||||||
use crate::provider_compat::private_envelope::transform_provider_private_stream_line;
|
use crate::provider_compat::private_envelope::transform_provider_private_stream_line;
|
||||||
use crate::provider_compat::surfaces::{
|
use crate::provider_compat::surfaces::{
|
||||||
provider_adaptation_should_unwrap_stream_envelope, KIRO_ENVELOPE_NAME,
|
provider_adaptation_should_unwrap_stream_envelope, KIRO_ENVELOPE_NAME,
|
||||||
};
|
};
|
||||||
use crate::response::openai_image_stream::OpenAiImageStreamState;
|
|
||||||
use crate::response::standard::stream_core::StreamingStandardFormatMatrix;
|
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum FinalizeStreamRewriteMode {
|
pub enum FinalizeStreamRewriteMode {
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use aether_ai_formats::protocol::conversion::response::{
|
use aether_ai_formats::formats::conversion::response::{
|
||||||
convert_claude_response_to_openai_responses, convert_gemini_chat_response_to_openai_chat,
|
convert_claude_response_to_openai_responses, convert_gemini_chat_response_to_openai_chat,
|
||||||
convert_openai_chat_response_to_claude_chat, convert_openai_chat_response_to_gemini_chat,
|
convert_openai_chat_response_to_claude_chat, convert_openai_chat_response_to_gemini_chat,
|
||||||
convert_openai_chat_response_to_openai_responses,
|
convert_openai_chat_response_to_openai_responses,
|
||||||
convert_openai_responses_response_to_openai_chat,
|
convert_openai_responses_response_to_openai_chat,
|
||||||
};
|
};
|
||||||
use aether_ai_formats::protocol::registry::{convert_response, FormatContext};
|
use aether_ai_formats::formats::registry::{convert_response, FormatContext};
|
||||||
use aether_ai_formats::{
|
use aether_ai_formats::{
|
||||||
canonical_to_claude_response, canonical_to_gemini_response, canonical_to_openai_chat_response,
|
canonical_to_claude_response, canonical_to_gemini_response, canonical_to_openai_chat_response,
|
||||||
canonical_to_openai_responses_compact_response, canonical_to_openai_responses_response,
|
canonical_to_openai_responses_compact_response, canonical_to_openai_responses_response,
|
||||||
@@ -18,9 +18,9 @@ use aether_ai_formats::{
|
|||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use super::AiSurfaceFinalizeError;
|
use super::AiSurfaceFinalizeError;
|
||||||
use crate::response::common::remove_empty_pages_from_tool_arguments;
|
use crate::formats::gemini::generate_content::stream::GeminiProviderState;
|
||||||
use crate::response::standard::gemini::stream::GeminiProviderState;
|
use crate::formats::shared::response::remove_empty_pages_from_tool_arguments;
|
||||||
use crate::response::standard::stream_core::common::{
|
use crate::formats::shared::stream_core::common::{
|
||||||
map_openai_finish_reason_to_gemini, parse_json_arguments_value, CanonicalContentPart,
|
map_openai_finish_reason_to_gemini, parse_json_arguments_value, CanonicalContentPart,
|
||||||
CanonicalStreamEvent, CanonicalUsage,
|
CanonicalStreamEvent, CanonicalUsage,
|
||||||
};
|
};
|
||||||
@@ -2875,7 +2875,7 @@ mod tests {
|
|||||||
maybe_build_standard_sync_finalize_product_from_normalized_payload,
|
maybe_build_standard_sync_finalize_product_from_normalized_payload,
|
||||||
StandardSyncFinalizeNormalizedProduct,
|
StandardSyncFinalizeNormalizedProduct,
|
||||||
};
|
};
|
||||||
use aether_ai_formats::protocol::conversion::response::{
|
use aether_ai_formats::formats::conversion::response::{
|
||||||
convert_claude_chat_response_to_openai_chat, convert_gemini_chat_response_to_openai_chat,
|
convert_claude_chat_response_to_openai_chat, convert_gemini_chat_response_to_openai_chat,
|
||||||
convert_openai_chat_response_to_claude_chat, convert_openai_chat_response_to_gemini_chat,
|
convert_openai_chat_response_to_claude_chat, convert_openai_chat_response_to_gemini_chat,
|
||||||
convert_openai_chat_response_to_openai_responses,
|
convert_openai_chat_response_to_openai_responses,
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
use aether_ai_formats::protocol::conversion::response::{
|
use aether_ai_formats::formats::conversion::response::{
|
||||||
convert_claude_response_to_openai_responses, convert_gemini_response_to_openai_responses,
|
convert_claude_response_to_openai_responses, convert_gemini_response_to_openai_responses,
|
||||||
convert_openai_chat_response_to_openai_responses,
|
convert_openai_chat_response_to_openai_responses,
|
||||||
};
|
};
|
||||||
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::response::sse::encode_json_sse;
|
use crate::formats::claude::messages::stream::ClaudeClientEmitter;
|
||||||
use crate::response::standard::claude::stream::ClaudeClientEmitter;
|
use crate::formats::gemini::generate_content::stream::GeminiClientEmitter;
|
||||||
use crate::response::standard::gemini::stream::GeminiClientEmitter;
|
use crate::formats::openai::chat::stream::{
|
||||||
use crate::response::standard::openai::stream::{
|
|
||||||
OpenAIChatClientEmitter, OpenAIResponsesClientEmitter, OpenAIResponsesProviderState,
|
OpenAIChatClientEmitter, OpenAIResponsesClientEmitter, OpenAIResponsesProviderState,
|
||||||
};
|
};
|
||||||
use crate::response::standard::stream_core::CanonicalStreamFrame;
|
use crate::formats::shared::sse::encode_json_sse;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::stream_core::CanonicalStreamFrame;
|
||||||
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
pub struct SyncToStreamBridgeOutcome {
|
pub struct SyncToStreamBridgeOutcome {
|
||||||
pub sse_body: Vec<u8>,
|
pub sse_body: Vec<u8>,
|
||||||
34
crates/aether-ai-formats/src/formats/shared/video.rs
Normal file
34
crates/aether-ai-formats/src/formats/shared/video.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum LocalVideoCreateFamily {
|
||||||
|
OpenAi,
|
||||||
|
Gemini,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct LocalVideoCreateSpec {
|
||||||
|
pub api_format: &'static str,
|
||||||
|
pub decision_kind: &'static str,
|
||||||
|
pub report_kind: &'static str,
|
||||||
|
pub family: LocalVideoCreateFamily,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalVideoCreateSpec> {
|
||||||
|
crate::formats::openai::video::spec::resolve_sync_spec(plan_kind)
|
||||||
|
.or_else(|| crate::formats::gemini::video::spec::resolve_sync_spec(plan_kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{resolve_sync_spec, LocalVideoCreateFamily};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolves_openai_and_gemini_video_create_specs() {
|
||||||
|
let openai = resolve_sync_spec("openai_video_create_sync").expect("openai spec");
|
||||||
|
assert_eq!(openai.api_format, "openai:video");
|
||||||
|
assert_eq!(openai.family, LocalVideoCreateFamily::OpenAi);
|
||||||
|
|
||||||
|
let gemini = resolve_sync_spec("gemini_video_create_sync").expect("gemini spec");
|
||||||
|
assert_eq!(gemini.api_format, "gemini:video");
|
||||||
|
assert_eq!(gemini.family, LocalVideoCreateFamily::Gemini);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,31 @@ extern crate self as aether_ai_formats;
|
|||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod contracts;
|
pub mod contracts;
|
||||||
|
pub mod formats;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod provider_compat;
|
pub mod provider_compat;
|
||||||
pub mod request;
|
|
||||||
pub mod response;
|
|
||||||
|
|
||||||
|
pub use formats::context::{FormatContext, FormatError};
|
||||||
|
pub use formats::id::{
|
||||||
|
api_format_alias_matches, api_format_storage_aliases, is_openai_responses_compact_format,
|
||||||
|
is_openai_responses_family_format, is_openai_responses_format, normalize_api_format_alias,
|
||||||
|
FormatFamily, FormatId, FormatProfile,
|
||||||
|
};
|
||||||
|
pub use formats::matrix::{
|
||||||
|
is_embedding_api_format, is_rerank_api_format, request_candidate_api_format_preference,
|
||||||
|
request_candidate_api_formats, request_conversion_kind,
|
||||||
|
request_conversion_requires_enable_flag, sync_chat_response_conversion_kind,
|
||||||
|
sync_cli_response_conversion_kind, RequestConversionKind, SyncChatResponseConversionKind,
|
||||||
|
SyncCliResponseConversionKind,
|
||||||
|
};
|
||||||
|
pub use formats::registry::{build_stream_transcoder, convert_request, convert_response};
|
||||||
|
pub use formats::shared::model_directives::{
|
||||||
|
apply_model_directive_mapping_patch, apply_model_directive_overrides_from_model,
|
||||||
|
apply_model_directive_overrides_from_request, claude_model_uses_adaptive_effort,
|
||||||
|
extract_gemini_model_from_path, gemini_model_uses_thinking_level, model_directive_base_model,
|
||||||
|
normalize_model_directive_model, parse_model_directive, ModelDirective, ModelOverride,
|
||||||
|
ReasoningEffort,
|
||||||
|
};
|
||||||
pub use protocol::canonical::{
|
pub use protocol::canonical::{
|
||||||
canonical_request_unknown_block_count, canonical_response_unknown_block_count,
|
canonical_request_unknown_block_count, canonical_response_unknown_block_count,
|
||||||
canonical_to_claude_request, canonical_to_claude_response, canonical_to_embedding_response,
|
canonical_to_claude_request, canonical_to_claude_response, canonical_to_embedding_response,
|
||||||
@@ -25,24 +45,3 @@ pub use protocol::canonical::{
|
|||||||
CanonicalStopReason, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalThinkingConfig,
|
CanonicalStopReason, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalThinkingConfig,
|
||||||
CanonicalToolChoice, CanonicalToolDefinition, CanonicalUsage,
|
CanonicalToolChoice, CanonicalToolDefinition, CanonicalUsage,
|
||||||
};
|
};
|
||||||
pub use protocol::context::{FormatContext, FormatError};
|
|
||||||
pub use protocol::formats::{
|
|
||||||
api_format_alias_matches, api_format_storage_aliases, is_openai_responses_compact_format,
|
|
||||||
is_openai_responses_family_format, is_openai_responses_format, normalize_api_format_alias,
|
|
||||||
FormatFamily, FormatId, FormatProfile,
|
|
||||||
};
|
|
||||||
pub use protocol::matrix::{
|
|
||||||
is_embedding_api_format, is_rerank_api_format, request_candidate_api_format_preference,
|
|
||||||
request_candidate_api_formats, request_conversion_kind,
|
|
||||||
request_conversion_requires_enable_flag, sync_chat_response_conversion_kind,
|
|
||||||
sync_cli_response_conversion_kind, RequestConversionKind, SyncChatResponseConversionKind,
|
|
||||||
SyncCliResponseConversionKind,
|
|
||||||
};
|
|
||||||
pub use protocol::registry::{build_stream_transcoder, convert_request, convert_response};
|
|
||||||
pub use request::model_directives::{
|
|
||||||
apply_model_directive_mapping_patch, apply_model_directive_overrides_from_model,
|
|
||||||
apply_model_directive_overrides_from_request, claude_model_uses_adaptive_effort,
|
|
||||||
extract_gemini_model_from_path, gemini_model_uses_thinking_level, model_directive_base_model,
|
|
||||||
normalize_model_directive_model, parse_model_directive, ModelDirective, ModelOverride,
|
|
||||||
ReasoningEffort,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::request::openai::map_thinking_budget_to_openai_reasoning_effort;
|
use crate::formats::openai::shared::map_thinking_budget_to_openai_reasoning_effort;
|
||||||
|
|
||||||
pub use crate::protocol::stream::{CanonicalStreamEvent, CanonicalStreamFrame};
|
pub use crate::protocol::stream::{CanonicalStreamEvent, CanonicalStreamFrame};
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ pub enum CanonicalEmbeddingInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CanonicalEmbeddingInput {
|
impl CanonicalEmbeddingInput {
|
||||||
fn is_empty(&self) -> bool {
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::String(value) => value.trim().is_empty(),
|
Self::String(value) => value.trim().is_empty(),
|
||||||
Self::StringArray(values) => {
|
Self::StringArray(values) => {
|
||||||
@@ -243,7 +243,7 @@ impl CanonicalEmbeddingInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_string_items(&self) -> Option<Vec<&str>> {
|
pub(crate) fn as_string_items(&self) -> Option<Vec<&str>> {
|
||||||
match self {
|
match self {
|
||||||
Self::String(value) => Some(vec![value.as_str()]),
|
Self::String(value) => Some(vec![value.as_str()]),
|
||||||
Self::StringArray(values) => Some(values.iter().map(String::as_str).collect()),
|
Self::StringArray(values) => Some(values.iter().map(String::as_str).collect()),
|
||||||
@@ -281,7 +281,7 @@ pub struct CanonicalRerankRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CanonicalRerankRequest {
|
impl CanonicalRerankRequest {
|
||||||
fn is_empty(&self) -> bool {
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
self.query.trim().is_empty()
|
self.query.trim().is_empty()
|
||||||
|| self.documents.is_empty()
|
|| self.documents.is_empty()
|
||||||
|| self.documents.iter().any(rerank_document_is_empty)
|
|| self.documents.iter().any(rerank_document_is_empty)
|
||||||
@@ -385,15 +385,15 @@ pub struct CanonicalResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_openai_chat_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
pub fn from_openai_chat_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||||
crate::protocol::formats::openai_chat::request::from_raw(body_json)
|
crate::formats::openai::chat::request::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_openai_chat_request(canonical: &CanonicalRequest) -> Value {
|
pub fn canonical_to_openai_chat_request(canonical: &CanonicalRequest) -> Value {
|
||||||
crate::protocol::formats::openai_chat::request::to_raw(canonical)
|
crate::formats::openai::chat::request::to_raw(canonical)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_openai_responses_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
pub fn from_openai_responses_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||||
crate::protocol::formats::openai_responses::request::from_raw(body_json)
|
crate::formats::openai::responses::request::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn canonical_to_openai_responses_request_with_profile(
|
pub(crate) fn canonical_to_openai_responses_request_with_profile(
|
||||||
@@ -402,7 +402,7 @@ pub(crate) fn canonical_to_openai_responses_request_with_profile(
|
|||||||
upstream_is_stream: bool,
|
upstream_is_stream: bool,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
crate::protocol::formats::openai_responses::request::to_raw(
|
crate::formats::openai::responses::request::to_raw(
|
||||||
canonical,
|
canonical,
|
||||||
mapped_model,
|
mapped_model,
|
||||||
upstream_is_stream,
|
upstream_is_stream,
|
||||||
@@ -431,7 +431,7 @@ pub fn canonical_to_openai_responses_compact_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_claude_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
pub fn from_claude_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||||
crate::protocol::formats::claude_messages::request::from_raw(body_json)
|
crate::formats::claude::messages::request::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_claude_request(
|
pub fn canonical_to_claude_request(
|
||||||
@@ -439,18 +439,14 @@ pub fn canonical_to_claude_request(
|
|||||||
mapped_model: &str,
|
mapped_model: &str,
|
||||||
upstream_is_stream: bool,
|
upstream_is_stream: bool,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
crate::protocol::formats::claude_messages::request::to_raw(
|
crate::formats::claude::messages::request::to_raw(canonical, mapped_model, upstream_is_stream)
|
||||||
canonical,
|
|
||||||
mapped_model,
|
|
||||||
upstream_is_stream,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_gemini_to_canonical_request(
|
pub fn from_gemini_to_canonical_request(
|
||||||
body_json: &Value,
|
body_json: &Value,
|
||||||
request_path: &str,
|
request_path: &str,
|
||||||
) -> Option<CanonicalRequest> {
|
) -> Option<CanonicalRequest> {
|
||||||
crate::protocol::formats::gemini_generate_content::request::from_raw(body_json, request_path)
|
crate::formats::gemini::generate_content::request::from_raw(body_json, request_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_gemini_request(
|
pub fn canonical_to_gemini_request(
|
||||||
@@ -458,72 +454,59 @@ pub fn canonical_to_gemini_request(
|
|||||||
mapped_model: &str,
|
mapped_model: &str,
|
||||||
upstream_is_stream: bool,
|
upstream_is_stream: bool,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
crate::protocol::formats::gemini_generate_content::request::to_raw(
|
crate::formats::gemini::generate_content::request::to_raw(
|
||||||
canonical,
|
canonical,
|
||||||
mapped_model,
|
mapped_model,
|
||||||
upstream_is_stream,
|
upstream_is_stream,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub(crate) fn from_embedding_to_canonical_request(
|
pub(crate) fn from_embedding_to_canonical_request(
|
||||||
body_json: &Value,
|
body_json: &Value,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Option<CanonicalRequest> {
|
) -> Option<CanonicalRequest> {
|
||||||
embedding_request_from_raw(body_json, namespace)
|
match namespace {
|
||||||
|
"openai" => crate::formats::openai::embedding::request::from_namespace(body_json, "openai"),
|
||||||
|
"jina" => crate::formats::openai::embedding::request::from_namespace(body_json, "jina"),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub(crate) fn canonical_to_embedding_request(
|
pub(crate) fn canonical_to_embedding_request(
|
||||||
canonical: &CanonicalRequest,
|
canonical: &CanonicalRequest,
|
||||||
mapped_model: &str,
|
mapped_model: &str,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
|
let ctx = crate::formats::context::FormatContext::default().with_mapped_model(mapped_model);
|
||||||
match namespace {
|
match namespace {
|
||||||
"openai" => canonical_to_openai_embedding_request(canonical, mapped_model),
|
"openai" => crate::formats::openai::embedding::request::to(canonical, &ctx),
|
||||||
"jina" => canonical_to_jina_embedding_request(canonical, mapped_model),
|
"jina" => crate::formats::jina::embedding::request::to(canonical, &ctx),
|
||||||
"gemini" => canonical_to_gemini_embedding_request(canonical, mapped_model),
|
"gemini" => crate::formats::gemini::embedding::request::to(canonical, &ctx),
|
||||||
"doubao" => canonical_to_doubao_embedding_request(canonical, mapped_model),
|
"doubao" => crate::formats::doubao::embedding::request::to(canonical, &ctx),
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_rerank_to_canonical_request(
|
|
||||||
body_json: &Value,
|
|
||||||
namespace: &str,
|
|
||||||
) -> Option<CanonicalRequest> {
|
|
||||||
rerank_request_from_raw(body_json, namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn canonical_to_rerank_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
namespace: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
match namespace {
|
|
||||||
"openai" | "jina" => {
|
|
||||||
canonical_to_openai_like_rerank_request(canonical, mapped_model, namespace)
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_openai_chat_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
pub fn from_openai_chat_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||||
crate::protocol::formats::openai_chat::response::from_raw(body_json)
|
crate::formats::openai::chat::response::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_openai_responses_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
pub fn from_openai_responses_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||||
crate::protocol::formats::openai_responses::response::from_raw(body_json)
|
crate::formats::openai::responses::response::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_claude_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
pub fn from_claude_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||||
crate::protocol::formats::claude_messages::response::from_raw(body_json)
|
crate::formats::claude::messages::response::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_gemini_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
pub fn from_gemini_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||||
crate::protocol::formats::gemini_generate_content::response::from_raw(body_json)
|
crate::formats::gemini::generate_content::response::from_raw(body_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_openai_chat_response(canonical: &CanonicalResponse) -> Value {
|
pub fn canonical_to_openai_chat_response(canonical: &CanonicalResponse) -> Value {
|
||||||
crate::protocol::formats::openai_chat::response::to_raw(canonical)
|
crate::formats::openai::chat::response::to_raw(canonical)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn canonical_blocks_to_openai_chat_message(content: &[CanonicalContentBlock]) -> Value {
|
pub(crate) fn canonical_blocks_to_openai_chat_message(content: &[CanonicalContentBlock]) -> Value {
|
||||||
@@ -661,7 +644,7 @@ pub(crate) fn canonical_to_openai_responses_response_with_profile(
|
|||||||
report_context: &Value,
|
report_context: &Value,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
crate::protocol::formats::openai_responses::response::to_raw(canonical, report_context, compact)
|
crate::formats::openai::responses::response::to_raw(canonical, report_context, compact)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_openai_responses_response(
|
pub fn canonical_to_openai_responses_response(
|
||||||
@@ -679,21 +662,27 @@ pub fn canonical_to_openai_responses_compact_response(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_claude_response(canonical: &CanonicalResponse) -> Value {
|
pub fn canonical_to_claude_response(canonical: &CanonicalResponse) -> Value {
|
||||||
crate::protocol::formats::claude_messages::response::to_raw(canonical)
|
crate::formats::claude::messages::response::to_raw(canonical)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_gemini_response(
|
pub fn canonical_to_gemini_response(
|
||||||
canonical: &CanonicalResponse,
|
canonical: &CanonicalResponse,
|
||||||
report_context: &Value,
|
report_context: &Value,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
crate::protocol::formats::gemini_generate_content::response::to_raw(canonical, report_context)
|
crate::formats::gemini::generate_content::response::to_raw(canonical, report_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_embedding_to_canonical_response(
|
pub fn from_embedding_to_canonical_response(
|
||||||
body_json: &Value,
|
body_json: &Value,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Option<CanonicalEmbeddingResponse> {
|
) -> Option<CanonicalEmbeddingResponse> {
|
||||||
embedding_response_from_raw(body_json, namespace)
|
match namespace {
|
||||||
|
"openai" => {
|
||||||
|
crate::formats::openai::embedding::response::from_namespace(body_json, "openai")
|
||||||
|
}
|
||||||
|
"jina" => crate::formats::openai::embedding::response::from_namespace(body_json, "jina"),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonical_to_embedding_response(
|
pub fn canonical_to_embedding_response(
|
||||||
@@ -701,7 +690,8 @@ pub fn canonical_to_embedding_response(
|
|||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
match namespace {
|
match namespace {
|
||||||
"openai" | "jina" => Some(canonical_to_openai_embedding_response(canonical, namespace)),
|
"openai" => crate::formats::openai::embedding::response::to(canonical),
|
||||||
|
"jina" => crate::formats::jina::embedding::response::to(canonical),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4273,140 +4263,6 @@ pub(crate) fn strip_claude_billing_header(text: &str) -> String {
|
|||||||
remainder.trim_start_matches('\n').trim().to_string()
|
remainder.trim_start_matches('\n').trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn embedding_request_from_raw(body_json: &Value, namespace: &str) -> Option<CanonicalRequest> {
|
|
||||||
let request = body_json.as_object()?;
|
|
||||||
let model = request
|
|
||||||
.get("model")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(str::trim)
|
|
||||||
.filter(|value| !value.is_empty())?
|
|
||||||
.to_string();
|
|
||||||
let input =
|
|
||||||
serde_json::from_value::<CanonicalEmbeddingInput>(request.get("input")?.clone()).ok()?;
|
|
||||||
if input.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let embedding = CanonicalEmbeddingRequest {
|
|
||||||
input,
|
|
||||||
encoding_format: request
|
|
||||||
.get("encoding_format")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(ToOwned::to_owned),
|
|
||||||
dimensions: request.get("dimensions").and_then(Value::as_u64),
|
|
||||||
task: request
|
|
||||||
.get("task")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(ToOwned::to_owned),
|
|
||||||
user: request
|
|
||||||
.get("user")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(ToOwned::to_owned),
|
|
||||||
extensions: namespace_extensions(
|
|
||||||
namespace,
|
|
||||||
request,
|
|
||||||
&[
|
|
||||||
"model",
|
|
||||||
"input",
|
|
||||||
"encoding_format",
|
|
||||||
"dimensions",
|
|
||||||
"task",
|
|
||||||
"user",
|
|
||||||
],
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(CanonicalRequest {
|
|
||||||
model,
|
|
||||||
embedding: Some(embedding),
|
|
||||||
..CanonicalRequest::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rerank_request_from_raw(body_json: &Value, namespace: &str) -> Option<CanonicalRequest> {
|
|
||||||
let request = body_json.as_object()?;
|
|
||||||
let model = request
|
|
||||||
.get("model")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(str::trim)
|
|
||||||
.filter(|value| !value.is_empty())?
|
|
||||||
.to_string();
|
|
||||||
let query = request
|
|
||||||
.get("query")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.map(str::trim)
|
|
||||||
.filter(|value| !value.is_empty())?
|
|
||||||
.to_string();
|
|
||||||
let documents = request.get("documents").and_then(Value::as_array)?.to_vec();
|
|
||||||
let rerank = CanonicalRerankRequest {
|
|
||||||
query,
|
|
||||||
documents,
|
|
||||||
top_n: request
|
|
||||||
.get("top_n")
|
|
||||||
.or_else(|| request.get("topN"))
|
|
||||||
.and_then(Value::as_u64),
|
|
||||||
return_documents: request
|
|
||||||
.get("return_documents")
|
|
||||||
.or_else(|| request.get("returnDocuments"))
|
|
||||||
.and_then(Value::as_bool),
|
|
||||||
extensions: namespace_extensions(
|
|
||||||
namespace,
|
|
||||||
request,
|
|
||||||
&[
|
|
||||||
"model",
|
|
||||||
"query",
|
|
||||||
"documents",
|
|
||||||
"top_n",
|
|
||||||
"topN",
|
|
||||||
"return_documents",
|
|
||||||
"returnDocuments",
|
|
||||||
],
|
|
||||||
),
|
|
||||||
};
|
|
||||||
if rerank.is_empty() || rerank.top_n == Some(0) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(CanonicalRequest {
|
|
||||||
model,
|
|
||||||
rerank: Some(rerank),
|
|
||||||
..CanonicalRequest::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_openai_like_rerank_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
namespace: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
let rerank = canonical.rerank.as_ref()?;
|
|
||||||
if rerank.is_empty() || rerank.top_n == Some(0) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut output = Map::new();
|
|
||||||
output.insert(
|
|
||||||
"model".to_string(),
|
|
||||||
Value::String(mapped_rerank_model(canonical, mapped_model)),
|
|
||||||
);
|
|
||||||
output.insert("query".to_string(), Value::String(rerank.query.clone()));
|
|
||||||
output.insert(
|
|
||||||
"documents".to_string(),
|
|
||||||
Value::Array(rerank.documents.clone()),
|
|
||||||
);
|
|
||||||
if let Some(value) = rerank.top_n {
|
|
||||||
output.insert("top_n".to_string(), Value::from(value));
|
|
||||||
}
|
|
||||||
if let Some(value) = rerank.return_documents {
|
|
||||||
output.insert("return_documents".to_string(), Value::Bool(value));
|
|
||||||
}
|
|
||||||
output.extend(namespace_extension_object(
|
|
||||||
&rerank.extensions,
|
|
||||||
namespace,
|
|
||||||
&output,
|
|
||||||
));
|
|
||||||
Some(Value::Object(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rerank_document_is_empty(value: &Value) -> bool {
|
fn rerank_document_is_empty(value: &Value) -> bool {
|
||||||
match value {
|
match value {
|
||||||
Value::String(text) => text.trim().is_empty(),
|
Value::String(text) => text.trim().is_empty(),
|
||||||
@@ -4419,265 +4275,6 @@ fn rerank_document_is_empty(value: &Value) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mapped_rerank_model(canonical: &CanonicalRequest, mapped_model: &str) -> String {
|
|
||||||
mapped_model
|
|
||||||
.trim()
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
.map(|_| mapped_model.trim().to_string())
|
|
||||||
.unwrap_or_else(|| canonical.model.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_openai_embedding_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
canonical_to_openai_like_embedding_request(canonical, mapped_model, "openai", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_jina_embedding_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
canonical_to_openai_like_embedding_request(canonical, mapped_model, "jina", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_openai_like_embedding_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
namespace: &str,
|
|
||||||
default_task: bool,
|
|
||||||
) -> Option<Value> {
|
|
||||||
let embedding = canonical.embedding.as_ref()?;
|
|
||||||
if embedding.input.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut output = Map::new();
|
|
||||||
output.insert(
|
|
||||||
"model".to_string(),
|
|
||||||
Value::String(mapped_embedding_model(canonical, mapped_model)),
|
|
||||||
);
|
|
||||||
output.insert(
|
|
||||||
"input".to_string(),
|
|
||||||
serde_json::to_value(&embedding.input).ok()?,
|
|
||||||
);
|
|
||||||
if let Some(value) = &embedding.encoding_format {
|
|
||||||
output.insert("encoding_format".to_string(), Value::String(value.clone()));
|
|
||||||
}
|
|
||||||
if let Some(value) = embedding.dimensions {
|
|
||||||
output.insert("dimensions".to_string(), Value::from(value));
|
|
||||||
}
|
|
||||||
if let Some(value) = &embedding.user {
|
|
||||||
output.insert("user".to_string(), Value::String(value.clone()));
|
|
||||||
}
|
|
||||||
if let Some(task) = embedding
|
|
||||||
.task
|
|
||||||
.as_ref()
|
|
||||||
.filter(|value| !value.trim().is_empty())
|
|
||||||
{
|
|
||||||
output.insert("task".to_string(), Value::String(task.clone()));
|
|
||||||
} else if default_task {
|
|
||||||
output.insert(
|
|
||||||
"task".to_string(),
|
|
||||||
Value::String("text-matching".to_string()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
output.extend(namespace_extension_object(
|
|
||||||
&embedding.extensions,
|
|
||||||
namespace,
|
|
||||||
&output,
|
|
||||||
));
|
|
||||||
Some(Value::Object(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_gemini_embedding_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
let embedding = canonical.embedding.as_ref()?;
|
|
||||||
let items = embedding.input.as_string_items()?;
|
|
||||||
if items.is_empty() || items.iter().any(|value| value.trim().is_empty()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let model = mapped_embedding_model(canonical, mapped_model);
|
|
||||||
if items.len() == 1 {
|
|
||||||
return Some(json!({
|
|
||||||
"model": model,
|
|
||||||
"content": {
|
|
||||||
"parts": [{"text": items[0]}]
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
Some(json!({
|
|
||||||
"model": model,
|
|
||||||
"requests": items.into_iter().map(|text| {
|
|
||||||
json!({
|
|
||||||
"model": model,
|
|
||||||
"content": {
|
|
||||||
"parts": [{"text": text}]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).collect::<Vec<_>>()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_doubao_embedding_request(
|
|
||||||
canonical: &CanonicalRequest,
|
|
||||||
mapped_model: &str,
|
|
||||||
) -> Option<Value> {
|
|
||||||
let embedding = canonical.embedding.as_ref()?;
|
|
||||||
let items = embedding.input.as_string_items()?;
|
|
||||||
if items.is_empty() || items.iter().any(|value| value.trim().is_empty()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut output = Map::new();
|
|
||||||
output.insert(
|
|
||||||
"model".to_string(),
|
|
||||||
Value::String(mapped_embedding_model(canonical, mapped_model)),
|
|
||||||
);
|
|
||||||
output.insert(
|
|
||||||
"input".to_string(),
|
|
||||||
Value::Array(
|
|
||||||
items
|
|
||||||
.into_iter()
|
|
||||||
.map(|text| json!({"type": "text", "text": text}))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if let Some(dimensions) = embedding.dimensions {
|
|
||||||
output.insert("dimensions".to_string(), Value::from(dimensions));
|
|
||||||
}
|
|
||||||
output.extend(namespace_extension_object(
|
|
||||||
&embedding.extensions,
|
|
||||||
"doubao",
|
|
||||||
&output,
|
|
||||||
));
|
|
||||||
Some(Value::Object(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn embedding_response_from_raw(
|
|
||||||
body_json: &Value,
|
|
||||||
namespace: &str,
|
|
||||||
) -> Option<CanonicalEmbeddingResponse> {
|
|
||||||
let body = body_json.as_object()?;
|
|
||||||
if body.contains_key("error") {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let data = body.get("data")?.as_array()?;
|
|
||||||
let mut embeddings = Vec::new();
|
|
||||||
for (fallback_index, item) in data.iter().enumerate() {
|
|
||||||
let item_object = item.as_object()?;
|
|
||||||
let values = item_object.get("embedding")?.as_array()?;
|
|
||||||
let embedding = values
|
|
||||||
.iter()
|
|
||||||
.map(Value::as_f64)
|
|
||||||
.collect::<Option<Vec<_>>>()?;
|
|
||||||
embeddings.push(CanonicalEmbedding {
|
|
||||||
index: item_object
|
|
||||||
.get("index")
|
|
||||||
.and_then(Value::as_u64)
|
|
||||||
.and_then(|value| usize::try_from(value).ok())
|
|
||||||
.unwrap_or(fallback_index),
|
|
||||||
embedding,
|
|
||||||
extensions: namespace_extensions(
|
|
||||||
namespace,
|
|
||||||
item_object,
|
|
||||||
&["object", "index", "embedding"],
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Some(CanonicalEmbeddingResponse {
|
|
||||||
id: body
|
|
||||||
.get("id")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.unwrap_or("embd-unknown")
|
|
||||||
.to_string(),
|
|
||||||
model: body
|
|
||||||
.get("model")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.unwrap_or("unknown")
|
|
||||||
.to_string(),
|
|
||||||
embeddings,
|
|
||||||
usage: openai_usage_to_canonical(body.get("usage")),
|
|
||||||
extensions: namespace_extensions(
|
|
||||||
namespace,
|
|
||||||
body,
|
|
||||||
&["id", "object", "model", "data", "usage"],
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonical_to_openai_embedding_response(
|
|
||||||
canonical: &CanonicalEmbeddingResponse,
|
|
||||||
namespace: &str,
|
|
||||||
) -> Value {
|
|
||||||
let mut response = Map::new();
|
|
||||||
response.insert("object".to_string(), Value::String("list".to_string()));
|
|
||||||
if !canonical.model.trim().is_empty() && canonical.model != "unknown" {
|
|
||||||
response.insert("model".to_string(), Value::String(canonical.model.clone()));
|
|
||||||
}
|
|
||||||
response.insert(
|
|
||||||
"data".to_string(),
|
|
||||||
Value::Array(
|
|
||||||
canonical
|
|
||||||
.embeddings
|
|
||||||
.iter()
|
|
||||||
.map(|embedding| {
|
|
||||||
let mut item = Map::new();
|
|
||||||
item.insert("object".to_string(), Value::String("embedding".to_string()));
|
|
||||||
item.insert("index".to_string(), Value::from(embedding.index as u64));
|
|
||||||
item.insert("embedding".to_string(), json!(embedding.embedding));
|
|
||||||
item.extend(namespace_extension_object(
|
|
||||||
&embedding.extensions,
|
|
||||||
namespace,
|
|
||||||
&item,
|
|
||||||
));
|
|
||||||
Value::Object(item)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if let Some(usage) = &canonical.usage {
|
|
||||||
response.insert("usage".to_string(), canonical_usage_to_openai(usage));
|
|
||||||
}
|
|
||||||
response.extend(namespace_extension_object(
|
|
||||||
&canonical.extensions,
|
|
||||||
namespace,
|
|
||||||
&response,
|
|
||||||
));
|
|
||||||
Value::Object(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mapped_embedding_model(canonical: &CanonicalRequest, mapped_model: &str) -> String {
|
|
||||||
let mapped_model = mapped_model.trim();
|
|
||||||
if mapped_model.is_empty() {
|
|
||||||
canonical.model.clone()
|
|
||||||
} else {
|
|
||||||
mapped_model.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_extensions(
|
|
||||||
namespace: &str,
|
|
||||||
object: &Map<String, Value>,
|
|
||||||
handled_keys: &[&str],
|
|
||||||
) -> BTreeMap<String, Value> {
|
|
||||||
let handled = handled_keys
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.collect::<std::collections::BTreeSet<_>>();
|
|
||||||
let raw = object
|
|
||||||
.iter()
|
|
||||||
.filter(|(key, _)| !handled.contains(key.as_str()))
|
|
||||||
.map(|(key, value)| (key.clone(), value.clone()))
|
|
||||||
.collect::<Map<String, Value>>();
|
|
||||||
if raw.is_empty() {
|
|
||||||
BTreeMap::new()
|
|
||||||
} else {
|
|
||||||
BTreeMap::from([(namespace.to_string(), Value::Object(raw))])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
//! Pairwise format conversion entry points.
|
|
||||||
//!
|
|
||||||
//! The public helpers in this module route through the registry, so request and
|
|
||||||
//! response conversion still pass through the typed canonical IR before a target
|
|
||||||
//! wire format is emitted.
|
|
||||||
|
|
||||||
pub mod request;
|
|
||||||
pub mod response;
|
|
||||||
@@ -1,7 +1,2 @@
|
|||||||
pub mod canonical;
|
pub mod canonical;
|
||||||
pub mod context;
|
|
||||||
pub mod conversion;
|
|
||||||
pub mod formats;
|
|
||||||
pub mod matrix;
|
|
||||||
pub mod registry;
|
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::provider_compat::kiro_stream::{
|
|||||||
find_kiro_real_thinking_start_tag, KIRO_MAX_THINKING_BUFFER,
|
find_kiro_real_thinking_start_tag, KIRO_MAX_THINKING_BUFFER,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
use super::super::AwsEventFrame;
|
use super::super::AwsEventFrame;
|
||||||
use super::super::KiroClaudeStreamState;
|
use super::super::KiroClaudeStreamState;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::provider_compat::kiro_stream::{
|
|||||||
find_kiro_real_thinking_end_tag_at_buffer_end,
|
find_kiro_real_thinking_end_tag_at_buffer_end,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
|
|
||||||
use super::super::KiroClaudeStreamState;
|
use super::super::KiroClaudeStreamState;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
use crate::provider_compat::kiro_stream::{
|
use crate::provider_compat::kiro_stream::{
|
||||||
build_kiro_initial_sse_events, build_kiro_stream_error_sse_events, encode_kiro_sse_events,
|
build_kiro_initial_sse_events, build_kiro_stream_error_sse_events, encode_kiro_sse_events,
|
||||||
};
|
};
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
|
||||||
|
|
||||||
use super::super::{EventStreamDecoder, KiroClaudeStreamState, KiroToClaudeCliStreamState};
|
use super::super::{EventStreamDecoder, KiroClaudeStreamState, KiroToClaudeCliStreamState};
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::formats::shared::AiSurfaceFinalizeError;
|
||||||
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
||||||
use crate::response::AiSurfaceFinalizeError;
|
|
||||||
|
|
||||||
use super::surfaces::{
|
use super::surfaces::{
|
||||||
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_descriptor_for_envelope,
|
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_descriptor_for_envelope,
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
pub mod common;
|
|
||||||
pub mod matrix;
|
|
||||||
pub mod model_directives;
|
|
||||||
pub mod openai;
|
|
||||||
pub mod passthrough;
|
|
||||||
pub mod route;
|
|
||||||
pub mod specialized;
|
|
||||||
pub mod standard;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod provider;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod files;
|
|
||||||
pub mod image;
|
|
||||||
pub mod video;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
use crate::contracts::{GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum LocalVideoCreateFamily {
|
|
||||||
OpenAi,
|
|
||||||
Gemini,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct LocalVideoCreateSpec {
|
|
||||||
pub api_format: &'static str,
|
|
||||||
pub decision_kind: &'static str,
|
|
||||||
pub report_kind: &'static str,
|
|
||||||
pub family: LocalVideoCreateFamily,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalVideoCreateSpec> {
|
|
||||||
match plan_kind {
|
|
||||||
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND => Some(LocalVideoCreateSpec {
|
|
||||||
api_format: "openai:video",
|
|
||||||
decision_kind: OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
|
||||||
report_kind: "openai_video_create_sync_finalize",
|
|
||||||
family: LocalVideoCreateFamily::OpenAi,
|
|
||||||
}),
|
|
||||||
GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND => Some(LocalVideoCreateSpec {
|
|
||||||
api_format: "gemini:video",
|
|
||||||
decision_kind: GEMINI_VIDEO_CREATE_SYNC_PLAN_KIND,
|
|
||||||
report_kind: "gemini_video_create_sync_finalize",
|
|
||||||
family: LocalVideoCreateFamily::Gemini,
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{resolve_sync_spec, LocalVideoCreateFamily};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolves_openai_video_create_spec() {
|
|
||||||
let spec = resolve_sync_spec("openai_video_create_sync").expect("spec");
|
|
||||||
assert_eq!(spec.api_format, "openai:video");
|
|
||||||
assert_eq!(spec.family, LocalVideoCreateFamily::OpenAi);
|
|
||||||
assert_eq!(spec.report_kind, "openai_video_create_sync_finalize");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resolves_gemini_video_create_spec() {
|
|
||||||
let spec = resolve_sync_spec("gemini_video_create_sync").expect("spec");
|
|
||||||
assert_eq!(spec.api_format, "gemini:video");
|
|
||||||
assert_eq!(spec.family, LocalVideoCreateFamily::Gemini);
|
|
||||||
assert_eq!(spec.report_kind, "gemini_video_create_sync_finalize");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
pub mod chat;
|
|
||||||
pub mod cli;
|
|
||||||
|
|
||||||
use crate::request::standard::LocalStandardSpec;
|
|
||||||
|
|
||||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
|
||||||
chat::resolve_sync_spec(plan_kind).or_else(|| cli::resolve_sync_spec(plan_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
|
||||||
chat::resolve_stream_spec(plan_kind).or_else(|| cli::resolve_stream_spec(plan_kind))
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
pub mod chat;
|
|
||||||
pub mod cli;
|
|
||||||
|
|
||||||
use crate::request::standard::LocalStandardSpec;
|
|
||||||
|
|
||||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
|
||||||
chat::resolve_sync_spec(plan_kind).or_else(|| cli::resolve_sync_spec(plan_kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
|
||||||
chat::resolve_stream_spec(plan_kind).or_else(|| cli::resolve_stream_spec(plan_kind))
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
pub mod claude;
|
|
||||||
pub mod codex;
|
|
||||||
pub mod family;
|
|
||||||
pub mod gemini;
|
|
||||||
pub mod matrix;
|
|
||||||
pub mod normalize;
|
|
||||||
pub mod openai_responses;
|
|
||||||
|
|
||||||
pub use codex::{
|
|
||||||
apply_codex_openai_responses_special_body_edits, apply_codex_openai_responses_special_headers,
|
|
||||||
apply_openai_responses_compact_special_body_edits, CODEX_OPENAI_IMAGE_DEFAULT_MODEL,
|
|
||||||
CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL,
|
|
||||||
CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_PROMPT, CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
|
||||||
};
|
|
||||||
pub use family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
|
|
||||||
pub use matrix::{
|
|
||||||
build_standard_request_body, build_standard_request_body_from_canonical_with_model_directives,
|
|
||||||
build_standard_request_body_with_model_directives,
|
|
||||||
build_standard_request_body_with_model_directives_and_request_headers,
|
|
||||||
normalize_standard_request_to_openai_chat_request,
|
|
||||||
};
|
|
||||||
pub use normalize::{
|
|
||||||
build_cross_format_openai_chat_request_body,
|
|
||||||
build_cross_format_openai_chat_request_body_with_model_directives,
|
|
||||||
build_cross_format_openai_responses_request_body,
|
|
||||||
build_cross_format_openai_responses_request_body_with_model_directives,
|
|
||||||
build_local_openai_chat_request_body,
|
|
||||||
build_local_openai_chat_request_body_with_model_directives,
|
|
||||||
build_local_openai_responses_request_body,
|
|
||||||
build_local_openai_responses_request_body_with_model_directives,
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub mod stream;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user