Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public enum Code implements ErrorCode<RequestException> {

INVALID_CREATE_COLLECTION_FIELD,
INVALID_RERANK_OVERRIDE,
MISSING_RERANK_ON_TEXT,
MISSING_RERANK_QUERY_TEXT,

REQUEST_NOT_JSON,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ private PathMatchLocator passageLocator() {
// user has to provide a field to rerank on
finalRerankField = rerankOn;
} else {
throw new IllegalArgumentException("rerankOn() - rerankOn required and not specified");
throw RequestException.Code.MISSING_RERANK_ON_TEXT.get();
}

return PathMatchLocator.forPath(finalRerankField);
Expand Down
18 changes: 18 additions & 0 deletions src/main/resources/errors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,24 @@ request-errors:

Resend the command with a valid reranking service override, or omit the override to use the collection's default reranking configuration.

# unscoped because this touches both the sort and the options
- scope:
code: MISSING_RERANK_ON_TEXT
title: Rerank field is missing
body: |-
The findAndRerank command is missing the field to read from each document for the reranking step.

Reranking involves using a model to compare passages of text to a user query.

For each document to rerank, a certain field is read to extract the associated passage.

If using `$vectorize`, the name of the rerankOn field defaults to "$vectorize";
otherwise, there is no default and the field name must be supplied.

The field to read must be specified as `rerankOn` in the options clause, e.g. `{"rerankOn": "title"}`.

Resend the command including the name of the field on which reranking should be done.

# unscoped because this touches both the sort and the options
- scope:
code: MISSING_RERANK_QUERY_TEXT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@
import io.stargate.sgv2.jsonapi.config.constants.RerankingConstants;
import io.stargate.sgv2.jsonapi.exception.RequestException;
import io.stargate.sgv2.jsonapi.exception.SchemaException;
import io.stargate.sgv2.jsonapi.service.cqldriver.executor.VectorColumnDefinition;
import io.stargate.sgv2.jsonapi.service.cqldriver.executor.VectorConfig;
import io.stargate.sgv2.jsonapi.service.cqldriver.executor.VectorizeDefinition;
import io.stargate.sgv2.jsonapi.service.provider.ApiModelSupport;
import io.stargate.sgv2.jsonapi.service.reranking.configuration.RerankingProvidersConfig;
import io.stargate.sgv2.jsonapi.service.reranking.configuration.RerankingProvidersConfigImpl;
import io.stargate.sgv2.jsonapi.service.reranking.operation.RerankingProvider;
import io.stargate.sgv2.jsonapi.service.schema.EmbeddingSourceModel;
import io.stargate.sgv2.jsonapi.service.schema.SimilarityFunction;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionLexicalDefSchemaFactory;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionRerankDef;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionRerankDefSchemaFactory;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionSchemaObject;
import io.stargate.sgv2.jsonapi.service.schema.collections.IdConfig;
import io.stargate.sgv2.jsonapi.testresource.NoGlobalResourcesTestProfile;
import jakarta.inject.Inject;
import java.util.List;
Expand Down Expand Up @@ -242,15 +250,123 @@ void acceptsBoundaryValues() throws Exception {
.build();
}

@Test
void failsWhenMissingRerankOnAndNotVectorizeSort() throws Exception {
var commandContext = commandContext();
var command =
command(
"""
{
"findAndRerank": {
"sort": { "$hybrid": { "$vector": [0.1, 0.2, 0.3], "$lexical": "text" } },
"options": {
"rerankQuery": "text"
}
}
}
""");

assertMissingRerankOnText(
"error when no rerankOn and not vectorize sort", commandContext, command);
}

@Test
void failsWhenBlankRerankOnAndNotVectorizeSort() throws Exception {
var commandContext = commandContext();
var command =
command(
"""
{
"findAndRerank": {
"sort": { "$hybrid": { "$vector": [0.1, 0.2, 0.3], "$lexical": "text" } },
"options": {
"rerankOn": " ",
"rerankQuery": "text"
}
}
}
""");

assertMissingRerankOnText(
"error when blank rerankOn and not vectorize sort", commandContext, command);
}

@Test
void failsWhenMissingRerankOnWithLexicalSortOnVectorizeCollection() throws Exception {
var commandContext = commandContextWithVectorize();
var command =
command(
"""
{
"findAndRerank": {
"sort": { "$hybrid": { "$lexical": "text" } },
"options": {
"rerankQuery": "text"
}
}
}
""");

assertMissingRerankOnText(
"error when no rerankOn on vectorize collection with lexical sort",
commandContext,
command);
}

private void assertMissingRerankOnText(
String context,
CommandContext<CollectionSchemaObject> commandContext,
FindAndRerankCommand command) {
var ex =
org.junit.jupiter.api.Assertions.assertThrowsExactly(
RequestException.class,
() ->
new FindAndRerankOperationBuilder(commandContext)
.withCommand(command)
.withFindCommandResolver(findCommandResolver)
.build(),
context);

assertThat(ex.code)
.as("error code is " + RequestException.Code.MISSING_RERANK_ON_TEXT.name())
.isEqualTo(RequestException.Code.MISSING_RERANK_ON_TEXT.name());
}

private FindAndRerankCommand command(String json) throws Exception {
return objectMapper.readValue(json, FindAndRerankCommand.class);
}

private CommandContext<CollectionSchemaObject> commandContext() {
var commandContext =
testConstants.collectionContext(
CommandName.FIND_AND_RERANK,
testConstants.VECTOR_LEXICAL_RERANK_COLLECTION_SCHEMA_OBJECT);
return commandContext(testConstants.VECTOR_LEXICAL_RERANK_COLLECTION_SCHEMA_OBJECT);
}

private CommandContext<CollectionSchemaObject> commandContextWithVectorize() {
var collectionSchema =
new CollectionSchemaObject(
testConstants.COLLECTION_IDENTIFIER,
IdConfig.defaultIdConfig(),
VectorConfig.fromColumnDefinitions(
List.of(
new VectorColumnDefinition(
io.stargate.sgv2.jsonapi.config.constants.DocumentConstants.Fields
.VECTOR_EMBEDDING_TEXT_FIELD,
-1,
SimilarityFunction.COSINE,
EmbeddingSourceModel.OTHER,
new VectorizeDefinition("custom", "custom", null, null)))),
null,
CollectionLexicalDefSchemaFactory.FOR_TESTING_ENABLED.currentVersion(null),
CollectionRerankDefSchemaFactory.FOR_TESTING_ENABLED.currentVersion(
new CollectionRerankDef(
true,
new CollectionRerankDef.RerankServiceDef(
"nvidia", "nvidia/llama-3.2-nv-rerankqa-1b-v2", null, null))));
return commandContext(collectionSchema);
}

private CommandContext<CollectionSchemaObject> commandContext(
CollectionSchemaObject schemaObject) {
var commandContext = testConstants.collectionContext(CommandName.FIND_AND_RERANK, schemaObject);

var rerankingProvidersConfig = mock(RerankingProvidersConfig.class);
var modelConfig = mock(RerankingProvidersConfig.RerankingProviderConfig.ModelConfig.class);
Expand Down