starweaver_model/message/
history.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use serde_json::Map;
6use starweaver_core::{ConversationId, RunId};
7use starweaver_usage::Usage;
8
9use super::{
10 ContentPart, FinishReason, Metadata, ModelRequestPart, ModelResponsePart, ProviderInfo,
11 ToolCallPart,
12};
13
14#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
16#[serde(tag = "kind", rename_all = "snake_case")]
17pub enum ModelMessage {
18 Request(ModelRequest),
20 Response(ModelResponse),
22}
23
24#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26pub struct ModelRequest {
27 pub parts: Vec<ModelRequestPart>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub timestamp: Option<DateTime<Utc>>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub instructions: Option<String>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub run_id: Option<RunId>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub conversation_id: Option<ConversationId>,
47 #[serde(default, skip_serializing_if = "Map::is_empty")]
49 pub metadata: Metadata,
50}
51
52impl ModelRequest {
53 #[must_use]
55 pub fn user_text(text: impl Into<String>) -> Self {
56 Self {
57 parts: vec![ModelRequestPart::UserPrompt {
58 content: vec![ContentPart::Text { text: text.into() }],
59 name: None,
60 metadata: Metadata::default(),
61 }],
62 timestamp: None,
63 instructions: None,
64 run_id: None,
65 conversation_id: None,
66 metadata: Metadata::default(),
67 }
68 }
69}
70
71#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
73pub struct ModelResponse {
74 pub parts: Vec<ModelResponsePart>,
76 #[serde(default)]
78 pub usage: Usage,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub model_name: Option<String>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub provider: Option<ProviderInfo>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub finish_reason: Option<FinishReason>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub timestamp: Option<DateTime<Utc>>,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub run_id: Option<RunId>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub conversation_id: Option<ConversationId>,
97 #[serde(default, skip_serializing_if = "Map::is_empty")]
99 pub metadata: Metadata,
100}
101
102impl ModelResponse {
103 #[must_use]
105 pub fn text(text: impl Into<String>) -> Self {
106 Self {
107 parts: vec![ModelResponsePart::Text { text: text.into() }],
108 usage: Usage::default(),
109 model_name: None,
110 provider: None,
111 finish_reason: None,
112 timestamp: None,
113 run_id: None,
114 conversation_id: None,
115 metadata: Metadata::default(),
116 }
117 }
118
119 #[must_use]
121 pub fn text_output(&self) -> String {
122 self.parts
123 .iter()
124 .filter_map(ModelResponsePart::text)
125 .collect::<Vec<_>>()
126 .join("")
127 }
128
129 #[must_use]
131 pub fn tool_calls(&self) -> Vec<ToolCallPart> {
132 self.parts
133 .iter()
134 .filter_map(ModelResponsePart::tool_call)
135 .cloned()
136 .collect()
137 }
138}