Java: Update to Service registry pattern (#1923)

Resolves #1628 

Also setKernelConfig -> withKernelConfig, to be in keeping with other
builder functions
This commit is contained in:
John Oliver
2023-07-10 19:38:17 +01:00
committed by GitHub
parent 5854b1df5b
commit 2212d46292
40 changed files with 492 additions and 53 deletions
@@ -107,7 +107,7 @@ public class InlineFunctionExample {
.addTextCompletionService(MODEL, kernel -> textCompletion)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
CompletionSKFunction summarize =
kernel.getSemanticFunctionBuilder()
@@ -108,7 +108,7 @@ public class InlineFunctionWithPreBuiltSkillExample {
.addTextCompletionService(MODEL, kernel -> textCompletion)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
CompletionSKFunction summarize =
SKBuilders.completionFunctions(kernel)
@@ -41,7 +41,7 @@ public class AbstractKernelTest {
.build();
return SKBuilders.kernel()
.setKernelConfig(kernelConfig)
.withKernelConfig(kernelConfig)
.withMemoryStore(new VolatileMemoryStore())
.build();
}
@@ -57,7 +57,7 @@ public class KernelTest extends AbstractKernelTest {
.addTextCompletionService(model, kernel -> textCompletion)
.build();
return SKBuilders.kernel().setKernelConfig(kernelConfig).build();
return SKBuilders.kernel().withKernelConfig(kernelConfig).build();
}
@Test
@@ -36,7 +36,7 @@ public class Example00_GettingStarted {
.build();
Kernel kernel = SKBuilders.kernel()
.setKernelConfig(config)
.withKernelConfig(config)
.build();
return kernel;
@@ -16,7 +16,7 @@ public class Example06_MemoryAndEmbeddings {
// TODO: Add Volatile memory
Kernel kernel = SKBuilders.kernel()
.setKernelConfig(config)
.withKernelConfig(config)
.build();
return kernel;
@@ -11,7 +11,7 @@ import reactor.core.publisher.Mono;
public class Example02_Pipeline {
public static void main(String[] args) {
KernelConfig kernelConfig = SKBuilders.kernelConfig().build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
// Load native skill
ReadOnlyFunctionCollection text = kernel.importSkill(new TextSkill(), null);
@@ -37,7 +37,7 @@ public class Example03_Variables {
public static void main(String[] args) {
KernelConfig kernelConfig = SKBuilders.kernelConfig().build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
// Load native skill
ReadOnlyFunctionCollection functionCollection =
@@ -36,7 +36,7 @@ public class Example04_CombineLLMPromptsAndNativeCode {
.addTextCompletionService("text-davinci-003", kernel -> textCompletion)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
kernel.importSkill(new SearchEngineSkill(), null);
kernel.importSkillFromDirectory("SummarizeSkill", "./samples/skills", "SummarizeSkill");
@@ -25,7 +25,7 @@ public class Example05_InlineFunctionDefinition {
.addTextCompletionService("text-davinci-003", kernel -> textCompletion)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
System.out.println("======== Inline Function Definition ========");
@@ -29,7 +29,7 @@ public class Example06_TemplateLanguage {
.build();
Kernel kernel = SKBuilders.kernel()
.setKernelConfig(kernelConfig)
.withKernelConfig(kernelConfig)
.build();
// Load native skill into the kernel skill collection, sharing its functions
@@ -77,7 +77,7 @@ public class Example08_RetryHandler {
Kernel kernel = SKBuilders
.kernel()
.setKernelConfig(kernelConfig)
.withKernelConfig(kernelConfig)
.build();
CompletionSKFunction summarizeFunc = SKBuilders.completionFunctions(kernel)
@@ -89,7 +89,6 @@ public class Example08_RetryHandler {
new PromptTemplateConfig.CompletionConfig(
0.2, 0.5, 0, 0, 2000));
kernel.registerSemanticFunction(summarizeFunc);
SKContext context = summarizeFunc.invokeAsync(text).block();
@@ -25,7 +25,7 @@ public class Example09_FunctionTypes {
.addTextCompletionService("text-davinci-003", kernel -> textCompletion)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
System.out.println("======== Native function types ========");
@@ -175,7 +175,7 @@ public class Example12_SequentialPlanner {
private static Kernel initializeKernel() throws IOException {
OpenAIAsyncClient client = Config.getClient();
var kernel = SKBuilders.kernel()
.setKernelConfig(SKBuilders
.withKernelConfig(SKBuilders
.kernelConfig()
.addTextCompletionService("text-davinci-003", kernel1 -> SKBuilders.textCompletionService()
.build(client, "text-davinci-003"))
@@ -186,9 +186,10 @@ public class Example13_ConversationSummarySkill {
KernelConfig kernelConfig =
new KernelConfig.Builder()
.addTextCompletionService("text-davinci-003", kernel -> textCompletion)
.build();
return SKBuilders.kernel().setKernelConfig(kernelConfig).build();
return SKBuilders.kernel()
.withKernelConfig(kernelConfig)
.withAIService("text-davinci-003", textCompletion, true, TextCompletion.class).build();
}
}
@@ -5,7 +5,6 @@ import com.microsoft.semantickernel.Config;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.connectors.memory.azurecognitivesearch.AzureCognitiveSearchMemory;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -42,7 +41,7 @@ public class Example14_SemanticMemory
var kernelConfig = SKBuilders.kernelConfig().build();
var kernelWithACS = SKBuilders.kernel()
.setKernelConfig(kernelConfig)
.withKernelConfig(kernelConfig)
.withMemory(new AzureCognitiveSearchMemory(System.getenv("ACS_ENDPOINT"), System.getenv("ACS_API_KEY")))
.build();
@@ -69,7 +68,7 @@ public class Example14_SemanticMemory
.build();
var kernelWithCustomDb = SKBuilders.kernel()
.setKernelConfig(kernelConfigWithTextEmbedding)
.withKernelConfig(kernelConfigWithTextEmbedding)
.withMemoryStore(SKBuilders.memoryStore().build())
.build();
@@ -4,7 +4,6 @@ package com.microsoft.semantickernel.syntaxexamples;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.Config;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.KernelConfig;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.chatcompletion.ChatCompletion;
import com.microsoft.semantickernel.chatcompletion.ChatHistory;
@@ -21,14 +20,17 @@ public class Example17_ChatGPT {
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
KernelConfig kernelConfig = SKBuilders.kernelConfig()
.addChatCompletionService(
Kernel kernel = SKBuilders.kernel()
.withKernelConfig(SKBuilders.kernelConfig()
.build())
.withAIService(
"chat-test",
kernel -> SKBuilders.chatCompletion().build(client, "chat-test"))
SKBuilders.chatCompletion().build(client, "chat-test"),
true,
ChatCompletion.class
)
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
ChatCompletion<OpenAIChatHistory> chatGPT = kernel.getService(null, ChatCompletion.class);
OpenAIChatHistory chatHistory = chatGPT.createNewChat("You are a librarian, expert about books");
@@ -16,7 +16,7 @@ public class Example28_ActionPlanner {
System.out.println("======== Action Planner ========");
var kernel = SKBuilders.kernel()
.setKernelConfig(SKBuilders
.withKernelConfig(SKBuilders
.kernelConfig()
.addTextCompletionService("text-davinci-002", kernel1 -> SKBuilders.textCompletionService()
.build(client, "text-davinci-002"))
@@ -7,11 +7,17 @@ import com.microsoft.semantickernel.memory.SemanticTextMemory;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.orchestration.SKFunction;
import com.microsoft.semantickernel.services.AIService;
import com.microsoft.semantickernel.services.AIServiceCollection;
import com.microsoft.semantickernel.services.AIServiceProvider;
import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import reactor.core.publisher.Mono;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/** Interface for the semantic kernel. */
@@ -66,7 +72,8 @@ public interface Kernel extends SkillExecutor {
CompletionSKFunction.Builder getSemanticFunctionBuilder();
/** Obtains the service with the given name and type */
<T> T getService(@Nullable String name, Class<T> clazz) throws KernelException;
<T extends AIService> T getService(@Nullable String name, Class<T> clazz)
throws KernelException;
/** Registers a semantic functon on this kernel */
<RequestConfiguration, FunctionType extends SKFunction<RequestConfiguration>>
@@ -80,14 +87,15 @@ public interface Kernel extends SkillExecutor {
@Nullable private KernelConfig kernelConfig = null;
@Nullable private PromptTemplateEngine promptTemplateEngine = null;
@Nullable private MemoryStore memoryStore = null;
@Nullable private AIServiceCollection aiServices = new AIServiceCollection();
@Nullable private SemanticTextMemory memory = null;
public Builder setKernelConfig(KernelConfig kernelConfig) {
public Builder withKernelConfig(KernelConfig kernelConfig) {
this.kernelConfig = kernelConfig;
return this;
}
public Builder setPromptTemplateEngine(PromptTemplateEngine promptTemplateEngine) {
public Builder withPromptTemplateEngine(PromptTemplateEngine promptTemplateEngine) {
this.promptTemplateEngine = promptTemplateEngine;
return this;
}
@@ -97,6 +105,65 @@ public interface Kernel extends SkillExecutor {
return this;
}
/**
* Adds an instance to the services collection
*
* @param instance The instance.
* @param clazz The class of the instance.
* @return The builder.
*/
public <T extends AIService> Builder withDefaultAIService(T instance, Class<T> clazz) {
this.aiServices.setService(instance, clazz);
return this;
}
/**
* Adds an instance to the services collection
*
* @param serviceId The service ID
* @param instance The instance.
* @param setAsDefault Optional: set as the default AI service for type T
* @param clazz The class of the instance.
*/
public <T extends AIService> Builder withAIService(
@Nullable String serviceId, T instance, boolean setAsDefault, Class<T> clazz) {
this.aiServices.setService(serviceId, instance, setAsDefault, clazz);
return this;
}
/**
* Adds a factory method to the services collection
*
* @param factory The factory method that creates the AI service instances of type T.
* @param clazz The class of the instance.
*/
public <T extends AIService> Builder withDefaultAIService(
Supplier<T> factory, Class<T> clazz) {
this.aiServices.setService(factory, clazz);
return this;
}
/**
* Adds a factory method to the services collection
*
* @param serviceId The service ID
* @param factory The factory method that creates the AI service instances of type T.
* @param setAsDefault Optional: set as the default AI service for type T
* @param clazz The class of the instance.
*/
public <T extends AIService> Builder withAIService(
@Nullable String serviceId,
Function<KernelConfig, T> factory,
boolean setAsDefault,
Class<T> clazz) {
this.aiServices.setService(
serviceId,
(Supplier<T>) () -> factory.apply(this.kernelConfig),
setAsDefault,
clazz);
return this;
}
public Builder withMemory(SemanticTextMemory memory) {
this.memory = memory;
return this;
@@ -109,7 +176,12 @@ public interface Kernel extends SkillExecutor {
return BuildersSingleton.INST
.getKernelBuilder()
.build(kernelConfig, promptTemplateEngine, memory, memoryStore);
.build(
kernelConfig,
promptTemplateEngine,
memory,
memoryStore,
aiServices.build());
}
}
@@ -118,6 +190,7 @@ public interface Kernel extends SkillExecutor {
KernelConfig kernelConfig,
@Nullable PromptTemplateEngine promptTemplateEngine,
@Nullable SemanticTextMemory memory,
@Nullable MemoryStore memoryStore);
@Nullable MemoryStore memoryStore,
@Nullable AIServiceProvider aiServiceProvider);
}
}
@@ -2,13 +2,14 @@
package com.microsoft.semantickernel.ai.embeddings;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.services.AIService;
import reactor.core.publisher.Mono;
import java.util.List;
/** Interface for text embedding generation services */
public interface EmbeddingGeneration<TValue, TEmbedding extends Number> {
public interface EmbeddingGeneration<TValue, TEmbedding extends Number> extends AIService {
/**
* Generates a list of embeddings associated to the data
*
@@ -2,12 +2,13 @@
package com.microsoft.semantickernel.chatcompletion;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.services.AIService;
import reactor.core.publisher.Mono;
import javax.annotation.Nullable;
public interface ChatCompletion<ChatHistoryType extends ChatHistory> {
public interface ChatCompletion<ChatHistoryType extends ChatHistory> extends AIService {
/**
* Generate a new chat message
*
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
/** Marker interface for AI services */
public interface AIService {}
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
import com.microsoft.semantickernel.Verify;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/** A collection that can hold AI services */
public class AIServiceCollection {
// A constant key for the default service
private static final String DefaultKey = "__DEFAULT__";
// A dictionary that maps a service type to a nested dictionary of names and service instances
// or factories
// private readonly Dictionary<Type, Dictionary<string, object>> _services = new();
private final Map<Class<? extends AIService>, Map<String, Supplier<? extends AIService>>>
services = new HashMap<>();
// A dictionary that maps a service type to the name of the default service
private final Map<Class<? extends AIService>, String> defaultIds = new HashMap<>();
/**
* Registers a singleton service instance with the default name.
*
* @param service The service instance.
* @param serviceType The type of the service.
*/
public <T extends AIService> void setService(T service, Class<T> serviceType) {
setService(DefaultKey, (Supplier<T>) () -> service, true, serviceType);
}
/**
* Registers a singleton service instance with an optional name and default flag.
*
* @param name The name of the service, or null for the default service.
* @param service The service instance.
* @param setAsDefault Whether the service should be the default for its type.
* @param serviceType The type of the service.
*/
public <T extends AIService> void setService(
String name, T service, boolean setAsDefault, Class<T> serviceType) {
setService(name, (Supplier<T>) () -> service, setAsDefault, serviceType);
}
/**
* Registers a transient service factory with the default name.
*
* @param factory The factory function to create the service instance.
* @param serviceType The type of the service.
*/
public <T extends AIService> void setService(Supplier<T> factory, Class<T> serviceType) {
setService(DefaultKey, factory, true, serviceType);
}
/**
* Registers a transient service factory with an optional name and default flag.
*
* @param name The name of the service, or null for the default service.
* @param factory The factory function to create the service instance.
* @param setAsDefault Whether the service should be the default for its type.
* @param serviceType The type of the service.
*/
public <T extends AIService> void setService(
@Nullable String name,
Supplier<T> factory,
boolean setAsDefault,
Class<T> serviceType) {
// Validate the factory function
if (factory == null) {
throw new IllegalArgumentException();
}
// Get or create the nested dictionary for the service type
Map<String, Supplier<? extends AIService>> namedServices = this.services.get(serviceType);
if (namedServices == null) {
namedServices = new HashMap<>();
services.put(serviceType, namedServices);
}
// Set as the default if the name is empty, or the default flag is true,
// or there is no default name for the service type.
if (name == null || setAsDefault || !this.hasDefault(serviceType)) {
// Update the default name for the service type
this.defaultIds.put(serviceType, name == null ? DefaultKey : name);
}
// Register the factory with the given name
namedServices.put(name == null ? DefaultKey : name, factory);
}
/**
* Builds an AIServiceProvider from the registered services and default names.
*
* @return The AIServiceProvider.
*/
public AIServiceProvider build() {
// Create a clone of the services and defaults Dictionaries to prevent further changes
// by the services provider.
Map<Class<? extends AIService>, Map<String, Supplier<? extends AIService>>> servicesClone =
cloneServices(services);
Map defaultsClone = new HashMap<>(defaultIds);
return new DefaultAIServiceProvider(servicesClone, defaultsClone);
}
/**
* Creates a clone of the services as a Map.
*
* @param services The services Map.
* @return The clone.
*/
static <T> Map<Class<? extends T>, Map<String, Supplier<? extends T>>> cloneServices(
Map<Class<? extends T>, Map<String, Supplier<? extends T>>> services) {
return Collections.unmodifiableMap(
services.entrySet().stream()
.reduce(
new HashMap<>(),
(a, b) -> {
a.put(b.getKey(), Collections.unmodifiableMap(b.getValue()));
return a;
},
(a, b) -> {
a.putAll(b);
return a;
}));
}
private <T extends AIService> boolean hasDefault(Class<T> type) {
String defaultName = defaultIds.get(type);
return !Verify.isNullOrEmpty(defaultName);
}
}
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
public interface AIServiceProvider extends NamedServiceProvider<AIService> {}
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
import java.util.Map;
import java.util.function.Supplier;
/** Default implementation of {@link AIServiceProvider} */
public class DefaultAIServiceProvider extends DefaultNamedServiceProvider<AIService>
implements AIServiceProvider {
public DefaultAIServiceProvider(
Map<Class<? extends AIService>, Map<String, Supplier<? extends AIService>>> services,
Map<Class<? extends AIService>, String> defaultIds) {
super(services, defaultIds);
}
}
@@ -0,0 +1,86 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
import com.microsoft.semantickernel.Verify;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/** Default implementation of {@link NamedServiceProvider} */
public class DefaultNamedServiceProvider<T> implements NamedServiceProvider<T> {
// A dictionary that maps a service type to a nested dictionary of names and service instances
// or factories
// private readonly Dictionary<Type, Dictionary<string, object>> _services = new();
private final Map<Class<? extends T>, Map<String, Supplier<? extends T>>> services;
// A dictionary that maps a service type to the name of the default service
private final Map<Class<? extends T>, String> defaultIds;
public DefaultNamedServiceProvider(
Map<Class<? extends T>, Map<String, Supplier<? extends T>>> services,
Map<Class<? extends T>, String> defaultIds) {
this.services = AIServiceCollection.cloneServices(services);
this.defaultIds = new HashMap<>(defaultIds);
}
/**
* Gets the service of the specified type and name, or null if not found.
*
* @param name The name of the service, or null for the default service.
* @param clazz The type of the service.
* @return The service instance, or null if not found.
*/
@Nullable
@Override
public <U extends T> U getService(@Nullable String name, Class<U> clazz) {
// Return the service, casting or invoking the factory if needed
Supplier<U> factory = this.getServiceFactory(name, clazz);
if (factory != null) {
return factory.get();
}
return null;
}
/**
* Get the default service instance.
*
* @param clazz The type of the service.
* @return The service instance, or null if none.
*/
@Nullable
private <U extends T> String getDefaultServiceName(Class<U> clazz) {
// Returns the name of the default service for the given type, or null if none
String name = this.defaultIds.get(clazz);
if (!Verify.isNullOrEmpty(name)) {
return name;
}
return null;
}
@Nullable
private <U extends T> Supplier<U> getServiceFactory(@Nullable String name, Class<U> clazz) {
// Get the nested dictionary for the service type
Map<String, Supplier<? extends T>> namedServices = services.get(clazz);
if (namedServices != null) {
Supplier<U> serviceFactory = null;
// If the name is not specified, try to load the default factory
name = name == null ? this.getDefaultServiceName(clazz) : name;
if (name != null) {
// Check if there is a service registered with the given name
serviceFactory = (Supplier<U>) namedServices.get(name);
}
return serviceFactory;
}
return null;
}
}
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.services;
import javax.annotation.Nullable;
public interface NamedServiceProvider<T> {
/**
* Gets the service of the specified type and name, or null if not found.
*
* @param name The name of the service, or null for the default service.
* @param clazz The type of the service.
* @return The service instance, or null if not found.
*/
@Nullable
public <U extends T> U getService(@Nullable String name, Class<U> clazz);
}
@@ -2,13 +2,14 @@
package com.microsoft.semantickernel.textcompletion;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.services.AIService;
import reactor.core.publisher.Mono;
import java.util.List;
/** Interface for text completion services */
public interface TextCompletion {
public interface TextCompletion extends AIService {
// TODO: Support Cancellation Token
/**
@@ -7,7 +7,6 @@ import com.microsoft.semantickernel.builders.FunctionBuilders;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.chatcompletion.ChatCompletion;
import com.microsoft.semantickernel.coreskills.SkillImporter;
import com.microsoft.semantickernel.exceptions.NotSupportedException;
import com.microsoft.semantickernel.exceptions.SkillsNotFoundException;
import com.microsoft.semantickernel.extensions.KernelExtensions;
import com.microsoft.semantickernel.memory.MemoryConfiguration;
@@ -16,6 +15,8 @@ import com.microsoft.semantickernel.memory.NullMemory;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
import com.microsoft.semantickernel.orchestration.*;
import com.microsoft.semantickernel.semanticfunctions.SemanticFunctionConfig;
import com.microsoft.semantickernel.services.AIService;
import com.microsoft.semantickernel.services.AIServiceProvider;
import com.microsoft.semantickernel.skilldefinition.DefaultSkillCollection;
import com.microsoft.semantickernel.skilldefinition.FunctionNotFound;
import com.microsoft.semantickernel.skilldefinition.ReadOnlyFunctionCollection;
@@ -41,18 +42,21 @@ public class DefaultKernel implements Kernel {
private final KernelConfig kernelConfig;
private final DefaultSkillCollection defaultSkillCollection;
private final PromptTemplateEngine promptTemplateEngine;
private final AIServiceProvider aiServiceProvider;
private SemanticTextMemory memory;
@Inject
public DefaultKernel(
KernelConfig kernelConfig,
PromptTemplateEngine promptTemplateEngine,
@Nullable SemanticTextMemory memoryStore) {
@Nullable SemanticTextMemory memoryStore,
AIServiceProvider aiServiceProvider) {
if (kernelConfig == null) {
throw new IllegalArgumentException();
}
this.kernelConfig = kernelConfig;
this.aiServiceProvider = aiServiceProvider;
this.promptTemplateEngine = promptTemplateEngine;
this.defaultSkillCollection = new DefaultSkillCollection();
@@ -64,7 +68,14 @@ public class DefaultKernel implements Kernel {
}
@Override
public <T> T getService(@Nullable String serviceId, Class<T> clazz) {
public <T extends AIService> T getService(String serviceId, Class<T> clazz) {
T service = aiServiceProvider.getService(serviceId, clazz);
if (service != null) {
return service;
}
if (TextCompletion.class.isAssignableFrom(clazz)) {
Function<Kernel, TextCompletion> factory =
kernelConfig.getTextCompletionServiceOrDefault(serviceId);
@@ -94,11 +105,25 @@ public class DefaultKernel implements Kernel {
"No chat completion service available");
}
return (T) factory.apply(this);
} else if (EmbeddingGeneration.class.isAssignableFrom(clazz)) {
Function<Kernel, EmbeddingGeneration<String, Float>> factory =
kernelConfig.getTextEmbeddingGenerationServiceOrDefault(serviceId);
if (factory == null) {
throw new KernelException(
KernelException.ErrorCodes.ServiceNotFound,
"No chat completion service available");
}
return (T) factory.apply(this);
} else {
// TODO correct exception
throw new NotSupportedException(
"The kernel service collection doesn't support the type " + clazz.getName());
throw new KernelException(
KernelException.ErrorCodes.ServiceNotFound,
"Service of type "
+ clazz.getName()
+ " and name "
+ serviceId
+ " not registered");
}
}
@@ -311,7 +336,8 @@ public class DefaultKernel implements Kernel {
KernelConfig kernelConfig,
@Nullable PromptTemplateEngine promptTemplateEngine,
@Nullable SemanticTextMemory memory,
@Nullable MemoryStore memoryStore) {
@Nullable MemoryStore memoryStore,
@Nullable AIServiceProvider aiServiceProvider) {
if (promptTemplateEngine == null) {
promptTemplateEngine = new DefaultPromptTemplateEngine();
}
@@ -322,7 +348,9 @@ public class DefaultKernel implements Kernel {
"It is required to set a kernelConfig to build a kernel");
}
DefaultKernel kernel = new DefaultKernel(kernelConfig, promptTemplateEngine, memory);
DefaultKernel kernel =
new DefaultKernel(
kernelConfig, promptTemplateEngine, memory, aiServiceProvider);
if (memoryStore != null) {
MemoryConfiguration.useMemory(kernel, memoryStore, null);
@@ -124,7 +124,7 @@ public class DefaultSkillCollection implements ReadOnlySkillCollection {
return addSemanticFunction(functionInstance);
}
public DefaultSkillCollection merge(DefaultSkillCollection in) {
public DefaultSkillCollection merge(ReadOnlySkillCollection in) {
in.asMap()
.entrySet()
.forEach(
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
import java.util.Collections;
public class AiServiceRegistryTest {
@Test
public void registeredServicesAreAvailableInTheKernel() {
runFunctionWithAiService(true);
}
@Test
public void singleServiceIsRegisteredAsDefault() {
runFunctionWithAiService(false);
}
private static void runFunctionWithAiService(boolean setAsDefault) {
KernelConfig config = SKBuilders.kernelConfig().build();
TextCompletion service = Mockito.mock(TextCompletion.class);
Mockito.when(service.completeAsync(Mockito.any(), Mockito.any()))
.thenReturn(Mono.just(Collections.singletonList("foo")));
Kernel kernel =
SKBuilders.kernel()
.withAIService("a-service", service, setAsDefault, TextCompletion.class)
.withKernelConfig(config)
.build();
Assertions.assertSame(kernel.getService("a-service", TextCompletion.class), service);
CompletionSKFunction function =
kernel.importSkillFromDirectory("FunSkill", "../../samples/skills")
.getFunction("joke", CompletionSKFunction.class);
function.invokeAsync("time travel to dinosaur age").block();
Mockito.verify(service, Mockito.times(1)).completeAsync(Mockito.any(), Mockito.any());
}
@Test
public void noServiceThrowsAnError() {
KernelConfig config = SKBuilders.kernelConfig().build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(config).build();
Assertions.assertThrows(
KernelException.class, () -> kernel.getService("a-service", TextCompletion.class));
Assertions.assertThrows(
KernelException.class,
() -> {
CompletionSKFunction function =
kernel.importSkillFromDirectory("FunSkill", "../../samples/skills")
.getFunction("joke", CompletionSKFunction.class);
function.invokeAsync("time travel to dinosaur age").block();
});
}
}
@@ -119,7 +119,7 @@ public class DefaultKernelTest {
.addTextCompletionService(model, kernel -> textCompletion)
.build();
return SKBuilders.kernel().setKernelConfig(kernelConfig).build();
return SKBuilders.kernel().withKernelConfig(kernelConfig).build();
}
private static OpenAIAsyncClient mockCompletionOpenAIAsyncClient(String arg, String response) {
@@ -46,7 +46,7 @@ public class OpenAIChatCompletionTest {
.build(client, "gpt-3.5-turbo-0301"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
ChatCompletion<OpenAIChatHistory> chatGPT = kernel.getService(null, ChatCompletion.class);
@@ -99,7 +99,7 @@ class AzureCognitiveSearchMemoryTests {
kernel =
SKBuilders.kernel()
.setKernelConfig(SKBuilders.kernelConfig().build())
.withKernelConfig(SKBuilders.kernelConfig().build())
.withMemory(new AzureCognitiveSearchMemory(searchIndexAsyncClient))
.build();
}
@@ -38,7 +38,7 @@ public class Example04CombineLLMPromptsAndNativeCodeTest {
.setDefaultTextCompletionService("text-davinci-003")
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
kernel.importSkill(new SearchEngineSkill(), null);
kernel.importSkillFromDirectory("SummarizeSkill", "../../samples/skills", "SummarizeSkill");
@@ -43,7 +43,7 @@ public class Example05UsingThePlannerTest {
.build(client, "text-davinci-003"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(config).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(config).build();
SequentialPlanner planner = getPlanner(kernel);
System.out.println(
@@ -35,7 +35,7 @@ public class Example06TemplateLanguageTest {
.build(client, "text-davinci-003"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
// Load native skill into the kernel skill collection, sharing its functions
// with prompt
@@ -80,7 +80,7 @@ public class Example12SequentialPlannerTest {
private static Kernel getKernel(OpenAIAsyncClient client) {
Kernel kernel =
SKBuilders.kernel()
.setKernelConfig(
.withKernelConfig(
SKBuilders.kernelConfig()
.addTextCompletionService(
"text-davinci-002",
@@ -48,7 +48,7 @@ public class Example17ChatGPTTest {
.build(client, "gpt-3.5-turbo-0301"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
ChatCompletion<OpenAIChatHistory> chatGPT = kernel.getService(null, ChatCompletion.class);
@@ -123,7 +123,7 @@ public class Example28ActionPlanner {
private static ActionPlanner createPlanner(OpenAIAsyncClient client) {
Kernel kernel =
SKBuilders.kernel()
.setKernelConfig(
.withKernelConfig(
SKBuilders.kernelConfig()
.addTextCompletionService(
"text-davinci-002",