From 8a8ac63427a117e3055b60df17bce68eed7e61b0 Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Fri, 13 Dec 2024 19:31:23 -0700 Subject: [PATCH 1/6] Add a non-docstring option for prompts --- outlines/prompts.py | 42 ++++++++++++++++++++++++++++++++++-------- tests/test_prompts.py | 24 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index a7824451a..44a0a8fc0 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -1,3 +1,4 @@ +import dis import functools import inspect import json @@ -42,6 +43,26 @@ def __str__(self): return self.template +def _template_variable_assigned(fn: Callable) -> Optional[str]: + instruction_set = list(dis.Bytecode(fn)) + if ( + len(instruction_set) >= 3 + and instruction_set[2].opname == "STORE_FAST" + and instruction_set[2].argrepr == "_" + ): + return instruction_set[1].argval + elif tuple( + instr + for instr in instruction_set + if instr.opname == "STORE_FAST" and instr.argrepr == "_" + ): + raise ValueError( + "Can only use simple string literals to `_` and it must be the only instruction in the function." + ) + else: + return None + + def prompt(fn: Callable) -> Prompt: """Decorate a function that contains a prompt template. @@ -79,15 +100,20 @@ def prompt(fn: Callable) -> Prompt: """ - signature = inspect.signature(fn) - - # The docstring contains the template that will be rendered to be used - # as a prompt to the language model. - docstring = fn.__doc__ - if docstring is None: - raise TypeError("Could not find a template in the function's docstring.") + # Either the docstring or `_` variable contains the template + # that will be rendered to be used as a prompt to the language model. + potential_template = _template_variable_assigned(fn) + if potential_template is None: + docstring = fn.__doc__ + if docstring is None: + raise TypeError( + "Could not find a template in the function's docstring or assigned to `_`" + ) + else: + potential_template = docstring - template = cast(str, docstring) + template = cast(str, potential_template) + signature = inspect.signature(fn) return Prompt(template, signature) diff --git a/tests/test_prompts.py b/tests/test_prompts.py index a0433c0e5..8cc30c9cf 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -148,6 +148,30 @@ def test_kwarg_tpl(var, other_var="other"): assert p == "test and test" +def test_prompt_template_keyword(): + @outlines.prompt + def test_tpl_keyword(variable): + _ = """{{variable}} test""" + + assert test_tpl_keyword.template == "{{variable}} test" + assert test_tpl_keyword.parameters == ["variable"] + + @outlines.prompt + def test_tpl_keyword_single_quotes(variable): + _ = "{{variable}} test" + + assert test_tpl_keyword_single_quotes.template == "{{variable}} test" + assert test_tpl_keyword_single_quotes.parameters == ["variable"] + + +def test_prompt_bad_template_keyword(): + with pytest.raises(ValueError, match="_"): + + @outlines.prompt + def test_tpl_keyword(variable): + _ = f"{'test'}" + + def test_no_prompt(): with pytest.raises(TypeError, match="template"): From bf50a3c5e4d2ed863b758a85779c57d322ecb834 Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Fri, 13 Dec 2024 19:46:29 -0700 Subject: [PATCH 2/6] Actually add docstring support --- outlines/prompts.py | 4 +++- tests/test_prompts.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index 44a0a8fc0..7d6541bbd 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -115,7 +115,9 @@ def prompt(fn: Callable) -> Prompt: template = cast(str, potential_template) signature = inspect.signature(fn) - return Prompt(template, signature) + prompt_instance = Prompt(template, signature) + prompt_instance.__doc__ = fn.__doc__ + return prompt_instance def render(template: str, **values: Optional[Dict[str, Any]]) -> str: diff --git a/tests/test_prompts.py b/tests/test_prompts.py index 8cc30c9cf..b04d6276b 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -163,6 +163,13 @@ def test_tpl_keyword_single_quotes(variable): assert test_tpl_keyword_single_quotes.template == "{{variable}} test" assert test_tpl_keyword_single_quotes.parameters == ["variable"] + @outlines.prompt + def test_tpl_keyword_docstring(variable): + """docstring""" + _ = "{{variable}} test" + + assert test_tpl_keyword_docstring.__doc__ == "docstring" + def test_prompt_bad_template_keyword(): with pytest.raises(ValueError, match="_"): From c6ddc05dd294df33bc267fd21894e6f9c42a171d Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Fri, 13 Dec 2024 21:24:47 -0700 Subject: [PATCH 3/6] undo __doc__ override --- outlines/prompts.py | 1 - tests/test_prompts.py | 7 ------- 2 files changed, 8 deletions(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index 7d6541bbd..9a901293b 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -116,7 +116,6 @@ def prompt(fn: Callable) -> Prompt: signature = inspect.signature(fn) prompt_instance = Prompt(template, signature) - prompt_instance.__doc__ = fn.__doc__ return prompt_instance diff --git a/tests/test_prompts.py b/tests/test_prompts.py index b04d6276b..8cc30c9cf 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -163,13 +163,6 @@ def test_tpl_keyword_single_quotes(variable): assert test_tpl_keyword_single_quotes.template == "{{variable}} test" assert test_tpl_keyword_single_quotes.parameters == ["variable"] - @outlines.prompt - def test_tpl_keyword_docstring(variable): - """docstring""" - _ = "{{variable}} test" - - assert test_tpl_keyword_docstring.__doc__ == "docstring" - def test_prompt_bad_template_keyword(): with pytest.raises(ValueError, match="_"): From ad458fe34f1d251ad9aa4e01d5c6264fb96313c2 Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Fri, 13 Dec 2024 21:25:44 -0700 Subject: [PATCH 4/6] any instead of tuple --- outlines/prompts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index 9a901293b..9bae2b37e 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -51,7 +51,7 @@ def _template_variable_assigned(fn: Callable) -> Optional[str]: and instruction_set[2].argrepr == "_" ): return instruction_set[1].argval - elif tuple( + elif any( instr for instr in instruction_set if instr.opname == "STORE_FAST" and instr.argrepr == "_" From 7f93c0cae26b1356dccac7dcfb9f7f527e9ad931 Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Fri, 13 Dec 2024 21:27:19 -0700 Subject: [PATCH 5/6] unneeded assigment --- outlines/prompts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index 9bae2b37e..e75759585 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -115,8 +115,7 @@ def prompt(fn: Callable) -> Prompt: template = cast(str, potential_template) signature = inspect.signature(fn) - prompt_instance = Prompt(template, signature) - return prompt_instance + return Prompt(template, signature) def render(template: str, **values: Optional[Dict[str, Any]]) -> str: From 9b5192a43288dd744c7723e47ac64dfbd2950d8d Mon Sep 17 00:00:00 2001 From: Matt Hauff Date: Sun, 15 Dec 2024 16:22:10 -0700 Subject: [PATCH 6/6] use functools.update_wrapper --- outlines/prompts.py | 4 +++- tests/test_prompts.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/outlines/prompts.py b/outlines/prompts.py index e75759585..40c6a4012 100644 --- a/outlines/prompts.py +++ b/outlines/prompts.py @@ -115,7 +115,9 @@ def prompt(fn: Callable) -> Prompt: template = cast(str, potential_template) signature = inspect.signature(fn) - return Prompt(template, signature) + prompt_instance = Prompt(template, signature) + functools.update_wrapper(prompt_instance, fn) + return prompt_instance def render(template: str, **values: Optional[Dict[str, Any]]) -> str: diff --git a/tests/test_prompts.py b/tests/test_prompts.py index 8cc30c9cf..f46d5837e 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -149,6 +149,8 @@ def test_kwarg_tpl(var, other_var="other"): def test_prompt_template_keyword(): + import pydoc + @outlines.prompt def test_tpl_keyword(variable): _ = """{{variable}} test""" @@ -156,6 +158,13 @@ def test_tpl_keyword(variable): assert test_tpl_keyword.template == "{{variable}} test" assert test_tpl_keyword.parameters == ["variable"] + @outlines.prompt + def test_tpl_keyword_w_docstring(variable): + """custom docstring""" + _ = """{{variable}} test""" + + assert "custom docstring" in pydoc.render_doc(test_tpl_keyword_w_docstring) + @outlines.prompt def test_tpl_keyword_single_quotes(variable): _ = "{{variable}} test"