mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Java: Add resource loader tests (#5374)
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
This commit is contained in:
+30
-20
@@ -51,12 +51,7 @@ public class EmbeddedResourceLoader {
|
||||
type -> {
|
||||
switch (type) {
|
||||
case CLASSPATH:
|
||||
try (InputStream inputStream = clazz.getResourceAsStream(fileName)) {
|
||||
return readInputStream(fileName, inputStream);
|
||||
} catch (Exception e) {
|
||||
// IGNORE
|
||||
}
|
||||
break;
|
||||
return getResourceAsStream(fileName, clazz);
|
||||
case CLASSPATH_ROOT:
|
||||
try (InputStream inputStream = Thread.currentThread()
|
||||
.getContextClassLoader()
|
||||
@@ -67,16 +62,7 @@ public class EmbeddedResourceLoader {
|
||||
}
|
||||
break;
|
||||
case FILESYSTEM:
|
||||
File file = new File(fileName);
|
||||
if (file.exists()) {
|
||||
try (
|
||||
InputStream inputStream = Files.newInputStream(file.toPath())) {
|
||||
return readInputStream(fileName, inputStream);
|
||||
} catch (IOException e) {
|
||||
// IGNORE
|
||||
}
|
||||
}
|
||||
break;
|
||||
return readFileFromFileSystem(fileName);
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
@@ -91,16 +77,40 @@ public class EmbeddedResourceLoader {
|
||||
throw new FileNotFoundException("Could not find file " + fileName);
|
||||
}
|
||||
|
||||
static String getResourceAsStream(String fileName, Class<?> clazz) {
|
||||
try (InputStream inputStream = clazz.getResourceAsStream(fileName)) {
|
||||
return readInputStream(fileName, inputStream);
|
||||
} catch (Exception e) {
|
||||
// IGNORE
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String readFileFromFileSystem(String fileName) {
|
||||
File file = new File(fileName);
|
||||
if (file.exists()) {
|
||||
try (InputStream inputStream = Files.newInputStream(file.toPath())) {
|
||||
return readInputStream(fileName, inputStream);
|
||||
} catch (IOException e) {
|
||||
// IGNORE
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String readInputStream(String fileName, InputStream inputStream)
|
||||
throws FileNotFoundException {
|
||||
if (inputStream == null) {
|
||||
throw new FileNotFoundException("File not found: " + fileName);
|
||||
}
|
||||
|
||||
return new BufferedReader(
|
||||
new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
try (BufferedReader bf = new BufferedReader(
|
||||
new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8))) {
|
||||
return bf.lines().collect(Collectors.joining("\n"));
|
||||
} catch (IOException e) {
|
||||
// IGNORE
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.implementation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class EmbeddedResourceLoaderTest {
|
||||
|
||||
@Test
|
||||
public void testReadFile_Classpath() {
|
||||
assertThrows(FileNotFoundException.class, () -> {
|
||||
EmbeddedResourceLoader.readFile("nonexistent.txt", this.getClass());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFile_ClasspathRoot() {
|
||||
assertThrows(FileNotFoundException.class, () -> {
|
||||
EmbeddedResourceLoader.readFile("nonexistent.txt", this.getClass(),
|
||||
EmbeddedResourceLoader.ResourceLocation.CLASSPATH_ROOT);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFile_FileSystem() {
|
||||
assertThrows(FileNotFoundException.class, () -> {
|
||||
EmbeddedResourceLoader.readFile("nonexistent.txt", this.getClass(),
|
||||
EmbeddedResourceLoader.ResourceLocation.FILESYSTEM);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFile_Classpath_ExistingFile() throws FileNotFoundException {
|
||||
try (MockedStatic<EmbeddedResourceLoader> mocked = Mockito.mockStatic(
|
||||
EmbeddedResourceLoader.class,
|
||||
withSettings().defaultAnswer(CALLS_REAL_METHODS))) {
|
||||
|
||||
mocked
|
||||
.when(() -> EmbeddedResourceLoader.getResourceAsStream(any(String.class),
|
||||
any(Class.class)))
|
||||
.thenReturn("file content");
|
||||
|
||||
String result = EmbeddedResourceLoader.readFile("existent.txt",
|
||||
EmbeddedResourceLoaderTest.class);
|
||||
assertEquals("file content", result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFile_ClasspathRoot_ExistingFile() throws FileNotFoundException {
|
||||
ClassLoader mockClassLoader = Mockito.mock(ClassLoader.class);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(
|
||||
"file content".getBytes(StandardCharsets.UTF_8));
|
||||
when(mockClassLoader.getResourceAsStream(any(String.class))).thenReturn(inputStream);
|
||||
|
||||
Thread.currentThread().setContextClassLoader(mockClassLoader);
|
||||
|
||||
String result = EmbeddedResourceLoader.readFile("existent.txt",
|
||||
EmbeddedResourceLoaderTest.class,
|
||||
EmbeddedResourceLoader.ResourceLocation.CLASSPATH_ROOT);
|
||||
assertEquals("file content", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFile_FileSystem_ExistingFile() throws IOException {
|
||||
try (MockedStatic<EmbeddedResourceLoader> mocked = Mockito.mockStatic(
|
||||
EmbeddedResourceLoader.class,
|
||||
withSettings().defaultAnswer(CALLS_REAL_METHODS))) {
|
||||
|
||||
mocked.when(() -> EmbeddedResourceLoader.readFileFromFileSystem(any(String.class)))
|
||||
.thenReturn("file content");
|
||||
|
||||
String result = EmbeddedResourceLoader.readFile("existent.txt",
|
||||
EmbeddedResourceLoaderTest.class,
|
||||
EmbeddedResourceLoader.ResourceLocation.FILESYSTEM);
|
||||
assertEquals("file content", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-55
@@ -1,27 +1,4 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2024 davidgrieve.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
package com.microsoft.semantickernel.templateengine.handlebars;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -54,24 +31,23 @@ public class HandlebarsPromptTemplateTest {
|
||||
public HandlebarsPromptTemplateTest() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) { new HandlebarsPromptTemplateTest().testRenderAsync(); }
|
||||
public static void main(String[] args) {
|
||||
new HandlebarsPromptTemplateTest().testRenderAsync();
|
||||
}
|
||||
|
||||
public static class StringFunctions {
|
||||
|
||||
@DefineKernelFunction(name = "upper", description = "Converts a string to upper case.")
|
||||
public String upper(
|
||||
@KernelFunctionParameter(name = "input", required = true, description = "The string to convert to upper case", type = String.class)
|
||||
String input) {
|
||||
return input.toUpperCase();
|
||||
@KernelFunctionParameter(name = "input", required = true, description = "The string to convert to upper case", type = String.class) String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
@DefineKernelFunction(name = "concat", description = "Concatenate the second string to the first string.")
|
||||
public String concat(
|
||||
@KernelFunctionParameter(name = "input", required = true, description = "The string to which the second string is concatenated.", type = String.class)
|
||||
String first,
|
||||
@KernelFunctionParameter(name = "suffix", required = true, description = "The string which is concatenated to the first string.", type = String.class)
|
||||
String suffix) {
|
||||
return first.concat(suffix);
|
||||
@KernelFunctionParameter(name = "input", required = true, description = "The string to which the second string is concatenated.", type = String.class) String first,
|
||||
@KernelFunctionParameter(name = "suffix", required = true, description = "The string which is concatenated to the first string.", type = String.class) String suffix) {
|
||||
return first.concat(suffix);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,39 +64,33 @@ public class HandlebarsPromptTemplateTest {
|
||||
new ChatHistory(
|
||||
Arrays.asList(
|
||||
new ChatMessageContent<String>(AuthorRole.SYSTEM, "a"),
|
||||
new ChatMessageContent<String>(AuthorRole.USER, "b")
|
||||
)
|
||||
),
|
||||
new ChatMessageContent<String>(AuthorRole.USER, "b"))),
|
||||
new ChatHistory(
|
||||
Arrays.asList(
|
||||
new ChatMessageContent<String>(AuthorRole.SYSTEM, "c"),
|
||||
new ChatMessageContent<String>(AuthorRole.USER, "d")
|
||||
)
|
||||
)
|
||||
);
|
||||
new ChatMessageContent<String>(AuthorRole.USER, "d"))));
|
||||
|
||||
KernelPlugin kernelPlugin = KernelPluginFactory.createFromObject(
|
||||
new StringFunctions(),
|
||||
"string"
|
||||
);
|
||||
"string");
|
||||
|
||||
Kernel kernel = Kernel.builder()
|
||||
.withPlugin(kernelPlugin)
|
||||
.build();
|
||||
|
||||
PromptTemplateConfig promptTemplate = PromptTemplateConfig.builder()
|
||||
.withTemplate(
|
||||
.withTemplate(
|
||||
"{{choices.[0]}}\n" +
|
||||
"{{choices}}\n" +
|
||||
"{{#each history}}\n" +
|
||||
" {{#each this}}\n" +
|
||||
" {{string-upper content}}\n" +
|
||||
" {{/each}}\n" +
|
||||
"{{/each}}\n" +
|
||||
"Hello World")
|
||||
// "{{string-concat input suffix}}") TODO - this is not working
|
||||
"{{choices}}\n" +
|
||||
"{{#each history}}\n" +
|
||||
" {{#each this}}\n" +
|
||||
" {{string-upper content}}\n" +
|
||||
" {{/each}}\n" +
|
||||
"{{/each}}\n" +
|
||||
"Hello World")
|
||||
// "{{string-concat input suffix}}") TODO - this is not working
|
||||
.withTemplateFormat("handlebars")
|
||||
.build();
|
||||
.build();
|
||||
|
||||
HandlebarsPromptTemplate instance = new HandlebarsPromptTemplate(promptTemplate);
|
||||
|
||||
@@ -130,15 +100,15 @@ public class HandlebarsPromptTemplateTest {
|
||||
.withVariable("choices", choices)
|
||||
.withVariable("history", history)
|
||||
.withVariable("kernelPlugins", Arrays.asList(kernelPlugin))
|
||||
.build();
|
||||
|
||||
.build();
|
||||
|
||||
// Return from renderAsync is normalized to remove empty lines and leading/trailing whitespace
|
||||
String expResult = "CHOICE-A [CHOICE-A, CHOICE-B] <messages> A B </messages> <messages> C D </messages> Hello World";
|
||||
|
||||
String result = instance.renderAsync(kernel, arguments, null).block();
|
||||
assertNotNull(result);
|
||||
|
||||
String normalizedResult =
|
||||
String normalizedResult =
|
||||
// split result into lines
|
||||
Arrays.stream(result.split("\\r?\\n|\\r"))
|
||||
// remove leading and trailing whitespace
|
||||
|
||||
Reference in New Issue
Block a user