Skip to content

Commit 85e183f

Browse files
authored
fix: better error message for llm metadata extractor (#11477)
1 parent 1661601 commit 85e183f

4 files changed

Lines changed: 31 additions & 7 deletions

File tree

docs-website/docs/pipeline-components/extractors/llmmetadataextractor.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Extracts metadata from documents using a Large Language Model. The metadata is e
1414
| | |
1515
| --- | --- |
1616
| **Most common position in a pipeline** | After [PreProcessors](../preprocessors.mdx) in an indexing pipeline |
17-
| **Mandatory init variables** | `prompt`: The prompt to instruct the LLM on how to extract metadata from the document <br /> <br />`chat_generator`: A Chat Generator instance which represents the LLM configured to return a JSON object |
17+
| **Mandatory init variables** | `prompt`: The prompt to instruct the LLM on how to extract metadata from the document. It must contain exactly one variable, called `document`. <br /> <br />`chat_generator`: A Chat Generator instance which represents the LLM configured to return a JSON object |
1818
| **Mandatory run variables** | `documents`: A list of documents |
1919
| **Output variables** | `documents`: A list of documents |
2020
| **API reference** | [Extractors](/reference/extractors-api) |
@@ -27,7 +27,7 @@ Extracts metadata from documents using a Large Language Model. The metadata is e
2727

2828
The `LLMMetadataExtractor` extraction relies on an LLM and a prompt to perform the metadata extraction. At initialization time, it expects an LLM, a Haystack Generator, and a prompt describing the metadata extraction process.
2929

30-
The prompt should have a variable called `document` that will point to a single document in the list of documents. So, to access the content of the document, you can use `{{ document.content }}` in the prompt.
30+
The prompt must have exactly one variable, called `document`, that points to a single document in the list of documents. So, to access the content of the document, you can use `{{ document.content }}` in the prompt. The component raises a `ValueError` at initialization if the prompt has no variables, more than one variable, or a variable with a different name.
3131

3232
At runtime, it expects a list of documents and will run the LLM on each document in the list, extracting metadata from the document. The metadata will be added to the document's metadata field.
3333

haystack/components/extractors/llm_metadata_extractor.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ class LLMMetadataExtractor:
3232
3333
The metadata is extracted by providing a prompt to an LLM that generates the metadata.
3434
35-
This component expects as input a list of documents and a prompt. The prompt should have a variable called
36-
`document` that will point to a single document in the list of documents. So to access the content of the document,
35+
This component expects as input a list of documents and a prompt. The prompt must have exactly one variable, called
36+
`document`, that points to a single document in the list of documents. So to access the content of the document,
3737
you can use `{{ document.content }}` in the prompt.
3838
3939
The component will run the LLM on each document in the list and extract metadata from the document. The metadata
@@ -162,7 +162,9 @@ def __init__(
162162
"""
163163
Initializes the LLMMetadataExtractor.
164164
165-
:param prompt: The prompt to be used for the LLM.
165+
:param prompt: The prompt to be used for the LLM. It must contain exactly one variable, called `document`,
166+
which points to a single document in the list of documents. For example, to access the content of the
167+
document, use `{{ document.content }}` in the prompt.
166168
:param chat_generator: a ChatGenerator instance which represents the LLM. In order for the component to work,
167169
the LLM should be configured to return a JSON object. For example, when using the OpenAIChatGenerator, you
168170
should pass `{"response_format": {"type": "json_object"}}` in the `generation_kwargs`.
@@ -182,9 +184,10 @@ def __init__(
182184
ast = SandboxedEnvironment().parse(prompt)
183185
template_variables = meta.find_undeclared_variables(ast)
184186
variables = list(template_variables)
185-
if len(variables) > 1 or variables[0] != "document":
187+
if variables != ["document"]:
186188
raise ValueError(
187-
f"Prompt must have exactly one variable called 'document'. Found {','.join(variables)} in the prompt."
189+
f"Prompt must have exactly one variable called 'document'. "
190+
f"Found {','.join(variables) or 'no variables'} in the prompt."
188191
)
189192
self.builder = PromptBuilder(prompt, required_variables=variables)
190193
self.raise_on_failure = raise_on_failure
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
``LLMMetadataExtractor`` now raises a clear ``ValueError`` when the ``prompt`` contains no
5+
template variables. Previously this case raised an unhelpful ``IndexError: list index out of
6+
range``. The error message now consistently explains that the prompt must contain exactly one
7+
variable called ``document``.

test/components/extractors/test_llm_metadata_extractor.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ def test_init_missing_prompt_variable(self, monkeypatch):
8080
prompt="prompt {{ wrong_variable }}", expected_keys=["key1", "key2"], chat_generator=chat_generator
8181
)
8282

83+
def test_init_no_prompt_variable(self, monkeypatch):
84+
monkeypatch.setenv("OPENAI_API_KEY", "test-api-key")
85+
chat_generator = OpenAIChatGenerator()
86+
87+
with pytest.raises(ValueError, match="exactly one variable called 'document'.*no variables"):
88+
_ = LLMMetadataExtractor(prompt="prompt without variables", chat_generator=chat_generator)
89+
90+
def test_init_too_many_prompt_variables(self, monkeypatch):
91+
monkeypatch.setenv("OPENAI_API_KEY", "test-api-key")
92+
chat_generator = OpenAIChatGenerator()
93+
94+
with pytest.raises(ValueError, match="exactly one variable called 'document'"):
95+
_ = LLMMetadataExtractor(prompt="prompt {{ document.content }} {{ extra }}", chat_generator=chat_generator)
96+
8397
def test_init_fails_without_chat_generator(self, monkeypatch):
8498
monkeypatch.setenv("OPENAI_API_KEY", "test-api-key")
8599
with pytest.raises(TypeError):

0 commit comments

Comments
 (0)