systemprompt_agent/services/
execution_tracking.rs1use anyhow::Result;
2use chrono::{DateTime, Utc};
3use std::sync::Arc;
4use systemprompt_identifiers::{SkillId, TaskId};
5use systemprompt_models::{ExecutionStep, PlannedTool, StepContent, StepId, TrackedStep};
6
7use crate::repository::execution::ExecutionStepRepository;
8
9#[derive(Debug, Clone)]
10pub struct ExecutionTrackingService {
11 repository: Arc<ExecutionStepRepository>,
12}
13
14impl ExecutionTrackingService {
15 pub const fn new(repository: Arc<ExecutionStepRepository>) -> Self {
16 Self { repository }
17 }
18
19 pub async fn track(&self, task_id: TaskId, content: StepContent) -> Result<ExecutionStep> {
20 let step = ExecutionStep::new(task_id, content);
21 self.repository.create(&step).await?;
22 Ok(step)
23 }
24
25 pub async fn track_async(
26 &self,
27 task_id: TaskId,
28 content: StepContent,
29 ) -> Result<(TrackedStep, ExecutionStep)> {
30 let step = ExecutionStep::new(task_id, content);
31 self.repository.create(&step).await?;
32
33 let tracked = TrackedStep {
34 step_id: step.step_id.clone(),
35 started_at: step.started_at,
36 };
37
38 Ok((tracked, step))
39 }
40
41 pub async fn complete(
42 &self,
43 tracked: TrackedStep,
44 result: Option<serde_json::Value>,
45 ) -> Result<()> {
46 self.repository
47 .complete_step(&tracked.step_id, tracked.started_at, result)
48 .await
49 }
50
51 pub async fn complete_planning(
52 &self,
53 tracked: TrackedStep,
54 reasoning: Option<String>,
55 planned_tools: Option<Vec<PlannedTool>>,
56 ) -> Result<ExecutionStep> {
57 self.repository
58 .complete_planning_step(
59 &tracked.step_id,
60 tracked.started_at,
61 reasoning,
62 planned_tools,
63 )
64 .await
65 }
66
67 pub async fn fail(&self, tracked: &TrackedStep, error: String) -> Result<()> {
68 self.repository
69 .fail_step(&tracked.step_id, tracked.started_at, &error)
70 .await
71 }
72
73 pub async fn fail_step(
74 &self,
75 step_id: &StepId,
76 started_at: DateTime<Utc>,
77 error: String,
78 ) -> Result<()> {
79 self.repository.fail_step(step_id, started_at, &error).await
80 }
81
82 pub async fn get_steps_by_task(&self, task_id: &TaskId) -> Result<Vec<ExecutionStep>> {
83 self.repository.list_by_task(task_id).await
84 }
85
86 pub async fn get_step(&self, step_id: &StepId) -> Result<Option<ExecutionStep>> {
87 self.repository.get(step_id).await
88 }
89
90 pub async fn fail_in_progress_steps(&self, task_id: &TaskId, error: &str) -> Result<u64> {
91 self.repository
92 .fail_in_progress_steps_for_task(task_id, error)
93 .await
94 }
95
96 pub async fn track_understanding(&self, task_id: TaskId) -> Result<ExecutionStep> {
97 self.track(task_id, StepContent::understanding()).await
98 }
99
100 pub async fn track_planning(
101 &self,
102 task_id: TaskId,
103 reasoning: Option<String>,
104 planned_tools: Option<Vec<PlannedTool>>,
105 ) -> Result<ExecutionStep> {
106 self.track(task_id, StepContent::planning(reasoning, planned_tools))
107 .await
108 }
109
110 pub async fn track_planning_async(
111 &self,
112 task_id: TaskId,
113 reasoning: Option<String>,
114 planned_tools: Option<Vec<PlannedTool>>,
115 ) -> Result<(TrackedStep, ExecutionStep)> {
116 self.track_async(task_id, StepContent::planning(reasoning, planned_tools))
117 .await
118 }
119
120 pub async fn track_skill_usage(
121 &self,
122 task_id: TaskId,
123 skill_id: SkillId,
124 skill_name: impl Into<String>,
125 ) -> Result<ExecutionStep> {
126 self.track(task_id, StepContent::skill_usage(skill_id, skill_name))
127 .await
128 }
129
130 pub async fn track_tool_execution(
131 &self,
132 task_id: TaskId,
133 tool_name: impl Into<String>,
134 tool_arguments: serde_json::Value,
135 ) -> Result<(TrackedStep, ExecutionStep)> {
136 self.track_async(
137 task_id,
138 StepContent::tool_execution(tool_name, tool_arguments),
139 )
140 .await
141 }
142
143 pub async fn track_completion(&self, task_id: TaskId) -> Result<ExecutionStep> {
144 self.track(task_id, StepContent::completion()).await
145 }
146}