data) {
+ this.data = data;
+ }
+
+ @SuppressFBWarnings("EI_EXPOSE_REP")
+ public EmbeddingUsage getUsage() {
+ return usage;
+ }
+
+ @SuppressFBWarnings("EI_EXPOSE_REP2")
+ public void setUsage(EmbeddingUsage usage) {
+ this.usage = usage;
+ }
+ }
+}
diff --git a/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/multimodalembedding/VoyageAIMultimodalEmbeddingGenerationService.java b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/multimodalembedding/VoyageAIMultimodalEmbeddingGenerationService.java
new file mode 100644
index 000000000..89304d37d
--- /dev/null
+++ b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/multimodalembedding/VoyageAIMultimodalEmbeddingGenerationService.java
@@ -0,0 +1,227 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai.multimodalembedding;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import com.microsoft.semantickernel.services.textembedding.TextEmbeddingGenerationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * VoyageAI multimodal embedding generation service.
+ * Generates embeddings for text, images, or interleaved text and images.
+ * Supports the voyage-multimodal-3 model.
+ *
+ * Constraints:
+ * - Maximum 1,000 inputs per request
+ * - Images: ≤16 million pixels, ≤20 MB
+ * - Total tokens per input: ≤32,000 (560 pixels = 1 token)
+ * - Aggregate tokens across inputs: ≤320,000
+ */
+public final class VoyageAIMultimodalEmbeddingGenerationService implements TextEmbeddingGenerationService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VoyageAIMultimodalEmbeddingGenerationService.class);
+
+ private final VoyageAIClient client;
+ private final String modelId;
+ private final String serviceId;
+
+ /**
+ * Creates a new instance of VoyageAI multimodal embedding generation service.
+ *
+ * @param client VoyageAI client
+ * @param modelId Model ID (e.g., "voyage-multimodal-3")
+ * @param serviceId Optional service ID
+ */
+ public VoyageAIMultimodalEmbeddingGenerationService(
+ VoyageAIClient client,
+ String modelId,
+ @Nullable String serviceId) {
+
+ if (client == null) {
+ throw new IllegalArgumentException("Client cannot be null");
+ }
+ if (modelId == null || modelId.trim().isEmpty()) {
+ throw new IllegalArgumentException("Model ID cannot be null or empty");
+ }
+
+ this.client = client;
+ this.modelId = modelId;
+ this.serviceId = serviceId != null ? serviceId : PromptExecutionSettings.DEFAULT_SERVICE_ID;
+ }
+
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String getModelId() {
+ return modelId;
+ }
+
+ /**
+ * Generates multimodal embeddings for text and/or images.
+ *
+ * @param inputs List of multimodal inputs
+ * @return A Mono containing a list of multimodal embeddings
+ */
+ public Mono> generateMultimodalEmbeddingsAsync(List inputs) {
+ if (inputs == null || inputs.isEmpty()) {
+ return Mono.just(Collections.emptyList());
+ }
+
+ LOGGER.debug("Generating multimodal embeddings for {} inputs using model {}", inputs.size(), modelId);
+
+ VoyageAIModels.MultimodalEmbeddingRequest request = new VoyageAIModels.MultimodalEmbeddingRequest();
+ request.setInputs(inputs);
+ request.setModel(modelId);
+
+ return client.sendRequestAsync("multimodalembeddings", request, VoyageAIModels.MultimodalEmbeddingResponse.class)
+ .map(response -> {
+ LOGGER.debug("Received {} multimodal embeddings from VoyageAI", response.getData().size());
+
+ List embeddings = response.getData().stream()
+ .sorted(Comparator.comparingInt(VoyageAIModels.EmbeddingDataItem::getIndex))
+ .map(item -> new Embedding(item.getEmbedding()))
+ .collect(Collectors.toList());
+
+ return embeddings;
+ });
+ }
+
+ /**
+ * Generates embeddings for the given text.
+ * For text-only input, converts to multimodal format.
+ *
+ * @param data The text to generate embeddings for
+ * @return A Mono that completes with the embedding
+ */
+ @Override
+ public Mono generateEmbeddingAsync(String data) {
+ return generateEmbeddingsAsync(Arrays.asList(data))
+ .flatMap(embeddings -> {
+ if (embeddings.isEmpty()) {
+ return Mono.empty();
+ }
+ return Mono.just(embeddings.get(0));
+ });
+ }
+
+ /**
+ * Generates embeddings for the given texts.
+ * Converts text-only inputs to multimodal format.
+ *
+ * @param data The texts to generate embeddings for
+ * @return A Mono that completes with the list of embeddings
+ */
+ @Override
+ public Mono> generateEmbeddingsAsync(List data) {
+ if (data == null || data.isEmpty()) {
+ return Mono.just(Collections.emptyList());
+ }
+
+ // Convert each text to multimodal input format
+ List inputs = new ArrayList<>();
+ for (String text : data) {
+ VoyageAIModels.MultimodalContentItem contentItem =
+ new VoyageAIModels.MultimodalContentItem("text", text);
+ VoyageAIModels.MultimodalInput input =
+ new VoyageAIModels.MultimodalInput(Arrays.asList(contentItem));
+ inputs.add(input);
+ }
+
+ if (inputs.isEmpty()) {
+ return Mono.just(Collections.emptyList());
+ }
+
+ LOGGER.debug("Generating multimodal embeddings for {} inputs using model {}", inputs.size(), modelId);
+
+ VoyageAIModels.MultimodalEmbeddingRequest request = new VoyageAIModels.MultimodalEmbeddingRequest();
+ request.setInputs(inputs);
+ request.setModel(modelId);
+
+ return client.sendRequestAsync("multimodalembeddings", request, VoyageAIModels.MultimodalEmbeddingResponse.class)
+ .map(response -> {
+ LOGGER.debug("Received {} multimodal embeddings from VoyageAI", response.getData().size());
+
+ List embeddings = response.getData().stream()
+ .sorted(Comparator.comparingInt(VoyageAIModels.EmbeddingDataItem::getIndex))
+ .map(item -> new Embedding(item.getEmbedding()))
+ .collect(Collectors.toList());
+
+ return embeddings;
+ });
+ }
+
+ /**
+ * Creates a builder for VoyageAI multimodal embedding generation service.
+ *
+ * @return A new builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link VoyageAIMultimodalEmbeddingGenerationService}.
+ */
+ public static class Builder {
+ private VoyageAIClient client;
+ private String modelId;
+ private String serviceId;
+
+ /**
+ * Sets the VoyageAI client.
+ *
+ * @param client VoyageAI client
+ * @return This builder
+ */
+ public Builder withClient(VoyageAIClient client) {
+ this.client = client;
+ return this;
+ }
+
+ /**
+ * Sets the model ID.
+ *
+ * @param modelId Model ID (e.g., "voyage-multimodal-3")
+ * @return This builder
+ */
+ public Builder withModelId(String modelId) {
+ this.modelId = modelId;
+ return this;
+ }
+
+ /**
+ * Sets the service ID.
+ *
+ * @param serviceId Service ID
+ * @return This builder
+ */
+ public Builder withServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ return this;
+ }
+
+ /**
+ * Builds the VoyageAI multimodal embedding generation service.
+ *
+ * @return A new instance of VoyageAIMultimodalEmbeddingGenerationService
+ */
+ public VoyageAIMultimodalEmbeddingGenerationService build() {
+ return new VoyageAIMultimodalEmbeddingGenerationService(client, modelId, serviceId);
+ }
+ }
+}
diff --git a/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/reranking/VoyageAITextRerankingService.java b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/reranking/VoyageAITextRerankingService.java
new file mode 100644
index 000000000..cda093d7e
--- /dev/null
+++ b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/reranking/VoyageAITextRerankingService.java
@@ -0,0 +1,182 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai.reranking;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
+import com.microsoft.semantickernel.services.reranking.RerankResult;
+import com.microsoft.semantickernel.services.reranking.TextRerankingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * VoyageAI implementation of {@link TextRerankingService}.
+ * Supports models like rerank-2, rerank-2-lite.
+ */
+public final class VoyageAITextRerankingService implements TextRerankingService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VoyageAITextRerankingService.class);
+
+ private final VoyageAIClient client;
+ private final String modelId;
+ private final String serviceId;
+ private final Integer topK;
+
+ /**
+ * Creates a new instance of VoyageAI text reranking service.
+ *
+ * @param client VoyageAI client
+ * @param modelId Model ID (e.g., "rerank-2")
+ * @param serviceId Optional service ID
+ * @param topK Optional top K results to return
+ */
+ public VoyageAITextRerankingService(
+ VoyageAIClient client,
+ String modelId,
+ @Nullable String serviceId,
+ @Nullable Integer topK) {
+
+ if (client == null) {
+ throw new IllegalArgumentException("Client cannot be null");
+ }
+ if (modelId == null || modelId.trim().isEmpty()) {
+ throw new IllegalArgumentException("Model ID cannot be null or empty");
+ }
+
+ this.client = client;
+ this.modelId = modelId;
+ this.serviceId = serviceId != null ? serviceId : PromptExecutionSettings.DEFAULT_SERVICE_ID;
+ this.topK = topK;
+ }
+
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String getModelId() {
+ return modelId;
+ }
+
+ /**
+ * Reranks documents based on their relevance to the query.
+ *
+ * @param query The query to rank documents against
+ * @param documents The list of documents to rerank
+ * @return A Mono containing a list of {@link RerankResult} sorted by relevance score in descending order
+ */
+ @Override
+ public Mono> rerankAsync(String query, List documents) {
+ if (query == null || query.trim().isEmpty()) {
+ throw new IllegalArgumentException("Query cannot be null or empty");
+ }
+ if (documents == null || documents.isEmpty()) {
+ return Mono.just(Collections.emptyList());
+ }
+
+ LOGGER.debug("Reranking {} documents using model {}", documents.size(), modelId);
+
+ VoyageAIModels.RerankRequest request = new VoyageAIModels.RerankRequest();
+ request.setQuery(query);
+ request.setDocuments(documents);
+ request.setModel(modelId);
+ request.setTopK(topK);
+ request.setTruncation(true);
+
+ return client.sendRequestAsync("rerank", request, VoyageAIModels.RerankResponse.class)
+ .map(response -> {
+ LOGGER.debug("Received {} reranked results from VoyageAI", response.getData().size());
+
+ List results = response.getData().stream()
+ .sorted(Comparator.comparingDouble(VoyageAIModels.RerankDataItem::getRelevanceScore).reversed())
+ .map(item -> new RerankResult(
+ item.getIndex(),
+ documents.get(item.getIndex()),
+ item.getRelevanceScore()
+ ))
+ .collect(Collectors.toList());
+
+ return results;
+ });
+ }
+
+ /**
+ * Creates a builder for VoyageAI text reranking service.
+ *
+ * @return A new builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link VoyageAITextRerankingService}.
+ */
+ public static class Builder {
+ private VoyageAIClient client;
+ private String modelId;
+ private String serviceId;
+ private Integer topK;
+
+ /**
+ * Sets the VoyageAI client.
+ *
+ * @param client VoyageAI client
+ * @return This builder
+ */
+ public Builder withClient(VoyageAIClient client) {
+ this.client = client;
+ return this;
+ }
+
+ /**
+ * Sets the model ID.
+ *
+ * @param modelId Model ID (e.g., "rerank-2")
+ * @return This builder
+ */
+ public Builder withModelId(String modelId) {
+ this.modelId = modelId;
+ return this;
+ }
+
+ /**
+ * Sets the service ID.
+ *
+ * @param serviceId Service ID
+ * @return This builder
+ */
+ public Builder withServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ return this;
+ }
+
+ /**
+ * Sets the top K results to return.
+ *
+ * @param topK Top K results
+ * @return This builder
+ */
+ public Builder withTopK(Integer topK) {
+ this.topK = topK;
+ return this;
+ }
+
+ /**
+ * Builds the VoyageAI text reranking service.
+ *
+ * @return A new instance of VoyageAITextRerankingService
+ */
+ public VoyageAITextRerankingService build() {
+ return new VoyageAITextRerankingService(client, modelId, serviceId, topK);
+ }
+ }
+}
diff --git a/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/textembedding/VoyageAITextEmbeddingGenerationService.java b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/textembedding/VoyageAITextEmbeddingGenerationService.java
new file mode 100644
index 000000000..4345aca58
--- /dev/null
+++ b/aiservices/voyageai/src/main/java/com/microsoft/semantickernel/aiservices/voyageai/textembedding/VoyageAITextEmbeddingGenerationService.java
@@ -0,0 +1,174 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai.textembedding;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import com.microsoft.semantickernel.services.textembedding.TextEmbeddingGenerationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * VoyageAI implementation of {@link TextEmbeddingGenerationService}.
+ * Supports models like voyage-3-large, voyage-3.5, voyage-code-3, voyage-finance-2, voyage-law-2.
+ */
+public final class VoyageAITextEmbeddingGenerationService implements TextEmbeddingGenerationService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VoyageAITextEmbeddingGenerationService.class);
+
+ private final VoyageAIClient client;
+ private final String modelId;
+ private final String serviceId;
+
+ /**
+ * Creates a new instance of VoyageAI text embedding generation service.
+ *
+ * @param client VoyageAI client
+ * @param modelId Model ID (e.g., "voyage-3-large")
+ * @param serviceId Optional service ID
+ */
+ public VoyageAITextEmbeddingGenerationService(
+ VoyageAIClient client,
+ String modelId,
+ @Nullable String serviceId) {
+
+ if (client == null) {
+ throw new IllegalArgumentException("Client cannot be null");
+ }
+ if (modelId == null || modelId.trim().isEmpty()) {
+ throw new IllegalArgumentException("Model ID cannot be null or empty");
+ }
+
+ this.client = client;
+ this.modelId = modelId;
+ this.serviceId = serviceId != null ? serviceId : PromptExecutionSettings.DEFAULT_SERVICE_ID;
+ }
+
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String getModelId() {
+ return modelId;
+ }
+
+ /**
+ * Generates embeddings for the given text.
+ *
+ * @param data The text to generate embeddings for
+ * @return A Mono that completes with the embedding
+ */
+ @Override
+ public Mono generateEmbeddingAsync(String data) {
+ return generateEmbeddingsAsync(Arrays.asList(data))
+ .flatMap(embeddings -> {
+ if (embeddings.isEmpty()) {
+ return Mono.empty();
+ }
+ return Mono.just(embeddings.get(0));
+ });
+ }
+
+ /**
+ * Generates embeddings for the given texts.
+ *
+ * @param data The texts to generate embeddings for
+ * @return A Mono that completes with the list of embeddings
+ */
+ @Override
+ public Mono> generateEmbeddingsAsync(List data) {
+ if (data == null || data.isEmpty()) {
+ return Mono.just(Collections.emptyList());
+ }
+
+ LOGGER.debug("Generating embeddings for {} texts using model {}", data.size(), modelId);
+
+ VoyageAIModels.EmbeddingRequest request = new VoyageAIModels.EmbeddingRequest();
+ request.setInput(data);
+ request.setModel(modelId);
+ request.setTruncation(true);
+
+ return client.sendRequestAsync("embeddings", request, VoyageAIModels.EmbeddingResponse.class)
+ .map(response -> {
+ LOGGER.debug("Received {} embeddings from VoyageAI", response.getData().size());
+
+ List embeddings = response.getData().stream()
+ .sorted(Comparator.comparingInt(VoyageAIModels.EmbeddingDataItem::getIndex))
+ .map(item -> new Embedding(item.getEmbedding()))
+ .collect(Collectors.toList());
+
+ return embeddings;
+ });
+ }
+
+ /**
+ * Creates a builder for VoyageAI text embedding generation service.
+ *
+ * @return A new builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link VoyageAITextEmbeddingGenerationService}.
+ */
+ public static class Builder {
+ private VoyageAIClient client;
+ private String modelId;
+ private String serviceId;
+
+ /**
+ * Sets the VoyageAI client.
+ *
+ * @param client VoyageAI client
+ * @return This builder
+ */
+ public Builder withClient(VoyageAIClient client) {
+ this.client = client;
+ return this;
+ }
+
+ /**
+ * Sets the model ID.
+ *
+ * @param modelId Model ID (e.g., "voyage-3-large")
+ * @return This builder
+ */
+ public Builder withModelId(String modelId) {
+ this.modelId = modelId;
+ return this;
+ }
+
+ /**
+ * Sets the service ID.
+ *
+ * @param serviceId Service ID
+ * @return This builder
+ */
+ public Builder withServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ return this;
+ }
+
+ /**
+ * Builds the VoyageAI text embedding generation service.
+ *
+ * @return A new instance of VoyageAITextEmbeddingGenerationService
+ */
+ public VoyageAITextEmbeddingGenerationService build() {
+ return new VoyageAITextEmbeddingGenerationService(client, modelId, serviceId);
+ }
+ }
+}
diff --git a/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIContextualizedEmbeddingGenerationServiceTest.java b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIContextualizedEmbeddingGenerationServiceTest.java
new file mode 100644
index 000000000..8b4d22609
--- /dev/null
+++ b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIContextualizedEmbeddingGenerationServiceTest.java
@@ -0,0 +1,142 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai;
+
+import com.microsoft.semantickernel.aiservices.voyageai.contextualizedembedding.VoyageAIContextualizedEmbeddingGenerationService;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+public class VoyageAIContextualizedEmbeddingGenerationServiceTest {
+
+ @Test
+ public void testGenerateContextualizedEmbeddings() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.ContextualizedEmbeddingResponse mockResponse =
+ new VoyageAIModels.ContextualizedEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item1 = new VoyageAIModels.EmbeddingDataItem();
+ item1.setEmbedding(new float[]{0.1f, 0.2f});
+ item1.setIndex(0);
+
+ VoyageAIModels.EmbeddingDataItem item2 = new VoyageAIModels.EmbeddingDataItem();
+ item2.setEmbedding(new float[]{0.3f, 0.4f});
+ item2.setIndex(0);
+
+ VoyageAIModels.ContextualizedEmbeddingDataList dataList1 =
+ new VoyageAIModels.ContextualizedEmbeddingDataList();
+ dataList1.setData(Arrays.asList(item1));
+
+ VoyageAIModels.ContextualizedEmbeddingDataList dataList2 =
+ new VoyageAIModels.ContextualizedEmbeddingDataList();
+ dataList2.setData(Arrays.asList(item2));
+
+ mockResponse.setData(Arrays.asList(dataList1, dataList2));
+
+ when(mockClient.sendRequestAsync(
+ eq("contextualizedembeddings"),
+ any(),
+ eq(VoyageAIModels.ContextualizedEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIContextualizedEmbeddingGenerationService service =
+ new VoyageAIContextualizedEmbeddingGenerationService(mockClient, "voyage-3", null);
+
+ List> inputs = Arrays.asList(
+ Arrays.asList("chunk1"),
+ Arrays.asList("chunk2")
+ );
+
+ List results = service.generateContextualizedEmbeddingsAsync(inputs).block();
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+ List expected1 = Arrays.asList(0.1f, 0.2f);
+ List expected2 = Arrays.asList(0.3f, 0.4f);
+ assertEquals(expected1, results.get(0).getVector());
+ assertEquals(expected2, results.get(1).getVector());
+ }
+
+ @Test
+ public void testGenerateEmbedding() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.ContextualizedEmbeddingResponse mockResponse =
+ new VoyageAIModels.ContextualizedEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item = new VoyageAIModels.EmbeddingDataItem();
+ item.setEmbedding(new float[]{0.1f, 0.2f, 0.3f});
+ item.setIndex(0);
+
+ VoyageAIModels.ContextualizedEmbeddingDataList dataList =
+ new VoyageAIModels.ContextualizedEmbeddingDataList();
+ dataList.setData(Arrays.asList(item));
+
+ mockResponse.setData(Arrays.asList(dataList));
+
+ when(mockClient.sendRequestAsync(
+ eq("contextualizedembeddings"),
+ any(),
+ eq(VoyageAIModels.ContextualizedEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIContextualizedEmbeddingGenerationService service =
+ new VoyageAIContextualizedEmbeddingGenerationService(mockClient, "voyage-3", null);
+
+ Embedding result2 = service.generateEmbeddingAsync("test text").block();
+
+ assertNotNull(result2);
+ List expected = Arrays.asList(0.1f, 0.2f, 0.3f);
+ assertEquals(expected, result2.getVector());
+ }
+
+ @Test
+ public void testServiceIdAndModelId() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIContextualizedEmbeddingGenerationService service =
+ new VoyageAIContextualizedEmbeddingGenerationService(mockClient, "voyage-3", "test-service");
+
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-3", service.getModelId());
+ }
+
+ @Test
+ public void testBuilderPattern() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIContextualizedEmbeddingGenerationService service =
+ VoyageAIContextualizedEmbeddingGenerationService.builder()
+ .withClient(mockClient)
+ .withModelId("voyage-3")
+ .withServiceId("test-service")
+ .build();
+
+ assertNotNull(service);
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-3", service.getModelId());
+ }
+
+ @Test
+ public void testNullClientThrowsException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAIContextualizedEmbeddingGenerationService(null, "voyage-3", null));
+ }
+
+ @Test
+ public void testNullModelIdThrowsException() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAIContextualizedEmbeddingGenerationService(mockClient, null, null));
+ }
+}
diff --git a/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIIntegrationTest.java b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIIntegrationTest.java
new file mode 100644
index 000000000..b9631d644
--- /dev/null
+++ b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIIntegrationTest.java
@@ -0,0 +1,216 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai;
+
+import com.microsoft.semantickernel.aiservices.voyageai.contextualizedembedding.VoyageAIContextualizedEmbeddingGenerationService;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.multimodalembedding.VoyageAIMultimodalEmbeddingGenerationService;
+import com.microsoft.semantickernel.aiservices.voyageai.reranking.VoyageAITextRerankingService;
+import com.microsoft.semantickernel.aiservices.voyageai.textembedding.VoyageAITextEmbeddingGenerationService;
+import com.microsoft.semantickernel.services.reranking.RerankResult;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Integration tests for VoyageAI services.
+ * Requires VOYAGE_API_KEY environment variable to be set.
+ */
+public class VoyageAIIntegrationTest {
+
+ private static final String API_KEY_ENV_VAR = "VOYAGE_API_KEY";
+ private static final String DEFAULT_EMBEDDING_MODEL = "voyage-3-large";
+ private static final String DEFAULT_CONTEXTUALIZED_MODEL = "voyage-context-3";
+ private static final String DEFAULT_MULTIMODAL_MODEL = "voyage-multimodal-3";
+ private static final String DEFAULT_RERANK_MODEL = "rerank-2";
+
+ private String apiKey;
+
+ @BeforeEach
+ public void setUp() {
+ apiKey = System.getenv(API_KEY_ENV_VAR);
+ Assumptions.assumeTrue(
+ apiKey != null && !apiKey.isEmpty(),
+ "Skipping integration test: " + API_KEY_ENV_VAR + " environment variable not set"
+ );
+ }
+
+ @Test
+ public void testTextEmbeddingGeneration() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAITextEmbeddingGenerationService service =
+ VoyageAITextEmbeddingGenerationService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_EMBEDDING_MODEL)
+ .build();
+
+ Embedding embedding = service.generateEmbeddingAsync("Hello, world!").block();
+
+ assertNotNull(embedding, "Embedding should not be null");
+ assertNotNull(embedding.getVector(), "Embedding vector should not be null");
+ assertTrue(embedding.getVector().size() > 0, "Embedding vector should not be empty");
+
+ System.out.println("Generated embedding with dimension: " + embedding.getVector().size());
+ }
+
+ @Test
+ public void testMultipleTextEmbeddings() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAITextEmbeddingGenerationService service =
+ VoyageAITextEmbeddingGenerationService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_EMBEDDING_MODEL)
+ .build();
+
+ List texts = Arrays.asList(
+ "Hello, world!",
+ "Semantic Kernel is awesome",
+ "VoyageAI provides great embeddings"
+ );
+
+ List embeddings = service.generateEmbeddingsAsync(texts).block();
+
+ assertNotNull(embeddings, "Embeddings should not be null");
+ assertEquals(3, embeddings.size(), "Should generate 3 embeddings");
+
+ for (Embedding embedding : embeddings) {
+ assertNotNull(embedding.getVector(), "Each embedding vector should not be null");
+ assertTrue(embedding.getVector().size() > 0, "Each embedding vector should not be empty");
+ }
+
+ System.out.println("Generated " + embeddings.size() + " embeddings");
+ }
+
+ @Test
+ public void testTextReranking() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAITextRerankingService service =
+ VoyageAITextRerankingService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_RERANK_MODEL)
+ .build();
+
+ String query = "What is the capital of France?";
+ List documents = Arrays.asList(
+ "Paris is the capital and most populous city of France.",
+ "Berlin is the capital of Germany.",
+ "The Eiffel Tower is located in Paris.",
+ "London is the capital of the United Kingdom."
+ );
+
+ List results = service.rerankAsync(query, documents).block();
+
+ assertNotNull(results, "Rerank results should not be null");
+ assertEquals(4, results.size(), "Should have 4 reranked results");
+
+ // The first result should have the highest relevance score
+ assertTrue(results.get(0).getRelevanceScore() >= results.get(1).getRelevanceScore(),
+ "Results should be sorted by relevance score descending");
+
+ System.out.println("Reranking results:");
+ for (int i = 0; i < results.size(); i++) {
+ RerankResult result = results.get(i);
+ System.out.printf("%d. [Index: %d, Score: %.4f] %s%n",
+ i + 1, result.getIndex(), result.getRelevanceScore(), result.getText());
+ }
+
+ // The most relevant document should be about Paris being the capital
+ assertEquals(0, results.get(0).getIndex(),
+ "Most relevant document should be the one about Paris being the capital");
+ }
+
+ @Test
+ public void testRerankingWithTopK() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAITextRerankingService service =
+ VoyageAITextRerankingService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_RERANK_MODEL)
+ .withTopK(2)
+ .build();
+
+ String query = "Machine learning";
+ List documents = Arrays.asList(
+ "Machine learning is a subset of artificial intelligence.",
+ "Cooking is an art form.",
+ "Deep learning uses neural networks.",
+ "The weather is nice today."
+ );
+
+ List results = service.rerankAsync(query, documents).block();
+
+ assertNotNull(results, "Rerank results should not be null");
+ // VoyageAI might return all results sorted, or just top K
+ assertTrue(results.size() >= 2, "Should have at least 2 results");
+
+ System.out.println("Top K reranking results:");
+ for (RerankResult result : results) {
+ System.out.printf("[Index: %d, Score: %.4f] %s%n",
+ result.getIndex(), result.getRelevanceScore(), result.getText());
+ }
+ }
+
+ @Test
+ public void testContextualizedEmbeddings() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAIContextualizedEmbeddingGenerationService service =
+ VoyageAIContextualizedEmbeddingGenerationService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_CONTEXTUALIZED_MODEL)
+ .build();
+
+ // Create document chunks with context
+ List> inputs = Arrays.asList(
+ Arrays.asList("Introduction to semantic kernel", "Semantic kernel is a framework"),
+ Arrays.asList("VoyageAI provides embeddings", "VoyageAI is an AI company")
+ );
+
+ List embeddings = service.generateContextualizedEmbeddingsAsync(inputs).block();
+
+ assertNotNull(embeddings, "Contextualized embeddings should not be null");
+ // Each input document has 2 chunks, so we expect 4 embeddings total (2 documents * 2 chunks each)
+ assertEquals(4, embeddings.size(), "Should generate 4 embeddings (2 per document)");
+
+ for (Embedding embedding : embeddings) {
+ assertNotNull(embedding.getVector(), "Each embedding vector should not be null");
+ assertTrue(embedding.getVector().size() > 0, "Each embedding vector should not be empty");
+ }
+
+ System.out.println("Generated " + embeddings.size() + " contextualized embeddings");
+ }
+
+ @Test
+ public void testMultimodalEmbeddings() {
+ VoyageAIClient client = new VoyageAIClient(apiKey);
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ VoyageAIMultimodalEmbeddingGenerationService.builder()
+ .withClient(client)
+ .withModelId(DEFAULT_MULTIMODAL_MODEL)
+ .build();
+
+ // Test using generateEmbeddingsAsync which handles text conversion
+ List texts = Arrays.asList(
+ "This is a text description",
+ "Another text example"
+ );
+
+ List embeddings = service.generateEmbeddingsAsync(texts).block();
+
+ assertNotNull(embeddings, "Multimodal embeddings should not be null");
+ assertEquals(2, embeddings.size(), "Should generate 2 embeddings");
+
+ for (Embedding embedding : embeddings) {
+ assertNotNull(embedding.getVector(), "Each embedding vector should not be null");
+ assertTrue(embedding.getVector().size() > 0, "Each embedding vector should not be empty");
+ }
+
+ System.out.println("Generated " + embeddings.size() + " multimodal embeddings");
+ System.out.println("Embedding dimension: " + embeddings.get(0).getVector().size());
+ }
+}
diff --git a/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIMultimodalEmbeddingGenerationServiceTest.java b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIMultimodalEmbeddingGenerationServiceTest.java
new file mode 100644
index 000000000..e97d19703
--- /dev/null
+++ b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAIMultimodalEmbeddingGenerationServiceTest.java
@@ -0,0 +1,230 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.aiservices.voyageai.multimodalembedding.VoyageAIMultimodalEmbeddingGenerationService;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+public class VoyageAIMultimodalEmbeddingGenerationServiceTest {
+
+ @Test
+ public void testGenerateMultimodalEmbeddings() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.MultimodalEmbeddingResponse mockResponse =
+ new VoyageAIModels.MultimodalEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item1 = new VoyageAIModels.EmbeddingDataItem();
+ item1.setEmbedding(new float[]{0.1f, 0.2f});
+ item1.setIndex(0);
+
+ VoyageAIModels.EmbeddingDataItem item2 = new VoyageAIModels.EmbeddingDataItem();
+ item2.setEmbedding(new float[]{0.3f, 0.4f});
+ item2.setIndex(1);
+
+ mockResponse.setData(Arrays.asList(item1, item2));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(20);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("multimodalembeddings"),
+ any(),
+ eq(VoyageAIModels.MultimodalEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, "voyage-multimodal-3", null);
+
+ // Create properly structured multimodal inputs
+ VoyageAIModels.MultimodalContentItem content1 = new VoyageAIModels.MultimodalContentItem("text", "text1");
+ VoyageAIModels.MultimodalContentItem content2 = new VoyageAIModels.MultimodalContentItem("text", "text2");
+ VoyageAIModels.MultimodalInput input1 = new VoyageAIModels.MultimodalInput(Arrays.asList(content1));
+ VoyageAIModels.MultimodalInput input2 = new VoyageAIModels.MultimodalInput(Arrays.asList(content2));
+ List inputs = Arrays.asList(input1, input2);
+
+ List results = service.generateMultimodalEmbeddingsAsync(inputs).block();
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+ List expected1 = Arrays.asList(0.1f, 0.2f);
+ List expected2 = Arrays.asList(0.3f, 0.4f);
+ assertEquals(expected1, results.get(0).getVector());
+ assertEquals(expected2, results.get(1).getVector());
+ }
+
+ @Test
+ public void testGenerateEmbedding() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.MultimodalEmbeddingResponse mockResponse =
+ new VoyageAIModels.MultimodalEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item = new VoyageAIModels.EmbeddingDataItem();
+ item.setEmbedding(new float[]{0.1f, 0.2f, 0.3f});
+ item.setIndex(0);
+
+ mockResponse.setData(Arrays.asList(item));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(10);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("multimodalembeddings"),
+ any(),
+ eq(VoyageAIModels.MultimodalEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, "voyage-multimodal-3", null);
+
+ Embedding result = service.generateEmbeddingAsync("test text").block();
+
+ assertNotNull(result);
+ List expected = Arrays.asList(0.1f, 0.2f, 0.3f);
+ assertEquals(expected, result.getVector());
+ }
+
+ @Test
+ public void testGenerateEmbeddingsFromTextList() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.MultimodalEmbeddingResponse mockResponse =
+ new VoyageAIModels.MultimodalEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item1 = new VoyageAIModels.EmbeddingDataItem();
+ item1.setEmbedding(new float[]{0.1f, 0.2f});
+ item1.setIndex(0);
+
+ VoyageAIModels.EmbeddingDataItem item2 = new VoyageAIModels.EmbeddingDataItem();
+ item2.setEmbedding(new float[]{0.3f, 0.4f});
+ item2.setIndex(1);
+
+ mockResponse.setData(Arrays.asList(item1, item2));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(20);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("multimodalembeddings"),
+ any(),
+ eq(VoyageAIModels.MultimodalEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, "voyage-multimodal-3", null);
+
+ List results = service.generateEmbeddingsAsync(
+ Arrays.asList("text1", "text2")).block();
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+ List expected1 = Arrays.asList(0.1f, 0.2f);
+ List expected2 = Arrays.asList(0.3f, 0.4f);
+ assertEquals(expected1, results.get(0).getVector());
+ assertEquals(expected2, results.get(1).getVector());
+ }
+
+ @Test
+ public void testServiceIdAndModelId() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, "voyage-multimodal-3", "test-service");
+
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-multimodal-3", service.getModelId());
+ }
+
+ @Test
+ public void testBuilderPattern() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ VoyageAIMultimodalEmbeddingGenerationService.builder()
+ .withClient(mockClient)
+ .withModelId("voyage-multimodal-3")
+ .withServiceId("test-service")
+ .build();
+
+ assertNotNull(service);
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-multimodal-3", service.getModelId());
+ }
+
+ @Test
+ public void testNullClientThrowsException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAIMultimodalEmbeddingGenerationService(null, "voyage-multimodal-3", null));
+ }
+
+ @Test
+ public void testNullModelIdThrowsException() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, null, null));
+ }
+
+ @Test
+ public void testVoyageMultimodal35ModelId() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ VoyageAIMultimodalEmbeddingGenerationService.builder()
+ .withClient(mockClient)
+ .withModelId("voyage-multimodal-3.5")
+ .withServiceId("multimodal-3.5-service")
+ .build();
+
+ assertNotNull(service);
+ assertEquals("voyage-multimodal-3.5", service.getModelId());
+ assertEquals("multimodal-3.5-service", service.getServiceId());
+ }
+
+ @Test
+ public void testVoyageMultimodal35GenerateEmbeddings() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.MultimodalEmbeddingResponse mockResponse =
+ new VoyageAIModels.MultimodalEmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item = new VoyageAIModels.EmbeddingDataItem();
+ item.setEmbedding(new float[]{0.5f, 0.6f, 0.7f, 0.8f});
+ item.setIndex(0);
+
+ mockResponse.setData(Arrays.asList(item));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(15);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("multimodalembeddings"),
+ any(),
+ eq(VoyageAIModels.MultimodalEmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAIMultimodalEmbeddingGenerationService service =
+ new VoyageAIMultimodalEmbeddingGenerationService(mockClient, "voyage-multimodal-3.5", null);
+
+ Embedding result = service.generateEmbeddingAsync("test with voyage-multimodal-3.5").block();
+
+ assertNotNull(result);
+ List expected = Arrays.asList(0.5f, 0.6f, 0.7f, 0.8f);
+ assertEquals(expected, result.getVector());
+ }
+}
diff --git a/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextEmbeddingGenerationServiceTest.java b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextEmbeddingGenerationServiceTest.java
new file mode 100644
index 000000000..962a340b2
--- /dev/null
+++ b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextEmbeddingGenerationServiceTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.aiservices.voyageai.textembedding.VoyageAITextEmbeddingGenerationService;
+import com.microsoft.semantickernel.services.textembedding.Embedding;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+public class VoyageAITextEmbeddingGenerationServiceTest {
+
+ @Test
+ public void testGenerateEmbedding() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.EmbeddingResponse mockResponse = new VoyageAIModels.EmbeddingResponse();
+ VoyageAIModels.EmbeddingDataItem item = new VoyageAIModels.EmbeddingDataItem();
+ item.setEmbedding(new float[]{0.1f, 0.2f, 0.3f});
+ item.setIndex(0);
+ mockResponse.setData(Arrays.asList(item));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(10);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("embeddings"),
+ any(),
+ eq(VoyageAIModels.EmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAITextEmbeddingGenerationService service =
+ new VoyageAITextEmbeddingGenerationService(mockClient, "voyage-3-large", null);
+
+ Embedding result = service.generateEmbeddingAsync("test text").block();
+
+ assertNotNull(result);
+ List expected = Arrays.asList(0.1f, 0.2f, 0.3f);
+ assertEquals(expected, result.getVector());
+ }
+
+ @Test
+ public void testGenerateMultipleEmbeddings() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.EmbeddingResponse mockResponse = new VoyageAIModels.EmbeddingResponse();
+
+ VoyageAIModels.EmbeddingDataItem item1 = new VoyageAIModels.EmbeddingDataItem();
+ item1.setEmbedding(new float[]{0.1f, 0.2f});
+ item1.setIndex(0);
+
+ VoyageAIModels.EmbeddingDataItem item2 = new VoyageAIModels.EmbeddingDataItem();
+ item2.setEmbedding(new float[]{0.3f, 0.4f});
+ item2.setIndex(1);
+
+ mockResponse.setData(Arrays.asList(item1, item2));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(20);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("embeddings"),
+ any(),
+ eq(VoyageAIModels.EmbeddingResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAITextEmbeddingGenerationService service =
+ new VoyageAITextEmbeddingGenerationService(mockClient, "voyage-3-large", null);
+
+ List results = service.generateEmbeddingsAsync(
+ Arrays.asList("text1", "text2")).block();
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+ List expected1 = Arrays.asList(0.1f, 0.2f);
+ List expected2 = Arrays.asList(0.3f, 0.4f);
+ assertEquals(expected1, results.get(0).getVector());
+ assertEquals(expected2, results.get(1).getVector());
+ }
+
+ @Test
+ public void testServiceIdAndModelId() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAITextEmbeddingGenerationService service =
+ new VoyageAITextEmbeddingGenerationService(mockClient, "voyage-3-large", "test-service");
+
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-3-large", service.getModelId());
+ }
+
+ @Test
+ public void testBuilderPattern() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAITextEmbeddingGenerationService service =
+ VoyageAITextEmbeddingGenerationService.builder()
+ .withClient(mockClient)
+ .withModelId("voyage-3-large")
+ .withServiceId("test-service")
+ .build();
+
+ assertNotNull(service);
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("voyage-3-large", service.getModelId());
+ }
+
+ @Test
+ public void testNullClientThrowsException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAITextEmbeddingGenerationService(null, "voyage-3-large", null));
+ }
+
+ @Test
+ public void testNullModelIdThrowsException() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAITextEmbeddingGenerationService(mockClient, null, null));
+ }
+}
diff --git a/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextRerankingServiceTest.java b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextRerankingServiceTest.java
new file mode 100644
index 000000000..9feddd271
--- /dev/null
+++ b/aiservices/voyageai/src/test/java/com/microsoft/semantickernel/aiservices/voyageai/VoyageAITextRerankingServiceTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.aiservices.voyageai;
+
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIClient;
+import com.microsoft.semantickernel.aiservices.voyageai.core.VoyageAIModels;
+import com.microsoft.semantickernel.aiservices.voyageai.reranking.VoyageAITextRerankingService;
+import com.microsoft.semantickernel.services.reranking.RerankResult;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+public class VoyageAITextRerankingServiceTest {
+
+ @Test
+ public void testRerank() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAIModels.RerankResponse mockResponse = new VoyageAIModels.RerankResponse();
+
+ VoyageAIModels.RerankDataItem item1 = new VoyageAIModels.RerankDataItem();
+ item1.setIndex(1);
+ item1.setRelevanceScore(0.9);
+
+ VoyageAIModels.RerankDataItem item2 = new VoyageAIModels.RerankDataItem();
+ item2.setIndex(0);
+ item2.setRelevanceScore(0.5);
+
+ mockResponse.setData(Arrays.asList(item1, item2));
+
+ VoyageAIModels.EmbeddingUsage usage = new VoyageAIModels.EmbeddingUsage();
+ usage.setTotalTokens(20);
+ mockResponse.setUsage(usage);
+
+ when(mockClient.sendRequestAsync(
+ eq("rerank"),
+ any(),
+ eq(VoyageAIModels.RerankResponse.class)))
+ .thenReturn(Mono.just(mockResponse));
+
+ VoyageAITextRerankingService service =
+ new VoyageAITextRerankingService(mockClient, "rerank-2", null, null);
+
+ List documents = Arrays.asList("Document A", "Document B");
+ List results = service.rerankAsync("test query", documents).block();
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+
+ // Results should be sorted by relevance score descending
+ assertEquals(1, results.get(0).getIndex());
+ assertEquals("Document B", results.get(0).getText());
+ assertEquals(0.9, results.get(0).getRelevanceScore(), 0.001);
+
+ assertEquals(0, results.get(1).getIndex());
+ assertEquals("Document A", results.get(1).getText());
+ assertEquals(0.5, results.get(1).getRelevanceScore(), 0.001);
+ }
+
+ @Test
+ public void testServiceIdAndModelId() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAITextRerankingService service =
+ new VoyageAITextRerankingService(mockClient, "rerank-2", "test-service", null);
+
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("rerank-2", service.getModelId());
+ }
+
+ @Test
+ public void testBuilderPattern() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+
+ VoyageAITextRerankingService service =
+ VoyageAITextRerankingService.builder()
+ .withClient(mockClient)
+ .withModelId("rerank-2")
+ .withServiceId("test-service")
+ .withTopK(5)
+ .build();
+
+ assertNotNull(service);
+ assertEquals("test-service", service.getServiceId());
+ assertEquals("rerank-2", service.getModelId());
+ }
+
+ @Test
+ public void testNullClientThrowsException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAITextRerankingService(null, "rerank-2", null, null));
+ }
+
+ @Test
+ public void testNullModelIdThrowsException() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+ assertThrows(IllegalArgumentException.class, () ->
+ new VoyageAITextRerankingService(mockClient, null, null, null));
+ }
+
+ @Test
+ public void testNullQueryThrowsException() {
+ VoyageAIClient mockClient = Mockito.mock(VoyageAIClient.class);
+ VoyageAITextRerankingService service =
+ new VoyageAITextRerankingService(mockClient, "rerank-2", null, null);
+
+ assertThrows(IllegalArgumentException.class, () ->
+ service.rerankAsync(null, Arrays.asList("doc")).block());
+ }
+}
diff --git a/api-test/integration-tests/pom.xml b/api-test/integration-tests/pom.xml
index 862cc5184..9b5c3883d 100644
--- a/api-test/integration-tests/pom.xml
+++ b/api-test/integration-tests/pom.xml
@@ -6,7 +6,7 @@
com.microsoft.semantic-kernel
api-test
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -90,18 +90,18 @@
com.mysql
mysql-connector-j
- 9.0.0
+ 9.6.0
test
org.postgresql
postgresql
- 42.7.3
+ 42.7.10
org.xerial
sqlite-jdbc
- 3.46.1.0
+ 3.53.0.0
@@ -122,7 +122,7 @@
com.redis
testcontainers-redis
- 2.2.2
+ 2.2.4
test
@@ -147,7 +147,12 @@
org.hsqldb
hsqldb
- 2.7.3
+ 2.7.4
+ test
+
+
+ com.microsoft.semantic-kernel
+ semantickernel-api-data
test
@@ -157,7 +162,7 @@
org.testcontainers
testcontainers-bom
- 1.18.3
+ 2.0.4
pom
import
diff --git a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ImportingMultiplePluginsTest.java b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ImportingMultiplePluginsTest.java
index a555937ed..e9e5c6d4a 100644
--- a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ImportingMultiplePluginsTest.java
+++ b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ImportingMultiplePluginsTest.java
@@ -4,7 +4,6 @@
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.plugin.KernelPlugin;
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
-import org.junit.Ignore;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
diff --git a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/Hotel.java b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/Hotel.java
index 416e06b66..46083425a 100644
--- a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/Hotel.java
+++ b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/Hotel.java
@@ -18,11 +18,11 @@ public class Hotel {
@VectorStoreRecordData(isFilterable = true)
private final String name;
- @VectorStoreRecordData
+ @VectorStoreRecordData(isFilterable = true)
private final int code;
@JsonProperty("summary")
- @VectorStoreRecordData()
+ @VectorStoreRecordData(isFilterable = true)
private final String description;
@JsonProperty("summaryEmbedding1")
diff --git a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisHashSetVectorStoreRecordCollectionTest.java b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisHashSetVectorStoreRecordCollectionTest.java
index c5b6a186f..6bfebb8d7 100644
--- a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisHashSetVectorStoreRecordCollectionTest.java
+++ b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisHashSetVectorStoreRecordCollectionTest.java
@@ -53,56 +53,56 @@ public enum RecordCollectionOptions {
@BeforeAll
static void setup() {
optionsMap.put(RecordCollectionOptions.DEFAULT, RedisHashSetVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(Hotel.class)
- .build());
+ .withRecordClass(Hotel.class)
+ .build());
List fields = new ArrayList<>();
fields.add(VectorStoreRecordKeyField.builder()
- .withName("id")
- .withFieldType(String.class)
- .build());
+ .withName("id")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("name")
- .withFieldType(String.class)
- .build());
+ .withName("name")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("code")
- .withFieldType(Integer.class)
- .build());
+ .withName("code")
+ .withFieldType(Integer.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("description")
- .withStorageName("summary")
- .withFieldType(String.class)
- .build());
+ .withName("description")
+ .withStorageName("summary")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("euclidean")
- .withStorageName("summaryEmbedding1")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("euclidean")
+ .withStorageName("summaryEmbedding1")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("cosineDistance")
- .withStorageName("summaryEmbedding2")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("cosineDistance")
+ .withStorageName("summaryEmbedding2")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("dotProduct")
- .withStorageName("summaryEmbedding3")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("dotProduct")
+ .withStorageName("summaryEmbedding3")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("rating")
- .withFieldType(Double.class)
- .isFilterable(true)
- .build());
+ .withName("rating")
+ .withFieldType(Double.class)
+ .isFilterable(true)
+ .build());
VectorStoreRecordDefinition recordDefinition = VectorStoreRecordDefinition.fromFields(fields);
optionsMap.put(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, RedisHashSetVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(Hotel.class)
- .withRecordDefinition(recordDefinition)
- .build());
+ .withRecordClass(Hotel.class)
+ .withRecordDefinition(recordDefinition)
+ .build());
// Search configuration
List hotels = getHotels();
@@ -127,20 +127,20 @@ static void setup() {
private static RedisHashSetVectorStoreRecordCollection createCollection(@Nonnull RedisHashSetVectorStoreRecordCollectionOptions options, @Nonnull String collectionName) {
return new RedisHashSetVectorStoreRecordCollection<>(new JedisPooled(redisContainer.getRedisURI()), collectionName, RedisHashSetVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(options.getRecordClass())
- .withVectorStoreRecordMapper(options.getVectorStoreRecordMapper())
- .withRecordDefinition(options.getRecordDefinition())
- .withPrefixCollectionName(options.isPrefixCollectionName())
- .build());
+ .withRecordClass(options.getRecordClass())
+ .withVectorStoreRecordMapper(options.getVectorStoreRecordMapper())
+ .withRecordDefinition(options.getRecordDefinition())
+ .withPrefixCollectionName(options.isPrefixCollectionName())
+ .build());
}
private static List getHotels() {
return Arrays.asList(
- new Hotel("id_1", "Hotel 1", 1, "Hotel 1 description", Arrays.asList(0.5f, 3.2f, 7.1f, -4.0f, 2.8f, 10.0f, -1.3f, 5.5f),null, null, 4.0),
- new Hotel("id_2", "Hotel 2", 2, "Hotel 2 description", Arrays.asList(-2.0f, 8.1f, 0.9f, 5.4f, -3.3f, 2.2f, 9.9f, -4.5f),null, null, 4.0),
- new Hotel("id_3", "Hotel 3", 3, "Hotel 3 description", Arrays.asList(4.5f, -6.2f, 3.1f, 7.7f, -0.8f, 1.1f, -2.2f, 8.3f),null, null, 5.0),
- new Hotel("id_4", "Hotel 4", 4, "Hotel 4 description", Arrays.asList(7.0f, 1.2f, -5.3f, 2.5f, 6.6f, -7.8f, 3.9f, -0.1f),null, null, 4.0),
- new Hotel("id_5", "Hotel 5", 5, "Hotel 5 description", Arrays.asList(-3.5f, 4.4f, -1.2f, 9.9f, 5.7f, -6.1f, 7.8f, -2.0f),null, null, 4.0)
+ new Hotel("id_1", "Hotel 1", 1, "Hotel 1 description", Arrays.asList(0.5f, 3.2f, 7.1f, -4.0f, 2.8f, 10.0f, -1.3f, 5.5f),null, null, 4.0),
+ new Hotel("id_2", "Hotel 2", 2, "Hotel 2 description", Arrays.asList(-2.0f, 8.1f, 0.9f, 5.4f, -3.3f, 2.2f, 9.9f, -4.5f),null, null, 4.0),
+ new Hotel("id_3", "Hotel 3", 3, "Hotel 3 description", Arrays.asList(4.5f, -6.2f, 3.1f, 7.7f, -0.8f, 1.1f, -2.2f, 8.3f),null, null, 5.0),
+ new Hotel("id_4", "Hotel 4", 4, "Hotel 4 description", Arrays.asList(7.0f, 1.2f, -5.3f, 2.5f, 6.6f, -7.8f, 3.9f, -0.1f),null, null, 4.0),
+ new Hotel("id_5", "Hotel 5", 5, "Hotel 5 description", Arrays.asList(-3.5f, 4.4f, -1.2f, 9.9f, 5.7f, -6.1f, 7.8f, -2.0f),null, null, 4.0)
);
}
@@ -362,12 +362,12 @@ public void getBatchAsyncWithNoVectors(RecordCollectionOptions options) {
private static Stream provideSearchParameters() {
return Stream.of(
- Arguments.of(RecordCollectionOptions.DEFAULT, "euclidean"),
- Arguments.of(RecordCollectionOptions.DEFAULT, "cosineDistance"),
- Arguments.of(RecordCollectionOptions.DEFAULT, "dotProduct"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "euclidean"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "cosineDistance"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "dotProduct")
+ Arguments.of(RecordCollectionOptions.DEFAULT, "euclidean"),
+ Arguments.of(RecordCollectionOptions.DEFAULT, "cosineDistance"),
+ Arguments.of(RecordCollectionOptions.DEFAULT, "dotProduct"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "euclidean"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "cosineDistance"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "dotProduct")
);
}
@@ -383,8 +383,8 @@ public void search(RecordCollectionOptions options, String embeddingName) {
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .build();
+ .withVectorFieldName(embeddingName)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
@@ -407,9 +407,9 @@ public void searchWithVectors(RecordCollectionOptions options, String embeddingN
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .withIncludeVectors(true)
- .build();
+ .withVectorFieldName(embeddingName)
+ .withIncludeVectors(true)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
@@ -430,10 +430,10 @@ public void searchWithOffSet(RecordCollectionOptions options, String embeddingNa
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .withSkip(1)
- .withTop(4)
- .build();
+ .withVectorFieldName(embeddingName)
+ .withSkip(1)
+ .withTop(4)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
@@ -453,16 +453,16 @@ public void searchWithFilterEqualToFilter(RecordCollectionOptions recordCollecti
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions options = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .withTop(3)
- .withVectorSearchFilter(
- VectorSearchFilter.builder()
- .equalTo("rating", 4.0).build())
- .build();
+ .withVectorFieldName(embeddingName)
+ .withTop(3)
+ .withVectorSearchFilter(
+ VectorSearchFilter.builder()
+ .equalTo("rating", 4.0).build())
+ .build();
// Embeddings similar to the third hotel, but as the filter is set to 4.0, the third hotel should not be returned
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS,
- options).block().getResults();
+ options).block().getResults();
assertNotNull(results);
assertEquals(3, results.size());
// The first hotel should be the most similar
diff --git a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisJsonVectorStoreRecordCollectionTest.java b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisJsonVectorStoreRecordCollectionTest.java
index 1b4c30e82..1190e4377 100644
--- a/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisJsonVectorStoreRecordCollectionTest.java
+++ b/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/data/redis/RedisJsonVectorStoreRecordCollectionTest.java
@@ -53,56 +53,56 @@ public enum RecordCollectionOptions {
@BeforeAll
static void setup() {
optionsMap.put(RecordCollectionOptions.DEFAULT, RedisJsonVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(Hotel.class)
- .build());
+ .withRecordClass(Hotel.class)
+ .build());
List fields = new ArrayList<>();
fields.add(VectorStoreRecordKeyField.builder()
- .withName("id")
- .withFieldType(String.class)
- .build());
+ .withName("id")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("name")
- .withFieldType(String.class)
- .build());
+ .withName("name")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("code")
- .withFieldType(Integer.class)
- .build());
+ .withName("code")
+ .withFieldType(Integer.class)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("description")
- .withStorageName("summary")
- .withFieldType(String.class)
- .build());
+ .withName("description")
+ .withStorageName("summary")
+ .withFieldType(String.class)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("euclidean")
- .withStorageName("summaryEmbedding1")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("euclidean")
+ .withStorageName("summaryEmbedding1")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("cosineDistance")
- .withStorageName("summaryEmbedding2")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("cosineDistance")
+ .withStorageName("summaryEmbedding2")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordVectorField.builder()
- .withName("dotProduct")
- .withStorageName("summaryEmbedding3")
- .withFieldType(List.class)
- .withDimensions(8)
- .build());
+ .withName("dotProduct")
+ .withStorageName("summaryEmbedding3")
+ .withFieldType(List.class)
+ .withDimensions(8)
+ .build());
fields.add(VectorStoreRecordDataField.builder()
- .withName("rating")
- .withFieldType(Double.class)
- .isFilterable(true)
- .build());
+ .withName("rating")
+ .withFieldType(Double.class)
+ .isFilterable(true)
+ .build());
VectorStoreRecordDefinition recordDefinition = VectorStoreRecordDefinition.fromFields(fields);
optionsMap.put(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, RedisJsonVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(Hotel.class)
- .withRecordDefinition(recordDefinition)
- .build());
+ .withRecordClass(Hotel.class)
+ .withRecordDefinition(recordDefinition)
+ .build());
// Search configuration
List hotels = getHotels();
@@ -127,20 +127,20 @@ static void setup() {
private static RedisJsonVectorStoreRecordCollection createCollection(@Nonnull RedisJsonVectorStoreRecordCollectionOptions options, @Nonnull String collectionName) {
return new RedisJsonVectorStoreRecordCollection<>(new JedisPooled(redisContainer.getRedisURI()), collectionName, RedisJsonVectorStoreRecordCollectionOptions.builder()
- .withRecordClass(options.getRecordClass())
- .withVectorStoreRecordMapper(options.getVectorStoreRecordMapper())
- .withRecordDefinition(options.getRecordDefinition())
- .withPrefixCollectionName(options.isPrefixCollectionName())
- .build());
+ .withRecordClass(options.getRecordClass())
+ .withVectorStoreRecordMapper(options.getVectorStoreRecordMapper())
+ .withRecordDefinition(options.getRecordDefinition())
+ .withPrefixCollectionName(options.isPrefixCollectionName())
+ .build());
}
private static List getHotels() {
return Arrays.asList(
- new Hotel("id_1", "Hotel 1", 1, "Hotel 1 description", Arrays.asList(0.5f, 3.2f, 7.1f, -4.0f, 2.8f, 10.0f, -1.3f, 5.5f),null, null, 4.0),
- new Hotel("id_2", "Hotel 2", 2, "Hotel 2 description", Arrays.asList(-2.0f, 8.1f, 0.9f, 5.4f, -3.3f, 2.2f, 9.9f, -4.5f),null, null, 4.0),
- new Hotel("id_3", "Hotel 3", 3, "Hotel 3 description", Arrays.asList(4.5f, -6.2f, 3.1f, 7.7f, -0.8f, 1.1f, -2.2f, 8.3f),null, null, 5.0),
- new Hotel("id_4", "Hotel 4", 4, "Hotel 4 description", Arrays.asList(7.0f, 1.2f, -5.3f, 2.5f, 6.6f, -7.8f, 3.9f, -0.1f),null, null, 4.0),
- new Hotel("id_5", "Hotel 5", 5, "Hotel 5 description", Arrays.asList(-3.5f, 4.4f, -1.2f, 9.9f, 5.7f, -6.1f, 7.8f, -2.0f),null, null, 4.0)
+ new Hotel("id_1", "Hotel 1", 1, "Hotel 1 description", Arrays.asList(0.5f, 3.2f, 7.1f, -4.0f, 2.8f, 10.0f, -1.3f, 5.5f),null, null, 4.0),
+ new Hotel("id_2", "Hotel 2", 2, "Hotel 2 description", Arrays.asList(-2.0f, 8.1f, 0.9f, 5.4f, -3.3f, 2.2f, 9.9f, -4.5f),null, null, 4.0),
+ new Hotel("id_3", "Hotel 3", 3, "Hotel 3 description", Arrays.asList(4.5f, -6.2f, 3.1f, 7.7f, -0.8f, 1.1f, -2.2f, 8.3f),null, null, 5.0),
+ new Hotel("id_4", "Hotel 4", 4, "Hotel 4 description", Arrays.asList(7.0f, 1.2f, -5.3f, 2.5f, 6.6f, -7.8f, 3.9f, -0.1f),null, null, 4.0),
+ new Hotel("id_5", "Hotel 5", 5, "Hotel 5 description", Arrays.asList(-3.5f, 4.4f, -1.2f, 9.9f, 5.7f, -6.1f, 7.8f, -2.0f),null, null, 4.0)
);
}
@@ -362,12 +362,12 @@ public void getBatchAsyncWithNoVectors(RecordCollectionOptions options) {
private static Stream provideSearchParameters() {
return Stream.of(
- Arguments.of(RecordCollectionOptions.DEFAULT, "euclidean"),
- Arguments.of(RecordCollectionOptions.DEFAULT, "cosineDistance"),
- Arguments.of(RecordCollectionOptions.DEFAULT, "dotProduct"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "euclidean"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "cosineDistance"),
- Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "dotProduct")
+ Arguments.of(RecordCollectionOptions.DEFAULT, "euclidean"),
+ Arguments.of(RecordCollectionOptions.DEFAULT, "cosineDistance"),
+ Arguments.of(RecordCollectionOptions.DEFAULT, "dotProduct"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "euclidean"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "cosineDistance"),
+ Arguments.of(RecordCollectionOptions.WITH_CUSTOM_DEFINITION, "dotProduct")
);
}
@@ -383,8 +383,8 @@ public void search(RecordCollectionOptions options, String embeddingName) {
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .build();
+ .withVectorFieldName(embeddingName)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
@@ -407,9 +407,9 @@ public void searchWithVectors(RecordCollectionOptions options, String embeddingN
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .withIncludeVectors(true)
- .build();
+ .withVectorFieldName(embeddingName)
+ .withIncludeVectors(true)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
@@ -430,10 +430,10 @@ public void searchWithOffSet(RecordCollectionOptions options, String embeddingNa
recordCollection.upsertBatchAsync(hotels, null).block();
VectorSearchOptions searchOptions = VectorSearchOptions.builder()
- .withVectorFieldName(embeddingName)
- .withSkip(1)
- .withTop(4)
- .build();
+ .withVectorFieldName(embeddingName)
+ .withSkip(1)
+ .withTop(4)
+ .build();
// Embeddings similar to the third hotel
List> results = recordCollection.searchAsync(SEARCH_EMBEDDINGS, searchOptions).block().getResults();
diff --git a/api-test/pom.xml b/api-test/pom.xml
index 587dfe5b1..7b023c30a 100644
--- a/api-test/pom.xml
+++ b/api-test/pom.xml
@@ -6,7 +6,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/data/semantickernel-data-azureaisearch/pom.xml b/data/semantickernel-data-azureaisearch/pom.xml
index e9db7daae..714c99484 100644
--- a/data/semantickernel-data-azureaisearch/pom.xml
+++ b/data/semantickernel-data-azureaisearch/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -40,6 +40,23 @@
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ com.microsoft.semantic-kernel
+ semantickernel-api-builders
+ test
+
\ No newline at end of file
diff --git a/data/semantickernel-data-azureaisearch/src/main/java/com/microsoft/semantickernel/data/azureaisearch/AzureAISearchVectorStoreCollectionSearchMapping.java b/data/semantickernel-data-azureaisearch/src/main/java/com/microsoft/semantickernel/data/azureaisearch/AzureAISearchVectorStoreCollectionSearchMapping.java
index c31af301d..5d61f9817 100644
--- a/data/semantickernel-data-azureaisearch/src/main/java/com/microsoft/semantickernel/data/azureaisearch/AzureAISearchVectorStoreCollectionSearchMapping.java
+++ b/data/semantickernel-data-azureaisearch/src/main/java/com/microsoft/semantickernel/data/azureaisearch/AzureAISearchVectorStoreCollectionSearchMapping.java
@@ -57,11 +57,11 @@ public String getFilter(VectorSearchFilter vectorSearchFilter,
@Override
public String getEqualToFilter(EqualToFilterClause filterClause) {
- String fieldName = filterClause.getFieldName();
+ String fieldName = validateFieldName(filterClause.getFieldName());
Object value = filterClause.getValue();
if (value instanceof String) {
- return String.format("%s eq '%s'", fieldName, value);
+ return String.format("%s eq '%s'", fieldName, escapeSingleQuotes((String) value));
} else if (value instanceof Boolean) {
return String.format("%s eq %s", fieldName,
value.toString().toLowerCase());
@@ -86,7 +86,18 @@ public String getEqualToFilter(EqualToFilterClause filterClause) {
@Override
public String getAnyTagEqualToFilter(AnyTagEqualToFilterClause filterClause) {
- return String.format("%s/any(t: t eq '%s')", filterClause.getFieldName(),
- filterClause.getValue());
+ return String.format("%s/any(t: t eq '%s')", validateFieldName(filterClause.getFieldName()),
+ escapeSingleQuotes(filterClause.getValue().toString()));
+ }
+
+ private String validateFieldName(String fieldName) {
+ if (fieldName.matches("[a-zA-Z_][a-zA-Z0-9_]*")) {
+ return fieldName;
+ }
+ throw new SKException("Invalid field name: " + fieldName);
+ }
+
+ private String escapeSingleQuotes(String value) {
+ return value.replaceAll("'", "''");
}
}
diff --git a/data/semantickernel-data-hsqldb/pom.xml b/data/semantickernel-data-hsqldb/pom.xml
index 1cd331795..bb06dfe4a 100644
--- a/data/semantickernel-data-hsqldb/pom.xml
+++ b/data/semantickernel-data-hsqldb/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
diff --git a/data/semantickernel-data-hsqldb/src/main/java/com/microsoft/semantickernel/data/jdbc/hsqldb/HSQLDBVectorStoreQueryProvider.java b/data/semantickernel-data-hsqldb/src/main/java/com/microsoft/semantickernel/data/jdbc/hsqldb/HSQLDBVectorStoreQueryProvider.java
index 484313168..e105a20db 100644
--- a/data/semantickernel-data-hsqldb/src/main/java/com/microsoft/semantickernel/data/jdbc/hsqldb/HSQLDBVectorStoreQueryProvider.java
+++ b/data/semantickernel-data-hsqldb/src/main/java/com/microsoft/semantickernel/data/jdbc/hsqldb/HSQLDBVectorStoreQueryProvider.java
@@ -12,7 +12,6 @@
import com.microsoft.semantickernel.data.vectorstorage.options.UpsertRecordOptions;
import com.microsoft.semantickernel.exceptions.SKException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -32,7 +31,6 @@ public class HSQLDBVectorStoreQueryProvider extends JDBCVectorStoreQueryProvider
private final ObjectMapper objectMapper;
- @SuppressFBWarnings("EI_EXPOSE_REP2")
private HSQLDBVectorStoreQueryProvider(
DataSource dataSource,
String collectionsTable,
diff --git a/data/semantickernel-data-jdbc/pom.xml b/data/semantickernel-data-jdbc/pom.xml
index 077d4da6c..cf8069ec0 100644
--- a/data/semantickernel-data-jdbc/pom.xml
+++ b/data/semantickernel-data-jdbc/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -66,17 +66,17 @@
org.postgresql
postgresql
- 42.7.7
+ 42.7.12
org.xerial
sqlite-jdbc
- 3.47.0.0
+ 3.53.0.0
com.oracle.database.jdbc
ojdbc11
- 23.7.0.25.01
+ 23.26.1.0.0
\ No newline at end of file
diff --git a/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreQueryProvider.java b/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreQueryProvider.java
index 2fcc2d5b7..3441ccfed 100644
--- a/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreQueryProvider.java
+++ b/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreQueryProvider.java
@@ -29,8 +29,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
@@ -59,7 +57,6 @@ public class JDBCVectorStoreQueryProvider
private final Object dbCreationLock = new Object();
- @SuppressFBWarnings("EI_EXPOSE_REP2") // DataSource is not exposed
protected JDBCVectorStoreQueryProvider(
@Nonnull DataSource dataSource,
@Nonnull String collectionsTable,
@@ -102,8 +99,9 @@ protected JDBCVectorStoreQueryProvider(
* @param supportedDataTypes the supported data types
* @param supportedVectorTypes the supported vector types
*/
+ @SuppressFBWarnings("EI_EXPOSE_REP2")
public JDBCVectorStoreQueryProvider(
- @SuppressFBWarnings("EI_EXPOSE_REP2") @Nonnull DataSource dataSource,
+ @Nonnull DataSource dataSource,
@Nonnull String collectionsTable,
@Nonnull String prefixForCollectionTables,
@Nonnull Map, String> supportedKeyTypes,
diff --git a/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreRecordCollection.java b/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreRecordCollection.java
index 1d6b3e09b..fc421c2b0 100644
--- a/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreRecordCollection.java
+++ b/data/semantickernel-data-jdbc/src/main/java/com/microsoft/semantickernel/data/jdbc/JDBCVectorStoreRecordCollection.java
@@ -46,7 +46,6 @@ public class JDBCVectorStoreRecordCollection
* @param collectionName the name of the collection
* @param options the options
*/
- @SuppressFBWarnings("EI_EXPOSE_REP2") // DataSource is not exposed
public JDBCVectorStoreRecordCollection(
@Nonnull DataSource dataSource,
@Nonnull String collectionName,
diff --git a/data/semantickernel-data-mysql/pom.xml b/data/semantickernel-data-mysql/pom.xml
index 3d6d40e6b..2ad3ec2de 100644
--- a/data/semantickernel-data-mysql/pom.xml
+++ b/data/semantickernel-data-mysql/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
diff --git a/data/semantickernel-data-mysql/src/main/java/com/microsoft/semantickernel/data/jdbc/mysql/MySQLVectorStoreQueryProvider.java b/data/semantickernel-data-mysql/src/main/java/com/microsoft/semantickernel/data/jdbc/mysql/MySQLVectorStoreQueryProvider.java
index feb6dc4f3..1ff0f7068 100644
--- a/data/semantickernel-data-mysql/src/main/java/com/microsoft/semantickernel/data/jdbc/mysql/MySQLVectorStoreQueryProvider.java
+++ b/data/semantickernel-data-mysql/src/main/java/com/microsoft/semantickernel/data/jdbc/mysql/MySQLVectorStoreQueryProvider.java
@@ -13,14 +13,13 @@
import com.microsoft.semantickernel.data.vectorstorage.options.UpsertRecordOptions;
import com.microsoft.semantickernel.exceptions.SKException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import javax.annotation.Nonnull;
-import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.sql.DataSource;
/**
* The MySQL vector store query provider.
@@ -32,7 +31,6 @@ public class MySQLVectorStoreQueryProvider extends
private final ObjectMapper objectMapper;
- @SuppressFBWarnings("EI_EXPOSE_REP2")
private MySQLVectorStoreQueryProvider(
@Nonnull DataSource dataSource,
@Nonnull String collectionsTable,
diff --git a/data/semantickernel-data-oracle/pom.xml b/data/semantickernel-data-oracle/pom.xml
index 9971679be..42ec5b2cd 100644
--- a/data/semantickernel-data-oracle/pom.xml
+++ b/data/semantickernel-data-oracle/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -13,7 +13,7 @@
Provides a Oracle connector for the Semantic Kernel
- 1.20.4
+ 1.21.4
@@ -61,12 +61,12 @@
com.oracle.database.jdbc
ojdbc11
- 23.7.0.25.01
+ 23.26.1.0.0
com.oracle.database.jdbc
ojdbc-provider-jackson-oson
- 1.0.4
+ 1.0.6
org.junit.jupiter
@@ -119,6 +119,13 @@
true
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ ${skipTests.oracle}
+
+
\ No newline at end of file
diff --git a/data/semantickernel-data-oracle/src/test/java/com/microsoft/semantickernel/data/jdbc/oracle/Hotel.java b/data/semantickernel-data-oracle/src/test/java/com/microsoft/semantickernel/data/jdbc/oracle/Hotel.java
index 0f93ff7f5..ef6ac824f 100644
--- a/data/semantickernel-data-oracle/src/test/java/com/microsoft/semantickernel/data/jdbc/oracle/Hotel.java
+++ b/data/semantickernel-data-oracle/src/test/java/com/microsoft/semantickernel/data/jdbc/oracle/Hotel.java
@@ -8,12 +8,8 @@
import com.microsoft.semantickernel.data.vectorstorage.annotations.VectorStoreRecordVector;
import com.microsoft.semantickernel.data.vectorstorage.definition.DistanceFunction;
import com.microsoft.semantickernel.data.vectorstorage.definition.IndexKind;
-
import java.util.List;
-import static com.fasterxml.jackson.annotation.JsonCreator.Mode.DELEGATING;
-import static com.fasterxml.jackson.annotation.JsonCreator.Mode.PROPERTIES;
-
public class Hotel {
@VectorStoreRecordKey
private final String id;
@@ -52,13 +48,8 @@ public class Hotel {
@VectorStoreRecordData
private double rating;
- @JsonCreator(mode = DELEGATING)
- public Hotel() {
- this(null, null, 0, 0d, null, null, null, null, null, null, 0.0);
- }
-
- @JsonCreator(mode = PROPERTIES)
- protected Hotel(
+ @JsonCreator
+ public Hotel(
@JsonProperty("id") String id,
@JsonProperty("name") String name,
@JsonProperty("code") int code,
diff --git a/data/semantickernel-data-postgres/pom.xml b/data/semantickernel-data-postgres/pom.xml
index 8f22241dc..cfc838f72 100644
--- a/data/semantickernel-data-postgres/pom.xml
+++ b/data/semantickernel-data-postgres/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -51,7 +51,7 @@
org.postgresql
postgresql
- 42.7.7
+ 42.7.12
\ No newline at end of file
diff --git a/data/semantickernel-data-postgres/src/main/java/com/microsoft/semantickernel/data/jdbc/postgres/PostgreSQLVectorStoreQueryProvider.java b/data/semantickernel-data-postgres/src/main/java/com/microsoft/semantickernel/data/jdbc/postgres/PostgreSQLVectorStoreQueryProvider.java
index 1f3273eb3..e65bb9eb1 100644
--- a/data/semantickernel-data-postgres/src/main/java/com/microsoft/semantickernel/data/jdbc/postgres/PostgreSQLVectorStoreQueryProvider.java
+++ b/data/semantickernel-data-postgres/src/main/java/com/microsoft/semantickernel/data/jdbc/postgres/PostgreSQLVectorStoreQueryProvider.java
@@ -4,10 +4,10 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.microsoft.semantickernel.data.jdbc.JDBCVectorStoreQueryProvider;
-import com.microsoft.semantickernel.data.jdbc.SQLVectorStoreQueryProvider;
import com.microsoft.semantickernel.data.filter.AnyTagEqualToFilterClause;
import com.microsoft.semantickernel.data.filter.EqualToFilterClause;
+import com.microsoft.semantickernel.data.jdbc.JDBCVectorStoreQueryProvider;
+import com.microsoft.semantickernel.data.jdbc.SQLVectorStoreQueryProvider;
import com.microsoft.semantickernel.data.vectorsearch.VectorSearchFilter;
import com.microsoft.semantickernel.data.vectorsearch.VectorSearchResult;
import com.microsoft.semantickernel.data.vectorsearch.VectorSearchResults;
@@ -22,9 +22,6 @@
import com.microsoft.semantickernel.data.vectorstorage.options.VectorSearchOptions;
import com.microsoft.semantickernel.exceptions.SKException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import javax.annotation.Nonnull;
-import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -38,6 +35,8 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.sql.DataSource;
/**
* The MySQL vector store query provider.
@@ -50,7 +49,6 @@ public class PostgreSQLVectorStoreQueryProvider extends
private final String prefixForCollectionTables;
private final ObjectMapper objectMapper;
- @SuppressFBWarnings("EI_EXPOSE_REP2")
private PostgreSQLVectorStoreQueryProvider(
@Nonnull DataSource dataSource,
@Nonnull String collectionsTable,
diff --git a/data/semantickernel-data-redis/pom.xml b/data/semantickernel-data-redis/pom.xml
index de2f60ee5..db151484b 100644
--- a/data/semantickernel-data-redis/pom.xml
+++ b/data/semantickernel-data-redis/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -70,6 +70,13 @@
redis.clients
jedis
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
\ No newline at end of file
diff --git a/data/semantickernel-data-redis/src/main/java/com/microsoft/semantickernel/data/redis/RedisVectorStoreCollectionSearchMapping.java b/data/semantickernel-data-redis/src/main/java/com/microsoft/semantickernel/data/redis/RedisVectorStoreCollectionSearchMapping.java
index f029f6787..7f7d31bcf 100644
--- a/data/semantickernel-data-redis/src/main/java/com/microsoft/semantickernel/data/redis/RedisVectorStoreCollectionSearchMapping.java
+++ b/data/semantickernel-data-redis/src/main/java/com/microsoft/semantickernel/data/redis/RedisVectorStoreCollectionSearchMapping.java
@@ -154,12 +154,12 @@ public String getFilter(VectorSearchFilter filter,
*/
@Override
public String getEqualToFilter(EqualToFilterClause filterClause) {
- String fieldName = filterClause.getFieldName();
+ String fieldName = validateFieldName(filterClause.getFieldName());
Object value = filterClause.getValue();
String formattedValue;
if (value instanceof String) {
- formattedValue = String.format("\"%s\"", value);
+ formattedValue = String.format("\"%s\"", escapeRedisString((String) value));
} else if (value instanceof Number) {
formattedValue = String.format("[%s %s]", value, value);
} else {
@@ -178,6 +178,19 @@ public String getEqualToFilter(EqualToFilterClause filterClause) {
*/
@Override
public String getAnyTagEqualToFilter(AnyTagEqualToFilterClause filterClause) {
- return String.format("@%s:\"%s\"", filterClause.getFieldName(), filterClause.getValue());
+ return String.format("@%s:\"%s\"",
+ validateFieldName(filterClause.getFieldName()),
+ escapeRedisString(filterClause.getValue().toString()));
+ }
+
+ private String validateFieldName(String fieldName) {
+ if (fieldName.matches("[a-zA-Z_][a-zA-Z0-9_]*")) {
+ return fieldName;
+ }
+ throw new SKException("Invalid field name: " + fieldName);
+ }
+
+ private String escapeRedisString(String searchString) {
+ return searchString.replaceAll("([,.<>{}\\[\\]\"':;!@#$%^&*()\\-+=~|\\\\/?\\s])", "\\\\$1");
}
}
diff --git a/data/semantickernel-data-sqlite/pom.xml b/data/semantickernel-data-sqlite/pom.xml
index fc4d80186..7b4faa3de 100644
--- a/data/semantickernel-data-sqlite/pom.xml
+++ b/data/semantickernel-data-sqlite/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../../pom.xml
@@ -52,7 +52,7 @@
org.xerial
sqlite-jdbc
- 3.47.0.0
+ 3.53.0.0
\ No newline at end of file
diff --git a/data/semantickernel-data-sqlite/src/main/java/com/microsoft/semantickernel/data/jdbc/sqlite/SQLiteVectorStoreQueryProvider.java b/data/semantickernel-data-sqlite/src/main/java/com/microsoft/semantickernel/data/jdbc/sqlite/SQLiteVectorStoreQueryProvider.java
index 57de12257..2d88702ee 100644
--- a/data/semantickernel-data-sqlite/src/main/java/com/microsoft/semantickernel/data/jdbc/sqlite/SQLiteVectorStoreQueryProvider.java
+++ b/data/semantickernel-data-sqlite/src/main/java/com/microsoft/semantickernel/data/jdbc/sqlite/SQLiteVectorStoreQueryProvider.java
@@ -13,13 +13,12 @@
import com.microsoft.semantickernel.data.vectorstorage.options.UpsertRecordOptions;
import com.microsoft.semantickernel.exceptions.SKException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import javax.annotation.Nonnull;
-import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
+import javax.annotation.Nonnull;
+import javax.sql.DataSource;
/**
* A query provider for a vector store in SQLite.
@@ -30,7 +29,6 @@ public class SQLiteVectorStoreQueryProvider extends
private final DataSource dataSource;
private final ObjectMapper objectMapper;
- @SuppressFBWarnings("EI_EXPOSE_REP2")
private SQLiteVectorStoreQueryProvider(
@Nonnull DataSource dataSource,
@Nonnull String collectionsTable,
diff --git a/pom.xml b/pom.xml
index 8399f1c79..37b4bf041 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
pom
https://www.github.com/microsoft/semantic-kernel
@@ -15,14 +15,14 @@
1.0.0-beta.16
- 10.18.2
+ 13.4.2
0.10.21
false
2.19.1
1.17.0
1.6.0
5.11.3
- 2.24.1
+ 2.25.4
3.1.0
2.12.1
3.5.0
@@ -42,8 +42,7 @@
3.10.1
3.5.0
2.4.0
-
- 3.27.0
+ 3.28.0
3.8.0
0.16.1
3.1.1
@@ -59,12 +58,11 @@
2.17.1
5.14.2
0.9.1
-
- 7.10.0
+ 7.23.0
UTF-8
microsoft/semantic-kernel
git@github.com:${project.github.repository}.git
- 4.8.6
+ 4.9.8
@@ -74,6 +72,7 @@
aiservices/openai
aiservices/google
aiservices/huggingface
+ aiservices/voyageai
data/semantickernel-data-azureaisearch
data/semantickernel-data-jdbc
data/semantickernel-data-redis
@@ -203,13 +202,32 @@
org.wiremock
wiremock
- 3.9.2
+ 3.13.2
test
org.mockito
mockito-junit-jupiter
- 5.14.2
+ 5.23.0
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ 1.21.4
+ test
+
+
+ org.testcontainers
+ postgresql
+ 1.21.4
+ test
+
+
+ org.testcontainers
+ mysql
+ 1.21.4
test
@@ -820,6 +838,15 @@
api-test
+
+ skip-container-tests
+
+ false
+
+
+ true
+
+
release
diff --git a/samples/pom.xml b/samples/pom.xml
index d2bea638a..6ddaea22d 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-concepts/pom.xml b/samples/semantickernel-concepts/pom.xml
index 0b51d1255..99d85497c 100644
--- a/samples/semantickernel-concepts/pom.xml
+++ b/samples/semantickernel-concepts/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-samples-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/pom.xml b/samples/semantickernel-concepts/semantickernel-syntax-examples/pom.xml
index bd5b298ce..8d42b664b 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/pom.xml
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-concepts
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -29,7 +29,7 @@
io.opentelemetry.instrumentation
opentelemetry-reactor-3.1
- 2.9.0-alpha
+ 2.26.1-alpha
com.microsoft.semantic-kernel
@@ -140,20 +140,20 @@
org.apache.pdfbox
pdfbox
- 3.0.3
+ 3.0.7
com.google.cloud
google-cloud-vertexai
- 1.6.0
+ 1.52.0
compile
com.mysql
mysql-connector-j
- 9.0.0
+ 9.6.0
com.github.victools
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java
index 5e01d13a2..c3ebd6a0f 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example62_CustomAIServiceSelector.java
@@ -50,7 +50,7 @@ public static void main(String[] args) {
var openAIChatCompletion = OpenAIChatCompletion.builder()
.withOpenAIAsyncClient(client)
.withServiceId("AzureOpenAIChat")
- .withModelId("gpt-35-turbo-2")
+ .withModelId("gpt-35-turbo")
.build();
var textGenerationService = TextGenerationService.builder()
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java
index 23bd37787..591bab7eb 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example69_MutableKernelPlugin.java
@@ -3,6 +3,7 @@
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.plugin.KernelPlugin;
+import com.microsoft.semantickernel.plugin.KernelPluginFactory;
import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction;
@@ -16,6 +17,7 @@ public class Example69_MutableKernelPlugin {
*/
public static void main(String[] args) throws NoSuchMethodException {
System.out.println("======== Example69_MutableKernelPlugin ========");
+ KernelPluginFactory.setTypeFilterEnable(false);
KernelPlugin plugin = new KernelPlugin("Plugin", "Mutable plugin", null);
plugin.addFunction(KernelFunction.createFromMethod(
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormat.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormat.java
index 5d0aad36e..8a5d02334 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormat.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormat.java
@@ -55,6 +55,7 @@ public static void main(String[] args) throws InterruptedException, JsonProcessi
.setResponseFormat(Pet.class)
.setName("Pet")
.build())
+ .withMaxCompletionTokensEnable(true)
.build();
FunctionResult response = kernel
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormatToolCall.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormatToolCall.java
index cd227ebd5..a3126eff5 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormatToolCall.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/chatcompletion/responseschema/Example_ChatWithResponseFormatToolCall.java
@@ -58,6 +58,7 @@ public static void main(String[] args) throws InterruptedException, JsonProcessi
PromptExecutionSettings promptExecutionSettings = PromptExecutionSettings.builder()
.withJsonSchemaResponseFormat(Pet.class)
+ .withMaxCompletionTokensEnable(true)
.build();
FunctionResult response = kernel.invokePromptAsync("Get pet with id 1234")
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/functions/Example59_OpenAIFunctionCalling.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/functions/Example59_OpenAIFunctionCalling.java
index e921bb784..d021caf88 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/functions/Example59_OpenAIFunctionCalling.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/functions/Example59_OpenAIFunctionCalling.java
@@ -39,7 +39,7 @@ public class Example59_OpenAIFunctionCalling {
// Only required if AZURE_CLIENT_KEY is set
private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT");
private static final String MODEL_ID = System.getenv()
- .getOrDefault("MODEL_ID", "gpt-4o");
+ .getOrDefault("MODEL_ID", "gpt-35-turbo");
// Define functions that can be called by the model
public static class HelperFunctions {
diff --git a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/plugin/Example13_ConversationSummaryPlugin.java b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/plugin/Example13_ConversationSummaryPlugin.java
index 63412b600..47f0e5a46 100644
--- a/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/plugin/Example13_ConversationSummaryPlugin.java
+++ b/samples/semantickernel-concepts/semantickernel-syntax-examples/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/plugin/Example13_ConversationSummaryPlugin.java
@@ -107,7 +107,7 @@ public class Example13_ConversationSummaryPlugin {
Jane: Darn, it's just repeating stuff now.
John: I think we're done.
Jane: We're not though! We need like 1500 more characters.
- John: Oh Cananda, our home and native land.
+ John: Oh Canada, our home and native land.
Jane: True patriot love in all thy sons command.
John: With glowing hearts we see thee rise.
Jane: The True North strong and free.
diff --git a/samples/semantickernel-demos/booking-agent-m365/pom.xml b/samples/semantickernel-demos/booking-agent-m365/pom.xml
index 60bc7ffbc..ad08549e4 100644
--- a/samples/semantickernel-demos/booking-agent-m365/pom.xml
+++ b/samples/semantickernel-demos/booking-agent-m365/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-demos
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -43,7 +43,7 @@
com.microsoft.graph
microsoft-graph
- 6.13.0
+ 6.62.0
diff --git a/samples/semantickernel-demos/pom.xml b/samples/semantickernel-demos/pom.xml
index c19477a7c..ba42e0dbd 100644
--- a/samples/semantickernel-demos/pom.xml
+++ b/samples/semantickernel-demos/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-samples-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-demos/semantickernel-spring-starter/pom.xml b/samples/semantickernel-demos/semantickernel-spring-starter/pom.xml
index d7755a11c..29454883a 100644
--- a/samples/semantickernel-demos/semantickernel-spring-starter/pom.xml
+++ b/samples/semantickernel-demos/semantickernel-spring-starter/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-demos
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -31,40 +31,40 @@
org.springframework.boot
spring-boot-test
- 3.3.2
+ 4.1.0-M4
test
org.assertj
assertj-core
- 3.26.3
+ 4.0.0-M1
test
org.springframework.boot
spring-boot-autoconfigure
- 3.3.2
+ 4.1.0-M4
org.springframework.boot
spring-boot
- 3.3.11
+ 4.1.0-M4
org.springframework
spring-test
- 6.1.10
+ 7.0.6
test
com.azure
azure-identity
- 1.12.2
+ 1.18.2
org.junit.jupiter
junit-jupiter-api
- 5.10.3
+ 6.1.0-M1
test
diff --git a/samples/semantickernel-demos/sk-presidio-sample/pom.xml b/samples/semantickernel-demos/sk-presidio-sample/pom.xml
index 079b5af0f..2684a88ca 100644
--- a/samples/semantickernel-demos/sk-presidio-sample/pom.xml
+++ b/samples/semantickernel-demos/sk-presidio-sample/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-demos
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-learn-resources/pom.xml b/samples/semantickernel-learn-resources/pom.xml
index 8d5130b22..420b98e30 100644
--- a/samples/semantickernel-learn-resources/pom.xml
+++ b/samples/semantickernel-learn-resources/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-samples-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -89,7 +89,7 @@
com.mysql
mysql-connector-j
- 9.0.0
+ 9.6.0
compile
diff --git a/samples/semantickernel-sample-plugins/pom.xml b/samples/semantickernel-sample-plugins/pom.xml
index 0abacea7f..c09c6ea8b 100644
--- a/samples/semantickernel-sample-plugins/pom.xml
+++ b/samples/semantickernel-sample-plugins/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-samples-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-sample-plugins/semantickernel-openapi-plugin/pom.xml b/samples/semantickernel-sample-plugins/semantickernel-openapi-plugin/pom.xml
index f0b576424..4016488ab 100644
--- a/samples/semantickernel-sample-plugins/semantickernel-openapi-plugin/pom.xml
+++ b/samples/semantickernel-sample-plugins/semantickernel-openapi-plugin/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-sample-plugins
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -62,7 +62,7 @@
io.swagger.parser.v3
swagger-parser
- 2.1.22
+ 2.1.40
com.microsoft.semantic-kernel
diff --git a/samples/semantickernel-sample-plugins/semantickernel-presidio-plugin/pom.xml b/samples/semantickernel-sample-plugins/semantickernel-presidio-plugin/pom.xml
index 10326e6ea..af97964db 100644
--- a/samples/semantickernel-sample-plugins/semantickernel-presidio-plugin/pom.xml
+++ b/samples/semantickernel-sample-plugins/semantickernel-presidio-plugin/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-sample-plugins
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/samples/semantickernel-sample-plugins/semantickernel-text-splitter-plugin/pom.xml b/samples/semantickernel-sample-plugins/semantickernel-text-splitter-plugin/pom.xml
index 1bf80430f..5236bcff4 100644
--- a/samples/semantickernel-sample-plugins/semantickernel-text-splitter-plugin/pom.xml
+++ b/samples/semantickernel-sample-plugins/semantickernel-text-splitter-plugin/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-sample-plugins
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api-ai-services/pom.xml b/semantickernel-api-ai-services/pom.xml
index 6187b56b4..e1fd17ef3 100644
--- a/semantickernel-api-ai-services/pom.xml
+++ b/semantickernel-api-ai-services/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api-builders/pom.xml b/semantickernel-api-builders/pom.xml
index be51b46f7..cbd35cb81 100644
--- a/semantickernel-api-builders/pom.xml
+++ b/semantickernel-api-builders/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
com.microsoft.semantic-kernel
diff --git a/semantickernel-api-data/pom.xml b/semantickernel-api-data/pom.xml
index 13bd19931..9e055c1e8 100644
--- a/semantickernel-api-data/pom.xml
+++ b/semantickernel-api-data/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api-exceptions/pom.xml b/semantickernel-api-exceptions/pom.xml
index b07b001c0..bb60deadb 100644
--- a/semantickernel-api-exceptions/pom.xml
+++ b/semantickernel-api-exceptions/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api-localization/pom.xml b/semantickernel-api-localization/pom.xml
index f1c3c2467..b57ad77f1 100644
--- a/semantickernel-api-localization/pom.xml
+++ b/semantickernel-api-localization/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api-textembedding-services/pom.xml b/semantickernel-api-textembedding-services/pom.xml
index eb8391355..3cba6dd41 100644
--- a/semantickernel-api-textembedding-services/pom.xml
+++ b/semantickernel-api-textembedding-services/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
diff --git a/semantickernel-api/pom.xml b/semantickernel-api/pom.xml
index ace57557c..dcebfbc60 100644
--- a/semantickernel-api/pom.xml
+++ b/semantickernel-api/pom.xml
@@ -6,7 +6,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
../pom.xml
@@ -42,7 +42,7 @@
io.opentelemetry.instrumentation
opentelemetry-reactor-3.1
- 2.9.0-alpha
+ 2.26.1-alpha
com.azure
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypeConverter.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypeConverter.java
index 22d53ec06..3082ecf8a 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypeConverter.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/ContextVariableTypeConverter.java
@@ -3,7 +3,6 @@
import com.microsoft.semantickernel.exceptions.SKException;
import com.microsoft.semantickernel.localization.SemanticKernelResources;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -341,7 +340,6 @@ public static class Builder {
*
* @param clazz the class of the type
*/
- @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public Builder(Class clazz) {
this.clazz = clazz;
fromObject = x -> ContextVariableTypes.convert(x, clazz);
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/CollectionVariableContextVariableTypeConverter.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/CollectionVariableContextVariableTypeConverter.java
index d7bf72e00..5b2661971 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/CollectionVariableContextVariableTypeConverter.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/contextvariables/converters/CollectionVariableContextVariableTypeConverter.java
@@ -7,7 +7,6 @@
import com.microsoft.semantickernel.contextvariables.ContextVariableType;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.stream.Collectors;
@@ -24,7 +23,6 @@ public class CollectionVariableContextVariableTypeConverter extends
* Creates a new instance of the {@link CollectionVariableContextVariableTypeConverter} class.
* @param delimiter The delimiter to use joining elements of the collection.
*/
- @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public CollectionVariableContextVariableTypeConverter(String delimiter) {
super(
Collection.class,
@@ -38,7 +36,6 @@ public CollectionVariableContextVariableTypeConverter(String delimiter) {
/**
* Creates a new instance of the {@link CollectionVariableContextVariableTypeConverter} class.
*/
- @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public CollectionVariableContextVariableTypeConverter() {
this(",");
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java
index 7a39cdfe8..715e71185 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/FunctionInvokedEvent.java
@@ -2,8 +2,8 @@
package com.microsoft.semantickernel.hooks;
import com.microsoft.semantickernel.orchestration.FunctionResult;
-import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.KernelArguments;
+import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.annotation.Nullable;
@@ -60,7 +60,6 @@ public KernelArguments getArguments() {
*
* @return the result
*/
- @SuppressFBWarnings("EI_EXPOSE_REP")
public FunctionResult getResult() {
return result;
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java
index e4d3f5300..586c93c5d 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java
@@ -104,6 +104,7 @@ static ChatCompletionsOptions cloneOptionsWithMessages(
.setFrequencyPenalty(options.getFrequencyPenalty())
.setLogitBias(options.getLogitBias())
.setMaxTokens(options.getMaxTokens())
+ .setMaxCompletionTokens(options.getMaxCompletionTokens())
.setModel(options.getModel())
.setStop(options.getStop())
.setTemperature(options.getTemperature())
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java
index 42430f085..e5850228c 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java
@@ -2,8 +2,8 @@
package com.microsoft.semantickernel.hooks;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
-import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.KernelArguments;
+import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.annotation.Nullable;
@@ -52,7 +52,6 @@ public KernelArguments getArguments() {
* Get the tool call function.
* @return The tool call function.
*/
- @SuppressFBWarnings("EI_EXPOSE_REP2")
public KernelFunction> getFunction() {
return function;
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PromptRenderedEvent.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PromptRenderedEvent.java
index 348d3bf12..fbd8cb801 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PromptRenderedEvent.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PromptRenderedEvent.java
@@ -55,7 +55,6 @@ public KernelArguments getArguments() {
*
* @return the prompt
*/
- @SuppressFBWarnings("EI_EXPOSE_REP")
public String getPrompt() {
return prompt;
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/implementation/chatcompletion/ChatXMLPromptParser.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/implementation/chatcompletion/ChatXMLPromptParser.java
index 8cc1cc972..975f5e533 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/implementation/chatcompletion/ChatXMLPromptParser.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/implementation/chatcompletion/ChatXMLPromptParser.java
@@ -18,6 +18,7 @@
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
+import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
@@ -32,6 +33,29 @@ public class ChatXMLPromptParser {
private static final Logger LOGGER = LoggerFactory.getLogger(ChatXMLPromptParser.class);
+ private static XMLInputFactory createXMLInputFactory() {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+
+ trySetProperty(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ trySetProperty(factory, XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ trySetProperty(factory, XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
+
+ return factory;
+ }
+
+ private static void trySetProperty(XMLInputFactory factory, String property, Object value) {
+ try {
+ factory.setProperty(property, value);
+ } catch (IllegalArgumentException e) {
+ // Property not supported by this XMLInputFactory implementation
+ LOGGER.trace("XMLInputFactory property '{}' not supported", property);
+ }
+ }
+
public static ChatPromptParseVisitor parse(
String rawPrompt,
ChatPromptParseVisitor chatPromptParseVisitor) {
@@ -64,7 +88,7 @@ private static ChatPromptParseVisitor getChatRequestMessages(String promp
// In this way, we can avoid parsing the whole prompt twice and easily extend the parsing logic.
try (InputStream is = new ByteArrayInputStream(prompt.getBytes(StandardCharsets.UTF_8))) {
- XMLInputFactory factory = XMLInputFactory.newInstance();
+ XMLInputFactory factory = createXMLInputFactory();
XMLEventReader reader = factory.createXMLEventReader(is);
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
@@ -109,7 +133,7 @@ private static ChatPromptParseVisitor getFunctionDefinitions(String promp
//
try (InputStream is = new ByteArrayInputStream(prompt.getBytes(StandardCharsets.UTF_8))) {
- XMLInputFactory factory = XMLInputFactory.newInstance();
+ XMLInputFactory factory = createXMLInputFactory();
XMLEventReader reader = factory.createXMLEventReader(is);
FunctionDefinition functionDefinition = null;
Map parameters = new HashMap<>();
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java
index 9b8a518c3..8641f201c 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java
@@ -14,9 +14,8 @@
import com.microsoft.semantickernel.hooks.KernelHooks.UnmodifiableKernelHooks;
import com.microsoft.semantickernel.implementation.telemetry.SemanticKernelTelemetry;
import com.microsoft.semantickernel.localization.SemanticKernelResources;
-import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.KernelArguments;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import java.util.NoSuchElementException;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
@@ -63,7 +62,6 @@ public class FunctionInvocation extends Mono> {
* @param kernel The kernel to invoke the function on.
* @param function The function to invoke.
*/
- @SuppressFBWarnings("EI_EXPOSE_REP2")
public FunctionInvocation(
Kernel kernel,
KernelFunction function) {
@@ -80,7 +78,6 @@ public FunctionInvocation(
* @param function The function to invoke.
* @param resultType The type of the result of the function invocation.
*/
- @SuppressFBWarnings("EI_EXPOSE_REP2")
public FunctionInvocation(
Kernel kernel,
KernelFunction> function,
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/PromptExecutionSettings.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/PromptExecutionSettings.java
index 19dfbd0dd..bfa2bb667 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/PromptExecutionSettings.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/PromptExecutionSettings.java
@@ -82,6 +82,7 @@ public class PromptExecutionSettings {
private static final String PRESENCE_PENALTY = "presence_penalty";
private static final String FREQUENCY_PENALTY = "frequency_penalty";
private static final String MAX_TOKENS = "max_tokens";
+ private static final String MAX_COMPLETION_TOKENS = "max_completion_tokens";
private static final String BEST_OF = "best_of";
private static final String USER = "user";
private static final String STOP_SEQUENCES = "stop_sequences";
@@ -89,13 +90,19 @@ public class PromptExecutionSettings {
private static final String TOKEN_SELECTION_BIASES = "token_selection_biases";
private static final String RESPONSE_FORMAT = "response_format";
+ private static final String MAX_COMPLETION_TOKENS_ENABLE = "MAX_COMPLETION_TOKENS_ENABLE";
+ private static final String DEFAULT_MAX_COMPLETION_TOKENS_ENABLE = System.getenv(
+ MAX_COMPLETION_TOKENS_ENABLE);
+
private final String serviceId;
private final String modelId;
private final double temperature;
private final double topP;
private final double presencePenalty;
private final double frequencyPenalty;
- private final int maxTokens;
+ private final boolean maxCompletionTokensEnable;
+ private final Integer maxCompletionTokens;
+ private final Integer maxTokens;
private final int bestOf;
private final int resultsPerPrompt;
private final String user;
@@ -135,14 +142,24 @@ public PromptExecutionSettings(
@JsonProperty(USER) String user,
@Nullable @JsonProperty(STOP_SEQUENCES) List stopSequences,
@Nullable @JsonProperty(TOKEN_SELECTION_BIASES) Map tokenSelectionBiases,
- @Nullable @JsonProperty(RESPONSE_FORMAT) ResponseFormat responseFormat) {
+ @Nullable @JsonProperty(RESPONSE_FORMAT) ResponseFormat responseFormat,
+ @JsonProperty(value = MAX_COMPLETION_TOKENS_ENABLE, defaultValue = "false") String maxCompletionTokensEnable) {
this.serviceId = serviceId != null ? serviceId : DEFAULT_SERVICE_ID;
this.modelId = modelId != null ? modelId : "";
this.temperature = clamp(temperature, 0d, 2d, DEFAULT_TEMPERATURE);
this.topP = clamp(topP, 0d, 1d, DEFAULT_TOP_P);
this.presencePenalty = clamp(presencePenalty, -2d, 2d, DEFAULT_PRESENCE_PENALTY);
this.frequencyPenalty = clamp(frequencyPenalty, -2d, 2d, DEFAULT_FREQUENCY_PENALTY);
- this.maxTokens = clamp(maxTokens, 1, Integer.MAX_VALUE, DEFAULT_MAX_TOKENS);
+
+ this.maxCompletionTokensEnable = isMaxCompletionTokensEnable(maxCompletionTokensEnable);
+
+ if (this.maxCompletionTokensEnable) {
+ this.maxCompletionTokens = clamp(maxTokens, 1, Integer.MAX_VALUE, DEFAULT_MAX_TOKENS);
+ this.maxTokens = null;
+ } else {
+ this.maxTokens = clamp(maxTokens, 1, Integer.MAX_VALUE, DEFAULT_MAX_TOKENS);
+ this.maxCompletionTokens = null;
+ }
this.resultsPerPrompt = clamp(resultsPerPrompt, 1, Integer.MAX_VALUE,
DEFAULT_RESULTS_PER_PROMPT);
this.bestOf = clamp(bestOf, 1, Integer.MAX_VALUE, DEFAULT_BEST_OF);
@@ -161,6 +178,22 @@ public PromptExecutionSettings(
}
}
+ private boolean isMaxCompletionTokensEnable(String maxCompletionTokensEnable) {
+ final boolean maxCompletionTokensEnabled;
+ if (maxCompletionTokensEnable != null && !maxCompletionTokensEnable.isEmpty()) {
+ maxCompletionTokensEnabled = Boolean.parseBoolean(maxCompletionTokensEnable);
+ } else {
+ if (DEFAULT_MAX_COMPLETION_TOKENS_ENABLE != null
+ && DEFAULT_MAX_COMPLETION_TOKENS_ENABLE.isEmpty()) {
+ maxCompletionTokensEnabled = Boolean.parseBoolean(
+ DEFAULT_MAX_COMPLETION_TOKENS_ENABLE);
+ } else {
+ maxCompletionTokensEnabled = false;
+ }
+ }
+ return maxCompletionTokensEnabled;
+ }
+
/**
* Create a new builder for PromptExecutionSettings.
*
@@ -257,7 +290,7 @@ public double getFrequencyPenalty() {
* @return The maximum number of tokens to generate in the output.
*/
@JsonProperty(MAX_TOKENS)
- public int getMaxTokens() {
+ public Integer getMaxTokens() {
return maxTokens;
}
@@ -351,7 +384,10 @@ public boolean equals(Object obj) {
if (Double.compare(frequencyPenalty, other.frequencyPenalty) != 0) {
return false;
}
- if (maxTokens != other.maxTokens) {
+ if (!Objects.equals(maxTokens, other.maxTokens)) {
+ return false;
+ }
+ if (!Objects.equals(maxCompletionTokens, other.maxCompletionTokens)) {
return false;
}
if (bestOf != other.bestOf) {
@@ -383,6 +419,12 @@ public ResponseFormat getResponseFormat() {
return responseFormat;
}
+
+ @JsonProperty(MAX_COMPLETION_TOKENS)
+ public Integer getMaxCompletionTokens() {
+ return maxCompletionTokens;
+ }
+
/**
* Builder for PromptExecutionSettings.
*/
@@ -480,6 +522,18 @@ public Builder withMaxTokens(int maxTokens) {
return this;
}
+ /**
+ * Enables the use of max_completion_tokens config parameter rather than the older
+ * max_completion
+ *
+ * @param enable Whether to enable
+ * @return This builder
+ */
+ public Builder withMaxCompletionTokensEnable(boolean enable) {
+ settings.put(MAX_COMPLETION_TOKENS_ENABLE, Boolean.toString(enable));
+ return this;
+ }
+
/**
* Set the number of results to generate for each prompt. The value is clamped to the range
* [1, Integer.MAX_VALUE], and the default is 1.
@@ -620,7 +674,9 @@ public PromptExecutionSettings build() {
(List) settings.getOrDefault(STOP_SEQUENCES, Collections.emptyList()),
(Map) settings.getOrDefault(TOKEN_SELECTION_BIASES,
Collections.emptyMap()),
- (ResponseFormat) settings.getOrDefault(RESPONSE_FORMAT, new TextResponseFormat()));
+ (ResponseFormat) settings.getOrDefault(RESPONSE_FORMAT, new TextResponseFormat()),
+ (String) settings.getOrDefault(MAX_COMPLETION_TOKENS_ENABLE,
+ DEFAULT_MAX_COMPLETION_TOKENS_ENABLE));
}
}
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java
index afa31cb00..fa31819f2 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java
@@ -25,12 +25,17 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.reactivestreams.Publisher;
@@ -50,7 +55,13 @@ public class KernelPluginFactory {
private static final CaseInsensitiveMap> COMMON_CLASS_NAMES = new CaseInsensitiveMap<>();
private static final Map, Class>> BOXED_FROM_PRIMITIVE = new HashMap<>();
+ public static final String CLASS_FILTER_ENABLE_PROPERTY = "semantic-kernel.class-filter-enable";
+ private static Boolean CLASS_FILTER_ENABLE;
+
static {
+ CLASS_FILTER_ENABLE = Boolean.parseBoolean(
+ System.getProperty(CLASS_FILTER_ENABLE_PROPERTY, "true"));
+
PRIMITIVE_CLASS_NAMES.put("void", void.class);
PRIMITIVE_CLASS_NAMES.put("int", int.class);
PRIMITIVE_CLASS_NAMES.put("double", double.class);
@@ -67,6 +78,15 @@ public class KernelPluginFactory {
COMMON_CLASS_NAMES.put("map", HashMap.class);
COMMON_CLASS_NAMES.put("set", HashSet.class);
+ COMMON_CLASS_NAMES.put(Integer.class.getName(), int.class);
+ COMMON_CLASS_NAMES.put(String.class.getName(), String.class);
+ COMMON_CLASS_NAMES.put(List.class.getName(), ArrayList.class);
+ COMMON_CLASS_NAMES.put(Map.class.getName(), HashMap.class);
+ COMMON_CLASS_NAMES.put(Set.class.getName(), HashSet.class);
+ COMMON_CLASS_NAMES.put(Temporal.class.getName(), Temporal.class);
+ COMMON_CLASS_NAMES.put(java.time.OffsetDateTime.class.getName(), java.time.OffsetDateTime.class);
+ COMMON_CLASS_NAMES.put(java.time.ZonedDateTime.class.getName(), java.time.ZonedDateTime.class);
+
BOXED_FROM_PRIMITIVE.put(void.class, Void.class);
BOXED_FROM_PRIMITIVE.put(int.class, Integer.class);
BOXED_FROM_PRIMITIVE.put(double.class, Double.class);
@@ -79,6 +99,10 @@ public class KernelPluginFactory {
}
+ public static void setTypeFilterEnable(boolean enable) {
+ KernelPluginFactory.CLASS_FILTER_ENABLE = enable;
+ }
+
/**
* Creates a plugin that wraps the specified target object. Methods decorated with
* {@code {@literal @}DefineSKFunction} will be included in the plugin.
@@ -240,21 +264,16 @@ public static Class> getTypeForName(String className) {
return clazz;
}
+ if (!checkClassName(className)) {
+ throw new SKException("Requested type is not allowed: " + className);
+ }
+
try {
clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
// ignore
}
- if (clazz == null) {
- try {
- // Seems that in tests specifically we need to use the class loader of the class itself
- clazz = KernelPluginFactory.class.getClassLoader().loadClass(className);
- } catch (ClassNotFoundException e) {
- // ignore
- }
- }
-
if (clazz == null) {
throw new SKException("Requested type could not be found: " + className
+ ". This needs to be a fully qualified class name, e.g. 'java.lang.String'.");
@@ -262,6 +281,13 @@ public static Class> getTypeForName(String className) {
return clazz;
}
+ public static boolean checkClassName(String className) {
+ if (CLASS_FILTER_ENABLE == false) {
+ return true;
+ }
+ return ClassFilter.CLASS_CHECKER.test(className);
+ }
+
/**
* Creates a plugin from the provided name and function collection.
*
@@ -429,6 +455,7 @@ private static KernelFunction getKernelFunction(
/**
* Imports a plugin from a resource directory on the filesystem.
+ *
* @param parentDirectory The parent directory containing the plugin directories.
* @param pluginDirectoryName The name of the plugin directory.
* @param functionName The name of the function to import.
@@ -552,4 +579,107 @@ private static PromptTemplateConfig getPromptTemplateConfig(
return null;
}
}
+
+ // Filters allowed classes that can be used as types in plugins
+ public static class ClassFilter {
+
+ // Selects which filter type to use, allow list or ban list
+ public static final String CLASS_BLOCK_TYPE_PROPERTY_NAME = "semantic-kernel.class-block-type";
+ public static final String CLASS_BLOCK_LIST_PROPERTY_NAME = "semantic-kernel.class-block-list";
+ public static final String CLASS_ALLOW_LIST_PROPERTY_NAME = "semantic-kernel.class-allow-list";
+
+ // allow nothing by default (other than java primitives and collections)
+ private static final List CLASS_ALLOW_LIST;
+ private static final List CLASS_ALLOW_LIST_DEFAULT = Collections.emptyList();
+
+ // block Java classes by default (other than java primitives and collections)
+ private static final List CLASS_BLOCK_LIST;
+ private static final List CLASS_BLOCK_LIST_DEFAULT = Arrays.asList(
+ "java\\..*",
+ "com\\.sun\\..*",
+ "javax\\..*",
+ "jdk\\..*",
+ "org\\.xml\\..*",
+ "org\\.w3c\\..*"
+ );
+
+ static Predicate CLASS_CHECKER;
+
+ private enum BlockType {
+ BLOCK,
+ ALLOW
+ }
+
+ static {
+ // Default to blocking type
+ String classFilterType = System.getProperty(CLASS_BLOCK_TYPE_PROPERTY_NAME,
+ BlockType.BLOCK.name());
+ CLASS_BLOCK_LIST = getList(CLASS_BLOCK_LIST_PROPERTY_NAME, CLASS_BLOCK_LIST_DEFAULT);
+ CLASS_ALLOW_LIST = getList(CLASS_ALLOW_LIST_PROPERTY_NAME, CLASS_ALLOW_LIST_DEFAULT);
+
+ BlockType type;
+
+ try {
+ type = BlockType.valueOf(classFilterType.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ type = BlockType.BLOCK;
+ }
+
+ switch (type) {
+ case ALLOW:
+ CLASS_CHECKER = ClassFilter::evaluateAllow;
+ break;
+ case BLOCK:
+ default:
+ CLASS_CHECKER = ClassFilter::evaluateBlock;
+ break;
+ }
+ }
+
+ private static List getList(String propertyName, List defaultList) {
+ String blockList = System.getProperty(propertyName);
+
+ if (blockList != null) {
+ return Arrays.asList(blockList.split(","));
+ } else {
+ return defaultList;
+ }
+ }
+
+ // Block classes/packages classes (other than common Java primitives and collections)
+ private static boolean evaluateBlock(String className) {
+ if (className == null || className.isEmpty()) {
+ return false;
+ }
+
+ for (String ban : CLASS_BLOCK_LIST) {
+ if (className.matches(ban)) {
+ LOGGER.warn(
+ "Skipping class not allowed by class block list {}, if you wish to unblock this class update the property: {}. Filtering can also be controlled with {} and KernelPluginFactory.setTypeFilterEnable",
+ className, CLASS_BLOCK_LIST_PROPERTY_NAME, CLASS_FILTER_ENABLE_PROPERTY);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Only allow explicitly allowed classes/packages (other than common Java primitives and collections)
+ private static boolean evaluateAllow(String className) {
+ if (className == null || className.isEmpty()) {
+ return false;
+ }
+
+ for (String allow : CLASS_ALLOW_LIST) {
+ if (className.matches(allow)) {
+ return true;
+ }
+ }
+
+ LOGGER.warn(
+ "Skipping class not allowed by class allow list {}, if you wish to allow this class update the property: {}. Filtering can also be controlled with {} and KernelPluginFactory.setTypeFilterEnable",
+ className, CLASS_ALLOW_LIST_DEFAULT, CLASS_FILTER_ENABLE_PROPERTY);
+ return false;
+ }
+ }
}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/RerankResult.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/RerankResult.java
new file mode 100644
index 000000000..d7d8b5c8a
--- /dev/null
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/RerankResult.java
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.services.reranking;
+
+/**
+ * Represents a single reranking result containing a document and its relevance score.
+ */
+public final class RerankResult {
+ private final int index;
+ private final String text;
+ private final double relevanceScore;
+
+ /**
+ * Initializes a new instance of the {@link RerankResult} class.
+ *
+ * @param index The index of the document in the original input list
+ * @param text The document text
+ * @param relevanceScore The relevance score (higher scores indicate greater relevance)
+ */
+ public RerankResult(int index, String text, double relevanceScore) {
+ if (text == null) {
+ throw new IllegalArgumentException("Text cannot be null");
+ }
+ this.index = index;
+ this.text = text;
+ this.relevanceScore = relevanceScore;
+ }
+
+ /**
+ * Gets the index of the document in the original input list.
+ *
+ * @return The index
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Gets the document text.
+ *
+ * @return The text
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Gets the relevance score assigned by the reranker.
+ * Higher scores indicate greater relevance to the query.
+ *
+ * @return The relevance score
+ */
+ public double getRelevanceScore() {
+ return relevanceScore;
+ }
+}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/TextRerankingService.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/TextRerankingService.java
new file mode 100644
index 000000000..84fe66c08
--- /dev/null
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/services/reranking/TextRerankingService.java
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All rights reserved.
+package com.microsoft.semantickernel.services.reranking;
+
+import com.microsoft.semantickernel.services.AIService;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * Interface for text reranking services that can reorder documents based on relevance to a query.
+ */
+public interface TextRerankingService extends AIService {
+
+ /**
+ * Reranks a list of documents based on their relevance to a query.
+ *
+ * @param query The query to rank documents against
+ * @param documents The list of documents to rerank
+ * @return A Mono containing a list of {@link RerankResult} sorted by relevance score in descending order
+ */
+ Mono> rerankAsync(String query, List documents);
+}
diff --git a/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java b/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java
index 460e48159..bd8df1383 100644
--- a/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java
+++ b/semantickernel-api/src/main/java/com/microsoft/semantickernel/templateengine/handlebars/HandlebarsPromptTemplate.java
@@ -18,13 +18,12 @@
import com.microsoft.semantickernel.orchestration.InvocationContext;
import com.microsoft.semantickernel.orchestration.ToolCallBehavior;
import com.microsoft.semantickernel.plugin.KernelPlugin;
-import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.KernelArguments;
+import com.microsoft.semantickernel.semanticfunctions.KernelFunction;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateOption;
import com.microsoft.semantickernel.services.chatcompletion.ChatMessageContent;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -192,7 +191,6 @@ private class HandleBarsPromptTemplateHandler {
private final String template;
private final Handlebars handlebars;
- @SuppressFBWarnings("CT_CONSTRUCTOR_THROW") // Think this is a false positive
public HandleBarsPromptTemplateHandler(
Kernel kernel,
String template,
diff --git a/semantickernel-bom/pom.xml b/semantickernel-bom/pom.xml
index 372e04c1f..d0eb376b7 100644
--- a/semantickernel-bom/pom.xml
+++ b/semantickernel-bom/pom.xml
@@ -5,7 +5,7 @@
com.microsoft.semantic-kernel
semantickernel-bom
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
pom
Semantic Kernel Java BOM
@@ -13,7 +13,7 @@
https://www.github.com/microsoft/semantic-kernel
- 2.18.0
+ 2.22.1
@@ -52,7 +52,7 @@
io.opentelemetry
opentelemetry-bom
- 1.43.0
+ 1.61.0
pom
import
@@ -150,17 +150,17 @@
com.azure
azure-identity
- 1.14.0
+ 1.18.2
com.azure
azure-core
- 1.53.0
+ 1.57.1
com.azure
azure-search-documents
- 11.8.0-beta.1
+ 11.8.1
com.azure
@@ -171,13 +171,13 @@
redis.clients
jedis
- 5.2.0
+ 7.4.1
com.fasterxml.jackson.core
jackson-annotations
- ${com.fasterxml.jackson.core.version}
+ 2.21
com.fasterxml.jackson.core
@@ -197,7 +197,7 @@
${com.fasterxml.jackson.core.version}
runtime
-
+
com.github.jknack
handlebars
@@ -211,7 +211,7 @@
org.slf4j
slf4j-api
- 2.0.16
+ 2.0.17
com.google.code.findbugs
@@ -230,36 +230,36 @@
com.github.spotbugs
spotbugs-annotations
- 4.8.6
+ 4.9.8
org.apache.commons
commons-text
- 1.14.0
+ 1.15.0
com.google.cloud
google-cloud-vertexai
- 1.12.0
+ 1.52.0
com.github.victools
jsonschema-generator
- 4.36.0
+ 4.38.0
true
com.github.victools
jsonschema-module-jackson
- 4.36.0
+ 4.38.0
true
io.projectreactor
reactor-core
- 3.7.8
+ 3.8.5
diff --git a/semantickernel-experimental/pom.xml b/semantickernel-experimental/pom.xml
index 5cdbd5349..63031771a 100644
--- a/semantickernel-experimental/pom.xml
+++ b/semantickernel-experimental/pom.xml
@@ -4,7 +4,7 @@
com.microsoft.semantic-kernel
semantickernel-parent
- 1.4.4-RC3-SNAPSHOT
+ 1.5.1-SNAPSHOT
semantickernel-experimental
@@ -115,12 +115,12 @@
org.postgresql
postgresql
- 42.7.7
+ 42.7.10
org.xerial
sqlite-jdbc
- 3.47.0.0
+ 3.53.0.0