async_dashscope/operation/multi_modal_conversation/
param.rs

1use derive_builder::Builder;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5use crate::{operation::common::Parameters, oss_util};
6use crate::{operation::request::RequestTrait, oss_util::is_valid_url};
7
8#[derive(Debug, Clone, Builder, Serialize, Deserialize, PartialEq)]
9pub struct MultiModalConversationParam {
10    #[builder(setter(into))]
11    pub model: String,
12    pub input: Input,
13
14    #[builder(setter(into, strip_option))]
15    #[builder(default=None)]
16    pub stream: Option<bool>,
17
18    #[serde(skip_serializing_if = "Option::is_none")]
19    #[builder(setter(into, strip_option))]
20    #[builder(default=None)]
21    pub parameters: Option<Parameters>,
22}
23
24impl MultiModalConversationParam {
25    pub(crate) async fn upload_file_to_oss(
26        mut self,
27        api_key: &str,
28    ) -> Result<Self, crate::error::DashScopeError> {
29        for message in self.input.messages.iter_mut() {
30            for content in message.contents.iter_mut() {
31                match content {
32                    Element::Image(url) => {
33                        if !is_valid_url(url) {
34                            let oss_url =
35                                oss_util::upload_file_and_get_url(api_key, &self.model, url)
36                                    .await?;
37                            *content = Element::Image(oss_url);
38                        }
39                    }
40                    Element::Audio(url) => {
41                        if !is_valid_url(url) {
42                            let oss_url =
43                                oss_util::upload_file_and_get_url(api_key, &self.model, url)
44                                    .await?;
45                            *content = Element::Audio(oss_url);
46                        }
47                    }
48                    Element::Video(url) => {
49                        if !is_valid_url(url) {
50                            let oss_url =
51                                oss_util::upload_file_and_get_url(api_key, &self.model, url)
52                                    .await?;
53                            *content = Element::Video(oss_url);
54                        }
55                    }
56                    _ => {}
57                }
58            }
59        }
60
61        Ok(self)
62    }
63}
64
65#[derive(Debug, Clone, Builder, Serialize, Deserialize, PartialEq)]
66pub struct Input {
67    messages: Vec<Message>,
68}
69
70#[derive(Debug, Clone, Builder, Serialize, Deserialize, PartialEq)]
71pub struct Message {
72    #[builder(setter(into))]
73    role: String,
74    #[serde(rename = "content")]
75    contents: Vec<Element>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
79#[serde(rename_all = "lowercase")]
80pub enum Element {
81    Image(String),
82    Video(String),
83    Audio(String),
84    Text(String),
85}
86
87impl TryFrom<Value> for Element {
88    type Error = crate::error::DashScopeError;
89
90    fn try_from(value: Value) -> Result<Self, Self::Error> {
91        // image: {"image": "https://example.com/image.png"}
92        // video: {"video": "https://example.com/video.mp4"}
93        // audio: {"audio": "https://example.com/audio.mp3"}
94        // text: {"text": "Hello, world!"}
95        if let Some(image) = value.get("image") {
96            if let Some(s) = image.as_str() {
97                return Ok(Element::Image(s.to_string()));
98            }
99        }
100        if let Some(video) = value.get("video") {
101            if let Some(s) = video.as_str() {
102                return Ok(Element::Video(s.to_string()));
103            }
104        }
105        if let Some(audio) = value.get("audio") {
106            if let Some(s) = audio.as_str() {
107                return Ok(Element::Audio(s.to_string()));
108            }
109        }
110        if let Some(text) = value.get("text") {
111            if let Some(s) = text.as_str() {
112                // 处理文本字段
113                return Ok(Element::Text(s.to_string()));
114            }
115        }
116        Err(crate::error::DashScopeError::ElementError(
117            "Invalid element type.".into(),
118        ))
119    }
120}
121
122impl Message {
123    /// Creates a new `Message` with a single content item.
124    ///
125    /// A convenience method for creating a message without the builder pattern.
126    pub fn new(role: impl Into<String>, contents: Vec<Element>) -> Self {
127        Self {
128            role: role.into(),
129            contents,
130        }
131    }
132
133    pub fn push_content(&mut self, content: Element) {
134        self.contents.push(content);
135    }
136}
137
138impl RequestTrait for MultiModalConversationParam {
139    fn model(&self) -> &str {
140        &self.model
141    }
142
143    fn parameters(&self) -> Option<&Parameters> {
144        self.parameters.as_ref()
145    }
146}