From 20ddcb3de3ca6c0c854ed6267313bf0280c88c6d Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Thu, 17 Oct 2024 18:17:37 -0400 Subject: [PATCH 01/12] Fix implicit unused analysis Signed-off-by: Mandana Vaziri --- src/pdl/pdl_analysis.py | 76 ++++++++++++++++++++++++----------- src/pdl/pdl_ast_utils.py | 58 ++++++++++++-------------- tests/test_implicit_ignore.py | 23 +++++++++++ 3 files changed, 101 insertions(+), 56 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index 91aa501f7..ddded4c8a 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -2,10 +2,13 @@ from dataclasses import dataclass from typing import Sequence +from .pdl_dumper import blocks_to_dict, dump_yaml + from .pdl_ast import ( AdvancedBlockType, ArrayBlock, Block, + BlocksType, BlockType, CallBlock, CodeBlock, @@ -44,7 +47,7 @@ def with_implicit_ignore(self, b): def unused_warning(block: BlockType): global _DISPLAY_UNUSED_HINT # pylint: disable= global-statement - print(f"Warning: the result of block `{block}` is not used.", file=sys.stderr) + print(f"Warning: the result of block `{dump_yaml(blocks_to_dict(block, json_compatible=True))}` is not used.", file=sys.stderr) if _DISPLAY_UNUSED_HINT: _DISPLAY_UNUSED_HINT = False print( @@ -55,15 +58,22 @@ def unused_warning(block: BlockType): def unused_program(prog: Program) -> None: state = UnusedConfig(implicit_ignore=False) - unused_advanced_block(state, LastOfBlock(lastOf=prog.root)) + unused_blocks(state, prog.root) + + +def unused_blocks(state: UnusedConfig, blocks: BlocksType) -> None: + if not isinstance(blocks, str) and isinstance(blocks, Sequence): + unused_advanced_block(state, LastOfBlock(lastOf=blocks)) + else: + unused_block(state, blocks) -def unused_block(state, block: BlockType) -> None: - if not isinstance(block, Block): +def unused_block(state, blocks: BlockType) -> None: + if isinstance(blocks, Block): + unused_advanced_block(state, blocks) + else: if state.implicit_ignore: - unused_warning(block) - return - unused_advanced_block(state, block) + unused_warning(blocks) def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None: @@ -80,26 +90,46 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None unused_block(state, block.lastOf[-1]) else: unused_block(state, block.lastOf) - # Leaf blocks without side effects - case DataBlock() | FunctionBlock() | GetBlock() | ModelBlock() | ReadBlock(): + case ArrayBlock() | ObjectBlock() | TextBlock(): if state.implicit_ignore: unused_warning(block) - return - # Leaf blocks with side effects - case CallBlock() | CodeBlock() | EmptyBlock() | ErrorBlock(): - return - # Non-leaf blocks + iter_block_children((lambda blocks: used_blocks(state, blocks)), block) + # Leaf blocks case ( - ArrayBlock() - | ForBlock() - | IfBlock() - | IncludeBlock() + DataBlock() + | FunctionBlock() + | GetBlock() | MessageBlock() - | ObjectBlock() - | RepeatBlock() - | RepeatUntilBlock() - | TextBlock() + | ModelBlock() + | CallBlock() + | CodeBlock() + | EmptyBlock() + | ReadBlock() ): - iter_block_children((lambda b: unused_block(state, b)), block) + if state.implicit_ignore: + unused_warning(block) + iter_block_children( + ( + lambda blocks: unused_blocks( + state.with_implicit_ignore(False), blocks + ) + ), + block, + ) + case ErrorBlock(): + pass + # Non-leaf blocks + case ( + ForBlock() | IfBlock() | IncludeBlock() | RepeatBlock() | RepeatUntilBlock() + ): + iter_block_children((lambda blocks: unused_blocks(state, blocks)), block) case _: assert False + + +def used_blocks(state: UnusedConfig, blocks: BlocksType) -> None: + if not isinstance(blocks, str) and isinstance(blocks, Sequence): + for block in blocks: + unused_block(state.with_implicit_ignore(False), block) + else: + unused_block(state.with_implicit_ignore(False), blocks) diff --git a/src/pdl/pdl_ast_utils.py b/src/pdl/pdl_ast_utils.py index d8af00658..8a764319b 100644 --- a/src/pdl/pdl_ast_utils.py +++ b/src/pdl/pdl_ast_utils.py @@ -32,69 +32,69 @@ ) -def iter_block_children(f: Callable[[BlockType], None], block: BlockType) -> None: +def iter_block_children(f: Callable[[BlocksType], None], block: BlockType) -> None: if not isinstance(block, Block): return for blocks in block.defs.values(): - iter_blocks(f, blocks) + f(blocks) match block: case FunctionBlock(): if block.returns is not None: - iter_blocks(f, block.returns) + f(block.returns) case CallBlock(): if block.trace is not None: - iter_blocks(f, block.trace) + f(block.trace) case ModelBlock(): if block.input is not None: - iter_blocks(f, block.input) + f(block.input) if block.trace is not None: - iter_blocks(f, block.trace) + f(block.trace) case CodeBlock(): - iter_blocks(f, block.code) + f(block.code) case GetBlock(): pass case DataBlock(): pass case TextBlock(): - iter_blocks(f, block.text) + f(block.text) case LastOfBlock(): - iter_blocks(f, block.lastOf) + f(block.lastOf) case ArrayBlock(): - iter_blocks(f, block.array) + f(block.array) case ObjectBlock(): if isinstance(block.object, dict): for blocks in block.object.values(): - iter_blocks(f, blocks) + f(blocks) else: - iter_blocks(f, block.object) + f(block.object) case MessageBlock(): - iter_blocks(f, block.content) + f(block.content) case IfBlock(): - iter_blocks(f, block.then) + f(block.then) if block.elses is not None: - iter_blocks(f, block.elses) + f(block.elses) case RepeatBlock(): - iter_blocks(f, block.repeat) + f(block.repeat) if block.trace is not None: for trace in block.trace: - iter_blocks(f, trace) + f(trace) case RepeatUntilBlock(): - iter_blocks(f, block.repeat) + f(block.repeat) if block.trace is not None: for trace in block.trace: - iter_blocks(f, trace) + f(trace) case ForBlock(): - iter_blocks(f, block.repeat) + f(block.repeat) if block.trace is not None: for trace in block.trace: - iter_blocks(f, trace) + f(trace) case ErrorBlock(): - iter_blocks(f, block.program) + f(block.program) case ReadBlock(): pass case IncludeBlock(): if block.trace is not None: - iter_blocks(f, block.trace) + f(block.trace) case EmptyBlock(): pass case _: @@ -105,17 +105,9 @@ def iter_block_children(f: Callable[[BlockType], None], block: BlockType) -> Non case "json" | "yaml" | RegexParser(): pass case PdlParser(): - iter_blocks(f, block.parser.pdl) + f(block.parser.pdl) if block.fallback is not None: - iter_blocks(f, block.fallback) - - -def iter_blocks(f: Callable[[BlockType], None], blocks: BlocksType) -> None: - if not isinstance(blocks, str) and isinstance(blocks, Sequence): - for block in blocks: - f(block) - else: - f(blocks) + f(block.fallback) class MappedFunctions: diff --git a/tests/test_implicit_ignore.py b/tests/test_implicit_ignore.py index be2fc7637..474d664b5 100644 --- a/tests/test_implicit_ignore.py +++ b/tests/test_implicit_ignore.py @@ -55,3 +55,26 @@ def test_no_warning2(capsys): "warnings": [""], } do_test(capsys, test) + + +def test_function(capsys): + test = { + "prog": """ +defs: + f: + function: + return: + - Hello + - How are you? + - Bye +call: f +args: {} +""", + "result": "Bye", + "warnings": [ + "Warning: the result of block `Hello` is not used.", + "Warning: the result of block `How are you?` is not used.", + "", + ], + } + do_test(capsys, test) From a6e9f569b1c0567a1d7e6cab97b6998139d51923 Mon Sep 17 00:00:00 2001 From: Mandana Vaziri Date: Fri, 18 Oct 2024 11:20:37 -0400 Subject: [PATCH 02/12] cleanup Signed-off-by: Mandana Vaziri --- src/pdl/pdl_analysis.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index ddded4c8a..b608a5e08 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -2,8 +2,6 @@ from dataclasses import dataclass from typing import Sequence -from .pdl_dumper import blocks_to_dict, dump_yaml - from .pdl_ast import ( AdvancedBlockType, ArrayBlock, @@ -32,6 +30,7 @@ TextBlock, ) from .pdl_ast_utils import iter_block_children +from .pdl_dumper import blocks_to_dict, dump_yaml @dataclass @@ -47,7 +46,10 @@ def with_implicit_ignore(self, b): def unused_warning(block: BlockType): global _DISPLAY_UNUSED_HINT # pylint: disable= global-statement - print(f"Warning: the result of block `{dump_yaml(blocks_to_dict(block, json_compatible=True))}` is not used.", file=sys.stderr) + print( + f"Warning: the result of block `{dump_yaml(blocks_to_dict(block, json_compatible=True))}` is not used.", + file=sys.stderr, + ) if _DISPLAY_UNUSED_HINT: _DISPLAY_UNUSED_HINT = False print( From 5d68a25de0598ea6b820431863ea5707722125db Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Fri, 18 Oct 2024 13:06:47 -0400 Subject: [PATCH 03/12] Fix analysis and dumper --- src/pdl/pdl_analysis.py | 10 +++++++++- src/pdl/pdl_dumper.py | 26 ++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index b608a5e08..7083d524a 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -105,7 +105,6 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None | ModelBlock() | CallBlock() | CodeBlock() - | EmptyBlock() | ReadBlock() ): if state.implicit_ignore: @@ -118,6 +117,15 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None ), block, ) + case EmptyBlock(): + iter_block_children( + ( + lambda blocks: unused_blocks( + state.with_implicit_ignore(False), blocks + ) + ), + block, + ) case ErrorBlock(): pass # Non-leaf blocks diff --git a/src/pdl/pdl_dumper.py b/src/pdl/pdl_dumper.py index 8c6dd261f..368bb7621 100644 --- a/src/pdl/pdl_dumper.py +++ b/src/pdl/pdl_dumper.py @@ -21,6 +21,10 @@ GetBlock, IfBlock, IncludeBlock, + JoinArray, + JoinLastOf, + JoinText, + JoinType, LastOfBlock, LitellmModelBlock, LitellmParameters, @@ -76,7 +80,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc if not isinstance(block, Block): return block d: dict[str, Any] = {} - d["kind"] = block.kind + d["kind"] = str(block.kind) if block.description is not None: d["description"] = block.description if block.spec is not None: @@ -87,7 +91,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc } match block: case BamModelBlock(): - d["platform"] = block.platform + d["platform"] = str(block.platform) d["model"] = block.model if block.input is not None: d["input"] = blocks_to_dict(block.input, json_compatible) @@ -105,7 +109,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc if block.constraints is not None: d["constraints"] = block.constraints case LitellmModelBlock(): - d["platform"] = block.platform + d["platform"] = str(block.platform) d["model"] = block.model if block.input is not None: d["input"] = blocks_to_dict(block.input, json_compatible) @@ -159,7 +163,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc case RepeatBlock(): d["repeat"] = blocks_to_dict(block.repeat, json_compatible) d["num_iterations"] = block.num_iterations - d["join"] = block.join.model_dump(by_alias=True) + d["join"] = join_to_dict(block.join) if block.trace is not None: d["trace"] = [ blocks_to_dict(blocks, json_compatible) for blocks in block.trace @@ -167,7 +171,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc case RepeatUntilBlock(): d["repeat"] = blocks_to_dict(block.repeat, json_compatible) d["until"] = block.until - d["join"] = block.join.model_dump(by_alias=True) + d["join"] = join_to_dict(block.join) if block.trace is not None: d["trace"] = [ blocks_to_dict(blocks, json_compatible) for blocks in block.trace @@ -175,7 +179,7 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc case ForBlock(): d["for"] = block.fors d["repeat"] = blocks_to_dict(block.repeat, json_compatible) - d["join"] = block.join.model_dump(by_alias=True) + d["join"] = join_to_dict(block.join) if block.trace is not None: d["trace"] = [ blocks_to_dict(blocks, json_compatible) for blocks in block.trace @@ -217,6 +221,16 @@ def block_to_dict(block: pdl_ast.BlockType, json_compatible: bool) -> DumpedBloc return d +def join_to_dict(join: JoinType) -> dict[str, Any]: + d = {} + match join: + case JoinText(): + d["with"] = join.join_string + case JoinArray() | JoinLastOf(): + d["as"] = str(join.iteration_type) + return d + + JsonType: TypeAlias = None | bool | int | float | str | dict[str, "JsonType"] From c1af353fb4c1b412c8794c10a5092f75295be403 Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Fri, 18 Oct 2024 13:07:10 -0400 Subject: [PATCH 04/12] Fix cldk assistant --- examples/cldk/cldk-assistant.pdl | 242 ++++++++++++++++--------------- 1 file changed, 122 insertions(+), 120 deletions(-) diff --git a/examples/cldk/cldk-assistant.pdl b/examples/cldk/cldk-assistant.pdl index 59524abea..42431da5a 100644 --- a/examples/cldk/cldk-assistant.pdl +++ b/examples/cldk/cldk-assistant.pdl @@ -27,137 +27,139 @@ text: result = cldk_state - " done!" - repeat: - - read: - def: query - message: "\n\nHow can I help you [Type 'quit' to quit]?\n" - contribute: [] - - "\n***Generating PDL code for your query:\n" - - if: ${ query != 'quit'} - then: - - model: watsonx/ibm/granite-20b-code-instruct - def: PDL - input: - - | - Question: What are all the classes? - Answer: - ``` - text: - - lang: python - code: + text: + - read: + def: query + message: "\n\nHow can I help you [Type 'quit' to quit]?\n" + contribute: [] + - "\n***Generating PDL code for your query:\n" + - if: ${ query != 'quit'} + then: + text: + - model: watsonx/ibm/granite-20b-code-instruct + def: PDL + input: - | - classes = PDL_SESSION.cldk_state.get_classes().keys() - result = ", ".join(classes) - ``` + Question: What are all the classes? + Answer: + ``` + text: + - lang: python + code: + - | + classes = PDL_SESSION.cldk_state.get_classes().keys() + result = ", ".join(classes) + ``` - Question: What are all the constructors of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - constructors = PDL_SESSION.cldk_state.get_constructors("org.ibm.App") - result = ", ".join(constructors) - ``` + Question: What are all the constructors of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + constructors = PDL_SESSION.cldk_state.get_constructors("org.ibm.App") + result = ", ".join(constructors) + ``` - Question: What are all the fields of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - fields = PDL_SESSION.cldk_state.get_fields("org.ibm.App") - names = sum([f.variables for f in fields], []) - result = ", ".join(names) - ``` + Question: What are all the fields of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + fields = PDL_SESSION.cldk_state.get_fields("org.ibm.App") + names = sum([f.variables for f in fields], []) + result = ", ".join(names) + ``` - Question: What are all the methods of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - methods = PDL_SESSION.cldk_state.get_methods_in_class("org.ibm.App") - result = ", ".join(methods) - ``` + Question: What are all the methods of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + methods = PDL_SESSION.cldk_state.get_methods_in_class("org.ibm.App") + result = ", ".join(methods) + ``` - Question: Show me the call graph of class "org.ibm.App" - Answer: - ``` - text: - - lang: python - code: - - | - graph = PDL_SESSION.cldk_state.get_class_call_graph("org.ibm.App", method_name=None) - result = graph - ``` + Question: Show me the call graph of class "org.ibm.App" + Answer: + ``` + text: + - lang: python + code: + - | + graph = PDL_SESSION.cldk_state.get_class_call_graph("org.ibm.App", method_name=None) + result = graph + ``` - Question: What is the code of method Foo(string) of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method.code - ``` + Question: What is the code of method Foo(string) of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method.code + ``` - Question: Generate a summary for method Foo(string) of class org.ibm.App - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method - - "\n\nGenerate a summary of method Foo\n\n" - - model: watsonx/meta-llama/llama-3-1-70b-instruct - ``` + Question: Generate a summary for method Foo(string) of class org.ibm.App + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method + - "\n\nGenerate a summary of method Foo\n\n" + - model: watsonx/meta-llama/llama-3-1-70b-instruct + ``` - Question: Generate a different comment for method Foo(string) in class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method - - "\n\nGenerate a different comment for method Foo(string)\n\n" - - model: watsonx/meta-llama/llama-3-1-70b-instruct - ``` + Question: Generate a different comment for method Foo(string) in class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method + - "\n\nGenerate a different comment for method Foo(string)\n\n" + - model: watsonx/meta-llama/llama-3-1-70b-instruct + ``` - If the query contains something about a field be sure to call a model. - - Question: ${ query } + If the query contains something about a field be sure to call a model. + + Question: ${ query } - parameters: - stop_sequence: ["Question"] - include_stop_sequence: false - - "\n\n***Executing the above PDL code:\n\n" - - lang: python - contribute: [] - code: | - from pdl import pdl_ast, pdl_interpreter - from pdl.pdl_ast import Program - from pdl.pdl_interpreter import process_prog - from pdl.pdl_interpreter import InterpreterState - from pdl.pdl_interpreter import empty_scope - import re - import yaml - s = """'${ PDL }'""" - print(s) - pdl = s.split("```")[1] - obj = yaml.safe_load(pdl) - state = InterpreterState() - data = Program.model_validate(obj) - - result, _, _, _ = process_prog(state, empty_scope, data) + parameters: + stop_sequence: ["Question"] + include_stop_sequence: false + - "\n\n***Executing the above PDL code:\n\n" + - lang: python + contribute: [] + code: | + from pdl import pdl_ast, pdl_interpreter + from pdl.pdl_ast import Program + from pdl.pdl_interpreter import process_prog + from pdl.pdl_interpreter import InterpreterState + from pdl.pdl_interpreter import empty_scope + import re + import yaml + s = """'${ PDL }'""" + print(s) + pdl = s.split("```")[1] + obj = yaml.safe_load(pdl) + state = InterpreterState() + data = Program.model_validate(obj) + + result, _, _, _ = process_prog(state, empty_scope, data) until: ${ query == 'quit' } From 0bb26104ee4720ab4937c7655078cbeaf8696ab2 Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Fri, 18 Oct 2024 13:12:48 -0400 Subject: [PATCH 05/12] Undo CLDK fixes --- examples/cldk/cldk-assistant.pdl | 242 +++++++++++++++---------------- 1 file changed, 120 insertions(+), 122 deletions(-) diff --git a/examples/cldk/cldk-assistant.pdl b/examples/cldk/cldk-assistant.pdl index 42431da5a..59524abea 100644 --- a/examples/cldk/cldk-assistant.pdl +++ b/examples/cldk/cldk-assistant.pdl @@ -27,139 +27,137 @@ text: result = cldk_state - " done!" - repeat: - text: - - read: - def: query - message: "\n\nHow can I help you [Type 'quit' to quit]?\n" - contribute: [] - - "\n***Generating PDL code for your query:\n" - - if: ${ query != 'quit'} - then: - text: - - model: watsonx/ibm/granite-20b-code-instruct - def: PDL - input: + - read: + def: query + message: "\n\nHow can I help you [Type 'quit' to quit]?\n" + contribute: [] + - "\n***Generating PDL code for your query:\n" + - if: ${ query != 'quit'} + then: + - model: watsonx/ibm/granite-20b-code-instruct + def: PDL + input: + - | + Question: What are all the classes? + Answer: + ``` + text: + - lang: python + code: - | - Question: What are all the classes? - Answer: - ``` - text: - - lang: python - code: - - | - classes = PDL_SESSION.cldk_state.get_classes().keys() - result = ", ".join(classes) - ``` + classes = PDL_SESSION.cldk_state.get_classes().keys() + result = ", ".join(classes) + ``` - Question: What are all the constructors of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - constructors = PDL_SESSION.cldk_state.get_constructors("org.ibm.App") - result = ", ".join(constructors) - ``` + Question: What are all the constructors of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + constructors = PDL_SESSION.cldk_state.get_constructors("org.ibm.App") + result = ", ".join(constructors) + ``` - Question: What are all the fields of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - fields = PDL_SESSION.cldk_state.get_fields("org.ibm.App") - names = sum([f.variables for f in fields], []) - result = ", ".join(names) - ``` + Question: What are all the fields of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + fields = PDL_SESSION.cldk_state.get_fields("org.ibm.App") + names = sum([f.variables for f in fields], []) + result = ", ".join(names) + ``` - Question: What are all the methods of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - methods = PDL_SESSION.cldk_state.get_methods_in_class("org.ibm.App") - result = ", ".join(methods) - ``` + Question: What are all the methods of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + methods = PDL_SESSION.cldk_state.get_methods_in_class("org.ibm.App") + result = ", ".join(methods) + ``` - Question: Show me the call graph of class "org.ibm.App" - Answer: - ``` - text: - - lang: python - code: - - | - graph = PDL_SESSION.cldk_state.get_class_call_graph("org.ibm.App", method_name=None) - result = graph - ``` + Question: Show me the call graph of class "org.ibm.App" + Answer: + ``` + text: + - lang: python + code: + - | + graph = PDL_SESSION.cldk_state.get_class_call_graph("org.ibm.App", method_name=None) + result = graph + ``` - Question: What is the code of method Foo(string) of class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method.code - ``` + Question: What is the code of method Foo(string) of class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method.code + ``` - Question: Generate a summary for method Foo(string) of class org.ibm.App - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method - - "\n\nGenerate a summary of method Foo\n\n" - - model: watsonx/meta-llama/llama-3-1-70b-instruct - ``` + Question: Generate a summary for method Foo(string) of class org.ibm.App + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method + - "\n\nGenerate a summary of method Foo\n\n" + - model: watsonx/meta-llama/llama-3-1-70b-instruct + ``` - Question: Generate a different comment for method Foo(string) in class org.ibm.App? - Answer: - ``` - text: - - lang: python - code: - - | - method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") - result = method - - "\n\nGenerate a different comment for method Foo(string)\n\n" - - model: watsonx/meta-llama/llama-3-1-70b-instruct - ``` + Question: Generate a different comment for method Foo(string) in class org.ibm.App? + Answer: + ``` + text: + - lang: python + code: + - | + method = PDL_SESSION.cldk_state.get_method("org.ibm.App", "Foo(string)") + result = method + - "\n\nGenerate a different comment for method Foo(string)\n\n" + - model: watsonx/meta-llama/llama-3-1-70b-instruct + ``` - If the query contains something about a field be sure to call a model. - - Question: ${ query } + If the query contains something about a field be sure to call a model. + + Question: ${ query } - parameters: - stop_sequence: ["Question"] - include_stop_sequence: false - - "\n\n***Executing the above PDL code:\n\n" - - lang: python - contribute: [] - code: | - from pdl import pdl_ast, pdl_interpreter - from pdl.pdl_ast import Program - from pdl.pdl_interpreter import process_prog - from pdl.pdl_interpreter import InterpreterState - from pdl.pdl_interpreter import empty_scope - import re - import yaml - s = """'${ PDL }'""" - print(s) - pdl = s.split("```")[1] - obj = yaml.safe_load(pdl) - state = InterpreterState() - data = Program.model_validate(obj) - - result, _, _, _ = process_prog(state, empty_scope, data) + parameters: + stop_sequence: ["Question"] + include_stop_sequence: false + - "\n\n***Executing the above PDL code:\n\n" + - lang: python + contribute: [] + code: | + from pdl import pdl_ast, pdl_interpreter + from pdl.pdl_ast import Program + from pdl.pdl_interpreter import process_prog + from pdl.pdl_interpreter import InterpreterState + from pdl.pdl_interpreter import empty_scope + import re + import yaml + s = """'${ PDL }'""" + print(s) + pdl = s.split("```")[1] + obj = yaml.safe_load(pdl) + state = InterpreterState() + data = Program.model_validate(obj) + + result, _, _, _ = process_prog(state, empty_scope, data) until: ${ query == 'quit' } From fdbc52f877978f9bbedd74797f4b7d306d40ee9f Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Tue, 29 Oct 2024 12:05:34 -0400 Subject: [PATCH 06/12] Do not output a warning when there is an explicit `lastOf` --- src/pdl/pdl_analysis.py | 65 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index 7083d524a..0b94adee4 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -36,16 +36,20 @@ @dataclass class UnusedConfig: implicit_ignore: bool + implicit_lastOf: bool # pylint: disable=invalid-name def with_implicit_ignore(self, b): - return UnusedConfig(implicit_ignore=b) + return UnusedConfig(implicit_ignore=b, implicit_lastOf=self.implicit_lastOf) + + def with_implicit_lastOf(self, b): # pylint: disable=invalid-name + return UnusedConfig(implicit_ignore=self.implicit_ignore, implicit_lastOf=b) _DISPLAY_UNUSED_HINT = True def unused_warning(block: BlockType): - global _DISPLAY_UNUSED_HINT # pylint: disable= global-statement + global _DISPLAY_UNUSED_HINT # pylint: disable=global-statement print( f"Warning: the result of block `{dump_yaml(blocks_to_dict(block, json_compatible=True))}` is not used.", file=sys.stderr, @@ -53,19 +57,26 @@ def unused_warning(block: BlockType): if _DISPLAY_UNUSED_HINT: _DISPLAY_UNUSED_HINT = False print( - " You might want to use a `text` block around the list or explicitly ignore the result with `contribute: [context]`.", + " You might want to use a `text` block around the list or explicitly ignore the result with a `lastOf` block or `contribute: [context]`.", file=sys.stderr, ) def unused_program(prog: Program) -> None: - state = UnusedConfig(implicit_ignore=False) + state = UnusedConfig(implicit_ignore=False, implicit_lastOf=True) unused_blocks(state, prog.root) def unused_blocks(state: UnusedConfig, blocks: BlocksType) -> None: if not isinstance(blocks, str) and isinstance(blocks, Sequence): - unused_advanced_block(state, LastOfBlock(lastOf=blocks)) + if state.implicit_lastOf: + state_with_ignore = state.with_implicit_ignore(True) + for b in blocks[:-1]: + unused_block(state_with_ignore, b) + unused_block(state, blocks[-1]) + else: + for b in blocks: + unused_block(state, b) else: unused_block(state, blocks) @@ -84,18 +95,16 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None if ContributeTarget.RESULT not in block.contribute: state = state.with_implicit_ignore(False) match block: - case LastOfBlock(): - if not isinstance(block.lastOf, str) and isinstance(block.lastOf, Sequence): - state_with_ignore = state.with_implicit_ignore(True) - for b in block.lastOf[:-1]: - unused_block(state_with_ignore, b) - unused_block(state, block.lastOf[-1]) - else: - unused_block(state, block.lastOf) - case ArrayBlock() | ObjectBlock() | TextBlock(): + case ArrayBlock() | LastOfBlock() | ObjectBlock() | TextBlock(): if state.implicit_ignore: unused_warning(block) - iter_block_children((lambda blocks: used_blocks(state, blocks)), block) + state_without_implicit_lastOf = state.with_implicit_lastOf( + False + ) # pylint: disable=invalid-name + iter_block_children( + (lambda blocks: used_blocks(state_without_implicit_lastOf, blocks)), + block, + ) # Leaf blocks case ( DataBlock() @@ -109,30 +118,26 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None ): if state.implicit_ignore: unused_warning(block) + state = state.with_implicit_ignore(False).with_implicit_lastOf(True) iter_block_children( - ( - lambda blocks: unused_blocks( - state.with_implicit_ignore(False), blocks - ) - ), + (lambda blocks: unused_blocks(state, blocks)), block, ) case EmptyBlock(): + state = state.with_implicit_ignore(False).with_implicit_lastOf(True) iter_block_children( - ( - lambda blocks: unused_blocks( - state.with_implicit_ignore(False), blocks - ) - ), + (lambda blocks: unused_blocks(state, blocks)), block, ) - case ErrorBlock(): - pass # Non-leaf blocks - case ( - ForBlock() | IfBlock() | IncludeBlock() | RepeatBlock() | RepeatUntilBlock() - ): + case IfBlock() | IncludeBlock(): + state = state.with_implicit_lastOf(True) + iter_block_children((lambda blocks: unused_blocks(state, blocks)), block) + # Loops blocks + case ForBlock() | RepeatBlock() | RepeatUntilBlock(): iter_block_children((lambda blocks: unused_blocks(state, blocks)), block) + case ErrorBlock(): + pass case _: assert False From c8cc66b2554a39bab13eb0fee812e40f3952fd93 Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Tue, 29 Oct 2024 22:29:02 -0400 Subject: [PATCH 07/12] Fix examples --- examples/talk/8-tools.pdl | 7 ++++--- examples/tools/calc.pdl | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/talk/8-tools.pdl b/examples/talk/8-tools.pdl index c90faba59..ac6468ef7 100644 --- a/examples/talk/8-tools.pdl +++ b/examples/talk/8-tools.pdl @@ -31,6 +31,7 @@ text: - "\n" - if: ${ action.name == "Calc" } then: - - "Obs: " - - lang: python - code: result = ${ action.arguments.expr } + text: + - "Obs: " + - lang: python + code: result = ${ action.arguments.expr } diff --git a/examples/tools/calc.pdl b/examples/tools/calc.pdl index c90faba59..ac6468ef7 100644 --- a/examples/tools/calc.pdl +++ b/examples/tools/calc.pdl @@ -31,6 +31,7 @@ text: - "\n" - if: ${ action.name == "Calc" } then: - - "Obs: " - - lang: python - code: result = ${ action.arguments.expr } + text: + - "Obs: " + - lang: python + code: result = ${ action.arguments.expr } From 99d23365676284eff733bfbf170dda0e4e2a75f7 Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Tue, 29 Oct 2024 22:33:50 -0400 Subject: [PATCH 08/12] Fix warnings --- src/pdl/pdl_analysis.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index 0b94adee4..69a1824cf 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -98,11 +98,9 @@ def unused_advanced_block(state: UnusedConfig, block: AdvancedBlockType) -> None case ArrayBlock() | LastOfBlock() | ObjectBlock() | TextBlock(): if state.implicit_ignore: unused_warning(block) - state_without_implicit_lastOf = state.with_implicit_lastOf( - False - ) # pylint: disable=invalid-name + state = state.with_implicit_lastOf(False) iter_block_children( - (lambda blocks: used_blocks(state_without_implicit_lastOf, blocks)), + (lambda blocks: used_blocks(state, blocks)), block, ) # Leaf blocks From 6a2646c23a2272c7b9cf59cf9a13fbe7af64b5ae Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Wed, 30 Oct 2024 14:37:22 -0400 Subject: [PATCH 09/12] Remove warnings --- examples/hello/hello-type.pdl | 3 ++- examples/talk/4-function.pdl | 11 ++++++----- examples/tutorial/function_definition.pdl | 3 ++- examples/tutorial/grouping_definitions.pdl | 3 ++- examples/tutorial/muting_block_output.pdl | 3 ++- tests/data/line/hello14.pdl | 1 + tests/data/line/hello15.pdl | 1 + tests/data/line/hello24.pdl | 1 + tests/data/line/hello25.pdl | 1 + 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/hello/hello-type.pdl b/examples/hello/hello-type.pdl index 9f6514312..7d13fd180 100644 --- a/examples/hello/hello-type.pdl +++ b/examples/hello/hello-type.pdl @@ -9,7 +9,8 @@ text: language: str spec: int return: - - "\nTranslate the sentence '${ sentence }' to ${ language }\n" + lastOf: + - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" - model: replicate/ibm-granite/granite-3.0-8b-instruct parameters: stop_sequences: "\n" diff --git a/examples/talk/4-function.pdl b/examples/talk/4-function.pdl index 15751c436..2d33c387f 100644 --- a/examples/talk/4-function.pdl +++ b/examples/talk/4-function.pdl @@ -5,11 +5,12 @@ text: sentence: str language: str return: - - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" - - model: replicate/ibm-granite/granite-3.0-8b-instruct - parameters: - stop_sequences: "\n" - temperature: 0 + lastOf: + - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + - model: replicate/ibm-granite/granite-3.0-8b-instruct + parameters: + stop_sequences: "\n" + temperature: 0 - call: translate args: sentence: I love Paris! diff --git a/examples/tutorial/function_definition.pdl b/examples/tutorial/function_definition.pdl index 15751c436..fd47bc40d 100644 --- a/examples/tutorial/function_definition.pdl +++ b/examples/tutorial/function_definition.pdl @@ -5,7 +5,8 @@ text: sentence: str language: str return: - - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + - text: "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + contribute: [context] - model: replicate/ibm-granite/granite-3.0-8b-instruct parameters: stop_sequences: "\n" diff --git a/examples/tutorial/grouping_definitions.pdl b/examples/tutorial/grouping_definitions.pdl index 88cf3e05b..81dd7060f 100644 --- a/examples/tutorial/grouping_definitions.pdl +++ b/examples/tutorial/grouping_definitions.pdl @@ -5,7 +5,8 @@ defs: sentence: str language: str return: - - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + - text: "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + contribute: [context] - model: replicate/ibm-granite/granite-3.0-8b-instruct parameters: stop_sequences: "\n" diff --git a/examples/tutorial/muting_block_output.pdl b/examples/tutorial/muting_block_output.pdl index ce1b0bf80..842cc01dd 100644 --- a/examples/tutorial/muting_block_output.pdl +++ b/examples/tutorial/muting_block_output.pdl @@ -5,7 +5,8 @@ defs: sentence: str language: str return: - - "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + - text: "\nTranslate the sentence '${ sentence }' to ${ language }.\n" + contribute: [context] - model: replicate/ibm-granite/granite-3.0-8b-instruct parameters: stop_sequences: "\n" diff --git a/tests/data/line/hello14.pdl b/tests/data/line/hello14.pdl index 8a601442f..fb049f368 100644 --- a/tests/data/line/hello14.pdl +++ b/tests/data/line/hello14.pdl @@ -14,6 +14,7 @@ text: language: str spec: int return: + lastOf: - "\nTranslate the sentence '${ sentence }' to ${ language }\n" - model: watsonx/ibm/granite-20b-multilingual parameters: diff --git a/tests/data/line/hello15.pdl b/tests/data/line/hello15.pdl index fa3c031ed..f07835947 100644 --- a/tests/data/line/hello15.pdl +++ b/tests/data/line/hello15.pdl @@ -3,6 +3,7 @@ text: - def: stutter function: return: + lastOf: - get: boolean - ${ something } - "Hello World!\n" diff --git a/tests/data/line/hello24.pdl b/tests/data/line/hello24.pdl index 8934e8e92..b9a0020a1 100644 --- a/tests/data/line/hello24.pdl +++ b/tests/data/line/hello24.pdl @@ -14,6 +14,7 @@ text: language: str spec: int return: + lastOf: - "\nTranslate the sentence '${ sentence }' to ${ language }\n" - model: watsonx/ibm/granite-34b-code-instruct parameters: diff --git a/tests/data/line/hello25.pdl b/tests/data/line/hello25.pdl index 0f42e5fea..aad49031d 100644 --- a/tests/data/line/hello25.pdl +++ b/tests/data/line/hello25.pdl @@ -12,6 +12,7 @@ text: sentence: str language: str return: + lastOf: - "\nTranslate the sentence '${ sentence1 }' to ${ language }\n" - model: watsonx/ibm/granite-20b-multilingual parameters: From 91844a8835c23977f55357ac42024f8cf2abc70c Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Thu, 31 Oct 2024 12:50:07 -0400 Subject: [PATCH 10/12] Fix tests --- tests/test_line_table.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_line_table.py b/tests/test_line_table.py index 60713e974..f236f7b97 100644 --- a/tests/test_line_table.py +++ b/tests/test_line_table.py @@ -169,7 +169,7 @@ def test_line13(capsys: CaptureFixture[str]): "file": "tests/data/line/hello14.pdl", "errors": [ "", - "tests/data/line/hello14.pdl:24 - Type errors in result of function call to translate:", + "tests/data/line/hello14.pdl:25 - Type errors in result of function call to translate:", "tests/data/line/hello14.pdl:16 - Bonjour le monde! should be of type ", ], } @@ -183,7 +183,7 @@ def test_line14(capsys: CaptureFixture[str]): "file": "tests/data/line/hello15.pdl", "errors": [ "", - "tests/data/line/hello15.pdl:6 - Error during the evaluation of ${ boolean }: 'boolean' is undefined", + "tests/data/line/hello15.pdl:7 - Error during the evaluation of ${ boolean }: 'boolean' is undefined", ], } @@ -304,7 +304,7 @@ def test_line23(capsys: CaptureFixture[str]): "file": "tests/data/line/hello24.pdl", "errors": [ "", - "tests/data/line/hello24.pdl:24 - Error during the evaluation of Hello,${ GEN1 }: 'GEN1' is undefined", + "tests/data/line/hello24.pdl:25 - Error during the evaluation of Hello,${ GEN1 }: 'GEN1' is undefined", ], } From 1fccde270f46e5d2709e95ac50dc10a3bbe1504a Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Thu, 31 Oct 2024 23:17:34 -0400 Subject: [PATCH 11/12] Fix tests --- tests/test_implicit_ignore.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/test_implicit_ignore.py b/tests/test_implicit_ignore.py index 474d664b5..2c11713dc 100644 --- a/tests/test_implicit_ignore.py +++ b/tests/test_implicit_ignore.py @@ -5,7 +5,7 @@ def do_test(capsys, test): result = exec_str(test["prog"]) captured = capsys.readouterr() warnings = {line.strip() for line in captured.err.split("\n")} - { - "You might want to use a `text` block around the list or explicitly ignore the result with `contribute: [context]`." + "You might want to use a `text` block around the list or explicitly ignore the result with a `lastOf` block or `contribute: [context]`." } assert result == test["result"] assert set(warnings) == set(test["warnings"]) @@ -20,8 +20,12 @@ def test_strings(capsys): """, "result": "Bye", "warnings": [ - "Warning: the result of block `Hello` is not used.", - "Warning: the result of block `How are you?` is not used.", + "Warning: the result of block `Hello", + "...", + "` is not used.", + "Warning: the result of block `How are you?", + "...", + "` is not used.", "", ], } @@ -72,8 +76,12 @@ def test_function(capsys): """, "result": "Bye", "warnings": [ - "Warning: the result of block `Hello` is not used.", - "Warning: the result of block `How are you?` is not used.", + "Warning: the result of block `Hello", + "...", + "` is not used.", + "Warning: the result of block `How are you?", + "...", + "` is not used.", "", ], } From 4e0887f6e4a48df147bdaffb009495a3563bf5b9 Mon Sep 17 00:00:00 2001 From: Louis Mandel Date: Thu, 31 Oct 2024 23:22:21 -0400 Subject: [PATCH 12/12] Never fail during the analysis of implicit ignored --- src/pdl/pdl_analysis.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pdl/pdl_analysis.py b/src/pdl/pdl_analysis.py index 69a1824cf..5c31a0333 100644 --- a/src/pdl/pdl_analysis.py +++ b/src/pdl/pdl_analysis.py @@ -63,8 +63,11 @@ def unused_warning(block: BlockType): def unused_program(prog: Program) -> None: - state = UnusedConfig(implicit_ignore=False, implicit_lastOf=True) - unused_blocks(state, prog.root) + try: + state = UnusedConfig(implicit_ignore=False, implicit_lastOf=True) + unused_blocks(state, prog.root) + except Exception as exc: + print(f"Unexpected error in implicit ignored analysis: {exc}") def unused_blocks(state: UnusedConfig, blocks: BlocksType) -> None: