Skip to content
Draft
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 @@ -9,8 +9,12 @@ public EmbeddingProviderException(ErrorInstance errorInstance) {
}

public enum Code implements ErrorCode<EmbeddingProviderException> {
CLIENT_ERROR,
SERVER_ERROR;
EMBEDDING_REQUEST_ENCODING_ERROR,
EMBEDDING_RESPONSE_DECODING_ERROR,
EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED,
EMBEDDING_PROVIDER_CLIENT_ERROR,
EMBEDDING_PROVIDER_RATE_LIMITED,
EMBEDDING_PROVIDER_SERVER_ERROR;

private final ErrorTemplate<EmbeddingProviderException> template;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ public enum ErrorCodeV1 {
CONCURRENCY_FAILURE("Unable to complete transaction due to concurrent transactions"),

/** Embedding provider service error codes. */
EMBEDDING_REQUEST_ENCODING_ERROR("Unable to create embedding provider request message"),
EMBEDDING_RESPONSE_DECODING_ERROR("Unable to parse embedding provider response message"),
EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED(
"The Embedding Provider authentication keys not provided"),
EMBEDDING_PROVIDER_CLIENT_ERROR("The Embedding Provider returned a HTTP client error"),
EMBEDDING_PROVIDER_SERVER_ERROR("The Embedding Provider returned a HTTP server error"),
EMBEDDING_PROVIDER_RATE_LIMITED("The Embedding Provider rate limited the request"),
EMBEDDING_PROVIDER_TIMEOUT("The Embedding Provider timed out"),
EMBEDDING_PROVIDER_UNEXPECTED_RESPONSE("The Embedding Provider returned an unexpected response"),
EMBEDDING_PROVIDER_API_KEY_MISSING("The Embedding Provider API key is missing"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.io.CountingOutputStream;
import io.smallrye.mutiny.Uni;
import io.stargate.sgv2.jsonapi.api.request.EmbeddingCredentials;
import io.stargate.sgv2.jsonapi.exception.EmbeddingProviderException;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.service.embedding.configuration.EmbeddingProvidersConfig;
import io.stargate.sgv2.jsonapi.service.embedding.configuration.ServiceConfigStore;
Expand Down Expand Up @@ -74,19 +75,31 @@ public Uni<BatchedEmbeddingResponse> vectorize(

// TODO: move to V2 errors
if (embeddingCredentials.accessId().isEmpty() && embeddingCredentials.secretId().isEmpty()) {
throw ErrorCodeV1.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.toApiException(
"Both '%s' and '%s' are missing in the header for provider '%s'",
EMBEDDING_AUTHENTICATION_ACCESS_ID_HEADER_NAME,
EMBEDDING_AUTHENTICATION_SECRET_ID_HEADER_NAME,
modelProvider().apiName());
} else if (embeddingCredentials.accessId().isEmpty()) {
throw ErrorCodeV1.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.toApiException(
"'%s' is missing in the header for provider '%s'",
EMBEDDING_AUTHENTICATION_ACCESS_ID_HEADER_NAME, modelProvider().apiName());
} else if (embeddingCredentials.secretId().isEmpty()) {
throw ErrorCodeV1.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.toApiException(
"'%s' is missing in the header for provider '%s'",
EMBEDDING_AUTHENTICATION_SECRET_ID_HEADER_NAME, modelProvider().apiName());
throw EmbeddingProviderException.Code.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.get(
Map.of(
"provider",
modelProvider().apiName(),
"message",
"both '%s' and '%s' headers are missing"
.formatted(
EMBEDDING_AUTHENTICATION_ACCESS_ID_HEADER_NAME,
EMBEDDING_AUTHENTICATION_SECRET_ID_HEADER_NAME)));
}
if (embeddingCredentials.accessId().isEmpty()) {
throw EmbeddingProviderException.Code.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.get(
Map.of(
"provider",
modelProvider().apiName(),
"message",
"'%s' header is missing".formatted(EMBEDDING_AUTHENTICATION_ACCESS_ID_HEADER_NAME)));
}
if (embeddingCredentials.secretId().isEmpty()) {
throw EmbeddingProviderException.Code.EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED.get(
Map.of(
"provider",
modelProvider().apiName(),
"message",
"'%s' header is missing".formatted(EMBEDDING_AUTHENTICATION_SECRET_ID_HEADER_NAME)));
}

var awsCreds =
Expand All @@ -110,15 +123,23 @@ public Uni<BatchedEmbeddingResponse> vectorize(
bedrockClient
.invokeModel(
requestBuilder -> {
byte[] inputData;
try {
var inputData =
inputData =
OBJECT_WRITER.writeValueAsBytes(
new AwsBedrockEmbeddingRequest(texts.getFirst(), dimension));
bytesUsageTracker.requestBytes = inputData.length;
requestBuilder.body(SdkBytes.fromByteArray(inputData)).modelId(modelName());
} catch (JsonProcessingException e) {
throw ErrorCodeV1.EMBEDDING_REQUEST_ENCODING_ERROR.toApiException();
} catch (JacksonException e) { // should never happen
throw EmbeddingProviderException.Code.EMBEDDING_REQUEST_ENCODING_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"model",
modelName(),
"errorMessage",
e.toString()));
}
bytesUsageTracker.requestBytes = inputData.length;
requestBuilder.body(SdkBytes.fromByteArray(inputData)).modelId(modelName());
})
.thenApply(
rawResponse -> {
Expand Down Expand Up @@ -151,7 +172,14 @@ public Uni<BatchedEmbeddingResponse> vectorize(
batchId, List.of(bedrockResponse.embedding), modelUsage);

} catch (IOException e) {
throw ErrorCodeV1.EMBEDDING_RESPONSE_DECODING_ERROR.toApiException();
throw EmbeddingProviderException.Code.EMBEDDING_RESPONSE_DECODING_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"model",
modelName(),
"errorMessage",
e.toString()));
}
});

Expand All @@ -161,31 +189,47 @@ public Uni<BatchedEmbeddingResponse> vectorize(
.transform(throwable -> mapBedrockException((BedrockRuntimeException) throwable));
}

private Throwable mapBedrockException(BedrockRuntimeException bedrockException) {
private Exception mapBedrockException(BedrockRuntimeException bedrockException) {

if (bedrockException.statusCode() == Response.Status.REQUEST_TIMEOUT.getStatusCode()
|| bedrockException.statusCode() == Response.Status.GATEWAY_TIMEOUT.getStatusCode()) {

return ErrorCodeV1.EMBEDDING_PROVIDER_TIMEOUT.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), bedrockException.statusCode(), bedrockException.getMessage());
}

if (bedrockException.statusCode() == Response.Status.TOO_MANY_REQUESTS.getStatusCode()) {
return ErrorCodeV1.EMBEDDING_PROVIDER_RATE_LIMITED.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), bedrockException.statusCode(), bedrockException.getMessage());
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_RATE_LIMITED.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(bedrockException.statusCode()),
"errorMessage",
bedrockException.getMessage()));
}

if (bedrockException.statusCode() > 400 && bedrockException.statusCode() < 500) {
return ErrorCodeV1.EMBEDDING_PROVIDER_CLIENT_ERROR.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), bedrockException.statusCode(), bedrockException.getMessage());
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_CLIENT_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(bedrockException.statusCode()),
"errorMessage",
bedrockException.getMessage()));
}

if (bedrockException.statusCode() >= 500) {
return ErrorCodeV1.EMBEDDING_PROVIDER_SERVER_ERROR.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), bedrockException.statusCode(), bedrockException.getMessage());
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_SERVER_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(bedrockException.statusCode()),
"errorMessage",
bedrockException.getMessage()));
}

// All other errors, Should never happen as all errors are covered above
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import io.smallrye.mutiny.Uni;
import io.stargate.sgv2.jsonapi.api.request.EmbeddingCredentials;
import io.stargate.sgv2.jsonapi.exception.EmbeddingProviderException;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.exception.ServerException;
Expand Down Expand Up @@ -226,23 +227,38 @@ protected RuntimeException mapHTTPError(Response jakartaResponse, String errorMe

// Status code == 429
if (jakartaResponse.getStatus() == Response.Status.TOO_MANY_REQUESTS.getStatusCode()) {
return ErrorCodeV1.EMBEDDING_PROVIDER_RATE_LIMITED.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), jakartaResponse.getStatus(), errorMessage);
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_RATE_LIMITED.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(jakartaResponse.getStatus()),
"errorMessage",
errorMessage));
}

// Status code in 4XX other than 429
if (jakartaResponse.getStatusInfo().getFamily() == CLIENT_ERROR) {
return ErrorCodeV1.EMBEDDING_PROVIDER_CLIENT_ERROR.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), jakartaResponse.getStatus(), errorMessage);
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_CLIENT_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(jakartaResponse.getStatus()),
"errorMessage",
errorMessage));
}

// Status code in 5XX
if (jakartaResponse.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) {
return ErrorCodeV1.EMBEDDING_PROVIDER_SERVER_ERROR.toApiException(
"Provider: %s; HTTP Status: %s; Error Message: %s",
modelProvider().apiName(), jakartaResponse.getStatus(), errorMessage);
return EmbeddingProviderException.Code.EMBEDDING_PROVIDER_SERVER_ERROR.get(
Map.of(
"provider",
modelProvider().apiName(),
"httpStatus",
String.valueOf(jakartaResponse.getStatus()),
"errorMessage",
errorMessage));
}

// All other errors, Should never happen as all errors are covered above
Expand Down
38 changes: 32 additions & 6 deletions src/main/resources/errors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2105,13 +2105,39 @@ server-errors:

# EMBEDDING scope server errors
- scope: EMBEDDING_PROVIDER
code: CLIENT_ERROR
title: The Embedding Provider returned a HTTP client error
code: EMBEDDING_REQUEST_ENCODING_ERROR
title: Embedding request encoding failure
body: |-
Provider: ${provider}; HTTP Status: ${httpStatus}; Error Message: ${errorMessage}
Internal error: failed to encode embedding request (provider '${provider}', model '${model}').
Underlying problem: ${errorMessage}

- scope: EMBEDDING_PROVIDER
code: SERVER_ERROR
title: The Embedding Provider returned a HTTP client error
code: EMBEDDING_RESPONSE_DECODING_ERROR
title: Embedding request response decoding failure
body: |-
Provider: ${provider}; HTTP Status: ${httpStatus}; Error Message: ${errorMessage}
Internal error: failed to decode response to embedding request (provider '${provider}', model '${model}').
Underlying problem: ${errorMessage}

- scope: EMBEDDING_PROVIDER
code: EMBEDDING_PROVIDER_AUTHENTICATION_KEYS_NOT_PROVIDED
title: Authentication header(s) missing from embedding request
body: |-
Missing authentication header(s) from embedding request (provider '${provider}'): ${message}').

- scope: EMBEDDING_PROVIDER
code: EMBEDDING_PROVIDER_CLIENT_ERROR
title: Embedding provider returned a HTTP client error
body: |-
Provider '${provider}' returned HTTP ${httpStatus}; error message: ${errorMessage}

- scope: EMBEDDING_PROVIDER
code: EMBEDDING_PROVIDER_RATE_LIMITED
title: Embedding provider rate limited the request
body: |-
Provider '${provider}' rate limited the request with HTTP ${httpStatus}; error message: ${errorMessage}

- scope: EMBEDDING_PROVIDER
code: EMBEDDING_PROVIDER_SERVER_ERROR
title: Embedding provider returned a HTTP server error
body: |-
Provider '${provider}' returned HTTP ${httpStatus}; error message: ${errorMessage}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import io.quarkus.test.common.WithTestResource;
import io.quarkus.test.junit.QuarkusIntegrationTest;
import io.stargate.sgv2.jsonapi.exception.DocumentException;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.exception.EmbeddingProviderException;
import io.stargate.sgv2.jsonapi.service.operation.filters.table.codecs.JSONCodecRegistryTestData;
import io.stargate.sgv2.jsonapi.testresource.DseTestResource;
import io.stargate.sgv2.jsonapi.util.Base64Util;
Expand Down Expand Up @@ -1734,8 +1734,10 @@ void insertDifferentVectorizeDimensions() {
}
""")
.hasSingleApiError(
ErrorCodeV1.EMBEDDING_PROVIDER_CLIENT_ERROR.name(),
"The Embedding Provider returned a HTTP client error: Provider: openai; HTTP Status: 401; Error Message: \"Incorrect API key provided: test_emb");
EmbeddingProviderException.Code.EMBEDDING_PROVIDER_CLIENT_ERROR,
EmbeddingProviderException.class,
"Provider 'openai' returned HTTP 401",
"error message: \"Incorrect API key provided:");
}

@Order(2)
Expand Down Expand Up @@ -1782,8 +1784,10 @@ void insertDifferentVectorizeModels() {
}
""")
.hasSingleApiError(
ErrorCodeV1.EMBEDDING_PROVIDER_CLIENT_ERROR.name(),
"The Embedding Provider returned a HTTP client error: Provider: openai; HTTP Status: 401; Error Message: \"Incorrect API key provided: test_emb");
EmbeddingProviderException.Code.EMBEDDING_PROVIDER_CLIENT_ERROR,
EmbeddingProviderException.class,
"Provider 'openai' returned HTTP 401",
"error message: \"Incorrect API key provided");
}

@Order(3)
Expand Down Expand Up @@ -1829,10 +1833,11 @@ void insertDifferentVectorizeProviders() {
}
""")
.hasSingleApiError(
ErrorCodeV1.EMBEDDING_PROVIDER_CLIENT_ERROR,
EmbeddingProviderException.Code.EMBEDDING_PROVIDER_CLIENT_ERROR,
EmbeddingProviderException.class,
anyOf(
containsString("Provider: openai; HTTP Status: 401; Error Message: "),
containsString("Provider: jinaAI; HTTP Status: 401; Error Message: ")));
containsString("Provider 'openai' returned HTTP 401; error message:"),
containsString("Provider 'jinaAI' returned HTTP 401; error message:")));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,6 @@ public <T extends APIException> DataApiResponseValidator hasSingleApiError(
.body("errors[0].errorCode", is(errorCode.toString()));
}

/**
* @param errorCode Error code to check for
* @param errorClass Error class to check for
* @param messageSnippet Set of pieces of error message to check: ALL must match
*/
public <T extends APIException> DataApiResponseValidator hasSingleApiError(
ErrorCode<T> errorCode, Class<T> errorClass, String... messageSnippet) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import io.stargate.embedding.gateway.EmbeddingService;
import io.stargate.sgv2.jsonapi.TestConstants;
import io.stargate.sgv2.jsonapi.api.request.EmbeddingCredentials;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.exception.APIException;
import io.stargate.sgv2.jsonapi.exception.EmbeddingProviderException;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.embedding.configuration.EmbeddingProvidersConfig;
import io.stargate.sgv2.jsonapi.service.embedding.configuration.EmbeddingProvidersConfigImpl;
Expand Down Expand Up @@ -197,13 +198,12 @@ void handleError() {
EmbeddingGateway.EmbeddingResponse.newBuilder();
EmbeddingGateway.EmbeddingResponse.ErrorResponse.Builder errorResponseBuilder =
EmbeddingGateway.EmbeddingResponse.ErrorResponse.newBuilder();
JsonApiException apiException =
ErrorCodeV1.EMBEDDING_PROVIDER_RATE_LIMITED.toApiException(
"Error Code : %s response description : %s", 429, "Too Many Requests");
APIException apiException =
EmbeddingProviderException.Code.EMBEDDING_PROVIDER_RATE_LIMITED.get(
Map.of(
"provider", "TEST-MODEL", "httpStatus", "429", "errorMessage", "Slow Down Dude!"));

errorResponseBuilder
.setErrorCode(apiException.getErrorCode().name())
.setErrorMessage(apiException.getMessage());
errorResponseBuilder.setErrorCode(apiException.code).setErrorMessage(apiException.getMessage());
builder.setError(errorResponseBuilder.build());
when(embeddingService.embed(any())).thenReturn(Uni.createFrom().item(builder.build()));

Expand Down Expand Up @@ -239,7 +239,7 @@ void handleError() {
e -> {
JsonApiException exception = (JsonApiException) e;
assertThat(exception.getMessage()).isEqualTo(apiException.getMessage());
assertThat(exception.getErrorCode()).isEqualTo(apiException.getErrorCode());
assertThat(exception.getErrorCode().name()).isEqualTo(apiException.code);
});
}
}
Loading
Loading