async_dashscope/operation/
validate.rs

1use crate::operation::request::RequestTrait;
2use crate::{
3    error::{DashScopeError, Result},
4    operation::{common::Parameters, embeddings::EmbeddingsParameters},
5};
6
7/// Defines the validation strategy for a given model.
8///
9/// This enum replaces the previous trait-based approach to resolve `dyn` compatibility issues.
10/// It allows for static dispatch, which is more performant and avoids the complexities of
11/// object safety.
12pub enum ModelValidator {
13    /// The default validation, which performs no special checks.
14    Default,
15    /// Validation specific to the `deepseek-r1` model.
16    NotSupportResultFormatText,
17    /// validation enable_thinking
18    NotSupportEnableThinking,
19    NotSupportToolCall,
20    NotSupportJsonOutput,
21    // dimensions 不匹配
22    DimensionNotMatch,
23}
24
25pub trait Validator<T> {
26    /// 验证请求的参数
27    fn validate<R: RequestTrait<P = T> + ?Sized>(&self, params: &R) -> Result<()>;
28}
29
30impl Validator<EmbeddingsParameters> for ModelValidator {
31    fn validate<R: RequestTrait<P = EmbeddingsParameters> + ?Sized>(
32        &self,
33        params: &R,
34    ) -> Result<()> {
35        match self {
36            ModelValidator::DimensionNotMatch => {
37                // text-embedding-v4 向量维度 只能是以下值: 2,048、1,536、1,024(默认)、768、512、256、128、64
38                // text-embedding-v3 向量维度 只能是以下值: 1,024(默认)、768、512、256、128或64
39                // text-embedding-v2 向量维度 只能是1,536
40                // text-embedding-v1 向量维度 只能是1,536
41
42                if let Some(p) = params.parameters() {
43                    let valid_dimensions = match params.model() {
44                        "text-embedding-v1" | "text-embedding-v2" => {
45                            vec![1536]
46                        }
47                        "text-embedding-v3" => {
48                            vec![1024, 768, 512, 256, 128, 64]
49                        }
50                        "text-embedding-v4" => {
51                            vec![2048, 1536, 1024, 768, 512, 256, 128, 64]
52                        }
53                        _ => vec![], // 未知模型不验证
54                    };
55                    if let Some(dimension) = p.dimension {
56                        if !valid_dimensions.contains(&dimension) {
57                            return Err(DashScopeError::InvalidArgument(format!(
58                                "Invalid dimension: {} for model: {}",
59                                dimension,
60                                params.model()
61                            )));
62                        }
63                    }
64                }
65                Ok(())
66            }
67            _ => Ok(()),
68        }
69    }
70}
71impl Validator<Parameters> for ModelValidator {
72    fn validate<R: RequestTrait<P = Parameters> + ?Sized>(&self, params: &R) -> Result<()> {
73        match self {
74            ModelValidator::Default => {
75                // No specific validation rules for the default case.
76                Ok(())
77            }
78            ModelValidator::NotSupportResultFormatText => {
79                // The deepseek-r1 model does not support `result_format: "text"`.
80                if let Some(p) = params.parameters() {
81                    if let Some(format) = &p.result_format {
82                        if format == "text" {
83                            return Err(DashScopeError::InvalidArgument(
84                                "deepseek-r1 does not support result_format = text".into(),
85                            ));
86                        }
87                    }
88                }
89                Ok(())
90            }
91            ModelValidator::NotSupportEnableThinking => {
92                if let Some(p) = params.parameters() {
93                    if let Some(thinking) = p.enable_thinking {
94                        if thinking {
95                            return Err(DashScopeError::InvalidArgument(
96                                "The model does not support enable_thinking = true".into(),
97                            ));
98                        }
99                    }
100                }
101                Ok(())
102            }
103            ModelValidator::NotSupportJsonOutput => {
104                if let Some(p) = params.parameters() {
105                    if let Some(response_format) = p.response_format.as_ref() {
106                        if response_format.type_ == "json_object" {
107                            return Err(DashScopeError::InvalidArgument(
108                                "The model does not support response_format=json_object".into(),
109                            ));
110                        }
111                    }
112                }
113                Ok(())
114            }
115
116            ModelValidator::NotSupportToolCall => {
117                if let Some(p) = params.parameters() {
118                    if p.tools.is_some() {
119                        return Err(DashScopeError::InvalidArgument(
120                            "The model does not support tool call".into(),
121                        ));
122                    }
123                }
124                Ok(())
125            }
126
127            _ => Ok(()),
128        }
129    }
130}
131
132/// Selects the appropriate validator for the given model name.
133///
134/// # Arguments
135///
136/// * `model` - The name of the model as a string slice.
137///
138/// # Returns
139///
140/// A `ModelValidator` enum variant corresponding to the required validation strategy.
141pub(crate) fn check_model_parameters(model: &str) -> Vec<ModelValidator> {
142    match model {
143        "deepseek-r1" => vec![
144            ModelValidator::NotSupportResultFormatText,
145            ModelValidator::NotSupportJsonOutput,
146        ],
147        "qwen-vl" | "qwen-audio" => vec![ModelValidator::NotSupportToolCall],
148        "Moonshot-Kimi-K2-Instruct" => vec![
149            ModelValidator::NotSupportEnableThinking,
150            ModelValidator::NotSupportResultFormatText,
151            ModelValidator::NotSupportJsonOutput,
152            ModelValidator::NotSupportToolCall,
153        ],
154        "text-embedding-v4" | "text-embedding-v3" | "text-embedding-v2" | "text-embedding-v1" => {
155            vec![ModelValidator::DimensionNotMatch]
156        }
157        _ => vec![ModelValidator::Default],
158    }
159}