Skip to content

Commit

Permalink
Add detailed explanations for failed assertions (#7)
Browse files Browse the repository at this point in the history
* Add detailed explanations for failed assertions

Fixes #2

Add detailed explanations for failed assertions using LLM.

* Modify `intentguard/intentguard.py` to include a `_generate_explanation` method that generates detailed explanations for failed assertions using the LLM.
* Update the `assert_code` method to call `_generate_explanation` when an assertion fails and include the explanation in the `AssertionError`.
* Add a new prompt template `explanation_prompt` in `intentguard/prompts.py` for generating detailed explanations for failed assertions.
* Update the existing test case `test_assert_code_false` in `tests/test_intentguard.py` to check for the presence of the explanation in the `AssertionError`.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/kdunee/intentguard/issues/2?shareId=XXXX-XXXX-XXXX-XXXX).

* Update `explanation_prompt` template for failed condition

* Correct the order of methods in the failed condition message
* Update the output message to reflect the corrected order of methods

* Add explanation generation for failed assertions

* Add `_generate_explanation` method to generate detailed explanations for failed assertions using the LLM.
* Modify `assert_code` method to call `_generate_explanation` when an assertion fails and include the explanation in the `AssertionError`.
* Update `_send_completion_request` method to handle the new explanation generation prompt.
* Import `explanation_prompt` from `prompts.py`.

* code reformat

* Mark the "Failed Assertion Explanations" task as completed in the roadmap.
  • Loading branch information
kdunee authored Nov 4, 2024
1 parent d8639b6 commit ccb77f8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 4 deletions.
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# IntentGuard Roadmap

- [ ] Failed Assertion Explanations (https://github.com/kdunee/intentguard/issues/2)
- [x] Failed Assertion Explanations (https://github.com/kdunee/intentguard/issues/2)
- Detailed reasoning for why assertions failed
- Suggestions for fixing the issues

Expand Down
32 changes: 30 additions & 2 deletions intentguard/intentguard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from litellm import completion

from intentguard.intentguard_options import IntentGuardOptions
from intentguard.prompts import system_prompt, reponse_schema
from intentguard.prompts import system_prompt, reponse_schema, explanation_prompt


class IntentGuard:
Expand Down Expand Up @@ -66,8 +66,9 @@ def assert_code(
final_result = self._vote_on_results(results)

if not final_result:
explanation = self._generate_explanation(prompt, options)
raise AssertionError(
f'Expected "{expectation}" to be true, but it was false'
f'Expected "{expectation}" to be true, but it was false. Explanation: {explanation}'
)

def _generate_objects_text(self, params: Dict[str, object]) -> str:
Expand Down Expand Up @@ -147,6 +148,33 @@ def _send_completion_request(
)
return json.loads(response.choices[0].message.content)["result"]

def _generate_explanation(self, prompt: str, options: IntentGuardOptions) -> str:
"""
Generate a detailed explanation for a failed assertion using the LLM.
This method sends a request to the LLM to generate a human-readable explanation
for why the given expectation was not met based on the provided objects.
Args:
objects_text (str): The formatted string of object source codes.
expectation (str): The condition that was evaluated.
options (IntentGuardOptions): The options for the LLM request.
Returns:
str: A detailed explanation of why the assertion failed.
"""
messages = [
{"content": explanation_prompt, "role": "system"},
{"content": prompt, "role": "user"},
]

response = completion(
model=options.model,
messages=messages,
temperature=1e-3,
)
return response.choices[0].message.content

def _vote_on_results(self, results: list) -> bool:
"""
Determine the final result based on voting.
Expand Down
46 changes: 46 additions & 0 deletions intentguard/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,52 @@ def another_method(self):
}
```"""

explanation_prompt = """Analyze Python code to explain why a given condition against named objects (classes or methods) failed.
You will receive:
- A list of named objects, where each object's value is the code of a class or method.
- A text of a condition that uses these object names.
- An indication that the condition was not met.
Your task is to provide a detailed explanation of why the condition was not fulfilled for the given objects.
# Steps
1. Parse the input to identify and load the classes or methods from the named objects.
2. Analyze the code structure of each named object, ensuring to understand the classes, methods, and their interactions.
3. Evaluate the specified condition using the parsed objects, referring to them by their given names.
4. Determine why the condition does not hold true based on the code analysis.
5. Provide a detailed explanation of the reasons for the failure.
# Output Format
Output a text explanation detailing why the condition was not met.
# Examples
### Input
**Objects:**
{obj1}:
```py
class MyClass:
def method(self):
return 5
```
{obj2}:
```py
class AnotherClass:
def another_method(self):
return 10
```
**Failed Condition:**
"{obj1} has method another_method and {obj2} has method method"
### Output
The condition was not met because {obj1} does not have a method named 'another_method' and {obj2} does not have a method named 'method'.
"""

reponse_schema = {
"name": "boolean_result",
"strict": True,
Expand Down
3 changes: 2 additions & 1 deletion tests/test_intentguard.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ def test_assert_code_true(self):
)

def test_assert_code_false(self):
with self.assertRaises(AssertionError):
with self.assertRaises(AssertionError) as cm:
self.guard.assert_code(
"{class} should not have any methods", {"class": IntentGuard}
)
self.assertIn("Explanation:", str(cm.exception))

def test_guard_options(self):
self.guard.assert_code(
Expand Down

0 comments on commit ccb77f8

Please sign in to comment.