mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +08:00
refactor ai serving modules and crates
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::{ConversionMode, ExecutionStrategy};
|
||||
|
||||
pub struct AiCandidateMetadataParts<'a> {
|
||||
pub provider_api_format: &'a str,
|
||||
pub client_api_format: &'a str,
|
||||
pub global_model_id: &'a str,
|
||||
pub global_model_name: &'a str,
|
||||
pub model_id: &'a str,
|
||||
pub selected_provider_model_name: &'a str,
|
||||
pub mapping_matched_model: Option<&'a str>,
|
||||
pub provider_name: &'a str,
|
||||
pub key_name: &'a str,
|
||||
pub extra_fields: Map<String, Value>,
|
||||
}
|
||||
|
||||
pub fn build_ai_candidate_metadata(parts: AiCandidateMetadataParts<'_>) -> Value {
|
||||
let mut object = Map::new();
|
||||
object.insert(
|
||||
"provider_api_format".to_string(),
|
||||
Value::String(parts.provider_api_format.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"client_api_format".to_string(),
|
||||
Value::String(parts.client_api_format.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"global_model_id".to_string(),
|
||||
Value::String(parts.global_model_id.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"global_model_name".to_string(),
|
||||
Value::String(parts.global_model_name.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"model_id".to_string(),
|
||||
Value::String(parts.model_id.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"selected_provider_model_name".to_string(),
|
||||
Value::String(parts.selected_provider_model_name.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"mapping_matched_model".to_string(),
|
||||
parts
|
||||
.mapping_matched_model
|
||||
.map(|value| Value::String(value.to_string()))
|
||||
.unwrap_or(Value::Null),
|
||||
);
|
||||
object.insert(
|
||||
"provider_name".to_string(),
|
||||
Value::String(parts.provider_name.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"key_name".to_string(),
|
||||
Value::String(parts.key_name.to_string()),
|
||||
);
|
||||
object.extend(parts.extra_fields);
|
||||
Value::Object(object)
|
||||
}
|
||||
|
||||
pub fn build_ai_candidate_metadata_from_candidate(
|
||||
candidate: &SchedulerMinimalCandidateSelectionCandidate,
|
||||
provider_api_format: &str,
|
||||
client_api_format: &str,
|
||||
extra_fields: Map<String, Value>,
|
||||
) -> Value {
|
||||
build_ai_candidate_metadata(AiCandidateMetadataParts {
|
||||
provider_api_format,
|
||||
client_api_format,
|
||||
global_model_id: candidate.global_model_id.as_str(),
|
||||
global_model_name: candidate.global_model_name.as_str(),
|
||||
model_id: candidate.model_id.as_str(),
|
||||
selected_provider_model_name: candidate.selected_provider_model_name.as_str(),
|
||||
mapping_matched_model: candidate.mapping_matched_model.as_deref(),
|
||||
provider_name: candidate.provider_name.as_str(),
|
||||
key_name: candidate.key_name.as_str(),
|
||||
extra_fields,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn append_ai_execution_contract_fields_to_value(
|
||||
value: Value,
|
||||
execution_strategy: &str,
|
||||
conversion_mode: &str,
|
||||
client_contract: &str,
|
||||
provider_contract: &str,
|
||||
) -> Value {
|
||||
match value {
|
||||
Value::Object(mut object) => {
|
||||
object.insert(
|
||||
"execution_strategy".to_string(),
|
||||
Value::String(execution_strategy.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"conversion_mode".to_string(),
|
||||
Value::String(conversion_mode.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"client_contract".to_string(),
|
||||
Value::String(client_contract.to_string()),
|
||||
);
|
||||
object.insert(
|
||||
"provider_contract".to_string(),
|
||||
Value::String(provider_contract.to_string()),
|
||||
);
|
||||
Value::Object(object)
|
||||
}
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ai_local_execution_contract_for_formats(
|
||||
client_api_format: &str,
|
||||
provider_api_format: &str,
|
||||
) -> (ExecutionStrategy, ConversionMode) {
|
||||
if aether_ai_formats::api_format_alias_matches(client_api_format, provider_api_format) {
|
||||
return (ExecutionStrategy::LocalSameFormat, ConversionMode::None);
|
||||
}
|
||||
|
||||
let conversion_mode =
|
||||
if aether_ai_formats::request_conversion_kind(client_api_format, provider_api_format)
|
||||
.is_some()
|
||||
{
|
||||
ConversionMode::Bidirectional
|
||||
} else {
|
||||
ConversionMode::None
|
||||
};
|
||||
(ExecutionStrategy::LocalCrossFormat, conversion_mode)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn candidate_metadata_builds_base_candidate_fields_and_extra_data() {
|
||||
let mut extra_fields = Map::new();
|
||||
extra_fields.insert("source".to_string(), json!("test"));
|
||||
|
||||
let metadata = build_ai_candidate_metadata(AiCandidateMetadataParts {
|
||||
provider_api_format: "openai:responses",
|
||||
client_api_format: "claude:messages",
|
||||
global_model_id: "global-1",
|
||||
global_model_name: "gpt-5.4",
|
||||
model_id: "model-1",
|
||||
selected_provider_model_name: "gpt-5.4",
|
||||
mapping_matched_model: Some("gpt-5"),
|
||||
provider_name: "RightCode",
|
||||
key_name: "key-a",
|
||||
extra_fields,
|
||||
});
|
||||
|
||||
assert_eq!(metadata["provider_api_format"], "openai:responses");
|
||||
assert_eq!(metadata["client_api_format"], "claude:messages");
|
||||
assert_eq!(metadata["global_model_id"], "global-1");
|
||||
assert_eq!(metadata["mapping_matched_model"], "gpt-5");
|
||||
assert_eq!(metadata["source"], "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execution_contract_fields_append_to_object_values_only() {
|
||||
let value = append_ai_execution_contract_fields_to_value(
|
||||
json!({"existing": true}),
|
||||
"local_cross_format",
|
||||
"bidirectional",
|
||||
"openai:chat",
|
||||
"claude:messages",
|
||||
);
|
||||
|
||||
assert_eq!(value["existing"], true);
|
||||
assert_eq!(value["execution_strategy"], "local_cross_format");
|
||||
assert_eq!(value["conversion_mode"], "bidirectional");
|
||||
assert_eq!(value["client_contract"], "openai:chat");
|
||||
assert_eq!(value["provider_contract"], "claude:messages");
|
||||
|
||||
assert_eq!(
|
||||
append_ai_execution_contract_fields_to_value(
|
||||
Value::Null,
|
||||
"local_same_format",
|
||||
"none",
|
||||
"openai:chat",
|
||||
"openai:chat",
|
||||
),
|
||||
Value::Null
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_execution_contract_is_derived_from_client_and_provider_formats() {
|
||||
assert_eq!(
|
||||
ai_local_execution_contract_for_formats(" OPENAI:CHAT ", "openai:chat"),
|
||||
(ExecutionStrategy::LocalSameFormat, ConversionMode::None)
|
||||
);
|
||||
assert_eq!(
|
||||
ai_local_execution_contract_for_formats("openai:chat", "claude:messages"),
|
||||
(
|
||||
ExecutionStrategy::LocalCrossFormat,
|
||||
ConversionMode::Bidirectional,
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
ai_local_execution_contract_for_formats("openai:chat", "unknown:format"),
|
||||
(ExecutionStrategy::LocalCrossFormat, ConversionMode::None)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user