harness_rs_core/
context.rs1use crate::{ModelOutput, Signal};
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
10#[non_exhaustive]
11pub enum Block {
12 Text(String),
14 FileRef { path: String, hash: Option<String>, excerpt: Option<String> },
17 Skill { name: String, body: String },
19 ToolCall { call_id: String, name: String, args: serde_json::Value },
21 ToolResult { call_id: String, content: serde_json::Value },
23 Feedback(Vec<Signal>),
25 Reasoning(String),
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct Turn {
34 pub role: TurnRole,
35 pub blocks: Vec<Block>,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(rename_all = "lowercase")]
40#[non_exhaustive]
41pub enum TurnRole {
42 User,
43 Assistant,
44 System,
45 Tool,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Task {
50 pub description: String,
51 pub source: Option<String>, pub deadline: Option<i64>,
53}
54
55#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
56pub struct Policy {
57 pub max_iters: u32,
58 pub max_input_tokens: u32,
59 pub max_output_tokens: u32,
60 pub self_correct_rounds: u32,
61}
62
63impl Default for Policy {
64 fn default() -> Self {
65 Self {
66 max_iters: 50,
67 max_input_tokens: 150_000,
68 max_output_tokens: 8_000,
69 self_correct_rounds: 3,
70 }
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct Context {
77 pub system: Vec<Block>,
78 pub guides: Vec<Block>,
79 pub history: Vec<Turn>,
80 pub task: Task,
81 pub policy: Policy,
82 pub metadata: BTreeMap<String, serde_json::Value>,
83 pub tools: Vec<crate::ToolSchema>,
86}
87
88impl Context {
89 pub fn new(task: Task) -> Self {
90 Self {
91 system: Vec::new(),
92 guides: Vec::new(),
93 history: Vec::new(),
94 task,
95 policy: Policy::default(),
96 metadata: BTreeMap::new(),
97 tools: Vec::new(),
98 }
99 }
100
101 pub fn push_model_output(&mut self, out: &ModelOutput) {
105 let mut blocks = Vec::new();
106 if let Some(r) = &out.reasoning
107 && !r.is_empty()
108 {
109 blocks.push(Block::Reasoning(r.clone()));
110 }
111 if let Some(t) = &out.text
112 && !t.is_empty()
113 {
114 blocks.push(Block::Text(t.clone()));
115 }
116 for c in &out.tool_calls {
117 blocks.push(Block::ToolCall {
118 call_id: c.id.clone(),
119 name: c.name.clone(),
120 args: c.args.clone(),
121 });
122 }
123 self.history.push(Turn { role: TurnRole::Assistant, blocks });
124 }
125
126 pub fn push_feedback(&mut self, signals: Vec<Signal>) {
128 self.history.push(Turn {
129 role: TurnRole::Tool,
130 blocks: vec![Block::Feedback(signals)],
131 });
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct Action {
138 pub tool: String,
139 pub call_id: String,
140 pub args: serde_json::Value,
141}