Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ Bob lives at the following address:

## Calling code

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.
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.

In order to define variables that are carried over to the next Python code block, a special variable `PDL_SESSION` can be used, and
variables assigned to it as fields.
Expand Down
4 changes: 4 additions & 0 deletions examples/hello/hello-code-command.pdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
description: Hello world showing call out to shell command
lang: command
code: |
echo "Hello World!"
6 changes: 6 additions & 0 deletions examples/hello/hello-code-jinja.pdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
description: Hello world showing call out to shell command
defs:
world: "World"
lang: jinja
code: |
Hello {{ world }}!
2 changes: 1 addition & 1 deletion pdl-live/src/pdl_ast.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2322,7 +2322,7 @@ export type Kind15 = "code";
* Programming language of the code.
*
*/
export type Lang = "python" | "command";
export type Lang = "python" | "command" | "jinja";
/**
* Code to execute.
*
Expand Down
3 changes: 2 additions & 1 deletion src/pdl/pdl-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2691,7 +2691,8 @@
"description": "Programming language of the code.\n ",
"enum": [
"python",
"command"
"command",
"jinja"
],
"title": "Lang",
"type": "string"
Expand Down
2 changes: 1 addition & 1 deletion src/pdl/pdl_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class CodeBlock(Block):
"""Execute a piece of code."""

kind: Literal[BlockKind.CODE] = BlockKind.CODE
lang: Literal["python", "command"]
lang: Literal["python", "command", "jinja"]
"""Programming language of the code.
"""
code: "BlocksType"
Expand Down
20 changes: 19 additions & 1 deletion src/pdl/pdl_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1261,8 +1261,18 @@ def step_call_code(
loc=loc,
trace=block.model_copy(update={"code": code_s}),
) from exc
case "jinja":
try:
result = call_jinja(code_s, scope)
background = [{"role": state.role, "content": result}]
except Exception as exc:
raise PDLRuntimeError(
f"Code error: {repr(exc)}",
loc=loc,
trace=block.model_copy(update={"code": code_s}),
) from exc
case _:
message = f"Unsupported language: {block.lan}"
message = f"Unsupported language: {block.lang}"
raise PDLRuntimeError(
message,
loc=loc,
Expand Down Expand Up @@ -1300,6 +1310,14 @@ def call_command(code: str) -> str:
return output


def call_jinja(code: str, scope: dict) -> Any:
template = Template(
code,
)
result = template.render(scope)
return result


def step_call(
state: InterpreterState, scope: ScopeType, block: CallBlock, loc: LocationType
) -> Generator[YieldMessage, Any, tuple[Any, Messages, ScopeType, CallBlock]]:
Expand Down
2 changes: 2 additions & 0 deletions tests/results/examples/hello/hello-code-command.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello World!

1 change: 1 addition & 0 deletions tests/results/examples/hello/hello-code-jinja.result
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World!
52 changes: 52 additions & 0 deletions tests/test_code.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from pdl.pdl import exec_str
from pdl.pdl_ast import Program
from pdl.pdl_interpreter import InterpreterState, empty_scope, process_prog

Expand Down Expand Up @@ -71,3 +72,54 @@ def test_command():
document, _, scope, _ = process_prog(state, empty_scope, data)
assert document == "Hello World!"
assert scope["world"] == "World"


def test_jinja1():
prog_str = """
defs:
world: "World"
lang: jinja
code: |
Hello {{ world }}!
"""
result = exec_str(prog_str)
assert result == "Hello World!"


def test_jinja2():
prog_str = """
defs:
world: "World"
lang: jinja
code: |
Hello ${ world }!
"""
result = exec_str(prog_str)
assert result == "Hello World!"


def test_jinja3():
prog_str = """
defs:
scores:
array:
- 10
- 90
- 50
- 60
- 100
lang: jinja
code: |
{% for score in scores %}
{% if score > 80 %}good{% else %}bad{% endif %}{% endfor %}
"""
result = exec_str(prog_str)
assert (
result
== """
bad
good
bad
bad
good"""
)