systemprompt-models 0.6.1

Foundation data models for systemprompt.io AI governance infrastructure. Shared DTOs, config, and domain types consumed by every layer of the MCP governance pipeline.
Documentation
//! Per-kind step content payload and the [`PlannedTool`] descriptor.

use serde::{Deserialize, Serialize};
use systemprompt_identifiers::SkillId;

use super::enums::StepType;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PlannedTool {
    pub tool_name: String,
    pub arguments: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StepContent {
    Understanding,
    Planning {
        #[serde(skip_serializing_if = "Option::is_none")]
        reasoning: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        planned_tools: Option<Vec<PlannedTool>>,
    },
    SkillUsage {
        skill_id: SkillId,
        skill_name: String,
    },
    ToolExecution {
        tool_name: String,
        tool_arguments: serde_json::Value,
        #[serde(skip_serializing_if = "Option::is_none")]
        tool_result: Option<serde_json::Value>,
    },
    Completion,
}

impl StepContent {
    #[must_use]
    pub const fn understanding() -> Self {
        Self::Understanding
    }

    #[must_use]
    pub const fn planning(
        reasoning: Option<String>,
        planned_tools: Option<Vec<PlannedTool>>,
    ) -> Self {
        Self::Planning {
            reasoning,
            planned_tools,
        }
    }

    pub fn skill_usage(skill_id: SkillId, skill_name: impl Into<String>) -> Self {
        Self::SkillUsage {
            skill_id,
            skill_name: skill_name.into(),
        }
    }

    pub fn tool_execution(tool_name: impl Into<String>, tool_arguments: serde_json::Value) -> Self {
        Self::ToolExecution {
            tool_name: tool_name.into(),
            tool_arguments,
            tool_result: None,
        }
    }

    #[must_use]
    pub const fn completion() -> Self {
        Self::Completion
    }

    #[must_use]
    pub const fn step_type(&self) -> StepType {
        match self {
            Self::Understanding => StepType::Understanding,
            Self::Planning { .. } => StepType::Planning,
            Self::SkillUsage { .. } => StepType::SkillUsage,
            Self::ToolExecution { .. } => StepType::ToolExecution,
            Self::Completion => StepType::Completion,
        }
    }

    #[must_use]
    pub fn title(&self) -> String {
        match self {
            Self::Understanding => "Analyzing request...".to_string(),
            Self::Planning { .. } => "Planning response...".to_string(),
            Self::SkillUsage { skill_name, .. } => format!("Using {skill_name} skill..."),
            Self::ToolExecution { tool_name, .. } => format!("Running {tool_name}..."),
            Self::Completion => "Complete".to_string(),
        }
    }

    #[must_use]
    pub const fn is_instant(&self) -> bool {
        !matches!(self, Self::ToolExecution { .. })
    }

    #[must_use]
    pub fn tool_name(&self) -> Option<&str> {
        match self {
            Self::ToolExecution { tool_name, .. } => Some(tool_name),
            Self::SkillUsage { skill_name, .. } => Some(skill_name),
            Self::Understanding | Self::Planning { .. } | Self::Completion => None,
        }
    }

    #[must_use]
    pub const fn tool_arguments(&self) -> Option<&serde_json::Value> {
        match self {
            Self::ToolExecution { tool_arguments, .. } => Some(tool_arguments),
            Self::Understanding
            | Self::Planning { .. }
            | Self::SkillUsage { .. }
            | Self::Completion => None,
        }
    }

    #[must_use]
    pub const fn tool_result(&self) -> Option<&serde_json::Value> {
        match self {
            Self::ToolExecution { tool_result, .. } => tool_result.as_ref(),
            Self::Understanding
            | Self::Planning { .. }
            | Self::SkillUsage { .. }
            | Self::Completion => None,
        }
    }

    #[must_use]
    pub fn reasoning(&self) -> Option<&str> {
        match self {
            Self::Planning { reasoning, .. } => reasoning.as_deref(),
            Self::Understanding
            | Self::SkillUsage { .. }
            | Self::ToolExecution { .. }
            | Self::Completion => None,
        }
    }

    #[must_use]
    pub fn planned_tools(&self) -> Option<&[PlannedTool]> {
        match self {
            Self::Planning { planned_tools, .. } => planned_tools.as_deref(),
            Self::Understanding
            | Self::SkillUsage { .. }
            | Self::ToolExecution { .. }
            | Self::Completion => None,
        }
    }

    #[must_use]
    pub fn with_tool_result(self, result: serde_json::Value) -> Self {
        match self {
            Self::ToolExecution {
                tool_name,
                tool_arguments,
                ..
            } => Self::ToolExecution {
                tool_name,
                tool_arguments,
                tool_result: Some(result),
            },
            other @ (Self::Understanding
            | Self::Planning { .. }
            | Self::SkillUsage { .. }
            | Self::Completion) => other,
        }
    }
}