Skip to content

Commit 0a3b1d1

Browse files
authored
Add support of Jinja code blocks (#183)
1 parent dd8f218 commit 0a3b1d1

File tree

10 files changed

+89
-5
lines changed

10 files changed

+89
-5
lines changed

docs/tutorial.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ Bob lives at the following address:
305305

306306
## Calling code
307307

308-
The following script shows how to execute python code ([file](https://github.com/IBM/prompt-declaration-language//blob/main/examples/tutorial/calling_code.pdl)). The python code is executed locally (or in a containerized way if using `pdl --sandbox`). In principle, PDL is agnostic of any specific programming language, but we currently only support Python. Variables defined in PDL are copied into the global scope of the Python code, so those variables can be used directly in the code. However, mutating variables in Python has no effect on the variables in the PDL program. The result of the code must be assigned to the variable `result` internally to be propagated to the result of the block. A variable `def` on the code block will then be set to this result.
308+
The following script shows how to execute python code ([file](https://github.com/IBM/prompt-declaration-language//blob/main/examples/tutorial/calling_code.pdl)). The python code is executed locally (or in a containerized way if using `pdl --sandbox`). In principle, PDL is agnostic of any specific programming language, but we currently only support Python, Jinja, and shell commands. Variables defined in PDL are copied into the global scope of the Python code, so those variables can be used directly in the code. However, mutating variables in Python has no effect on the variables in the PDL program. The result of the code must be assigned to the variable `result` internally to be propagated to the result of the block. A variable `def` on the code block will then be set to this result.
309309

310310
In order to define variables that are carried over to the next Python code block, a special variable `PDL_SESSION` can be used, and
311311
variables assigned to it as fields.

examples/hello/hello-code-command.pdl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
description: Hello world showing call out to shell command
2+
lang: command
3+
code: |
4+
echo "Hello World!"

examples/hello/hello-code-jinja.pdl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
description: Hello world showing call out to shell command
2+
defs:
3+
world: "World"
4+
lang: jinja
5+
code: |
6+
Hello {{ world }}!

pdl-live/src/pdl_ast.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2322,7 +2322,7 @@ export type Kind15 = "code";
23222322
* Programming language of the code.
23232323
*
23242324
*/
2325-
export type Lang = "python" | "command";
2325+
export type Lang = "python" | "command" | "jinja";
23262326
/**
23272327
* Code to execute.
23282328
*

src/pdl/pdl-schema.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2691,7 +2691,8 @@
26912691
"description": "Programming language of the code.\n ",
26922692
"enum": [
26932693
"python",
2694-
"command"
2694+
"command",
2695+
"jinja"
26952696
],
26962697
"title": "Lang",
26972698
"type": "string"

src/pdl/pdl_ast.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ class CodeBlock(Block):
281281
"""Execute a piece of code."""
282282

283283
kind: Literal[BlockKind.CODE] = BlockKind.CODE
284-
lang: Literal["python", "command"]
284+
lang: Literal["python", "command", "jinja"]
285285
"""Programming language of the code.
286286
"""
287287
code: "BlocksType"

src/pdl/pdl_interpreter.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1261,8 +1261,18 @@ def step_call_code(
12611261
loc=loc,
12621262
trace=block.model_copy(update={"code": code_s}),
12631263
) from exc
1264+
case "jinja":
1265+
try:
1266+
result = call_jinja(code_s, scope)
1267+
background = [{"role": state.role, "content": result}]
1268+
except Exception as exc:
1269+
raise PDLRuntimeError(
1270+
f"Code error: {repr(exc)}",
1271+
loc=loc,
1272+
trace=block.model_copy(update={"code": code_s}),
1273+
) from exc
12641274
case _:
1265-
message = f"Unsupported language: {block.lan}"
1275+
message = f"Unsupported language: {block.lang}"
12661276
raise PDLRuntimeError(
12671277
message,
12681278
loc=loc,
@@ -1300,6 +1310,14 @@ def call_command(code: str) -> str:
13001310
return output
13011311

13021312

1313+
def call_jinja(code: str, scope: dict) -> Any:
1314+
template = Template(
1315+
code,
1316+
)
1317+
result = template.render(scope)
1318+
return result
1319+
1320+
13031321
def step_call(
13041322
state: InterpreterState, scope: ScopeType, block: CallBlock, loc: LocationType
13051323
) -> Generator[YieldMessage, Any, tuple[Any, Messages, ScopeType, CallBlock]]:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Hello World!
2+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello World!

tests/test_code.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from pdl.pdl import exec_str
12
from pdl.pdl_ast import Program
23
from pdl.pdl_interpreter import InterpreterState, empty_scope, process_prog
34

@@ -71,3 +72,54 @@ def test_command():
7172
document, _, scope, _ = process_prog(state, empty_scope, data)
7273
assert document == "Hello World!"
7374
assert scope["world"] == "World"
75+
76+
77+
def test_jinja1():
78+
prog_str = """
79+
defs:
80+
world: "World"
81+
lang: jinja
82+
code: |
83+
Hello {{ world }}!
84+
"""
85+
result = exec_str(prog_str)
86+
assert result == "Hello World!"
87+
88+
89+
def test_jinja2():
90+
prog_str = """
91+
defs:
92+
world: "World"
93+
lang: jinja
94+
code: |
95+
Hello ${ world }!
96+
"""
97+
result = exec_str(prog_str)
98+
assert result == "Hello World!"
99+
100+
101+
def test_jinja3():
102+
prog_str = """
103+
defs:
104+
scores:
105+
array:
106+
- 10
107+
- 90
108+
- 50
109+
- 60
110+
- 100
111+
lang: jinja
112+
code: |
113+
{% for score in scores %}
114+
{% if score > 80 %}good{% else %}bad{% endif %}{% endfor %}
115+
"""
116+
result = exec_str(prog_str)
117+
assert (
118+
result
119+
== """
120+
bad
121+
good
122+
bad
123+
bad
124+
good"""
125+
)

0 commit comments

Comments
 (0)