Skip to main content

stasis/application/orchestration/
sequential_pattern_pipeline.rs

1use crate::application::orchestration::prompt_pipeline::{
2    PromptExecutionContext, PromptExecutionPipeline, PromptExecutionRequest,
3};
4use crate::application::runtime::chat_options_resolver::resolve_reasoning_effort;
5use crate::domain::errors::Result;
6
7#[derive(Clone, Debug)]
8pub struct SequentialPatternStage {
9    pub stage_id: String,
10    pub user_prompt_template: String,
11    pub system_prompt: Option<String>,
12    pub policy_profile: Option<String>,
13    pub model_hint: Option<String>,
14    pub reasoning_effort: Option<String>,
15}
16
17#[derive(Clone, Debug)]
18pub struct SequentialPatternExecutionRequest {
19    pub initial_user_prompt: String,
20    pub trace_id: Option<String>,
21    pub correlation_id: Option<String>,
22    pub policy_profile: Option<String>,
23    pub model_hint: Option<String>,
24    pub reasoning_effort: Option<String>,
25    pub stages: Vec<SequentialPatternStage>,
26}
27
28#[derive(Clone, Debug)]
29pub struct SequentialPatternStageResult {
30    pub stage_id: String,
31    pub rendered_prompt: String,
32    pub output_text: String,
33}
34
35#[derive(Clone, Debug)]
36pub struct SequentialPatternExecutionResponse {
37    pub final_text: String,
38    pub stages: Vec<SequentialPatternStageResult>,
39    pub termination_reason: String,
40}
41
42#[derive(Clone)]
43pub struct SequentialPatternPipeline {
44    prompt_pipeline: PromptExecutionPipeline,
45}
46
47impl SequentialPatternPipeline {
48    pub fn new(prompt_pipeline: PromptExecutionPipeline) -> Self {
49        Self { prompt_pipeline }
50    }
51
52    pub async fn execute(
53        &self,
54        request: SequentialPatternExecutionRequest,
55    ) -> Result<SequentialPatternExecutionResponse> {
56        let mut current_input = request.initial_user_prompt;
57        let mut stage_results = Vec::with_capacity(request.stages.len());
58
59        for stage in request.stages {
60            let rendered_prompt = stage
61                .user_prompt_template
62                .replace("{{input}}", &current_input)
63                .replace("{input}", &current_input);
64
65            let context = PromptExecutionContext {
66                trace_id: request.trace_id.clone(),
67                correlation_id: request.correlation_id.clone(),
68                policy_profile: stage
69                    .policy_profile
70                    .clone()
71                    .or_else(|| request.policy_profile.clone()),
72                model_hint: stage
73                    .model_hint
74                    .clone()
75                    .or_else(|| request.model_hint.clone()),
76                reasoning_effort: resolve_reasoning_effort(
77                    stage.reasoning_effort.clone(),
78                    request.reasoning_effort.clone(),
79                ),
80            };
81
82            let mut prompt_request =
83                PromptExecutionRequest::from_user_prompt(rendered_prompt.clone())
84                    .with_context(context);
85            if let Some(system_prompt) = stage.system_prompt {
86                prompt_request = prompt_request.with_system_prompt(system_prompt);
87            }
88
89            let response = self.prompt_pipeline.execute(prompt_request).await?;
90            current_input = response.text.clone();
91            stage_results.push(SequentialPatternStageResult {
92                stage_id: stage.stage_id,
93                rendered_prompt,
94                output_text: response.text,
95            });
96        }
97
98        Ok(SequentialPatternExecutionResponse {
99            final_text: current_input,
100            stages: stage_results,
101            termination_reason: "completed_all_stages".to_string(),
102        })
103    }
104}