feat(dynamic_routing): Decision engine config API integration (#8044)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Sarthak Soni
2025-05-22 23:42:40 +05:30
committed by GitHub
parent d1fe2841c1
commit d41f65381a
9 changed files with 889 additions and 100 deletions

View File

@ -1,6 +1,10 @@
use std::fmt::Debug;
use common_utils::{errors::ParsingError, ext_traits::ValueExt, pii};
use common_utils::{
errors::{ParsingError, ValidationError},
ext_traits::ValueExt,
pii,
};
pub use euclid::{
dssa::types::EuclidAnalysable,
frontend::{
@ -11,7 +15,17 @@ pub use euclid::{
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use crate::enums::{RoutableConnectors, TransactionType};
use crate::{
enums::{RoutableConnectors, TransactionType},
open_router,
};
// Define constants for default values
const DEFAULT_LATENCY_THRESHOLD: f64 = 90.0;
const DEFAULT_BUCKET_SIZE: i32 = 200;
const DEFAULT_HEDGING_PERCENT: f64 = 5.0;
const DEFAULT_ELIMINATION_THRESHOLD: f64 = 0.35;
const DEFAULT_PAYMENT_METHOD: &str = "CARD";
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
@ -834,6 +848,8 @@ pub struct ToggleDynamicRoutingPath {
pub struct EliminationRoutingConfig {
pub params: Option<Vec<DynamicRoutingConfigParams>>,
pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
#[schema(value_type = DecisionEngineEliminationData)]
pub decision_engine_configs: Option<open_router::DecisionEngineEliminationData>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, ToSchema)]
@ -861,6 +877,7 @@ impl Default for EliminationRoutingConfig {
bucket_size: Some(5),
bucket_leak_interval_in_secs: Some(60),
}),
decision_engine_configs: None,
}
}
}
@ -875,6 +892,34 @@ impl EliminationRoutingConfig {
.as_mut()
.map(|config| config.update(new_config));
}
if let Some(new_config) = new.decision_engine_configs {
self.decision_engine_configs
.as_mut()
.map(|config| config.update(new_config));
}
}
pub fn open_router_config_default() -> Self {
Self {
elimination_analyser_config: None,
params: None,
decision_engine_configs: Some(open_router::DecisionEngineEliminationData {
threshold: DEFAULT_ELIMINATION_THRESHOLD,
}),
}
}
pub fn get_decision_engine_configs(
&self,
) -> Result<open_router::DecisionEngineEliminationData, error_stack::Report<ValidationError>>
{
self.decision_engine_configs
.clone()
.ok_or(error_stack::Report::new(
ValidationError::MissingRequiredField {
field_name: "decision_engine_configs".to_string(),
},
))
}
}
@ -882,6 +927,8 @@ impl EliminationRoutingConfig {
pub struct SuccessBasedRoutingConfig {
pub params: Option<Vec<DynamicRoutingConfigParams>>,
pub config: Option<SuccessBasedRoutingConfigBody>,
#[schema(value_type = DecisionEngineSuccessRateData)]
pub decision_engine_configs: Option<open_router::DecisionEngineSuccessRateData>,
}
impl Default for SuccessBasedRoutingConfig {
@ -898,6 +945,7 @@ impl Default for SuccessBasedRoutingConfig {
}),
specificity_level: SuccessRateSpecificityLevel::default(),
}),
decision_engine_configs: None,
}
}
}
@ -982,6 +1030,51 @@ impl SuccessBasedRoutingConfig {
if let Some(new_config) = new.config {
self.config.as_mut().map(|config| config.update(new_config));
}
if let Some(new_config) = new.decision_engine_configs {
self.decision_engine_configs
.as_mut()
.map(|config| config.update(new_config));
}
}
pub fn open_router_config_default() -> Self {
Self {
params: None,
config: None,
decision_engine_configs: Some(open_router::DecisionEngineSuccessRateData {
default_latency_threshold: Some(DEFAULT_LATENCY_THRESHOLD),
default_bucket_size: Some(DEFAULT_BUCKET_SIZE),
default_hedging_percent: Some(DEFAULT_HEDGING_PERCENT),
default_lower_reset_factor: None,
default_upper_reset_factor: None,
default_gateway_extra_score: None,
sub_level_input_config: Some(vec![
open_router::DecisionEngineSRSubLevelInputConfig {
payment_method_type: Some(DEFAULT_PAYMENT_METHOD.to_string()),
payment_method: None,
latency_threshold: None,
bucket_size: Some(DEFAULT_BUCKET_SIZE),
hedging_percent: Some(DEFAULT_HEDGING_PERCENT),
lower_reset_factor: None,
upper_reset_factor: None,
gateway_extra_score: None,
},
]),
}),
}
}
pub fn get_decision_engine_configs(
&self,
) -> Result<open_router::DecisionEngineSuccessRateData, error_stack::Report<ValidationError>>
{
self.decision_engine_configs
.clone()
.ok_or(error_stack::Report::new(
ValidationError::MissingRequiredField {
field_name: "decision_engine_configs".to_string(),
},
))
}
}