Skip to content

Commit 250bdd5

Browse files
committed
Merge branch 'main' into pdl-351
2 parents a8c9f97 + aaeba0f commit 250bdd5

File tree

11 files changed

+201
-30
lines changed

11 files changed

+201
-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
@@ -305,8 +305,21 @@ def process_prog(
305305
PDLRuntimeError: If the program raises an error.
306306
"""
307307
scope = empty_scope | scope
308+
309+
# Process stdlib
310+
stdlib_file = Path(__file__).parent / "pdl_stdlib.pdl"
311+
stdlib, _ = parse_file(stdlib_file)
312+
_, _, stdlib_dict, _ = process_block(
313+
state.with_yield_background(False).with_yield_result(False),
314+
empty_scope,
315+
stdlib.root,
316+
loc,
317+
)
318+
319+
stdlib_scope = scope | PdlDict({"stdlib": stdlib_dict})
320+
308321
result, document, final_scope, trace = process_block(
309-
state, scope, block=prog.root, loc=loc
322+
state, stdlib_scope, block=prog.root, loc=loc
310323
)
311324
return result, document, final_scope, trace
312325

@@ -442,7 +455,7 @@ def set_error_to_scope_for_retry(
442455
return scope
443456

444457

445-
def process_advanced_block(
458+
def process_advanced_block( # noqa: C901
446459
state: InterpreterState,
447460
scope: ScopeType,
448461
block: AdvancedBlockType,
@@ -472,7 +485,7 @@ def process_advanced_block(
472485
return result, background, new_scope, trace
473486

474487

475-
def process_advance_block_retry(
488+
def process_advance_block_retry( # noqa: C901
476489
state: InterpreterState,
477490
scope: ScopeType,
478491
block: AdvancedBlockType,
@@ -495,31 +508,39 @@ def process_advance_block_retry(
495508

496509
max_retry = block.retry if block.retry else 0
497510
trial_total = max_retry + 1
498-
for trial_idx in range(trial_total):
511+
for trial_idx in range(trial_total): # pylint: disable=too-many-nested-blocks
499512
try:
500513
result, background, new_scope, trace = process_block_body_with_replay(
501514
state, scope, block, loc
502515
)
503516
if block.requirements != []:
504517
requirements_satisfied = True
505518
for req in block.requirements:
506-
evalfn, _ = process_expr(scope, getattr(req, "evaluate"), loc)
507-
evaluation = evalfn(
508-
requirement=getattr(req, "description"), response=result
509-
)
510-
if evaluation.result() is False:
519+
evaluate = getattr(req, "evaluate", None)
520+
stdlib_dict: Any = scope["stdlib"]
521+
if evaluate is None:
522+
evaluate = stdlib_dict["requirements"]["evaluation"]
523+
evalfn: Any
524+
evalfn, _ = process_expr(scope, evaluate, loc)
525+
requirement, _ = process_expr(scope, getattr(req, "expect"), loc)
526+
evaluation = evalfn(requirement=requirement, response=result)
527+
if evaluation < -0.3:
511528
requirements_satisfied = False
512-
transfn, _ = process_expr(
513-
scope, getattr(req, "transformContext"), loc
514-
)
529+
transform_context = getattr(req, "transformContext", None)
530+
if transform_context is None:
531+
transform_context = stdlib_dict["requirements"][
532+
"transformContext"
533+
]
534+
transfn: Any
535+
transfn, _ = process_expr(scope, transform_context, loc)
515536
new_context = transfn(
516537
pdl_context=scope["pdl_context"],
517-
requirement=getattr(req, "description"),
538+
requirement=requirement,
518539
response=result,
519540
)
520-
scope = scope | {"pdl_context": new_context}
541+
if trial_idx < max_retry:
542+
scope = scope | {"pdl_context": new_context}
521543
if requirements_satisfied is False:
522-
print("\nTrying again!")
523544
continue
524545

525546
result = lazy_apply(id_with_set_first_use_nanos(block.pdl__timing), result)
@@ -710,7 +731,7 @@ def process_block_body(
710731
yield_result(result.result(), block.kind)
711732
if state.yield_background:
712733
yield_background(background)
713-
case TextBlock(): # HERE
734+
case TextBlock():
714735
result, background, scope, trace = process_blocks_of(
715736
block,
716737
"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+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The way to say hello in French is Bonjour.

0 commit comments

Comments
 (0)