gent/interpreter/
types.rs

1//! Value types for the GENT interpreter
2
3use crate::parser::ast::{
4    Block, FieldType, OutputType, Param, StructField, TypeName as ParserTypeName,
5};
6use std::collections::HashMap;
7use std::fmt;
8
9/// Runtime representation of an output schema
10#[derive(Debug, Clone, PartialEq)]
11pub struct OutputSchema {
12    pub fields: Vec<StructField>,
13}
14
15impl OutputSchema {
16    pub fn from_output_type(
17        output_type: &OutputType,
18        structs: &HashMap<String, Vec<StructField>>,
19    ) -> Result<Self, String> {
20        match output_type {
21            OutputType::Inline(fields) => Ok(OutputSchema {
22                fields: fields.clone(),
23            }),
24            OutputType::Named(name) => structs
25                .get(name)
26                .map(|fields| OutputSchema {
27                    fields: fields.clone(),
28                })
29                .ok_or_else(|| format!("Unknown struct: {}", name)),
30        }
31    }
32
33    pub fn to_json_schema(&self) -> serde_json::Value {
34        use serde_json::json;
35
36        let properties: serde_json::Map<String, serde_json::Value> = self
37            .fields
38            .iter()
39            .map(|f| (f.name.clone(), field_type_to_json_schema(&f.field_type)))
40            .collect();
41
42        let required: Vec<String> = self.fields.iter().map(|f| f.name.clone()).collect();
43
44        json!({
45            "type": "object",
46            "properties": properties,
47            "required": required
48        })
49    }
50}
51
52fn field_type_to_json_schema(ft: &FieldType) -> serde_json::Value {
53    use serde_json::json;
54    match ft {
55        FieldType::String => json!({"type": "string"}),
56        FieldType::Number => json!({"type": "number"}),
57        FieldType::Boolean => json!({"type": "boolean"}),
58        FieldType::Array(inner) => json!({
59            "type": "array",
60            "items": field_type_to_json_schema(inner)
61        }),
62        FieldType::Object(fields) => {
63            let properties: serde_json::Map<String, serde_json::Value> = fields
64                .iter()
65                .map(|f| (f.name.clone(), field_type_to_json_schema(&f.field_type)))
66                .collect();
67            let required: Vec<String> = fields.iter().map(|f| f.name.clone()).collect();
68            json!({
69                "type": "object",
70                "properties": properties,
71                "required": required
72            })
73        }
74        FieldType::Named(name) => json!({"$ref": format!("#/definitions/{}", name)}),
75    }
76}
77
78/// Represents a user-defined tool at runtime
79#[derive(Debug, Clone, PartialEq)]
80pub struct UserToolValue {
81    pub name: String,
82    pub params: Vec<Param>,
83    pub return_type: Option<ParserTypeName>,
84    pub body: Block,
85}
86
87/// Represents a user-defined function at runtime (pure, no agent access)
88#[derive(Debug, Clone, PartialEq)]
89pub struct FnValue {
90    pub name: String,
91    pub params: Vec<Param>,
92    pub return_type: Option<ParserTypeName>,
93    pub body: Block,
94}
95
96/// Represents a lambda/closure at runtime
97#[derive(Debug, Clone, PartialEq)]
98pub struct LambdaValue {
99    pub params: Vec<String>,
100    pub body: crate::parser::ast::LambdaBody,
101}
102
103/// Runtime value for a parallel execution block
104#[derive(Debug, Clone, PartialEq)]
105pub struct ParallelValue {
106    pub name: String,
107    pub agents: Vec<crate::parser::ast::Expression>,
108    pub timeout_ms: u64,
109}
110
111/// Definition of an enum type (stored in environment)
112#[derive(Debug, Clone, PartialEq)]
113pub struct EnumDef {
114    pub name: String,
115    pub variants: Vec<EnumVariantDef>,
116}
117
118/// Definition of an enum variant
119#[derive(Debug, Clone, PartialEq)]
120pub struct EnumVariantDef {
121    pub name: String,
122    pub fields: Vec<EnumFieldDef>,
123}
124
125/// Definition of a field in an enum variant
126#[derive(Debug, Clone, PartialEq)]
127pub struct EnumFieldDef {
128    pub name: Option<String>,
129    pub type_name: String,
130}
131
132/// Runtime value of an enum instance
133#[derive(Debug, Clone, PartialEq)]
134pub struct EnumValue {
135    pub enum_name: String,
136    pub variant: String,
137    pub data: Vec<Value>,
138}
139
140/// Intermediate value for enum variant with data (before being called)
141#[derive(Debug, Clone, PartialEq)]
142pub struct EnumConstructor {
143    pub enum_name: String,
144    pub variant: String,
145    pub expected_fields: usize,
146}
147
148/// Runtime values in GENT
149#[derive(Debug, Clone, PartialEq)]
150pub enum Value {
151    /// String value
152    String(String),
153    /// Numeric value (f64)
154    Number(f64),
155    /// Boolean value
156    Boolean(bool),
157    /// Null/none value
158    Null,
159    /// Agent value
160    Agent(AgentValue),
161    /// Array value
162    Array(Vec<Value>),
163    /// Object value (key-value map)
164    Object(HashMap<String, Value>),
165    /// User-defined tool
166    Tool(UserToolValue),
167    /// User-defined function (pure, no agent access)
168    Function(FnValue),
169    /// Lambda/closure value
170    Lambda(LambdaValue),
171    /// Enum value
172    Enum(EnumValue),
173    /// Enum constructor (intermediate value before calling with args)
174    EnumConstructor(EnumConstructor),
175    /// Parallel execution block
176    Parallel(ParallelValue),
177}
178
179/// Represents a defined agent at runtime
180#[derive(Debug, Clone, PartialEq)]
181pub struct AgentValue {
182    /// Name of the agent
183    pub name: String,
184    /// System prompt for the agent
185    pub system_prompt: String,
186    /// User prompt for the agent (optional)
187    pub user_prompt: Option<String>,
188    /// Tools available to this agent
189    pub tools: Vec<String>,
190    /// Maximum steps before stopping (None = default 10)
191    pub max_steps: Option<u32>,
192    /// Model to use (None = default)
193    pub model: Option<String>,
194    /// Output schema for structured responses
195    pub output_schema: Option<OutputSchema>,
196    /// Number of retries for output validation
197    pub output_retries: u32,
198    /// Custom instructions for schema output (None = default)
199    pub output_instructions: Option<String>,
200    /// Custom prompt for validation retries (None = default)
201    pub retry_prompt: Option<String>,
202}
203
204impl AgentValue {
205    /// Create a new agent value
206    pub fn new(name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
207        Self {
208            name: name.into(),
209            system_prompt: system_prompt.into(),
210            user_prompt: None,
211            tools: Vec::new(),
212            max_steps: None,
213            model: None,
214            output_schema: None,
215            output_retries: 1, // default: retry once
216            output_instructions: None,
217            retry_prompt: None,
218        }
219    }
220
221    /// Add tools to the agent
222    pub fn with_tools(mut self, tools: Vec<String>) -> Self {
223        self.tools = tools;
224        self
225    }
226
227    /// Set max steps
228    pub fn with_max_steps(mut self, steps: u32) -> Self {
229        self.max_steps = Some(steps);
230        self
231    }
232
233    /// Set model
234    pub fn with_model(mut self, model: impl Into<String>) -> Self {
235        self.model = Some(model.into());
236        self
237    }
238
239    /// Set output schema
240    pub fn with_output_schema(mut self, schema: OutputSchema) -> Self {
241        self.output_schema = Some(schema);
242        self
243    }
244
245    /// Set output retries
246    pub fn with_output_retries(mut self, retries: u32) -> Self {
247        self.output_retries = retries;
248        self
249    }
250
251    /// Set custom output instructions
252    pub fn with_output_instructions(mut self, instructions: impl Into<String>) -> Self {
253        self.output_instructions = Some(instructions.into());
254        self
255    }
256
257    /// Set custom retry prompt
258    pub fn with_retry_prompt(mut self, prompt: impl Into<String>) -> Self {
259        self.retry_prompt = Some(prompt.into());
260        self
261    }
262
263    /// Set user prompt
264    pub fn with_user_prompt(mut self, prompt: impl Into<String>) -> Self {
265        self.user_prompt = Some(prompt.into());
266        self
267    }
268}
269
270impl fmt::Display for Value {
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        match self {
273            Value::String(s) => write!(f, "{}", s),
274            Value::Number(n) => {
275                if n.fract() == 0.0 {
276                    write!(f, "{}", *n as i64)
277                } else {
278                    write!(f, "{}", n)
279                }
280            }
281            Value::Boolean(b) => write!(f, "{}", b),
282            Value::Null => write!(f, "null"),
283            Value::Agent(agent) => write!(f, "<agent {}>", agent.name),
284            Value::Array(items) => {
285                let formatted: Vec<String> = items.iter().map(|v| format!("{}", v)).collect();
286                write!(f, "[{}]", formatted.join(", "))
287            }
288            Value::Object(map) => {
289                let formatted: Vec<String> =
290                    map.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
291                write!(f, "{{{}}}", formatted.join(", "))
292            }
293            Value::Tool(t) => write!(f, "<tool {}>", t.name),
294            Value::Function(func) => write!(f, "<fn {}>", func.name),
295            Value::Lambda(_) => write!(f, "<lambda>"),
296            Value::Enum(e) => {
297                if e.data.is_empty() {
298                    write!(f, "{}.{}", e.enum_name, e.variant)
299                } else {
300                    let data_str: Vec<String> = e.data.iter().map(|v| v.to_string()).collect();
301                    write!(f, "{}.{}({})", e.enum_name, e.variant, data_str.join(", "))
302                }
303            }
304            Value::EnumConstructor(c) => {
305                write!(f, "<enum constructor {}.{}>", c.enum_name, c.variant)
306            }
307            Value::Parallel(p) => write!(f, "<parallel {}>", p.name),
308        }
309    }
310}
311
312impl fmt::Display for AgentValue {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "<agent {}>", self.name)
315    }
316}
317
318impl Value {
319    /// Check if value is truthy
320    pub fn is_truthy(&self) -> bool {
321        match self {
322            Value::Boolean(b) => *b,
323            Value::Null => false,
324            Value::String(s) => !s.is_empty(),
325            Value::Number(n) => *n != 0.0,
326            Value::Agent(_) => true,
327            Value::Array(items) => !items.is_empty(),
328            Value::Object(map) => !map.is_empty(),
329            Value::Tool(_) => true,
330            Value::Function(_) => true,
331            Value::Lambda(_) => true,
332            Value::Enum(_) => true,
333            Value::EnumConstructor(_) => true,
334            Value::Parallel(_) => true,
335        }
336    }
337
338    /// Get type name for error messages
339    pub fn type_name(&self) -> String {
340        match self {
341            Value::String(_) => "String".to_string(),
342            Value::Number(_) => "Number".to_string(),
343            Value::Boolean(_) => "Boolean".to_string(),
344            Value::Null => "Null".to_string(),
345            Value::Agent(_) => "Agent".to_string(),
346            Value::Array(_) => "Array".to_string(),
347            Value::Object(_) => "Object".to_string(),
348            Value::Tool(_) => "Tool".to_string(),
349            Value::Function(_) => "Function".to_string(),
350            Value::Lambda(_) => "Lambda".to_string(),
351            Value::Enum(e) => format!("{}.{}", e.enum_name, e.variant),
352            Value::EnumConstructor(c) => format!("EnumConstructor({}.{})", c.enum_name, c.variant),
353            Value::Parallel(_) => "parallel".to_string(),
354        }
355    }
356
357    /// Try to get as string
358    pub fn as_string(&self) -> Option<&String> {
359        match self {
360            Value::String(s) => Some(s),
361            _ => None,
362        }
363    }
364
365    /// Try to get as agent
366    pub fn as_agent(&self) -> Option<&AgentValue> {
367        match self {
368            Value::Agent(a) => Some(a),
369            _ => None,
370        }
371    }
372
373    /// Try to get as array
374    pub fn as_array(&self) -> Option<&Vec<Value>> {
375        match self {
376            Value::Array(arr) => Some(arr),
377            _ => None,
378        }
379    }
380
381    /// Try to get as object
382    pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
383        match self {
384            Value::Object(map) => Some(map),
385            _ => None,
386        }
387    }
388
389    /// Try to get as tool
390    pub fn as_tool(&self) -> Option<&UserToolValue> {
391        match self {
392            Value::Tool(t) => Some(t),
393            _ => None,
394        }
395    }
396
397    /// Try to get as function
398    pub fn as_function(&self) -> Option<&FnValue> {
399        match self {
400            Value::Function(f) => Some(f),
401            _ => None,
402        }
403    }
404}