Java: Fix TimeSkill unit test (#1882)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: John Oliver <[email protected]>
Co-authored-by: Milder Hernandez <[email protected]>
Co-authored-by: GitHub <[email protected]>
Co-authored-by: Derek Keeler <[email protected]>
Co-authored-by: Devis Lucato <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Craig Presti <[email protected]>
Co-authored-by: Craig Presti <[email protected]>
Co-authored-by: Shawn Callegari <[email protected]>
Co-authored-by: Mark Wallace <[email protected]>
Co-authored-by: Teresa Hoang <[email protected]>
Co-authored-by: Abby Harrison <[email protected]>
Co-authored-by: Tao Chen <[email protected]>
Co-authored-by: Aman Sachan <[email protected]>
Co-authored-by: cschadewitz <[email protected]>
Co-authored-by: Dmytro Struk <[email protected]>
Co-authored-by: Abby Harrison <[email protected]>
Co-authored-by: Desmond Howard <[email protected]>
Co-authored-by: Jean-Sylvain Boige <[email protected]>
Co-authored-by: Dave Arlin <[email protected]>
Co-authored-by: Gina Triolo <[email protected]>
Co-authored-by: Ben Thomas <[email protected]>
Co-authored-by: Ben Thomas <[email protected]>
Co-authored-by: Sebastian Santoyo <[email protected]>
Co-authored-by: sneha <[email protected]>
Co-authored-by: Alisha Maddy <[email protected]>
Co-authored-by: Alisha Maddy <[email protected]>
Co-authored-by: Sneha <[email protected]>
Co-authored-by: Joe Braley <[email protected]>
Co-authored-by: joebraley <[email protected]>
Co-authored-by: Luigi Montoya <[email protected]>
Co-authored-by: Luigi96 <[email protected]>
Co-authored-by: David Grieve <[email protected]>
This commit is contained in:
Bruno Borges
2023-07-20 19:08:09 +01:00
committed by GitHub
co-authored by John Oliver Milder Hernandez GitHub Derek Keeler Devis Lucato dependabot[bot] Craig Presti Craig Presti Shawn Callegari Mark Wallace Teresa Hoang Abby Harrison Tao Chen Aman Sachan cschadewitz Dmytro Struk Abby Harrison Desmond Howard Jean-Sylvain Boige Dave Arlin Gina Triolo Ben Thomas Ben Thomas Sebastian Santoyo sneha Alisha Maddy Alisha Maddy Sneha Joe Braley joebraley Luigi Montoya Luigi96 David Grieve
parent f3c407ece0
commit bde13ee2e1
6 changed files with 389 additions and 251 deletions
@@ -19,5 +19,11 @@ public @interface SKFunctionParameters {
String defaultValue() default NO_DEFAULT_VALUE;
/**
* If required is false, no value is set, and no default value is provided, null will be passed
* as this argument.
*/
boolean required() default true;
Class<?> type() default String.class;
}
@@ -2,6 +2,9 @@
package com.microsoft.semantickernel.coreskills;
import com.microsoft.semantickernel.skilldefinition.annotations.DefineSKFunction;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionInputAttribute;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
import com.microsoft.semantickernel.util.LocaleParser;
import java.time.ZoneId;
import java.time.ZoneOffset;
@@ -57,6 +60,15 @@ public class TimeSkill {
public static final String DAY_MONTH_DAY_YEAR = "EEEE, MMMM d, yyyy";
/**
* Get the current date and time for the system default timezone.
*
* @return a ZonedDateTime object with the current date and time.
*/
public ZonedDateTime now() {
return ZonedDateTime.now(ZoneId.systemDefault());
}
/**
* Get the current date.
*
@@ -65,9 +77,16 @@ public class TimeSkill {
* @return The current date.
*/
@DefineSKFunction(name = "date", description = "Get the current date")
public String date() {
public String date(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
// Example: Sunday, 12 January, 2025
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR).format(ZonedDateTime.now());
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR)
.withLocale(parseLocale(locale))
.format(now());
}
/**
@@ -78,9 +97,16 @@ public class TimeSkill {
* @return The current time.
*/
@DefineSKFunction(name = "time", description = "Get the current time")
public String time() {
public String time(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
// Example: 09:15:07 PM
return DateTimeFormatter.ofPattern("hh:mm:ss a").format(ZonedDateTime.now());
return DateTimeFormatter.ofPattern("hh:mm:ss a")
.withLocale(parseLocale(locale))
.format(now());
}
/**
@@ -91,9 +117,15 @@ public class TimeSkill {
* @return The current UTC date and time.
*/
@DefineSKFunction(name = "utcNow", description = "Get the current UTC date and time")
public String utcNow() {
public String utcNow(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR + " h:mm a")
.format(ZonedDateTime.now().withZoneSameInstant(ZoneOffset.UTC));
.withLocale(parseLocale(locale))
.format(now().withZoneSameInstant(ZoneOffset.UTC));
}
/**
@@ -104,8 +136,13 @@ public class TimeSkill {
* @return The current date.
*/
@DefineSKFunction(name = "today", description = "Get the current date")
public String today() {
return date();
public String today(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return date(locale);
}
/**
@@ -118,9 +155,15 @@ public class TimeSkill {
@DefineSKFunction(
name = "now",
description = "Get the current date and time in the local time zone")
public String now() {
public String now(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR + " h:mm a")
.format(ZonedDateTime.now());
.withLocale(parseLocale(locale))
.format(now());
}
/**
@@ -131,8 +174,13 @@ public class TimeSkill {
* @return The current year.
*/
@DefineSKFunction(name = "year", description = "Get the current year")
public String year() {
return DateTimeFormatter.ofPattern("yyyy").format(ZonedDateTime.now());
public String year(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("yyyy").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -143,8 +191,13 @@ public class TimeSkill {
* @return The current month name.
*/
@DefineSKFunction(name = "month", description = "Get the current month name")
public String month() {
return DateTimeFormatter.ofPattern("MMMM").format(ZonedDateTime.now());
public String month(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("MMMM").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -155,8 +208,13 @@ public class TimeSkill {
* @return The current month number.
*/
@DefineSKFunction(name = "monthNumber", description = "Get the current month number")
public String monthNumber() {
return DateTimeFormatter.ofPattern("MM").format(ZonedDateTime.now());
public String monthNumber(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("MM").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -167,8 +225,13 @@ public class TimeSkill {
* @return The current day of the month.
*/
@DefineSKFunction(name = "day", description = "Get the current day of the month")
public String day() {
return DateTimeFormatter.ofPattern("d").format(ZonedDateTime.now());
public String day(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("d").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -179,8 +242,13 @@ public class TimeSkill {
* @return The current day of the week.
*/
@DefineSKFunction(name = "dayOfWeek", description = "Get the current day of the week")
public String dayOfWeek() {
return DateTimeFormatter.ofPattern("EEEE").format(ZonedDateTime.now());
public String dayOfWeek(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("EEEE").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -191,8 +259,13 @@ public class TimeSkill {
* @return The current clock hour.
*/
@DefineSKFunction(name = "hour", description = "Get the current clock hour")
public String hour() {
return DateTimeFormatter.ofPattern("h a").format(ZonedDateTime.now());
public String hour(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("h a").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -203,8 +276,13 @@ public class TimeSkill {
* @return The current clock 24-hour number.
*/
@DefineSKFunction(name = "hourNumber", description = "Get the current clock 24-hour number")
public String hourNumber() {
return DateTimeFormatter.ofPattern("HH").format(ZonedDateTime.now());
public String hourNumber(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("HH").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -219,10 +297,21 @@ public class TimeSkill {
@DefineSKFunction(
name = "daysAgo",
description = "Get the date of offset from today by a provided number of days")
public static String daysAgo(String days) {
public String daysAgo(
@SKFunctionInputAttribute
@SKFunctionParameters(
name = "input",
description = "Number of days to offset from today.")
String days,
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
int offsetDays = Integer.parseInt(days);
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR)
.format(ZonedDateTime.now().minusDays(offsetDays));
.withLocale(parseLocale(locale))
.format(now().minusDays(offsetDays));
}
/**
@@ -236,16 +325,25 @@ public class TimeSkill {
@DefineSKFunction(
name = "dateMatchingLastDayName",
description = "Get the date of the last day matching the supplied week day name")
public static String dateMatchingLastDayName(String dayName) {
ZonedDateTime currentDate = ZonedDateTime.now();
Locale systemLocale = Locale.getDefault();
public String dateMatchingLastDayName(
@SKFunctionInputAttribute
@SKFunctionParameters(name = "input", description = "Week name day.")
String dayName,
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
ZonedDateTime currentDate = now();
for (int i = 1; i <= 7; i++) {
currentDate = currentDate.minusDays(1);
String currentDayName =
currentDate.getDayOfWeek().getDisplayName(TextStyle.FULL, systemLocale);
currentDate.getDayOfWeek().getDisplayName(TextStyle.FULL, parseLocale(locale));
if (currentDayName.equalsIgnoreCase(dayName)) {
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR).format(currentDate);
return DateTimeFormatter.ofPattern(DAY_MONTH_DAY_YEAR)
.withLocale(parseLocale(locale))
.format(currentDate);
}
}
throw new IllegalArgumentException("dayName is not recognized");
@@ -259,8 +357,13 @@ public class TimeSkill {
* @return The minutes on the current hour.
*/
@DefineSKFunction(name = "minute", description = "Get the minutes on the current hour")
public String minute() {
return DateTimeFormatter.ofPattern("mm").format(ZonedDateTime.now());
public String minute(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("mm").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -271,8 +374,13 @@ public class TimeSkill {
* @return The seconds on the current minute.
*/
@DefineSKFunction(name = "second", description = "Get the seconds on the current minute")
public String second() {
return DateTimeFormatter.ofPattern("ss").format(ZonedDateTime.now());
public String second(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("ss").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -285,8 +393,13 @@ public class TimeSkill {
@DefineSKFunction(
name = "timeZoneOffset",
description = "Get the local time zone offset from UTC")
public String timeZoneOffset() {
return DateTimeFormatter.ofPattern("XXX").format(ZonedDateTime.now());
public String timeZoneOffset(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
return DateTimeFormatter.ofPattern("XXX").withLocale(parseLocale(locale)).format(now());
}
/**
@@ -297,8 +410,25 @@ public class TimeSkill {
* @return The local time zone name.
*/
@DefineSKFunction(name = "timeZoneName", description = "Get the local time zone name")
public String timeZoneName() {
public String timeZoneName(
@SKFunctionParameters(
name = "locale",
description = "Locale to use when formatting the date",
required = false)
String locale) {
ZoneId zoneId = ZoneId.systemDefault();
return zoneId.getDisplayName(TextStyle.FULL, Locale.getDefault());
return zoneId.getDisplayName(TextStyle.FULL, parseLocale(locale));
}
/**
* Parse the locale string into a Locale object.
*
* <p>By default, it parses using the LocaleParser utility class.
*
* @param locale string
* @return a locale object
*/
protected Locale parseLocale(String locale) {
return LocaleParser.parseLocale(locale);
}
}
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
package com.microsoft.semantickernel.util;
import com.microsoft.semantickernel.skilldefinition.annotations.SKFunctionParameters;
import java.util.Locale;
/**
* Locale parser to support Java 8 and Java 9+ due to JEP 252.
*
* <p>Supports parsing strings containing languageCode_countryCode and languageCode-countryCode
* styles.
*/
public final class LocaleParser {
private LocaleParser() {}
/**
* Parses the locale. If the input is "en-US", it will return a Locale based on the language
* tag. If the input is "en_US", it will return a Locale based on the language and country code.
* Otherwise, it will return a Locale based on the input.
*
* <p>'en' 'US' used as example above.
*
* @param locale the locale in String text.
* @return
*/
public static final Locale parseLocale(String locale) {
Locale parsedLocale = null;
if (locale == null
|| "".equals(locale.trim())
|| SKFunctionParameters.NO_DEFAULT_VALUE.equals(locale)) {
return Locale.getDefault();
} else if (locale.indexOf("-") > -1) {
parsedLocale = Locale.forLanguageTag(locale);
} else if (locale.indexOf("_") > -1) {
String[] parts = locale.split("_");
parsedLocale = new Locale(parts[0], parts[1]);
} else {
parsedLocale = new Locale(locale);
}
return parsedLocale;
}
}
@@ -5,8 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@@ -14,209 +14,133 @@ import java.util.Locale;
@SuppressWarnings("ReturnValueIgnored")
public class TimeSkillTest {
private static final String defaultLocale = "en-US";
private static final ZonedDateTime mockDateTime;
static {
mockDateTime = ZonedDateTime.of(2025, 1, 12, 9, 15, 7, 0, ZoneId.systemDefault());
}
private TimeSkill timeSkill;
@BeforeEach
public void setUp() {
timeSkill = spy(new TimeSkill());
when(timeSkill.now()).thenReturn(mockDateTime);
}
@Test
public void testDate() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.date();
assertEquals("Sunday, January 12, 2025", result);
}
String result = timeSkill.date(defaultLocale);
assertEquals("Sunday, January 12, 2025", result);
}
@Test
public void testTime() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.time();
assertEqualsIgnoreCase("09:15:07 AM", result);
}
String result = timeSkill.time(defaultLocale);
assertEqualsIgnoreCase("09:15:07 AM", result);
}
@Test
public void testToday() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.today();
assertEquals("Sunday, January 12, 2025", result);
}
String result = timeSkill.today(defaultLocale);
assertEquals("Sunday, January 12, 2025", result);
}
@Test
public void testNow() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.now();
assertEqualsIgnoreCase("Sunday, January 12, 2025 9:15 AM", result);
}
String result = timeSkill.now(defaultLocale);
assertEqualsIgnoreCase("Sunday, January 12, 2025 9:15 AM", result);
}
@Test
public void testYear() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.year();
assertEquals("2025", result);
}
String result = timeSkill.year(defaultLocale);
assertEquals("2025", result);
}
@Test
public void testMonth() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.month();
assertEquals("January", result);
}
String result = timeSkill.month(defaultLocale);
assertEquals("January", result);
}
@Test
public void testMonthNumber() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.monthNumber();
assertEquals("01", result);
}
String result = timeSkill.monthNumber(defaultLocale);
assertEquals("01", result);
}
@Test
public void testDay() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.day();
assertEquals("12", result);
}
String result = timeSkill.day(defaultLocale);
assertEquals("12", result);
}
@Test
public void testDayOfWeek() {
TimeSkill timeSkill = new TimeSkill();
String result = timeSkill.dayOfWeek(defaultLocale);
assertEquals("Sunday", result);
}
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
@Test
public void testHour() {
String result = timeSkill.hour(defaultLocale);
assertEqualsIgnoreCase("9 AM", result);
}
String result = timeSkill.dayOfWeek();
assertEquals("Sunday", result);
}
@Test
public void testHourNumber() {
String result = timeSkill.hourNumber(defaultLocale);
assertEquals("09", result);
}
@Test
public void testMinute() {
String result = timeSkill.minute(defaultLocale);
assertEquals("15", result);
}
@Test
public void testSecond() {
String result = timeSkill.second(defaultLocale);
assertEquals("07", result);
}
@Test
public void daysAgo() {
assertEquals("Thursday, January 9, 2025", timeSkill.daysAgo("3", defaultLocale));
assertEquals("Tuesday, January 7, 2025", timeSkill.daysAgo("5", defaultLocale));
}
@Test
public void testDateMatchingLastDayName() {
assertEquals(
"Sunday, January 5, 2025",
timeSkill.dateMatchingLastDayName("Sunday", defaultLocale));
assertEquals(
"Monday, January 6, 2025",
timeSkill.dateMatchingLastDayName("Monday", defaultLocale));
assertEquals(
"Tuesday, January 7, 2025",
timeSkill.dateMatchingLastDayName("Tuesday", defaultLocale));
assertEquals(
"Wednesday, January 8, 2025",
timeSkill.dateMatchingLastDayName("wednesday", defaultLocale));
assertEquals(
"Thursday, January 9, 2025",
timeSkill.dateMatchingLastDayName("thursday", defaultLocale));
assertEquals(
"Friday, January 10, 2025",
timeSkill.dateMatchingLastDayName("Friday", defaultLocale));
assertEquals(
"Saturday, January 11, 2025",
timeSkill.dateMatchingLastDayName("Saturday", defaultLocale));
}
public void assertEqualsIgnoreCase(String a, String b) {
Assertions.assertEquals(a.toUpperCase(Locale.ROOT), b.toUpperCase(Locale.ROOT));
}
@Test
public void testHour() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.hour();
assertEqualsIgnoreCase("9 AM", result);
}
}
@Test
public void testHourNumber() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.hourNumber();
assertEquals("09", result);
}
}
@Test
public void testMinute() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.minute();
assertEquals("15", result);
}
}
@Test
public void testSecond() {
TimeSkill timeSkill = new TimeSkill();
try (MockedStatic<ZonedDateTime> mocked = mockStatic(ZonedDateTime.class)) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
String result = timeSkill.second();
assertEquals("07", result);
}
}
@Test
public void daysAgo() {
try (MockedStatic<ZonedDateTime> mocked =
mockStatic(
ZonedDateTime.class,
withSettings()
.useConstructor()
.outerInstance(mockDateTime)
.defaultAnswer(CALLS_REAL_METHODS))) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
assertEquals("Thursday, January 9, 2025", TimeSkill.daysAgo("3"));
assertEquals("Tuesday, January 7, 2025", TimeSkill.daysAgo("5"));
}
}
@Test
public void testDateMatchingLastDayName() {
try (MockedStatic<ZonedDateTime> mocked =
mockStatic(
ZonedDateTime.class,
withSettings()
.useConstructor()
.outerInstance(mockDateTime)
.defaultAnswer(CALLS_REAL_METHODS))) {
mocked.when(ZonedDateTime::now).thenReturn(mockDateTime);
assertEquals("Sunday, January 5, 2025", TimeSkill.dateMatchingLastDayName("Sunday"));
assertEquals("Monday, January 6, 2025", TimeSkill.dateMatchingLastDayName("Monday"));
assertEquals("Tuesday, January 7, 2025", TimeSkill.dateMatchingLastDayName("Tuesday"));
assertEquals(
"Wednesday, January 8, 2025", TimeSkill.dateMatchingLastDayName("wednesday"));
assertEquals(
"Thursday, January 9, 2025", TimeSkill.dateMatchingLastDayName("thursday"));
assertEquals("Friday, January 10, 2025", TimeSkill.dateMatchingLastDayName("Friday"));
assertEquals(
"Saturday, January 11, 2025", TimeSkill.dateMatchingLastDayName("Saturday"));
}
}
}
@@ -23,6 +23,9 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@@ -210,26 +213,7 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
try {
List<Object> args =
Arrays.stream(method.getParameters())
.map(
parameter -> {
if (SKContext.class.isAssignableFrom(
parameter.getType())) {
return context; // .copy();
} else {
String value =
getArgumentValue(
method, context, parameter,
inputArgs);
if (value != null) {
return value;
} else {
throw new AIException(
AIException.ErrorCodes
.InvalidConfiguration,
"Unknown arg " + parameter.getName());
}
}
})
.map(getParameters(method, context, inputArgs))
.collect(Collectors.toList());
Mono mono;
@@ -240,26 +224,7 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
return Mono.error(e);
}
} else {
mono =
Mono.defer(
() -> {
return Mono.fromCallable(
() -> {
try {
Object result =
method.invoke(
instance,
args.toArray());
return result;
} catch (IllegalAccessException
| InvocationTargetException e) {
throw new RuntimeException(
e.getCause());
}
})
.subscribeOn(Schedulers.boundedElastic());
});
mono = invokeAsyncFunction(method, instance, args);
}
return mono.map(
@@ -276,6 +241,56 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
};
}
private static Mono<Object> invokeAsyncFunction(
Method method, Object instance, List<Object> args) {
return Mono.defer(
() ->
Mono.fromCallable(
() -> {
try {
Object result =
method.invoke(instance, args.toArray());
return result;
} catch (IllegalAccessException
| InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
})
.subscribeOn(Schedulers.boundedElastic()));
}
private static Function<Parameter, Object> getParameters(
Method method, SKContext context, Set<Parameter> inputArgs) {
return parameter -> {
if (SKContext.class.isAssignableFrom(parameter.getType())) {
return context;
} else {
return getArgumentValue(method, context, parameter, inputArgs);
}
};
}
private static String formErrorMessage(Method method, Parameter parameter) {
Matcher matcher = Pattern.compile("arg(\\d)").matcher(parameter.getName());
matcher.find();
return "For the function "
+ method.getDeclaringClass().getName()
+ "."
+ method.getName()
+ ", the unknown parameter"
+ " name was detected as \""
+ parameter.getName()
+ "\" this is argument"
+ " number "
+ matcher.group(1)
+ " to the function, this indicates that the argument name for this function was"
+ " removed during compilation and semantic-kernel is unable to determine the name"
+ " of the parameter. To support this function the argument must be annotated with"
+ " @SKFunctionParameters or @SKFunctionInputAttribute. Alternatively the function"
+ " was invoked with a required context variable missing and no default value.";
}
private static String getArgumentValue(
Method method, SKContext context, Parameter parameter, Set<Parameter> inputArgs) {
String variableName = getGetVariableName(parameter);
@@ -295,26 +310,41 @@ public class NativeSKFunction extends AbstractSkFunction<Void> {
parameter.getAnnotation(SKFunctionParameters.class);
if (annotation != null) {
arg = annotation.defaultValue();
if (NO_DEFAULT_VALUE.equals(arg)) {
if (!annotation.required()) {
return null;
}
throw new AIException(
AIException.ErrorCodes.InvalidConfiguration,
"Attempted to invoke function "
+ method.getDeclaringClass().getName()
+ "."
+ method.getName()
+ ". The context variable \""
+ variableName
+ "\" has not been set, and no default value is"
+ " specified.");
}
}
}
}
if (arg == null && variableName.matches("arg\\d")) {
LOGGER.warn(
"For the function "
+ method.getDeclaringClass().getName()
+ "."
+ method.getName()
+ ", the parameter argument name was detected as \""
+ variableName
+ "\" this indicates that the argument name for this function was"
+ " removed during compilation. To support this function its arguments"
+ " must be annotated with @SKFunctionParameters with the name defined,"
+ " or @SKFunctionInputAttribute.");
LOGGER.warn(formErrorMessage(method, parameter));
}
if (NO_DEFAULT_VALUE.equals(arg)) {
return null;
if (parameter.getName().matches("arg\\d")) {
throw new AIException(
AIException.ErrorCodes.InvalidConfiguration,
formErrorMessage(method, parameter));
} else {
throw new AIException(
AIException.ErrorCodes.InvalidConfiguration,
"Unknown arg " + parameter.getName());
}
}
return arg;
}
@@ -65,8 +65,10 @@ public class Example06TemplateLanguageTest {
Mono<String> renderedPrompt = promptRenderer.renderAsync(skContext);
String renderedText = renderedPrompt.block();
// Check that it has been rendered
Assertions.assertTrue(!renderedPrompt.block().contains("time.Date"));
Assertions.assertTrue(!renderedText.contains("time.Date"));
// Run the prompt / semantic function
CompletionSKFunction kindOfDay =