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