Releases: IBM/prompt-declaration-language
Version 0.9.0
New Features
The main new features are:
contributeto a file or standard input and error@pdlPython decorator
contribute to files
The user can declare a file aggregator that can be used in the contribute to accumulate the results of the blocks.
For example, in the following program, an aggregator named log associated to the file /tmp/log.txt is declared.
This aggregator is used to store the output of the model in the file /tmp/log.txt.
defs:
log:
aggregator:
file: /tmp/log.txt
text:
- Hello
- model: ollama_chat/granite3.3:2b
contribute: [ result, context, log ]
By default, the aggregator stdin and stderr are available to contribute to the standard input and standard error.
It is useful, for example to outpout debug information. In the following program, the raw output of the model is emitted
on the standard error:
text:
- Hello
- model: ollama_chat/granite3.3:2b
modelResponse: raw_response
contribute:
- result
- context
- stderr:
value: ${raw_response}
@pdl Python decorator
In order to make it easy to define functions as PDL programs, we provide a @pdl decorator.
from pdl.pdl import pdl
@pdl
def chain_of_thought(scope):
"""
lastOf:
- "Question: ${ question }\n"
- "Answer: Let's think step by step. "
- model: ollama_chat/granite3.3:2b
parameters:
stop:
- "<|endoftext|>"
- "Question:"
include_stop_sequence: false
"""
return
def main():
result = chain_of_thought(
scope={"model": "ollama_chat/granite3.2:2b",
"question": "How to write Hello World in OCaml?\n"}
)
print(result)
if __name__ == "__main__":
main()
In this example, the function chain_pf_thought is a string representing a PDL program. The free variables in the string are values passed in the dictionary scope given on argument.
What's Changed
- tests: update expected result by @mandel in #1158
- First class aggregators by @mandel in #706
- refactor: react pattern in prompt library by @mandel in #1162
- PDL Python decorator by @vazirim in #1166
- fix: type error location by @mandel in #1169
- refactor: interpreter by @mandel in #1170
- fix: return the value not the future when a closure is called fro jin… by @mandel in #1208
- feat: the scope of the
contentfield is local by @mandel in #1225 - feat: replay an execution by @mandel in #1211
- refactor: performance optimizations by @mandel in #1228
- feat: add
event_loopto the interpreter config by @mandel in #1235 - feat: do not reload library when importing multiple time a file by @mandel in #1243
- feat:
structuredDecodingfield on themodelblock by @mandel in #1250 - docs: update type checking and structured decoding in the tutorial by @mandel in #1254
- feat: improve error messages by @mandel in #1259
- fix: imported files can use stdlib by @mandel in #1264
- fix: improve error messages by @mandel in #1276
- fix: avoid catching
KeyboardInterruptby @mandel in #1298 - fix: bug in ipython code block by @mandel in #1314
- refactor: use TypedDict for exec program output by @mandel in #1322
- feat: do not dump location tables in the trace by @mandel in #1326
- feat: collect global statistics on LLM usage by @mandel in #1328
- feat: add
llm_usageto theexec_*config by @mandel in #1331
Full Changelog: v0.8.0...v0.9.0
Version 0.8.0
New Features
The main changes in this release are:
- AutoPDL: automatic optimization of PDL programs
- Calling PDL functions from Jinja and Python
- Map/reduce block
AutoPDL: automatic optimization of PDL programs
Given a PDL program with some free variables, a search space for these free variable, a loss function, and a training set, you can optimize the PDL program using the pdl-optimize command. This allows to optimize any part of a PDL program such as the textual prompts, the few-shots examples, or the prompting patterns. This work is based on the paper AutoPDL: Automatic Prompt Optimization for LLM Agents. A new section of the manual is describing how to use the optimizer.
Calling PDL functions from Jinja and Python
It is now possible to call a function defined in PDL inside a Jinja expression or a Python code block. Here is an example where the function translate is called in the Jinja expression ${ translate("Hello", language="French") }:
description: Calling a PDL function from Jinja
defs:
translate:
function:
sentence: string
language: string
return:
lastOf:
- |
Translate the sentence '${ sentence }' to ${ language }.
Only give the result of the translation.
- model: ollama_chat/granite3.2:2b
text: |
The way to say hello in French is ${ translate("Hello", language="French") }.
Map/reduce block
Similar to the repeat loops, PDL now offers a map block. The difference with the repeat block is that the context is not accumulated between iterations, each iteration is executed with the same context. Here is an example of map block:
lastOf:
- "Hello, "
- for:
name: [Alice, Bob, Charlie]
map:
lastOf:
- my name is ${ name }
- model: ollama/granite3.2:2b
join:
as: array
What's Changed
- chore: Configure Renovate by @renovate[bot] in #944
- Dataset preprocessing scripts for AutoPDL by @claudiosv in #994
- fix: avoid side effects in PdlContext by @mandel in #1054
- tests: update react example by @mandel in #1001
- tests: update calc example to use
.signatureby @mandel in #1067 - fix: output streaming in case of fallback by @mandel in #1071
- feat: make PDL functions callable as Python and jinja functions by @mandel in #1070
- feat: allow unnamed arguments when calling a PDL function in Jinja by @mandel in #1076
- feat: map/reduce by @mandel in #1010
- refactor: refactor AST to generate simpler JSON Schema by @mandel in #1079
- Fix for exception in optimizer by @vazirim in #1084
- docs: update tutorial by @mandel in #1080
- Requirements implementation by @vazirim in #1097
- fix: update AST in the UI by @mandel in #1100
- fix: ensure that
PazyApply.resultreturns a non-lazy value by @mandel in #1101 - feat: parallel map reduce by @mandel in #1102
- Fix for llm message post-processing by @vazirim in #1106
- AutoPDL Simplification by @claudiosv in #1088
- tests: map-based gsm8k by @mandel in #1107
- optimizer CLI and doc by @vazirim in #1151
Full Changelog: v0.7.1...v0.8.0
Version 0.7.1
What's Changed
- refactor: make the event loop thread part of the interpreter state by @mandel in #990
- fix: contribute directly a value to the context by @mandel in #991
Full Changelog: v0.7.0...v0.7.1
Version 0.7.0
New Features
The main new features are the following and are detailed bellow:
- new
retryattribute to any block, - new
indexfield to introduce a loop index inrepeatblocks - new syntax for types with suport for JSON Schema,
- extract the signature of a function
fwithf.signature, - loop and sequences with independent contexts,
- support for granite-io processors created in Python.
The retry field
Any block can now have a retry field indicating how many times a block should be executed if it encounters a runtime error. For example, a model call can be retried 5 times as follows:
model: replicate/ibm-granite/granite-3.3-8b-instruct
input: How to program "Hello World!"?
retry: 5The loop index field
A common pattern in PDL programs is to introduce a variable to index the loop iterations. For example, a loop that turns a list of strings into a list of object with a field name and id would be written as follows:
defs:
id: -1
for:
name: [ "Alice", "Nicolas", "Rosa", "Remi" ]
repeat:
defs:
id: ${ id + 1}
data:
name: ${name}
id: ${id}
join:
as: arrayWith the new index field that introduced a variable name as loop index, this code can now be simplified as follows:
for:
name: [ "Alice", "Nicolas", "Rosa", "Remi" ]
index: id
repeat:
data:
name: ${name}
id: ${id}
join:
as: arrayNew type syntax
We extended the type syntax to be able to write directly some JSON Schema as block specification. To do so, the type must contain the type or enum field. For example, we can write a model block that checks that the output starts with the letter "A" as follows:
model: ollama/granite3.3:2b
input: Generate a word that starts with the letter "A". Just output the single word.
spec:
type: string
pattern: "^[Aa]"To make the syntax more uniform, we are using the JSON Schema syntax for the base type. So for example, we are using number instead of float for floating point numbers. We discuss the breaking changes below.
Extract function signature
In order to make the use of PDL functions as tools by LLMs easier, we provide the ability to extract the signature of a function f by executing f.signature. Here is an example:
defs:
calc:
description: Calculator function
function:
expr:
type: string
description: Arithmetic expression to calculate
return:
lang: python
code: result = ${ expr }
text: ${ calc.signature }The output is:
{"type": "function", "name": "calc", "description": "Calculator function", "parameters": {"type": "object", "properties": {"expr": {"type": "string", "description": "Arithmetic expression to calculate"}}, "required": ["expr"], "additionalProperties": false}}
An example of tool use is given in search.pdl.
Independent contexts
Blocks containing lists of blocks (text, array, object, and lastOf) as well as loops can now be annotated with context: independent. It means that each sub-block is executed in an independent copy of the context. Therefore, if we execute the following program, both model calls are executed with the same input containing the message Hello:
lastOf:
- Hello
- context: independent
array:
- model: ollama/granite3.2:2b
- model: ollama/granite3.3:2bSupport for granite-io processors objects
In addition to the ability to call granite-io processors using the lookup mechanism using backend and processors names, it is possible now to use granite-io processors or backends created in Python. Here is an example:
defs:
io_proc:
lang: python
code: |
from granite_io import make_backend, make_io_processor
model_name = "granite3.2:2b"
backend = make_backend("openai", { "model_name": model_name })
result = make_io_processor(model_name, backend=backend)
processor: ${ io_proc }
input: Write an Hello World program in Python.Breaking Changes
This version of PDL is coming with a large number of breaking changes:
- types syntax,
- granite-io syntax,
- rename
max_iterationsintomaxIterations, - trace format.
Types syntax
As mentioned above, to be consistent with JSON Schema, we renamed the basic types as follows:
str->stringbool->booleanint->integerfloat->numberobj->objectlist->array
Moreover, since we can use JSON schema, we removed the old way to put constraints on types. So for example, the following type:
spec: { int: { minimum: 0 } }must be rewritten:
spec: { type: integer, minimum: 0 }Finally, the type null now corresponds to a value of any type. For example, the identity function can be written as follows:
function:
x:
return: ${ x }Granite-io processors
The structure of the granite-io block changed. Now, the processor field is required and the definition of io-processor is given has sub-fields of this field. So a block that was defined as follows:
processor:
model: "granite3.2:2b"
backend: openai
input: Hellois now defined like this:
processor:
type: Granite 3.2
model: "granite3.2:2b"
backend: openai
input: HelloLoop iteration bound
The syntax to bound the number of iterations of a loop changed. It is now maxIteration. Here is an example
index: i
repeat: ${ i }
maxIterations: 3Trace format
The format of the traces generated with the --trace (-t) option has changed. Some internal fields like defsite have changed to pdl__defsite. It means that traces generated with old version of the interpreter are not compatible with the new version of the UI.
What's Changed
- Fix rag example by fully qualifying import by @esnible in #930
- Change to sys.path for python code block by @vazirim in #931
- granite-io hallucination demo example and notebook by @vazirim in #932
- Add contrib. prompt library by @claudiosv in #927
- Fixed the bug where pdl.version was not set by @vite-falcon in #882
- Use granite-io async interface by @mandel in #936
- chore: bump ui dependences by @starpit in #939
- independent implementation by @vazirim in #934
- feat: add a
parse_dictfunction topdl_parserby @mandel in #943 - feat: specify PDL types in the AST by @mandel in #942
- PDL Optimizer by @claudiosv in #941
- Add a new retry feature to
blockby @hirokuni-kitahara in #824 - feat: extend PDL types with json schema types by @mandel in #947
- feat: add a
signaturefield to closures containing the function signature by @mandel in #948 - feat: add
indexfield torepeatblocks to name loop index by @mandel in #950 - Fix for extra fields in messages sent to LLMs by @vazirim in #952
- dependent/independent context implementation by @vazirim in #945
- docs: add example of
f.signatureby @mandel in #955 - fix: remove
defsitefrom messages in model inputs by @mandel in #956 - Introducing Run Examples check in PRs by @jgchn in #908
- Capturing new results for Run Examples by @jgchn in #957
- fix: ensure that model inputs are always contexts by @mandel in #958
- feat: change type syntax by @mandel in #951
- feat: rename
max_iterationsintomaxIterationsby @mandel in #961 - fix: unable to set OPENAI_API_BASE for litellm by @starpit in #962
- docs: AST documentation by @mandel in #963
- refactor: rename internal field
returnsintoreturn_by @mandel in #964 - feat: export
write_traceinpdl.pdlby @mandel in #966 - chore: remove dependency on termcolor types by @mandel in #967
- fix: trace generation with context by @mandel in #968
- chore(deps): Update termcolor requirement from ~=2.0 to >=2,<4 by @mandel in #971
- refactor: make the use of dependent and independent context explicit by @mandel in https://github.com/IBM/prompt-declaration-language/pul...
Version 0.6.2
What's Changed
- Fix rag example by fully qualifying import by @esnible in #930
- Change to sys.path for python code block by @vazirim in #931
- granite-io hallucination demo example and notebook by @vazirim in #932
- Add contrib. prompt library by @claudiosv in #927
- Fixed the bug where pdl.version was not set by @vite-falcon in #882
- feat: add a
parse_dictfunction topdl_parserby @mandel in #943 - feat: specify PDL types in the AST by @mandel in #942
- chore(deps): Update granite-io requirement from <0.4,>=0.2 to >=0.2,<0.5 by @dependabot in #946
- chore(deps): Update faiss-cpu requirement from ~=1.10.0 to >=1.10,<1.12 by @dependabot in #940
- Fix for extra fields in messages sent to LLMs by @vazirim in #952
Full Changelog: v0.6.1...v0.6.2
Version 0.6.1
What's Changed
- feat: update rust python support to pull in python stdlib by @starpit in #877
- feat: port rust model pull logic to use rust AST by @starpit in #875
- fix: avoid openssl in rust app for now by @starpit in #880
- gsm8k multi-plan, tree-of-thought, tree-of-thought with few shots by @vazirim in #881
- feat: rust interpreter support for modelResponse, ollama-rs tooling calling, and no-stream by @starpit in #883
- chore: minor code cleanups to rust interpreter by @starpit in #884
- test: update github action rust interpreter test to remove ollama pull by @starpit in #885
- fix: add kind tags to rust ast blocks by @starpit in #887
- fix: update rust interpreter to avoid global emit/scope state by @starpit in #888
- fix: rust pull logic may not always pull by @starpit in #889
- feat: remove tauri cli support for running python interpreter by @starpit in #890
- chore: bump rust dependencies to resolve alert by @starpit in #893
- test: tauri github actions test should apt install the deb by @starpit in #892
- feat: support for --data and --data-file in rust interpreter by @starpit in #894
- fix: begin phasing in Metadata (common defs, etc. attrs) into rust AST by @starpit in #895
- chore: update granite-io dependency by @mandel in #896
- refactor: move def attr into Metadata (rust interpreter) by @starpit in #897
- feat: introduce Expr typing and apply it to IfBlock.condition by @starpit in #899
- feat: update rust Call AST to use Expr for condition attr by @starpit in #901
- chore: bump to rust 2024 edition by @starpit in #902
- feat: continue to flesh out block metadata structure in rust by @starpit in #898
- refactor: add metadata attr to remaining rust block asts by @starpit in #903
- feat: update rust Repeat AST to use Expr for
forattr by @mandel in #904 - refactor: introduce Advanced enum to rust AST by @starpit in #906
- refactor: refactor rust ast to place metadata in common struct by @starpit in #909
- fix: improve deserialization of python-generated model block traces by @starpit in #910
- fix: in rust ast, allow ModelBlock model to be an expr by @starpit in #911
- feat: initial pdl__id and --trace support for rust interpreter by @starpit in #912
- fix: update rust interpreter to create Data blocks for expr eval, and model_input trace field by @starpit in #914
- fix: populate trace context field in rust interpreter by @starpit in #915
- Update stop sequences in parameters by @jgchn in #861
- refactor: extract platform-generic logic from run_ollama_model() handler by @starpit in #916
- fix: rust interpreter was not handling pdl__context for re-runs of traces by @starpit in #917
- feat: improve support for importing stdlib in python code blocks by @starpit in #918
- skeleton-of-thought example by @vazirim in #919
- Bump litellm and openai versions by @vazirim in #920
- fix: improve support for rust interpreter python imports from venv by @starpit in #922
- chore: bump tauri and npm dependencies by @starpit in #923
- chore: bump ui to 0.6.1 by @starpit in #921
Full Changelog: v0.6.0...v0.6.1
Version 0.6.0
This new release has two major changes:
pdl-lint, a linter for PDL programs (thanks to @vite-falcon!)- mesages in the context with the same role are not automatically merged anymore.
For example, the following program generates 3 messages:
lastOf:
- role: user
text:
- hello
- "\n"
- world
- ${pdl_context}
result:
[{"role": "user", "content": "hello", "defsite": "lastOf.text.0"}, {"role": "user", "content": "\n", "defsite": "lastOf.text.1"}, {"role": "user", "content": "world", "defsite": "lastOf.text.2"}]
To generate only one message, you have to use a message block:
lastOf:
- role: user
content:
text:
- hello
- "\n"
- world
- ${pdl_context}
result
[{"role": "user", "content": "hello\nworld", "defsite": "lastOf.message"}]
What's Changed
- Switch to granite-io version 0.2 by @mandel in #818
- feat: update beeai compiler to support compiling directly from python source by @starpit in #834
- Examples restructuring, tutorial changes by @vazirim in #836
- Bug fixes for setting default parameters by @vazirim in #838
- fix: pdl view trace.json fixes by @starpit in #843
- fix: Bug in pdl_schema_error_analyzer that raises exception during analysis by @vite-falcon in #851
- Clean up run examples and automate result updating via GH Actions by @jgchn in #853
- feat: do not merge messages with same role by @mandel in #846
- fixes to react examples by @vazirim in #859
- feat:
messageblocks contribute the message instead of the content to the context by @mandel in #862 - feat: do not stringify messages content by @mandel in #858
- feat: rust interpreter by @starpit in #857
- gsm8k plan with few-shots by @vazirim in #870
- feat: add pdl-lint tool that can be configured via pyproject.toml by @vite-falcon in #864
New Contributors
- @vite-falcon made their first contribution in #851
Full Changelog: v0.5.1...v0.6.0
Version 0.5.1
What's Changed
- Fix parsing of localized expressions by @mandel in #804
- fix: update ui to support pulling interpreter from local dir by @starpit in #807
- fix: interpreter squashes PdlRuntimeErrors by @starpit in #800
- fix: interpreter fails when passed parameters with null/None values by @starpit in #809
- Rename
exprfield ofLocalizedExpressionintopdl__exprby @mandel in #806 - feat: compile bee to pdl by @starpit in #817
- fix: rerun in ui bombs by @starpit in #819
- fix: async model call prints should only occur if PDL_VERBOSE_ASYNC env is set by @starpit in #820
- fix: skip litellm 1.63.14 by @starpit in #827
- fix: pin openai==1.61.0 pip due to breaking change on their side by @starpit in #829
Full Changelog: v0.5.0...v0.5.1
Version 0.5.0
What's Changed
- feat: add model token usage stats to trace by @starpit in #762
- feat: add support for CodeBlocks with argv by @starpit in #756
- feat: add Usage tab to detail drawer for model blocks by @starpit in #778
- Systematically used localized expressions in the trace and add
pdl__resultin them by @mandel in #760 - feat: add Result tab to detail drawer by @starpit in #782
- doc: update readme ui screenshot to show usage metrics by @starpit in #793
- Copy the schema on the website by @mandel in #797
Full Changelog: v0.4.2...v0.5.0
Version 0.4.2
What's Changed
- feat: in UI allow pty execution to be canceled by @starpit in #673
- feat: switch ui tile run menu to split action dropdown by @starpit in #675
- default parameters for ollama_chat models by @vazirim in #689
- Change examples to ollama_chat by @jgchn in #691
- Grade School Math example by @esnible in #694
- Docstrings and examples for PDL concepts by @esnible in #693
- feat: ui temperature stability by @starpit in #697
- feat: add Pagination to UI by @starpit in #704
- demo hallucination trace by @vazirim in #705
- Structured decoding bug fix for watsonx, ollama + traceback for python code blocks by @vazirim in #708
- feat: interpreter should report (stderr) call start/end and timing by @starpit in #711
- feat: update interpreter to print out model response messages in async mode by @starpit in #717
- Update parse_str interface by @mandel in #741
- Support dev version of granite-io by @mandel in #742
- feat: pull models in rust by @starpit in #743
- feat: update auto-pull logic to run in parallel with pip install by @starpit in #744
- feat: use shasum as cache key for venv by @starpit in #748
- feat: add gsm8k demo to ui (demo9) by @starpit in #753
- Contributed values are expressions by @mandel in #754
- Use
${ pdl_context }as default value for theinputfield of a model block by @mandel in #757 - added litellm param to ignore structure decoding param in tools example by @vazirim in #750
- gsm8k with planning by @vazirim in #761
Full Changelog: v0.4.1...v0.4.2