From 7d7a96b077722558e9f7deb135abeb6695269d08 Mon Sep 17 00:00:00 2001 From: Scott Lundberg Date: Mon, 6 May 2024 19:08:58 +0000 Subject: [PATCH 1/2] Optimistically try to trace all functions --- guidance/_guidance.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/guidance/_guidance.py b/guidance/_guidance.py index 510cac9e0..b43ae91e6 100644 --- a/guidance/_guidance.py +++ b/guidance/_guidance.py @@ -2,7 +2,7 @@ import inspect from . import models -from ._grammar import Placeholder, RawFunction, Terminal, replace_grammar_node, string +from ._grammar import Placeholder, RawFunction, Terminal, replace_grammar_node, string, StatefulException from ._utils import strip_multiline_string_indents @@ -36,9 +36,7 @@ def _decorator(f, *, stateless, cache, dedent, model): def wrapped(*args, **kwargs): # make a stateless grammar if we can - if stateless is True or ( - callable(stateless) and stateless(*args, **kwargs) - ): + try: # if we have a placeholder set then we must be in a recursive definition and so we return the placeholder placeholder = getattr(f, "_self_call_placeholder_", None) @@ -66,7 +64,7 @@ def wrapped(*args, **kwargs): return node # otherwise must be stateful (which means we can't be inside a select() call) - else: + except StatefulException: return RawFunction(f, args, kwargs) # Remove the first argument from the wrapped function From bed7492af39e0a9523374771e4e0a6d39f5c8aa7 Mon Sep 17 00:00:00 2001 From: Scott Lundberg Date: Mon, 6 May 2024 20:51:33 +0000 Subject: [PATCH 2/2] Clean up placeholders and add token count stateful check --- guidance/_grammar.py | 4 ++++ guidance/_guidance.py | 51 ++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/guidance/_grammar.py b/guidance/_grammar.py index e85c46bb0..14ba5dd56 100644 --- a/guidance/_grammar.py +++ b/guidance/_grammar.py @@ -170,6 +170,10 @@ def __radd__(self, value): def __getitem__(self, value): raise StatefulException("GrammarFunctions can't access state!") + + @property + def token_count(self): + raise StatefulException("GrammarFunctions can't access state!") def match( self, diff --git a/guidance/_guidance.py b/guidance/_guidance.py index b43ae91e6..1fd6ddff9 100644 --- a/guidance/_guidance.py +++ b/guidance/_guidance.py @@ -35,37 +35,38 @@ def _decorator(f, *, stateless, cache, dedent, model): @functools.wraps(f) def wrapped(*args, **kwargs): - # make a stateless grammar if we can - try: - - # if we have a placeholder set then we must be in a recursive definition and so we return the placeholder - placeholder = getattr(f, "_self_call_placeholder_", None) - if placeholder is not None: - return placeholder - - # otherwise we call the function to generate the grammar - else: - - # set a placeholder for recursive calls (only if we don't have arguments that might make caching a bad idea) - no_args = len(args) + len(kwargs) == 0 - if no_args: - f._self_call_placeholder_ = Placeholder() - - # call the function to get the grammar node + # if we have a placeholder set then we must be in a recursive definition and so we return the placeholder + placeholder = getattr(f, "_self_call_placeholder_", None) + if placeholder is not None: + return placeholder + + # otherwise we call the function to generate the grammar + else: + + # set a placeholder for recursive calls (only if we don't have arguments that might make caching a bad idea) + no_args = len(args) + len(kwargs) == 0 + if no_args: + f._self_call_placeholder_ = Placeholder() + + # try to trace the function to get a grammar node + node = None + try: node = f(_null_grammar, *args, **kwargs) if not isinstance(node, (Terminal, str)): node.name = f.__name__ - - # replace all the placeholders with our generated node + + # if that fails we must be stateful (which means we can't be inside a select() call) + except StatefulException: + return RawFunction(f, args, kwargs) + + # clean up, replacing all the placeholders with our generated node + finally: if no_args: - replace_grammar_node(node, f._self_call_placeholder_, node) + if node: + replace_grammar_node(node, f._self_call_placeholder_, node) del f._self_call_placeholder_ - return node - - # otherwise must be stateful (which means we can't be inside a select() call) - except StatefulException: - return RawFunction(f, args, kwargs) + return node # Remove the first argument from the wrapped function signature = inspect.signature(f)