Skip to content

Commit aaeba0f

Browse files
authored
Changes to requirements feature to support ppdl (#1191)
Signed-off-by: Mandana Vaziri <[email protected]>
1 parent a7d6e4f commit aaeba0f

File tree

10 files changed

+200
-30
lines changed

10 files changed

+200
-30
lines changed

examples/requirements/email.pdl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ defs:
1212
Does the following email end with Kind regards. Answer with a JSON object and a result field with value True or False only.
1313
Email: ${ response }
1414
parser: json
15-
- ${ result.result }
15+
- if: ${ result.result }
16+
then: 0
17+
else: -1000000
1618

1719
fix:
1820
function:
@@ -36,7 +38,7 @@ text:
3638
- "Write an email to ${ name } using the notes following: ${ notes }"
3739
- model: ollama_chat/granite3.2:2b
3840
requirements:
39-
- description: The email should end with Kind regards
41+
- expect: The email should end with Kind regards
4042
evaluate: ${ eval }
4143
transformContext: ${ fix }
4244
retry: 5

examples/requirements/gsm8k.pdl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
defs:
2+
model: ollama_chat/granite3.3:8b
3+
llm_as_judge: ollama_chat/gpt-oss:20b
4+
5+
problem: |
6+
Carla is downloading a 200 GB file. Normally she can download 2 GB/minute,
7+
but 40% of the way through the download, Windows forces a restart to install updates,
8+
which takes 20 minutes. Then Carla has to restart the download from the beginning.
9+
How load does it take to download the file?
10+
11+
extract_answer:
12+
function:
13+
solution: string
14+
return:
15+
lastOf:
16+
- ${ solution }
17+
- Extract the result from the above solution into a JSON object with field "answer" and a float as value. Remove any dollar signs or other symbols.
18+
- model: ${ model }
19+
parser: json
20+
spec: { "answer": number }
21+
22+
solve:
23+
function:
24+
problem: string
25+
return:
26+
defs:
27+
solution:
28+
text:
29+
- ${ problem }
30+
- "\n\n"
31+
- model: ${ model }
32+
parameters:
33+
temperature: 0.1
34+
requirements:
35+
- expect: "The solution to this problem should be correct. Problem: ${ problem }"
36+
retry: 1
37+
answer_obj:
38+
call: ${ extract_answer }
39+
args:
40+
solution: ${ solution }
41+
pdl_context: []
42+
debug:
43+
lang: python
44+
code: |
45+
print(answer_obj)
46+
result = ""
47+
data: ${ answer_obj.answer }
48+
49+
call: ${ solve }
50+
args:
51+
problem: ${ problem }
52+
pdl_context: []
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defs:
2+
problem: |+
3+
Carla is downloading a 200 GB file. Normally she can download 2 GB/minute,
4+
but 40% of the way through the download, Windows forces a restart to install updates,
5+
which takes 20 minutes. Then Carla has to restart the download from the beginning.
6+
How load does it take to download the file?
7+
8+
9+
lastOf:
10+
- ${ problem }
11+
- model: ollama_chat/granite3.3:8b
12+
parameters:
13+
temperature: 0.2
14+
- Extract the result from the above solution into a JSON object with field "answer" and a float as value. Remove any dollar signs or other symbols.
15+
- model: ollama_chat/granite3.3:8b
16+
def: result
17+
parser: json
18+
spec: { "answer": number }
19+
requirements:
20+
- expect: "This solution to the following math problem is correct: ${ problem }"
21+
- ${ result.answer }

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ version_file = "src/pdl/_version.py"
8787
where = ["src"]
8888

8989
[tool.setuptools.package-data]
90-
pdl = ["pdl-schema.json"]
90+
pdl = ["pdl-schema.json", "pdl_stlib.pdl"]
9191

9292
[tool.pyright]
9393
include = ["src", "tests", "examples", "docs"]

src/pdl/pdl-schema.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4504,7 +4504,7 @@
45044504
"additionalProperties": false,
45054505
"description": "Single requirement definition.",
45064506
"properties": {
4507-
"description": {
4507+
"expect": {
45084508
"anyOf": [
45094509
{
45104510
"$ref": "#/$defs/LocalizedExpression_TypeVar_"
@@ -4514,7 +4514,7 @@
45144514
"type": "string"
45154515
}
45164516
],
4517-
"title": "Description"
4517+
"title": "Expect"
45184518
},
45194519
"evaluate": {
45204520
"anyOf": [
@@ -4531,6 +4531,7 @@
45314531
"type": "null"
45324532
}
45334533
],
4534+
"default": null,
45344535
"title": "Evaluate"
45354536
},
45364537
"transformContext": {
@@ -4548,13 +4549,12 @@
45484549
"type": "null"
45494550
}
45504551
],
4552+
"default": null,
45514553
"title": "Transformcontext"
45524554
}
45534555
},
45544556
"required": [
4555-
"description",
4556-
"evaluate",
4557-
"transformContext"
4557+
"expect"
45584558
],
45594559
"title": "RequirementType",
45604560
"type": "object"

src/pdl/pdl_ast.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,13 @@ class RequirementType(BaseModel):
338338

339339
model_config = ConfigDict(extra="forbid")
340340

341-
description: ExpressionType
341+
expect: ExpressionType
342342
"""English description of the requirement"""
343343

344-
evaluate: Optional[ExpressionType["FunctionBlock"]]
344+
evaluate: Optional[ExpressionType["FunctionBlock"]] = None
345345
"""Evaluation function for the requirement"""
346346

347-
transformContext: Optional[ExpressionType["FunctionBlock"]]
347+
transformContext: Optional[ExpressionType["FunctionBlock"]] = None
348348
"""Function to transform the context for the requirement"""
349349

350350

src/pdl/pdl_dumper.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,11 @@ def usage_to_dict(usage: PdlUsage) -> dict:
412412

413413
def requirement_to_dict(req: RequirementType, json_compatible: bool) -> dict:
414414
d: dict = {}
415-
d["description"] = req.description
416-
d["evaluate"] = expr_to_dict(req.evaluate, json_compatible)
417-
d["transformContext"] = expr_to_dict(req.transformContext, json_compatible)
415+
d["expect"] = req.expect
416+
if req.evaluate is not None:
417+
d["evaluate"] = expr_to_dict(req.evaluate, json_compatible)
418+
if req.transformContext is not None:
419+
d["transformContext"] = expr_to_dict(req.transformContext, json_compatible)
418420
return d
419421

420422

src/pdl/pdl_interpreter.py

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,21 @@ def process_prog(
304304
PDLRuntimeError: If the program raises an error.
305305
"""
306306
scope = empty_scope | scope
307+
308+
# Process stdlib
309+
stdlib_file = Path(__file__).parent / "pdl_stdlib.pdl"
310+
stdlib, _ = parse_file(stdlib_file)
311+
_, _, stdlib_dict, _ = process_block(
312+
state.with_yield_background(False).with_yield_result(False),
313+
empty_scope,
314+
stdlib.root,
315+
loc,
316+
)
317+
318+
stdlib_scope = scope | PdlDict({"stdlib": stdlib_dict})
319+
307320
result, document, final_scope, trace = process_block(
308-
state, scope, block=prog.root, loc=loc
321+
state, stdlib_scope, block=prog.root, loc=loc
309322
)
310323
return result, document, final_scope, trace
311324

@@ -441,7 +454,7 @@ def set_error_to_scope_for_retry(
441454
return scope
442455

443456

444-
def process_advanced_block(
457+
def process_advanced_block( # noqa: C901
445458
state: InterpreterState,
446459
scope: ScopeType,
447460
block: AdvancedBlockType,
@@ -471,7 +484,7 @@ def process_advanced_block(
471484
return result, background, new_scope, trace
472485

473486

474-
def process_advance_block_retry(
487+
def process_advance_block_retry( # noqa: C901
475488
state: InterpreterState,
476489
scope: ScopeType,
477490
block: AdvancedBlockType,
@@ -494,31 +507,39 @@ def process_advance_block_retry(
494507

495508
max_retry = block.retry if block.retry else 0
496509
trial_total = max_retry + 1
497-
for trial_idx in range(trial_total):
510+
for trial_idx in range(trial_total): # pylint: disable=too-many-nested-blocks
498511
try:
499512
result, background, new_scope, trace = process_block_body(
500513
state, scope, block, loc
501514
)
502515
if block.requirements != []:
503516
requirements_satisfied = True
504517
for req in block.requirements:
505-
evalfn, _ = process_expr(scope, getattr(req, "evaluate"), loc)
506-
evaluation = evalfn(
507-
requirement=getattr(req, "description"), response=result
508-
)
509-
if evaluation.result() is False:
518+
evaluate = getattr(req, "evaluate", None)
519+
stdlib_dict: Any = scope["stdlib"]
520+
if evaluate is None:
521+
evaluate = stdlib_dict["requirements"]["evaluation"]
522+
evalfn: Any
523+
evalfn, _ = process_expr(scope, evaluate, loc)
524+
requirement, _ = process_expr(scope, getattr(req, "expect"), loc)
525+
evaluation = evalfn(requirement=requirement, response=result)
526+
if evaluation < -0.3:
510527
requirements_satisfied = False
511-
transfn, _ = process_expr(
512-
scope, getattr(req, "transformContext"), loc
513-
)
528+
transform_context = getattr(req, "transformContext", None)
529+
if transform_context is None:
530+
transform_context = stdlib_dict["requirements"][
531+
"transformContext"
532+
]
533+
transfn: Any
534+
transfn, _ = process_expr(scope, transform_context, loc)
514535
new_context = transfn(
515536
pdl_context=scope["pdl_context"],
516-
requirement=getattr(req, "description"),
537+
requirement=requirement,
517538
response=result,
518539
)
519-
scope = scope | {"pdl_context": new_context}
540+
if trial_idx < max_retry:
541+
scope = scope | {"pdl_context": new_context}
520542
if requirements_satisfied is False:
521-
print("\nTrying again!")
522543
continue
523544

524545
result = lazy_apply(id_with_set_first_use_nanos(block.pdl__timing), result)
@@ -680,7 +701,7 @@ def process_block_body(
680701
yield_result(result.result(), block.kind)
681702
if state.yield_background:
682703
yield_background(background)
683-
case TextBlock(): # HERE
704+
case TextBlock():
684705
result, background, scope, trace = process_blocks_of(
685706
block,
686707
"text",

src/pdl/pdl_stdlib.pdl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
defs:
3+
reward:
4+
function:
5+
response:
6+
return:
7+
defs:
8+
top_logprobs: ${ response.choices[0].logprobs.content[0].top_logprobs}
9+
lastOf:
10+
- for:
11+
tp: ${ top_logprobs }
12+
repeat:
13+
match: ${ tp.token }
14+
with:
15+
- case: "Yes"
16+
then:
17+
data: ${ tp.logprob }
18+
def: lp_y
19+
- case: "No"
20+
then:
21+
data: ${ tp.logprob }
22+
def: lp_n
23+
- lang: python
24+
code: |
25+
import math
26+
result = math.log(math.exp(lp_y) / (math.exp(lp_y) + math.exp(lp_n)))
27+
28+
requirements:
29+
object:
30+
evaluation:
31+
function:
32+
requirement: string
33+
response: string
34+
llm_as_judge: {optional: string}
35+
return:
36+
lastOf:
37+
- model: ${ llm_as_judge | default('watsonx/meta-llama/llama-3-3-70b-instruct') }
38+
def: evaluation
39+
input: |
40+
Is the following requirement satisfied in the solution below? Requirement: ${ requirement }
41+
${ response }
42+
43+
Respond with only 'Yes' or 'No'.
44+
modelResponse: out
45+
parameters:
46+
temperature: 0
47+
logprobs: true
48+
top_logprobs: 5
49+
- ${ reward(out) }
50+
51+
52+
transformContext:
53+
function:
54+
requirement: string
55+
response: string
56+
model: {optional: string}
57+
return:
58+
lastOf:
59+
- model: ${ model | default('ollama_chat/granite3.3:8b') }
60+
input: |
61+
The following requirement is not satisfied, what instruction can be added to get the correct answer?
62+
Requirement: ${ requirement }
63+
Answer with only the instruction.
64+
- ${ pdl_context }
65+
66+
67+
68+
69+

tests/test_examples_run.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ skip:
3434
- examples/optimizer/optimized_grammar_correction.pdl
3535
- examples/optimizer/eval_levenshtein.pdl
3636
- examples/requirements/email.pdl
37+
- examples/requirements/gsm8k.pdl
38+
- examples/requirements/gsm8k_short.pdl
3739
- examples/skeleton-of-thought/tips.pdl
3840
- examples/tutorial/sdk/lib.pdl
41+
- src/pdl/pdl_stdlib.pdl
3942
with_inputs:
4043
examples/tutorial/programs/chatbot.pdl:
4144
stdin: |

0 commit comments

Comments
 (0)