async_dashscope/operation/multi_modal_conversation/
param.rs1use 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 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 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 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}