caliban_provider/message.rs
1//! Core message types: roles, messages, content blocks.
2
3use serde::{Deserialize, Serialize};
4
5use crate::cache::CacheControl;
6use crate::thinking::ThinkingBlock;
7use crate::tool::{ToolResultBlock, ToolUseBlock};
8
9/// The conversational role of a message.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum Role {
13 /// A message from the end user.
14 User,
15 /// A message generated by the assistant.
16 Assistant,
17 /// An instruction message that precedes the conversation.
18 System,
19}
20
21/// A single turn in a conversation.
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct Message {
24 /// The role of the speaker.
25 pub role: Role,
26 /// The content of the message as an ordered list of blocks.
27 pub content: Vec<ContentBlock>,
28}
29
30impl Message {
31 /// Construct a single-block user text message.
32 pub fn user_text(text: impl Into<String>) -> Self {
33 Self {
34 role: Role::User,
35 content: vec![ContentBlock::Text(TextBlock {
36 text: text.into(),
37 cache_control: None,
38 })],
39 }
40 }
41
42 /// Construct a single-block assistant text message.
43 pub fn assistant_text(text: impl Into<String>) -> Self {
44 Self {
45 role: Role::Assistant,
46 content: vec![ContentBlock::Text(TextBlock {
47 text: text.into(),
48 cache_control: None,
49 })],
50 }
51 }
52
53 /// Construct a single-block system text message.
54 pub fn system_text(text: impl Into<String>) -> Self {
55 Self {
56 role: Role::System,
57 content: vec![ContentBlock::Text(TextBlock {
58 text: text.into(),
59 cache_control: None,
60 })],
61 }
62 }
63}
64
65/// A typed content block within a message.
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67#[serde(tag = "type", rename_all = "snake_case")]
68pub enum ContentBlock {
69 /// A plain-text block.
70 Text(TextBlock),
71 /// An image block.
72 Image(ImageBlock),
73 /// A tool-use invocation block.
74 ToolUse(ToolUseBlock),
75 /// A tool-result block.
76 ToolResult(ToolResultBlock),
77 /// An extended-thinking block.
78 Thinking(ThinkingBlock),
79}
80
81/// A plain-text content block.
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83pub struct TextBlock {
84 /// The text content.
85 pub text: String,
86 /// Optional cache-control marker.
87 #[serde(skip_serializing_if = "Option::is_none", default)]
88 pub cache_control: Option<CacheControl>,
89}
90
91/// An image content block.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub struct ImageBlock {
94 /// The source of the image data.
95 pub source: ImageSource,
96 /// Optional cache-control marker.
97 #[serde(skip_serializing_if = "Option::is_none", default)]
98 pub cache_control: Option<CacheControl>,
99 /// SHA-256 fingerprint (64-char hex) of the (post-resize) image bytes.
100 /// Populated by [`caliban-images`] at ingest time; the provider IR
101 /// itself does not require it.
102 #[serde(skip_serializing_if = "Option::is_none", default)]
103 pub sha256: Option<String>,
104 /// (width, height) in pixels, populated at ingest.
105 #[serde(skip_serializing_if = "Option::is_none", default)]
106 pub dims: Option<(u32, u32)>,
107}
108
109/// The source of image data.
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(tag = "type", rename_all = "snake_case")]
112pub enum ImageSource {
113 /// Base64-encoded image with a MIME type.
114 Base64 {
115 /// MIME type of the image (e.g. `"image/png"`).
116 media_type: String,
117 /// Base64-encoded image bytes.
118 data: String,
119 },
120 /// A URL pointing to the image.
121 Url {
122 /// The URL of the image.
123 url: String,
124 },
125 /// A reference to a session-local blob, identified by SHA-256.
126 ///
127 /// **Never** sent to a provider: provider adapters must reject `BlobRef`
128 /// (or, equivalently, the session loader resolves it to
129 /// [`ImageSource::Base64`] before dispatch). Round-trips through
130 /// `serde_json` because session storage persists this variant on-disk.
131 BlobRef {
132 /// SHA-256 fingerprint (64-char hex).
133 sha256: String,
134 /// MIME type of the referenced blob.
135 media_type: String,
136 },
137}