SOLR-18187: Document enrichment with LLMs#4259
SOLR-18187: Document enrichment with LLMs#4259nicolo-rinaldi wants to merge 16 commits intoapache:mainfrom
Conversation
…tUpdateProcessorFactory
- multivalued outputField - outputField different from Str/Text, with numeric, boolean and date
…t with LLMs' module
| restTestHarness.delete(ManagedChatModelStore.REST_END_POINT + "/model1"); | ||
| } | ||
|
|
||
| private UpdateRequestProcessor createUpdateProcessor( |
There was a problem hiding this comment.
Can't this always be generalised and used for all the tests? In some of them, you are now repeating this code with small changes...
There was a problem hiding this comment.
this is the same as createUpdateProcessor a part from the creation of the request and getInstance()
maybe we can exclude the solr request + getInstance() and use that method also here? calling it like "initializeUpdateProcessorFactory"?
what do you think?
There was a problem hiding this comment.
I created a function initializeUpdateProcessorFactory that is used inside createUpdateProcessor. In this way, the code inside the first one can be reused
There was a problem hiding this comment.
why some test could not use these new functions?
e.g. init_multipleInputFields_shouldInitAllFields
There was a problem hiding this comment.
I kept them unrelated to the model creation, just to see the proper initialization of the Factory. I can see if this can be changed if you want
|
|
||
| @Test | ||
| public void init_promptFileWithMissingPlaceholder_shouldThrowExceptionInInform() { | ||
| NamedList<String> args = new NamedList<>(); |
There was a problem hiding this comment.
this is the same as createUpdateProcessor a part from the creation of the request and getInstance()
maybe we can exclude the solr request + getInstance() and use that method also here? calling it like "initializeUpdateProcessorFactory"?
what do you think?
There was a problem hiding this comment.
changed and fixed tests
| restTestHarness.delete(ManagedChatModelStore.REST_END_POINT + "/model1"); | ||
| } | ||
|
|
||
| private UpdateRequestProcessor createUpdateProcessor( |
There was a problem hiding this comment.
this is the same as createUpdateProcessor a part from the creation of the request and getInstance()
maybe we can exclude the solr request + getInstance() and use that method also here? calling it like "initializeUpdateProcessorFactory"?
what do you think?
| example above). These tokens are _mandatory_ for this module to work properly. Solr will throw an error if the | ||
| parameters are not properly defined. | ||
| For example, both the prompt and the content of the file prompt.txt, must contain the text '{string_field}', which | ||
| will be substituted with the content of the `string_field` field for each document. An example of a valid prompt with |
There was a problem hiding this comment.
I think the part so far could be explained in a more schematic and better understandable way.
| </updateRequestProcessorChain> | ||
| ---- | ||
|
|
||
| Another way of using more than one `inputField` is by using the following notation, instead of more than one parameter |
There was a problem hiding this comment.
Multiple inputField could also be defined by using the following notation:
| </arr> | ||
| ---- | ||
|
|
||
| The LLM response is mapped to the specified `outputField`. Note that this module only supports a subset of Solr's |
There was a problem hiding this comment.
Maybe we can also specify that only one outputField is supported
| ==== | ||
|
|
||
| === Index first and enrich your documents on a second pass | ||
| LLM calls are usually quite slow, so, depending on your use case it could be a good idea to index first your documents |
There was a problem hiding this comment.
LLM calls are typically slow, so depending on your use case, it may be preferable to first index your documents and enrich them with LLM-generated fields at a later stage.
…fields and updated documentation
|
|
||
| === Models | ||
|
|
||
| * A model in this module is a chat model, that answers with text given a prompt. |
| === Models | ||
|
|
||
| * A model in this module is a chat model, that answers with text given a prompt. | ||
| * A model in this Solr module is a reference to an external API that runs the Large Language Model responsible for chat |
|
|
||
| Exactly one of the following parameters is required: `prompt` or `promptFile`. | ||
|
|
||
| Another important feature of this module is that one (or more) `inputField` needs to be injected in the prompt. This is |
| </arr> | ||
| ---- | ||
|
|
||
| The LLM response is mapped to the specified `outputField`. Note that this module only supports a subset of Solr's |
| </updateRequestProcessorChain> | ||
| ---- | ||
|
|
||
| Another way of using more than one `inputField` is by using the following notation, instead of more than one parameter |
| ==== | ||
|
|
||
| === Index first and enrich your documents on a second pass | ||
| LLM calls are usually quite slow, so, depending on your use case it could be a good idea to index first your documents |
| boolean `enriched` field to `true`. | ||
|
|
||
| Faceting or querying on the boolean `enriched` field can also give you a quick idea on how many documents have been | ||
| enriched with the new generated fields. |
There was a problem hiding this comment.
Added a note to link to the section of the documentation related to the use of update chains
| } | ||
| ---- | ||
|
|
||
| == How to Trigger Document Enrichment during Indexing |
There was a problem hiding this comment.
This part has not been moved to the top near the model configuration in the solrconfig
| |=== | ||
| + | ||
| One (or more) `inputField` needs to be injected in the prompt. This is done by some special tokens, that are the |
There was a problem hiding this comment.
I would just say this is the field whose content is used as input/passed to the LLM to enrich the document. And that there could be more than one inputField defined.
I would move the other part about the special tokens to the prompt parameter explanation.
There was a problem hiding this comment.
At most, you could say that every inputField declared must be referred to in the prompt
| module to work properly. Solr will throw an error if the parameters are not properly defined. | ||
| For example, both the prompt or the content of the file `prompt.txt`, must contain the text '{string_field}', which | ||
| will be substituted with the content of the `string_field` field for each document. An example of a valid prompt with | ||
| multiple input fields is as follows: |
There was a problem hiding this comment.
maybe here I would say:
Multiple inputField could also be defined by using one of the following notations:
and then list the two ways
| These fields _can_ be multivalued. Solr uses structured output from LangChain4j to deal with LLMs' responses. | ||
|
|
||
|
|
||
| `prompt` or `promptFile`:: |
There was a problem hiding this comment.
here I would say that there are two ways of defining a prompt, one directly in the config and one through a file...
then I would explain how the prompt should be structured and the thing related to the inputFields
https://issues.apache.org/jira/browse/SOLR-18187
Description
The goal of this PR is to add a way to integrate LLMs directly into Solr at index time to fill fields that might be useful (e.g., categories, tags, etc.)
Solution
This PR adds LLM-based document enrichment capabilities to Solr's indexing pipeline via a new DocumentEnrichmentUpdateProcessorFactory in the language-models module. The processor allows users to enrich documents at index time by calling an LLM (via https://github.com/langchain4j/langchain4j) with a configurable prompt built from one or more existing document fields (inputFields), and storing the model's response into an output field. The output field can be of different types (i.e., string, text, int, long, float, double, boolean, and date) and can be single-valued or multi-valued. The structured output has been used to adapt to the output field type.
The implementation has taken inspiration from the text-to-vector feature in the same module. This has been done to keep the implementation consistent with conventions already in the language-models module.
Note: this PR was developed with assistance from Claude Code (Anthropic).
Tests
Tests covering configuration validation (missing required params, conflicting params, invalid field types, placeholder mismatches), and processor initialization.
Tests covering single-valued and multi-valued output fields of all supported types, multi-input-field prompts, prompt file loading, error handling (model exceptions, ambiguous/malformed JSON responses, unsupported model types), and skipNullOrMissingFieldValues behaviour. All the supported models have been tested.
Checklist
Please review the following and check all that apply:
mainbranch../gradlew check.