Skip to main content

tiny_agent_rs/core/
steps.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4/// Represents a single step in the agent's reasoning process
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type", rename_all = "snake_case")]
7pub enum AgentStep {
8    /// Initial task provided by the user
9    Task { content: String },
10    /// Planning step where the agent thinks about how to approach the task
11    Planning { plan: String },
12    /// Action step where the agent calls a tool
13    Action {
14        tool_name: String,
15        tool_call_id: String,
16        arguments: Value,
17    },
18    /// Observation step capturing the result of a tool execution
19    Observation {
20        tool_call_id: String,
21        result: String,
22        is_error: bool,
23    },
24    /// Final answer from the agent
25    FinalAnswer {
26        answer: String,
27        #[serde(default)]
28        #[serde(skip_serializing_if = "Option::is_none")]
29        structured: Option<Value>,
30    },
31}
32
33impl AgentStep {
34    /// Convert step to OpenAI message format
35    pub fn to_message(&self) -> Value {
36        match self {
37            AgentStep::Task { content } => {
38                serde_json::json!({
39                    "role": "user",
40                    "content": content
41                })
42            }
43            AgentStep::Planning { plan } => {
44                serde_json::json!({
45                    "role": "assistant",
46                    "content": plan
47                })
48            }
49            AgentStep::Action {
50                tool_name,
51                tool_call_id,
52                arguments,
53            } => {
54                serde_json::json!({
55                    "role": "assistant",
56                    "content": null,
57                    "tool_calls": [{
58                        "id": tool_call_id,
59                        "type": "function",
60                        "function": {
61                            "name": tool_name,
62                            "arguments": serde_json::to_string(arguments).unwrap_or_default()
63                        }
64                    }]
65                })
66            }
67            AgentStep::Observation {
68                tool_call_id,
69                result,
70                ..
71            } => {
72                serde_json::json!({
73                    "role": "tool",
74                    "tool_call_id": tool_call_id,
75                    "content": result
76                })
77            }
78            AgentStep::FinalAnswer { answer, .. } => {
79                serde_json::json!({
80                    "role": "assistant",
81                    "content": answer
82                })
83            }
84        }
85    }
86
87    /// Get a human-readable description of the step
88    pub fn describe(&self) -> String {
89        match self {
90            AgentStep::Task { content } => format!("🧭 Task: {}", content),
91            AgentStep::Planning { plan } => format!("🧩 Plan: {}", plan),
92            AgentStep::Action {
93                tool_name,
94                arguments,
95                ..
96            } => {
97                format!("🔧 Action: {}({})", tool_name, arguments)
98            }
99            AgentStep::Observation {
100                result, is_error, ..
101            } => {
102                if *is_error {
103                    format!("❌ Error: {}", result)
104                } else {
105                    format!("👁 Observation: {}", result)
106                }
107            }
108            AgentStep::FinalAnswer { answer, .. } => format!("✅ Final Answer: {}", answer),
109        }
110    }
111}