Skip to main content

vv_agent/types/dict/
messages.rs

1use super::common::*;
2use super::*;
3
4impl Message {
5    pub fn to_openai_message(&self, include_reasoning_content: bool) -> Value {
6        let mut payload = serde_json::Map::from_iter([
7            (
8                "role".to_string(),
9                Value::String(message_role_value(self.role).to_string()),
10            ),
11            ("content".to_string(), Value::String(self.content.clone())),
12        ]);
13        insert_non_empty_optional_string(&mut payload, "name", &self.name);
14        insert_non_empty_optional_string(&mut payload, "tool_call_id", &self.tool_call_id);
15        if self.role == MessageRole::Assistant && !self.tool_calls.is_empty() {
16            payload.insert(
17                "tool_calls".to_string(),
18                Value::Array(self.tool_calls.iter().map(tool_call_to_openai).collect()),
19            );
20            if self.content.is_empty() {
21                payload.insert("content".to_string(), Value::Null);
22            }
23        }
24        if include_reasoning_content && self.role == MessageRole::Assistant {
25            insert_non_empty_optional_string(
26                &mut payload,
27                "reasoning_content",
28                &self.reasoning_content,
29            );
30        }
31        if self.role == MessageRole::User {
32            if let Some(image_url) = self.image_url.as_deref().filter(|value| !value.is_empty()) {
33                let mut blocks = Vec::new();
34                if !self.content.is_empty() {
35                    blocks.push(serde_json::json!({
36                        "type": "text",
37                        "text": self.content,
38                    }));
39                }
40                blocks.push(serde_json::json!({
41                    "type": "image_url",
42                    "image_url": {"url": image_url},
43                }));
44                payload.insert("content".to_string(), Value::Array(blocks));
45            }
46        }
47        Value::Object(payload)
48    }
49
50    pub fn to_dict(&self) -> Value {
51        let mut payload = serde_json::Map::from_iter([
52            (
53                "role".to_string(),
54                Value::String(message_role_value(self.role).to_string()),
55            ),
56            ("content".to_string(), Value::String(self.content.clone())),
57        ]);
58        insert_optional_string(&mut payload, "name", &self.name);
59        insert_optional_string(&mut payload, "tool_call_id", &self.tool_call_id);
60        if !self.tool_calls.is_empty() {
61            payload.insert(
62                "tool_calls".to_string(),
63                Value::Array(self.tool_calls.iter().map(tool_call_to_openai).collect()),
64            );
65        }
66        insert_optional_string(&mut payload, "reasoning_content", &self.reasoning_content);
67        insert_optional_string(&mut payload, "image_url", &self.image_url);
68        if !self.metadata.is_empty() {
69            payload.insert("metadata".to_string(), metadata_to_value(&self.metadata));
70        }
71        Value::Object(payload)
72    }
73
74    pub fn from_dict(data: &Value) -> Result<Self, String> {
75        let object = expect_object(data, "Message")?;
76        let role = parse_message_role(read_required_string(object, "role")?)?;
77        let tool_calls = read_array(object, "tool_calls")
78            .unwrap_or(&[])
79            .iter()
80            .map(ToolCall::from_dict)
81            .collect::<Result<Vec<_>, _>>()?;
82        Ok(Self {
83            role,
84            content: read_string(object, "content").unwrap_or_default(),
85            name: read_optional_string(object, "name"),
86            tool_call_id: read_optional_string(object, "tool_call_id"),
87            tool_calls,
88            reasoning_content: read_optional_string(object, "reasoning_content"),
89            image_url: read_optional_string(object, "image_url"),
90            metadata: read_metadata(object, "metadata")?,
91        })
92    }
93}
94
95fn tool_call_to_openai(tool_call: &ToolCall) -> Value {
96    let mut payload = serde_json::json!({
97        "id": tool_call.id,
98        "type": "function",
99        "function": {
100            "name": tool_call.name,
101            "arguments": Value::Object(tool_call.arguments.clone().into_iter().collect()).to_string(),
102        },
103    });
104    if let Some(extra_content) = &tool_call.extra_content {
105        payload["extra_content"] = extra_content.clone();
106    }
107    payload
108}
109
110impl ToolCall {
111    pub fn to_dict(&self) -> Value {
112        let mut payload = serde_json::Map::from_iter([
113            ("id".to_string(), Value::String(self.id.clone())),
114            ("name".to_string(), Value::String(self.name.clone())),
115            (
116                "arguments".to_string(),
117                Value::Object(self.arguments.clone().into_iter().collect()),
118            ),
119        ]);
120        if let Some(extra_content) = &self.extra_content {
121            payload.insert("extra_content".to_string(), extra_content.clone());
122        }
123        Value::Object(payload)
124    }
125
126    pub fn from_dict(data: &Value) -> Result<Self, String> {
127        let object = expect_object(data, "ToolCall")?;
128        if let Some(function) = object.get("function").and_then(Value::as_object) {
129            let id = read_required_string(object, "id")?.to_string();
130            let name = read_required_string(function, "name")?.to_string();
131            let raw_arguments = function
132                .get("arguments")
133                .cloned()
134                .unwrap_or_else(|| Value::String("{}".to_string()));
135            let mut tool_call = ToolCall::from_raw_arguments(id, name, raw_arguments);
136            tool_call.extra_content = object
137                .get("extra_content")
138                .filter(|value| value.is_object())
139                .cloned();
140            return Ok(tool_call);
141        }
142        Ok(Self {
143            id: read_required_string(object, "id")?.to_string(),
144            name: read_required_string(object, "name")?.to_string(),
145            arguments: read_metadata(object, "arguments")?,
146            extra_content: object
147                .get("extra_content")
148                .filter(|value| value.is_object())
149                .cloned(),
150        })
151    }
152}