Java: Add documentation for configuring samples and add maven exec (#1850)

- Add documentation for running samples via maven exec.
- Add support for defining OpenAI client type without recompiling
- Fix detecting the sample skills directory when invoking from maven
exec
This commit is contained in:
John Oliver
2023-07-12 11:41:05 +01:00
committed by GitHub
parent 2b86b1f82c
commit 51819bc0a4
56 changed files with 982 additions and 353 deletions
+5
View File
@@ -64,5 +64,10 @@
<artifactId>semantickernel-core-skills</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.semantickernel</groupId>
<artifactId>sample-code</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
@@ -2,16 +2,12 @@
package com.microsoft.semantickernel.e2e;
import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.ai.openai.models.NonAzureOpenAIKeyCredential;
import com.azure.core.credential.AzureKeyCredential;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.KernelConfig;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.connectors.ai.openai.textcompletion.OpenAITextCompletion;
import com.microsoft.semantickernel.memory.VolatileMemoryStore;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import org.junit.jupiter.api.condition.EnabledIf;
import java.io.File;
@@ -27,7 +23,7 @@ public class AbstractKernelTest {
public static Kernel buildTextCompletionKernel() throws IOException {
String model = "text-davinci-003";
final OpenAIAsyncClient openAIClient = getAzureOpenAIClient();
final OpenAIAsyncClient openAIClient = getOpenAIClient();
TextCompletion textCompletion = new OpenAITextCompletion(openAIClient, model);
KernelConfig kernelConfig =
@@ -47,35 +43,13 @@ public class AbstractKernelTest {
}
public static OpenAIAsyncClient getOpenAIClient() throws IOException {
String apiKey = getToken(CONF_OPENAI_PROPERTIES);
NonAzureOpenAIKeyCredential credential = new NonAzureOpenAIKeyCredential(apiKey);
return new OpenAIClientBuilder().credential(credential).buildAsyncClient();
}
public static OpenAIAsyncClient getAzureOpenAIClient() throws IOException {
String apiKey = getToken(AZURE_CONF_PROPERTIES);
OpenAIAsyncClient client =
new OpenAIClientBuilder()
.endpoint(getEndpoint(AZURE_CONF_PROPERTIES))
.credential(new AzureKeyCredential(apiKey))
.buildAsyncClient();
return client;
}
public static String getAzureModel() throws IOException {
return getConfigValue(AZURE_CONF_PROPERTIES, "model");
return com.microsoft.semantickernel.Config.getClient();
}
public static String getOpenAIModel() throws IOException {
return getConfigValue(CONF_OPENAI_PROPERTIES, "model");
}
public static String getModel(String configName) throws IOException {
return getConfigValue(configName, "model");
}
public static String getToken(String configName) throws IOException {
return getConfigValue(configName, "token");
}
@@ -63,7 +63,7 @@ public class KernelTest extends AbstractKernelTest {
@Test
@EnabledIf("isAzureTestEnabled")
public void endToEndTextGenerationTestAzureOpenAI() throws IOException {
Kernel kernel = buildKernel(getAzureOpenAIClient(), "text-davinci-003");
Kernel kernel = buildKernel(getOpenAIClient(), "text-davinci-003");
executeCompletion(kernel);
}
@@ -11,7 +11,6 @@ import com.microsoft.semantickernel.memory.SemanticTextMemory;
import com.microsoft.semantickernel.memory.VolatileMemoryStore;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
@@ -27,16 +26,25 @@ public class TextEmbeddingsTest extends AbstractKernelTest {
private static final Logger LOGGER = LoggerFactory.getLogger(TextEmbeddingsTest.class);
private static final int EXPECTED_EMBEDDING_SIZE = 1536;
@Test
@EnabledIf("isOpenAIComTestEnabled")
public void testEmbeddingGenerationOpenAI() throws IOException {
testEmbeddingGeneration(getOpenAIClient(), EXPECTED_EMBEDDING_SIZE);
}
@Test
@EnabledIf("isAzureTestEnabled")
public void testEmbeddingGenerationAzure() throws IOException {
testEmbeddingGeneration(getAzureOpenAIClient(), EXPECTED_EMBEDDING_SIZE);
public void testEmbeddingGenerationOpenAI() throws IOException {
OpenAIAsyncClient client = getOpenAIClient();
String model = "text-embedding-ada-002";
OpenAITextEmbeddingGeneration embeddingGeneration =
new OpenAITextEmbeddingGeneration(client, model);
List<String> data = new ArrayList<>();
data.add("This is just");
//data.add("a test");
embeddingGeneration
.generateEmbeddingsAsync(data)
.block()
.forEach(
embedding ->
Assertions.assertEquals(
EXPECTED_EMBEDDING_SIZE, embedding.getVector().size()));
}
@Test
@@ -86,7 +94,7 @@ public class TextEmbeddingsTest extends AbstractKernelTest {
volatileMemoryStore.createCollectionAsync("aboutMe").block();
OpenAITextEmbeddingGeneration embeddingGeneration =
new OpenAITextEmbeddingGeneration(getAzureOpenAIClient(), "text-embedding-ada-002");
new OpenAITextEmbeddingGeneration(getOpenAIClient(), "text-embedding-ada-002");
SemanticTextMemory memory =
SKBuilders.semanticTextMemory()
@@ -138,21 +146,4 @@ public class TextEmbeddingsTest extends AbstractKernelTest {
query, result.getMetadata().getText(), result.getRelevance()));
}
public void testEmbeddingGeneration(OpenAIAsyncClient client, int expectedEmbeddingSize) {
String model = "text-embedding-ada-002";
OpenAITextEmbeddingGeneration embeddingGeneration =
new OpenAITextEmbeddingGeneration(client, model);
List<String> data = new ArrayList<>();
data.add("This is just");
data.add("a test");
embeddingGeneration
.generateEmbeddingsAsync(data)
.block()
.forEach(
embedding ->
Assertions.assertEquals(
expectedEmbeddingSize, embedding.getVector().size()));
}
}
+5 -16
View File
@@ -243,6 +243,8 @@
<configuration>
<!-- TODO: Tighten this up when we go GA -->
<failOnError>false</failOnError>
<nohelp>true</nohelp>
<show>public</show>
</configuration>
</plugin>
<plugin>
@@ -423,10 +425,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@@ -610,6 +608,9 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<modules>
<module>samples</module>
</modules>
</profile>
<profile>
<id>bug-check</id>
@@ -742,18 +743,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
+127
View File
@@ -0,0 +1,127 @@
# Java Samples
## TL;DR
Run with:
```shell
# AZURE:
OPENAI_CLIENT_TYPE=AZURE_OPEN_AI \
AZURE_OPEN_AI_KEY="my-key" \
AZURE_OPEN_AI_ENDPOINT="endpoint url" \
../../mvnw clean package exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
# OPENAI:
OPENAI_CLIENT_TYPE=OPENAI \
OPEN_AI_KEY="my-key" \
OPEN_AI_ORGANIZATION_ID="organisation id" \
../../mvnw clean package exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
```
# Compile
These samples can be compiled via:
```shell
../../mvnw clean package
```
They can then be run by:
```shell
../../mvnw exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
```
# Configuration
By default, the samples will use the Open AI client, but you can also use the Azure Open AI client.
## Open AI client type
You can define the provider of Open AI (openai.com or Azure), this can be done by setting the `OPENAI_CLIENT_TYPE`
property or environment variable to either `OPENAI` or `AZURE_OPEN_AI`, i.e.:
```shell
OPENAI_CLIENT_TYPE=OPENAI ../../mvnw exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
OR
../../mvnw exec:java -DOPENAI_CLIENT_TYPE=AZURE_OPEN_AI -Dsample=Example04_CombineLLMPromptsAndNativeCode
```
## Client Settings
The tests search for the client settings in the following order:
1. Properties file whose location is defined by the `CONF_PROPERTIES` property or environment variable.
1. System properties defined on the command line.
1. Environment variables.
1. Properties file at `java/samples/conf.properties`.
1. Properties file at `~/.sk/conf.properties`.
## Properties File
You can set the location of a properties file, by setting the `CONF_PROPERTIES` property or environment variable, ie:
```shell
CONF_PROPERTIES=my.properties \
OPENAI_CLIENT_TYPE=OPENAI \
../../mvnw exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
OR
../../mvnw exec:java \
-DCONF_PROPERTIES=my.properties \
-DOPENAI_CLIENT_TYPE=AZURE_OPEN_AI \
-Dsample=Example04_CombineLLMPromptsAndNativeCode
```
A properties file looks as follows:
```properties
# If using openai.com
client.openai.key:"my-key"
client.openai.organizationid:"my-org-id"
# if using Azure Open AI
client.azureopenai.key:"my-key"
client.azureopenai.endpoint:"url of azure openai endpoint"
client.azureopenai.deploymentname:"deployment name"
```
## System Properties
As an alternative to providing the key/endpoint properties via a file, you can set them directly via system properties,
ie:
```shell
# OpenAI
../../mvnw exec:java \
-DOPENAI_CLIENT_TYPE=AZURE_OPEN_AI \
-Dclient.openai.key="my-key" \
-Dclient.openai.organizationid="my-org-id" \
-Dsample=Example04_CombineLLMPromptsAndNativeCode
# Azure
../../mvnw exec:java \
-DOPENAI_CLIENT_TYPE=AZURE_OPEN_AI \
-Dclient.azureopenai.key="my-key" \
-Dclient.azureopenai.endpoint="url of azure openai endpoint" \
-Dsample=Example04_CombineLLMPromptsAndNativeCode
```
## Environment variables
Alternative to properties, you can set environment variables as follows:
```shell
# AZURE:
OPENAI_CLIENT_TYPE=AZURE_OPEN_AI \
AZURE_OPEN_AI_KEY="my-key" \
AZURE_OPEN_AI_ENDPOINT="endpoint url" \
../../mvnw clean package exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
# OPENAI:
OPENAI_CLIENT_TYPE=OPENAI \
OPEN_AI_KEY="my-key" \
OPEN_AI_ORGANIZATION_ID="organisation id" \
../../mvnw clean package exec:java -Dsample=Example04_CombineLLMPromptsAndNativeCode
```
+16
View File
@@ -63,6 +63,22 @@
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>run-sample</id>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.microsoft.semantickernel.syntaxexamples.${sample}</mainClass>
</configuration>
</plugin>
</plugins>
</build>
@@ -5,58 +5,197 @@ import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.ai.openai.models.NonAzureOpenAIKeyCredential;
import com.azure.core.credential.AzureKeyCredential;
import com.microsoft.semantickernel.util.AIProviderSettings;
import com.microsoft.semantickernel.util.AzureOpenAISettings;
import com.microsoft.semantickernel.util.OpenAISettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.AIProviderSettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.AzureOpenAISettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.ClientSettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.OpenAISettings;
import org.slf4j.Logger;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
public class Config {
public static final String CONF_PROPERTIES =
System.getProperty("CONF_PROPERTIES", "java/samples/conf.properties");
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(Config.class);
private static final List<String> DEFAULT_PROPERTIES_LOCATIONS = Arrays.asList(
"java/samples/conf.properties",
System.getProperty("user.home") + "/.sk/conf.properties"
);
public static final String CONF_PROPERTIES = getEnvOrProperty("CONF_PROPERTIES", "java/samples/conf.properties");
public static final String OPENAI_CLIENT_TYPE =
System.getProperty("OPENAI_CLIENT_TYPE", "OPEN_AI");
getEnvOrProperty("OPENAI_CLIENT_TYPE", "OPEN_AI");
private static String getEnvOrProperty(String propertyName, String defaultValue) {
String env = System.getenv(propertyName);
if (env != null && !env.isEmpty()) {
return env;
}
String property = System.getProperty(propertyName);
if (property != null && !property.isEmpty()) {
return property;
}
return defaultValue;
}
/**
* Looks for settings in the order of:
* <p>
* 1. Properties file
* 2. System properties
* 3. Environment variables
* 4. Properties file: java/samples/conf.properties
* 5. Properties file: ~/.sk/conf.properties
*
* @return A client instance
* @throws IOException If the settings are not found
*/
public static OpenAIAsyncClient getClient() throws IOException {
return ClientType.valueOf(OPENAI_CLIENT_TYPE).getClient();
}
private static Supplier<ClientSettings<?>> getOpenAIClientFromDefaultPropertiesLocations(ClientType type) {
return () -> {
try {
for (String location : DEFAULT_PROPERTIES_LOCATIONS) {
if (Files.isRegularFile(Path.of(location))) {
return switch (type) {
case OPEN_AI -> AIProviderSettings.getOpenAISettingsFromFile(location);
case AZURE_OPEN_AI -> AIProviderSettings.getAzureOpenAISettingsFromFile(location);
};
}
}
} catch (FileNotFoundException e) {
// Ignore
} catch (IOException e) {
LOGGER.error("Failed to read properties file", e);
}
return null;
};
}
public enum ClientType {
OPEN_AI {
@Override
public OpenAIAsyncClient getClient() throws IOException {
return getClient(CONF_PROPERTIES);
return buildClient(
() -> {
try {
return AIProviderSettings.getOpenAISettingsFromFile(CONF_PROPERTIES);
} catch (IOException e) {
return null;
}
},
AIProviderSettings::getOpenAISettingsFromSystemProperties,
AIProviderSettings::getOpenAISettingsFromEnv,
getOpenAIClientFromDefaultPropertiesLocations(OPEN_AI));
}
@Override
public OpenAIAsyncClient getClient(String file) throws IOException {
OpenAISettings settings = AIProviderSettings.getOpenAISettingsFromFile(file);
return getClient(settings);
}
@Override
protected OpenAIAsyncClient getClient(ClientSettings settingsIn) {
if (!(settingsIn instanceof OpenAISettings settings)) {
throw new IllegalArgumentException("Must provide a settings of type OpenAISettings");
}
return new OpenAIClientBuilder()
.credential(new NonAzureOpenAIKeyCredential(settings.getKey()))
.buildAsyncClient();
}
@Override
protected String getErrorMessage() {
return "Unable to find OpenAI settings. Please provide an endpoint and organisation Id.";
}
},
AZURE_OPEN_AI {
@Override
public OpenAIAsyncClient getClient() throws IOException {
return getClient(CONF_PROPERTIES);
return buildClient(
() -> {
try {
return AIProviderSettings.getAzureOpenAISettingsFromFile(CONF_PROPERTIES);
} catch (IOException e) {
return null;
}
},
AIProviderSettings::getAzureOpenAISettingsFromSystemProperties,
AIProviderSettings::getAzureOpenAISettingsFromEnv,
getOpenAIClientFromDefaultPropertiesLocations(AZURE_OPEN_AI));
}
@Override
public OpenAIAsyncClient getClient(String file)
throws IOException {
public OpenAIAsyncClient getClient(String file) throws IOException {
AzureOpenAISettings settings = AIProviderSettings.getAzureOpenAISettingsFromFile(file);
return getClient(settings);
}
@Override
public OpenAIAsyncClient getClient(ClientSettings settingsIn) {
if (!(settingsIn instanceof AzureOpenAISettings settings)) {
throw new IllegalArgumentException("Must provide a settings of type AzureOpenAISettings");
}
return new OpenAIClientBuilder()
.endpoint(settings.getEndpoint())
.credential(new AzureKeyCredential(settings.getKey()))
.buildAsyncClient();
}
@Override
protected String getErrorMessage() {
return "Unable to find Azure OpenAI settings. Please provide an endpoint and key.";
}
};
/**
* Builds the client based on the first valid settings provided.
*
* @param settings Suppliers that provide settings
* @return The client
* @throws IOException If no valid settings are found
*/
protected OpenAIAsyncClient buildClient(Supplier<ClientSettings<?>>... settings) throws IOException {
ClientSettings<?> firstSetting = Arrays.stream(settings)
.map(Supplier::get)
.filter(clientSettings -> clientSettings != null && clientSettings.isValid())
.findFirst()
.orElse(null);
if (firstSetting != null) {
return getClient(firstSetting);
} else {
throw new IOException(getErrorMessage());
}
}
/**
* Looks for settings in the order of:
* <p>
* 1. Properties file
* 2. System properties
* 3. Environment variables
* 4. Properties file: java/samples/conf.properties
* 5. Properties file: ~/.sk/conf.properties
*
* @return A client instance
* @throws IOException If the settings are not found
*/
public abstract OpenAIAsyncClient getClient() throws IOException;
/**
@@ -65,5 +204,15 @@ public class Config {
* @return client to be used by the kernel.
*/
public abstract OpenAIAsyncClient getClient(String file) throws IOException;
/**
* Builds the client based on the settings provided.
*
* @param settings The settings to use
* @return The client
*/
protected abstract OpenAIAsyncClient getClient(ClientSettings settings);
protected abstract String getErrorMessage();
}
}
@@ -6,6 +6,7 @@ import com.azure.ai.openai.OpenAIAsyncClient;
import com.microsoft.semantickernel.builders.SKBuilders;
import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.skilldefinition.ReadOnlyFunctionCollection;
import com.microsoft.semantickernel.syntaxexamples.SampleSkillsUtil;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import reactor.core.publisher.Mono;
@@ -27,7 +28,7 @@ public class Example00_GettingStarted {
* Returns a Semantic Kernel with Text Completion.
*
* @param client Client that will handle requests to AzureOpenAI or OpenAI.
* @return
* @return Kernel.
*/
public static Kernel getKernel(OpenAIAsyncClient client) {
KernelConfig config = SKBuilders.kernelConfig()
@@ -50,7 +51,7 @@ public class Example00_GettingStarted {
*/
public static void joke(Kernel kernel) {
ReadOnlyFunctionCollection skill = kernel.importSkillFromDirectory("FunSkill", "samples/skills", "FunSkill");
ReadOnlyFunctionCollection skill = kernel.importSkillFromDirectory("FunSkill", SampleSkillsUtil.detectSkillDirLocation(), "FunSkill");
CompletionSKFunction function = skill.getFunction("Joke",
CompletionSKFunction.class);
@@ -1,6 +1,7 @@
package com.microsoft.semantickernel;
import com.microsoft.semantickernel.skilldefinition.ReadOnlyFunctionCollection;
import com.microsoft.semantickernel.syntaxexamples.SampleSkillsUtil;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import java.io.IOException;
@@ -29,7 +30,7 @@ public class Example02_RunnningPromptsFromFile {
*/
public static CompletionSKFunction getJokeFunction(Kernel kernel) {
ReadOnlyFunctionCollection skill = kernel
.importSkillFromDirectory("FunSkill", "samples/skills", "FunSkill");
.importSkillFromDirectory("FunSkill", SampleSkillsUtil.detectSkillDirLocation(), "FunSkill");
return skill.getFunction("Joke", CompletionSKFunction.class);
}
@@ -1,14 +1,15 @@
package com.microsoft.semantickernel;
import com.microsoft.semantickernel.planner.sequentialplanner.SequentialPlanner;
import com.microsoft.semantickernel.syntaxexamples.SampleSkillsUtil;
import java.io.IOException;
public class Example05_UsingThePlanner {
public static SequentialPlanner getPlanner(Kernel kernel) {
kernel.importSkillFromDirectory("SummarizeSkill", "samples/skills", "SummarizeSkill");
kernel.importSkillFromDirectory("WriterSkill", "samples/skills", "WriterSkill");
kernel.importSkillFromDirectory("SummarizeSkill", SampleSkillsUtil.detectSkillDirLocation(), "SummarizeSkill");
kernel.importSkillFromDirectory("WriterSkill", SampleSkillsUtil.detectSkillDirLocation(), "WriterSkill");
return new SequentialPlanner(kernel, null, null);
}
@@ -28,7 +28,7 @@ public class Example04_CombineLLMPromptsAndNativeCode {
}
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
OpenAIAsyncClient client = Config.getClient();
TextCompletion textCompletion = SKBuilders.textCompletionService().build(client, "text-davinci-003");
@@ -38,7 +38,7 @@ public class Example04_CombineLLMPromptsAndNativeCode {
Kernel kernel = SKBuilders.kernel().withKernelConfig(kernelConfig).build();
kernel.importSkill(new SearchEngineSkill(), null);
kernel.importSkillFromDirectory("SummarizeSkill", "./samples/skills", "SummarizeSkill");
kernel.importSkillFromDirectory("SummarizeSkill", SampleSkillsUtil.detectSkillDirLocation(), "SummarizeSkill");
// Run
String ask = "What's the tallest building in South America?";
@@ -17,7 +17,7 @@ import java.time.format.FormatStyle;
public class Example05_InlineFunctionDefinition {
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
OpenAIAsyncClient client = Config.getClient();
TextCompletion textCompletion = SKBuilders.textCompletionService().build(client, "text-davinci-003");
@@ -13,8 +13,8 @@ import com.microsoft.semantickernel.orchestration.SKContext;
import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig;
import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import com.microsoft.semantickernel.util.AIProviderSettings;
import com.microsoft.semantickernel.util.AzureOpenAISettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.AIProviderSettings;
import com.microsoft.semantickernel.connectors.ai.openai.util.AzureOpenAISettings;
import java.io.IOException;
import java.time.Duration;
@@ -17,7 +17,7 @@ import java.time.Duration;
public class Example09_FunctionTypes {
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
OpenAIAsyncClient client = Config.getClient();
TextCompletion textCompletion = SKBuilders.textCompletionService().build(client, "text-davinci-003");
@@ -33,7 +33,7 @@ public class Example09_FunctionTypes {
var test = kernel
.importSkill(new LocalExampleSkill(), "test");
kernel.importSkillsFromDirectory("./samples/skills", "SummarizeSkill");
kernel.importSkillsFromDirectory(SampleSkillsUtil.detectSkillDirLocation(), "SummarizeSkill");
var fakeContext = SKBuilders.context()
@@ -191,9 +191,9 @@ public class Example12_SequentialPlanner {
// Load additional skills to enable planner to do non-trivial asks.
kernel.importSkillFromDirectory("SummarizeSkill", "samples/skills", "SummarizeSkill");
kernel.importSkillFromDirectory("WriterSkill", "samples/skills", "WriterSkill");
kernel.importSkillFromDirectory("MiscSkill", "samples/skills", "MiscSkill");
kernel.importSkillFromDirectory("SummarizeSkill", SampleSkillsUtil.detectSkillDirLocation(), "SummarizeSkill");
kernel.importSkillFromDirectory("WriterSkill", SampleSkillsUtil.detectSkillDirLocation(), "WriterSkill");
kernel.importSkillFromDirectory("MiscSkill", SampleSkillsUtil.detectSkillDirLocation(), "MiscSkill");
return kernel;
}
@@ -127,16 +127,16 @@ public class Example13_ConversationSummarySkill {
""".stripIndent();
public static void main(String[] args) throws IOException {
Config.ClientType clientType = Config.ClientType.AZURE_OPEN_AI;
OpenAIAsyncClient client = Config.getClient();
conversationSummarySkillAsync(clientType);
getConversationActionItemsAsync(clientType);
getConversationTopicsAsync(clientType);
conversationSummarySkillAsync(client);
getConversationActionItemsAsync(client);
getConversationTopicsAsync(client);
}
private static void conversationSummarySkillAsync(Config.ClientType clientType) throws IOException {
private static void conversationSummarySkillAsync(OpenAIAsyncClient client) {
System.out.println("======== SampleSkills - Conversation Summary Skill - Summarize ========");
Kernel kernel = initializeKernel(clientType);
Kernel kernel = initializeKernel(client);
ReadOnlyFunctionCollection conversationSummarySkill =
kernel.importSkill(new ConversationSummarySkill(kernel), null);
@@ -150,9 +150,9 @@ public class Example13_ConversationSummarySkill {
}
private static void getConversationActionItemsAsync(Config.ClientType clientType) throws IOException {
private static void getConversationActionItemsAsync(OpenAIAsyncClient client) {
System.out.println("======== SampleSkills - Conversation Summary Skill - Action Items ========");
Kernel kernel = initializeKernel(clientType);
Kernel kernel = initializeKernel(client);
ReadOnlyFunctionCollection conversationSummarySkill =
kernel.importSkill(new ConversationSummarySkill(kernel), null);
@@ -165,8 +165,8 @@ public class Example13_ConversationSummarySkill {
System.out.println(summary.block().getResult());
}
private static void getConversationTopicsAsync(Config.ClientType clientType) throws IOException {
Kernel kernel = initializeKernel(clientType);
private static void getConversationTopicsAsync(OpenAIAsyncClient client) {
Kernel kernel = initializeKernel(client);
ReadOnlyFunctionCollection conversationSummarySkill =
kernel.importSkill(new ConversationSummarySkill(kernel), null);
@@ -179,9 +179,7 @@ public class Example13_ConversationSummarySkill {
System.out.println(summary.block().getResult());
}
private static Kernel initializeKernel(Config.ClientType clientType) throws IOException {
OpenAIAsyncClient client = clientType.getClient();
private static Kernel initializeKernel(OpenAIAsyncClient client) {
TextCompletion textCompletion = SKBuilders.textCompletionService().build(client, "text-davinci-003");
KernelConfig kernelConfig =
@@ -59,7 +59,7 @@ public class Example14_SemanticMemory
* You can replace VolatileMemoryStore with Qdrant (see QdrantMemoryStore connector)
* or implement your connectors for Pinecone, Vespa, Postgres + pgvector, SQLite VSS, etc.
*/
var openAIAsyncClient = Config.ClientType.AZURE_OPEN_AI.getClient();
var openAIAsyncClient = Config.getClient();
var kernelConfigWithTextEmbedding = SKBuilders.kernelConfig()
.addTextEmbeddingsGenerationService(
@@ -18,7 +18,7 @@ import java.io.IOException;
public class Example17_ChatGPT {
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
OpenAIAsyncClient client = Config.getClient();
Kernel kernel = SKBuilders.kernel()
.withKernelConfig(SKBuilders.kernelConfig()
@@ -11,7 +11,7 @@ import java.io.IOException;
public class Example28_ActionPlanner {
public static void main(String[] args) throws IOException {
OpenAIAsyncClient client = Config.ClientType.AZURE_OPEN_AI.getClient();
OpenAIAsyncClient client = Config.getClient();
System.out.println("======== Action Planner ========");
@@ -23,9 +23,9 @@ public class Example28_ActionPlanner {
.build())
.build();
kernel.importSkillFromDirectory("SummarizeSkill", "./samples/skills", "SummarizeSkill");
kernel.importSkillFromDirectory("SummarizeSkill", SampleSkillsUtil.detectSkillDirLocation(), "SummarizeSkill");
kernel.importSkillFromDirectory("WriterSkill", "./samples/skills", "WriterSkill");
kernel.importSkillFromDirectory("WriterSkill", SampleSkillsUtil.detectSkillDirLocation(), "WriterSkill");
kernel.importSkill(new TextSkill(), "TextSkill");
@@ -0,0 +1,30 @@
package com.microsoft.semantickernel.syntaxexamples;
import java.nio.file.Files;
import java.nio.file.Path;
public class SampleSkillsUtil {
public static final String SAMPLES_SKILLS_DIR = "samples/skills";
/**
* Detects the location of the sample skill directory by searching up til the top of the repository.
*/
public static String detectSkillDirLocation() {
Path dir = Path.of(System.getProperty("user.dir"));
do {
if (Files.isDirectory(dir.resolve(SAMPLES_SKILLS_DIR))) {
return dir.resolve(SAMPLES_SKILLS_DIR).toFile().getAbsolutePath();
}
if (Files.exists(dir.resolve(".gitignore"))) {
// We've reached the root of the repo
throw new RuntimeException("Could not find skill directory");
}
} while ((dir = dir.getParent()) != null);
throw new RuntimeException("Could not find skill directory");
}
}
@@ -69,20 +69,23 @@ public interface Kernel extends SkillExecutor {
*/
Mono<SKContext> runAsync(ContextVariables variables, SKFunction<?>... pipeline);
/**
* Get a completion function builder, functions created with this builder will be registered on
* the kernel
*/
CompletionSKFunction.Builder getSemanticFunctionBuilder();
/** Obtains the service with the given name and type */
<T extends AIService> T getService(@Nullable String name, Class<T> clazz)
throws KernelException;
/** Registers a semantic functon on this kernel */
/** Registers a semantic function on this kernel */
<RequestConfiguration, FunctionType extends SKFunction<RequestConfiguration>>
FunctionType registerSemanticFunction(FunctionType semanticFunctionDefinition);
/** Obtains a semantic function with the given name */
SKFunction getFunction(String skill, String function);
// <T extends ReadOnlySKContext<T>> T createNewContext();
class Builder {
@Nullable private KernelConfig kernelConfig = null;
@Nullable private PromptTemplateEngine promptTemplateEngine = null;
@@ -90,16 +93,34 @@ public interface Kernel extends SkillExecutor {
@Nullable private AIServiceCollection aiServices = new AIServiceCollection();
@Nullable private SemanticTextMemory memory = null;
/**
* Set the kernel configuration
*
* @param kernelConfig Kernel configuration
* @return Builder
*/
public Builder withKernelConfig(KernelConfig kernelConfig) {
this.kernelConfig = kernelConfig;
return this;
}
/**
* Set the prompt template engine
*
* @param promptTemplateEngine Prompt template engine
* @return Builder
*/
public Builder withPromptTemplateEngine(PromptTemplateEngine promptTemplateEngine) {
this.promptTemplateEngine = promptTemplateEngine;
return this;
}
/**
* Set the memory store
*
* @param memoryStore Memory store
* @return Builder
*/
public Builder withMemoryStore(MemoryStore memoryStore) {
this.memoryStore = memoryStore;
return this;
@@ -169,6 +190,11 @@ public interface Kernel extends SkillExecutor {
return this;
}
/**
* Build the kernel
*
* @return Kernel
*/
public Kernel build() {
if (kernelConfig == null) {
throw new IllegalStateException("Must provide a kernel configuration");
@@ -5,7 +5,10 @@ import com.microsoft.semantickernel.ai.embeddings.EmbeddingGeneration;
import com.microsoft.semantickernel.chatcompletion.ChatCompletion;
import com.microsoft.semantickernel.textcompletion.TextCompletion;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nullable;
@@ -30,6 +33,12 @@ public final class KernelConfig {
this.chatCompletionServices = new HashMap<>(chatCompletionServices);
}
/**
* Get the text completion services with the given id
*
* @param serviceId Service id
* @return Map of text completion services
*/
@Nullable
public Function<Kernel, TextCompletion> getTextCompletionService(String serviceId) {
return textCompletionServices.get(serviceId);
@@ -15,10 +15,10 @@ public interface SkillExecutor {
/**
* Import a set of skills
*
* @param skillName
* @param skills
* @return
* @throws SkillsNotFoundException
* @param skillName name of the skill
* @param skills map of skill names to skill configs
* @return the function collection
* @throws SkillsNotFoundException if the skill is not found
*/
ReadOnlyFunctionCollection importSkill(
String skillName, Map<String, SemanticFunctionConfig> skills)
@@ -27,17 +27,17 @@ public interface SkillExecutor {
/**
* Get function collection with the skill name
*
* @param skillName
* @return
* @throws SkillsNotFoundException
* @param skillName name of the skill
* @return the function collection
* @throws SkillsNotFoundException if the skill is not found
*/
ReadOnlyFunctionCollection getSkill(String skillName) throws SkillsNotFoundException;
/**
* Imports the native functions annotated on the given object as a skill.
*
* @param skillName
* @return
* @param skillName name of the skill
* @return the function collection
*/
ReadOnlyFunctionCollection importSkillFromDirectory(
String skillName, String parentDirectory, String skillDirectoryName);
@@ -49,17 +49,18 @@ public interface SkillExecutor {
* Imports the native functions annotated on the given object as a skill. Assumes that the
* directory that contains the skill is the same as skillName
*
* @param skillName
* @return
* @param skillName name of the skill
* @param parentDirectory directory that contains the skill
* @return the function collection
*/
ReadOnlyFunctionCollection importSkillFromDirectory(String skillName, String parentDirectory);
/**
* Imports the native functions annotated on the given object as a skill.
*
* @param nativeSkill
* @param skillName
* @return
* @param nativeSkill object containing the native functions
* @param skillName name of the skill
* @return the function collection
*/
ReadOnlyFunctionCollection importSkill(Object nativeSkill, @Nullable String skillName);
@@ -60,27 +60,35 @@ public class AIException extends SKException {
return String.format("%s: %s", errorCode.getMessage(), message);
}
/** Error codes */
public enum ErrorCodes {
/** Unknown error. */
UnknownError("Unknown error"),
/** No response. */
NoResponse("No response"),
/** Access denied. */
AccessDenied("Access is denied"),
/** Invalid request. */
InvalidRequest("The request was invalid"),
/** Invalid response. */
InvalidResponseContent("The content of the response was invalid"),
/** Throttling. */
Throttling("The request was throttled"),
/** Request timeout. */
RequestTimeout("The request timed out"),
/** Service error. */
ServiceError("There was an error in the service"),
/** Model not available. */
ModelNotAvailable("The requested model is not available"),
/** Invalid configuration. */
InvalidConfiguration("The supplied configuration was invalid"),
/** Function type not supported. */
FunctionTypeNotSupported("The function is not supported");
private final String message;
@@ -88,6 +96,11 @@ public class AIException extends SKException {
this.message = message;
}
/**
* Gets the error message.
*
* @return The error message.
*/
public String getMessage() {
return message;
}
@@ -6,11 +6,7 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Represents a strongly typed vector of numeric data.
*
* @param <EmbeddingType>
*/
/** Represents a strongly typed vector of numeric data. */
public class Embedding<EmbeddingType extends Number> {
public List<EmbeddingType> getVector() {
@@ -27,24 +23,16 @@ public class Embedding<EmbeddingType extends Number> {
return (Embedding<EmbeddingType>) EMPTY;
}
//
// /// <summary>
// /// Initializes a new instance of the <see cref="Embedding{TEmbedding}"/> class that
// contains numeric elements copied from the specified collection.
// /// </summary>
// /// <exception cref="ArgumentException">Type <typeparamref name="TEmbedding"/> is
// unsupported.</exception>
// /// <exception cref="ArgumentNullException">A <c>null</c> vector is passed in.</exception>
/** Initializes a new instance of the Embedding class. */
public Embedding() {
this.vector = Collections.emptyList();
}
/**
* Initializes a new instance of the <see cref="Embedding{TEmbedding}"/> class that contains
* numeric elements copied from the specified collection
* Initializes a new instance of the Embedding class that contains numeric elements copied from
* the specified collection
*
* @param vector
* @param vector The collection whose elements are copied to the new Embedding
*/
public Embedding(List<EmbeddingType> vector) {
// Verify.NotNull(vector, nameof(vector));
@@ -106,50 +106,111 @@ public enum BuildersSingleton {
return (T) builders.get(clazz).get();
}
/**
* Builder for creating a {@link
* com.microsoft.semantickernel.textcompletion.CompletionSKFunction}
*
* @return a {@link FunctionBuilders}
*/
public FunctionBuilders getFunctionBuilders() {
return getInstance(FunctionBuilders.class);
}
/**
* Builder for creating a {@link Kernel}
*
* @return a {@link Kernel.InternalBuilder}
*/
public Kernel.InternalBuilder getKernelBuilder() {
return getInstance(Kernel.InternalBuilder.class);
}
/**
* Builder for creating a {@link TextCompletion}
*
* @return a {@link TextCompletion.Builder}
*/
public TextCompletion.Builder getTextCompletionBuilder() {
return getInstance(TextCompletion.Builder.class);
}
/**
* Builder for creating a {@link EmbeddingGeneration}
*
* @return a {@link EmbeddingGeneration.Builder}
*/
public EmbeddingGeneration.Builder<String, Float> getTextEmbeddingGenerationBuilder() {
return getInstance(EmbeddingGeneration.Builder.class);
}
/**
* Builder for creating a {@link ReadOnlySkillCollection}
*
* @return a {@link ReadOnlySkillCollection.Builder}
*/
public ReadOnlySkillCollection.Builder getReadOnlySkillCollection() {
return getInstance(ReadOnlySkillCollection.Builder.class);
}
/**
* Builder for creating a {@link PromptTemplate}
*
* @return a {@link PromptTemplate.Builder}
*/
public PromptTemplate.Builder getPromptTemplateBuilder() {
return getInstance(PromptTemplate.Builder.class);
}
/**
* Builder for creating a {@link PromptTemplateEngine}
*
* @return a {@link PromptTemplateEngine.Builder}
*/
public PromptTemplateEngine.Builder getPromptTemplateEngineBuilder() {
return getInstance(PromptTemplateEngine.Builder.class);
}
/**
* Builder for creating a {@link ContextVariables}
*
* @return a {@link ContextVariables.Builder}
*/
public ContextVariables.Builder variables() {
return getInstance(ContextVariables.Builder.class);
}
/**
* Builder for creating a {@link SKContext}
*
* @return a {@link SKContext.Builder}
*/
public SKContext.Builder context() {
return getInstance(SKContext.Builder.class);
}
/**
* Builder for creating a {@link SemanticTextMemory}
*
* @return a {@link SemanticTextMemory.Builder}
*/
public SemanticTextMemory.Builder getSemanticTextMemoryBuilder() {
return getInstance(SemanticTextMemory.Builder.class);
}
/**
* Builder for creating a {@link ChatCompletion}
*
* @return a {@link ChatCompletion.Builder}
*/
public ChatCompletion.Builder getChatCompletion() {
return getInstance(ChatCompletion.Builder.class);
}
/**
* Builder for creating a {@link MemoryStore}
*
* @return a {@link MemoryStore.Builder}
*/
public MemoryStore.Builder getMemoryStoreBuilder() {
return getInstance(MemoryStore.Builder.class);
}
@@ -16,6 +16,8 @@ public class ChatHistory {
}
/**
* Get the chat history
*
* @return List of messages in the chat
*/
public List<Message> getMessages() {
@@ -23,6 +25,8 @@ public class ChatHistory {
}
/**
* Get last message
*
* @return The most recent message in chat
*/
public Optional<Message> getLastMessage() {
@@ -32,6 +36,7 @@ public class ChatHistory {
return Optional.of(messages.get(messages.size() - 1));
}
/** Role of the author of a chat message */
public enum AuthorRoles {
Unknown,
System,
@@ -57,6 +62,8 @@ public class ChatHistory {
}
/**
* Get the role of the message author
*
* @return Role of the message author, e.g. user/assistant/system
*/
public AuthorRoles getAuthorRoles() {
@@ -64,6 +71,8 @@ public class ChatHistory {
}
/**
* Get the message content
*
* @return Message content
*/
public String getContent() {
@@ -41,7 +41,7 @@ public interface MemoryStore {
* Deletes a collection from the data store.
*
* @param collectionName The name associated with a collection of embeddings.
* @return
* @return A future that completes when the collection is deleted.
*/
Mono<Void> deleteCollectionAsync(@Nonnull String collectionName);
@@ -12,9 +12,7 @@ import java.util.Collections;
import javax.annotation.CheckReturnValue;
/// <summary>
/// Semantic Kernel context.
/// </summary>memory
/** Semantic Kernel context */
public abstract class AbstractSKContext implements SKContext {
private final ReadOnlySkillCollection skills;
private final WritableContextVariables variables;
@@ -38,15 +36,13 @@ public abstract class AbstractSKContext implements SKContext {
this(variables, null, null);
}
/// <summary>
/// Constructor for the context.
/// </summary>
/// <param name="variables">Context variables to include in context.</param>
/// <param name="memory">Semantic text memory unit to include in context.</param>
/// <param name="skills">Skills to include in context.</param>
/// <param name="logger">Logger for operations in context.</param>
/// <param name="cancellationToken">Optional cancellation token for operations in
// context.</param>
/**
* Constructor for the context.
*
* @param variables Context variables to include in context.
* @param memory Semantic text memory unit to include in context.
* @param skills Skills to include in context.
*/
protected AbstractSKContext(
ContextVariables variables,
@Nullable SemanticTextMemory memory,
@@ -101,13 +97,6 @@ public abstract class AbstractSKContext implements SKContext {
return getThis();
}
/// <summary>
/// Updates the main input text with the new value after a function is complete.
/// </summary>
/// <param name="content">The new input value, for the next function in the pipeline, or as a
// result for the user
/// if the pipeline reached the end.</param>
/// <returns>The current instance</returns>
@Override
public SKContext update(@NonNull String content) {
variables.update(content);
@@ -17,25 +17,32 @@ import java.util.stream.Collectors;
import javax.annotation.Nullable;
/** Abstract implementation of the SKFunction interface. */
public abstract class AbstractSkFunction<RequestConfiguration>
implements SKFunction<RequestConfiguration>, RegistrableSkFunction {
private final DelegateTypes delegateType;
private final List<ParameterView> parameters;
private final String skillName;
private final String functionName;
private final String description;
@Nullable private KernelSkillsSupplier skillsSupplier;
/**
* Constructor.
*
* @param parameters The parameters of the function.
* @param skillName The name of the skill.
* @param functionName The name of the function.
* @param description The description of the function.
* @param skillsSupplier The skill supplier.
*/
public AbstractSkFunction(
DelegateTypes delegateType,
List<ParameterView> parameters,
String skillName,
String functionName,
String description,
@Nullable KernelSkillsSupplier skillsSupplier) {
this.delegateType = delegateType;
this.parameters = new ArrayList<>(parameters);
this.skillName = skillName;
this.functionName = functionName;
@@ -43,16 +50,31 @@ public abstract class AbstractSkFunction<RequestConfiguration>
this.skillsSupplier = skillsSupplier;
}
/**
* Asserts that the skill supplier is registered.
*
* @throws FunctionNotRegisteredException if the skill supplier is not registered.
*/
protected void assertSkillSupplierRegistered() {
if (skillsSupplier == null) {
throw new FunctionNotRegisteredException(getName());
}
}
/**
* Sets the skill supplier.
*
* @param skillsSupplier The skill supplier.
*/
protected void setSkillsSupplier(@Nullable KernelSkillsSupplier skillsSupplier) {
this.skillsSupplier = skillsSupplier;
}
/**
* Gets the skill supplier.
*
* @return The skill supplier.
*/
@Nullable
public KernelSkillsSupplier getSkillsSupplier() {
return skillsSupplier;
@@ -103,6 +125,13 @@ public abstract class AbstractSkFunction<RequestConfiguration>
return this.invokeAsyncInternal(context, settings);
}
/**
* The function to invoke asynchronously.
*
* @param context The context.
* @param settings The settings.
* @return A mono of the context with the result.
*/
protected abstract Mono<SKContext> invokeAsyncInternal(
SKContext context, @Nullable RequestConfiguration settings);
@@ -116,42 +145,15 @@ public abstract class AbstractSkFunction<RequestConfiguration>
return functionName;
}
public DelegateTypes getDelegateType() {
return delegateType;
}
/**
* The parameters of the function.
*
* @return The parameters of the function.
*/
public List<ParameterView> getParameters() {
return Collections.unmodifiableList(parameters);
}
public enum DelegateTypes {
Unknown(0),
Void(1),
OutString(2),
OutTaskString(3),
InSKContext(4),
InSKContextOutString(5),
InSKContextOutTaskString(6),
ContextSwitchInSKContextOutTaskSKContext(7),
InString(8),
InStringOutString(9),
InStringOutTaskString(10),
InStringAndContext(11),
InStringAndContextOutString(12),
InStringAndContextOutTaskString(13),
ContextSwitchInStringAndContextOutTaskContext(14),
InStringOutTask(15),
InContextOutTask(16),
InStringAndContextOutTask(17),
OutTask(18);
final int num;
DelegateTypes(int num) {
this.num = num;
}
}
/**
* The function to create a fully qualified name for
*
@@ -212,21 +214,13 @@ public abstract class AbstractSkFunction<RequestConfiguration>
+ inputs;
}
protected SKContext buildContext() {
assertSkillSupplierRegistered();
return SKBuilders.context()
.with(SKBuilders.variables().build())
.with(skillsSupplier == null ? null : skillsSupplier.get())
.build();
}
@Override
public Mono<SKContext> invokeWithCustomInputAsync(
ContextVariables input,
ContextVariables variables,
@Nullable SemanticTextMemory semanticMemory,
@Nullable ReadOnlySkillCollection skills) {
SKContext tmpContext =
SKBuilders.context().with(input).with(semanticMemory).with(skills).build();
SKBuilders.context().with(variables).with(semanticMemory).with(skills).build();
return invokeAsync(tmpContext, null);
}
@@ -5,7 +5,6 @@ package com.microsoft.semantickernel.orchestration;
import java.util.Map;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
/**
@@ -14,18 +13,39 @@ import javax.annotation.Nullable;
*/
public interface ContextVariables {
/** Default key for the main input */
String MAIN_KEY = "input";
/**
* Get variables as a map
*
* @return Map of variables
*/
Map<String, String> asMap();
@CheckReturnValue
/**
* Get a clone of the variables that can be modified
*
* @return Writable clone of the variables
*/
WritableContextVariables writableClone();
/**
* Get the input (entry in the MAIN_KEY slot)
*
* @return input
*/
@Nullable
String getInput();
/**
* Create formatted string of the variables
*
* @return formatted string
*/
String prettyPrint();
/** Builder for ContextVariables */
interface Builder {
ContextVariables build();
@@ -35,14 +35,18 @@ public interface SKContext {
*/
ContextVariables getVariables();
/** Provides access to the contexts semantic memory */
/**
* Provides access to the contexts semantic memory
*
* @return the semantic memory
*/
@Nullable
SemanticTextMemory getSemanticMemory();
/**
* Provides access to the skills within this context
*
* @return
* @return the skills
*/
ReadOnlySkillCollection getSkills();
@@ -50,7 +54,7 @@ public interface SKContext {
* Sets the given variable
*
* @param key if null defaults to the "input" key
* @param content
* @param content value to set
* @return Context for fluent calls
*/
SKContext setVariable(String key, String content);
@@ -58,8 +62,8 @@ public interface SKContext {
/**
* Appends data to the given key
*
* @param key
* @param content
* @param key key to set
* @param content value to set
* @return Context for fluent calls
*/
SKContext appendToVariable(String key, String content);
@@ -67,7 +71,7 @@ public interface SKContext {
/**
* Updates the input entry with the given data
*
* @param content
* @param content value to set
* @return Context for fluent calls
*/
@CheckReturnValue
@@ -77,12 +81,17 @@ public interface SKContext {
* Merges in the given variables. Duplicate keys provided by newData will overwrite existing
* entries.
*
* @param newData
* @param newData variables to merge in
* @return Context for fluent calls
*/
@CheckReturnValue
SKContext update(ContextVariables newData);
/**
* Clones the context
*
* @return a copy of this context
*/
SKContext copy();
interface BuilderSupplier extends Supplier<Builder> {}
@@ -118,8 +127,8 @@ public interface SKContext {
* Builds a context from the given kernel. If not explicitly set, the skills and memory will
* be used from the kernel.
*
* @param kernel
* @return
* @param kernel Kernel to use
* @return Context
*/
SKContext build(Kernel kernel);
}
@@ -36,6 +36,11 @@ public interface SKFunction<RequestConfiguration> {
@CheckReturnValue
Mono<SKContext> invokeAsync(String input, SKContext context, RequestConfiguration settings);
/**
* Invokes the function
*
* @return an updated context with the result of the request
*/
Mono<SKContext> invokeAsync();
/**
@@ -47,6 +52,11 @@ public interface SKFunction<RequestConfiguration> {
@CheckReturnValue
Mono<SKContext> invokeAsync(String input);
/**
* The type of the configuration argument that will be provided when the function is invoked
*
* @return The type
*/
@Nullable
Class<RequestConfiguration> getType();
@@ -91,12 +101,30 @@ public interface SKFunction<RequestConfiguration> {
*/
String getDescription();
/**
* Create a string for generating an embedding for a function.
*
* @return A string for generating an embedding for a function.
*/
String toEmbeddingString();
/**
* Create a manual-friendly string for a function.
*
* @return A manual-friendly string for a function.
*/
String toManualString();
/**
* Invokes the function with the given input, context and settings
*
* @param variables variables to be used in the function
* @param semanticMemory semantic memory to be used in the function
* @param skills skills to be used in the function
* @return an updated context with the result of the request
*/
Mono<SKContext> invokeWithCustomInputAsync(
ContextVariables variablesClone,
ContextVariables variables,
@Nullable SemanticTextMemory semanticMemory,
@Nullable ReadOnlySkillCollection skills);
}
@@ -20,15 +20,22 @@ public interface WritableContextVariables extends ContextVariables {
ContextVariables appendToVariable(String key, String content);
/// <summary>
/// Updates the main input text with the new value after a function is complete.
/// </summary>
/// <param name="content">The new input value, for the next function in the pipeline, or as a
// result for the user
/// if the pipeline reached the end.</param>
/// <returns>The current instance</returns>
/**
* Updates the main input text with the new value after a function is complete.
*
* @param content The new input value, for the next function in the pipeline, or as a result for
* the user if the pipeline reached the end.
* @return The current instance
*/
ContextVariables update(String content);
/**
* Updates the variables merging or overwriting in the new values.
*
* @param newData Data to merge or overwrite.
* @param merge Whether to merge the new data with the existing data or to replace it
* @return The current instance
*/
ContextVariables update(ContextVariables newData, boolean merge);
ContextVariables remove(String key);
@@ -26,8 +26,8 @@ public class PlanningException extends SKException {
}
/**
* Initializes a new instance of the <see cref="PlanningException"/> class with a provided error
* code, message, and inner exception.
* Initializes a new instance of the PlanningException class with a provided error code,
* message, and inner exception.
*
* @param errorCode The error code
* @param message A string that describes the error
@@ -18,7 +18,7 @@ public class PromptTemplateConfig {
/**
* A returns the configuration for the text completion
*
* @return
* @return CompletionConfig
*/
public CompletionConfig getCompletionConfig() {
return completionConfig;
@@ -6,11 +6,12 @@ public abstract class Block {
private final String content;
private final BlockTypes type;
/// <summary>
/// Base constructor
/// </summary>
/// <param name="content">Block content</param>
/// <param name="log">App logger</param>
/**
* Base constructor
*
* @param content Block content
* @param type Block type
*/
public Block(String content, BlockTypes type) {
if (content == null) {
content = "";
@@ -20,18 +21,27 @@ public abstract class Block {
this.type = type;
}
/**
* Get the block content
*
* @return Block content
*/
public String getContent() {
return content;
}
/// <summary>
/// Check if the block content is valid.
/// </summary>
/// <param name="errorMsg">Error message in case the content is not valid</param>
/// <returns>True if the block content is valid</returns>
// TODO ERROR MESSAGE
/**
* Check if the block content is valid.
*
* @return True if the block content is valid
*/
public abstract boolean isValid();
/**
* Get the block type
*
* @return Block type
*/
public BlockTypes getType() {
return type;
}
@@ -2,11 +2,17 @@
package com.microsoft.semantickernel.templateengine.blocks;
public enum BlockTypes {
/** Undefined block type */
Undefined(0),
/** Text block type */
Text(1),
/** Code block type */
Code(2),
/** Variable block type */
Variable(3),
/** Value block type */
Value(4),
/** Function block type */
FunctionId(5);
BlockTypes(int i) {}
@@ -52,6 +52,19 @@ public class CompletionRequestSettings {
*/
private final String user;
/**
* Create a new settings object with the given values.
*
* @param temperature Temperature controls the randomness of the completion.
* @param topP TopP controls the diversity of the completion.
* @param presencePenalty Number between -2.0 and 2.0. Positive values penalize new tokens based
* on whether they appear in the text so far, increasing the model's likelihood to talk
* about new topics.
* @param frequencyPenalty Number between -2.0 and 2.0. Positive values penalize new tokens
* based on their existing frequency in the text so far, decreasing the model's likelihood
* to repeat the same line verbatim.
* @param maxTokens The maximum number of tokens to generate in the completion.
*/
public CompletionRequestSettings(
double temperature,
double topP,
@@ -69,6 +82,24 @@ public class CompletionRequestSettings {
Collections.emptyList());
}
/**
* Create a new settings object with the given values.
*
* @param temperature Temperature controls the randomness of the completion.
* @param topP TopP controls the diversity of the completion.
* @param presencePenalty Number between -2.0 and 2.0. Positive values penalize new tokens based
* on whether they appear in the text so far, increasing the model's likelihood to talk
* about new topics.
* @param frequencyPenalty Number between -2.0 and 2.0. Positive values penalize new tokens
* based on their existing frequency in the text so far, decreasing the model's likelihood
* to repeat the same line verbatim.
* @param maxTokens The maximum number of tokens to generate in the completion.
* @param bestOf The maximum number of completions to generate for each prompt. This is used by
* the CompletionService to generate multiple completions for a single prompt.
* @param user A unique identifier representing your end-user, which can help OpenAI to monitor
* and detect abuse
* @param stopSequences Sequences where the completion will stop generating further tokens.
*/
public CompletionRequestSettings(
double temperature,
double topP,
@@ -88,15 +119,16 @@ public class CompletionRequestSettings {
this.stopSequences = new ArrayList<>(stopSequences);
}
/** Create a new settings object with default values. */
public CompletionRequestSettings() {
this(0, 0, 0, 0, 256, 1, "", new ArrayList<>());
}
/// <summary>
/// Create a new settings object with the values from another settings object.
/// </summary>
/// <param name="config"></param>
/// <returns>An instance of <see cref="CompleteRequestSettings"/> </returns>
/**
* Create a new settings object with the values from another settings object.
*
* @param config The config to copy values from
*/
public static CompletionRequestSettings fromCompletionConfig(
PromptTemplateConfig.CompletionConfig config) {
return new CompletionRequestSettings(
@@ -12,6 +12,7 @@ import java.util.List;
import javax.annotation.Nullable;
/** Semantic function for text completion */
public interface CompletionSKFunction extends SKFunction<CompletionRequestSettings> {
/**
@@ -24,21 +25,45 @@ public interface CompletionSKFunction extends SKFunction<CompletionRequestSettin
Mono<SKContext> aggregatePartitionedResultsAsync(
List<String> partitionedInput, @Nullable SKContext context);
/** Builder for completion functions */
abstract class Builder {
protected Builder() {}
/**
* Create a new completion function
*
* @param promptTemplate Prompt template
* @param config Prompt template config
* @param functionName Function name
* @param skillName Skill name
* @return Completion function
*/
public abstract CompletionSKFunction createFunction(
String promptTemplate,
PromptTemplateConfig config,
String functionName,
@Nullable String skillName);
/**
* Create a new completion function
*
* @param prompt Prompt
* @param functionConfig Function config
* @return Completion function
*/
public abstract CompletionSKFunction createFunction(
String prompt, PromptTemplateConfig.CompletionConfig functionConfig);
/**
* Create a new completion function
*
* @param functionName Function name
* @param skillName Skill name
* @return Completion function
*/
public abstract CompletionSKFunction createFunction(
@Nullable String skillNameFinal,
@Nullable String skillName,
String functionName,
SemanticFunctionConfig functionConfig);
@@ -1,37 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public abstract class ClientSettings<T extends ClientSettings<T>> {
public abstract T fromEnv();
public abstract T fromFile(String path) throws IOException;
public abstract T fromFile(String path, String clientSettingsId) throws IOException;
private static String getPropertyNameForClientId(String clientSettingsId, String propertyName) {
return "client." + clientSettingsId + "." + propertyName;
}
protected String getSettingsValueFromEnv(String property) {
return System.getenv(property);
}
protected String getSettingsValueFromFile(
String settingsFile, String property, String clientSettingsId) throws IOException {
File Settings = new File(settingsFile);
try (FileInputStream fis = new FileInputStream(Settings.getAbsolutePath())) {
Properties props = new Properties();
props.load(fis);
return props.getProperty(getPropertyNameForClientId(clientSettingsId, property));
} catch (IOException e) {
throw new IOException(settingsFile + " not configured properly");
}
}
}
@@ -1,8 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.util;
package com.microsoft.semantickernel.connectors.ai.openai.util;
import java.io.IOException;
/** Utility class for OpenAI Client settings */
public class AIProviderSettings {
/**
* Returns an instance of OpenAISettings with key and organizationId from the environment
@@ -28,17 +29,28 @@ public class AIProviderSettings {
*
* @param path Path to the properties file
* @return OpenAISettings
* @throws IOException if the file cannot be read
*/
public static OpenAISettings getOpenAISettingsFromFile(String path) throws IOException {
return new OpenAISettings().fromFile(path);
}
/**
* Returns an instance of OpenAISettings with key and organizationId from the System properties
*
* @return OpenAISettings
*/
public static OpenAISettings getOpenAISettingsFromSystemProperties() {
return new OpenAISettings().fromSystemProperties();
}
/**
* Returns an instance of OpenAISettings with key and organizationId from the properties file
*
* @param path Path to the properties file
* @param clientSettingsId ID of the client settings in the properties file schema
* @return OpenAISettings
* @throws IOException if the file cannot be read
*/
public static OpenAISettings getOpenAISettingsFromFile(String path, String clientSettingsId)
throws IOException {
@@ -51,12 +63,23 @@ public class AIProviderSettings {
*
* @param path Path to the properties file
* @return AzureOpenAISettings
* @throws IOException if the file cannot be read
*/
public static AzureOpenAISettings getAzureOpenAISettingsFromFile(String path)
throws IOException {
return new AzureOpenAISettings().fromFile(path);
}
/**
* Returns an instance of AzureOpenAISettings with key, endpoint and deploymentName from System
* properties
*
* @return AzureOpenAISettings
*/
public static AzureOpenAISettings getAzureOpenAISettingsFromSystemProperties() {
return new AzureOpenAISettings().fromSystemProperties();
}
/**
* Returns an instance of AzureOpenAISettings with key, endpoint and deploymentName from the
* properties file
@@ -64,6 +87,7 @@ public class AIProviderSettings {
* @param path Path to the properties file
* @param clientSettingsId ID of the client settings in the properties file schema
* @return AzureOpenAISettings
* @throws IOException if the file cannot be read
*/
public static AzureOpenAISettings getAzureOpenAISettingsFromFile(
String path, String clientSettingsId) throws IOException {
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.util;
package com.microsoft.semantickernel.connectors.ai.openai.util;
import java.io.IOException;
import javax.annotation.Nullable;
/** Settings for Azure OpenAI client */
public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
private static final String DEFAULT_CLIENT_ID = "azureopenai";
@@ -15,7 +16,7 @@ public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
@Nullable private String deploymentName = null;
public enum Property {
private enum Property {
AZURE_OPEN_AI_KEY("key"),
AZURE_OPEN_AI_ENDPOINT("endpoint"),
AZURE_OPEN_AI_DEPLOYMENT_NAME("deploymentname");
@@ -30,6 +31,11 @@ public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
}
}
/**
* Get the Azure OpenAI key
*
* @return Azure OpenAI key
*/
public String getKey() {
if (key == null) {
throw new RuntimeException("Azure OpenAI key is not set");
@@ -37,6 +43,11 @@ public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
return key;
}
/**
* Get the Azure OpenAI endpoint
*
* @return Azure OpenAI endpoint
*/
public String getEndpoint() {
if (endpoint == null) {
throw new RuntimeException("Azure OpenAI endpoint is not set");
@@ -44,6 +55,11 @@ public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
return endpoint;
}
/**
* Get the Azure OpenAI deployment name
*
* @return Azure OpenAI deployment name
*/
public String getDeploymentName() {
if (deploymentName == null) {
throw new RuntimeException("Azure OpenAI deployment name is not set");
@@ -78,4 +94,28 @@ public class AzureOpenAISettings extends ClientSettings<AzureOpenAISettings> {
path, Property.AZURE_OPEN_AI_DEPLOYMENT_NAME.label(), clientSettingsId);
return this;
}
@Override
public boolean isValid() {
return key != null && endpoint != null;
}
@Override
public AzureOpenAISettings fromSystemProperties() {
return fromSystemProperties(DEFAULT_CLIENT_ID);
}
@Override
public AzureOpenAISettings fromSystemProperties(String clientSettingsId) {
this.key =
getSettingsValueFromSystemProperties(
Property.AZURE_OPEN_AI_KEY.label(), clientSettingsId);
this.endpoint =
getSettingsValueFromSystemProperties(
Property.AZURE_OPEN_AI_ENDPOINT.label(), clientSettingsId);
this.deploymentName =
getSettingsValueFromSystemProperties(
Property.AZURE_OPEN_AI_DEPLOYMENT_NAME.label(), clientSettingsId);
return this;
}
}
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.connectors.ai.openai.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public abstract class ClientSettings<T extends ClientSettings<T>> {
/**
* Returns settings obtained from the environment
*
* @return T settings
*/
public abstract T fromEnv();
/**
* Returns an instance of the settings defined by the properties fil
*
* @param path Path to the properties file
* @return T settings
* @throws IOException if the file cannot be read
*/
public abstract T fromFile(String path) throws IOException;
/**
* Returns an instance of the settings defined by the properties fil
*
* @param path Path to the properties file
* @param clientSettingsId ID of the client settings in the properties file schema
* @return T settings
* @throws IOException if the file cannot be read
*/
public abstract T fromFile(String path, String clientSettingsId) throws IOException;
/**
* Check if the settings are valid
*
* @return true if the settings are valid
*/
public abstract boolean isValid();
/**
* Returns an instance of the settings defined by the system properties
*
* @return T settings
*/
public abstract T fromSystemProperties();
/**
* Returns an instance of the settings defined by the system properties
*
* @return T settings
*/
public abstract T fromSystemProperties(String clientSettingsId);
private static String getPropertyNameForClientId(String clientSettingsId, String propertyName) {
return "client." + clientSettingsId + "." + propertyName;
}
/**
* Returns the value of the property from the environment
*
* @param property Name of the property to retrieve
* @return String value of the property
*/
protected String getSettingsValueFromEnv(String property) {
return System.getenv(property);
}
/**
* Returns the value of the property from the properties file
*
* @param settingsFile Path to the properties file
* @param property Name of the property to retrieve
* @param clientSettingsId ID of the client settings in the properties file schema
* @return String value of the property
*/
protected String getSettingsValueFromFile(
String settingsFile, String property, String clientSettingsId) throws IOException {
File Settings = new File(settingsFile);
try (FileInputStream fis = new FileInputStream(Settings.getAbsolutePath())) {
Properties props = new Properties();
props.load(fis);
return props.getProperty(getPropertyNameForClientId(clientSettingsId, property));
} catch (IOException e) {
throw new IOException(settingsFile + " not configured properly");
}
}
/**
* Returns the value of the property from the system properties
*
* @param property Name of the property to retrieve
* @param clientSettingsId ID of the client settings in the properties file
* @return String value of the property
*/
protected String getSettingsValueFromSystemProperties(
String property, String clientSettingsId) {
Properties props = System.getProperties();
return props.getProperty(getPropertyNameForClientId(clientSettingsId, property));
}
}
@@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.util;
package com.microsoft.semantickernel.connectors.ai.openai.util;
import java.io.IOException;
@@ -58,4 +58,25 @@ public class OpenAISettings extends ClientSettings<OpenAISettings> {
path, Property.OPEN_AI_ORGANIZATION_ID.label(), clientSettingsId);
return this;
}
@Override
public boolean isValid() {
return key != null && organizationId != null;
}
@Override
public OpenAISettings fromSystemProperties() {
return fromSystemProperties(DEFAULT_CLIENT_ID);
}
@Override
public OpenAISettings fromSystemProperties(String clientSettingsId) {
this.key =
getSettingsValueFromSystemProperties(
Property.OPEN_AI_KEY.label(), clientSettingsId);
this.organizationId =
getSettingsValueFromSystemProperties(
Property.OPEN_AI_ORGANIZATION_ID.label(), clientSettingsId);
return this;
}
}
@@ -81,7 +81,7 @@ public class ConversationSummarySkill {
*
* @param input A long conversation transcript
* @param context The SKContext for function execution
* @return
* @return A context containing the summarized conversation
*/
@DefineSKFunction(
description = "Given a long conversation transcript, summarize the conversation.",
@@ -112,7 +112,7 @@ public class ConversationSummarySkill {
*
* @param input A long conversation transcript
* @param context The SKContext for function execution
* @return
* @return A context containing the action items
*/
@DefineSKFunction(
description = "Given a long conversation transcript, identify action items.",
@@ -138,7 +138,7 @@ public class ConversationSummarySkill {
*
* @param input A long conversation transcript
* @param context The SKContext for function execution
* @return
* @return A context containing the topics
*/
@DefineSKFunction(
description =
@@ -28,7 +28,7 @@ import java.math.BigDecimal;
*
* </blockquote>
*
* @implNote This skill uses BigDecimal to perform the number conversion and the calculations.
* <p>This skill uses BigDecimal to perform the number conversion and the calculations.
*/
public class MathSkill {
@@ -22,7 +22,7 @@ public class WaitSkill {
* <p>{{wait.seconds $input}}
*
* @param seconds The number of seconds to wait as a string.
* @return A Mono<Void> that completes after the specified delay.
* @return A Mono that completes after the specified delay.
* @throws IllegalArgumentException If the input is not a valid number.
*/
@DefineSKFunction(name = "seconds", description = "Wait a given amount of seconds")
@@ -10,6 +10,7 @@ import com.microsoft.semantickernel.textcompletion.CompletionSKFunction;
import javax.annotation.Nullable;
/** Provides various builders for completion functions */
public class SkFunctionBuilders implements FunctionBuilders {
public SkFunctionBuilders() {}
@@ -53,7 +53,7 @@ public class EmbeddingVector<TEmbedding extends Number>
* Calculates the dot product of this vector with another.
*
* @param other The other vector to compute the dot product with
* @return
* @return Dot product between vectors
*/
@Override
public double dot(EmbeddingVector<TEmbedding> other) {
@@ -46,7 +46,6 @@ public class DefaultCompletionSKFunction
@Nullable private DefaultTextCompletionSupplier aiService;
public DefaultCompletionSKFunction(
DelegateTypes delegateTypes,
List<ParameterView> parameters,
String skillName,
String functionName,
@@ -54,13 +53,7 @@ public class DefaultCompletionSKFunction
CompletionRequestSettings requestSettings,
SemanticFunctionConfig functionConfig,
@Nullable KernelSkillsSupplier kernelSkillsSupplier) {
super(
delegateTypes,
parameters,
skillName,
functionName,
description,
kernelSkillsSupplier);
super(parameters, skillName, functionName, description, kernelSkillsSupplier);
// TODO
// Verify.NotNull(delegateFunction, "The function delegate is empty");
// Verify.ValidSkillName(skillName);
@@ -104,6 +97,14 @@ public class DefaultCompletionSKFunction
/// <returns>SK function instance</returns>
*/
private SKContext buildContext() {
assertSkillSupplierRegistered();
return SKBuilders.context()
.with(SKBuilders.variables().build())
.with(getSkillsSupplier() == null ? null : getSkillsSupplier().get())
.build();
}
/**
* Method to aggregate partitioned results of a semantic function
*
@@ -306,7 +307,6 @@ public class DefaultCompletionSKFunction
PromptTemplate promptTemplate = functionConfig.getTemplate();
return new DefaultCompletionSKFunction(
DelegateTypes.ContextSwitchInSKContextOutTaskSKContext,
promptTemplate.getParameters(),
skillName,
functionName,
@@ -23,13 +23,12 @@ public abstract class DefaultSemanticSKFunction<RequestConfiguration>
implements SKFunction<RequestConfiguration> {
public DefaultSemanticSKFunction(
DelegateTypes delegateType,
List<ParameterView> parameters,
String skillName,
String functionName,
String description,
@Nullable KernelSkillsSupplier kernelSkillsSupplier) {
super(delegateType, parameters, skillName, functionName, description, kernelSkillsSupplier);
super(parameters, skillName, functionName, description, kernelSkillsSupplier);
}
@Override
@@ -40,14 +40,13 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
private final SKNativeTask<SKContext> function;
public NativeSKFunction(
AbstractSkFunction.DelegateTypes delegateType,
SKNativeTask<SKContext> delegateFunction,
List<ParameterView> parameters,
String skillName,
String functionName,
String description,
KernelSkillsSupplier skillCollection) {
super(delegateType, parameters, skillName, functionName, description, skillCollection);
super(parameters, skillName, functionName, description, skillCollection);
// TODO
// Verify.NotNull(delegateFunction, "The function delegate is empty");
// Verify.ValidSkillName(skillName);
@@ -80,7 +79,6 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
private static class MethodDetails {
public final boolean hasSkFunctionAttribute;
public final AbstractSkFunction.DelegateTypes type;
public final SKNativeTask<SKContext> function;
public final List<ParameterView> parameters;
public final String name;
@@ -88,13 +86,11 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
private MethodDetails(
boolean hasSkFunctionAttribute,
AbstractSkFunction.DelegateTypes type,
SKNativeTask<SKContext> function,
List<ParameterView> parameters,
String name,
String description) {
this.hasSkFunctionAttribute = hasSkFunctionAttribute;
this.type = type;
this.function = function;
this.parameters = parameters;
this.name = name;
@@ -119,7 +115,6 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
}
return new NativeSKFunction(
methodDetails.type,
methodDetails.function,
methodDetails.parameters,
skillName,
@@ -146,7 +141,6 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
if (!hasSkFunctionAttribute) {
throw new RuntimeException("method is not annotated with DefineSKFunction");
}
DelegateTypes type = getDelegateType(methodSignature);
SKNativeTask<SKContext> function = getFunction(methodSignature, methodContainerInstance);
// boolean hasStringParam =
@@ -162,8 +156,7 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
List<ParameterView> parameters = getParameters(methodSignature);
return new MethodDetails(
hasSkFunctionAttribute, type, function, parameters, name, description);
return new MethodDetails(hasSkFunctionAttribute, function, parameters, name, description);
}
private static List<ParameterView> getParameters(Method method) {
@@ -408,22 +401,4 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
return params;
}
// Inspect a method and returns the corresponding delegate and related info
private static AbstractSkFunction.DelegateTypes getDelegateType(Method method) {
// TODO ALL TYPES
if (method.getReturnType().equals(String.class)) {
return AbstractSkFunction.DelegateTypes.OutString;
}
if (method.getReturnType().getName().equals("void")) {
return DelegateTypes.Void;
}
if (method.getReturnType().equals(Mono.class)) {
return AbstractSkFunction.DelegateTypes.InSKContextOutTaskString;
}
throw new RuntimeException("Unknown function type");
}
}
@@ -53,13 +53,7 @@ public class Plan extends AbstractSkFunction<CompletionRequestSettings> {
String goal,
ContextVariables state,
@Nullable KernelSkillsSupplier kernelSkillsSupplier) {
super(
DelegateTypes.ContextSwitchInSKContextOutTaskSKContext,
new ArrayList<>(),
Plan.class.getName(),
"",
goal,
kernelSkillsSupplier);
super(new ArrayList<>(), Plan.class.getName(), "", goal, kernelSkillsSupplier);
this.state = state;
}
@@ -73,7 +67,6 @@ public class Plan extends AbstractSkFunction<CompletionRequestSettings> {
List<String> functionOutputs,
KernelSkillsSupplier kernelSkillsSupplier) {
super(
DelegateTypes.ContextSwitchInSKContextOutTaskSKContext,
function.describe().getParameters(),
function.getSkillName(),
function.getName(),
@@ -93,7 +86,6 @@ public class Plan extends AbstractSkFunction<CompletionRequestSettings> {
List<String> functionOutputs,
KernelSkillsSupplier kernelSkillsSupplier) {
super(
DelegateTypes.ContextSwitchInSKContextOutTaskSKContext,
function.describe().getParameters(),
function.getSkillName(),
function.getName(),
@@ -10,7 +10,7 @@ public class EmbeddedResource {
// private static readonly string? s_namespace = typeof(EmbeddedResource).Namespace;
public static String read(String name) {
try (InputStream stream = SequentialPlanner.class.getResourceAsStream("skprompt.txt")) {
try (InputStream stream = SequentialPlanner.class.getResourceAsStream(name)) {
byte[] buffer = new byte[stream.available()];
stream.read(buffer);
return new String(buffer, StandardCharsets.UTF_8);