Skip to main content

vv_agent/
result.rs

1use crate::agent::Agent;
2use crate::config::ResolvedModelConfig;
3use crate::run_config::RunConfig;
4use crate::types::{AgentResult, AgentStatus};
5
6#[derive(Clone)]
7pub struct RunResult {
8    agent_name: String,
9    result: AgentResult,
10    resolved: ResolvedModelConfig,
11    resume_context: Option<RunResumeContext>,
12}
13
14impl RunResult {
15    pub fn new(
16        agent_name: impl Into<String>,
17        result: AgentResult,
18        resolved: ResolvedModelConfig,
19    ) -> Self {
20        Self {
21            agent_name: agent_name.into(),
22            result,
23            resolved,
24            resume_context: None,
25        }
26    }
27
28    pub fn agent_name(&self) -> &str {
29        &self.agent_name
30    }
31
32    pub fn status(&self) -> AgentStatus {
33        self.result.status
34    }
35
36    pub fn final_output(&self) -> Option<&str> {
37        self.result.final_answer.as_deref()
38    }
39
40    pub fn result(&self) -> &AgentResult {
41        &self.result
42    }
43
44    pub fn resolved_model(&self) -> &ResolvedModelConfig {
45        &self.resolved
46    }
47
48    pub fn into_state(self) -> Result<RunState, String> {
49        RunState::from_result(self)
50    }
51
52    pub(crate) fn with_resume_context(mut self, context: RunResumeContext) -> Self {
53        self.resume_context = Some(context);
54        self
55    }
56
57    pub(crate) fn resume_context(&self) -> Option<&RunResumeContext> {
58        self.resume_context.as_ref()
59    }
60}
61
62#[derive(Clone)]
63pub struct RunState {
64    result: RunResult,
65    approved_interruption_ids: Vec<String>,
66}
67
68impl RunState {
69    pub fn from_result(result: RunResult) -> Result<Self, String> {
70        if result.status() != AgentStatus::WaitUser {
71            return Err("only interrupted runs can be converted into RunState".to_string());
72        }
73        Ok(Self {
74            result,
75            approved_interruption_ids: Vec::new(),
76        })
77    }
78
79    pub fn result(&self) -> &RunResult {
80        &self.result
81    }
82
83    pub fn approve(&mut self, interruption_id: &str) -> Result<(), String> {
84        if !self
85            .pending_approval_ids()
86            .iter()
87            .any(|id| id == interruption_id)
88        {
89            return Err(format!("unknown approval interruption: {interruption_id}"));
90        }
91        if !self
92            .approved_interruption_ids
93            .iter()
94            .any(|id| id == interruption_id)
95        {
96            self.approved_interruption_ids
97                .push(interruption_id.to_string());
98        }
99        Ok(())
100    }
101
102    pub fn approved_interruption_ids(&self) -> &[String] {
103        &self.approved_interruption_ids
104    }
105
106    pub fn pending_approval_ids(&self) -> Vec<String> {
107        self.result
108            .result()
109            .cycles
110            .iter()
111            .flat_map(|cycle| cycle.tool_results.iter())
112            .filter_map(|tool_result| {
113                tool_result
114                    .metadata
115                    .get("approval_interruption_id")
116                    .and_then(serde_json::Value::as_str)
117                    .map(str::to_string)
118            })
119            .collect()
120    }
121
122    pub(crate) fn into_inner(self) -> (RunResult, Vec<String>) {
123        (self.result, self.approved_interruption_ids)
124    }
125}
126
127#[derive(Clone)]
128pub(crate) struct RunResumeContext {
129    pub agent: Agent,
130    pub input: crate::runner::NormalizedInput,
131    pub config: RunConfig,
132}