Skip to main content

systemprompt_models/execution/step/
content.rs

1//! Per-kind step content payload and the [`PlannedTool`] descriptor.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use serde::{Deserialize, Serialize};
7use systemprompt_identifiers::SkillId;
8
9use super::enums::StepType;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
12pub struct PlannedTool {
13    pub tool_name: String,
14    // JSON: MCP tool-call arguments / result are the tool's own JSON.
15    pub arguments: serde_json::Value,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
19#[serde(tag = "type", rename_all = "snake_case")]
20pub enum StepContent {
21    Understanding,
22    Planning {
23        #[serde(skip_serializing_if = "Option::is_none")]
24        reasoning: Option<String>,
25        #[serde(skip_serializing_if = "Option::is_none")]
26        planned_tools: Option<Vec<PlannedTool>>,
27    },
28    SkillUsage {
29        skill_id: SkillId,
30        skill_name: String,
31    },
32    ToolExecution {
33        tool_name: String,
34        // JSON: MCP tool-call arguments / result are the tool's own JSON.
35        tool_arguments: serde_json::Value,
36        #[serde(skip_serializing_if = "Option::is_none")]
37        // JSON: MCP tool-call arguments / result are the tool's own JSON.
38        tool_result: Option<serde_json::Value>,
39    },
40    Completion,
41}
42
43impl StepContent {
44    #[must_use]
45    pub const fn understanding() -> Self {
46        Self::Understanding
47    }
48
49    #[must_use]
50    pub const fn planning(
51        reasoning: Option<String>,
52        planned_tools: Option<Vec<PlannedTool>>,
53    ) -> Self {
54        Self::Planning {
55            reasoning,
56            planned_tools,
57        }
58    }
59
60    pub fn skill_usage(skill_id: SkillId, skill_name: impl Into<String>) -> Self {
61        Self::SkillUsage {
62            skill_id,
63            skill_name: skill_name.into(),
64        }
65    }
66
67    // JSON: MCP tool-call arguments / result are the tool's own JSON.
68    pub fn tool_execution(tool_name: impl Into<String>, tool_arguments: serde_json::Value) -> Self {
69        Self::ToolExecution {
70            tool_name: tool_name.into(),
71            tool_arguments,
72            tool_result: None,
73        }
74    }
75
76    #[must_use]
77    pub const fn completion() -> Self {
78        Self::Completion
79    }
80
81    #[must_use]
82    pub const fn step_type(&self) -> StepType {
83        match self {
84            Self::Understanding => StepType::Understanding,
85            Self::Planning { .. } => StepType::Planning,
86            Self::SkillUsage { .. } => StepType::SkillUsage,
87            Self::ToolExecution { .. } => StepType::ToolExecution,
88            Self::Completion => StepType::Completion,
89        }
90    }
91
92    #[must_use]
93    pub fn title(&self) -> String {
94        match self {
95            Self::Understanding => "Analyzing request...".to_owned(),
96            Self::Planning { .. } => "Planning response...".to_owned(),
97            Self::SkillUsage { skill_name, .. } => format!("Using {skill_name} skill..."),
98            Self::ToolExecution { tool_name, .. } => format!("Running {tool_name}..."),
99            Self::Completion => "Complete".to_owned(),
100        }
101    }
102
103    #[must_use]
104    pub const fn is_instant(&self) -> bool {
105        !matches!(self, Self::ToolExecution { .. })
106    }
107
108    #[must_use]
109    pub fn tool_name(&self) -> Option<&str> {
110        match self {
111            Self::ToolExecution { tool_name, .. } => Some(tool_name),
112            Self::SkillUsage { skill_name, .. } => Some(skill_name),
113            Self::Understanding | Self::Planning { .. } | Self::Completion => None,
114        }
115    }
116
117    #[must_use]
118    // JSON: MCP tool-call arguments / result are the tool's own JSON.
119    pub const fn tool_arguments(&self) -> Option<&serde_json::Value> {
120        match self {
121            Self::ToolExecution { tool_arguments, .. } => Some(tool_arguments),
122            Self::Understanding
123            | Self::Planning { .. }
124            | Self::SkillUsage { .. }
125            | Self::Completion => None,
126        }
127    }
128
129    #[must_use]
130    // JSON: MCP tool-call arguments / result are the tool's own JSON.
131    pub const fn tool_result(&self) -> Option<&serde_json::Value> {
132        match self {
133            Self::ToolExecution { tool_result, .. } => tool_result.as_ref(),
134            Self::Understanding
135            | Self::Planning { .. }
136            | Self::SkillUsage { .. }
137            | Self::Completion => None,
138        }
139    }
140
141    #[must_use]
142    pub fn reasoning(&self) -> Option<&str> {
143        match self {
144            Self::Planning { reasoning, .. } => reasoning.as_deref(),
145            Self::Understanding
146            | Self::SkillUsage { .. }
147            | Self::ToolExecution { .. }
148            | Self::Completion => None,
149        }
150    }
151
152    #[must_use]
153    pub fn planned_tools(&self) -> Option<&[PlannedTool]> {
154        match self {
155            Self::Planning { planned_tools, .. } => planned_tools.as_deref(),
156            Self::Understanding
157            | Self::SkillUsage { .. }
158            | Self::ToolExecution { .. }
159            | Self::Completion => None,
160        }
161    }
162
163    #[must_use]
164    // JSON: MCP tool-call arguments / result are the tool's own JSON.
165    pub fn with_tool_result(self, result: serde_json::Value) -> Self {
166        match self {
167            Self::ToolExecution {
168                tool_name,
169                tool_arguments,
170                ..
171            } => Self::ToolExecution {
172                tool_name,
173                tool_arguments,
174                tool_result: Some(result),
175            },
176            other @ (Self::Understanding
177            | Self::Planning { .. }
178            | Self::SkillUsage { .. }
179            | Self::Completion) => other,
180        }
181    }
182}