1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::JsonObject;
7use crate::ids::{MessageId, SubagentRunId, ToolCallId};
8use crate::tool::ToolCall;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
14#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
15pub enum Role {
16 Developer,
18 System,
20 Assistant,
22 User,
24 Tool,
26 Activity,
28 Reasoning,
30}
31
32impl Role {
33 pub const fn as_str(&self) -> &'static str {
35 match self {
36 Self::Developer => "developer",
37 Self::System => "system",
38 Self::Assistant => "assistant",
39 Self::User => "user",
40 Self::Tool => "tool",
41 Self::Activity => "activity",
42 Self::Reasoning => "reasoning",
43 }
44 }
45}
46
47impl std::fmt::Display for Role {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.write_str(self.as_str())
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
55#[serde(tag = "type", rename_all = "lowercase")]
56#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
57#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
58pub enum InputContentSource {
59 Data {
61 value: String,
63 #[serde(rename = "mimeType")]
65 mime_type: String,
66 },
67 Url {
69 value: String,
71 #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
73 mime_type: Option<String>,
74 },
75}
76
77#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
79#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
80#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
81pub struct TextInputContent {
82 pub text: String,
84}
85
86#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
92#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
93pub struct MediaInputContent {
94 pub source: InputContentSource,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub metadata: Option<Value>,
99}
100
101impl MediaInputContent {
102 pub fn new(source: InputContentSource) -> Self {
104 Self {
105 source,
106 metadata: None,
107 }
108 }
109}
110
111#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
120#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
121pub struct BinaryInputContent {
122 pub mime_type: String,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub id: Option<String>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub url: Option<String>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub data: Option<String>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub filename: Option<String>,
136}
137
138impl BinaryInputContent {
139 pub fn has_payload(&self) -> bool {
141 self.id.is_some() || self.url.is_some() || self.data.is_some()
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
147#[serde(tag = "type", rename_all = "lowercase")]
148#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
149#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
150pub enum InputContent {
151 Text(TextInputContent),
153 Image(MediaInputContent),
155 Audio(MediaInputContent),
157 Video(MediaInputContent),
159 Document(MediaInputContent),
161 Binary(BinaryInputContent),
163}
164
165impl InputContent {
166 pub fn text(text: impl Into<String>) -> Self {
168 Self::Text(TextInputContent { text: text.into() })
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
175#[serde(untagged)]
176#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
177#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
178pub enum UserContent {
179 Text(String),
181 Parts(Vec<InputContent>),
183}
184
185impl UserContent {
186 pub fn as_text(&self) -> Option<&str> {
192 match self {
193 Self::Text(text) => Some(text),
194 Self::Parts(_) => None,
195 }
196 }
197
198 pub fn to_text(&self) -> String {
223 match self {
224 Self::Text(text) => text.clone(),
225 Self::Parts(parts) => parts
226 .iter()
227 .filter_map(|part| match part {
228 InputContent::Text(part) => Some(part.text.as_str()),
229 _ => None,
230 })
231 .collect::<Vec<_>>()
232 .join("\n"),
233 }
234 }
235}
236
237impl Default for UserContent {
238 fn default() -> Self {
239 Self::Text(String::new())
240 }
241}
242
243impl From<String> for UserContent {
244 fn from(value: String) -> Self {
245 Self::Text(value)
246 }
247}
248
249impl From<&str> for UserContent {
250 fn from(value: &str) -> Self {
251 Self::Text(value.to_owned())
252 }
253}
254
255impl From<Vec<InputContent>> for UserContent {
256 fn from(value: Vec<InputContent>) -> Self {
257 Self::Parts(value)
258 }
259}
260
261#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
263#[serde(rename_all = "camelCase")]
264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
265#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
266pub struct DeveloperMessage {
267 pub id: MessageId,
269 pub content: String,
271 #[serde(default, skip_serializing_if = "Option::is_none")]
273 pub name: Option<String>,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub encrypted_value: Option<String>,
277 #[serde(
280 default,
281 deserialize_with = "crate::serde_util::reject_null",
282 skip_serializing_if = "Option::is_none"
283 )]
284 pub subagent_run_id: Option<SubagentRunId>,
285 #[serde(
288 default,
289 deserialize_with = "crate::serde_util::reject_null",
290 skip_serializing_if = "Option::is_none"
291 )]
292 #[cfg_attr(
293 feature = "schemars",
294 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
295 )]
296 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
297 pub metadata: Option<JsonObject>,
298}
299
300#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
302#[serde(rename_all = "camelCase")]
303#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
304#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
305pub struct SystemMessage {
306 pub id: MessageId,
308 pub content: String,
310 #[serde(default, skip_serializing_if = "Option::is_none")]
312 pub name: Option<String>,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub encrypted_value: Option<String>,
316 #[serde(
319 default,
320 deserialize_with = "crate::serde_util::reject_null",
321 skip_serializing_if = "Option::is_none"
322 )]
323 pub subagent_run_id: Option<SubagentRunId>,
324 #[serde(
327 default,
328 deserialize_with = "crate::serde_util::reject_null",
329 skip_serializing_if = "Option::is_none"
330 )]
331 #[cfg_attr(
332 feature = "schemars",
333 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
334 )]
335 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
336 pub metadata: Option<JsonObject>,
337}
338
339#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
341#[serde(rename_all = "camelCase")]
342#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
343#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
344pub struct AssistantMessage {
345 pub id: MessageId,
347 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub content: Option<String>,
350 #[serde(default, skip_serializing_if = "Option::is_none")]
352 pub name: Option<String>,
353 #[serde(default, skip_serializing_if = "Option::is_none")]
355 pub encrypted_value: Option<String>,
356 #[serde(default, skip_serializing_if = "Option::is_none")]
358 pub tool_calls: Option<Vec<ToolCall>>,
359 #[serde(
362 default,
363 deserialize_with = "crate::serde_util::reject_null",
364 skip_serializing_if = "Option::is_none"
365 )]
366 pub subagent_run_id: Option<SubagentRunId>,
367 #[serde(
370 default,
371 deserialize_with = "crate::serde_util::reject_null",
372 skip_serializing_if = "Option::is_none"
373 )]
374 #[cfg_attr(
375 feature = "schemars",
376 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
377 )]
378 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
379 pub metadata: Option<JsonObject>,
380}
381
382#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
384#[serde(rename_all = "camelCase")]
385#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
386#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
387pub struct UserMessage {
388 pub id: MessageId,
390 pub content: UserContent,
392 #[serde(default, skip_serializing_if = "Option::is_none")]
394 pub name: Option<String>,
395 #[serde(default, skip_serializing_if = "Option::is_none")]
397 pub encrypted_value: Option<String>,
398 #[serde(
401 default,
402 deserialize_with = "crate::serde_util::reject_null",
403 skip_serializing_if = "Option::is_none"
404 )]
405 pub subagent_run_id: Option<SubagentRunId>,
406 #[serde(
409 default,
410 deserialize_with = "crate::serde_util::reject_null",
411 skip_serializing_if = "Option::is_none"
412 )]
413 #[cfg_attr(
414 feature = "schemars",
415 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
416 )]
417 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
418 pub metadata: Option<JsonObject>,
419}
420
421#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
423#[serde(rename_all = "camelCase")]
424#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
425#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
426pub struct ToolMessage {
427 pub id: MessageId,
429 pub content: String,
431 pub tool_call_id: ToolCallId,
433 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub error: Option<String>,
437 #[serde(default, skip_serializing_if = "Option::is_none")]
439 pub encrypted_value: Option<String>,
440 #[serde(
445 default,
446 deserialize_with = "crate::serde_util::reject_null",
447 skip_serializing_if = "Option::is_none"
448 )]
449 pub subagent_run_id: Option<SubagentRunId>,
450 #[serde(
453 default,
454 deserialize_with = "crate::serde_util::reject_null",
455 skip_serializing_if = "Option::is_none"
456 )]
457 #[cfg_attr(
458 feature = "schemars",
459 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
460 )]
461 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
462 pub metadata: Option<JsonObject>,
463}
464
465#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
467#[serde(rename_all = "camelCase")]
468#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
469#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
470pub struct ActivityMessage {
471 pub id: MessageId,
473 pub activity_type: String,
475 #[cfg_attr(
477 feature = "schemars",
478 schemars(with = "std::collections::BTreeMap<String, serde_json::Value>")
479 )]
480 #[cfg_attr(feature = "utoipa", schema(value_type = Object))]
481 pub content: JsonObject,
482 #[serde(
485 default,
486 deserialize_with = "crate::serde_util::reject_null",
487 skip_serializing_if = "Option::is_none"
488 )]
489 pub subagent_run_id: Option<SubagentRunId>,
490 #[serde(
493 default,
494 deserialize_with = "crate::serde_util::reject_null",
495 skip_serializing_if = "Option::is_none"
496 )]
497 #[cfg_attr(
498 feature = "schemars",
499 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
500 )]
501 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
502 pub metadata: Option<JsonObject>,
503}
504
505#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
507#[serde(rename_all = "camelCase")]
508#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
509#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
510pub struct ReasoningMessage {
511 pub id: MessageId,
513 pub content: String,
516 #[serde(default, skip_serializing_if = "Option::is_none")]
518 pub encrypted_value: Option<String>,
519 #[serde(
522 default,
523 deserialize_with = "crate::serde_util::reject_null",
524 skip_serializing_if = "Option::is_none"
525 )]
526 pub subagent_run_id: Option<SubagentRunId>,
527 #[serde(
530 default,
531 deserialize_with = "crate::serde_util::reject_null",
532 skip_serializing_if = "Option::is_none"
533 )]
534 #[cfg_attr(
535 feature = "schemars",
536 schemars(with = "Option<std::collections::BTreeMap<String, serde_json::Value>>")
537 )]
538 #[cfg_attr(feature = "utoipa", schema(value_type = Option<Object>))]
539 pub metadata: Option<JsonObject>,
540}
541
542#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
544#[serde(tag = "role", rename_all = "lowercase")]
545#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
546#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
547pub enum Message {
548 Developer(DeveloperMessage),
550 System(SystemMessage),
552 Assistant(AssistantMessage),
554 User(UserMessage),
556 Tool(ToolMessage),
558 Activity(ActivityMessage),
560 Reasoning(ReasoningMessage),
562}
563
564impl Message {
565 pub fn user(id: impl Into<MessageId>, content: impl Into<UserContent>) -> Self {
567 Self::User(UserMessage {
568 id: id.into(),
569 content: content.into(),
570 ..Default::default()
571 })
572 }
573
574 pub fn assistant(id: impl Into<MessageId>, content: impl Into<String>) -> Self {
576 Self::Assistant(AssistantMessage {
577 id: id.into(),
578 content: Some(content.into()),
579 ..Default::default()
580 })
581 }
582
583 pub fn system(id: impl Into<MessageId>, content: impl Into<String>) -> Self {
585 Self::System(SystemMessage {
586 id: id.into(),
587 content: content.into(),
588 ..Default::default()
589 })
590 }
591
592 pub fn developer(id: impl Into<MessageId>, content: impl Into<String>) -> Self {
594 Self::Developer(DeveloperMessage {
595 id: id.into(),
596 content: content.into(),
597 ..Default::default()
598 })
599 }
600
601 pub fn tool(
603 id: impl Into<MessageId>,
604 tool_call_id: impl Into<ToolCallId>,
605 content: impl Into<String>,
606 ) -> Self {
607 Self::Tool(ToolMessage {
608 id: id.into(),
609 content: content.into(),
610 tool_call_id: tool_call_id.into(),
611 ..Default::default()
612 })
613 }
614
615 pub const fn id(&self) -> &MessageId {
617 match self {
618 Self::Developer(m) => &m.id,
619 Self::System(m) => &m.id,
620 Self::Assistant(m) => &m.id,
621 Self::User(m) => &m.id,
622 Self::Tool(m) => &m.id,
623 Self::Activity(m) => &m.id,
624 Self::Reasoning(m) => &m.id,
625 }
626 }
627
628 pub const fn role(&self) -> Role {
630 match self {
631 Self::Developer(_) => Role::Developer,
632 Self::System(_) => Role::System,
633 Self::Assistant(_) => Role::Assistant,
634 Self::User(_) => Role::User,
635 Self::Tool(_) => Role::Tool,
636 Self::Activity(_) => Role::Activity,
637 Self::Reasoning(_) => Role::Reasoning,
638 }
639 }
640
641 pub const fn subagent_run_id(&self) -> Option<&SubagentRunId> {
644 match self {
645 Self::Developer(m) => m.subagent_run_id.as_ref(),
646 Self::System(m) => m.subagent_run_id.as_ref(),
647 Self::Assistant(m) => m.subagent_run_id.as_ref(),
648 Self::User(m) => m.subagent_run_id.as_ref(),
649 Self::Tool(m) => m.subagent_run_id.as_ref(),
650 Self::Activity(m) => m.subagent_run_id.as_ref(),
651 Self::Reasoning(m) => m.subagent_run_id.as_ref(),
652 }
653 }
654
655 pub fn set_subagent_run_id(&mut self, subagent_run_id: Option<SubagentRunId>) {
657 match self {
658 Self::Developer(m) => m.subagent_run_id = subagent_run_id,
659 Self::System(m) => m.subagent_run_id = subagent_run_id,
660 Self::Assistant(m) => m.subagent_run_id = subagent_run_id,
661 Self::User(m) => m.subagent_run_id = subagent_run_id,
662 Self::Tool(m) => m.subagent_run_id = subagent_run_id,
663 Self::Activity(m) => m.subagent_run_id = subagent_run_id,
664 Self::Reasoning(m) => m.subagent_run_id = subagent_run_id,
665 }
666 }
667
668 pub const fn metadata(&self) -> Option<&JsonObject> {
670 match self {
671 Self::Developer(m) => m.metadata.as_ref(),
672 Self::System(m) => m.metadata.as_ref(),
673 Self::Assistant(m) => m.metadata.as_ref(),
674 Self::User(m) => m.metadata.as_ref(),
675 Self::Tool(m) => m.metadata.as_ref(),
676 Self::Activity(m) => m.metadata.as_ref(),
677 Self::Reasoning(m) => m.metadata.as_ref(),
678 }
679 }
680
681 pub fn metadata_mut(&mut self) -> &mut Option<JsonObject> {
685 match self {
686 Self::Developer(m) => &mut m.metadata,
687 Self::System(m) => &mut m.metadata,
688 Self::Assistant(m) => &mut m.metadata,
689 Self::User(m) => &mut m.metadata,
690 Self::Tool(m) => &mut m.metadata,
691 Self::Activity(m) => &mut m.metadata,
692 Self::Reasoning(m) => &mut m.metadata,
693 }
694 }
695}
696
697macro_rules! message_from {
698 ($($ty:ident => $variant:ident),* $(,)?) => {
699 $(
700 impl From<$ty> for Message {
701 fn from(value: $ty) -> Self {
702 Self::$variant(value)
703 }
704 }
705 )*
706 };
707}
708
709message_from! {
710 DeveloperMessage => Developer,
711 SystemMessage => System,
712 AssistantMessage => Assistant,
713 UserMessage => User,
714 ToolMessage => Tool,
715 ActivityMessage => Activity,
716 ReasoningMessage => Reasoning,
717}