Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,16 @@ private Mono<List<ChatMessageContent<?>>> internalInvokeAsync(
? invocationContext.getPromptExecutionSettings()
: kernelArguments.getExecutionSettings().get(chatCompletionService.getServiceId());

ToolCallBehavior toolCallBehavior = invocationContext != null
? invocationContext.getToolCallBehavior()
: ToolCallBehavior.allowAllKernelFunctions(true);

// Build base invocation context
InvocationContext.Builder builder = InvocationContext.builder()
.withPromptExecutionSettings(executionSettings)
.withToolCallBehavior(toolCallBehavior)
.withReturnMode(InvocationReturnMode.NEW_MESSAGES_ONLY);

if (invocationContext != null) {
builder = builder
.withTelemetry(invocationContext.getTelemetry())
.withFunctionChoiceBehavior(invocationContext.getFunctionChoiceBehavior())
.withToolCallBehavior(invocationContext.getToolCallBehavior())
.withContextVariableConverter(invocationContext.getContextVariableTypes())
.withKernelHooks(invocationContext.getKernelHooks());
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.microsoft.semantickernel.aiservices.openai.chatcompletion;

import com.azure.ai.openai.models.ChatCompletionsToolDefinition;
import com.azure.ai.openai.models.ChatCompletionsToolSelection;
import com.microsoft.semantickernel.functionchoice.FunctionChoiceBehaviorOptions;

import javax.annotation.Nullable;
import java.util.List;

public class OpenAIToolCallConfig {
private final List<ChatCompletionsToolDefinition> tools;
private final ChatCompletionsToolSelection toolChoice;
private final boolean autoInvoke;
private final FunctionChoiceBehaviorOptions options;

/**
* Creates a new instance of the {@link OpenAIToolCallConfig} class.
*
* @param tools The list of tools available for the call.
* @param toolChoice The tool selection strategy.
* @param autoInvoke Indicates whether to automatically invoke the tool.
* @param options Additional options for function choice behavior.
*/
public OpenAIToolCallConfig(
List<ChatCompletionsToolDefinition> tools,
ChatCompletionsToolSelection toolChoice,
boolean autoInvoke,
@Nullable FunctionChoiceBehaviorOptions options) {
this.tools = tools;
this.toolChoice = toolChoice;
this.autoInvoke = autoInvoke;
this.options = options;
}

/**
* Gets the list of tools available for the call.
*
* @return The list of tools.
*/
public List<ChatCompletionsToolDefinition> getTools() {
return tools;
}

/**
* Gets the tool selection strategy.
*
* @return The tool selection strategy.
*/
public ChatCompletionsToolSelection getToolChoice() {
return toolChoice;
}

/**
* Indicates whether to automatically invoke the tool.
*
* @return True if auto-invocation is enabled; otherwise, false.
*/
public boolean isAutoInvoke() {
return autoInvoke;
}

/**
* Gets additional options for function choice behavior.
*
* @return The function choice behavior options.
*/
public FunctionChoiceBehaviorOptions getOptions() {
return options;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
import com.microsoft.semantickernel.agents.chatcompletion.ChatHistoryAgentThread;
import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.functionchoice.FunctionChoiceBehavior;
import com.microsoft.semantickernel.implementation.templateengine.tokenizer.DefaultPromptTemplate;
import com.microsoft.semantickernel.orchestration.InvocationContext;
import com.microsoft.semantickernel.orchestration.PromptExecutionSettings;
import com.microsoft.semantickernel.orchestration.ToolCallBehavior;
import com.microsoft.semantickernel.plugin.KernelPluginFactory;
import com.microsoft.semantickernel.samples.plugins.github.GitHubModel;
import com.microsoft.semantickernel.samples.plugins.github.GitHubPlugin;
Expand Down Expand Up @@ -68,7 +67,7 @@ public static void main(String[] args) {
.build();

InvocationContext invocationContext = InvocationContext.builder()
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true))
.withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(true))
.withContextVariableConverter(new ContextVariableTypeConverter<>(
GitHubModel.Issue.class,
o -> (GitHubModel.Issue) o,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatMessageContent;
import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIFunctionToolCall;
import com.microsoft.semantickernel.contextvariables.ContextVariableTypes;
import com.microsoft.semantickernel.functionchoice.FunctionChoiceBehavior;
import com.microsoft.semantickernel.implementation.CollectionUtil;
import com.microsoft.semantickernel.orchestration.FunctionResult;
import com.microsoft.semantickernel.orchestration.FunctionResultMetadata;
Expand Down Expand Up @@ -38,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-35-turbo-2");
.getOrDefault("MODEL_ID", "gpt-4o");

// Define functions that can be called by the model
public static class HelperFunctions {
Expand Down Expand Up @@ -118,7 +119,7 @@ public static void main(String[] args) throws NoSuchMethodException {

var result = kernel
.invokeAsync(function)
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true))
.withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(true))
.withResultType(ContextVariableTypes.getGlobalVariableTypeForClass(String.class))
.block();
System.out.println(result.getResult());
Expand All @@ -134,7 +135,7 @@ public static void main(String[] args) throws NoSuchMethodException {
chatHistory,
kernel,
InvocationContext.builder()
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(false))
.withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(false))
.withReturnMode(InvocationReturnMode.FULL_HISTORY)
.build())
.block();
Expand Down Expand Up @@ -243,7 +244,7 @@ public static void multiTurnaroundCall() {
chatHistory,
kernel,
InvocationContext.builder()
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true))
.withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(true))
.withReturnMode(InvocationReturnMode.FULL_HISTORY)
.build())
.block();
Expand All @@ -258,7 +259,7 @@ public static void multiTurnaroundCall() {
chatHistory,
kernel,
InvocationContext.builder()
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true))
.withFunctionChoiceBehavior(FunctionChoiceBehavior.auto(true))
.withReturnMode(InvocationReturnMode.FULL_HISTORY)
.build())
.block();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.microsoft.semantickernel.functionchoice;

import com.microsoft.semantickernel.semanticfunctions.KernelFunction;

import javax.annotation.Nullable;
import java.util.List;

/**
* A set of allowed kernel functions. All kernel functions are allowed if allKernelFunctionsAllowed is true.
* Otherwise, only the functions in allowedFunctions are allowed.
* <p>
* If a function is allowed, it may be called. If it is not allowed, it will not be called.
*/
public class AutoFunctionChoiceBehavior extends FunctionChoiceBehavior {
private final boolean autoInvoke;

/**
* Create a new instance of AutoFunctionChoiceBehavior.
*
* @param autoInvoke Whether auto-invocation is enabled.
* @param functions A set of functions to advertise to the model.
* @param options Options for the function choice behavior.
*/
public AutoFunctionChoiceBehavior(boolean autoInvoke,
@Nullable List<KernelFunction<?>> functions,
@Nullable FunctionChoiceBehaviorOptions options) {
super(functions, options);
this.autoInvoke = autoInvoke;
}

/**
* Check whether the given function is allowed.
*
* @return Whether the function is allowed.
*/
public boolean isAutoInvoke() {
return autoInvoke;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.functionchoice;

import com.microsoft.semantickernel.semanticfunctions.KernelFunction;

import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
* Defines the behavior of a tool call. Currently, the only tool available is function calling.
*/
public abstract class FunctionChoiceBehavior {
private final Set<String> fullFunctionNames;

protected final List<KernelFunction<?>> functions;
protected final FunctionChoiceBehaviorOptions options;

protected FunctionChoiceBehavior(List<KernelFunction<?>> functions,
@Nullable FunctionChoiceBehaviorOptions options) {
this.functions = functions;
this.fullFunctionNames = new HashSet<>();

if (functions != null) {
functions.stream().filter(Objects::nonNull).forEach(
f -> this.fullFunctionNames
.add(formFullFunctionName(f.getPluginName(), f.getName())));
}

if (options != null) {
this.options = options;
} else {
this.options = FunctionChoiceBehaviorOptions.builder().build();
}
}

/**
* Gets the functions that are allowed.
*
* @return The functions that are allowed.
*/
public List<KernelFunction<?>> getFunctions() {
return functions;
}

/**
* Gets the options for the function choice behavior.
*
* @return The options for the function choice behavior.
*/
public FunctionChoiceBehaviorOptions getOptions() {
return options;
}

/**
* Gets an instance of the FunctionChoiceBehavior that provides all the Kernel's plugins functions to the AI model to call.
*
* @param autoInvoke Indicates whether the functions should be automatically invoked by AI connectors
*
* @return A new ToolCallBehavior instance with all kernel functions allowed.
*/
public static FunctionChoiceBehavior auto(boolean autoInvoke) {
return new AutoFunctionChoiceBehavior(autoInvoke, null, null);
}

/**
* Gets an instance of the FunctionChoiceBehavior that provides either all the Kernel's plugins functions to the AI model to call or specific functions.
*
* @param autoInvoke Enable or disable auto-invocation.
* If auto-invocation is enabled, the model may request that the Semantic Kernel
* invoke the kernel functions and return the value to the model.
* @param functions Functions to provide to the model. If null, all the Kernel's plugins' functions are provided to the model.
* If empty, no functions are provided to the model, which is equivalent to disabling function calling.
* @param options Options for the function choice behavior.
*
* @return A new FunctionChoiceBehavior instance with all kernel functions allowed.
*/
public static FunctionChoiceBehavior auto(boolean autoInvoke,
List<KernelFunction<?>> functions,
@Nullable FunctionChoiceBehaviorOptions options) {
return new AutoFunctionChoiceBehavior(autoInvoke, functions, options);
}

/**
* Gets an instance of the FunctionChoiceBehavior that provides either all the Kernel's plugins functions to the AI model to call or specific functions.
* <p>
* This behavior forces the model to call the provided functions.
* SK connectors will invoke a requested function or multiple requested functions if the model requests multiple ones in one request,
* while handling the first request, and stop advertising the functions for the following requests to prevent the model from repeatedly calling the same function(s).
*
* @param functions Functions to provide to the model. If null, all the Kernel's plugins' functions are provided to the model.
* If empty, no functions are provided to the model, which is equivalent to disabling function calling.
* @return A new FunctionChoiceBehavior instance with the required function.
*/
public static FunctionChoiceBehavior required(boolean autoInvoke,
List<KernelFunction<?>> functions,
@Nullable FunctionChoiceBehaviorOptions options) {
return new RequiredFunctionChoiceBehavior(autoInvoke, functions, options);
}

/**
* Gets an instance of the FunctionChoiceBehavior that provides either all the Kernel's plugins functions to the AI model to call or specific functions.
* <p>
* This behavior is useful if the user should first validate what functions the model will use.
*
* @param functions Functions to provide to the model. If null, all the Kernel's plugins' functions are provided to the model.
* If empty, no functions are provided to the model, which is equivalent to disabling function calling.
*/
public static FunctionChoiceBehavior none(List<KernelFunction<?>> functions,
@Nullable FunctionChoiceBehaviorOptions options) {
return new NoneFunctionChoiceBehavior(functions, options);
}


/**
* The separator between the plugin name and the function name.
*/
public static final String FUNCTION_NAME_SEPARATOR = "-";

/**
* Form the full function name.
*
* @param pluginName The name of the plugin that the function is in.
* @param functionName The name of the function.
* @return The key for the function.
*/
public static String formFullFunctionName(@Nullable String pluginName, String functionName) {
if (pluginName == null) {
pluginName = "";
}
return String.format("%s%s%s", pluginName, FUNCTION_NAME_SEPARATOR, functionName);
}

/**
* Check whether the given function is allowed.
*
* @param function The function to check.
* @return Whether the function is allowed.
*/
public boolean isFunctionAllowed(KernelFunction<?> function) {
return isFunctionAllowed(function.getPluginName(), function.getName());
}

/**
* Check whether the given function is allowed.
*
* @param pluginName The name of the plugin that the function is in.
* @param functionName The name of the function.
* @return Whether the function is allowed.
*/
public boolean isFunctionAllowed(@Nullable String pluginName, String functionName) {
// If no functions are provided, all functions are allowed.
if (functions == null || functions.isEmpty()) {
return true;
}

String key = formFullFunctionName(pluginName, functionName);
return fullFunctionNames.contains(key);
}
}
Loading
Loading