Skip to main content

systemprompt_models/execution/step/
content.rs

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