Skip to content

Commit 04a42c7

Browse files
committed
datablock support
Signed-off-by: Nick Mitchell <[email protected]>
1 parent 46e775a commit 04a42c7

File tree

7 files changed

+202
-26
lines changed

7 files changed

+202
-26
lines changed

pdl-live-react/src-tauri/src/pdl/ast.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,51 @@ pub struct MessageBlock {
381381
pub tool_call_id: Option<String>,
382382
}
383383

384+
/// Return the object where the value of each field is defined by a
385+
/// block. If the body of the object is an array, the resulting object
386+
/// is the union of the objects computed by each element of the array.
384387
#[derive(Serialize, Deserialize, Debug, Clone)]
385388
pub struct ObjectBlock {
386389
pub object: HashMap<String, PdlBlock>,
387390
}
388391

392+
/// Arbitrary value, equivalent to JSON.
393+
///
394+
/// Example. As part of a `defs` section, set `numbers` to the list `[1, 2, 3, 4]`:
395+
/// ```PDL
396+
/// defs:
397+
/// numbers:
398+
/// data: [1, 2, 3, 4]
399+
/// ```
400+
///
401+
/// Example. Evaluate `${ TEST.answer }` in
402+
/// [Jinja](https://jinja.palletsprojects.com/en/stable/), passing
403+
/// the result to a regex parser with capture groups. Set
404+
/// `EXTRACTED_GROUND_TRUTH` to an object with attribute `answer`,
405+
/// a string, containing the value of the capture group.
406+
/// ```PDL
407+
/// - data: ${ TEST.answer }
408+
/// parser:
409+
/// regex: "(.|\\n)*#### (?P<answer>([0-9])*)\\n*"
410+
/// spec:
411+
/// answer: str
412+
/// def: EXTRACTED_GROUND_TRUTH
413+
/// ```
414+
#[derive(Serialize, Deserialize, Debug, Clone)]
415+
pub struct DataBlock {
416+
pub data: Value,
417+
418+
/// Do not evaluate expressions inside strings.
419+
#[serde(skip_serializing_if = "Option::is_none")]
420+
pub raw: Option<bool>,
421+
422+
#[serde(skip_serializing_if = "Option::is_none")]
423+
pub def: Option<String>,
424+
425+
#[serde(skip_serializing_if = "Option::is_none")]
426+
pub parser: Option<PdlParser>,
427+
}
428+
389429
/// Execute a piece of Python code.
390430
///
391431
/// Example:
@@ -491,6 +531,7 @@ pub enum PdlBlock {
491531
String(String),
492532
If(IfBlock),
493533
Include(IncludeBlock),
534+
Data(DataBlock),
494535
Object(ObjectBlock),
495536
Call(CallBlock),
496537
Array(ArrayBlock),
@@ -557,6 +598,11 @@ impl From<String> for PdlResult {
557598
PdlResult::String(s)
558599
}
559600
}
601+
impl From<&bool> for PdlResult {
602+
fn from(b: &bool) -> Self {
603+
PdlResult::Bool(*b)
604+
}
605+
}
560606
impl From<Number> for PdlResult {
561607
fn from(n: Number) -> Self {
562608
PdlResult::Number(n)

pdl-live-react/src-tauri/src/pdl/interpreter.rs

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use serde_json::{from_str, to_string, Value};
2525
use serde_norway::{from_reader, from_str as from_yaml_str};
2626

2727
use crate::pdl::ast::{
28-
ArrayBlock, CallBlock, Closure, FunctionBlock, IfBlock, IncludeBlock, ListOrString,
28+
ArrayBlock, CallBlock, Closure, DataBlock, FunctionBlock, IfBlock, IncludeBlock, ListOrString,
2929
MessageBlock, ModelBlock, ObjectBlock, PdlBlock, PdlParser, PdlResult, PdlUsage,
3030
PythonCodeBlock, ReadBlock, RepeatBlock, Role, Scope, SequencingBlock, StringOrBoolean,
3131
};
@@ -102,6 +102,7 @@ impl<'a> Interpreter<'a> {
102102
PdlBlock::If(block) => self.run_if(block, context).await,
103103
PdlBlock::Include(block) => self.run_include(block, context).await,
104104
PdlBlock::Model(block) => self.run_model(block, context).await,
105+
PdlBlock::Data(block) => self.run_data(block, context).await,
105106
PdlBlock::Object(block) => self.run_object(block, context).await,
106107
PdlBlock::PythonCode(block) => self.run_python_code(block, context).await,
107108
PdlBlock::Read(block) => self.run_read(block, context).await,
@@ -183,7 +184,6 @@ impl<'a> Interpreter<'a> {
183184
})
184185
.collect::<Result<_, _>>()?,
185186
)),
186-
// v => Ok(v.clone()),
187187
}
188188
}
189189

@@ -252,9 +252,6 @@ impl<'a> Interpreter<'a> {
252252
}?;
253253

254254
if let Some(def) = &variable {
255-
if self.debug {
256-
eprintln!("def {def} -> {:?}", value);
257-
}
258255
if let Some(scope) = self.scope.last_mut() {
259256
if self.debug {
260257
eprintln!("Def {} -> {}", def, result);
@@ -575,6 +572,39 @@ impl<'a> Interpreter<'a> {
575572
}
576573
}
577574

575+
fn resultify(&self, value: &Value) -> PdlResult {
576+
match value {
577+
Value::Null => "".into(),
578+
Value::Bool(b) => b.into(),
579+
Value::Number(n) => n.clone().into(),
580+
Value::String(s) => s.clone().into(),
581+
Value::Array(a) => {
582+
PdlResult::List(a.iter().map(|v| self.resultify(v)).collect::<Vec<_>>())
583+
}
584+
Value::Object(m) => PdlResult::Dict(
585+
m.iter()
586+
.map(|(k, v)| (k.clone(), self.resultify(v)))
587+
.collect::<HashMap<_, _>>(),
588+
),
589+
}
590+
}
591+
592+
async fn run_data(&mut self, block: &DataBlock, _context: Context) -> Interpretation {
593+
if self.debug {
594+
eprintln!("Data raw={:?} {:?}", block.raw, block.data);
595+
}
596+
597+
let mut trace = block.clone();
598+
if let Some(true) = block.raw {
599+
let result = self.def(&block.def, &self.resultify(&block.data), &block.parser)?;
600+
Ok((result, vec![], PdlBlock::Data(trace)))
601+
} else {
602+
let result = self.def(&block.def, &self.eval_complex(&block.data)?, &block.parser)?;
603+
trace.data = from_str(to_string(&result)?.as_str())?;
604+
Ok((result, vec![], PdlBlock::Data(trace)))
605+
}
606+
}
607+
578608
async fn run_object(&mut self, block: &ObjectBlock, context: Context) -> Interpretation {
579609
if self.debug {
580610
eprintln!("Object {:?}", block.object);
@@ -583,6 +613,7 @@ impl<'a> Interpreter<'a> {
583613
let mut messages = vec![];
584614
let mut result_map = HashMap::new();
585615
let mut trace_map = HashMap::new();
616+
586617
let mut iter = block.object.iter();
587618
while let Some((k, v)) = iter.next() {
588619
let (this_result, this_messages, this_trace) =
@@ -660,8 +691,9 @@ impl<'a> Interpreter<'a> {
660691
}
661692

662693
fn push_and_extend_scope(&mut self, scope: HashMap<String, PdlResult>) {
663-
let cur_scope = self.scope.last().unwrap_or(&HashMap::new()).clone();
664-
self.scope.push(scope);
694+
let mut new_scope = self.scope.last().unwrap_or(&HashMap::new()).clone();
695+
new_scope.extend(scope);
696+
self.scope.push(new_scope);
665697
}
666698

667699
fn push_and_extend_scope_with(
@@ -679,28 +711,22 @@ impl<'a> Interpreter<'a> {
679711

680712
async fn process_defs(
681713
&mut self,
682-
map: &Option<HashMap<String, PdlBlock>>,
714+
defs: &Option<HashMap<String, PdlBlock>>,
683715
) -> Result<(), PdlError> {
684-
let cur_scope = self.scope.last().unwrap_or(&HashMap::new()).clone();
685-
let new_scope = match map {
686-
Some(defs) => {
687-
// this is all non-optimal
688-
let mut scope: Scope = HashMap::from(cur_scope);
689-
let mut iter = defs.iter();
690-
while let Some((var, def)) = iter.next() {
691-
let (result, _, _) = self.run_quiet(def, vec![]).await?;
692-
scope.insert(
693-
var.clone(),
694-
result,
695-
//from_str(to_string(&block).unwrap().as_str()).unwrap(),
696-
);
697-
}
698-
scope
716+
let mut new_scope: Scope = HashMap::new();
717+
if let Some(cur_scope) = self.scope.last() {
718+
new_scope.extend(cur_scope.clone());
719+
}
720+
self.scope.push(new_scope);
721+
722+
if let Some(defs) = defs {
723+
let mut iter = defs.iter();
724+
while let Some((var, def)) = iter.next() {
725+
let (result, _, _) = self.run_quiet(def, vec![]).await?;
726+
let _ = self.def(&Some(var.clone()), &result, &None);
699727
}
700-
None => cur_scope,
701-
};
728+
}
702729

703-
self.scope.push(new_scope);
704730
Ok(())
705731
}
706732

@@ -737,6 +763,7 @@ impl<'a> Interpreter<'a> {
737763
output_messages.extend(this_messages);
738764
output_blocks.push(trace);
739765
}
766+
740767
self.scope.pop();
741768

742769
let trace = block.with_items(output_blocks);

pdl-live-react/src-tauri/src/pdl/interpreter_tests.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,56 @@ mod tests {
469469
assert_eq!(messages[0].content, "hello world 4 bye");
470470
Ok(())
471471
}
472+
473+
#[test]
474+
fn text_data_1() -> Result<(), Box<dyn Error>> {
475+
let program = json!({
476+
"include": "./tests/cli/data1.pdl"
477+
});
478+
479+
let (_, messages, _) = run_json(program, false)?;
480+
assert_eq!(messages.len(), 1);
481+
assert_eq!(messages[0].role, MessageRole::User);
482+
assert_eq!(messages[0].content, "xxxx3true");
483+
Ok(())
484+
}
485+
486+
#[test]
487+
fn text_data_2() -> Result<(), Box<dyn Error>> {
488+
let program = json!({
489+
"include": "./tests/cli/data2.pdl"
490+
});
491+
492+
let (_, messages, _) = run_json(program, false)?;
493+
assert_eq!(messages.len(), 1);
494+
assert_eq!(messages[0].role, MessageRole::User);
495+
assert_eq!(messages[0].content, "xxxx3true");
496+
Ok(())
497+
}
498+
499+
#[test]
500+
fn text_data_3() -> Result<(), Box<dyn Error>> {
501+
let program = json!({
502+
"include": "./tests/cli/data3.pdl"
503+
});
504+
505+
let (_, messages, _) = run_json(program, false)?;
506+
assert_eq!(messages.len(), 1);
507+
assert_eq!(messages[0].role, MessageRole::User);
508+
assert_eq!(messages[0].content, "${x}3true");
509+
Ok(())
510+
}
511+
512+
/*#[test]
513+
fn text_data_4() -> Result<(), Box<dyn Error>> {
514+
let program = json!({
515+
"include": "./tests/cli/data4.pdl"
516+
});
517+
518+
let (_, messages, _) = run_json(program, false)?;
519+
assert_eq!(messages.len(), 1);
520+
assert_eq!(messages[0].role, MessageRole::User);
521+
assert_eq!(messages[0].content, "yyyyxxxx3true");
522+
Ok(())
523+
}*/
472524
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
lastOf:
2+
- def: x
3+
text:
4+
- xxxx
5+
- def: y
6+
data:
7+
n: 3
8+
x: ${x}
9+
b: true
10+
- ${y.x~y.n~y.b}
11+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defs:
2+
x:
3+
text:
4+
- xxxx
5+
y:
6+
data:
7+
n: 3
8+
x: ${x}
9+
b: true
10+
lastOf:
11+
- ${y.x~y.n~y.b}
12+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
lastOf:
2+
- def: x
3+
text:
4+
- xxxx
5+
- def: y
6+
raw: true
7+
data:
8+
n: 3
9+
x: ${x}
10+
b: true
11+
- ${y.x~y.n~y.b}
12+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
defs:
2+
x:
3+
description: Outer x
4+
text:
5+
- xxxx
6+
y:
7+
data:
8+
n: 3
9+
x: ${x}
10+
b: true
11+
lastOf:
12+
- defs:
13+
x: yyyy
14+
description: Inner x
15+
text:
16+
- ${x~y.x~y.n~y.b}

0 commit comments

Comments
 (0)