mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Java: sample Example07_BingAndGooglePlugins (#4685)
Add Example07_BingAndGooglePlugins
This commit is contained in:
+35
-9
@@ -7,6 +7,7 @@ import com.azure.ai.openai.models.ChatCompletionsFunctionToolCall;
|
||||
import com.azure.ai.openai.models.ChatCompletionsFunctionToolDefinition;
|
||||
import com.azure.ai.openai.models.ChatCompletionsOptions;
|
||||
import com.azure.ai.openai.models.ChatCompletionsToolCall;
|
||||
import com.azure.ai.openai.models.ChatCompletionsToolDefinition;
|
||||
import com.azure.ai.openai.models.ChatRequestAssistantMessage;
|
||||
import com.azure.ai.openai.models.ChatRequestMessage;
|
||||
import com.azure.ai.openai.models.ChatRequestSystemMessage;
|
||||
@@ -28,6 +29,7 @@ import com.microsoft.semantickernel.exceptions.AIException;
|
||||
import com.microsoft.semantickernel.orchestration.FunctionResultMetadata;
|
||||
import com.microsoft.semantickernel.orchestration.KernelFunction;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.orchestration.ToolCallBehavior;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariable;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
@@ -251,26 +253,26 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
List<ChatRequestMessage> chatRequestMessages,
|
||||
List<FunctionDefinition> functions,
|
||||
PromptExecutionSettings promptExecutionSettings) {
|
||||
|
||||
ChatCompletionsOptions options = new ChatCompletionsOptions(chatRequestMessages)
|
||||
.setModel(chatCompletionService.getModelId());
|
||||
|
||||
if (functions != null && !functions.isEmpty()) {
|
||||
// options.setFunctions(functions);
|
||||
options.setTools(
|
||||
functions.stream()
|
||||
.map(ChatCompletionsFunctionToolDefinition::new)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
if (promptExecutionSettings == null) {
|
||||
return options;
|
||||
}
|
||||
|
||||
if (promptExecutionSettings.getResultsPerPrompt() < 1
|
||||
|| promptExecutionSettings.getResultsPerPrompt() > MAX_RESULTS_PER_PROMPT) {
|
||||
throw new AIException(AIException.ErrorCodes.INVALID_REQUEST, String.format("Results per prompt must be in range between 1 and %d, inclusive.", MAX_RESULTS_PER_PROMPT));
|
||||
}
|
||||
|
||||
List<ChatCompletionsToolDefinition> toolDefinitions =
|
||||
chatCompletionsToolDefinitions(promptExecutionSettings.getToolCallBehavior(), functions);
|
||||
if (toolDefinitions != null && !toolDefinitions.isEmpty()) {
|
||||
options.setTools(toolDefinitions);
|
||||
// TODO: options.setToolChoices(toolChoices);
|
||||
}
|
||||
|
||||
Map<String, Integer> logit = null;
|
||||
if (promptExecutionSettings.getTokenSelectionBiases() != null) {
|
||||
logit = promptExecutionSettings
|
||||
@@ -302,6 +304,30 @@ public class OpenAIChatCompletion implements ChatCompletionService {
|
||||
return options;
|
||||
}
|
||||
|
||||
private static List<ChatCompletionsToolDefinition> chatCompletionsToolDefinitions(
|
||||
ToolCallBehavior toolCallBehavior,
|
||||
List<FunctionDefinition> functions) {
|
||||
|
||||
if (functions == null || functions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (toolCallBehavior == null || !(toolCallBehavior.kernelFunctionsEnabled() || toolCallBehavior.autoInvokeEnabled())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return functions.stream()
|
||||
.filter(function -> {
|
||||
String[] parts = function.getName().split("-");
|
||||
String pluginName = parts.length > 0 ? parts[0] : "";
|
||||
String fnName = parts.length > 1 ? parts[1] : "";
|
||||
return toolCallBehavior.functionEnabled(pluginName, fnName);
|
||||
})
|
||||
.map(ChatCompletionsFunctionToolDefinition::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
private static List<ChatRequestMessage> getChatRequestMessages(ChatHistory chatHistory) {
|
||||
List<ChatMessageContent> messages = chatHistory.getMessages();
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-parent</artifactId>
|
||||
<version>1.0.0-alpha.1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>semantickernel-connectors-web-bing</artifactId>
|
||||
|
||||
<name>Semantic Kernel Bing Connector</name>
|
||||
<description>Defines Bing connectors to be used with the Semantic Kernel</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-api</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+38
-41
@@ -1,29 +1,27 @@
|
||||
package com.microsoft.semantickernel.plugins.web.bing;
|
||||
package com.microsoft.semantickernel.connectors.web.bing;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.azure.core.http.HttpClient;
|
||||
import com.azure.core.http.HttpHeader;
|
||||
import com.azure.core.http.HttpHeaderName;
|
||||
import com.azure.core.http.HttpHeaders;
|
||||
import com.azure.core.http.HttpMethod;
|
||||
import com.azure.core.http.HttpRequest;
|
||||
import com.azure.core.http.HttpResponse;
|
||||
import com.azure.core.implementation.jackson.ObjectMapperShim;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.microsoft.semantickernel.connectors.WebSearchEngineConnector;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class BingConnector {
|
||||
public class BingConnector implements WebSearchEngineConnector {
|
||||
|
||||
private static String BING_SEARCH_URL;
|
||||
static {
|
||||
@@ -54,7 +52,7 @@ public class BingConnector {
|
||||
private final WebPage[] value;
|
||||
@JsonCreator
|
||||
public WebPages(
|
||||
@JsonProperty("value") WebPage[] value
|
||||
@JsonProperty("value") BingWebPage[] value
|
||||
) {
|
||||
this.value = value;
|
||||
}
|
||||
@@ -64,52 +62,52 @@ public class BingConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public static class WebPage {
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
public static class BingWebPage implements WebPage {
|
||||
private final String name;
|
||||
private final String url;
|
||||
private final String snippet;
|
||||
|
||||
@JsonProperty("url")
|
||||
private String url;
|
||||
|
||||
@JsonProperty("snippet")
|
||||
private String snippet;
|
||||
|
||||
public WebPage() {
|
||||
@JsonCreator
|
||||
public BingWebPage(
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("url") String url,
|
||||
@JsonProperty("snippet") String snippet
|
||||
) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.snippet = snippet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setSnippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
}
|
||||
}
|
||||
|
||||
private final String apiKey; // TODO: secure this
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public BingConnector(String apiKey, HttpClient httpClient) {
|
||||
this.apiKey = apiKey;
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Mono<List<String>> searchAsync(String query, int count, int offset) {
|
||||
public BingConnector(String apiKey) {
|
||||
this(apiKey, HttpClient.createDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<List<WebPage>> searchAsync(String query, int count, int offset) {
|
||||
|
||||
if (count <= 0 || 50 <= count) throw new IllegalArgumentException("count must be between 1 and 50");
|
||||
if (offset < 0) throw new IllegalArgumentException("offset must be greater than or equal to 0");
|
||||
@@ -134,24 +132,23 @@ public class BingConnector {
|
||||
return url;
|
||||
}
|
||||
|
||||
private static Mono<List<String>> handleResponse(HttpResponse response) {
|
||||
private static Mono<List<WebPage>> handleResponse(HttpResponse response) {
|
||||
return response.getBodyAsString()
|
||||
.flatMap(body -> {
|
||||
if (body == null || body.isEmpty()) return Mono.empty();
|
||||
.map(body -> {
|
||||
if (body == null || body.isEmpty()) return null;
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false);
|
||||
BingSearchResponse bingSearchResponse = objectMapper.readValue(body, BingSearchResponse.class);
|
||||
List<String> urls = new ArrayList<String>();
|
||||
for (WebPage webPage : bingSearchResponse.getWebPages().getValue()) {
|
||||
urls.add(webPage.getUrl());
|
||||
|
||||
BingSearchResponse bingSearchResponse = objectMapper.readValue(body, BingSearchResponse.class);
|
||||
if (bingSearchResponse.getWebPages() != null && bingSearchResponse.getWebPages().getValue() != null) {
|
||||
return Arrays.asList(bingSearchResponse.getWebPages().getValue());
|
||||
}
|
||||
return Mono.just(urls);
|
||||
} catch (JsonProcessingException e) {
|
||||
Mono.error(e);
|
||||
throw new SKException(e.getMessage(), e);
|
||||
}
|
||||
return Mono.empty();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package com.microsoft.semantickernel.plugins.web;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
|
||||
public class SearchUrlPlugin {
|
||||
/*
|
||||
* Amazon Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for Amazon
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="AmazonSearchUrl", description="Return URL for Amazon search query")
|
||||
public String AmazonSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.amazon.com/s?k=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bing Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for Bing
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingSearchUrl", description="Return URL for Bing search query.")
|
||||
public String BingSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get search URL for Bing Images
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingImagesSearchUrl", description="Return URL for Bing Images search query.")
|
||||
public String BingImagesSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/images/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get search URL for Bing Maps
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingMapsSearchUrl", description="Return URL for Bing Maps search query.")
|
||||
public String BingMapsSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/maps?q=%s", encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get search URL for Bing Shopping
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingShoppingSearchUrl", description="Return URL for Bing Shopping search query.")
|
||||
public String BingShoppingSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/shop?q=%s", encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get search URL for Bing News
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingNewsSearchUrl", description="Return URL for Bing News search query.")
|
||||
public String BingNewsSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/news/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get search URL for Bing Travel
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="BingTravelSearchUrl", description="Return URL for Bing Travel search query.")
|
||||
public String BingTravelSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.bing.com/travel/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* Facebook Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for Facebook
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="FacebookSearchUrl", description="Return URL for Facebook search query.")
|
||||
public String FacebookSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.facebook.com/search/top/?q=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* GitHub Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for GitHub
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="GitHubSearchUrl", description="Return URL for GitHub search query.")
|
||||
public String GitHubSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://github.com/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* LinkedIn Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for LinkedIn
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="LinkedInSearchUrl", description="Return URL for LinkedIn search query.")
|
||||
public String LinkedInSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://www.linkedin.com/search/results/index/?keywords=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* Twitter Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for Twitter
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="TwitterSearchUrl", description="Return URL for Twitter search query.")
|
||||
public String TwitterSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://twitter.com/search?q=%s", encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wikipedia Search URLs
|
||||
*/
|
||||
/// <summary>
|
||||
/// Get search URL for Wikipedia
|
||||
/// </summary>
|
||||
@DefineKernelFunction(name="WikipediaSearchUrl", description="Return URL for Wikipedia search query.")
|
||||
public String WikipediaSearchUrl(@KernelFunctionParameter(description="Text to search for", name="query", required=true, type=String.class) String query)
|
||||
{
|
||||
String encoded = encode(query);
|
||||
return String.format("https://wikipedia.org/w/index.php?search=%s", encoded);
|
||||
}
|
||||
|
||||
private static String encode(String query) {
|
||||
try {
|
||||
return URLEncoder.encode(query, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// should never happen since UTF-8 is always supported
|
||||
return query;
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.plugins.web;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.microsoft.semantickernel.connectors.WebSearchEngineConnector;
|
||||
import com.microsoft.semantickernel.connectors.WebSearchEngineConnector.WebPage;
|
||||
import com.microsoft.semantickernel.exceptions.SKException;
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/// <summary>
|
||||
/// Web search engine plugin (e.g. Bing).
|
||||
/// </summary>
|
||||
public class WebSearchEnginePlugin {
|
||||
|
||||
/// <summary>
|
||||
/// The count parameter name.
|
||||
/// </summary>
|
||||
public static final String COUNT_PARAM = "count";
|
||||
|
||||
/// <summary>
|
||||
/// The offset parameter name.
|
||||
/// </summary>
|
||||
public static final String OFFSET_PARAM = "offset";
|
||||
|
||||
private final WebSearchEngineConnector connector;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebSearchEnginePlugin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connector">The web search engine connector.</param>
|
||||
public WebSearchEnginePlugin(WebSearchEngineConnector connector) {
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a web search using the provided query, count, and offset.
|
||||
/// </summary>
|
||||
/// <param name="query">The text to search for.</param>
|
||||
/// <param name="count">The number of results to return. Default is 1.</param>
|
||||
/// <param name="offset">The number of results to skip. Default is 0.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The value of the TResult parameter contains the search results as a string.</returns>
|
||||
/// <remarks>
|
||||
/// This method is marked as "unsafe." The usage of JavaScriptEncoder.UnsafeRelaxedJsonEscaping may introduce security risks.
|
||||
/// Only use this method if you are aware of the potential risks and have validated the input to prevent security vulnerabilities.
|
||||
/// </remarks>
|
||||
@DefineKernelFunction(name="search", description="Searches the web for the given query")
|
||||
public Mono<String> searchAsync(
|
||||
@KernelFunctionParameter(description="The search query", name="query", type=String.class) String query,
|
||||
@KernelFunctionParameter(description="The number of results to return", name="count", defaultValue = "1", type=int.class) int count,
|
||||
@KernelFunctionParameter(description="The number of results to skip", name="offset", defaultValue = "0", type=int.class) int offset)
|
||||
{
|
||||
|
||||
return connector.searchAsync(query, count, offset).map(results -> {
|
||||
|
||||
if (results == null || results.isEmpty()) {
|
||||
throw new SKException("Failed to get a response from the web search engine.");
|
||||
}
|
||||
|
||||
return count == 1
|
||||
? results.get(0).getSnippet()
|
||||
// TODO: .NET code does `JsonSerializer.Serialize(results, s_jsonOptionsCache)` here
|
||||
// The joiner results in "[\"result1\",\"result2\"]"
|
||||
: results.stream()
|
||||
.limit(count)
|
||||
.map(WebPage::getSnippet)
|
||||
.collect(Collectors.joining("\",\"", "[\"", "\"]"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>connectors/semantickernel-connectors-web-bing</module>
|
||||
<!-- <module>connectors/semantickernel-connectors-ai-openai</module>-->
|
||||
<!-- <module>connectors/semantickernel-connectors-memory-sqlite</module>-->
|
||||
<!-- <module>connectors/semantickernel-connectors-memory-azurecognitivesearch</module>-->
|
||||
@@ -150,6 +151,11 @@
|
||||
<artifactId>semantickernel-actionplanner-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-connectors-web-bing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-plugin-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-connectors-web-bing</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.samples.syntaxexamples;
|
||||
|
||||
import com.azure.ai.openai.OpenAIAsyncClient;
|
||||
import com.azure.ai.openai.OpenAIClientBuilder;
|
||||
import com.azure.core.credential.AzureKeyCredential;
|
||||
import com.azure.core.credential.KeyCredential;
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.chatcompletion.ChatCompletionService;
|
||||
import com.microsoft.semantickernel.connectors.web.bing.BingConnector;
|
||||
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
|
||||
import com.microsoft.semantickernel.plugins.web.WebSearchEnginePlugin;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelFunctionFromPrompt;
|
||||
import com.microsoft.semantickernel.semanticfunctions.KernelPromptTemplateFactory;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
|
||||
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The example shows how to use Bing and Google to search for current data
|
||||
/// you might want to import into your system, e.g. providing AI prompts with
|
||||
/// recent information, or for AI to generate recent information to display to users.
|
||||
/// </summary>
|
||||
public class Example07_BingAndGooglePlugins {
|
||||
|
||||
private static final String CLIENT_KEY = System.getenv("CLIENT_KEY");
|
||||
private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY");
|
||||
|
||||
// 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-3.5-turbo");
|
||||
|
||||
private static final String BING_API_KEY = System.getenv("BING_API_KEY");
|
||||
private static final String GOOGLE_API_KEY = System.getenv("GOOGLE_API_KEY");
|
||||
private static final String GOOGLE_SEARCH_ENGINE_ID = System.getenv("GOOGLE_SEARCH_ENGINE_ID");
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
OpenAIAsyncClient client;
|
||||
|
||||
if (AZURE_CLIENT_KEY != null) {
|
||||
client = new OpenAIClientBuilder()
|
||||
.credential(new AzureKeyCredential(AZURE_CLIENT_KEY))
|
||||
.endpoint(CLIENT_ENDPOINT)
|
||||
.buildAsyncClient();
|
||||
|
||||
} else {
|
||||
client = new OpenAIClientBuilder()
|
||||
.credential(new KeyCredential(CLIENT_KEY))
|
||||
.buildAsyncClient();
|
||||
}
|
||||
|
||||
// Load Bing plugin
|
||||
var bingConnector = new BingConnector(BING_API_KEY);
|
||||
var bing = KernelPluginFactory.createFromObject(new WebSearchEnginePlugin(bingConnector), "bing");
|
||||
|
||||
var chatCompletionService = ChatCompletionService.builder()
|
||||
.withOpenAIAsyncClient(client)
|
||||
.withModelId(MODEL_ID)
|
||||
.build();
|
||||
|
||||
var kernel = Kernel.builder()
|
||||
.withPlugin(bing)
|
||||
.withAIService(ChatCompletionService.class, chatCompletionService)
|
||||
.build();
|
||||
|
||||
example1Async(kernel, "bing");
|
||||
example2Async(kernel);
|
||||
|
||||
// Load Google plugin
|
||||
// WebSearchEngineConnector googleConnector = new GoogleConnector(GOOGLE_API_KEY, GOOGLE_SEARCH_ENGINE_ID);
|
||||
// WebSearchEnginePlugin bing = new WebSearchEnginePlugin(googleConnector);
|
||||
// kernel.importPluginFromObject(new WebSearchEnginePlugin(googleConnector), "google");
|
||||
}
|
||||
|
||||
private static void example1Async(Kernel kernel, String searchPluginName)
|
||||
{
|
||||
System.out.println("======== Bing and Google Search Plugins ========");
|
||||
|
||||
// Run
|
||||
var question = "What's the largest building in the world?";
|
||||
var kernelArguments = KernelArguments.builder()
|
||||
.withVariable("query", question)
|
||||
.build();
|
||||
|
||||
var function = kernel.getPlugins().getFunction(searchPluginName, "search");
|
||||
var result = kernel.invokeAsync(function, kernelArguments, String.class).block();
|
||||
|
||||
System.out.println(question);
|
||||
System.out.printf("----%s----%n", searchPluginName);
|
||||
System.out.println(result.getResult());
|
||||
|
||||
/* OUTPUT:
|
||||
|
||||
What's the largest building in the world?
|
||||
----
|
||||
The Aerium near Berlin, Germany is the largest uninterrupted volume in the world, while Boeing's
|
||||
factory in Everett, Washington, United States is the world's largest building by volume. The AvtoVAZ
|
||||
main assembly building in Tolyatti, Russia is the largest building in area footprint.
|
||||
----
|
||||
The Aerium near Berlin, Germany is the largest uninterrupted volume in the world, while Boeing's
|
||||
factory in Everett, Washington, United States is the world's ...
|
||||
*/
|
||||
}
|
||||
|
||||
private static void example2Async(Kernel kernel)
|
||||
{
|
||||
System.out.println("======== Use Search Plugin to answer user questions ========");
|
||||
|
||||
var semanticFunction = """
|
||||
Answer questions only when you know the facts or the information is provided.
|
||||
When you don't have sufficient information you reply with a list of commands to find the information needed.
|
||||
When answering multiple questions, use a bullet point list.
|
||||
Note: make sure single and double quotes are escaped using a backslash char.
|
||||
|
||||
[COMMANDS AVAILABLE]
|
||||
- bing.search
|
||||
|
||||
[INFORMATION PROVIDED]
|
||||
{{ $externalInformation }}
|
||||
|
||||
[EXAMPLE 1]
|
||||
Question: what's the biggest lake in Italy?
|
||||
Answer: Lake Garda, also known as Lago di Garda.
|
||||
|
||||
[EXAMPLE 2]
|
||||
Question: what's the biggest lake in Italy? What's the smallest positive number?
|
||||
Answer:
|
||||
* Lake Garda, also known as Lago di Garda.
|
||||
* The smallest positive number is 1.
|
||||
|
||||
[EXAMPLE 3]
|
||||
Question: what's Ferrari stock price? Who is the current number one female tennis player in the world?
|
||||
Answer:
|
||||
{{ '{{' }} bing.search "what's Ferrari stock price?" {{ '}}' }}.
|
||||
{{ '{{' }} bing.search "Who is the current number one female tennis player in the world?" {{ '}}' }}.
|
||||
|
||||
[END OF EXAMPLES]
|
||||
|
||||
[TASK]
|
||||
Question: {{ $question }}.
|
||||
Answer:
|
||||
""".stripIndent();
|
||||
|
||||
// The prompt function will append the answer here
|
||||
var question = "Who is the most followed person on TikTok right now? What's the exchange rate EUR:USD?";
|
||||
System.out.println(question);
|
||||
|
||||
var promptExecutionSettings = PromptExecutionSettings.builder()
|
||||
.withMaxTokens(150)
|
||||
.withTemperature(0)
|
||||
.withTopP(1)
|
||||
.build();
|
||||
|
||||
var oracle = KernelFunctionFromPrompt.builder()
|
||||
.withTemplate(semanticFunction)
|
||||
.withDefaultExecutionSettings(promptExecutionSettings)
|
||||
.build();
|
||||
|
||||
var kernelArguments = KernelArguments.builder()
|
||||
.withVariable("question", question)
|
||||
.withVariable("externalInformation", "")
|
||||
.build();
|
||||
|
||||
var answer = kernel.invokeAsync(oracle, kernelArguments, String.class).block();
|
||||
|
||||
var result = answer.getResult();
|
||||
|
||||
// If the answer contains commands, execute them using the prompt renderer.
|
||||
if (result.contains("bing.search"))
|
||||
{
|
||||
PromptTemplate promptTemplate = new KernelPromptTemplateFactory()
|
||||
.tryCreate(new PromptTemplateConfig(result));
|
||||
|
||||
System.out.println("---- Fetching information from Bing...");
|
||||
var information = promptTemplate.renderAsync(kernel, null).block();
|
||||
|
||||
System.out.println("Information found:");
|
||||
System.out.println(information);
|
||||
|
||||
kernelArguments = KernelArguments.builder()
|
||||
.withVariable("question", question)
|
||||
.withVariable("externalInformation", information)
|
||||
.build();
|
||||
|
||||
// Run the prompt function again, now including information from Bing
|
||||
answer = kernel.invokeAsync(oracle, kernelArguments, String.class).block();
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("AI had all the information, no need to query Bing.");
|
||||
}
|
||||
|
||||
System.out.println("---- ANSWER:");
|
||||
System.out.println(answer.getResult());
|
||||
|
||||
/* OUTPUT:
|
||||
|
||||
Who is the most followed person on TikTok right now? What's the exchange rate EUR:USD?
|
||||
---- Fetching information from Bing...
|
||||
Information found:
|
||||
|
||||
Khaby Lame is the most-followed user on TikTok. This list contains the top 50 accounts by number
|
||||
of followers on the Chinese social media platform TikTok, which was merged with musical.ly in 2018.
|
||||
[1] The most-followed individual on the platform is Khaby Lame, with over 153 million followers..
|
||||
EUR – Euro To USD – US Dollar 1.00 Euro = 1.10 37097 US Dollars 1 USD = 0.906035 EUR We use the
|
||||
mid-market rate for our Converter. This is for informational purposes only. You won’t receive this
|
||||
rate when sending money. Check send rates Convert Euro to US Dollar Convert US Dollar to Euro..
|
||||
---- ANSWER:
|
||||
|
||||
* The most followed person on TikTok right now is Khaby Lame, with over 153 million followers.
|
||||
* The exchange rate for EUR to USD is 1.1037097 US Dollars for 1 Euro.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.microsoft.semantickernel.samples.syntaxexamples;
|
||||
|
||||
import com.microsoft.semantickernel.Kernel;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.KernelArguments;
|
||||
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
|
||||
import com.microsoft.semantickernel.plugins.web.SearchUrlPlugin;
|
||||
|
||||
public class Example11_WebSearchQueries {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
var kernel = Kernel.builder().build();
|
||||
|
||||
// Load native plugins
|
||||
var searchUrlPlugin = KernelPluginFactory.createFromObject(new SearchUrlPlugin(), "SearchUrlPlugin");
|
||||
var bingSearchFunction = searchUrlPlugin.get("BingSearchUrl");
|
||||
|
||||
// Run
|
||||
var ask = "What's the largest building in Europe?";
|
||||
var kernelArguments = KernelArguments.builder()
|
||||
.withVariable("query", ask)
|
||||
.build();
|
||||
|
||||
var result = kernel.invokeAsync(bingSearchFunction, kernelArguments, String.class).block();
|
||||
|
||||
System.out.println(ask);
|
||||
System.out.println(result.getResult());
|
||||
|
||||
/* Expected output:
|
||||
* ======== WebSearchQueries ========
|
||||
* What's the tallest building in Europe?
|
||||
*
|
||||
* https://www.bing.com/search?q=What%27s%20the%20tallest%20building%20in%20Europe%3F
|
||||
* == DONE ==
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package plugins.searchplugin;
|
||||
import com.azure.core.http.HttpClient;
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
import com.microsoft.semantickernel.plugins.web.bing.BingConnector;
|
||||
import com.microsoft.semantickernel.connectors.web.bing.BingConnector;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -25,7 +25,9 @@ public class Search {
|
||||
public Mono<String> searchAsync(
|
||||
@KernelFunctionParameter(description="The search query", name="query", type=String.class) String query
|
||||
){
|
||||
return bingConnector.searchAsync(query, 1, 0).map(results -> results.get(0));
|
||||
return bingConnector
|
||||
.searchAsync(query, 1, 0).map(results -> results.get(0))
|
||||
.map(result -> result.getUrl());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>aiservices-azureopenai</artifactId>
|
||||
<artifactId>aiservices-openai</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
|
||||
@@ -47,12 +47,16 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>aiservices-azureopenai</artifactId>
|
||||
<artifactId>aiservices-openai</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-plugin-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-connectors-web-bing</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>aiservices-openai</artifactId>
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.connectors;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface WebSearchEngineConnector {
|
||||
|
||||
interface WebPage {
|
||||
String getName();
|
||||
String getUrl();
|
||||
String getSnippet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a web search engine search.
|
||||
* @param query Query to search.
|
||||
* @param count Number of results. Defaults to 1. Must be between 1 and 50.
|
||||
* @param offset Number of results to skip. Defaults to 0.
|
||||
* @return First snippet returned from search.
|
||||
*/
|
||||
Mono<List<WebPage>> searchAsync(String query, int count, int offset);
|
||||
|
||||
}
|
||||
+3
-3
@@ -15,14 +15,14 @@ public class KernelFunctionMetadata {
|
||||
|
||||
|
||||
/// <summary>The function's return parameter.</summary>
|
||||
private final KernelReturnParameterMetadata returnParameter;
|
||||
private final KernelReturnParameterMetadata<?> returnParameter;
|
||||
|
||||
|
||||
public KernelFunctionMetadata(
|
||||
String name,
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata returnParameter) {
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.parameters = parameters;
|
||||
@@ -41,7 +41,7 @@ public class KernelFunctionMetadata {
|
||||
return description;
|
||||
}
|
||||
|
||||
public KernelReturnParameterMetadata getReturnParameter() {
|
||||
public KernelReturnParameterMetadata<?> getReturnParameter() {
|
||||
return returnParameter;
|
||||
}
|
||||
}
|
||||
|
||||
+145
-64
@@ -1,20 +1,41 @@
|
||||
package com.microsoft.semantickernel.orchestration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class PromptExecutionSettings {
|
||||
|
||||
public static final String DEFAULT_SERVICE_ID = "default";
|
||||
public static final int DEFAULT_MAX_TOKENS = 256;
|
||||
public static final double DEFAULT_TEMPERATURE = 1.0;
|
||||
public static final double DEFAULT_TEMPERATURE= 1.0;
|
||||
public static final double DEFAULT_TOP_P = 1.0;
|
||||
public static final double DEFAULT_PRESENCE_PENALTY = 0.0;
|
||||
public static final double DEFAULT_FREQUENCY_PENALTY = 0.0;
|
||||
public static final int DEFAULT_BEST_OF = 1;
|
||||
public static final int DEFAULT_RESULTS_PER_PROMPT = 1;
|
||||
|
||||
private static final String SERVICE_ID = "service_id";
|
||||
private static final String MODEL_ID = "model_id";
|
||||
private static final String TEMPERATURE = "temperature";
|
||||
private static final String TOP_P = "top_p";
|
||||
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 BEST_OF = "best_of";
|
||||
private static final String USER = "user";
|
||||
private static final String STOP_SEQUENCES = "stop_sequences";
|
||||
private static final String RESULTS_PER_PROMPT = "results_per_prompt";
|
||||
private static final String TOKEN_SELECTION_BIASES = "token_selection_biases";
|
||||
|
||||
/// <summary>
|
||||
/// Service identifier.
|
||||
/// This identifies a service and is set when the AI service is registered.
|
||||
@@ -28,7 +49,6 @@ public class PromptExecutionSettings {
|
||||
private final String modelId;
|
||||
|
||||
private final double temperature;
|
||||
|
||||
private final double topP;
|
||||
private final double presencePenalty;
|
||||
private final double frequencyPenalty;
|
||||
@@ -37,182 +57,243 @@ public class PromptExecutionSettings {
|
||||
private final int resultsPerPrompt;
|
||||
private final String user;
|
||||
private final List<String> stopSequences;
|
||||
private final ToolCallBehavior toolCallBehavior;
|
||||
|
||||
/// <summary>
|
||||
/// Modify the likelihood of specified tokens appearing in the completion.
|
||||
/// </summary>s
|
||||
public Map<Integer, Integer> tokenSelectionBiases;
|
||||
|
||||
@JsonCreator
|
||||
public PromptExecutionSettings(
|
||||
@JsonProperty("service_id") String serviceId,
|
||||
@JsonProperty("model_id") String modelId,
|
||||
@JsonProperty("temperature") double temperature,
|
||||
@JsonProperty("top_p") double topP,
|
||||
@JsonProperty("presence_penalty") double presencePenalty,
|
||||
@JsonProperty("frequency_penalty") double frequencyPenalty,
|
||||
@JsonProperty("max_tokens") int maxTokens,
|
||||
@JsonProperty("results_per_prompt") int resultsPerPrompt,
|
||||
@JsonProperty("best_of") int bestOf,
|
||||
@JsonProperty("user") String user,
|
||||
@JsonProperty(value = "stop_sequences") List<String> stopSequences,
|
||||
@JsonProperty(value = "token_selection_biases") Map<Integer, Integer> tokenSelectionBiases) {
|
||||
this.serviceId = serviceId;
|
||||
this.modelId = modelId;
|
||||
this.temperature = temperature;
|
||||
this.topP = topP;
|
||||
this.presencePenalty = presencePenalty;
|
||||
this.frequencyPenalty = frequencyPenalty;
|
||||
this.maxTokens = maxTokens;
|
||||
this.resultsPerPrompt = resultsPerPrompt;
|
||||
this.bestOf = bestOf;
|
||||
this.user = user;
|
||||
this.stopSequences = stopSequences;
|
||||
this.tokenSelectionBiases = tokenSelectionBiases;
|
||||
@JsonProperty(SERVICE_ID) String serviceId,
|
||||
@JsonProperty(MODEL_ID) String modelId,
|
||||
@JsonProperty(TEMPERATURE) double temperature,
|
||||
@JsonProperty(TOP_P) double topP,
|
||||
@JsonProperty(PRESENCE_PENALTY) double presencePenalty,
|
||||
@JsonProperty(FREQUENCY_PENALTY) double frequencyPenalty,
|
||||
@JsonProperty(MAX_TOKENS) int maxTokens,
|
||||
@JsonProperty(RESULTS_PER_PROMPT) int resultsPerPrompt,
|
||||
@JsonProperty(BEST_OF) int bestOf,
|
||||
@JsonProperty(USER) String user,
|
||||
@JsonProperty(STOP_SEQUENCES) List<String> stopSequences,
|
||||
@JsonProperty(TOKEN_SELECTION_BIASES) Map<Integer, Integer> tokenSelectionBiases) {
|
||||
this(
|
||||
serviceId,
|
||||
modelId,
|
||||
temperature,
|
||||
topP,
|
||||
presencePenalty,
|
||||
frequencyPenalty,
|
||||
maxTokens,
|
||||
resultsPerPrompt,
|
||||
bestOf,
|
||||
user,
|
||||
stopSequences,
|
||||
tokenSelectionBiases,
|
||||
null);
|
||||
}
|
||||
|
||||
public PromptExecutionSettings(
|
||||
String serviceId,
|
||||
String modelId,
|
||||
double temperature,
|
||||
double topP,
|
||||
double presencePenalty,
|
||||
double frequencyPenalty,
|
||||
int maxTokens,
|
||||
int resultsPerPrompt,
|
||||
int bestOf,
|
||||
String user,
|
||||
@Nullable List<String> stopSequences,
|
||||
@Nullable Map<Integer, Integer> tokenSelectionBiases,
|
||||
@Nullable ToolCallBehavior toolCallBehavior) {
|
||||
this.serviceId = serviceId;
|
||||
this.modelId = modelId;
|
||||
this.temperature = temperature;
|
||||
this.topP = topP;
|
||||
this.presencePenalty = presencePenalty;
|
||||
this.frequencyPenalty = frequencyPenalty;
|
||||
this.maxTokens = maxTokens;
|
||||
this.resultsPerPrompt = resultsPerPrompt;
|
||||
this.bestOf = bestOf;
|
||||
this.user = user;
|
||||
this.stopSequences = stopSequences != null ? stopSequences : Collections.emptyList();
|
||||
this.tokenSelectionBiases = tokenSelectionBiases != null ? tokenSelectionBiases : Collections.emptyMap();
|
||||
this.toolCallBehavior = toolCallBehavior;
|
||||
}
|
||||
|
||||
@JsonProperty(SERVICE_ID)
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@JsonProperty(MODEL_ID)
|
||||
public String getModelId() {
|
||||
return modelId;
|
||||
}
|
||||
|
||||
@JsonProperty(TEMPERATURE)
|
||||
public double getTemperature() {
|
||||
return Double.isNaN(temperature) ? DEFAULT_TEMPERATURE : temperature;
|
||||
}
|
||||
|
||||
@JsonProperty(TOP_P)
|
||||
public double getTopP() {
|
||||
return topP;
|
||||
}
|
||||
|
||||
@JsonProperty(PRESENCE_PENALTY)
|
||||
public double getPresencePenalty() {
|
||||
return presencePenalty;
|
||||
}
|
||||
|
||||
@JsonProperty(FREQUENCY_PENALTY)
|
||||
public double getFrequencyPenalty() {
|
||||
return frequencyPenalty;
|
||||
}
|
||||
|
||||
@JsonProperty(MAX_TOKENS)
|
||||
public int getMaxTokens() {
|
||||
return maxTokens;
|
||||
}
|
||||
|
||||
@JsonProperty(RESULTS_PER_PROMPT)
|
||||
public int getResultsPerPrompt() {
|
||||
return resultsPerPrompt;
|
||||
}
|
||||
|
||||
@JsonProperty(BEST_OF)
|
||||
public int getBestOf() {
|
||||
// TODO: not present in com.azure:azure-ai-openai
|
||||
return bestOf;
|
||||
}
|
||||
|
||||
@JsonProperty(USER)
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@JsonProperty(STOP_SEQUENCES)
|
||||
public List<String> getStopSequences() {
|
||||
return stopSequences;
|
||||
}
|
||||
|
||||
|
||||
@JsonProperty(TOKEN_SELECTION_BIASES)
|
||||
public Map<Integer, Integer> getTokenSelectionBiases() {
|
||||
return tokenSelectionBiases;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Nullable
|
||||
public ToolCallBehavior getToolCallBehavior() {
|
||||
return toolCallBehavior;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String serviceId;
|
||||
private String modelId;
|
||||
private double temperature = Double.NaN;
|
||||
private double topP = Double.NaN;
|
||||
private double presencePenalty = Double.NaN;
|
||||
private double frequencyPenalty = Double.NaN;
|
||||
private int maxTokens = Integer.MIN_VALUE;
|
||||
private int resultsPerPrompt = Integer.MIN_VALUE;
|
||||
private int bestOf = Integer.MIN_VALUE;
|
||||
private String user;
|
||||
private List<String> stopSequences;
|
||||
public Map<Integer, Integer> tokenSelectionBiases;
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
|
||||
public Builder withServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
settings.put(SERVICE_ID, serviceId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withModelId(String modelId) {
|
||||
this.modelId = modelId;
|
||||
settings.put(MODEL_ID, modelId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTemperature(double temperature) {
|
||||
this.temperature = temperature;
|
||||
if (!Double.isNaN(temperature)) {
|
||||
settings.put(TEMPERATURE, temperature);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTopP(double topP) {
|
||||
this.topP = topP;
|
||||
if (!Double.isNaN(topP)) {
|
||||
settings.put(TOP_P, topP);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPresencePenalty(double presencePenalty) {
|
||||
this.presencePenalty = presencePenalty;
|
||||
if (!Double.isNaN(presencePenalty)) {
|
||||
settings.put(PRESENCE_PENALTY, presencePenalty);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withFrequencyPenalty(double frequencyPenalty) {
|
||||
this.frequencyPenalty = frequencyPenalty;
|
||||
if (!Double.isNaN(frequencyPenalty)) {
|
||||
settings.put(FREQUENCY_PENALTY, frequencyPenalty);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withMaxTokens(int maxTokens) {
|
||||
this.maxTokens = maxTokens;
|
||||
settings.put(MAX_TOKENS, maxTokens);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withResultsPerPrompt(int resultsPerPrompt) {
|
||||
this.resultsPerPrompt = resultsPerPrompt;
|
||||
settings.put(RESULTS_PER_PROMPT, resultsPerPrompt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withBestOf(int bestOf) {
|
||||
this.bestOf = bestOf;
|
||||
settings.put(BEST_OF, bestOf);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withUser(String user) {
|
||||
this.user = user;
|
||||
settings.put(USER, user);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withToolCallBehavior(ToolCallBehavior toolCallBehavior) {
|
||||
settings.put("toolCallBehavior", toolCallBehavior);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Builder withStopSequences(List<String> stopSequences) {
|
||||
this.stopSequences = stopSequences;
|
||||
if (stopSequences != null) {
|
||||
((List<String>)settings.computeIfAbsent(STOP_SEQUENCES, k -> new ArrayList<>())).addAll(stopSequences);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Builder withTokenSelectionBiases(Map<Integer, Integer> tokenSelectionBiases) {
|
||||
this.tokenSelectionBiases = tokenSelectionBiases;
|
||||
if (tokenSelectionBiases != null) {
|
||||
((Map<Integer, Integer>)settings.computeIfAbsent(TOKEN_SELECTION_BIASES, k -> new HashMap<>())).putAll(tokenSelectionBiases);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PromptExecutionSettings build() {
|
||||
return new PromptExecutionSettings(
|
||||
serviceId,
|
||||
modelId,
|
||||
Double.isNaN(temperature) ? DEFAULT_TEMPERATURE : temperature,
|
||||
Double.isNaN(topP) ? DEFAULT_TOP_P : topP,
|
||||
Double.isNaN(presencePenalty) ? DEFAULT_PRESENCE_PENALTY : presencePenalty,
|
||||
Double.isNaN(frequencyPenalty) ? DEFAULT_FREQUENCY_PENALTY : frequencyPenalty,
|
||||
maxTokens == Integer.MIN_VALUE ? DEFAULT_MAX_TOKENS : maxTokens,
|
||||
resultsPerPrompt == Integer.MIN_VALUE ? DEFAULT_RESULTS_PER_PROMPT : resultsPerPrompt,
|
||||
bestOf == Integer.MIN_VALUE ? DEFAULT_BEST_OF : bestOf,
|
||||
user,
|
||||
stopSequences,
|
||||
tokenSelectionBiases
|
||||
(String)settings.getOrDefault(SERVICE_ID, ""),
|
||||
(String)settings.getOrDefault(MODEL_ID, ""),
|
||||
(double)settings.getOrDefault(TEMPERATURE, DEFAULT_TEMPERATURE),
|
||||
(double)settings.getOrDefault(TOP_P, DEFAULT_TOP_P),
|
||||
(double)settings.getOrDefault(PRESENCE_PENALTY, DEFAULT_PRESENCE_PENALTY),
|
||||
(double)settings.getOrDefault(FREQUENCY_PENALTY, DEFAULT_FREQUENCY_PENALTY),
|
||||
(int)settings.getOrDefault(MAX_TOKENS, DEFAULT_MAX_TOKENS),
|
||||
(int)settings.getOrDefault(RESULTS_PER_PROMPT, DEFAULT_RESULTS_PER_PROMPT),
|
||||
(int)settings.getOrDefault(BEST_OF, DEFAULT_BEST_OF),
|
||||
(String)settings.getOrDefault(USER, ""),
|
||||
(List<String>)settings.getOrDefault(STOP_SEQUENCES, Collections.emptyList()),
|
||||
(Map<Integer, Integer>)settings.getOrDefault(TOKEN_SELECTION_BIASES, Collections.emptyMap()),
|
||||
(ToolCallBehavior)settings.getOrDefault("toolCallBehavior", new ToolCallBehavior())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package com.microsoft.semantickernel.orchestration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Defines the behavior of a tool call. Currently, the only tool available is function calling.
|
||||
*/
|
||||
public class ToolCallBehavior {
|
||||
|
||||
private static final int DEFAULT_MAXIMUM_AUTO_INVOKE_ATTEMPTS = 5;
|
||||
|
||||
private static class FunctionCallBehavior {
|
||||
|
||||
private final String key;
|
||||
private final boolean required;
|
||||
private final boolean enabled;
|
||||
|
||||
FunctionCallBehavior(KernelFunction function, boolean required, boolean enabled) {
|
||||
this.key = getKey(function);
|
||||
this.required = required;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
boolean required() {
|
||||
return required;
|
||||
}
|
||||
|
||||
boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static String getKey(KernelFunction function) {
|
||||
return getKey(function.getSkillName(), function.getName());
|
||||
}
|
||||
|
||||
static String getKey(String skillName, String functionName) {
|
||||
return String.format("%s-%s", skillName, functionName);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, Boolean> flags = new HashMap<>();
|
||||
private final Map<String, FunctionCallBehavior> functionSettings = new HashMap<>();
|
||||
private final Map<String, Integer> settings = new HashMap<>();
|
||||
|
||||
public ToolCallBehavior() {
|
||||
}
|
||||
|
||||
public ToolCallBehavior kernelFunctions(boolean enable) {
|
||||
setFlag("kernelFunctions", enable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolCallBehavior autoInvoke(boolean enable) {
|
||||
setFlag("autoInvoke", true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolCallBehavior requireFunction(KernelFunction function, boolean require) {
|
||||
if (function != null) {
|
||||
String key = FunctionCallBehavior.getKey(function);
|
||||
functionSettings.compute(
|
||||
key,
|
||||
(k,v) -> {
|
||||
if (v == null) {
|
||||
return new FunctionCallBehavior(function, require, false);
|
||||
} else {
|
||||
return new FunctionCallBehavior(function, require, v.enabled());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolCallBehavior enableFunction(KernelFunction function, boolean enable) {
|
||||
if (function != null) {
|
||||
String key = FunctionCallBehavior.getKey(function);
|
||||
functionSettings.compute(
|
||||
key,
|
||||
(k,v) -> {
|
||||
if (v == null) {
|
||||
return new FunctionCallBehavior(function, false, enable);
|
||||
} else {
|
||||
return new FunctionCallBehavior(function, v.required(), enable);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolCallBehavior maximumAutoInvokeAttempts(int maximumAutoInvokeAttempts) {
|
||||
setSetting("maximumAutoInvokeAttempts", maximumAutoInvokeAttempts);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean kernelFunctionsEnabled() {
|
||||
return getFlag("kernelFunctions");
|
||||
}
|
||||
|
||||
public boolean autoInvokeEnabled() {
|
||||
return getFlag("autoInvoke");
|
||||
}
|
||||
|
||||
public boolean functionRequired(KernelFunction function) {
|
||||
if (function != null) {
|
||||
return functionRequired(function.getSkillName(), function.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean functionRequired(String pluginName, String functionName) {
|
||||
String key = FunctionCallBehavior.getKey(pluginName, functionName);
|
||||
if (functionSettings.containsKey(key)) {
|
||||
return functionSettings.get(key).required();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean functionEnabled(KernelFunction function) {
|
||||
|
||||
if (function != null) {
|
||||
return functionEnabled(function.getSkillName(), function.getName());
|
||||
}
|
||||
return functionSettings.isEmpty();
|
||||
}
|
||||
|
||||
public boolean functionEnabled(String pluginName, String functionName) {
|
||||
|
||||
String key = FunctionCallBehavior.getKey(pluginName, functionName);
|
||||
if (functionSettings.containsKey(key)) {
|
||||
return functionSettings.get(key).enabled();
|
||||
}
|
||||
return functionSettings.isEmpty();
|
||||
}
|
||||
|
||||
public int maximumAutoInvokeAttempts() {
|
||||
return getSetting("maximumAutoInvokeAttempts", DEFAULT_MAXIMUM_AUTO_INVOKE_ATTEMPTS);
|
||||
}
|
||||
|
||||
protected void setFlag(String key, boolean value) {
|
||||
flags.put(key, value);
|
||||
}
|
||||
|
||||
protected void setSetting(String key, int value) {
|
||||
settings.put(key, value);
|
||||
}
|
||||
|
||||
protected boolean getFlag(String key) {
|
||||
return flags.getOrDefault(key, false);
|
||||
}
|
||||
|
||||
protected int getSetting(String key, int defaultValue) {
|
||||
return settings.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -41,13 +41,14 @@ public class ContextVariable<T> {
|
||||
}
|
||||
|
||||
public ContextVariable<T> cloneVariable() {
|
||||
return new ContextVariable<T>(type, value);
|
||||
return new ContextVariable<>(type, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ContextVariable<T> of(T value) {
|
||||
ContextVariableType<T> type = ContextVariableTypes.getDefaultVariableTypeForClass(
|
||||
(Class<T>) value.getClass());
|
||||
return new ContextVariable<T>(type, value);
|
||||
return new ContextVariable<>(type, value);
|
||||
}
|
||||
|
||||
public static <T> ContextVariable<T> of(T value, ContextVariableTypeConverter<T> converter) {
|
||||
|
||||
+11
@@ -78,7 +78,9 @@ public class ContextVariableTypeConverter<T> {
|
||||
this.toObjects = toObjects;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <U> U toObject(Object t, Class<U> clazz) {
|
||||
Optional<Converter<T, ?>> converter = toObjects
|
||||
.stream()
|
||||
@@ -103,14 +105,23 @@ public class ContextVariableTypeConverter<T> {
|
||||
}
|
||||
|
||||
public T fromObject(Object s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return fromObject.apply(s);
|
||||
}
|
||||
|
||||
public String toPromptString(T t) {
|
||||
if (t == null) {
|
||||
return "";
|
||||
}
|
||||
return toPromptString.apply(t);
|
||||
}
|
||||
|
||||
public T fromPromptString(String t) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
return fromPromptString.apply(t);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -64,13 +64,14 @@ public class ContextVariableTypes {
|
||||
contextVariableTypeConverter.getType()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T convert(Object s, Class<T> clazz) {
|
||||
if (s instanceof ContextVariable && ((ContextVariable<?>) s).getType().getClazz()
|
||||
.isAssignableFrom(clazz)) {
|
||||
return ((ContextVariable<T>) s).getValue();
|
||||
}
|
||||
if (s != null && clazz.isAssignableFrom(s.getClass())) {
|
||||
return (T) s;
|
||||
return clazz.cast(s);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
+42
-2
@@ -1,9 +1,11 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import java.util.function.Function;
|
||||
import static com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes.convert;
|
||||
|
||||
public class NumberVariableContextVariableTypeConverter<T extends Number> extends
|
||||
ContextVariableTypeConverter<T> {
|
||||
@@ -16,4 +18,42 @@ public class NumberVariableContextVariableTypeConverter<T extends Number> extend
|
||||
Number::toString,
|
||||
fromPromptString);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <U> U toObject(Object t, Class<U> clazz) {
|
||||
// Let the parent class have a crack at it first
|
||||
// since someone may have installed a special converter.
|
||||
U obj = super.toObject(t, clazz);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
}
|
||||
// If the type is a string, and the object is of the same type as the
|
||||
// converter, then we can convert with the toPromptString method.
|
||||
if (clazz == String.class && t.getClass() == super.getType()) {
|
||||
return clazz.cast(toPromptString(getType().cast(t)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromObject(Object s) {
|
||||
T obj = super.fromObject(s);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
}
|
||||
if (s instanceof String) {
|
||||
return fromPromptString((String) s);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T fromPromptString(String s) {
|
||||
if (s != null && !s.isEmpty()) {
|
||||
return super.fromPromptString(s);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -31,9 +31,10 @@ public class KernelFunctionFactory {
|
||||
@Nullable String functionName,
|
||||
@Nullable String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata returnParameter) {
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
return createFromMethod(
|
||||
method,
|
||||
null,
|
||||
functionName,
|
||||
description,
|
||||
parameters,
|
||||
@@ -58,7 +59,7 @@ public class KernelFunctionFactory {
|
||||
@Nullable String functionName,
|
||||
@Nullable String description,
|
||||
@Nullable List<KernelParameterMetadata> parameters,
|
||||
@Nullable KernelReturnParameterMetadata returnParameter) {
|
||||
@Nullable KernelReturnParameterMetadata<?> returnParameter) {
|
||||
return KernelFunctionFromMethod.create(method, target, functionName, description,
|
||||
parameters, returnParameter);
|
||||
}
|
||||
|
||||
+3
-3
@@ -13,13 +13,13 @@ public class KernelParameterMetadata {
|
||||
public KernelParameterMetadata(
|
||||
String name,
|
||||
String description,
|
||||
String defaultValue,
|
||||
boolean isRequired) {
|
||||
Class<?> parameterType,
|
||||
String defaultValue, boolean isRequired) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.defaultValue = defaultValue;
|
||||
this.isRequired = isRequired;
|
||||
this.parameterType = String.class; // TODO
|
||||
this.parameterType = String.class;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ public class KernelPluginFactory {
|
||||
KernelFunctionParameter.class);
|
||||
|
||||
return new KernelParameterMetadata(annotation.name(), annotation.description(),
|
||||
annotation.defaultValue(), annotation.required());
|
||||
null, annotation.defaultValue(), annotation.required());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
+14
-5
@@ -19,6 +19,7 @@ import com.microsoft.semantickernel.plugin.KernelParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.KernelReturnParameterMetadata;
|
||||
import com.microsoft.semantickernel.plugin.annotations.DefineKernelFunction;
|
||||
import com.microsoft.semantickernel.plugin.annotations.KernelFunctionParameter;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
@@ -47,7 +48,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
String functionName,
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata returnParameter) {
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
super(
|
||||
new KernelFunctionMetadata(
|
||||
functionName,
|
||||
@@ -84,7 +85,7 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
String functionName,
|
||||
String description,
|
||||
List<KernelParameterMetadata> parameters,
|
||||
KernelReturnParameterMetadata returnParameter) {
|
||||
KernelReturnParameterMetadata<?> returnParameter) {
|
||||
|
||||
MethodDetails methodDetails = getMethodDetails(functionName, method, target);
|
||||
|
||||
@@ -246,7 +247,15 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
KernelFunctionParameter annotation =
|
||||
parameter.getAnnotation(KernelFunctionParameter.class);
|
||||
if (annotation != null) {
|
||||
arg = ContextVariable.of(annotation.defaultValue());
|
||||
// Convert from the defaultValue, which is a String to the argument type
|
||||
// Expectation here is that the fromPromptString method will be able to handle a null or empty string
|
||||
Class<?> type = annotation.type();
|
||||
ContextVariableType<?> cvType = ContextVariableTypes.getDefaultVariableTypeForClass(type);
|
||||
if (cvType != null) {
|
||||
String defaultValue = annotation.defaultValue();
|
||||
Object value = cvType.getConverter().fromPromptString(defaultValue);
|
||||
arg = ContextVariable.of(value);
|
||||
}
|
||||
|
||||
if (NO_DEFAULT_VALUE.equals(arg)) {
|
||||
if (!annotation.required()) {
|
||||
@@ -490,8 +499,8 @@ public class KernelFunctionFromMethod extends DefaultKernelFunction {
|
||||
return new KernelParameterMetadata(
|
||||
name,
|
||||
description,
|
||||
defaultValue,
|
||||
isRequired
|
||||
null,
|
||||
defaultValue, isRequired
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -116,8 +116,8 @@ public class PromptTemplateConfig {
|
||||
.map(inputVariable -> new KernelParameterMetadata(
|
||||
inputVariable.getName(),
|
||||
inputVariable.getDescription(),
|
||||
inputVariable.getDefaultValue(),
|
||||
inputVariable.isRequired()
|
||||
null,
|
||||
inputVariable.getDefaultValue(), inputVariable.isRequired()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.microsoft.semantickernel.orchestration.contextvariables.converters;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableType;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypeConverter;
|
||||
import com.microsoft.semantickernel.orchestration.contextvariables.ContextVariableTypes;
|
||||
|
||||
public class NumberVariableContextVariableTypeConverterTest {
|
||||
|
||||
/**
|
||||
* Test of toObject method, of class NumberVariableContextVariableTypeConverter.
|
||||
*/
|
||||
@Test
|
||||
void testConvertIntegerToString() {
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
Object expResult = "1";
|
||||
Object result = instance.toObject(1, String.class);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of fromObject method, of class NumberVariableContextVariableTypeConverter.
|
||||
*/
|
||||
@Test
|
||||
void testFromStringToInteger() {
|
||||
Object s = "123";
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
Number expResult = Integer.valueOf((String)s);
|
||||
Number result = instance.fromObject(s);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromPromptStringToInteger() {
|
||||
String s = "123";
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
Number expResult = Integer.valueOf(s);
|
||||
Number result = instance.fromPromptString(s);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromPromptStringReturnsNullForEmptyString() {
|
||||
String s = "";
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
Number expResult = null;
|
||||
Number result = instance.fromPromptString(s);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromPromptStringReturnsNullForNullString() {
|
||||
String s = null;
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
Number expResult = null;
|
||||
Number result = instance.fromPromptString(s);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToPromptString() {
|
||||
Integer num = 123;
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
String expResult = "123";
|
||||
String result = instance.toPromptString(num);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToPromptStringReturnsEmptyStringForNull() {
|
||||
Integer num = null;
|
||||
ContextVariableType<Integer> type = ContextVariableTypes.getDefaultVariableTypeForClass(Integer.class);
|
||||
ContextVariableTypeConverter<Integer> instance = type.getConverter();
|
||||
String expResult = "";
|
||||
String result = instance.toPromptString(num);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,6 +71,11 @@
|
||||
<artifactId>semantickernel-plugin-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
<artifactId>semantickernel-connectors-web-bing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.microsoft.semantic-kernel</groupId>
|
||||
|
||||
Reference in New Issue
Block a user