Merge branch 'experimental-java' of https://github.com/microsoft/semantic-kernel into experimental-java

This commit is contained in:
markwallace-microsoft
2023-05-24 11:21:49 +01:00
64 changed files with 643 additions and 495 deletions
+11 -12
View File
@@ -10,7 +10,7 @@ The Semantic Kernel for Java is an SDK that implements the key concepts of the S
to be used in Java applications and services in an idiomatic way, and to be easily integrated with other Java libraries
and frameworks.
## Get Started
## Get started
To run the LLM prompts and semantic functions in this kernel, make sure you have
an [Open AI API Key](https://openai.com/api/)
@@ -18,26 +18,25 @@ or [Azure Open AI service key](https://learn.microsoft.com/azure/cognitive-servi
### Requirements
To build the semantic kernel, you will need to have:
To build the Semantic Kernel, you will need:
- **Required**:
- [JDK 8](https://microsoft.com/openjdk/) or newer to build the Semantic Kernel library
- [JDK 17](https://microsoft.com/openjdk/) or newer to run samples
- [OpenJDK 17](https://microsoft.com/openjdk/) or newer
### Build the semantic kernel
### Build the Semantic Kernel
1. Clone this repository
git clone git@github.com:microsoft/semantic-kernel.git
2. Build the semantic kernel
2. Build the Semantic Kernel
cd semantic-kernel/java
mvn install
./mvnw install
## Using the semantic kernel
## Using the Semantic Kernel
Check the `samples` folder for examples of how to use the semantic kernel.
Check the `samples` folder for examples of how to use the Semantic Kernel for Java.
## Discord community
@@ -48,7 +47,7 @@ and get help from the community. We have a `#java` channel for Java-specific que
### Testing locally
The project may contain integration tests that require an OpenAI key to run. To run the integration tests locally, you
The project may contain end-to-end tests that require an OpenAI key to run. To run these tests locally, you
will need to set the following environment variable:
- `OPENAI_API_KEY` - the OpenAI API key.
@@ -65,11 +64,11 @@ on [how to get your Azure OpenAI credentials](https://learn.microsoft.com/en-us/
To run the unit tests only, run the following command:
mvn package
./mvnw package
To run all tests, including integration tests that require an OpenAI key, run the following command:
mvn verify
./mvnw verify
### Submitting a pull request
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.e2e; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.e2e;
import com.microsoft.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.Kernel;
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.openai; // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.microsoft.openai;
import com.azure.ai.openai.models.*;
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.openai; // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) AutoRest Code Generator.
package com.microsoft.openai;
/** Builds an OpenAIClient for use with <a href="https://api.openai.com/v1">api.openai.com/v1</a> */
public final class OpenAIClientBuilder {
+1
View File
@@ -203,6 +203,7 @@
<licenseHeader>
<content>// Copyright (c) Microsoft. All rights reserved.</content>
</licenseHeader>
<toggleOffOn/>
</java>
</configuration>
</plugin>
@@ -1,4 +1,5 @@
package com.microsoft.semantickernel.syntaxexamples;// Copyright (c) Microsoft. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import com.microsoft.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.Config;
@@ -1,4 +1,5 @@
package com.microsoft.semantickernel.syntaxexamples;// Copyright (c) Microsoft. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
@@ -55,7 +55,7 @@ public interface Kernel {
* @param pipeline List of functions
* @return Result of the function composition
*/
Mono<SKContext<?>> runAsync(SKFunction... pipeline);
Mono<SKContext<?>> runAsync(SKFunction<?, ?>... pipeline);
/**
* Run a pipeline composed of synchronous and asynchronous functions.
@@ -64,7 +64,7 @@ public interface Kernel {
* @param pipeline List of functions
* @return Result of the function composition
*/
Mono<SKContext<?>> runAsync(String input, SKFunction... pipeline);
Mono<SKContext<?>> runAsync(String input, SKFunction<?, ?>... pipeline);
/**
* Run a pipeline composed of synchronous and asynchronous functions.
@@ -73,7 +73,7 @@ public interface Kernel {
* @param pipeline List of functions
* @return Result of the function composition
*/
Mono<SKContext<?>> runAsync(ContextVariables variables, SKFunction... pipeline);
Mono<SKContext<?>> runAsync(ContextVariables variables, SKFunction<?, ?>... pipeline);
/**
* Import a set of skills
@@ -128,8 +128,6 @@ public interface Kernel {
@Nullable private KernelConfig kernelConfig = null;
@Nullable private PromptTemplateEngine promptTemplateEngine = null;
@Nullable private ReadOnlySkillCollection skillCollection = null;
public Builder setKernelConfig(KernelConfig kernelConfig) {
this.kernelConfig = kernelConfig;
return this;
@@ -140,11 +138,6 @@ public interface Kernel {
return this;
}
public Builder setSkillCollection(@Nullable ReadOnlySkillCollection skillCollection) {
this.skillCollection = skillCollection;
return this;
}
public Kernel build() {
if (kernelConfig == null) {
throw new IllegalStateException("Must provide a kernel configuration");
@@ -152,14 +145,12 @@ public interface Kernel {
return BuildersSingleton.INST
.getKernelBuilder()
.build(kernelConfig, promptTemplateEngine, skillCollection);
.build(kernelConfig, promptTemplateEngine);
}
}
interface InternalBuilder {
Kernel build(
KernelConfig kernelConfig,
@Nullable PromptTemplateEngine promptTemplateEngine,
@Nullable ReadOnlySkillCollection skillCollection);
KernelConfig kernelConfig, @Nullable PromptTemplateEngine promptTemplateEngine);
}
}
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.ai; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.ai;
import com.microsoft.semantickernel.SKException;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.ai.embeddings; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.ai.embeddings;
import com.microsoft.openai.OpenAIAsyncClient;
@@ -21,7 +21,7 @@ public enum BuildersSingleton {
private static final String FALLBACK_FUNCTION_BUILDER_CLASS =
"com.microsoft.semantickernel.SkFunctionBuilders";
private static final String FALLBACK_KERNEL_BUILDER_CLASS =
"com.microsoft.semantickernel.KernelDefaultBuilder";
"com.microsoft.semantickernel.DefaultKernelBuilder";
private static final String FALLBACK_TEXT_COMPLETION_BUILDER_CLASS =
"com.microsoft.semantickernel.connectors.ai.openai.textcompletion.OpenAITextCompletionBuilder";
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.memory; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.memory;
import reactor.core.publisher.Mono;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
// Copyright (c) Microsoft. All rights reserved.
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
// Copyright (c) Microsoft. All rights reserved.
import reactor.util.annotation.NonNull;
import java.util.Map;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.semanticfunctions; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.semanticfunctions;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.skilldefinition.ParameterView;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.semanticfunctions; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.semanticfunctions;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.skilldefinition; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.skilldefinition;
/// <summary>
/// Class used to copy and export data from
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.skilldefinition; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.skilldefinition;
import com.microsoft.semantickernel.orchestration.SKFunction;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine;
import com.microsoft.semantickernel.orchestration.SKContext;
@@ -6,20 +6,6 @@ public abstract class Block {
private final String content;
private final BlockTypes type;
/*
// internal virtual bool? SynchronousRendering => null;
/// <summary>
/// The block content
/// </summary>
internal string Content { get; }
/// <summary>
/// App logger
/// </summary>
protected ILogger Log { get; } = NullLogger.Instance;
*/
/// <summary>
/// Base constructor
/// </summary>
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
public enum BlockTypes {
Undefined(0),
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.textcompletion; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.textcompletion;
import com.microsoft.openai.OpenAIAsyncClient;
@@ -23,6 +23,11 @@
<groupId>com.microsoft.semantickernel</groupId>
<artifactId>openai-sdk</artifactId>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.connectors.ai.openai.azuresdk; // Copyright (c) Microsoft. All
// rights reserved.
package com.microsoft.semantickernel.connectors.ai.openai.azuresdk;
import com.microsoft.openai.OpenAIAsyncClient;
@@ -10,6 +10,8 @@ import com.microsoft.semantickernel.connectors.ai.openai.azuresdk.ClientBase;
import com.microsoft.semantickernel.textcompletion.CompletionRequestSettings;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import jakarta.inject.Inject;
import reactor.core.publisher.Mono;
import java.util.Collections;
@@ -29,6 +31,7 @@ public class OpenAITextCompletion extends ClientBase implements TextCompletion {
/// <param name="organization">OpenAI Organization Id (usually optional)</param>
/// <param name="handlerFactory">Retry handler factory for HTTP requests.</param>
/// <param name="log">Application logger</param>
@Inject
public OpenAITextCompletion(OpenAIAsyncClient client, String modelId) {
super(client, modelId);
}
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
import com.microsoft.semantickernel.orchestration.SKContext;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills;
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.coreskills;
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
+11
View File
@@ -90,5 +90,16 @@
<artifactId>semantickernel-core-skills</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.semantickernel.extensions</groupId>
<artifactId>sequentialplanner-extensions</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
@@ -7,7 +7,11 @@ import com.microsoft.semantickernel.coreskills.SkillImporter;
import com.microsoft.semantickernel.exceptions.NotSupportedException;
import com.microsoft.semantickernel.exceptions.SkillsNotFoundException;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
import com.microsoft.semantickernel.orchestration.*;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import com.microsoft.semantickernel.orchestration.DefaultCompletionSKFunction;
import com.microsoft.semantickernel.orchestration.RegistrableSkFunction;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.orchestration.SKFunction;
import com.microsoft.semantickernel.semanticfunctions.SemanticFunctionConfig;
import com.microsoft.semantickernel.skilldefinition.DefaultSkillCollection;
import com.microsoft.semantickernel.skilldefinition.FunctionNotFound;
@@ -17,6 +21,8 @@ import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import jakarta.inject.Inject;
import reactor.core.publisher.Mono;
import java.util.Arrays;
@@ -26,32 +32,37 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class KernelDefault implements Kernel {
public class DefaultKernel implements Kernel {
private final KernelConfig kernelConfig;
private DefaultSkillCollection defaultSkillCollection;
private final DefaultSkillCollection defaultSkillCollection;
private final PromptTemplateEngine promptTemplateEngine;
@Nullable private SemanticTextMemory memory; // TODO: make this final
public KernelDefault(
@Inject
public DefaultKernel(
KernelConfig kernelConfig,
PromptTemplateEngine promptTemplateEngine,
ReadOnlySkillCollection skillCollection) {
@Nullable SemanticTextMemory memory) {
if (kernelConfig == null) {
throw new IllegalArgumentException();
}
this.kernelConfig = kernelConfig;
this.promptTemplateEngine = promptTemplateEngine;
this.defaultSkillCollection = new DefaultSkillCollection(skillCollection);
this.defaultSkillCollection = new DefaultSkillCollection();
if (kernelConfig.getSkills() != null) {
kernelConfig.getSkills().forEach(this::registerSemanticFunction);
if (memory != null) {
this.memory = memory.copy();
} else {
this.memory = null;
}
kernelConfig.getSkills().forEach(this::registerSemanticFunction);
}
@Override
public <T> T getService(@Nullable String serviceId, Class<T> clazz) throws KernelException {
public <T> T getService(String serviceId, Class<T> clazz) {
if (TextCompletion.class.isAssignableFrom(clazz)) {
Function<Kernel, TextCompletion> factory =
kernelConfig.getTextCompletionServiceOrDefault(serviceId);
@@ -84,7 +95,7 @@ public class KernelDefault implements Kernel {
throw new RuntimeException("This function does not implement RegistrableSkFunction");
}
((RegistrableSkFunction) func).registerOnKernel(this);
defaultSkillCollection = defaultSkillCollection.addSemanticFunction(func);
defaultSkillCollection.addSemanticFunction(func);
return func;
}
@@ -136,8 +147,6 @@ public class KernelDefault implements Kernel {
Object skillInstance, @Nullable String skillName) {
if (skillName == null || skillName.isEmpty()) {
skillName = ReadOnlySkillCollection.GlobalSkill;
// this._log.LogTrace("Importing skill {0} in the global namespace",
// skillInstance.GetType().FullName);
}
// skill = new Dictionary<string, ISKFunction>(StringComparer.OrdinalIgnoreCase);
@@ -151,7 +160,7 @@ public class KernelDefault implements Kernel {
DefaultSkillCollection::addNativeFunction,
DefaultSkillCollection::merge);
this.defaultSkillCollection = this.defaultSkillCollection.merge(newSkills);
this.defaultSkillCollection.merge(newSkills);
return functions;
}
@@ -250,27 +259,34 @@ public class KernelDefault implements Kernel {
*/
@Override
public Mono<SKContext<?>> runAsync(SKFunction... pipeline) {
public Mono<SKContext<?>> runAsync(SKFunction<?, ?>... pipeline) {
return runAsync(SKBuilders.variables().build(), pipeline);
}
@Override
public Mono<SKContext<?>> runAsync(String input, SKFunction... pipeline) {
public Mono<SKContext<?>> runAsync(String input, SKFunction<?, ?>... pipeline) {
return runAsync(SKBuilders.variables().build(input), pipeline);
}
@Override
public Mono<SKContext<?>> runAsync(ContextVariables variables, SKFunction... pipeline) {
DefaultSemanticSKContext context =
new DefaultSemanticSKContext(variables, this.memory, this.defaultSkillCollection);
public Mono<SKContext<?>> runAsync(ContextVariables variables, SKFunction<?, ?>... pipeline) {
if (pipeline == null || pipeline.length == 0) {
throw new SKException("No parameters provided to pipeline");
}
Mono<SKContext<?>> pipelineBuilder = Mono.just(context);
Mono<SKContext<?>> pipelineBuilder =
Mono.just(pipeline[0].buildContext(variables, memory, getSkills()));
for (SKFunction f : Arrays.asList(pipeline)) {
pipelineBuilder =
pipelineBuilder.flatMap(
newContext -> {
return f.invokeAsync(newContext, null);
SKContext context =
f.buildContext(
newContext.getVariables(),
newContext.getSemanticMemory(),
newContext.getSkills());
return f.invokeAsync(context, null);
});
}
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel;
import com.microsoft.semantickernel.templateengine.DefaultPromptTemplateEngine;
import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
import javax.annotation.Nullable;
public class DefaultKernelBuilder implements Kernel.InternalBuilder {
@Override
public Kernel build(
KernelConfig kernelConfig, @Nullable PromptTemplateEngine promptTemplateEngine) {
if (promptTemplateEngine == null) {
promptTemplateEngine = new DefaultPromptTemplateEngine();
}
if (kernelConfig == null) {
throw new IllegalArgumentException();
}
return new DefaultKernel(kernelConfig, promptTemplateEngine, null);
}
}
@@ -1,32 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel;
import com.microsoft.semantickernel.skilldefinition.DefaultSkillCollection;
import com.microsoft.semantickernel.skilldefinition.ReadOnlySkillCollection;
import com.microsoft.semantickernel.templateengine.DefaultPromptTemplateEngine;
import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
import javax.annotation.Nullable;
public class KernelDefaultBuilder implements Kernel.InternalBuilder {
@Override
public Kernel build(
KernelConfig kernelConfig,
@Nullable PromptTemplateEngine promptTemplateEngine,
@Nullable ReadOnlySkillCollection skillCollection) {
if (promptTemplateEngine == null) {
promptTemplateEngine = new DefaultPromptTemplateEngine();
}
if (skillCollection == null) {
skillCollection = new DefaultSkillCollection();
}
if (kernelConfig == null) {
throw new IllegalArgumentException();
}
return new KernelDefault(kernelConfig, promptTemplateEngine, skillCollection);
}
}
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
// Copyright (c) Microsoft. All rights reserved.
import com.microsoft.semantickernel.skilldefinition.CaseInsensitiveMap;
import reactor.util.annotation.NonNull;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.memory.NullMemory;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.orchestration;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.memory.SemanticTextMemory;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.semanticfunctions; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.semanticfunctions;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.skilldefinition.ParameterView;
@@ -1,11 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.skilldefinition; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.skilldefinition;
import com.microsoft.semantickernel.orchestration.SKFunction;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@@ -63,27 +61,18 @@ public class DefaultSkillCollection implements ReadOnlySkillCollection {
this.skillCollection = new CaseInsensitiveMap<>();
}
@CheckReturnValue
public DefaultSkillCollection addSemanticFunction(SKFunction functionInstance) {
FunctionCollection existingFunctionCollection;
if (!skillCollection.containsKey(functionInstance.getSkillName().toLowerCase())) {
existingFunctionCollection =
new FunctionCollection(functionInstance.getSkillName().toLowerCase());
if (!skillCollection.containsKey(functionInstance.getSkillName())) {
existingFunctionCollection = new FunctionCollection(functionInstance.getSkillName());
skillCollection.put(functionInstance.getSkillName(), existingFunctionCollection);
} else {
existingFunctionCollection =
skillCollection.get(functionInstance.getSkillName().toLowerCase());
existingFunctionCollection = skillCollection.get(functionInstance.getSkillName());
}
existingFunctionCollection =
existingFunctionCollection.put(
functionInstance.getName().toLowerCase(), functionInstance);
existingFunctionCollection.put(functionInstance.getName(), functionInstance);
HashMap<String, FunctionCollection> clonedSkillCollection = new HashMap<>(skillCollection);
clonedSkillCollection.put(
functionInstance.getSkillName().toLowerCase(), existingFunctionCollection);
return new DefaultSkillCollection(clonedSkillCollection);
return this;
}
@Override
@@ -127,25 +116,21 @@ public class DefaultSkillCollection implements ReadOnlySkillCollection {
}
public DefaultSkillCollection merge(DefaultSkillCollection in) {
HashMap<String, FunctionCollection> clone = new HashMap<>(skillCollection);
in.asMap()
.entrySet()
.forEach(
entry -> {
FunctionCollection existing = clone.get(entry.getKey().toLowerCase());
FunctionCollection existing =
skillCollection.get(entry.getKey().toLowerCase());
if (existing == null) {
clone.put(
skillCollection.put(
entry.getKey().toLowerCase(),
new FunctionCollection(entry.getValue()));
} else {
clone.put(
entry.getKey().toLowerCase(),
existing.merge(new FunctionCollection(entry.getValue())));
existing.merge(new FunctionCollection(entry.getValue()));
}
});
return new DefaultSkillCollection(clone);
return this;
}
@Override
@@ -102,13 +102,9 @@ public class FunctionCollection implements ReadOnlyFunctionCollection {
* @param functionInstance
* @return Collection for fluent callse
*/
@CheckReturnValue
public FunctionCollection put(String functionName, SKFunction<?, ?> functionInstance) {
HashMap<String, SKFunction<?, ?>> functionCollection =
new HashMap<>(this.functionCollection);
functionCollection.put(functionName.toLowerCase(), functionInstance);
return new FunctionCollection(skillName, functionCollection);
this.functionCollection.put(functionName.toLowerCase(), functionInstance);
return this;
}
/**
@@ -118,10 +114,7 @@ public class FunctionCollection implements ReadOnlyFunctionCollection {
* @return Collection for fluent calls
*/
public FunctionCollection merge(FunctionCollection value) {
HashMap<String, SKFunction<?, ?>> mutable = new HashMap<>(functionCollection);
mutable.putAll(value.functionCollection);
return new FunctionCollection(skillName, mutable);
functionCollection.putAll(value.functionCollection);
return this;
}
}
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine;
import com.microsoft.semantickernel.templateengine.blocks.*;
@@ -169,10 +168,9 @@ public class CodeTokenizer {
if (currentTokenType == TokenTypes.None) {
if (!spaceSeparatorFound) {
throw new TemplateException();
// TODO error code
// throw new TemplateException(TemplateException.ErrorCodes.SyntaxError,
// "Tokens must be separated by one space least");
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Tokens must be separated by one space least");
}
if (isQuote(currentChar)) {
@@ -205,10 +203,9 @@ public class CodeTokenizer {
break;
case None:
throw new TemplateException();
// TODO error code
// throw new TemplateException(TemplateException.ErrorCodes.SyntaxError,
// "Tokens must be separated by one space least");
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Tokens must be separated by one space least");
}
return blocks;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.templateengine.blocks.Block;
@@ -8,6 +7,9 @@ import com.microsoft.semantickernel.templateengine.blocks.CodeBlock;
import com.microsoft.semantickernel.templateengine.blocks.CodeRendering;
import com.microsoft.semantickernel.templateengine.blocks.TextRendering;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -25,7 +27,7 @@ import java.util.List;
// immutable when invoked within the template
/// </summary>
public class DefaultPromptTemplateEngine implements PromptTemplateEngine {
// private readonly ILogger _log;
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPromptTemplateEngine.class);
private final TemplateTokenizer tokenizer;
@@ -46,9 +48,8 @@ public class DefaultPromptTemplateEngine implements PromptTemplateEngine {
if (validate) {
blocks.forEach(
block -> {
// TODO ERROR MESSAGE
if (!block.isValid()) {
throw new TemplateException();
throw new TemplateException(TemplateException.ErrorCodes.SyntaxError);
}
});
}
@@ -76,13 +77,15 @@ public class DefaultPromptTemplateEngine implements PromptTemplateEngine {
} else if (block instanceof CodeRendering) {
return ((CodeBlock) block).renderCodeAsync(context);
} else {
// const string error = "Unexpected block type, the block doesn't
// have a rendering method";
// this._log.LogError(error);
// throw new
// TemplateException(TemplateException.ErrorCodes.UnexpectedBlockType, error);
String message =
"Unexpected block type, the block doesn't have a rendering"
+ " method";
LOGGER.error(message);
return Mono.error(
new TemplateException(
TemplateException.ErrorCodes.UnexpectedBlockType,
message));
}
return Mono.just("");
})
.collectList()
.map(
@@ -3,4 +3,135 @@ package com.microsoft.semantickernel.templateengine;
import com.microsoft.semantickernel.SKException;
public class TemplateException extends SKException {}
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class TemplateException extends SKException {
@Nonnull private final ErrorCodes errorCode;
/**
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
*
* @param error The error code.
*/
public TemplateException(@Nonnull ErrorCodes error) {
this(error, null, null);
}
/**
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
*
* @param errorCode The error code.
* @param message The exception message.
*/
public TemplateException(@Nonnull ErrorCodes errorCode, @Nullable String message) {
this(errorCode, message, null);
}
/**
* Initializes a new instance of the {@code TemplateException} class with a provided error code.
*
* @param errorCode The error code.
* @param message The exception message.
* @param innerException The exception that is the cause of the current exception.
*/
public TemplateException(
@Nonnull ErrorCodes errorCode,
@Nullable String message,
@Nullable Throwable innerException) {
super(getDefaultMessage(errorCode, message), innerException);
this.errorCode = errorCode;
}
// spotless:off
/**
* <p>
* Gets the error code for this exception.
* </p>
*/
//spotless:on
public ErrorCodes getErrorCode() {
return errorCode;
}
// spotless:off
/**
* <p>
* Translate the error code into a default message.
* </p>
*/
//spotless:on
private static String getDefaultMessage(
@Nonnull ErrorCodes errorCode, @Nullable String message) {
return String.format("%s: %s", errorCode.getMessage(), message);
}
// spotless:off
/**
* <p>
* Error codes for {@code TemplateException}.
* </p>
*/
//spotless:on
public enum ErrorCodes {
// spotless:off
/**
* <p>
* Unknown error.
* </p>
*/
//spotless:on
UnknownError("Unknown error"),
// spotless:off
/**
* <p>
* Syntax error, the template syntax used is not valid.
* </p>
*/
//spotless:on
SyntaxError("Syntax error, the template syntax used is not valid"),
// spotless:off
/**
* <p>
* The block type produced be the tokenizer was not expected.
* </p>
*/
UnexpectedBlockType("The block type produced be the tokenizer was not expected"),
//spotless:off
/**
* <p>
* The template requires an unknown function.
* </p>
*/
//spotless:on
FunctionNotFound("The template requires an unknown function"),
// spotless:off
/**
* <p>
* The template execution failed, e.g. a function call threw an exception.
* </p>
*/
//spotless:on
RuntimeError("The template execution failed, e.g. a function call threw an exception"),
;
private final String message;
ErrorCodes(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
}
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine;
import com.microsoft.semantickernel.templateengine.blocks.Block;
import com.microsoft.semantickernel.templateengine.blocks.CodeBlock;
@@ -69,7 +68,7 @@ public class TemplateTokenizer {
return Collections.singletonList(new TextBlock(text));
}
List<Block> blocks = new ArrayList<Block>();
List<Block> blocks = new ArrayList<>();
int endOfLastBlock = 0;
@@ -148,12 +147,10 @@ public class TemplateTokenizer {
switch (codeBlocks.get(0).getType()) {
case Variable:
if (codeBlocks.size() > 1) {
throw new TemplateException();
// TODO errorcode
// throw new
// TemplateException(TemplateException.ErrorCodes.SyntaxError,
// $"Invalid token detected after the variable:
// {contentWithoutDelimiters}");
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Invalid token detected after the variable: "
+ contentWithoutDelimiters);
}
blocks.add(codeBlocks.get(0));
@@ -161,12 +158,10 @@ public class TemplateTokenizer {
case Value:
if (codeBlocks.size() > 1) {
throw new TemplateException();
// TODO errorcode
// throw new
// TemplateException(TemplateException.ErrorCodes.SyntaxError,
// $"Invalid token detected after the value:
// {contentWithoutDelimiters}");
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Invalid token detected after the value: "
+ contentWithoutDelimiters);
}
blocks.add(codeBlocks.get(0));
@@ -174,12 +169,10 @@ public class TemplateTokenizer {
case FunctionId:
if (codeBlocks.size() > 2) {
throw new TemplateException();
// TODO errorcode
// throw new
// TemplateException(TemplateException.ErrorCodes.SyntaxError,
// $"Functions support only one parameter:
// {contentWithoutDelimiters}");
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Functions support only one parameter: "
+ contentWithoutDelimiters);
}
blocks.add(new CodeBlock(codeBlocks, contentWithoutDelimiters));
@@ -189,12 +182,10 @@ public class TemplateTokenizer {
case Text:
case Undefined:
default:
throw new TemplateException();
// TODO errorcode
// throw new
// TemplateException(TemplateException.ErrorCodes.UnexpectedBlockType,
// $"Code tokenizer returned an incorrect first token
// type {codeBlocks[0].Type:G}");
throw new TemplateException(
TemplateException.ErrorCodes.UnexpectedBlockType,
"Code tokenizer returned an incorrect first token type "
+ codeBlocks.get(0).getType());
}
}
@@ -1,21 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.orchestration.SKFunction;
import com.microsoft.semantickernel.skilldefinition.ReadOnlyFunctionCollection;
import com.microsoft.semantickernel.skilldefinition.ReadOnlySkillCollection;
import com.microsoft.semantickernel.templateengine.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import reactor.util.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
// ReSharper disable TemplateIsNotCompileTimeConstantProblem
public class CodeBlock extends Block implements CodeRendering {
public final class CodeBlock extends Block implements CodeRendering {
private static final Logger LOGGER = LoggerFactory.getLogger(CodeBlock.class);
private final List<Block> tokens;
public CodeBlock(List<Block> tokens, String content) {
@@ -30,21 +35,39 @@ public class CodeBlock extends Block implements CodeRendering {
@Override
public boolean isValid() {
// TODO
Optional<Block> invalid = tokens.stream().filter(token -> !token.isValid()).findFirst();
if (invalid.isPresent()) {
LOGGER.error("Invalid block" + invalid.get().getContent());
return false;
}
if (this.tokens.size() > 1) {
if (this.tokens.get(0).getType() != BlockTypes.FunctionId) {
LOGGER.error("Unexpected second token found: " + this.tokens.get(1).getContent());
return false;
}
if (this.tokens.get(1).getType() != BlockTypes.Value
&& this.tokens.get(1).getType() != BlockTypes.Variable) {
LOGGER.error("Functions support only one parameter");
return false;
}
}
if (this.tokens.size() > 2) {
LOGGER.error("Unexpected second token found: " + this.tokens.get(1).getContent());
return false;
}
return true;
}
@Override
@Nullable
public Mono<String> renderCodeAsync(SKContext context) {
/* TODO
if (!this.isValid()) {
throw new TemplateException(TemplateException.ErrorCodes.SyntaxError);
}
if (!this._validated && !this.IsValid(out var error))
{
throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, error);
}
*/
// this.Log.LogTrace("Rendering code: `{0}`", this.Content);
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.SKContext;
@@ -1,12 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import javax.annotation.Nullable;
public class FunctionIdBlock extends Block implements TextRendering {
public final class FunctionIdBlock extends Block implements TextRendering {
private final String skillName;
private final String functionName;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
public class Symbols {
public static final char BlockStarter = '{';
@@ -1,12 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
public class TextBlock extends Block implements TextRendering {
// internal override BlockTypes Type => BlockTypes.Text;
public final class TextBlock extends Block implements TextRendering {
public TextBlock(String text) {
super(text, BlockTypes.Text);
@@ -25,18 +22,4 @@ public class TextBlock extends Block implements TextRendering {
public TextBlock(String text, int startIndex, int stopIndex) {
super(text.substring(startIndex, stopIndex), BlockTypes.Text);
}
/*
public override bool IsValid(out string errorMsg)
{
errorMsg = "";
return true;
}
public string Render(ContextVariables? variables)
{
return this.Content;
}
*/
}
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
@@ -1,91 +1,65 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.util.annotation.Nullable;
public class ValBlock extends Block implements TextRendering {
public ValBlock(String content) {
super(content, BlockTypes.Value);
}
public final class ValBlock extends Block implements TextRendering {
@Override
public boolean isValid() {
return false;
private static final Logger LOGGER = LoggerFactory.getLogger(ValBlock.class);
// Cache the first and last char
private char first = '\0';
private char last = '\0';
// Content, excluding start/end quote chars
private String value = "";
public ValBlock(String quotedValue) {
super(quotedValue.trim(), BlockTypes.Value);
if (this.getContent().length() < 2) {
LOGGER.error("A value must have single quotes or double quotes on both sides");
return;
}
this.first = this.getContent().charAt(0);
this.last = this.getContent().charAt(getContent().length() - 1);
this.value = this.getContent().substring(1, this.getContent().length() - 2);
}
@Override
@Nullable
public String render(ContextVariables variables) {
return null;
return value;
}
/*
internal override BlockTypes Type => BlockTypes.Value;
// Cache the first and last char
private readonly char _first = '\0';
private readonly char _last = '\0';
// Content, excluding start/end quote chars
private readonly string _value = string.Empty;
/// <summary>
/// Create an instance
/// </summary>
/// <param name="quotedValue">Block content, including the delimiting chars</param>
/// <param name="log">Optional logger</param>
public ValBlock(string? quotedValue, ILogger? log = null)
: base(quotedValue?.Trim(), log)
{
if (this.Content.Length < 2)
{
this.Log.LogError("A value must have single quotes or double quotes on both sides");
return;
}
this._first = this.Content[0];
this._last = this.Content[this.Content.Length - 1];
this._value = this.Content.Substring(1, this.Content.Length - 2);
@Override
public boolean isValid() {
// Content includes the quotes, so it must be at least 2 chars long
if (this.getContent().length() < 2) {
LOGGER.error("A value must have single quotes or double quotes on both sides");
return false;
}
#pragma warning disable CA2254 // error strings are used also internally, not just for logging
// ReSharper disable TemplateIsNotCompileTimeConstantProblem
public override bool IsValid(out string errorMsg)
{
errorMsg = string.Empty;
// Content includes the quotes, so it must be at least 2 chars long
if (this.Content.Length < 2)
{
errorMsg = "A value must have single quotes or double quotes on both sides";
this.Log.LogError(errorMsg);
return false;
}
// Check if delimiting chars are consistent
if (this._first != this._last)
{
errorMsg = "A value must be defined using either single quotes or double quotes, not both";
this.Log.LogError(errorMsg);
return false;
}
return true;
}
#pragma warning restore CA2254
public string Render(ContextVariables? variables)
{
return this._value;
// Check if delimiting chars are consistent
if (first != last) {
LOGGER.error(
"A value must be defined using either single quotes or double quotes, not"
+ " both");
return false;
}
public static bool HasValPrefix(string? text)
{
return !text.IsNullOrEmpty()
&& text.Length > 0
&& (text[0] is Symbols.DblQuote or Symbols.SglQuote);
}*/
return true;
}
public static boolean hasValPrefix(@Nullable String text) {
return text != null
&& text.length() > 0
&& (text.charAt(0) == Symbols.DblQuote || text.charAt(0) == Symbols.SglQuote);
}
}
@@ -1,23 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.templateengine.blocks; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.templateengine.blocks;
import com.microsoft.semantickernel.orchestration.ContextVariables;
import com.microsoft.semantickernel.templateengine.TemplateException;
public class VarBlock extends Block implements TextRendering {
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class VarBlock extends Block implements TextRendering {
private static final Logger LOGGER = LoggerFactory.getLogger(VarBlock.class);
private final String name;
public VarBlock(String content) {
super(content, BlockTypes.Variable);
this.name = content.substring(1);
}
if (content.length() < 2) {
LOGGER.error("The variable name is empty");
}
@Override
public boolean isValid() {
return true;
this.name = content.substring(1);
}
@Override
@@ -27,93 +29,47 @@ public class VarBlock extends Block implements TextRendering {
}
if (name == null || name.isEmpty()) {
// TODO
// const string errMsg = "Variable rendering failed, the variable name is empty";
// this.Log.LogError(errMsg);
// throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, errMsg);
throw new TemplateException();
throw new TemplateException(
TemplateException.ErrorCodes.SyntaxError,
"Variable rendering failed, the variable name is empty");
}
String value = variables.asMap().get(name);
if (value == null) {
// TODO
// this.Log.LogWarning("Variable `{0}{1}` not found", Symbols.VarPrefix, this.Name);
LOGGER.warn("Variable `{}{}` not found", Symbols.VarPrefix, name);
}
return value != null ? value : "";
}
/*
internal override BlockTypes Type => BlockTypes.Variable;
internal string Name { get; } = string.Empty;
public VarBlock(string? content, ILogger? log = null) : base(content?.Trim(), log)
{
if (this.Content.Length < 2)
{
this.Log.LogError("The variable name is empty");
return;
}
this.Name = this.Content.Substring(1);
@Override
public boolean isValid() {
if (getContent() == null || getContent().isEmpty()) {
LOGGER.error(
"A variable must start with the symbol {} and have a name", Symbols.VarPrefix);
return false;
}
#pragma warning disable CA2254 // error strings are used also internally, not just for logging
// ReSharper disable TemplateIsNotCompileTimeConstantProblem
public override bool IsValid(out string errorMsg)
{
errorMsg = string.Empty;
if (string.IsNullOrEmpty(this.Content))
{
errorMsg = $"A variable must start with the symbol {Symbols.VarPrefix} and have a name";
this.Log.LogError(errorMsg);
return false;
}
if (this.Content[0] != Symbols.VarPrefix)
{
errorMsg = $"A variable must start with the symbol {Symbols.VarPrefix}";
this.Log.LogError(errorMsg);
return false;
}
if (this.Content.Length < 2)
{
errorMsg = "The variable name is empty";
this.Log.LogError(errorMsg);
return false;
}
if (!Regex.IsMatch(this.Name, "^[a-zA-Z0-9_]*$"))
{
errorMsg = $"The variable name '{this.Name}' contains invalid characters. " +
"Only alphanumeric chars and underscore are allowed.";
this.Log.LogError(errorMsg);
return false;
}
return true;
}
#pragma warning restore CA2254
public string Render(ContextVariables? variables)
{
if (variables == null) { return string.Empty; }
if (string.IsNullOrEmpty(this.Name))
{
const string errMsg = "Variable rendering failed, the variable name is empty";
this.Log.LogError(errMsg);
throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, errMsg);
}
var exists = variables.Get(this.Name, out string value);
if (!exists) { this.Log.LogWarning("Variable `{0}{1}` not found", Symbols.VarPrefix, this.Name); }
return exists ? value : string.Empty;
if (getContent().charAt(0) != Symbols.VarPrefix) {
LOGGER.error("A variable must start with the symbol {}", Symbols.VarPrefix);
return false;
}
*/
if (getContent().length() < 2) {
LOGGER.error("The variable name is empty");
return false;
}
if (!name.matches("^[a-zA-Z0-9_]*$")) {
LOGGER.error(
"The variable name '{}' contains invalid characters. "
+ "Only alphanumeric chars and underscore are allowed.",
name);
return false;
}
return true;
}
}
@@ -1 +1 @@
com.microsoft.semantickernel.KernelDefaultBuilder
com.microsoft.semantickernel.DefaultKernelBuilder
@@ -5,6 +5,7 @@ import com.azure.ai.openai.models.Choice;
import com.azure.ai.openai.models.Completions;
import com.azure.ai.openai.models.CompletionsOptions;
import com.microsoft.openai.AzureOpenAIClient;
import com.microsoft.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.connectors.ai.openai.textcompletion.OpenAITextCompletion;
import com.microsoft.semantickernel.extensions.KernelExtensions;
@@ -17,6 +18,7 @@ import com.microsoft.semantickernel.textcompletion.TextCompletion;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
@@ -27,6 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class DefaultKernelTest {
@@ -34,10 +37,9 @@ public class DefaultKernelTest {
void contextVariableTest() {
String model = "a-model";
List<Tuple2<String, String>> responses =
Arrays.asList(Tuples.of("A", "a"), Tuples.of("user: Aauser: B", "b"));
com.azure.ai.openai.OpenAIAsyncClient client = mockCompletionOpenAIAsyncClient(responses);
OpenAIAsyncClient client =
mockCompletionOpenAIAsyncClient(
Tuples.of("A", "a"), Tuples.of("user: Aauser: B", "b"));
Kernel kernel = buildKernel(model, client);
@@ -82,8 +84,7 @@ public class DefaultKernelTest {
@Test
void tellAJoke() {
String expectedResponse = "a result joke";
com.azure.ai.openai.OpenAIAsyncClient client =
mockCompletionOpenAIAsyncClient("WRITE", expectedResponse);
OpenAIAsyncClient client = mockCompletionOpenAIAsyncClient("WRITE", expectedResponse);
String model = "a-model-name";
Kernel kernel = buildKernel(model, client);
@@ -117,10 +118,7 @@ public class DefaultKernelTest {
assertTheResultEquals(result, expectedResponse);
}
public static Kernel buildKernel(
String model, com.azure.ai.openai.OpenAIAsyncClient openAIAsyncClient) {
com.microsoft.openai.OpenAIAsyncClient client = new AzureOpenAIClient(openAIAsyncClient);
public static Kernel buildKernel(String model, OpenAIAsyncClient client) {
TextCompletion textCompletion = new OpenAITextCompletion(client, model);
@@ -132,12 +130,11 @@ public class DefaultKernelTest {
return SKBuilders.kernel().setKernelConfig(kernelConfig).build();
}
private static com.azure.ai.openai.OpenAIAsyncClient mockCompletionOpenAIAsyncClient(
String arg, String response) {
private static AzureOpenAIClient mockCompletionOpenAIAsyncClient(String arg, String response) {
List<Tuple2<String, String>> responses =
Collections.singletonList(Tuples.of(arg, response));
return mockCompletionOpenAIAsyncClient(responses);
return mockCompletionOpenAIAsyncClient(responses.toArray(new Tuple2[0]));
}
/*
@@ -152,12 +149,28 @@ public class DefaultKernelTest {
This if the client is prompted with "Tell me a joke", the mocked client would respond with "This is a joke"
*/
public static com.azure.ai.openai.OpenAIAsyncClient mockCompletionOpenAIAsyncClient(
List<Tuple2<String, String>> responses) {
public static AzureOpenAIClient mockCompletionOpenAIAsyncClient(
Tuple2<String, String>... responses) {
List<Tuple2<ArgumentMatcher<String>, String>> matchers =
Arrays.stream(responses)
.map(
response ->
Tuples.<ArgumentMatcher<String>, String>of(
arg -> arg.contains(response.getT1()),
response.getT2()))
.collect(Collectors.toList());
return mockCompletionOpenAIAsyncClientMatchers(matchers.toArray(new Tuple2[0]));
}
public static AzureOpenAIClient mockCompletionOpenAIAsyncClientMatchers(
Tuple2<ArgumentMatcher<String>, String>... responses) {
com.azure.ai.openai.OpenAIAsyncClient openAIAsyncClient =
Mockito.mock(com.azure.ai.openai.OpenAIAsyncClient.class);
for (Tuple2<String, String> response : responses) {
for (Tuple2<ArgumentMatcher<String>, String> response : responses) {
Choice choice = Mockito.mock(Choice.class);
Mockito.when(choice.getText()).thenReturn(response.getT2());
@@ -170,13 +183,11 @@ public class DefaultKernelTest {
openAIAsyncClient.getCompletions(
Mockito.any(String.class),
Mockito.<CompletionsOptions>argThat(
it ->
it.getPrompt()
.get(0)
.contains(response.getT1()))))
it -> response.getT1().matches(it.getPrompt().get(0)))))
.thenReturn(Mono.just(completions));
}
return openAIAsyncClient;
AzureOpenAIClient client = new AzureOpenAIClient(openAIAsyncClient);
return Mockito.spy(client);
}
@Test
@@ -185,7 +196,7 @@ public class DefaultKernelTest {
String model = "a-model";
String expectedResponse = "foo";
com.azure.ai.openai.OpenAIAsyncClient openAIAsyncClient =
OpenAIAsyncClient openAIAsyncClient =
mockCompletionOpenAIAsyncClient("block", expectedResponse);
Kernel kernel = buildKernel(model, openAIAsyncClient);
@@ -219,13 +230,11 @@ public class DefaultKernelTest {
}
private static void assertCompletionsWasCalledWithModelAndText(
com.azure.ai.openai.OpenAIAsyncClient openAIAsyncClient,
String model,
String expected) {
OpenAIAsyncClient openAIAsyncClient, String model, String expected) {
Mockito.verify(openAIAsyncClient, Mockito.times(1))
.getCompletions(
Mockito.matches(model),
Mockito.<CompletionsOptions>argThat(
Mockito.argThat(
completionsOptions ->
completionsOptions.getPrompt().size() == 1
&& completionsOptions
@@ -8,7 +8,6 @@ import com.microsoft.semantickernel.ai.embeddings.EmbeddingGeneration;
import com.microsoft.semantickernel.connectors.ai.openai.textembeddings.OpenAITextEmbeddingGeneration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -43,7 +42,6 @@ public class TextEmbeddingsTest {
}
@Test
@Disabled
public void testEmbedding() {
testEmbeddingGeneration(mockEmbeddingOpenAIAsyncClient(), 1);
}
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import com.microsoft.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.DefaultKernelTest;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.orchestration.SKContext;
@@ -12,13 +13,10 @@ import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
public class Example02PipelineTest {
@Test
public void run() {
com.azure.ai.openai.OpenAIAsyncClient client =
DefaultKernelTest.mockCompletionOpenAIAsyncClient(new ArrayList<>());
OpenAIAsyncClient client = DefaultKernelTest.mockCompletionOpenAIAsyncClient();
Kernel kernel = DefaultKernelTest.buildKernel("model", client);
// Load native skill
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import com.microsoft.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.DefaultKernelTest;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.builders.SKBuilders;
@@ -14,13 +15,10 @@ import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
public class Example03VariablesTest {
@Test
public void run() {
com.azure.ai.openai.OpenAIAsyncClient client =
DefaultKernelTest.mockCompletionOpenAIAsyncClient(new ArrayList<>());
OpenAIAsyncClient client = DefaultKernelTest.mockCompletionOpenAIAsyncClient();
Kernel kernel = DefaultKernelTest.buildKernel("model", client);
// Load native skill
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.syntaxexamples;
import static com.microsoft.semantickernel.DefaultKernelTest.mockCompletionOpenAIAsyncClient;
import com.microsoft.openai.AzureOpenAIClient;
import com.microsoft.semantickernel.DefaultKernelTest;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.KernelConfig;
import com.microsoft.semantickernel.builders.SKBuilders;
@@ -16,17 +16,17 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;
import java.util.ArrayList;
// ReSharper disable once InconsistentNaming
public class Example04CombineLLMPromptsAndNativeCode {
public class Example04CombineLLMPromptsAndNativeCodeTest {
@Test
public void run() {
com.microsoft.openai.OpenAIAsyncClient client =
new AzureOpenAIClient(
DefaultKernelTest.mockCompletionOpenAIAsyncClient(new ArrayList<>()));
AzureOpenAIClient client =
mockCompletionOpenAIAsyncClient(
Tuples.of(
"Gran Torre Santiago is the tallest building in South America",
"A-SUMMARY"));
KernelConfig kernelConfig =
SKBuilders.kernelConfig()
@@ -62,28 +62,6 @@ public class Example04CombineLLMPromptsAndNativeCode {
kernel.getSkills().getFunction("Search", null),
kernel.getSkill("SummarizeSkill").getFunction("Summarize", null));
Assertions.assertEquals(
"Gran Torre Santiago is the tallest building in South America summary",
result.block().getResult());
/*
var result2 = await kernel.RunAsync(
ask,
search["Search"],
sumSkill["Summarize"]
);
var result3 = await kernel.RunAsync(
ask,
search["Search"],
sumSkill["Notegen"]
);
Console.WriteLine(ask + "\n");
Console.WriteLine("Bing Answer: " + result1 + "\n");
Console.WriteLine("Summary: " + result2 + "\n");
Console.WriteLine("Notes: " + result3 + "\n");
*/
Assertions.assertEquals("A-SUMMARY", result.block().getResult());
}
}
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import static com.microsoft.semantickernel.DefaultKernelTest.mockCompletionOpenAIAsyncClientMatchers;
import com.microsoft.openai.AzureOpenAIClient;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.KernelConfig;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.extensions.KernelExtensions;
import com.microsoft.semantickernel.planner.sequentialplanner.SequentialPlanner;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import reactor.util.function.Tuples;
public class Example05UsingThePlannerTest {
public static SequentialPlanner getPlanner(Kernel kernel) {
kernel.importSkill(
"SummarizeSkill",
KernelExtensions.importSemanticSkillFromDirectory(
"../../samples/skills", "SummarizeSkill"));
kernel.importSkill(
"WriterSkill",
KernelExtensions.importSemanticSkillFromDirectory(
"../../samples/skills", "WriterSkill"));
return new SequentialPlanner(kernel, null, null);
}
@Test
public void run() {
ArgumentMatcher<String> matcher =
prompt -> {
return prompt.contains(
"Create an XML plan step by step, to satisfy the goal given");
};
AzureOpenAIClient client =
mockCompletionOpenAIAsyncClientMatchers(Tuples.of(matcher, "A-PLAN"));
KernelConfig config =
SKBuilders.kernelConfig()
.addTextCompletionService(
"davinci",
kernel ->
SKBuilders.textCompletionService()
.build(client, "text-davinci-003"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(config).build();
SequentialPlanner planner = getPlanner(kernel);
System.out.println(
planner.createPlanAsync(
"Write a poem about John Doe, then translate it into Italian.")
.block()
.getResult());
}
}
@@ -0,0 +1,89 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples;
import static com.microsoft.semantickernel.DefaultKernelTest.mockCompletionOpenAIAsyncClient;
import com.microsoft.openai.AzureOpenAIClient;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.KernelConfig;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.coreskills.TimeSkill;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplate;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
import com.microsoft.semantickernel.templateengine.PromptTemplateEngine;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;
import java.util.ArrayList;
public class Example06TemplateLanguageTest {
@Test
public void run() {
AzureOpenAIClient client =
mockCompletionOpenAIAsyncClient(Tuples.of("Today is", "A-RESULT"));
KernelConfig kernelConfig =
SKBuilders.kernelConfig()
.addTextCompletionService(
"text-davinci-003",
kernel ->
SKBuilders.textCompletionService()
.build(client, "text-davinci-003"))
.addTextEmbeddingsGenerationService(
"text-embedding-ada-002",
kernel ->
SKBuilders.textEmbeddingGenerationService()
.build(client, "text-embedding-ada-002"))
.build();
Kernel kernel = SKBuilders.kernel().setKernelConfig(kernelConfig).build();
// Load native skill into the kernel skill collection, sharing its functions with prompt
// templates
// Functions loaded here are available as "time.*"
kernel.importSkill(new TimeSkill(), "time");
// Semantic Function invoking time.Date and time.Time native functions
String functionDefinition =
"\n"
+ "Today is: {{time.Date}}\n"
+ "Current time is: {{time.Time}}\n"
+ "\n"
+ "Answer to the following questions using JSON syntax, including the data"
+ " used.\n"
+ "Is it morning, afternoon, evening, or night"
+ " (morning/afternoon/evening/night)?\n"
+ "Is it weekend time (weekend/not weekend)?";
PromptTemplate promptRenderer = SKBuilders.promptTemplate().build(functionDefinition, null);
SKContext skContext = SKBuilders.context().build(kernel.getSkills());
PromptTemplateEngine promptTemplateEngine = SKBuilders.promptTemplateEngine().build();
Mono<String> renderedPrompt = promptRenderer.renderAsync(skContext, promptTemplateEngine);
// Check that it has been rendered
Assertions.assertTrue(!renderedPrompt.block().contains("time.Date"));
// Run the prompt / semantic function
CompletionSKFunction kindOfDay =
kernel.getSemanticFunctionBuilder()
.createFunction(
functionDefinition,
null,
null,
null,
new PromptTemplateConfig.CompletionConfig(
0, 0, 0, 0, 256, new ArrayList<>()));
Assertions.assertEquals("A-RESULT", kindOfDay.invokeAsync("").block().getResult());
}
}
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples.skills; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.syntaxexamples.skills;
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.syntaxexamples.skills; // Copyright (c) Microsoft. All rights
// reserved.
package com.microsoft.semantickernel.syntaxexamples.skills;
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.planner; // Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.planner;
import java.util.Collections;
import java.util.HashSet;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.planner.sequentialplanner; // Copyright (c) Microsoft. All
// rights reserved.
package com.microsoft.semantickernel.planner.sequentialplanner;
import java.io.IOException;
import java.io.InputStream;
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.planner.sequentialplanner; // Copyright (c) Microsoft. All
// rights reserved.
package com.microsoft.semantickernel.planner.sequentialplanner;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.builders.FunctionBuilders;