Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: SingleChunkQa crashing on specific prompt ordering #931

Merged
merged 2 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
...

### Fixes
- Serialization and Deserialization of `Union[SpanAttributes, TaskSpanAttributes]` is working now.
- Serialization and deserialization of `ExportedSpan` and its `attributes` now works as expected.
- `PromptTemplate.to_rich_prompt` now always returns an empty list for prompt ranges that are empty.
- `SingleChunkQa` no longer crashes if given an empty input and a specific prompt template. This did not affect users who used models provided in `core`.

### Deprecations
...
Expand Down
16 changes: 13 additions & 3 deletions src/intelligence_layer/core/prompt_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,19 @@ def valid_range_for(
yield new_prompt_item(placeholder_prompt_item)
else:
if range_starts.get(placeholder):
placeholder_ranges[placeholder].extend(
valid_range_for(placeholder, end_cursor())
)
test_range = list(valid_range_for(placeholder, end_cursor()))
# the test_range is empty if there was never any text and the range is empty
# However, if there has been text previously, an empty text range will have
# a range consisting of start==end. This check is there to filter these as well.
if len(test_range) == 0 or (
test_range[0].start.item == test_range[0].end.item
and isinstance(test_range[0].end, TextCursor)
and isinstance(test_range[0].start, TextCursor)
and test_range[0].end.position == test_range[0].start.position
):
placeholder_ranges[placeholder].extend([])
else:
placeholder_ranges[placeholder].extend(test_range)
else:
range_starts[placeholder] = initial_start_text_cursor()
last_to = placeholder_to
Expand Down
4 changes: 3 additions & 1 deletion src/intelligence_layer/examples/qa/single_chunk_qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class SingleChunkQa(Task[SingleChunkQaInput, SingleChunkQaOutput]):
model: The model used throughout the task for model related API calls.
text_highlight: The task that is used for highlighting that parts of the input that are
relevant for the answer. Defaults to :class:`TextHighlight` .
instruction_config: defines instructions for different langaugaes.
instruction_config: defines instructions for different languages.
maximum_token: the maximal number of tokens to be generated for an answer.

Attributes:
Expand Down Expand Up @@ -176,6 +176,8 @@ def _shift_highlight_ranges_to_input(
self, prompt: RichPrompt, raw_highlights: Sequence[ScoredTextHighlight]
) -> Sequence[ScoredTextHighlight]:
# This only works with models that have an 'input' range, e.g. control models.
if "input" not in prompt.ranges or len(prompt.ranges["input"]) == 0:
return raw_highlights
input_cursor = prompt.ranges["input"][0].start
assert isinstance(input_cursor, TextCursor)
input_offset = input_cursor.position
Expand Down
17 changes: 11 additions & 6 deletions tests/core/test_prompt_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def test_to_prompt_resets_template(prompt_image: Image) -> None:
assert prompt_with_reset_template != prompt


def test_to_prompt_data_returns_ranges(prompt_image: Image) -> None:
def test_to_rich_prompt_returns_ranges(prompt_image: Image) -> None:
embedded_text = "Embedded"
prefix_items: list[PromptItem] = [
Text.from_text("Prefix Text Item"),
Expand Down Expand Up @@ -229,7 +229,7 @@ def test_to_prompt_data_returns_ranges(prompt_image: Image) -> None:
]


def test_to_prompt_data_returns_ranges_for_image_only_prompt(
def test_to_rich_prompt_returns_ranges_for_image_only_prompt(
prompt_image: Image,
) -> None:
template = PromptTemplate(
Expand All @@ -250,13 +250,18 @@ def test_to_prompt_data_returns_ranges_for_image_only_prompt(
assert r1 == prompt_data.ranges.get("r2")


def test_to_prompt_data_returns_no_range_with_empty_template() -> None:
def test_to_rich_prompt_returns_no_range_with_empty_template() -> None:
template = PromptTemplate("{% promptrange r1 %}{% endpromptrange %}")

assert template.to_rich_prompt().ranges.get("r1") == []


def test_to_prompt_data_returns_no_empty_ranges(prompt_image: Image) -> None:
def test_to_rich_prompt_returns_no_range_with_range_with_other_ranges() -> None:
template = PromptTemplate("""text{% promptrange r1 %}{% endpromptrange %}""")
assert template.to_rich_prompt().ranges.get("r1") == []


def test_to_rich_prompt_returns_no_empty_ranges(prompt_image: Image) -> None:
template = PromptTemplate(
"{{image}}{% promptrange r1 %}{% endpromptrange %} Some Text"
)
Expand All @@ -276,7 +281,7 @@ def test_prompt_template_raises_liquid_error_on_illeagal_range() -> None:
)


def test_to_prompt_data_returns_multiple_text_ranges_in_for_loop() -> None:
def test_to_rich_prompt_returns_multiple_text_ranges_in_for_loop() -> None:
embedded = "Some Content"
template = PromptTemplate(
"{% for i in (1..4) %}{% promptrange r1 %}{{embedded}}{% endpromptrange %}{% endfor %}"
Expand All @@ -293,7 +298,7 @@ def test_to_prompt_data_returns_multiple_text_ranges_in_for_loop() -> None:
]


def test_to_prompt_data_returns_multiple_imgae_ranges_in_for_loop(
def test_to_rich_prompt_returns_multiple_image_ranges_in_for_loop(
prompt_image: Image,
) -> None:
template = PromptTemplate(
Expand Down
13 changes: 13 additions & 0 deletions tests/examples/qa/test_single_chunk_qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,16 @@ def test_qa_highlights_will_not_become_out_of_bounds(
for highlight in output.highlights:
assert highlight.start >= 0
assert 0 < highlight.end <= len(input_text)


def test_qa_single_chunk_does_not_crash_when_input_is_empty(
single_chunk_qa: SingleChunkQa,
) -> None:
input_text = ""
input = SingleChunkQaInput(
chunk=TextChunk(input_text),
question="something",
language=Language("de"),
)
res = single_chunk_qa.run(input, NoOpTracer())
assert res.answer is None