Skip to content

Commit 06e0544

Browse files
Improve tool and state isolation in .llm() method (#40)
- Add _clear_tools() and _disable_state() methods to ensure clean tool and state isolation - Modify .llm() method to create a clean copy of the Promptic instance - Bump version to 5.3.1 to reflect the isolation improvement - Add test case to verify tool isolation between parent and child functions
1 parent c0497a7 commit 06e0544

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

promptic.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from litellm import completion as litellm_completion
1818
from pydantic import BaseModel
1919

20-
__version__ = "5.3.0"
20+
__version__ = "5.3.1"
2121

2222
SystemPrompt = Optional[Union[str, List[str], List[Dict[str, str]]]]
2323

@@ -360,11 +360,22 @@ def decorate(cls, func: Callable = None, **kwargs):
360360
instance = cls(**kwargs)
361361
return instance._decorator(func) if func else instance
362362

363+
def _clear_tools(self):
364+
self.tools = {}
365+
self.tool_definitions = None
366+
367+
def _disable_state(self):
368+
self.state = None
369+
363370
def llm(self, func: Callable = None, **kwargs):
364371
"""Decorate a function with the Promptic instance."""
365372
new_instance = copy.copy(self)
373+
new_instance._clear_tools()
374+
new_instance._disable_state()
375+
366376
for key, value in kwargs.items():
367377
setattr(new_instance, key, value)
378+
368379
return new_instance._decorator(func) if func else new_instance._decorator
369380

370381
def _decorator(self, func: Callable):

tests/test_promptic.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,3 +1623,48 @@ def test_completion_method(model, create_completion_fn):
16231623
):
16241624
response = p_with_state.completion(messages)
16251625
assert "Paris" in response.choices[0].message.content
1626+
1627+
1628+
@pytest.mark.parametrize("model", CHEAP_MODELS)
1629+
@pytest.mark.parametrize(
1630+
"create_completion_fn", [openai_completion_fn, litellm_completion]
1631+
)
1632+
def test_tool_isolation_with_llm_method(model, create_completion_fn):
1633+
"""Test that tools don't leak between parent and child functions when using llm method"""
1634+
if create_completion_fn == openai_completion_fn and not model.startswith("gpt"):
1635+
pytest.skip("Non-GPT models are not supported with OpenAI client")
1636+
1637+
p = Promptic(
1638+
model=model,
1639+
temperature=0,
1640+
timeout=5,
1641+
create_completion_fn=create_completion_fn,
1642+
)
1643+
1644+
@retry(
1645+
wait=wait_exponential(multiplier=1, min=4, max=10),
1646+
retry=retry_if_exception_type(ERRORS),
1647+
)
1648+
@p.llm
1649+
def parent_function(command):
1650+
"""Respond to: {command}"""
1651+
1652+
# Add a tool to the parent function
1653+
@parent_function.tool
1654+
def get_time():
1655+
"""Get the current time"""
1656+
return "12:00 PM"
1657+
1658+
# Create a child function using the parent's llm method
1659+
@p.llm
1660+
def child_function(command):
1661+
"""Respond to: {command}"""
1662+
1663+
# Verify parent function has access to the tool
1664+
result = parent_function("What time is it?")
1665+
assert "12:00" in result
1666+
1667+
# Verify child function does not have access to the tool
1668+
# It should respond without using the tool
1669+
result = child_function("What time is it?")
1670+
assert "12:00" not in result

0 commit comments

Comments
 (0)