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