mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: fold ai surfaces into formats
This commit is contained in:
@@ -4,10 +4,15 @@ version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Canonical AI format IR and adapters for Aether"
|
||||
description = "Pure AI API format, surface, planning, and finalize logic for Aether"
|
||||
|
||||
[dependencies]
|
||||
aether-contracts.workspace = true
|
||||
base64.workspace = true
|
||||
http.workspace = true
|
||||
regex.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha1 = "0.10"
|
||||
sha2.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -1,29 +1,8 @@
|
||||
pub use crate::adaptation::kiro_stream::{
|
||||
build_kiro_final_message_sse_events, build_kiro_initial_sse_events,
|
||||
build_kiro_stream_error_sse_events, calculate_kiro_context_input_tokens,
|
||||
encode_kiro_sse_events, estimate_kiro_tokens, find_kiro_real_thinking_end_tag,
|
||||
find_kiro_real_thinking_end_tag_at_buffer_end, find_kiro_real_thinking_start_tag, kiro_crc32,
|
||||
KiroToClaudeCliStreamState, KIRO_MAX_THINKING_BUFFER,
|
||||
};
|
||||
pub use crate::adaptation::private_envelope::{
|
||||
maybe_build_provider_private_stream_normalizer, normalize_provider_private_report_context,
|
||||
normalize_provider_private_response_value, provider_private_response_allows_sync_finalize,
|
||||
stream_body_contains_error_event, transform_provider_private_stream_line,
|
||||
ProviderPrivateStreamNormalizer,
|
||||
};
|
||||
pub use crate::adaptation::surfaces::{
|
||||
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_anchor_api_format,
|
||||
provider_adaptation_descriptor_for_envelope, provider_adaptation_descriptor_for_provider_type,
|
||||
provider_adaptation_requires_eventstream_accept,
|
||||
provider_adaptation_should_unwrap_stream_envelope, ProviderAdaptationDescriptor,
|
||||
ProviderAdaptationSurface, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, KIRO_ENVELOPE_NAME,
|
||||
};
|
||||
pub use crate::contracts::{
|
||||
core_error_background_report_kind, core_error_default_client_api_format,
|
||||
core_success_background_report_kind, implicit_sync_finalize_report_kind,
|
||||
is_openai_responses_stream_plan_kind, is_openai_responses_sync_plan_kind,
|
||||
ExecutionRuntimeAuthContext, GatewayControlPlanRequest, CLAUDE_CHAT_STREAM_PLAN_KIND,
|
||||
is_openai_responses_stream_plan_kind, is_openai_responses_sync_plan_kind, AiControlPlanRequest,
|
||||
ExecutionRuntimeAuthContext, CLAUDE_CHAT_STREAM_PLAN_KIND,
|
||||
CLAUDE_CHAT_STREAM_SUCCESS_REPORT_KIND, CLAUDE_CHAT_SYNC_ERROR_REPORT_KIND,
|
||||
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND,
|
||||
CLAUDE_CHAT_SYNC_SUCCESS_REPORT_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
|
||||
@@ -58,71 +37,47 @@ pub use crate::contracts::{
|
||||
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
};
|
||||
pub use crate::finalize::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::provider_compat::kiro_stream::{
|
||||
build_kiro_final_message_sse_events, build_kiro_initial_sse_events,
|
||||
build_kiro_stream_error_sse_events, calculate_kiro_context_input_tokens,
|
||||
encode_kiro_sse_events, estimate_kiro_tokens, find_kiro_real_thinking_end_tag,
|
||||
find_kiro_real_thinking_end_tag_at_buffer_end, find_kiro_real_thinking_start_tag, kiro_crc32,
|
||||
KiroToClaudeCliStreamState, KIRO_MAX_THINKING_BUFFER,
|
||||
};
|
||||
pub use crate::finalize::error_body::{
|
||||
build_core_error_body_for_client_format, is_core_error_finalize_kind, LocalCoreSyncErrorKind,
|
||||
pub use crate::provider_compat::private_envelope::{
|
||||
maybe_build_provider_private_stream_normalizer, normalize_provider_private_report_context,
|
||||
normalize_provider_private_response_value, provider_private_response_allows_sync_finalize,
|
||||
stream_body_contains_error_event, transform_provider_private_stream_line,
|
||||
ProviderPrivateStreamNormalizer,
|
||||
};
|
||||
pub use crate::finalize::openai_image_stream::{
|
||||
maybe_build_openai_image_sync_finalize_product, OpenAiImageStreamState,
|
||||
OpenAiImageSyncFinalizeProduct,
|
||||
pub use crate::provider_compat::surfaces::{
|
||||
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_anchor_api_format,
|
||||
provider_adaptation_descriptor_for_envelope, provider_adaptation_descriptor_for_provider_type,
|
||||
provider_adaptation_requires_eventstream_accept,
|
||||
provider_adaptation_should_unwrap_stream_envelope, ProviderAdaptationDescriptor,
|
||||
ProviderAdaptationSurface, ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, KIRO_ENVELOPE_NAME,
|
||||
};
|
||||
pub use crate::finalize::sse::{encode_done_sse, encode_json_sse, map_claude_stop_reason};
|
||||
pub use crate::finalize::standard::claude::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
||||
pub use crate::finalize::standard::gemini::stream::{GeminiClientEmitter, GeminiProviderState};
|
||||
pub use crate::finalize::standard::openai::stream::{
|
||||
OpenAIChatClientEmitter, OpenAIChatProviderState, OpenAIResponsesClientEmitter,
|
||||
OpenAIResponsesProviderState,
|
||||
};
|
||||
pub use crate::finalize::standard::stream_core::common::*;
|
||||
pub use crate::finalize::standard::stream_core::{
|
||||
CanonicalStreamFrame, StreamingStandardFormatMatrix, StreamingStandardTerminalObserver,
|
||||
};
|
||||
pub use crate::finalize::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::finalize::sync_to_stream::{
|
||||
maybe_bridge_standard_sync_json_to_stream, SyncToStreamBridgeOutcome,
|
||||
};
|
||||
pub use crate::finalize::{
|
||||
maybe_build_ai_surface_stream_rewriter, resolve_finalize_stream_rewrite_mode,
|
||||
AiSurfaceFinalizeError, AiSurfaceStreamRewriter, FinalizeStreamRewriteMode,
|
||||
};
|
||||
pub use crate::planner::common::{
|
||||
pub use crate::request::common::{
|
||||
force_upstream_streaming_for_provider, parse_direct_request_body,
|
||||
};
|
||||
pub use crate::planner::matrix::build_standard_request_body_from_canonical;
|
||||
pub use crate::planner::openai::{
|
||||
pub use crate::request::matrix::build_standard_request_body_from_canonical;
|
||||
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::planner::passthrough::provider::{
|
||||
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::planner::route::{
|
||||
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_scheduler_decision_kind, supports_sync_scheduler_decision_kind,
|
||||
supports_stream_execution_decision_kind, supports_sync_execution_decision_kind,
|
||||
};
|
||||
pub use crate::planner::specialized::{
|
||||
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,
|
||||
@@ -140,7 +95,7 @@ pub use crate::planner::specialized::{
|
||||
LocalVideoCreateSpec,
|
||||
},
|
||||
};
|
||||
pub use crate::planner::standard::{
|
||||
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_responses_request_body, build_local_openai_chat_request_body,
|
||||
@@ -163,14 +118,59 @@ pub use crate::planner::standard::{
|
||||
CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_PROMPT,
|
||||
CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
||||
};
|
||||
pub use aether_ai_formats::conversion::request::{
|
||||
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_openai_responses_request, extract_openai_text_content,
|
||||
normalize_claude_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,
|
||||
};
|
||||
pub use aether_ai_formats::conversion::response::{
|
||||
pub use aether_ai_formats::protocol::conversion::response::{
|
||||
build_openai_responses_response, build_openai_responses_response_with_content,
|
||||
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,
|
||||
@@ -5,7 +5,7 @@ use serde::Serialize;
|
||||
use crate::contracts::ExecutionRuntimeAuthContext;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct GatewayControlPlanRequest {
|
||||
pub struct AiControlPlanRequest {
|
||||
pub trace_id: String,
|
||||
pub method: String,
|
||||
pub path: String,
|
||||
@@ -18,7 +18,7 @@ pub struct GatewayControlPlanRequest {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_gateway_control_plan_request(
|
||||
pub fn build_ai_control_plan_request(
|
||||
trace_id: &str,
|
||||
method: &str,
|
||||
path: &str,
|
||||
@@ -27,8 +27,8 @@ pub fn build_gateway_control_plan_request(
|
||||
body_json: serde_json::Value,
|
||||
body_base64: Option<String>,
|
||||
auth_context: Option<ExecutionRuntimeAuthContext>,
|
||||
) -> GatewayControlPlanRequest {
|
||||
GatewayControlPlanRequest {
|
||||
) -> AiControlPlanRequest {
|
||||
AiControlPlanRequest {
|
||||
trace_id: trace_id.to_string(),
|
||||
method: method.to_string(),
|
||||
path: path.to_string(),
|
||||
@@ -44,11 +44,11 @@ pub fn build_gateway_control_plan_request(
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{build_gateway_control_plan_request, ExecutionRuntimeAuthContext};
|
||||
use super::{build_ai_control_plan_request, ExecutionRuntimeAuthContext};
|
||||
|
||||
#[test]
|
||||
fn build_gateway_control_plan_request_preserves_request_shape() {
|
||||
let payload = build_gateway_control_plan_request(
|
||||
fn build_ai_control_plan_request_preserves_request_shape() {
|
||||
let payload = build_ai_control_plan_request(
|
||||
"trace-123",
|
||||
"POST",
|
||||
"/v1/chat/completions",
|
||||
@@ -9,7 +9,7 @@ pub use actions::{
|
||||
EXECUTION_RUNTIME_SYNC_ACTION, EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
|
||||
};
|
||||
pub use auth_context::ExecutionRuntimeAuthContext;
|
||||
pub use control_payloads::{build_gateway_control_plan_request, GatewayControlPlanRequest};
|
||||
pub use control_payloads::{build_ai_control_plan_request, AiControlPlanRequest};
|
||||
pub use plan_kinds::{
|
||||
is_openai_responses_stream_plan_kind, is_openai_responses_sync_plan_kind,
|
||||
CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND, CLAUDE_CLI_STREAM_PLAN_KIND,
|
||||
@@ -1,14 +1,13 @@
|
||||
pub mod canonical;
|
||||
pub mod context;
|
||||
pub mod conversion;
|
||||
pub mod formats;
|
||||
pub mod matrix;
|
||||
pub mod planner;
|
||||
pub mod proxy;
|
||||
pub mod registry;
|
||||
pub mod stream;
|
||||
extern crate self as aether_ai_formats;
|
||||
|
||||
pub use canonical::{
|
||||
pub mod api;
|
||||
pub mod contracts;
|
||||
pub mod protocol;
|
||||
pub mod provider_compat;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
pub use protocol::canonical::{
|
||||
canonical_request_unknown_block_count, canonical_response_unknown_block_count,
|
||||
canonical_to_claude_request, canonical_to_claude_response, canonical_to_gemini_request,
|
||||
canonical_to_gemini_response, canonical_to_openai_chat_request,
|
||||
@@ -24,16 +23,16 @@ pub use canonical::{
|
||||
CanonicalRole, CanonicalStopReason, CanonicalStreamEvent, CanonicalStreamFrame,
|
||||
CanonicalThinkingConfig, CanonicalToolChoice, CanonicalToolDefinition, CanonicalUsage,
|
||||
};
|
||||
pub use context::{FormatContext, FormatError};
|
||||
pub use formats::{
|
||||
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 matrix::{
|
||||
pub use protocol::matrix::{
|
||||
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 registry::{build_stream_transcoder, convert_request, convert_response};
|
||||
pub use protocol::registry::{build_stream_transcoder, convert_request, convert_response};
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod openai;
|
||||
@@ -3,9 +3,9 @@ use std::collections::BTreeMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::planner::openai::map_thinking_budget_to_openai_reasoning_effort;
|
||||
use crate::request::openai::map_thinking_budget_to_openai_reasoning_effort;
|
||||
|
||||
pub use crate::stream::{CanonicalStreamEvent, CanonicalStreamFrame};
|
||||
pub use crate::protocol::stream::{CanonicalStreamEvent, CanonicalStreamFrame};
|
||||
|
||||
pub(crate) const OPENAI_RESPONSES_EXTENSION_NAMESPACE: &str = "openai_responses";
|
||||
pub(crate) const OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE: &str = "openai_cli";
|
||||
@@ -293,15 +293,15 @@ pub struct CanonicalResponse {
|
||||
}
|
||||
|
||||
pub fn from_openai_chat_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||
crate::formats::openai_chat::request::from_raw(body_json)
|
||||
crate::protocol::formats::openai_chat::request::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn canonical_to_openai_chat_request(canonical: &CanonicalRequest) -> Value {
|
||||
crate::formats::openai_chat::request::to_raw(canonical)
|
||||
crate::protocol::formats::openai_chat::request::to_raw(canonical)
|
||||
}
|
||||
|
||||
pub fn from_openai_responses_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||
crate::formats::openai_responses::request::from_raw(body_json)
|
||||
crate::protocol::formats::openai_responses::request::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub(crate) fn canonical_to_openai_responses_request_with_profile(
|
||||
@@ -310,7 +310,7 @@ pub(crate) fn canonical_to_openai_responses_request_with_profile(
|
||||
upstream_is_stream: bool,
|
||||
compact: bool,
|
||||
) -> Option<Value> {
|
||||
crate::formats::openai_responses::request::to_raw(
|
||||
crate::protocol::formats::openai_responses::request::to_raw(
|
||||
canonical,
|
||||
mapped_model,
|
||||
upstream_is_stream,
|
||||
@@ -339,7 +339,7 @@ pub fn canonical_to_openai_responses_compact_request(
|
||||
}
|
||||
|
||||
pub fn from_claude_to_canonical_request(body_json: &Value) -> Option<CanonicalRequest> {
|
||||
crate::formats::claude_messages::request::from_raw(body_json)
|
||||
crate::protocol::formats::claude_messages::request::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn canonical_to_claude_request(
|
||||
@@ -347,14 +347,18 @@ pub fn canonical_to_claude_request(
|
||||
mapped_model: &str,
|
||||
upstream_is_stream: bool,
|
||||
) -> Option<Value> {
|
||||
crate::formats::claude_messages::request::to_raw(canonical, mapped_model, upstream_is_stream)
|
||||
crate::protocol::formats::claude_messages::request::to_raw(
|
||||
canonical,
|
||||
mapped_model,
|
||||
upstream_is_stream,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_gemini_to_canonical_request(
|
||||
body_json: &Value,
|
||||
request_path: &str,
|
||||
) -> Option<CanonicalRequest> {
|
||||
crate::formats::gemini_generate_content::request::from_raw(body_json, request_path)
|
||||
crate::protocol::formats::gemini_generate_content::request::from_raw(body_json, request_path)
|
||||
}
|
||||
|
||||
pub fn canonical_to_gemini_request(
|
||||
@@ -362,7 +366,7 @@ pub fn canonical_to_gemini_request(
|
||||
mapped_model: &str,
|
||||
upstream_is_stream: bool,
|
||||
) -> Option<Value> {
|
||||
crate::formats::gemini_generate_content::request::to_raw(
|
||||
crate::protocol::formats::gemini_generate_content::request::to_raw(
|
||||
canonical,
|
||||
mapped_model,
|
||||
upstream_is_stream,
|
||||
@@ -370,23 +374,23 @@ pub fn canonical_to_gemini_request(
|
||||
}
|
||||
|
||||
pub fn from_openai_chat_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||
crate::formats::openai_chat::response::from_raw(body_json)
|
||||
crate::protocol::formats::openai_chat::response::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn from_openai_responses_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||
crate::formats::openai_responses::response::from_raw(body_json)
|
||||
crate::protocol::formats::openai_responses::response::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn from_claude_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||
crate::formats::claude_messages::response::from_raw(body_json)
|
||||
crate::protocol::formats::claude_messages::response::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn from_gemini_to_canonical_response(body_json: &Value) -> Option<CanonicalResponse> {
|
||||
crate::formats::gemini_generate_content::response::from_raw(body_json)
|
||||
crate::protocol::formats::gemini_generate_content::response::from_raw(body_json)
|
||||
}
|
||||
|
||||
pub fn canonical_to_openai_chat_response(canonical: &CanonicalResponse) -> Value {
|
||||
crate::formats::openai_chat::response::to_raw(canonical)
|
||||
crate::protocol::formats::openai_chat::response::to_raw(canonical)
|
||||
}
|
||||
|
||||
pub(crate) fn canonical_blocks_to_openai_chat_message(content: &[CanonicalContentBlock]) -> Value {
|
||||
@@ -524,7 +528,7 @@ pub(crate) fn canonical_to_openai_responses_response_with_profile(
|
||||
report_context: &Value,
|
||||
compact: bool,
|
||||
) -> Value {
|
||||
crate::formats::openai_responses::response::to_raw(canonical, report_context, compact)
|
||||
crate::protocol::formats::openai_responses::response::to_raw(canonical, report_context, compact)
|
||||
}
|
||||
|
||||
pub fn canonical_to_openai_responses_response(
|
||||
@@ -542,14 +546,14 @@ pub fn canonical_to_openai_responses_compact_response(
|
||||
}
|
||||
|
||||
pub fn canonical_to_claude_response(canonical: &CanonicalResponse) -> Value {
|
||||
crate::formats::claude_messages::response::to_raw(canonical)
|
||||
crate::protocol::formats::claude_messages::response::to_raw(canonical)
|
||||
}
|
||||
|
||||
pub fn canonical_to_gemini_response(
|
||||
canonical: &CanonicalResponse,
|
||||
report_context: &Value,
|
||||
) -> Option<Value> {
|
||||
crate::formats::gemini_generate_content::response::to_raw(canonical, report_context)
|
||||
crate::protocol::formats::gemini_generate_content::response::to_raw(canonical, report_context)
|
||||
}
|
||||
|
||||
pub fn canonical_unknown_block_count(blocks: &[CanonicalContentBlock]) -> usize {
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{context::FormatContext, registry};
|
||||
use crate::protocol::{context::FormatContext, registry};
|
||||
|
||||
pub fn convert_openai_chat_request_to_claude_request(
|
||||
body_json: &Value,
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{context::FormatContext, registry};
|
||||
use crate::protocol::{context::FormatContext, registry};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct OpenAiResponsesResponseUsage {
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_extension_object_mut, canonical_instructions_to_claude_system,
|
||||
canonical_messages_to_claude, canonical_openai_reasoning_effort,
|
||||
canonical_tool_choice_to_claude, canonical_tools_to_claude, claude_extensions,
|
||||
@@ -11,8 +11,8 @@ use crate::{
|
||||
compact_canonical_claude_messages, insert_f64, namespace_extension_object,
|
||||
CanonicalRequest,
|
||||
},
|
||||
context::FormatContext,
|
||||
planner::openai::{
|
||||
protocol::context::FormatContext,
|
||||
request::openai::{
|
||||
map_openai_reasoning_effort_to_claude_output,
|
||||
map_openai_reasoning_effort_to_thinking_budget,
|
||||
},
|
||||
@@ -3,13 +3,13 @@ use std::collections::BTreeMap;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
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_usage_to_canonical, namespace_extension_object, CanonicalResponse,
|
||||
CanonicalResponseOutput, CanonicalRole,
|
||||
},
|
||||
context::FormatContext,
|
||||
protocol::context::FormatContext,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
apply_gemini_request_extensions, canonical_extension_object_mut,
|
||||
canonical_openai_reasoning_effort, extract_gemini_model_from_path,
|
||||
gemini_contents_to_canonical_messages, gemini_extensions, gemini_generation_config,
|
||||
@@ -14,8 +14,8 @@ use crate::{
|
||||
CanonicalResponseFormat, CanonicalRole, CanonicalToolChoice, CanonicalToolDefinition,
|
||||
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||
},
|
||||
context::FormatContext,
|
||||
planner::openai::map_openai_reasoning_effort_to_gemini_budget,
|
||||
protocol::context::FormatContext,
|
||||
request::openai::map_openai_reasoning_effort_to_gemini_budget,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||
@@ -1,13 +1,13 @@
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_extension_object_mut, gemini_extensions, gemini_part_to_canonical_block,
|
||||
gemini_stop_reason_to_canonical, gemini_usage_to_canonical, CanonicalContentBlock,
|
||||
CanonicalResponse, CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
||||
CanonicalUsage,
|
||||
},
|
||||
context::FormatContext,
|
||||
protocol::context::FormatContext,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||
@@ -134,7 +134,7 @@ fn canonical_to_gemini_response(
|
||||
let outputs = if canonical.outputs.is_empty() {
|
||||
vec![CanonicalResponseOutput {
|
||||
index: 0,
|
||||
role: crate::canonical::CanonicalRole::Assistant,
|
||||
role: crate::protocol::canonical::CanonicalRole::Assistant,
|
||||
content: canonical.content.clone(),
|
||||
stop_reason: canonical.stop_reason.clone(),
|
||||
extensions: Default::default(),
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_extension_object_mut, canonical_message_to_openai_chat,
|
||||
canonical_response_format_to_openai, canonical_tool_choice_to_openai,
|
||||
canonical_tool_to_openai, namespace_extension_object, openai_content_text,
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
CanonicalInstruction, CanonicalRequest, CanonicalRole, CanonicalThinkingConfig,
|
||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||
},
|
||||
context::FormatContext,
|
||||
protocol::context::FormatContext,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||
@@ -61,14 +61,16 @@ pub fn from_raw(body_json: &Value) -> Option<CanonicalRequest> {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
canonical.messages.push(crate::canonical::CanonicalMessage {
|
||||
role,
|
||||
content: openai_message_content_blocks(message_object)?,
|
||||
extensions: openai_extensions(
|
||||
message_object,
|
||||
&["role", "content", "tool_calls", "tool_call_id"],
|
||||
),
|
||||
});
|
||||
canonical
|
||||
.messages
|
||||
.push(crate::protocol::canonical::CanonicalMessage {
|
||||
role,
|
||||
content: openai_message_content_blocks(message_object)?,
|
||||
extensions: openai_extensions(
|
||||
message_object,
|
||||
&["role", "content", "tool_calls", "tool_call_id"],
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ use std::collections::BTreeMap;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_blocks_to_openai_chat_message, canonical_stop_reason_to_openai,
|
||||
canonical_usage_to_openai, openai_extensions, openai_finish_reason_to_canonical,
|
||||
openai_message_content_blocks, openai_usage_to_canonical, CanonicalContentBlock,
|
||||
CanonicalResponse, CanonicalResponseOutput, CanonicalRole,
|
||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||
},
|
||||
context::FormatContext,
|
||||
protocol::context::FormatContext,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_response_format_to_openai, canonicalize_tool_arguments, media_data_or_url,
|
||||
namespace_extension_object, openai_content_text, openai_extensions,
|
||||
openai_response_format_to_canonical, openai_responses_extension,
|
||||
@@ -11,8 +11,8 @@ use crate::{
|
||||
CanonicalThinkingConfig, CanonicalToolChoice, CanonicalToolDefinition,
|
||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||
},
|
||||
context::FormatContext,
|
||||
planner::openai::map_thinking_budget_to_openai_reasoning_effort,
|
||||
protocol::context::FormatContext,
|
||||
request::openai::map_thinking_budget_to_openai_reasoning_effort,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalRequest> {
|
||||
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::{
|
||||
canonical::{
|
||||
protocol::canonical::{
|
||||
canonical_content_block_to_openai_responses_part,
|
||||
canonical_usage_to_openai_responses_usage, canonicalize_tool_arguments,
|
||||
flush_openai_responses_message_item, namespace_extension_object,
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
CanonicalResponseOutput, CanonicalRole, CanonicalStopReason,
|
||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE, OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||
},
|
||||
context::FormatContext,
|
||||
protocol::context::FormatContext,
|
||||
};
|
||||
|
||||
pub fn from(body: &Value, _ctx: &FormatContext) -> Option<CanonicalResponse> {
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
api_format_alias_matches,
|
||||
formats::{is_openai_responses_compact_format, normalize_api_format_alias},
|
||||
protocol::formats::{is_openai_responses_compact_format, normalize_api_format_alias},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
7
crates/aether-ai-formats/src/protocol/mod.rs
Normal file
7
crates/aether-ai-formats/src/protocol/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod canonical;
|
||||
pub mod context;
|
||||
pub mod conversion;
|
||||
pub mod formats;
|
||||
pub mod matrix;
|
||||
pub mod registry;
|
||||
pub mod stream;
|
||||
@@ -1,11 +1,13 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
canonical::{CanonicalRequest, CanonicalResponse},
|
||||
formats::{claude_messages, gemini_generate_content, openai_chat, openai_responses, FormatId},
|
||||
protocol::canonical::{CanonicalRequest, CanonicalResponse},
|
||||
protocol::formats::{
|
||||
claude_messages, gemini_generate_content, openai_chat, openai_responses, FormatId,
|
||||
},
|
||||
};
|
||||
|
||||
pub use crate::context::{FormatContext, FormatError};
|
||||
pub use crate::protocol::context::{FormatContext, FormatError};
|
||||
|
||||
pub fn parse_request(
|
||||
source_format: &str,
|
||||
@@ -144,7 +146,7 @@ mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::{convert_request, FormatContext};
|
||||
use crate::formats::FormatId;
|
||||
use crate::protocol::formats::FormatId;
|
||||
|
||||
#[test]
|
||||
fn openai_cli_alias_is_not_a_primary_format() {
|
||||
@@ -184,7 +186,7 @@ mod tests {
|
||||
] {
|
||||
assert!(
|
||||
!implementation.contains(forbidden),
|
||||
"registry should dispatch through formats::<format> adapters, found {forbidden}"
|
||||
"registry should dispatch through protocol::formats::<format> adapters, found {forbidden}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use super::{
|
||||
AwsEventFrame, AwsHeaderValue, AwsHeaders, EventStreamDecoder, FrameParseError,
|
||||
MAX_BUFFER_SIZE, MAX_ERRORS, MAX_MESSAGE_SIZE,
|
||||
};
|
||||
use crate::adaptation::kiro_stream::kiro_crc32 as crc32;
|
||||
use crate::provider_compat::kiro_stream::kiro_crc32 as crc32;
|
||||
|
||||
impl EventStreamDecoder {
|
||||
pub(super) fn feed(&mut self, data: &[u8]) -> Result<(), String> {
|
||||
@@ -1,12 +1,12 @@
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::adaptation::kiro_stream::{
|
||||
use crate::provider_compat::kiro_stream::{
|
||||
calculate_kiro_context_input_tokens, encode_kiro_sse_events, estimate_kiro_tokens,
|
||||
find_kiro_real_thinking_end_tag, find_kiro_real_thinking_end_tag_at_buffer_end,
|
||||
find_kiro_real_thinking_start_tag, KIRO_MAX_THINKING_BUFFER,
|
||||
};
|
||||
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
use super::super::AwsEventFrame;
|
||||
use super::super::KiroClaudeStreamState;
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::adaptation::kiro_stream::{
|
||||
use crate::provider_compat::kiro_stream::{
|
||||
build_kiro_final_message_sse_events, encode_kiro_sse_events,
|
||||
find_kiro_real_thinking_end_tag_at_buffer_end,
|
||||
};
|
||||
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
use super::super::KiroClaudeStreamState;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::adaptation::kiro_stream::{
|
||||
use crate::provider_compat::kiro_stream::{
|
||||
build_kiro_initial_sse_events, build_kiro_stream_error_sse_events, encode_kiro_sse_events,
|
||||
};
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
use super::super::{EventStreamDecoder, KiroClaudeStreamState, KiroToClaudeCliStreamState};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::adaptation::kiro_stream::kiro_crc32 as crc32;
|
||||
use crate::provider_compat::kiro_stream::kiro_crc32 as crc32;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use super::KiroToClaudeCliStreamState;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod kiro_stream;
|
||||
pub mod private_envelope;
|
||||
pub mod proxy;
|
||||
pub mod surfaces;
|
||||
@@ -2,8 +2,8 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::adaptation::kiro_stream::KiroToClaudeCliStreamState;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
use super::surfaces::{
|
||||
provider_adaptation_allows_sync_finalize_envelope, provider_adaptation_descriptor_for_envelope,
|
||||
1
crates/aether-ai-formats/src/request/matrix.rs
Normal file
1
crates/aether-ai-formats/src/request/matrix.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub use crate::request::standard::matrix::build_standard_request_body_from_canonical;
|
||||
@@ -14,7 +14,7 @@ use crate::contracts::{
|
||||
OPENAI_VIDEO_CREATE_SYNC_PLAN_KIND, OPENAI_VIDEO_DELETE_SYNC_PLAN_KIND,
|
||||
OPENAI_VIDEO_REMIX_SYNC_PLAN_KIND,
|
||||
};
|
||||
use crate::planner::specialized::image::is_openai_image_stream_request;
|
||||
use crate::request::specialized::image::is_openai_image_stream_request;
|
||||
|
||||
pub fn resolve_execution_runtime_stream_plan_kind(
|
||||
route_class: Option<&str>,
|
||||
@@ -313,7 +313,7 @@ pub fn is_matching_stream_http_request(
|
||||
is_matching_stream_request(plan_kind, parts.uri.path(), body_json)
|
||||
}
|
||||
|
||||
pub fn supports_sync_scheduler_decision_kind(plan_kind: &str) -> bool {
|
||||
pub fn supports_sync_execution_decision_kind(plan_kind: &str) -> bool {
|
||||
matches!(
|
||||
plan_kind,
|
||||
OPENAI_CHAT_SYNC_PLAN_KIND
|
||||
@@ -337,7 +337,7 @@ pub fn supports_sync_scheduler_decision_kind(plan_kind: &str) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn supports_stream_scheduler_decision_kind(plan_kind: &str) -> bool {
|
||||
pub fn supports_stream_execution_decision_kind(plan_kind: &str) -> bool {
|
||||
matches!(
|
||||
plan_kind,
|
||||
OPENAI_CHAT_STREAM_PLAN_KIND
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
use super::{
|
||||
is_matching_stream_http_request, is_matching_stream_request,
|
||||
resolve_execution_runtime_stream_plan_kind, resolve_execution_runtime_sync_plan_kind,
|
||||
supports_stream_scheduler_decision_kind, supports_sync_scheduler_decision_kind,
|
||||
supports_stream_execution_decision_kind, supports_sync_execution_decision_kind,
|
||||
};
|
||||
use crate::contracts::{
|
||||
OPENAI_CHAT_STREAM_PLAN_KIND, OPENAI_CHAT_SYNC_PLAN_KIND, OPENAI_IMAGE_STREAM_PLAN_KIND,
|
||||
@@ -426,10 +426,10 @@ mod tests {
|
||||
),
|
||||
Some(OPENAI_RESPONSES_SYNC_PLAN_KIND)
|
||||
);
|
||||
assert!(supports_sync_scheduler_decision_kind(
|
||||
assert!(supports_sync_execution_decision_kind(
|
||||
OPENAI_RESPONSES_SYNC_PLAN_KIND
|
||||
));
|
||||
assert!(supports_stream_scheduler_decision_kind(
|
||||
assert!(supports_stream_execution_decision_kind(
|
||||
OPENAI_RESPONSES_STREAM_PLAN_KIND
|
||||
));
|
||||
}
|
||||
@@ -466,10 +466,10 @@ mod tests {
|
||||
),
|
||||
Some(OPENAI_RESPONSES_COMPACT_SYNC_PLAN_KIND)
|
||||
);
|
||||
assert!(supports_sync_scheduler_decision_kind(
|
||||
assert!(supports_sync_execution_decision_kind(
|
||||
OPENAI_RESPONSES_COMPACT_SYNC_PLAN_KIND
|
||||
));
|
||||
assert!(supports_stream_scheduler_decision_kind(
|
||||
assert!(supports_stream_execution_decision_kind(
|
||||
OPENAI_RESPONSES_COMPACT_STREAM_PLAN_KIND
|
||||
));
|
||||
}
|
||||
@@ -486,10 +486,10 @@ mod tests {
|
||||
"/v1/chat/completions",
|
||||
&serde_json::json!({"stream": true}),
|
||||
));
|
||||
assert!(supports_sync_scheduler_decision_kind(
|
||||
assert!(supports_sync_execution_decision_kind(
|
||||
OPENAI_CHAT_SYNC_PLAN_KIND
|
||||
));
|
||||
assert!(supports_stream_scheduler_decision_kind(
|
||||
assert!(supports_stream_execution_decision_kind(
|
||||
OPENAI_CHAT_STREAM_PLAN_KIND
|
||||
));
|
||||
}
|
||||
@@ -526,7 +526,7 @@ mod tests {
|
||||
),
|
||||
Some(OPENAI_IMAGE_SYNC_PLAN_KIND)
|
||||
);
|
||||
assert!(supports_sync_scheduler_decision_kind(
|
||||
assert!(supports_sync_execution_decision_kind(
|
||||
OPENAI_IMAGE_SYNC_PLAN_KIND
|
||||
));
|
||||
}
|
||||
@@ -553,7 +553,7 @@ mod tests {
|
||||
),
|
||||
Some(OPENAI_IMAGE_STREAM_PLAN_KIND)
|
||||
);
|
||||
assert!(supports_stream_scheduler_decision_kind(
|
||||
assert!(supports_stream_execution_decision_kind(
|
||||
OPENAI_IMAGE_STREAM_PLAN_KIND
|
||||
));
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use base64::Engine as _;
|
||||
use serde_json::{json, Map, Number, Value};
|
||||
|
||||
use crate::contracts::{OPENAI_IMAGE_STREAM_PLAN_KIND, OPENAI_IMAGE_SYNC_PLAN_KIND};
|
||||
use crate::planner::standard::{
|
||||
use crate::request::standard::{
|
||||
CODEX_OPENAI_IMAGE_DEFAULT_MODEL, CODEX_OPENAI_IMAGE_DEFAULT_VARIATION_MODEL,
|
||||
};
|
||||
|
||||
@@ -808,7 +808,7 @@ mod tests {
|
||||
normalize_openai_image_request, resolve_stream_spec, resolve_sync_spec,
|
||||
OpenAiImageOperation,
|
||||
};
|
||||
use crate::planner::standard::{
|
||||
use crate::request::standard::{
|
||||
apply_codex_openai_responses_special_body_edits, CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::contracts::{CLAUDE_CHAT_STREAM_PLAN_KIND, CLAUDE_CHAT_SYNC_PLAN_KIND};
|
||||
use crate::planner::standard::family::{
|
||||
use crate::request::standard::family::{
|
||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::contracts::{CLAUDE_CLI_STREAM_PLAN_KIND, CLAUDE_CLI_SYNC_PLAN_KIND};
|
||||
use crate::planner::standard::family::{
|
||||
use crate::request::standard::family::{
|
||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod chat;
|
||||
pub mod cli;
|
||||
|
||||
use crate::planner::standard::LocalStandardSpec;
|
||||
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))
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
use aether_ai_formats::proxy::rules::body_rules_handle_path;
|
||||
use aether_ai_formats::provider_compat::proxy::rules::body_rules_handle_path;
|
||||
use serde_json::{json, Value};
|
||||
use sha1::{Digest as Sha1Digest, Sha1};
|
||||
use sha2::Sha256;
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::contracts::{GEMINI_CHAT_STREAM_PLAN_KIND, GEMINI_CHAT_SYNC_PLAN_KIND};
|
||||
use crate::planner::standard::family::{
|
||||
use crate::request::standard::family::{
|
||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::contracts::{GEMINI_CLI_STREAM_PLAN_KIND, GEMINI_CLI_SYNC_PLAN_KIND};
|
||||
use crate::planner::standard::family::{
|
||||
use crate::request::standard::family::{
|
||||
LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod chat;
|
||||
pub mod cli;
|
||||
|
||||
use crate::planner::standard::LocalStandardSpec;
|
||||
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))
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use aether_ai_formats::conversion::request::{
|
||||
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_openai_responses_request,
|
||||
normalize_claude_request_to_openai_chat_request,
|
||||
normalize_gemini_request_to_openai_chat_request,
|
||||
normalize_openai_responses_request_to_openai_chat_request,
|
||||
};
|
||||
use aether_ai_formats::proxy::rules::apply_local_body_rules;
|
||||
use aether_ai_formats::registry::{convert_request, FormatContext};
|
||||
use aether_ai_formats::protocol::registry::{convert_request, FormatContext};
|
||||
use aether_ai_formats::provider_compat::proxy::rules::apply_local_body_rules;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{
|
||||
@@ -1,4 +1,4 @@
|
||||
use aether_ai_formats::conversion::request::{
|
||||
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_openai_responses_request,
|
||||
normalize_openai_responses_request_to_openai_chat_request,
|
||||
@@ -28,6 +28,20 @@ pub fn canonicalize_tool_arguments(value: Option<Value>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_empty_pages_from_tool_arguments(arguments: &str) -> String {
|
||||
let Ok(mut value) = serde_json::from_str::<Value>(arguments) else {
|
||||
return arguments.to_string();
|
||||
};
|
||||
let Some(object) = value.as_object_mut() else {
|
||||
return arguments.to_string();
|
||||
};
|
||||
if object.get("pages").and_then(Value::as_str) != Some("") {
|
||||
return arguments.to_string();
|
||||
}
|
||||
object.remove("pages");
|
||||
serde_json::to_string(&value).unwrap_or_else(|_| arguments.to_string())
|
||||
}
|
||||
|
||||
pub fn prepare_local_success_response_parts(
|
||||
headers: &BTreeMap<String, String>,
|
||||
body_json: &Value,
|
||||
@@ -116,7 +130,7 @@ mod tests {
|
||||
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,
|
||||
remove_empty_pages_from_tool_arguments, LocalSyncReportParts,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -134,6 +148,24 @@ mod tests {
|
||||
assert_eq!(canonicalize_tool_arguments(None), "{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_empty_pages_from_tool_arguments() {
|
||||
assert_eq!(
|
||||
remove_empty_pages_from_tool_arguments(
|
||||
r#"{"file_path":"/tmp/a.txt","offset":1,"limit":20,"pages":""}"#
|
||||
),
|
||||
r#"{"file_path":"/tmp/a.txt","offset":1,"limit":20}"#
|
||||
);
|
||||
assert_eq!(
|
||||
remove_empty_pages_from_tool_arguments(r#"{"pages":"1-2"}"#),
|
||||
r#"{"pages":"1-2"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
remove_empty_pages_from_tool_arguments(r#"{"pages":"#),
|
||||
r#"{"pages":"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_local_success_response_parts_normalizes_headers() {
|
||||
let headers = BTreeMap::from([
|
||||
@@ -2,9 +2,9 @@ use base64::Engine as _;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::contracts::OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND;
|
||||
use crate::finalize::sse::encode_json_sse;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::planner::standard::CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT;
|
||||
use crate::request::standard::CODEX_OPENAI_IMAGE_DEFAULT_OUTPUT_FORMAT;
|
||||
use crate::response::sse::encode_json_sse;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OpenAiImageStreamState {
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
pub fn map_claude_stop_reason(
|
||||
stop_reason: Option<&str>,
|
||||
@@ -2,10 +2,13 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::finalize::common::{build_generated_tool_call_id, canonicalize_tool_arguments};
|
||||
use crate::finalize::sse::{encode_json_sse, map_claude_stop_reason};
|
||||
use crate::finalize::standard::stream_core::common::*;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::common::{
|
||||
build_generated_tool_call_id, canonicalize_tool_arguments,
|
||||
remove_empty_pages_from_tool_arguments,
|
||||
};
|
||||
use crate::response::sse::{encode_json_sse, map_claude_stop_reason};
|
||||
use crate::response::standard::stream_core::common::*;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClaudeProviderToolState {
|
||||
@@ -658,6 +661,10 @@ impl ClaudeClientEmitter {
|
||||
Ok(out)
|
||||
}
|
||||
CanonicalStreamEvent::ToolCallArgumentsDelta { index, arguments } => {
|
||||
let arguments = remove_empty_pages_from_tool_arguments(&arguments);
|
||||
if arguments.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let mut out = self.ensure_started()?;
|
||||
let state = self.tool_states.entry(index).or_default();
|
||||
let call_id = if state.call_id.is_empty() {
|
||||
@@ -1195,6 +1202,39 @@ mod tests {
|
||||
assert!(sse.contains("\"usage\":{\"input_tokens\":0,\"output_tokens\":0}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claude_client_emitter_removes_empty_pages_from_tool_arguments() {
|
||||
let mut emitter = ClaudeClientEmitter::default();
|
||||
let mut bytes = emitter
|
||||
.emit(CanonicalStreamFrame {
|
||||
id: "msg_123".to_string(),
|
||||
model: "claude-sonnet-4-5".to_string(),
|
||||
event: CanonicalStreamEvent::ToolCallStart {
|
||||
index: 0,
|
||||
call_id: "toolu_read".to_string(),
|
||||
name: "Read".to_string(),
|
||||
},
|
||||
})
|
||||
.expect("tool start should encode");
|
||||
bytes.extend(
|
||||
emitter
|
||||
.emit(CanonicalStreamFrame {
|
||||
id: "msg_123".to_string(),
|
||||
model: "claude-sonnet-4-5".to_string(),
|
||||
event: CanonicalStreamEvent::ToolCallArgumentsDelta {
|
||||
index: 0,
|
||||
arguments: r#"{"file_path":"/tmp/a.txt","offset":1,"limit":20,"pages":""}"#
|
||||
.to_string(),
|
||||
},
|
||||
})
|
||||
.expect("tool delta should encode"),
|
||||
);
|
||||
|
||||
let sse = String::from_utf8(bytes).expect("sse should be utf8");
|
||||
assert!(sse.contains("\"partial_json\":\"{\\\"file_path\\\":\\\"/tmp/a.txt\\\",\\\"offset\\\":1,\\\"limit\\\":20}\""));
|
||||
assert!(!sse.contains("\\\"pages\\\":\\\"\\\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claude_client_emitter_injects_default_usage_into_finish_events() {
|
||||
let mut emitter = ClaudeClientEmitter::default();
|
||||
@@ -2,10 +2,10 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::finalize::common::{build_generated_tool_call_id, canonicalize_tool_arguments};
|
||||
use crate::finalize::sse::encode_json_sse;
|
||||
use crate::finalize::standard::stream_core::common::*;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::common::{build_generated_tool_call_id, canonicalize_tool_arguments};
|
||||
use crate::response::sse::encode_json_sse;
|
||||
use crate::response::standard::stream_core::common::*;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Default)]
|
||||
struct GeminiProviderToolState {
|
||||
@@ -2,10 +2,10 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::finalize::common::build_generated_tool_call_id;
|
||||
use crate::finalize::sse::{encode_done_sse, encode_json_sse};
|
||||
use crate::finalize::standard::stream_core::common::*;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::common::build_generated_tool_call_id;
|
||||
use crate::response::sse::{encode_done_sse, encode_json_sse};
|
||||
use crate::response::standard::stream_core::common::*;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Default)]
|
||||
struct OpenAIChatProviderToolState {
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
pub use aether_ai_formats::stream::{
|
||||
pub use aether_ai_formats::protocol::stream::{
|
||||
CanonicalContentPart, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalUsage,
|
||||
};
|
||||
|
||||
@@ -2,20 +2,20 @@ use aether_ai_formats::FormatId;
|
||||
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::finalize::error_body::{
|
||||
use crate::response::error_body::{
|
||||
build_core_error_body_for_client_format, LocalCoreSyncErrorKind,
|
||||
};
|
||||
use crate::finalize::sse::encode_json_sse;
|
||||
use crate::finalize::standard::claude::stream::{ClaudeClientEmitter, ClaudeProviderState};
|
||||
use crate::finalize::standard::gemini::stream::{GeminiClientEmitter, GeminiProviderState};
|
||||
use crate::finalize::standard::openai::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,
|
||||
OpenAIResponsesProviderState,
|
||||
};
|
||||
use crate::finalize::standard::stream_core::common::{
|
||||
use crate::response::standard::stream_core::common::{
|
||||
decode_json_data_line, CanonicalStreamEvent, CanonicalStreamFrame, CanonicalUsage,
|
||||
};
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StreamingStandardFormatMatrix {
|
||||
@@ -1,13 +1,13 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::adaptation::kiro_stream::KiroToClaudeCliStreamState;
|
||||
use crate::adaptation::private_envelope::transform_provider_private_stream_line;
|
||||
use crate::adaptation::surfaces::{
|
||||
use crate::provider_compat::kiro_stream::KiroToClaudeCliStreamState;
|
||||
use crate::provider_compat::private_envelope::transform_provider_private_stream_line;
|
||||
use crate::provider_compat::surfaces::{
|
||||
provider_adaptation_should_unwrap_stream_envelope, KIRO_ENVELOPE_NAME,
|
||||
};
|
||||
use crate::finalize::openai_image_stream::OpenAiImageStreamState;
|
||||
use crate::finalize::standard::stream_core::StreamingStandardFormatMatrix;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::openai_image_stream::OpenAiImageStreamState;
|
||||
use crate::response::standard::stream_core::StreamingStandardFormatMatrix;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FinalizeStreamRewriteMode {
|
||||
@@ -1,13 +1,13 @@
|
||||
use base64::Engine as _;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_ai_formats::conversion::response::{
|
||||
use aether_ai_formats::protocol::conversion::response::{
|
||||
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_openai_responses,
|
||||
convert_openai_responses_response_to_openai_chat,
|
||||
};
|
||||
use aether_ai_formats::registry::{convert_response, FormatContext};
|
||||
use aether_ai_formats::protocol::registry::{convert_response, FormatContext};
|
||||
use aether_ai_formats::{
|
||||
canonical_to_claude_response, canonical_to_gemini_response, canonical_to_openai_chat_response,
|
||||
canonical_to_openai_responses_compact_response, canonical_to_openai_responses_response,
|
||||
@@ -18,8 +18,9 @@ use aether_ai_formats::{
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use super::AiSurfaceFinalizeError;
|
||||
use crate::finalize::standard::gemini::stream::GeminiProviderState;
|
||||
use crate::finalize::standard::stream_core::common::{
|
||||
use crate::response::common::remove_empty_pages_from_tool_arguments;
|
||||
use crate::response::standard::gemini::stream::GeminiProviderState;
|
||||
use crate::response::standard::stream_core::common::{
|
||||
map_openai_finish_reason_to_gemini, parse_json_arguments_value, CanonicalContentPart,
|
||||
CanonicalStreamEvent, CanonicalUsage,
|
||||
};
|
||||
@@ -2363,8 +2364,9 @@ pub fn aggregate_claude_stream_sync_response(body: &[u8]) -> Option<Value> {
|
||||
}
|
||||
"tool_use" => {
|
||||
if !state.partial_json.is_empty() {
|
||||
let input = serde_json::from_str::<Value>(&state.partial_json)
|
||||
.unwrap_or(Value::String(state.partial_json));
|
||||
let arguments = remove_empty_pages_from_tool_arguments(&state.partial_json);
|
||||
let input = serde_json::from_str::<Value>(&arguments)
|
||||
.unwrap_or(Value::String(arguments));
|
||||
block.insert("input".to_string(), input);
|
||||
}
|
||||
}
|
||||
@@ -2873,7 +2875,7 @@ mod tests {
|
||||
maybe_build_standard_sync_finalize_product_from_normalized_payload,
|
||||
StandardSyncFinalizeNormalizedProduct,
|
||||
};
|
||||
use aether_ai_formats::conversion::response::{
|
||||
use aether_ai_formats::protocol::conversion::response::{
|
||||
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_openai_responses,
|
||||
@@ -2909,6 +2911,35 @@ mod tests {
|
||||
assert_eq!(aggregated["usage"]["output_tokens"], 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aggregates_claude_stream_removes_empty_pages_from_tool_input() {
|
||||
let body = concat!(
|
||||
"event: message_start\n",
|
||||
"data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_123\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null}}\n\n",
|
||||
"event: content_block_start\n",
|
||||
"data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_read\",\"name\":\"Read\",\"input\":{}}}\n\n",
|
||||
"event: content_block_delta\n",
|
||||
"data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"file_path\\\":\\\"/tmp/a.txt\\\",\\\"offset\\\":1,\\\"limit\\\":20,\\\"pages\\\":\\\"\\\"}\"}}\n\n",
|
||||
"event: content_block_stop\n",
|
||||
"data: {\"type\":\"content_block_stop\",\"index\":0}\n\n",
|
||||
"event: message_stop\n",
|
||||
"data: {\"type\":\"message_stop\"}\n\n",
|
||||
);
|
||||
|
||||
let aggregated =
|
||||
aggregate_claude_stream_sync_response(body.as_bytes()).expect("body should aggregate");
|
||||
|
||||
assert_eq!(aggregated["content"][0]["type"], "tool_use");
|
||||
assert_eq!(
|
||||
aggregated["content"][0]["input"],
|
||||
json!({
|
||||
"file_path": "/tmp/a.txt",
|
||||
"offset": 1,
|
||||
"limit": 20,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aggregates_gemini_stream_deltas_media_and_signatures_into_sync_body() {
|
||||
let body = concat!(
|
||||
@@ -1,18 +1,18 @@
|
||||
use aether_ai_formats::conversion::response::{
|
||||
use aether_ai_formats::protocol::conversion::response::{
|
||||
convert_claude_response_to_openai_responses, convert_gemini_response_to_openai_responses,
|
||||
convert_openai_chat_response_to_openai_responses,
|
||||
};
|
||||
use aether_contracts::{ExecutionStreamTerminalSummary, StandardizedUsage};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::finalize::sse::encode_json_sse;
|
||||
use crate::finalize::standard::claude::stream::ClaudeClientEmitter;
|
||||
use crate::finalize::standard::gemini::stream::GeminiClientEmitter;
|
||||
use crate::finalize::standard::openai::stream::{
|
||||
use crate::response::sse::encode_json_sse;
|
||||
use crate::response::standard::claude::stream::ClaudeClientEmitter;
|
||||
use crate::response::standard::gemini::stream::GeminiClientEmitter;
|
||||
use crate::response::standard::openai::stream::{
|
||||
OpenAIChatClientEmitter, OpenAIResponsesClientEmitter, OpenAIResponsesProviderState,
|
||||
};
|
||||
use crate::finalize::standard::stream_core::CanonicalStreamFrame;
|
||||
use crate::finalize::AiSurfaceFinalizeError;
|
||||
use crate::response::standard::stream_core::CanonicalStreamFrame;
|
||||
use crate::response::AiSurfaceFinalizeError;
|
||||
|
||||
pub struct SyncToStreamBridgeOutcome {
|
||||
pub sse_body: Vec<u8>,
|
||||
@@ -8,7 +8,6 @@ description = "AI serving application contracts and ports for Aether"
|
||||
|
||||
[dependencies]
|
||||
aether-ai-formats.workspace = true
|
||||
aether-ai-surfaces.workspace = true
|
||||
aether-contracts.workspace = true
|
||||
aether-scheduler-core.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_ai_surfaces::api::ExecutionRuntimeAuthContext;
|
||||
use aether_ai_formats::api::ExecutionRuntimeAuthContext;
|
||||
use aether_contracts::{ExecutionPlan, RequestBody};
|
||||
use url::Url;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_ai_surfaces::api::{
|
||||
use aether_ai_formats::api::{
|
||||
ExecutionRuntimeAuthContext, EXECUTION_RUNTIME_STREAM_DECISION_ACTION,
|
||||
EXECUTION_RUNTIME_SYNC_DECISION_ACTION,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_ai_surfaces::api::ExecutionRuntimeAuthContext;
|
||||
use aether_ai_formats::api::ExecutionRuntimeAuthContext;
|
||||
use aether_contracts::{ExecutionPlan, ExecutionTimeouts, ProxySnapshot};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use aether_ai_surfaces::api::{
|
||||
use aether_ai_formats::api::{
|
||||
ExecutionRuntimeAuthContext, EXECUTION_RUNTIME_STREAM_ACTION, EXECUTION_RUNTIME_SYNC_ACTION,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_ai_surfaces::api::ExecutionRuntimeAuthContext;
|
||||
use aether_ai_formats::api::ExecutionRuntimeAuthContext;
|
||||
use aether_scheduler_core::SchedulerRankingOutcome;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use aether_ai_surfaces::api::is_openai_responses_family_format;
|
||||
use aether_ai_formats::api::is_openai_responses_family_format;
|
||||
|
||||
use crate::{CandidateFailureDiagnostic, CandidateFailureDiagnosticKind};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use aether_ai_surfaces::api::{
|
||||
use aether_ai_formats::api::{
|
||||
LocalGeminiFilesSpec, LocalOpenAiImageSpec, LocalOpenAiResponsesSpec,
|
||||
LocalSameFormatProviderFamily, LocalSameFormatProviderSpec, LocalStandardSourceFamily,
|
||||
LocalStandardSpec, LocalVideoCreateFamily, LocalVideoCreateSpec,
|
||||
@@ -156,7 +156,7 @@ pub fn extract_ai_requested_model_from_request_path(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aether_ai_surfaces::api::{LocalStandardSourceMode, OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND};
|
||||
use aether_ai_formats::api::{LocalStandardSourceMode, OPENAI_CHAT_SYNC_SUCCESS_REPORT_KIND};
|
||||
|
||||
#[test]
|
||||
fn standard_spec_metadata_maps_model_family_and_report_kind() {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
[package]
|
||||
name = "aether-ai-surfaces"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Pure AI API surface contracts, routing, adaptation, and finalize logic for Aether"
|
||||
|
||||
[dependencies]
|
||||
aether-ai-formats.workspace = true
|
||||
aether-contracts.workspace = true
|
||||
base64.workspace = true
|
||||
http.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sha1 = "0.10"
|
||||
sha2.workspace = true
|
||||
uuid.workspace = true
|
||||
@@ -1,5 +0,0 @@
|
||||
pub mod adaptation;
|
||||
pub mod api;
|
||||
pub mod contracts;
|
||||
pub mod finalize;
|
||||
pub mod planner;
|
||||
@@ -1 +0,0 @@
|
||||
pub use crate::planner::standard::matrix::build_standard_request_body_from_canonical;
|
||||
@@ -1,104 +0,0 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
pub fn parse_openai_stop_sequences(stop: Option<&Value>) -> Option<Vec<Value>> {
|
||||
match stop {
|
||||
Some(Value::String(value)) if !value.trim().is_empty() => {
|
||||
Some(vec![Value::String(value.clone())])
|
||||
}
|
||||
Some(Value::Array(values)) => Some(
|
||||
values
|
||||
.iter()
|
||||
.filter_map(|value| value.as_str())
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| Value::String(value.to_string()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.filter(|values| !values.is_empty()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_openai_chat_max_tokens(request: &Map<String, Value>) -> u64 {
|
||||
request
|
||||
.get("max_completion_tokens")
|
||||
.and_then(value_as_u64)
|
||||
.or_else(|| request.get("max_tokens").and_then(value_as_u64))
|
||||
.unwrap_or(4096)
|
||||
}
|
||||
|
||||
pub fn value_as_u64(value: &Value) -> Option<u64> {
|
||||
value
|
||||
.as_u64()
|
||||
.or_else(|| value.as_i64().and_then(|value| u64::try_from(value).ok()))
|
||||
}
|
||||
|
||||
pub fn copy_request_number_field(
|
||||
request: &Map<String, Value>,
|
||||
target: &mut Map<String, Value>,
|
||||
key: &str,
|
||||
) {
|
||||
copy_request_number_field_as(request, target, key, key);
|
||||
}
|
||||
|
||||
pub fn copy_request_number_field_as(
|
||||
request: &Map<String, Value>,
|
||||
target: &mut Map<String, Value>,
|
||||
source_key: &str,
|
||||
target_key: &str,
|
||||
) {
|
||||
if let Some(value) = request.get(source_key).cloned() {
|
||||
if value.is_number() {
|
||||
target.insert(target_key.to_string(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_openai_reasoning_effort_to_claude_output(value: &str) -> Option<&'static str> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"low" => Some("low"),
|
||||
"medium" => Some("medium"),
|
||||
"high" => Some("high"),
|
||||
"xhigh" => Some("max"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_openai_reasoning_effort_to_thinking_budget(value: &str) -> Option<u64> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"low" => Some(1280),
|
||||
"medium" => Some(2048),
|
||||
"high" => Some(4096),
|
||||
"xhigh" => Some(8192),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_openai_reasoning_effort_to_gemini_budget(value: &str) -> Option<u64> {
|
||||
map_openai_reasoning_effort_to_thinking_budget(value)
|
||||
}
|
||||
|
||||
pub fn map_thinking_budget_to_openai_reasoning_effort(value: u64) -> &'static str {
|
||||
match value {
|
||||
0..=1664 => "low",
|
||||
1665..=3072 => "medium",
|
||||
3073..=6144 => "high",
|
||||
_ => "xhigh",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_openai_reasoning_effort(request: &Map<String, Value>) -> Option<String> {
|
||||
request
|
||||
.get("reasoning_effort")
|
||||
.and_then(Value::as_str)
|
||||
.or_else(|| {
|
||||
request
|
||||
.get("reasoning")
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|reasoning| reasoning.get("effort"))
|
||||
.and_then(Value::as_str)
|
||||
})
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| value.to_ascii_lowercase())
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use aether_ai_formats::matrix::{
|
||||
use aether_ai_formats::normalize_api_format_alias;
|
||||
use aether_ai_formats::protocol::matrix::{
|
||||
request_conversion_kind, request_conversion_requires_enable_flag, RequestConversionKind,
|
||||
};
|
||||
use aether_ai_formats::normalize_api_format_alias;
|
||||
|
||||
use crate::auth::{
|
||||
resolve_local_gemini_auth, resolve_local_openai_bearer_auth, resolve_local_standard_auth,
|
||||
@@ -336,7 +336,7 @@ mod tests {
|
||||
request_conversion_transport_supported, request_pair_allowed_for_transport,
|
||||
CandidateTransportPolicyFacts,
|
||||
};
|
||||
use aether_ai_formats::matrix::RequestConversionKind;
|
||||
use aether_ai_formats::protocol::matrix::RequestConversionKind;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::snapshot::{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user