From 0a7f96ae4b77bb2d2a76ca4c619da46312ad512e Mon Sep 17 00:00:00 2001 From: John Oliver <1615532+johnoliver@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:28:34 +0100 Subject: [PATCH] Java: Add OpenApi plugin importer and upgrade tool calling to cover more usecases (#5829) ### Motivation and Context ### Description ### Contribution Checklist - [ ] 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 :smile: --- .../chatcompletion/OpenAIChatCompletion.java | 193 +- .../openai/chatcompletion/OpenAIFunction.java | 40 +- .../semantickernel/tests/RenderingTest.java | 211 ++ .../tests/ToolCallBehaviourTest.java | 59 +- java/samples/pom.xml | 1 + .../ConfiguringPrompts.java | 2 + .../syntaxexamples/Example17_ChatGPT.java | 4 + .../semantickernel-openapi-plugin/pom.xml | 125 ++ .../openapi/OpenAPIHttpRequestPlugin.java | 176 ++ .../SemanticKernelOpenAPIImporter.java | 594 ++++++ .../test/java/ExampleAdoptiumImporter.java | 69 + .../src/test/java/ExampleOpenAPIParent.java | 47 + .../test/java/ExamplePetstoreImporter.java | 56 + .../src/test/resources/adoptium.json | 1857 +++++++++++++++++ .../src/test/resources/adoptium.yaml | 1381 ++++++++++++ .../src/test/resources/petstore.yaml | 822 ++++++++ java/semantickernel-api/pom.xml | 3 +- .../semantickernel/hooks/KernelHook.java | 11 + .../semantickernel/hooks/KernelHooks.java | 12 + .../hooks/PreToolCallEvent.java | 48 + .../orchestration/FunctionInvocation.java | 8 + .../plugin/KernelPluginFactory.java | 36 +- .../semanticfunctions/InputVariable.java | 27 +- .../KernelFunctionFromMethod.java | 22 + .../KernelFunctionFromMethodTest.java | 4 +- .../KernelFunctionYamlTest.java | 6 +- .../PromptTemplateConfigTest.java | 6 +- .../PromptTemplateFactoryTest.java | 4 +- java/semantickernel-bom/pom.xml | 3 +- 29 files changed, 5726 insertions(+), 101 deletions(-) create mode 100644 java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/RenderingTest.java create mode 100644 java/samples/semantickernel-openapi-plugin/pom.xml create mode 100644 java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/OpenAPIHttpRequestPlugin.java create mode 100644 java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/SemanticKernelOpenAPIImporter.java create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/java/ExampleAdoptiumImporter.java create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/java/ExampleOpenAPIParent.java create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/java/ExamplePetstoreImporter.java create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.json create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.yaml create mode 100644 java/samples/semantickernel-openapi-plugin/src/test/resources/petstore.yaml create mode 100644 java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java index b14189d5c9..c8f4e37244 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java @@ -22,6 +22,7 @@ import com.azure.core.util.BinaryData; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ContainerNode; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.implementation.OpenAIRequestSettings; import com.microsoft.semantickernel.contextvariables.ContextVariable; @@ -29,9 +30,11 @@ import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.exceptions.AIException; import com.microsoft.semantickernel.exceptions.AIException.ErrorCodes; import com.microsoft.semantickernel.exceptions.SKException; +import com.microsoft.semantickernel.hooks.KernelHookEvent; import com.microsoft.semantickernel.hooks.KernelHooks; import com.microsoft.semantickernel.hooks.PostChatCompletionEvent; import com.microsoft.semantickernel.hooks.PreChatCompletionEvent; +import com.microsoft.semantickernel.hooks.PreToolCallEvent; import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; import com.microsoft.semantickernel.orchestration.InvocationContext; @@ -147,15 +150,15 @@ public class OpenAIChatCompletion implements ChatCompletionService { @Nullable InvocationContext invocationContext, int autoInvokeAttempts) { - KernelHooks kernelHooks = invocationContext != null - && invocationContext.getKernelHooks() != null - ? invocationContext.getKernelHooks() - : new KernelHooks(); - - ChatCompletionsOptions options = kernelHooks - .executeHooks(new PreChatCompletionEvent( - getCompletionsOptions(this, messages, - functions, invocationContext, autoInvokeAttempts))) + ChatCompletionsOptions options = executeHook( + invocationContext, + new PreChatCompletionEvent( + getCompletionsOptions( + this, + messages, + functions, + invocationContext, + autoInvokeAttempts))) .getOptions(); Mono> result = client @@ -177,7 +180,7 @@ public class OpenAIChatCompletion implements ChatCompletionService { .collect(Collectors.toList()); // execute post chat completion hook - kernelHooks.executeHooks(new PostChatCompletionEvent(completions)); + executeHook(invocationContext, new PostChatCompletionEvent(completions)); // Just return the result: // If we don't want to attempt to invoke any functions @@ -206,53 +209,122 @@ public class OpenAIChatCompletion implements ChatCompletionService { Mono.just(messages), (requestMessages, toolCall) -> { if (toolCall instanceof ChatCompletionsFunctionToolCall) { - return requestMessages - .flatMap(msgs -> { - // OpenAI only supports function tool call at the moment - ChatCompletionsFunctionToolCall functionToolCall = (ChatCompletionsFunctionToolCall) toolCall; - if (kernel == null) { - return Mono - .error(new SKException( - "A tool call was requested, but no kernel was provided to the invocation, this is a unsupported configuration")); - } - - ContextVariableTypes contextVariableTypes = invocationContext == null - ? new ContextVariableTypes() - : invocationContext.getContextVariableTypes(); - - return invokeFunctionTool( - kernel, - functionToolCall, - contextVariableTypes) - .map(functionResult -> { - // Add chat request tool message to the chat options - ChatRequestMessage requestToolMessage = new ChatRequestToolMessage( - functionResult.getResult(), - functionToolCall.getId()); - - ArrayList res = new ArrayList<>( - msgs); - res.add(requestToolMessage); - return res; - }); - }); + return performToolCall(kernel, invocationContext, requestMessages, + toolCall); } return requestMessages; }) .flatMap(it -> it) - .flatMap( - msgs -> { - return internalChatMessageContentsAsync(msgs, kernel, functions, + .flatMap(msgs -> { + return internalChatMessageContentsAsync(msgs, kernel, functions, + invocationContext, autoInvokeAttempts - 1); + }) + .onErrorResume(e -> { + // If FunctionInvocationError occurred and there are still attempts left, retry, else exit + if (autoInvokeAttempts > 0) { + List currentMessages = messages; + if (e instanceof FunctionInvocationError) { + currentMessages = ((FunctionInvocationError) e).getMessages(); + } + return internalChatMessageContentsAsync(currentMessages, kernel, + functions, invocationContext, autoInvokeAttempts - 1); - }); + } else { + return Mono.error(e); + } + }); }); return result.map(op -> (List>) op); } + private Mono> performToolCall( + @Nullable Kernel kernel, + @Nullable InvocationContext invocationContext, + Mono> requestMessages, + ChatCompletionsToolCall toolCall) { + return requestMessages + .flatMap(msgs -> { + try { + // OpenAI only supports function tool call at the moment + ChatCompletionsFunctionToolCall functionToolCall = (ChatCompletionsFunctionToolCall) toolCall; + if (kernel == null) { + return Mono + .error(new SKException( + "A tool call was requested, but no kernel was provided to the invocation, this is a unsupported configuration")); + } + + ContextVariableTypes contextVariableTypes = invocationContext == null + ? new ContextVariableTypes() + : invocationContext.getContextVariableTypes(); + + return invokeFunctionTool( + kernel, + invocationContext, + functionToolCall, + contextVariableTypes) + .map(functionResult -> { + // Add chat request tool message to the chat options + ChatRequestMessage requestToolMessage = new ChatRequestToolMessage( + functionResult.getResult(), + functionToolCall.getId()); + + ArrayList res = new ArrayList<>( + msgs); + res.add(requestToolMessage); + return res; + }) + .switchIfEmpty(Mono.fromSupplier( + () -> { + ChatRequestMessage requestToolMessage = new ChatRequestToolMessage( + "Completed successfully with no return value", + functionToolCall.getId()); + + ArrayList res = new ArrayList<>(msgs); + res.add(requestToolMessage); + return res; + })) + .onErrorResume(e -> emitError(toolCall, msgs, e)); + } catch (Exception e) { + return emitError(toolCall, msgs, e); + } + }); + } + + private Mono> emitError( + ChatCompletionsToolCall toolCall, + List msgs, + Throwable e) { + + msgs.add(new ChatRequestToolMessage( + "Call failed: " + e.getMessage(), + toolCall.getId())); + + return Mono.error( + new FunctionInvocationError(e, msgs)); + } + + /** + * Exception to be thrown when a function invocation fails. + */ + private static class FunctionInvocationError extends SKException { + + private final List messages; + + public FunctionInvocationError(Throwable e, List msgs) { + super(e.getMessage(), e); + this.messages = msgs; + } + + public List getMessages() { + return messages; + } + } + @SuppressWarnings("StringSplitter") private Mono> invokeFunctionTool( Kernel kernel, + @Nullable InvocationContext invocationContext, ChatCompletionsFunctionToolCall toolCall, ContextVariableTypes contextVariableTypes) { @@ -268,15 +340,35 @@ public class OpenAIChatCompletion implements ChatCompletionService { pluginName, openAIFunctionToolCall.getFunctionName()); + PreToolCallEvent hookResult = executeHook(invocationContext, new PreToolCallEvent( + openAIFunctionToolCall.getFunctionName(), + openAIFunctionToolCall.getArguments(), + function, + contextVariableTypes)); + + function = hookResult.getFunction(); + KernelFunctionArguments arguments = hookResult.getArguments(); + return function .invokeAsync(kernel) - .withArguments(openAIFunctionToolCall.getArguments()) + .withArguments(arguments) .withResultType(contextVariableTypes.getVariableTypeForClass(String.class)); } catch (JsonProcessingException e) { return Mono.error(new SKException("Failed to parse tool arguments")); } } + private static T executeHook( + @Nullable InvocationContext invocationContext, + T event) { + KernelHooks kernelHooks = invocationContext != null + && invocationContext.getKernelHooks() != null + ? invocationContext.getKernelHooks() + : new KernelHooks(); + + return kernelHooks.executeHooks(event); + } + @SuppressWarnings("StringSplitter") private OpenAIFunctionToolCall extractOpenAIFunctionToolCall( ChatCompletionsFunctionToolCall toolCall) throws JsonProcessingException { @@ -293,8 +385,15 @@ public class OpenAIChatCompletion implements ChatCompletionService { JsonNode jsonToolCallArguments = mapper.readTree(toolCall.getFunction().getArguments()); jsonToolCallArguments.fields().forEachRemaining( - entry -> arguments.put(entry.getKey(), - ContextVariable.of(entry.getValue().asText()))); + entry -> { + if (entry.getValue() instanceof ContainerNode) { + arguments.put(entry.getKey(), + ContextVariable.of(entry.getValue().toPrettyString())); + } else { + arguments.put(entry.getKey(), + ContextVariable.of(entry.getValue().asText())); + } + }); return new OpenAIFunctionToolCall( toolCall.getId(), diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIFunction.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIFunction.java index 49cd2b819e..862e7cc4c6 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIFunction.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIFunction.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -87,8 +88,8 @@ class OpenAIFunction { try { ObjectMapper objectMapper = new ObjectMapper(); for (InputVariable parameter : metadata.getParameters()) { - String parameterJsonSchema = getSchemaForFunctionParameter( - parameter.getDescription()); + String parameterJsonSchema = getSchemaForFunctionParameter(parameter); + properties.put(parameter.getName(), objectMapper.readTree(parameterJsonSchema)); if (parameter.isRequired()) { @@ -145,10 +146,37 @@ class OpenAIFunction { } } - private static String getSchemaForFunctionParameter(@Nullable String description) { - if (description == null) { - return "{\"type\":\"string\"}"; + private static String getSchemaForFunctionParameter(@Nullable InputVariable parameter) { + List entries = new ArrayList<>(); + + entries.add("\"type\":\"string\""); + + // Add description if present + if (parameter != null && parameter.getDescription() != null && !parameter.getDescription() + .isEmpty()) { + String description = parameter.getDescription(); + description = description.replace("\n", ""); + description = description.replace("\"", "\\\""); + + description = String.format("\"description\":\"%s\"", description); + entries.add(description); } - return String.format("{\"type\":\"string\", \"description\":\"%s\"}", description); + + // Add enum options if parameter is an enum + if (parameter != null && parameter.getEnumValues() != null && !parameter.getEnumValues() + .isEmpty()) { + String enumEntry = parameter + .getEnumValues() + .stream() + .map(Object::toString) + .map(it -> "\"" + it + "\"") + .collect(Collectors.joining(",")); + + entries.add("\"enum\":[ " + enumEntry + " ]"); + } + + String schema = String.join(",", entries); + + return "{" + schema + "}"; } } diff --git a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/RenderingTest.java b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/RenderingTest.java new file mode 100644 index 0000000000..9e3a534a37 --- /dev/null +++ b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/RenderingTest.java @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.tests; + +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; + +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; +import com.microsoft.semantickernel.aiservices.openai.textcompletion.OpenAITextGenerationService; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import com.microsoft.semantickernel.semanticfunctions.PromptTemplateConfig; +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; +import com.microsoft.semantickernel.services.textcompletion.TextGenerationService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@WireMockTest +public class RenderingTest { + + private WireMockServer wm; + + @BeforeEach + public void before() { + wm = new WireMockServer(); + } + + @AfterEach + public void after() { + wm.stop(); + } + + @Test + public void textSemanticKernelTemplate() { + buildTextKernel() + .invokeAsync( + KernelFunction + .createFromPrompt(""" + Value: {{$value}} + """) + .withTemplateFormat(PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT) + .build()) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("value", "{{$ignore}}") + .withVariable("ignore", "dont show") + .build()) + .block(); + + Assertions.assertFalse( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("dont show")); + Assertions.assertTrue( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("{{$ignore}}")); + } + + @Test + public void textHandleBarsTemplate() { + buildTextKernel() + .invokeAsync( + KernelFunction + .createFromPrompt(""" + Value: {{value}} + """) + .withTemplateFormat("handlebars") + .build()) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("value", "{{ignore}}") + .withVariable("ignore", "dont show") + .build()) + .block(); + + Assertions.assertFalse( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("dont show")); + Assertions.assertTrue( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("{{ignore}}")); + } + + @Test + public void chatSemanticKernelTemplate() { + buildChatKernel() + .invokeAsync( + KernelFunction + .createFromPrompt(""" + Value: {{$value}} + """) + .withTemplateFormat(PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT) + .build()) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("value", "{{$ignore}}") + .withVariable("ignore", "dont show") + .build()) + .block(); + + Assertions.assertFalse( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("dont show")); + Assertions.assertTrue( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("{{$ignore}}")); + } + + @Test + public void chatHandleBarsTemplate() { + buildChatKernel() + .invokeAsync( + KernelFunction + .createFromPrompt(""" + Value: {{value}} + """) + .withTemplateFormat("handlebars") + .build()) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("value", "{{ignore}}") + .withVariable("ignore", "dont show") + .build()) + .block(); + + Assertions.assertFalse( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("dont show")); + Assertions.assertTrue( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("{{ignore}}")); + } + + @Test + public void chatSemanticKernelTemplate2() { + buildChatKernel() + .invokeAsync( + KernelFunction + .createFromPrompt(""" + Value: {{$value}} + """) + .withTemplateFormat(PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT) + .build()) + .withArguments(KernelFunctionArguments + .builder() + .withVariable("value", "{{$ignore}}") + .withVariable("ignore", "dont show") + .build()) + .block(); + + Assertions.assertFalse( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("dont show")); + Assertions.assertTrue( + wm.getAllServeEvents().get(0).getRequest().getBodyAsString().contains("{{$ignore}}")); + } + + private Kernel buildTextKernel() { + wm.addStubMapping( + stubFor( + ToolCallBehaviourTest.buildTextResponse(" ", """ + "choices": [ + { + "text": "Value: bar" + } + ] + """))); + wm.start(); + + final OpenAIAsyncClient client = new OpenAIClientBuilder() + .endpoint("http://localhost:" + wm.port() + "/") + .buildAsyncClient(); + + TextGenerationService textGenerationService = OpenAITextGenerationService.builder() + .withOpenAIAsyncClient(client) + .withModelId("gpt-35-turbo-2") + .build(); + + Kernel kernel = Kernel.builder() + .withAIService(TextGenerationService.class, textGenerationService) + .build(); + return kernel; + } + + private Kernel buildChatKernel() { + wm.addStubMapping( + stubFor( + ToolCallBehaviourTest.buildResponse(" ", """ + "choices" : [ + { + "finish_reason" : "stop", + "index" : 0, + "message" : { + "content" : "done", + "role" : "assistant" + } + } + ] + """))); + wm.start(); + + final OpenAIAsyncClient client = new OpenAIClientBuilder() + .endpoint("http://localhost:" + wm.port() + "/") + .buildAsyncClient(); + + ChatCompletionService textGenerationService = OpenAIChatCompletion.builder() + .withOpenAIAsyncClient(client) + .withModelId("gpt-35-turbo-2") + .build(); + + Kernel kernel = Kernel.builder() + .withAIService(ChatCompletionService.class, textGenerationService) + .build(); + return kernel; + } +} diff --git a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ToolCallBehaviourTest.java b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ToolCallBehaviourTest.java index cb113ad1ba..19ad3ae31a 100644 --- a/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ToolCallBehaviourTest.java +++ b/java/api-test/integration-tests/src/test/java/com/microsoft/semantickernel/tests/ToolCallBehaviourTest.java @@ -15,7 +15,6 @@ import com.github.tomakehurst.wiremock.matching.ContainsPattern; import com.microsoft.semantickernel.Kernel; import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatMessageContent; import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIFunctionToolCall; -import com.microsoft.semantickernel.exceptions.SKException; import com.microsoft.semantickernel.orchestration.InvocationContext; import com.microsoft.semantickernel.orchestration.ToolCallBehavior; import com.microsoft.semantickernel.plugin.KernelPlugin; @@ -49,37 +48,6 @@ public class ToolCallBehaviourTest { wm.stop(); } - @Test - public void toolThatDoesNotExistThrows() { - ChatCompletionService chatCompletionService = getChatCompletionService(); - - Kernel kernel = Kernel.builder() - .withAIService(ChatCompletionService.class, chatCompletionService) - .build(); - - ChatHistory messages = new ChatHistory(); - messages.addMessage( - new ChatMessageContent<>( - AuthorRole.USER, - "Call A function")); - - try { - chatCompletionService - .getChatMessageContentsAsync( - messages, - kernel, - InvocationContext.builder() - .withToolCallBehavior( - ToolCallBehavior.allowAllKernelFunctions(true)) - .build()) - .block(); - Assertions.fail("Expected an exception to be thrown"); - } catch (SKException e) { - Assertions.assertTrue(e.getMessage() - .contains("Failed to find plugin apluginname")); - } - } - @Test public void nonAutoInvokedIsNotCalled() throws NoSuchMethodException { ChatCompletionService chatCompletionService = getChatCompletionService(); @@ -251,7 +219,18 @@ public class ToolCallBehaviourTest { .build(); } - private static MappingBuilder buildResponse(String bodyMatcher, String responseBody) { + public static MappingBuilder buildTextResponse(String bodyMatcher, String responseBody) { + return post(urlEqualTo( + "//openai/deployments/gpt-35-turbo-2/completions?api-version=2024-02-15-preview")) + .withRequestBody(new ContainsPattern(bodyMatcher)) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(formTextResponse(responseBody))); + } + + public static MappingBuilder buildResponse(String bodyMatcher, String responseBody) { return post(urlEqualTo( "//openai/deployments/gpt-35-turbo-2/chat/completions?api-version=2024-02-15-preview")) .withRequestBody(new ContainsPattern(bodyMatcher)) @@ -269,6 +248,20 @@ public class ToolCallBehaviourTest { } } + private static String formTextResponse(String chatChoices) { + return """ + { + "id": "cmpl-3QJ9z1J9z1J9z1J9z1J9z1J9", + "usage": { + "total_tokens": 4, + "completion_tokens": 1, + "prompt_tokens": 1 + }, + %s + } + """.formatted(chatChoices); + } + private static String formResponse(String chatChoices) { return """ { diff --git a/java/samples/pom.xml b/java/samples/pom.xml index dddfb6c6fd..ca04c49159 100644 --- a/java/samples/pom.xml +++ b/java/samples/pom.xml @@ -16,6 +16,7 @@ sample-code + semantickernel-openapi-plugin booking-agent-m365 diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/ConfiguringPrompts.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/ConfiguringPrompts.java index c220d13803..2f73c58bb5 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/ConfiguringPrompts.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/documentationexamples/ConfiguringPrompts.java @@ -74,12 +74,14 @@ public class ConfiguringPrompts { String.class, "The history of the conversation.", null, + null, false)) .addInputVariable(InputVariable.build( "request", String.class, "The user's request.", null, + null, true)) .withExecutionSettings(new HashMap() { { diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java index 16b701ef6d..286b3394c7 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example17_ChatGPT.java @@ -37,6 +37,10 @@ public class Example17_ChatGPT { .buildAsyncClient(); } + client = new OpenAIClientBuilder() + .endpoint("http://localhost:8123") + .buildAsyncClient(); + ChatCompletionService chatGPT = OpenAIChatCompletion.builder() .withModelId(MODEL_ID) .withOpenAIAsyncClient(client) diff --git a/java/samples/semantickernel-openapi-plugin/pom.xml b/java/samples/semantickernel-openapi-plugin/pom.xml new file mode 100644 index 0000000000..23f97938f4 --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/pom.xml @@ -0,0 +1,125 @@ + + + 4.0.0 + + com.microsoft.semantic-kernel + semantickernel-samples-parent + 1.0.2-SNAPSHOT + ../pom.xml + + + semantickernel-openapi-plugin + semantickernel-openapi-plugin + jar + + + + + com.microsoft.semantic-kernel + semantickernel-bom + ${project.version} + pom + import + + + + + + + com.microsoft.semantic-kernel + semantickernel-api + + + + org.apache.logging.log4j + log4j-api + runtime + + + org.apache.logging.log4j + log4j-core + runtime + + + org.apache.logging.log4j + log4j-slf4j2-impl + runtime + + + com.fasterxml.jackson.core + jackson-databind + compile + + + com.fasterxml.jackson.core + jackson-core + compile + + + com.azure + azure-identity + + + io.swagger.parser.v3 + swagger-parser + 2.1.22 + + + com.microsoft.semantic-kernel + semantickernel-aiservices-openai + + + + + + bug-check + + + false + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + true + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-sample + + java + + + + + com.microsoft.semantickernel.samples.syntaxexamples.${sample} + + + + + diff --git a/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/OpenAPIHttpRequestPlugin.java b/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/OpenAPIHttpRequestPlugin.java new file mode 100644 index 0000000000..f62eb17f8f --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/OpenAPIHttpRequestPlugin.java @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.samples.openapi; + +import com.azure.core.http.ContentType; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.semantickernel.contextvariables.ContextVariable; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +/** + * Plugin for making HTTP requests specifically to endpoints discovered via OpenAPI. + */ +class OpenAPIHttpRequestPlugin { + + private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIHttpRequestPlugin.class); + + private final String serverUrl; + private final String path; + private final PathItem pathItem; + private final HttpClient client; + private final HttpMethod method; + private final Operation operation; + + public OpenAPIHttpRequestPlugin( + HttpMethod method, + String serverUrl, + String path, + PathItem pathItem, + HttpClient client, + Operation operation) { + this.method = method; + this.serverUrl = serverUrl; + this.path = path; + this.pathItem = pathItem; + this.client = client; + this.operation = operation; + } + + /** + * Executes the HTTP request and return the body of the response. + * + * @param arguments The arguments to the http request. + * @return The body of the response. + */ + public Mono execute(KernelFunctionArguments arguments) { + String body = getBody(arguments); + String query = buildQueryString(arguments); + String path = buildQueryPath(arguments); + + String url; + if (!query.isEmpty()) { + url = serverUrl + path + "?" + query; + } else { + url = serverUrl + path; + } + + HttpRequest request = new HttpRequest(method, url); + + if (body != null) { + request = request + .setHeaders(new HttpHeaders() + .set(HttpHeaderName.CONTENT_TYPE, ContentType.APPLICATION_JSON)) + .setBody(body); + } + + LOGGER.debug("Executing {} {}", method.name(), url); + if (body != null) { + LOGGER.debug("Body: {}", body); + } + + return client + .send(request) + .flatMap(response -> { + if (response.getStatusCode() >= 400) { + return Mono.error(new RuntimeException( + "Request failed with status code: " + response.getStatusCode())); + } else { + return Mono.just(response); + } + }) + .flatMap(HttpResponse::getBodyAsString) + .doOnNext(response -> LOGGER.debug("Request response: {}", response)); + } + + private static @Nullable String getBody(KernelFunctionArguments arguments) { + String body = null; + if (arguments.containsKey("requestbody")) { + ContextVariable requestBody = arguments.get("requestbody"); + if (requestBody != null) { + try { + JsonNode tree = new ObjectMapper().readTree(requestBody.getValue(String.class)); + body = tree.toPrettyString(); + } catch (JsonProcessingException e) { + body = requestBody.getValue(String.class); + } + } + arguments.remove("requestbody"); + } + return body; + } + + private String buildQueryPath(KernelFunctionArguments arguments) { + return getParameterStreamOfArguments(arguments) + .filter(p -> p instanceof PathParameter) + .reduce(path, (path, parameter) -> { + String name = parameter.getName(); + String rendered = getRenderedParameter(arguments, name); + + return path.replaceAll("\\{" + name + "}", rendered); + }, (a, b) -> a + b); + } + + private static String getRenderedParameter( + KernelFunctionArguments arguments, String name) { + ContextVariable value = arguments.get(name); + + if (value == null) { + throw new IllegalArgumentException("Missing value for path parameter: " + name); + } + String rendered = value.getValue(String.class); + + if (rendered == null) { + throw new IllegalArgumentException("Path parameter value is null: " + name); + } + return URLEncoder.encode(rendered, StandardCharsets.US_ASCII); + } + + private String buildQueryString(KernelFunctionArguments arguments) { + return getParameterStreamOfArguments(arguments) + .filter(p -> p instanceof QueryParameter) + .map(parameter -> { + String name = parameter.getName(); + String rendered = getRenderedParameter(arguments, name); + return name + "=" + rendered; + }) + .collect(Collectors.joining("&")); + } + + private Stream getParameterStreamOfArguments( + KernelFunctionArguments arguments) { + if (operation.getParameters() == null) { + return Stream.empty(); + } + return arguments + .keySet() + .stream() + .map(contextVariable -> pathItem + .getGet() + .getParameters() + .stream() + .filter(param -> param.getName().equals(contextVariable)).findFirst()) + .filter(Optional::isPresent) + .map(Optional::get); + } +} \ No newline at end of file diff --git a/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/SemanticKernelOpenAPIImporter.java b/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/SemanticKernelOpenAPIImporter.java new file mode 100644 index 0000000000..f9899cd55e --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/main/java/com/microsoft/semantickernel/samples/openapi/SemanticKernelOpenAPIImporter.java @@ -0,0 +1,594 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.samples.openapi; + +import com.azure.core.http.ContentType; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.util.Header; +import com.azure.core.util.HttpClientOptions; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.semantickernel.exceptions.SKException; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.semanticfunctions.InputVariable; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import com.microsoft.semantickernel.semanticfunctions.OutputVariable; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.BooleanSchema; +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.MapSchema; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.NumberSchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.ParseOptions; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.apache.commons.text.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SemanticKernelOpenAPIImporter { + + private static final Logger LOGGER = LoggerFactory.getLogger( + SemanticKernelOpenAPIImporter.class); + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String pluginName; + private String schema; + private HttpHeaders httpHeaders; + private HttpClient client; + private Function, String> serverSelector; + + public Builder withPluginName(String pluginName) { + this.pluginName = pluginName; + return this; + } + + public Builder withSchema(String schema) { + this.schema = schema; + return this; + } + + public Builder withHttpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; + return this; + } + + public Builder withClient(HttpClient client) { + this.client = client; + return this; + } + + public Builder withServerSelector(Function, String> serverSelector) { + this.serverSelector = serverSelector; + return this; + } + + public Builder withServer(String serverUrl) { + this.serverSelector = (ignore) -> serverUrl; + return this; + } + + public KernelPlugin build() { + return SemanticKernelOpenAPIImporter.fromSchema(pluginName, schema, httpHeaders, client, + serverSelector); + } + } + + public static KernelPlugin fromSchema( + String pluginName, + String schema, + @Nullable HttpHeaders httpHeaders, + @Nullable HttpClient client, + @Nullable Function, String> serverSelector) { + ParseOptions parseOptions = new ParseOptions(); + parseOptions.setResolve(true); + parseOptions.setResolveFully(true); + + OpenAPI openAPI = new OpenAPIV3Parser().readContents(schema, null, parseOptions) + .getOpenAPI(); + + client = getHttpClient(httpHeaders, client); + + Paths paths = openAPI.getPaths(); + + String serverUrl; + + if (serverSelector != null) { + serverUrl = serverSelector.apply(openAPI.getServers()); + } else { + serverUrl = openAPI.getServers().get(0).getUrl(); + if (openAPI.getServers().size() > 1) { + LOGGER.warn("Multiple servers found, using the first one: {}", serverUrl); + } + } + + Map> functions = getFunctions( + client, + pluginName, + paths, + serverUrl); + + return new KernelPlugin( + pluginName, + openAPI.getInfo().getDescription(), + functions); + } + + private static HttpClient getHttpClient( + @Nullable HttpHeaders httpHeaders, + @Nullable HttpClient client) { + + if (httpHeaders != null && client != null) { + throw new IllegalArgumentException( + "Both httpHeaders and client cannot be provided at the same time"); + } + + HttpClientOptions options = new HttpClientOptions(); + if (httpHeaders != null) { + options.setHeaders(httpHeaders.stream() + .map(header -> new Header(header.getName(), header.getValuesList())) + .toList()); + } + + if (client == null) { + client = HttpClient.createDefault(options); + } + return client; + } + + private static Map> getFunctions( + HttpClient client, + String pluginName, + Paths paths, + String serverUrl) { + return paths + .entrySet() + .stream() + .flatMap((entry) -> { + return formGetOperation( + client, + pluginName, + serverUrl, + entry.getKey(), + entry.getValue()) + .stream(); + }) + .collect(Collectors.toMap(KernelFunction::getName, (func) -> func)); + } + + private static List> formGetOperation( + HttpClient client, + String pluginName, + String serverUrl, + String path, + PathItem pathItem) { + + List> plugins = new ArrayList<>(); + + if (pathItem.getGet() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getGet(), + HttpMethod.GET); + + plugins.add(function); + } + if (pathItem.getDelete() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getDelete(), + HttpMethod.DELETE); + + plugins.add(function); + } + if (pathItem.getPost() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getPost(), + HttpMethod.POST); + + plugins.add(function); + } + if (pathItem.getPut() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getPut(), + HttpMethod.PUT); + + plugins.add(function); + } + if (pathItem.getPatch() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getPatch(), + HttpMethod.PATCH); + + plugins.add(function); + } + if (pathItem.getOptions() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getOptions(), + HttpMethod.OPTIONS); + + plugins.add(function); + } + if (pathItem.getHead() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getHead(), + HttpMethod.HEAD); + + plugins.add(function); + } + if (pathItem.getTrace() != null) { + var function = getKernelFunctionFromRequest( + client, + pluginName, + serverUrl, + path, + pathItem, + pathItem.getTrace(), + HttpMethod.TRACE); + + plugins.add(function); + } + + return plugins; + } + + private static @Nullable KernelFunction getKernelFunctionFromRequest(HttpClient client, + String pluginName, String serverUrl, String path, PathItem pathItem, + Operation operation, + HttpMethod method) { + + List variableList = getInputVariables(operation); + OutputVariable ov = getOutputVariable(operation); + String description = getDescription(operation.getDescription()); + + OpenAPIHttpRequestPlugin plugin = new OpenAPIHttpRequestPlugin(method, serverUrl, path, + pathItem, client, + operation); + + RequestBody requestBody = operation.getRequestBody(); + + if (requestBody != null) { + if (requestBody.getContent().get(ContentType.APPLICATION_JSON) != null) { + MediaType mediaType = requestBody.getContent().get(ContentType.APPLICATION_JSON); + Schema schema = mediaType.getSchema(); + + String example = renderJsonExample(schema); + + try { + JsonNode parsed = new ObjectMapper() + .enable(Feature.ALLOW_TRAILING_COMMA) + .readTree(example); + example = parsed.get("root").toPrettyString(); + } catch (JsonProcessingException e) { + throw new RuntimeException("Failed to parse example JSON", e); + } + + List enums = getEnumValues(schema); + + variableList = new ArrayList<>(variableList); + variableList.add(InputVariable.build( + "requestBody", + String.class, + "Example request body:\n" + example, + example, + enums, + true)); + + } else { + LOGGER.warn("No content type found for operation {}", operation.getOperationId()); + } + } + + return buildKernelFunction(pluginName, plugin, operation, description, variableList, ov); + } + + private static String renderJsonExample(Schema schema) { + StringBuilder stringBuilder = new StringBuilder(); + renderJsonExample(stringBuilder, "root", schema); + return "{" + stringBuilder + "}"; + } + + private static void renderJsonExample( + StringBuilder stringBuilder, + String valueKey, + Schema schema) { + + if (schema.getExample() != null) { + if (valueKey != null) { + stringBuilder.append("\"").append(valueKey).append("\": "); + } + + if (schema instanceof StringSchema) { + stringBuilder.append("\"").append(schema.getExample().toString()).append("\","); + } else { + stringBuilder.append(schema.getExample().toString()).append(","); + } + return; + } + + if (schema instanceof IntegerSchema || + schema instanceof StringSchema || + schema instanceof NumberSchema || + schema instanceof BooleanSchema) { + + String value; + + if (schema instanceof IntegerSchema) { + value = "1"; + } else if (schema instanceof StringSchema) { + if (schema.getEnum() != null && !schema.getEnum().isEmpty()) { + value = "\"" + schema.getEnum().get(0).toString() + "\""; + } else { + value = "\"string\""; + } + } else if (schema instanceof NumberSchema) { + value = "1.0"; + } else { + value = "true"; + } + + if (valueKey != null) { + stringBuilder.append("\"").append(valueKey).append("\": "); + } + stringBuilder.append(value).append(","); + + } else if (schema instanceof ObjectSchema objectSchema) { + + if (valueKey != null) { + stringBuilder.append("\"").append(valueKey).append("\": "); + } + + stringBuilder.append("{"); + + objectSchema.getProperties().forEach((key, value) -> { + renderJsonExample(stringBuilder, key, value); + }); + stringBuilder.append("},"); + } else if (schema instanceof ArraySchema arraySchema) { + + if (valueKey != null) { + stringBuilder.append("\"").append(valueKey).append("\": "); + } + + stringBuilder.append("["); + renderJsonExample(stringBuilder, null, + arraySchema.getItems()); + stringBuilder.append("],"); + + } else if (schema instanceof MapSchema) { + throw new SKException("Not yet supported"); + } else { + LOGGER.warn("Unsupported schema type {}", schema.getClass().getName()); + } + } + + private static String renderXmlExample(Schema schema) { + StringBuilder stringBuilder = new StringBuilder(); + renderXmlExample(stringBuilder, null, schema); + return stringBuilder.toString(); + } + + private static void renderXmlExample(StringBuilder stringBuilder, String valueKey, + Schema schema) { + + if (schema.getExample() != null) { + stringBuilder.append("<").append(valueKey).append(">"); + stringBuilder.append(schema.getExample().toString()); + stringBuilder.append(""); + return; + } + + if (schema instanceof IntegerSchema || + schema instanceof StringSchema || + schema instanceof NumberSchema || + schema instanceof BooleanSchema) { + + String value; + + if (schema instanceof IntegerSchema) { + value = "1"; + } else if (schema instanceof StringSchema) { + if (schema.getEnum() != null && !schema.getEnum().isEmpty()) { + value = schema.getEnum().get(0).toString(); + } else { + value = "string"; + } + } else if (schema instanceof NumberSchema) { + value = "1.0"; + } else { + value = "true"; + } + + stringBuilder.append("<").append(valueKey).append(">"); + stringBuilder.append(value); + stringBuilder.append(""); + } else if (schema instanceof ObjectSchema objectSchema) { + + String nameTag = "<" + schema.getXml().getName() + ">"; + String closingNameTag = ""; + + stringBuilder.append(nameTag); + objectSchema.getProperties().forEach((key, value) -> { + renderXmlExample(stringBuilder, key, value); + }); + stringBuilder.append(closingNameTag); + } else if (schema instanceof ArraySchema arraySchema) { + String nameTag = "<" + valueKey + ">"; + String closingNameTag = ""; + stringBuilder.append(nameTag); + renderXmlExample(stringBuilder, arraySchema.getItems().getXml().getName(), + arraySchema.getItems()); + stringBuilder.append(closingNameTag); + } else if (schema instanceof MapSchema) { + throw new SKException("Not yet supported"); + } else { + LOGGER.warn("Unsupported schema type {}", schema.getClass().getName()); + } + } + + private static KernelFunction buildKernelFunction( + String pluginName, + OpenAPIHttpRequestPlugin plugin, + Operation operation, + String description, + List variableList, + OutputVariable ov) { + + try { + Method method = OpenAPIHttpRequestPlugin.class.getMethod("execute", + KernelFunctionArguments.class); + + return KernelFunction + .createFromMethod(method, plugin) + .withFunctionName(operation.getOperationId()) + .withPluginName(pluginName) + .withDescription(description) + .withParameters(variableList) + .withReturnParameter(ov) + .build(); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static OutputVariable getOutputVariable(Operation get) { + return new OutputVariable<>( + get.getDescription(), + String.class); + } + + private static @Nullable String getDescription(String get) { + String description = get; + if (description != null) { + description = StringEscapeUtils.escapeXml11(description); + description = description.replaceAll("\\n", ""); + } + return description; + } + + private static List getInputVariables(Operation get) { + List variableList = List.of(); + if (get.getParameters() != null) { + variableList = get + .getParameters() + .stream() + .map(parameter -> { + Class type = getType(parameter); + + Object def = parameter.getSchema().getDefault(); + + String description = getDescription(parameter.getDescription()); + + List enums = getEnumValues(parameter.getSchema()); + + return InputVariable.build( + parameter.getName(), + type, + description, + def != null ? def.toString() : null, + enums, + parameter.getRequired()); + }) + .collect(Collectors.toList()); + } + return variableList; + } + + private static List getEnumValues(Schema parameter) { + if (parameter.getEnum() == null) { + return null; + } + return parameter.getEnum() + .stream() + .map(Object::toString) + .toList(); + } + + private static Class getType(Parameter parameter) { + Class type = String.class; + String t = parameter.getSchema().getType(); + if (t != null) { + switch (t) { + case "integer": + type = Integer.class; + break; + case "number": + type = Double.class; + break; + case "boolean": + type = Boolean.class; + break; + case "array": + type = List.class; + break; + + // Not sure if we can support this + case "null": + break; + case "object": + break; + default: + break; + } + } + return type; + } +} diff --git a/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleAdoptiumImporter.java b/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleAdoptiumImporter.java new file mode 100644 index 0000000000..b1c77c337a --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleAdoptiumImporter.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.implementation.EmbeddedResourceLoader; +import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.orchestration.ToolCallBehavior; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.samples.openapi.SemanticKernelOpenAPIImporter; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import java.io.FileNotFoundException; + +public class ExampleAdoptiumImporter { + + public static void main(String[] args) throws FileNotFoundException { + runJson(); + runYaml(); + } + + private static void runJson() throws FileNotFoundException { + String json = EmbeddedResourceLoader.readFile("adoptium.json", + ExampleAdoptiumImporter.class); + makeRequests(json); + } + + private static void runYaml() throws FileNotFoundException { + String yaml = EmbeddedResourceLoader.readFile("adoptium.yaml", + ExampleAdoptiumImporter.class); + makeRequests(yaml); + } + + private static void makeRequests(String schema) { + KernelPlugin plugin = SemanticKernelOpenAPIImporter + .builder() + .withPluginName("adoptium") + .withSchema(schema) + .build(); + + Kernel kernel = ExampleOpenAPIParent.kernelBuilder() + .withPlugin(plugin) + .build(); + + performRequest(kernel, + """ + Parse the java version 11.0.22+9? + """); + + performRequest(kernel, + """ + What are the names of the GA versions of Java available between 8 and 9 inclusive? + """); + + performRequest(kernel, + """ + What is the version of the latest GA release of Java between 11 and 12? + """); + } + + private static void performRequest(Kernel kernel, String request) { + KernelFunction function = KernelFunction.createFromPrompt(request) + .build(); + + FunctionResult result = kernel.invokeAsync(function) + .withResultType(String.class) + .withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true)) + .block(); + + System.out.println(result.getResult()); + } + +} diff --git a/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleOpenAPIParent.java b/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleOpenAPIParent.java new file mode 100644 index 0000000000..9cfc77a3a0 --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/java/ExampleOpenAPIParent.java @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.KeyCredential; +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.Kernel.Builder; +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; + +public class ExampleOpenAPIParent { + + private static final String PLUGIN_DIR = System.getenv("PLUGIN_DIR") == null ? "." + : System.getenv("PLUGIN_DIR"); + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); + + // Only required if AZURE_CLIENT_KEY is set + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); + private static final String MODEL_ID = System.getenv() + .getOrDefault("MODEL_ID", "gpt-35-turbo-2"); + + public static Builder kernelBuilder() { + + OpenAIAsyncClient client; + + if (AZURE_CLIENT_KEY != null) { + client = new OpenAIClientBuilder() + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) + .endpoint(CLIENT_ENDPOINT) + .buildAsyncClient(); + + } else { + client = new OpenAIClientBuilder() + .credential(new KeyCredential(CLIENT_KEY)) + .buildAsyncClient(); + } + + ChatCompletionService openAIChatCompletion = ChatCompletionService.builder() + .withOpenAIAsyncClient(client) + .withModelId(MODEL_ID) + .build(); + + return Kernel.builder() + .withAIService(ChatCompletionService.class, openAIChatCompletion); + } + +} diff --git a/java/samples/semantickernel-openapi-plugin/src/test/java/ExamplePetstoreImporter.java b/java/samples/semantickernel-openapi-plugin/src/test/java/ExamplePetstoreImporter.java new file mode 100644 index 0000000000..4996707535 --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/java/ExamplePetstoreImporter.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft. All rights reserved. +import com.microsoft.semantickernel.Kernel; +import com.microsoft.semantickernel.implementation.EmbeddedResourceLoader; +import com.microsoft.semantickernel.orchestration.FunctionResult; +import com.microsoft.semantickernel.orchestration.ToolCallBehavior; +import com.microsoft.semantickernel.plugin.KernelPlugin; +import com.microsoft.semantickernel.samples.openapi.SemanticKernelOpenAPIImporter; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import java.io.FileNotFoundException; + +public class ExamplePetstoreImporter { + + public static void main(String[] args) throws FileNotFoundException { + + String yaml = EmbeddedResourceLoader.readFile("petstore.yaml", + ExamplePetstoreImporter.class); + + KernelPlugin plugin = SemanticKernelOpenAPIImporter + .builder() + .withPluginName("petstore") + .withSchema(yaml) + .withServer("http://localhost:8090/api/v3") + .build(); + + Kernel kernel = ExampleOpenAPIParent.kernelBuilder() + .withPlugin(plugin) + .build(); + + performRequest(kernel, + """ + Create a user with the following details: + username: john_doe + first name: John + last name: Doe + email: john_doe@example.com + password: password123 + phone: 1234567890 + user status: 1"""); + performRequest(kernel, "Add a new cat called Sandy to the store."); + performRequest(kernel, "List all available pets."); + + } + + private static void performRequest(Kernel kernel, String request) { + KernelFunction function = KernelFunction.createFromPrompt(request) + .build(); + + FunctionResult result = kernel.invokeAsync(function) + .withResultType(String.class) + .withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(true)) + .block(); + + System.out.println(result.getResult()); + } + +} diff --git a/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.json b/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.json new file mode 100644 index 0000000000..9b439f427a --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.json @@ -0,0 +1,1857 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "v3", + "description" : "", + "version" : "3.0.0" + }, + "servers" : [ { + "url" : "https://api.adoptium.net" + }, { + "url" : "https://staging-api.adoptium.net" + } ], + "tags" : [ { + "name" : "Assets" + }, { + "name" : "Binary" + }, { + "name" : "Checksum" + }, { + "name" : "Installer" + }, { + "name" : "Release Info" + }, { + "name" : "Signature" + }, { + "name" : "Version" + } ], + "paths" : { + "/v3/assets/feature_releases/{feature_version}/{release_type}" : { + "get" : { + "tags" : [ "Assets" ], + "summary" : "Returns release information", + "description" : "List of information about builds that match the current query", + "operationId" : "searchReleases", + "parameters" : [ { + "name" : "feature_version", + "in" : "path", + "description" : "\n

\n Feature release version you wish to download. Feature versions are whole numbers e.g. 8,11,16,17,18.\n

\n

\n Available Feature versions can be obtained from \n https://api.adoptium.net/v3/info/available_releases\n

\n", + "required" : true, + "schema" : { + "default" : 8, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "release_type", + "in" : "path", + "description" : "\n

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

\n", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "before", + "in" : "query", + "description" : "

Return binaries whose updated_at is before the given date/time. When a date is given the match is inclusive of the given day.

  • 2020-01-21
  • 2020-01-21T10:15:30
  • 20200121
  • 2020-12-21T10:15:30Z
  • 2020-12-21+01:00

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/DateTime" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "query", + "description" : "Heap Size", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "query", + "description" : "JVM Implementation", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "page", + "in" : "query", + "description" : "Pagination page number", + "required" : false, + "schema" : { + "default" : 0, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "page_size", + "in" : "query", + "description" : "Pagination page size", + "required" : false, + "schema" : { + "default" : 10, + "maximum" : 20, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + }, { + "name" : "sort_method", + "in" : "query", + "description" : "Result sort method", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortMethod" + }, { + "nullable" : true + } ] + } + }, { + "name" : "sort_order", + "in" : "query", + "description" : "Result sort order", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortOrder" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "query", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "200" : { + "description" : "search results matching criteria", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Release" + } + } + } + } + }, + "400" : { + "description" : "bad input parameter" + } + } + } + }, + "/v3/assets/latest/{feature_version}/{jvm_impl}" : { + "get" : { + "tags" : [ "Assets" ], + "summary" : "Returns list of latest assets for the given feature version and jvm impl", + "operationId" : "getLatestAssets", + "parameters" : [ { + "name" : "feature_version", + "in" : "path", + "description" : "\n

\n Feature release version you wish to download. Feature versions are whole numbers e.g. 8,11,16,17,18.\n

\n

\n Available Feature versions can be obtained from \n https://api.adoptium.net/v3/info/available_releases\n

\n", + "required" : true, + "schema" : { + "default" : 8, + "type" : "integer" + } + }, { + "name" : "jvm_impl", + "in" : "path", + "description" : "JVM Implementation", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/JvmImpl" + } + }, { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "query", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/BinaryAssetView" + } + } + } + } + } + } + } + }, + "/v3/assets/release_name/{vendor}/{release_name}" : { + "get" : { + "tags" : [ "Assets" ], + "summary" : "Returns release information", + "description" : "List of releases with the given release name", + "operationId" : "getReleaseInfo", + "parameters" : [ { + "name" : "release_name", + "in" : "path", + "description" : "Name of the release i.e ", + "required" : true, + "schema" : { + "type" : "string", + "nullable" : true + } + }, { + "name" : "vendor", + "in" : "path", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + }, { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "query", + "description" : "Heap Size", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "query", + "description" : "JVM Implementation", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "200" : { + "description" : "Release with the given vendor and name", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Release" + } + } + } + }, + "400" : { + "description" : "bad input parameter" + }, + "404" : { + "description" : "no releases match the request" + }, + "500" : { + "description" : "multiple releases match the request" + } + } + } + }, + "/v3/assets/version/{version}" : { + "get" : { + "tags" : [ "Assets" ], + "summary" : "Returns release information about the specified version.", + "description" : "List of information about builds that match the current query ", + "operationId" : "searchReleasesByVersion", + "parameters" : [ { + "name" : "version", + "in" : "path", + "description" : "\nJava version range (maven style) of versions to include.\n\ne.g:\n* `11.0.4.1+11.1`\n* `[1.0,2.0)`\n* `(,1.0]`\n\nDetails of maven version ranges can be found at\n \n", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "query", + "description" : "Heap Size", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "query", + "description" : "JVM Implementation", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "lts", + "in" : "query", + "description" : "Include only LTS releases", + "required" : false, + "schema" : { + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "page", + "in" : "query", + "description" : "Pagination page number", + "required" : false, + "schema" : { + "default" : 0, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "page_size", + "in" : "query", + "description" : "Pagination page size", + "required" : false, + "schema" : { + "default" : 10, + "maximum" : 20, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + }, { + "name" : "release_type", + "in" : "query", + "description" : "\n

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

\n", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "semver", + "in" : "query", + "description" : "Indicates that any version arguments provided in this request were Adoptium semantic versions", + "required" : false, + "schema" : { + "default" : false, + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "sort_method", + "in" : "query", + "description" : "Result sort method", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortMethod" + }, { + "nullable" : true + } ] + } + }, { + "name" : "sort_order", + "in" : "query", + "description" : "Result sort order", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortOrder" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "query", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "200" : { + "description" : "search results matching criteria", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Release" + } + } + } + } + }, + "400" : { + "description" : "bad input parameter" + } + } + } + }, + "/v3/binary/latest/{feature_version}/{release_type}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}" : { + "get" : { + "tags" : [ "Binary" ], + "summary" : "Redirects to the binary that matches your current query", + "description" : "Redirects to the binary that matches your current query", + "operationId" : "getBinary", + "parameters" : [ { + "name" : "arch", + "in" : "path", + "description" : "Architecture", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "feature_version", + "in" : "path", + "description" : "\n

\n Feature release version you wish to download. Feature versions are whole numbers e.g. 8,11,16,17,18.\n

\n

\n Available Feature versions can be obtained from \n https://api.adoptium.net/v3/info/available_releases\n

\n", + "required" : true, + "schema" : { + "default" : 8, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "heap_size", + "in" : "path", + "description" : "Heap Size", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "path", + "description" : "Image Type", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "path", + "description" : "JVM Implementation", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "os", + "in" : "path", + "description" : "Operating System", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "release_type", + "in" : "path", + "description" : "\n

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

\n", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "path", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "307" : { + "description" : "link to binary that matches your current query" + }, + "400" : { + "description" : "bad input parameter" + }, + "404" : { + "description" : "No matching binary found" + } + } + } + }, + "/v3/binary/version/{release_name}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}" : { + "get" : { + "tags" : [ "Binary" ], + "summary" : "Redirects to the binary that matches your current query", + "description" : "Redirects to the binary that matches your current query", + "operationId" : "getBinaryByVersion", + "parameters" : [ { + "name" : "arch", + "in" : "path", + "description" : "Architecture", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "path", + "description" : "Heap Size", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "path", + "description" : "Image Type", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "path", + "description" : "JVM Implementation", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "os", + "in" : "path", + "description" : "Operating System", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "release_name", + "in" : "path", + "description" : "\n

\n Name of the release as displayed in github or https://adoptopenjdk.net/ e.g\n jdk-11.0.4+11, jdk8u172-b00-201807161800.\n

\n

\n A list of release names can be obtained from \n https://api.adoptium.net/v3/info/release_names\n

\n", + "required" : true, + "schema" : { + "default" : "jdk-11.0.6+10", + "type" : "string", + "nullable" : true + } + }, { + "name" : "vendor", + "in" : "path", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : true, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + } ], + "responses" : { + "307" : { + "description" : "link to binary that matches your current query" + }, + "400" : { + "description" : "bad input parameter" + }, + "404" : { + "description" : "No matching binary found" + } + } + } + }, + "/v3/info/available_releases" : { + "get" : { + "tags" : [ "Release Info" ], + "summary" : "Returns information about available releases", + "operationId" : "getAvailableReleases", + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ReleaseInfo" + } + } + } + } + } + } + }, + "/v3/info/release_names" : { + "get" : { + "tags" : [ "Release Info" ], + "summary" : "Returns a list of all release names", + "operationId" : "getReleaseNames", + "parameters" : [ { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "query", + "description" : "Heap Size", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "query", + "description" : "JVM Implementation", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "lts", + "in" : "query", + "description" : "Include only LTS releases", + "required" : false, + "schema" : { + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "page", + "in" : "query", + "description" : "Pagination page number", + "required" : false, + "schema" : { + "default" : 0, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "page_size", + "in" : "query", + "description" : "Pagination page size", + "required" : false, + "schema" : { + "default" : 10, + "maximum" : 20, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + }, { + "name" : "release_type", + "in" : "query", + "description" : "\n

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

\n", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "semver", + "in" : "query", + "description" : "Indicates that any version arguments provided in this request were Adoptium semantic versions", + "required" : false, + "schema" : { + "default" : false, + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "sort_method", + "in" : "query", + "description" : "Result sort method", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortMethod" + }, { + "nullable" : true + } ] + } + }, { + "name" : "sort_order", + "in" : "query", + "description" : "Result sort order", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortOrder" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "query", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + }, { + "name" : "version", + "in" : "query", + "description" : "\nJava version range (maven style) of versions to include.\n\ne.g:\n* `11.0.4.1+11.1`\n* `[1.0,2.0)`\n* `(,1.0]`\n\nDetails of maven version ranges can be found at\n \n", + "required" : false, + "schema" : { + "type" : "string", + "nullable" : true + } + } ], + "responses" : { + "200" : { + "description" : "A list of all release names", + "content" : { + "application/json" : { + "schema" : { + "required" : [ "releases" ], + "type" : "object", + "properties" : { + "releases" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + } + } + } + } + } + } + }, + "/v3/info/release_versions" : { + "get" : { + "tags" : [ "Release Info" ], + "summary" : "Returns a list of all release versions", + "operationId" : "getReleaseVersions", + "parameters" : [ { + "name" : "architecture", + "in" : "query", + "description" : "Architecture", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Architecture" + }, { + "nullable" : true + } ] + } + }, { + "name" : "c_lib", + "in" : "query", + "description" : "C Lib type, typically would imply image_type has been set to staticlibs", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + }, { + "nullable" : true + } ] + } + }, { + "name" : "heap_size", + "in" : "query", + "description" : "Heap Size", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/HeapSize" + }, { + "nullable" : true + } ] + } + }, { + "name" : "image_type", + "in" : "query", + "description" : "Image Type", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ImageType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "jvm_impl", + "in" : "query", + "description" : "JVM Implementation", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/JvmImpl" + }, { + "nullable" : true + } ] + } + }, { + "name" : "lts", + "in" : "query", + "description" : "Include only LTS releases", + "required" : false, + "schema" : { + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "os", + "in" : "query", + "description" : "Operating System", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/OperatingSystem" + }, { + "nullable" : true + } ] + } + }, { + "name" : "page", + "in" : "query", + "description" : "Pagination page number", + "required" : false, + "schema" : { + "default" : 0, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "page_size", + "in" : "query", + "description" : "Pagination page size", + "required" : false, + "schema" : { + "default" : 10, + "maximum" : 50, + "type" : "integer", + "nullable" : true + } + }, { + "name" : "project", + "in" : "query", + "description" : "Project", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/project" + }, { + "nullable" : true + } ] + } + }, { + "name" : "release_type", + "in" : "query", + "description" : "\n

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

\n", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseType" + }, { + "nullable" : true + } ] + } + }, { + "name" : "semver", + "in" : "query", + "description" : "Indicates that any version arguments provided in this request were Adoptium semantic versions", + "required" : false, + "schema" : { + "default" : false, + "type" : "boolean", + "nullable" : true + } + }, { + "name" : "sort_method", + "in" : "query", + "description" : "Result sort method", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortMethod" + }, { + "nullable" : true + } ] + } + }, { + "name" : "sort_order", + "in" : "query", + "description" : "Result sort order", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/SortOrder" + }, { + "nullable" : true + } ] + } + }, { + "name" : "vendor", + "in" : "query", + "description" : "

Vendor of the binary. This is the organisation that produced the binary package.

", + "required" : false, + "schema" : { + "allOf" : [ { + "$ref" : "#/components/schemas/Vendor" + }, { + "nullable" : true + } ] + } + }, { + "name" : "version", + "in" : "query", + "description" : "\nJava version range (maven style) of versions to include.\n\ne.g:\n* `11.0.4.1+11.1`\n* `[1.0,2.0)`\n* `(,1.0]`\n\nDetails of maven version ranges can be found at\n \n", + "required" : false, + "schema" : { + "type" : "string", + "nullable" : true + } + } ], + "responses" : { + "200" : { + "description" : "A list of all release versions", + "content" : { + "application/json" : { + "schema" : { + "required" : [ "versions" ], + "type" : "object", + "properties" : { + "versions" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/VersionData" + } + } + } + } + } + } + } + } + } + }, + "/v3/version/{version}" : { + "get" : { + "tags" : [ "Version" ], + "summary" : "Parses a java version string", + "description" : "Parses a java version string and returns that data in a structured format", + "operationId" : "parseVersion", + "parameters" : [ { + "name" : "version", + "in" : "path", + "description" : "Version", + "required" : true, + "schema" : { + "type" : "string", + "nullable" : true + } + } ], + "responses" : { + "400" : { + "description" : "bad input parameter" + } + } + } + } + }, + "components" : { + "schemas" : { + "AdoptiumJvmImpl" : { + "enum" : [ "hotspot" ], + "type" : "string" + }, + "AdoptiumVendor" : { + "default" : "eclipse", + "enum" : [ "eclipse" ], + "type" : "string", + "example" : "eclipse" + }, + "Architecture" : { + "enum" : [ "x64", "x86", "x32", "ppc64", "ppc64le", "s390x", "aarch64", "arm", "sparcv9", "riscv64" ], + "type" : "string" + }, + "Binary" : { + "required" : [ "os", "architecture", "image_type", "jvm_impl", "heap_size", "updated_at", "project" ], + "type" : "object", + "properties" : { + "os" : { + "$ref" : "#/components/schemas/OperatingSystem" + }, + "architecture" : { + "$ref" : "#/components/schemas/Architecture" + }, + "image_type" : { + "$ref" : "#/components/schemas/ImageType" + }, + "c_lib" : { + "type" : "string", + "allOf" : [ { + "$ref" : "#/components/schemas/CLib" + } ], + "nullable" : true + }, + "jvm_impl" : { + "$ref" : "#/components/schemas/JvmImpl" + }, + "package" : { + "$ref" : "#/components/schemas/Package" + }, + "installer" : { + "$ref" : "#/components/schemas/Installer" + }, + "heap_size" : { + "$ref" : "#/components/schemas/HeapSize" + }, + "download_count" : { + "format" : "int64", + "type" : "integer", + "example" : 3899 + }, + "updated_at" : { + "$ref" : "#/components/schemas/DateTime" + }, + "scm_ref" : { + "type" : "string", + "example" : "dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93", + "nullable" : true + }, + "project" : { + "$ref" : "#/components/schemas/project" + } + } + }, + "BinaryAssetView" : { + "required" : [ "release_name", "release_link" ], + "type" : "object", + "properties" : { + "binary" : { + "$ref" : "#/components/schemas/Binary" + }, + "release_name" : { + "type" : "string", + "example" : "jdk8u162-b12_openj9-0.8.0" + }, + "release_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/tag/jdk8u162-b12_openj9-0.8.0" + }, + "vendor" : { + "$ref" : "#/components/schemas/Vendor" + }, + "version" : { + "$ref" : "#/components/schemas/VersionData" + } + } + }, + "CLib" : { + "enum" : [ "musl", "glibc" ], + "type" : "string" + }, + "DateTime" : { + "description" : "

Date/time. When only a date is given the time is set to the end of the given day.

  • 2020-01-21
  • 2020-01-21T10:15:30
  • 20200121
  • 2020-12-21T10:15:30Z
  • 2020-12-21+01:00

", + "type" : "string" + }, + "HeapSize" : { + "enum" : [ "normal", "large" ], + "type" : "string", + "example" : "normal" + }, + "ImageType" : { + "enum" : [ "jdk", "jre", "testimage", "debugimage", "staticlibs", "sources", "sbom" ], + "type" : "string", + "example" : "jdk" + }, + "Installer" : { + "required" : [ "name", "link" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi" + }, + "link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-binaries/ga/download/jdk8u212-b04/OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi" + }, + "size" : { + "format" : "int64", + "type" : "integer", + "example" : 82573385 + }, + "checksum" : { + "type" : "string", + "example" : "dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93", + "nullable" : true + }, + "checksum_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.sha256.txt", + "nullable" : true + }, + "signature_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk11-upstream-binaries/releases/download/jdk-11.0.5%2B10/OpenJDK11U-jdk_x64_linux_11.0.5_10.tar.gz.sign", + "nullable" : true + }, + "download_count" : { + "format" : "int64", + "type" : "integer", + "example" : 2 + }, + "metadata_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.json", + "nullable" : true + } + } + }, + "JvmImpl" : { + "$ref" : "#/components/schemas/AdoptiumJvmImpl" + }, + "OperatingSystem" : { + "enum" : [ "linux", "windows", "mac", "solaris", "aix", "alpine-linux" ], + "type" : "string" + }, + "Package" : { + "required" : [ "name", "link" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi" + }, + "link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-binaries/ga/download/jdk8u212-b04/OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi" + }, + "size" : { + "format" : "int64", + "type" : "integer", + "example" : 82573385 + }, + "checksum" : { + "type" : "string", + "example" : "dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93", + "nullable" : true + }, + "checksum_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.sha256.txt", + "nullable" : true + }, + "signature_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk11-upstream-binaries/releases/download/jdk-11.0.5%2B10/OpenJDK11U-jdk_x64_linux_11.0.5_10.tar.gz.sign", + "nullable" : true + }, + "download_count" : { + "format" : "int64", + "type" : "integer", + "example" : 2 + }, + "metadata_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.json", + "nullable" : true + } + } + }, + "Release" : { + "required" : [ "id", "release_link", "release_name", "timestamp", "updated_at", "binaries", "release_type", "vendor", "version_data" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "example" : "VXNlci0xMA==" + }, + "release_link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/tag/jdk8u162-b12_openj9-0.8.0" + }, + "release_name" : { + "type" : "string", + "example" : "jdk8u162-b12_openj9-0.8.0" + }, + "timestamp" : { + "$ref" : "#/components/schemas/DateTime" + }, + "updated_at" : { + "$ref" : "#/components/schemas/DateTime" + }, + "binaries" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Binary" + } + }, + "download_count" : { + "format" : "int64", + "type" : "integer", + "example" : 7128 + }, + "release_type" : { + "$ref" : "#/components/schemas/ReleaseType" + }, + "vendor" : { + "$ref" : "#/components/schemas/Vendor" + }, + "version_data" : { + "$ref" : "#/components/schemas/VersionData" + }, + "source" : { + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/SourcePackage" + } ], + "nullable" : true + }, + "release_notes" : { + "type" : "object", + "allOf" : [ { + "$ref" : "#/components/schemas/ReleaseNotesPackage" + } ], + "nullable" : true + }, + "aqavit_results_link" : { + "type" : "string", + "example" : "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/AQAvitTapFiles.tar.gz", + "nullable" : true + } + } + }, + "ReleaseInfo" : { + "required" : [ "available_releases", "available_lts_releases" ], + "type" : "object", + "properties" : { + "available_releases" : { + "description" : "The versions for which adopt have produced a ga release", + "type" : "array", + "items" : { + "format" : "int32", + "type" : "integer" + }, + "example" : [ 8, 9, 10, 11, 12, 13, 14 ] + }, + "available_lts_releases" : { + "description" : "The LTS versions for which adopt have produced a ga release", + "type" : "array", + "items" : { + "format" : "int32", + "type" : "integer" + }, + "example" : [ 8, 11 ] + }, + "most_recent_lts" : { + "format" : "int32", + "description" : "The highest LTS version for which adopt have produced a ga release", + "type" : "integer", + "example" : 11 + }, + "most_recent_feature_release" : { + "format" : "int32", + "description" : "The highest version (LTS or not) for which adopt have produced a ga release", + "type" : "integer", + "example" : 13 + }, + "most_recent_feature_version" : { + "format" : "int32", + "description" : "The highest version (LTS or not) for which we have produced a build, this may be a version that has not yet produced a ga release", + "type" : "integer", + "example" : 15 + }, + "tip_version" : { + "format" : "int32", + "description" : "The version that is currently in development at openjdk", + "type" : "integer", + "example" : 15 + } + } + }, + "ReleaseNote" : { + "required" : [ "id" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "title" : { + "type" : "string", + "nullable" : true + }, + "priority" : { + "type" : "string", + "nullable" : true + }, + "component" : { + "type" : "string", + "nullable" : true + }, + "subcomponent" : { + "type" : "string", + "nullable" : true + }, + "link" : { + "type" : "string", + "nullable" : true + }, + "type" : { + "type" : "string", + "nullable" : true + }, + "backportOf" : { + "type" : "string", + "nullable" : true + } + } + }, + "ReleaseNotesPackage" : { + "required" : [ "name", "link" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "OpenJDK8U-sources_8u232b09.tar.gz" + }, + "link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u232-b09/OpenJDK8U-sources_8u232b09.tar.gz" + }, + "size" : { + "format" : "int64", + "type" : "integer", + "example" : 82573385 + } + } + }, + "ReleaseType" : { + "default" : "ga", + "enum" : [ "ga", "ea" ], + "type" : "string" + }, + "SortMethod" : { + "description" : "DEFAULT sort order is by: version, then date, then name, then id. DATE sorts by date, then version, then name, then id.", + "default" : "DEFAULT", + "enum" : [ "DEFAULT", "DATE" ], + "type" : "string" + }, + "SortOrder" : { + "default" : "DESC", + "enum" : [ "ASC", "DESC" ], + "type" : "string" + }, + "SourcePackage" : { + "required" : [ "name", "link" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "example" : "OpenJDK8U-sources_8u232b09.tar.gz" + }, + "link" : { + "type" : "string", + "example" : "https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u232-b09/OpenJDK8U-sources_8u232b09.tar.gz" + }, + "size" : { + "format" : "int64", + "type" : "integer", + "example" : 82573385 + } + } + }, + "StatsSource" : { + "default" : "all", + "enum" : [ "github", "dockerhub", "all" ], + "type" : "string", + "example" : "all" + }, + "Vendor" : { + "$ref" : "#/components/schemas/AdoptiumVendor" + }, + "VersionData" : { + "required" : [ "semver", "openjdk_version" ], + "type" : "object", + "properties" : { + "major" : { + "format" : "int32", + "type" : "integer" + }, + "minor" : { + "format" : "int32", + "type" : "integer" + }, + "security" : { + "format" : "int32", + "type" : "integer" + }, + "patch" : { + "format" : "int32", + "type" : "integer", + "nullable" : true + }, + "pre" : { + "type" : "string", + "nullable" : true + }, + "adopt_build_number" : { + "format" : "int32", + "type" : "integer", + "nullable" : true + }, + "semver" : { + "type" : "string", + "example" : "11.0.0+28" + }, + "openjdk_version" : { + "type" : "string", + "example" : "11.0.4+10-201907081820" + }, + "build" : { + "format" : "int32", + "type" : "integer" + }, + "optional" : { + "type" : "string", + "nullable" : true + } + } + }, + "project" : { + "description" : "Project", + "default" : "jdk", + "enum" : [ "jdk", "valhalla", "metropolis", "jfr", "shenandoah" ], + "type" : "string" + } + } + } +} \ No newline at end of file diff --git a/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.yaml b/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.yaml new file mode 100644 index 0000000000..a19b7d24ee --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/resources/adoptium.yaml @@ -0,0 +1,1381 @@ +--- +openapi: 3.0.3 +info: + title: v3 + description: "" + version: 3.0.0 +servers: + - url: https://api.adoptium.net + - url: https://staging-api.adoptium.net +tags: + - name: Assets + - name: Binary + - name: Checksum + - name: Installer + - name: Release Info + - name: Signature + - name: Version +paths: + /v3/assets/feature_releases/{feature_version}/{release_type}: + get: + tags: + - Assets + summary: Returns release information + description: List of information about builds that match the current query + operationId: searchReleases + parameters: + - name: feature_version + in: path + description: "\n

\n Feature release version you wish to download. Feature\ + \ versions are whole numbers e.g. 8,11,16,17,18.\n

\n

\n\ + \ Available Feature versions can be obtained from \n https://api.adoptium.net/v3/info/available_releases\n

\n" + required: true + schema: + default: 8 + type: integer + nullable: true + - name: release_type + in: path + description: |2 + +

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

+ required: true + schema: + allOf: + - $ref: '#/components/schemas/ReleaseType' + - nullable: true + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: before + in: query + description: '

Return binaries whose updated_at is before the given date/time. + When a date is given the match is inclusive of the given day.

  • 2020-01-21
  • +
  • 2020-01-21T10:15:30
  • 20200121
  • 2020-12-21T10:15:30Z
  • +
  • 2020-12-21+01:00

' + required: false + schema: + allOf: + - $ref: '#/components/schemas/DateTime' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: heap_size + in: query + description: Heap Size + required: false + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: query + description: JVM Implementation + required: false + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: page + in: query + description: Pagination page number + required: false + schema: + default: 0 + type: integer + nullable: true + - name: page_size + in: query + description: Pagination page size + required: false + schema: + default: 10 + maximum: 20 + type: integer + nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + - name: sort_method + in: query + description: Result sort method + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortMethod' + - nullable: true + - name: sort_order + in: query + description: Result sort order + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + - nullable: true + - name: vendor + in: query + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + responses: + "200": + description: search results matching criteria + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Release' + "400": + description: bad input parameter + /v3/assets/latest/{feature_version}/{jvm_impl}: + get: + tags: + - Assets + summary: Returns list of latest assets for the given feature version and jvm + impl + operationId: getLatestAssets + parameters: + - name: feature_version + in: path + description: "\n

\n Feature release version you wish to download. Feature\ + \ versions are whole numbers e.g. 8,11,16,17,18.\n

\n

\n\ + \ Available Feature versions can be obtained from \n https://api.adoptium.net/v3/info/available_releases\n

\n" + required: true + schema: + default: 8 + type: integer + - name: jvm_impl + in: path + description: JVM Implementation + required: true + schema: + $ref: '#/components/schemas/JvmImpl' + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: vendor + in: query + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BinaryAssetView' + /v3/assets/release_name/{vendor}/{release_name}: + get: + tags: + - Assets + summary: Returns release information + description: List of releases with the given release name + operationId: getReleaseInfo + parameters: + - name: release_name + in: path + description: 'Name of the release i.e ' + required: true + schema: + type: string + nullable: true + - name: vendor + in: path + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: true + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: heap_size + in: query + description: Heap Size + required: false + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: query + description: JVM Implementation + required: false + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + responses: + "200": + description: Release with the given vendor and name + content: + application/json: + schema: + $ref: '#/components/schemas/Release' + "400": + description: bad input parameter + "404": + description: no releases match the request + "500": + description: multiple releases match the request + /v3/assets/version/{version}: + get: + tags: + - Assets + summary: Returns release information about the specified version. + description: 'List of information about builds that match the current query ' + operationId: searchReleasesByVersion + parameters: + - name: version + in: path + description: |2 + + Java version range (maven style) of versions to include. + + e.g: + * `11.0.4.1+11.1` + * `[1.0,2.0)` + * `(,1.0]` + + Details of maven version ranges can be found at + + required: true + schema: + type: string + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: heap_size + in: query + description: Heap Size + required: false + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: query + description: JVM Implementation + required: false + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: lts + in: query + description: Include only LTS releases + required: false + schema: + type: boolean + nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: page + in: query + description: Pagination page number + required: false + schema: + default: 0 + type: integer + nullable: true + - name: page_size + in: query + description: Pagination page size + required: false + schema: + default: 10 + maximum: 20 + type: integer + nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + - name: release_type + in: query + description: |2 + +

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/ReleaseType' + - nullable: true + - name: semver + in: query + description: Indicates that any version arguments provided in this request + were Adoptium semantic versions + required: false + schema: + default: false + type: boolean + nullable: true + - name: sort_method + in: query + description: Result sort method + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortMethod' + - nullable: true + - name: sort_order + in: query + description: Result sort order + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + - nullable: true + - name: vendor + in: query + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + responses: + "200": + description: search results matching criteria + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Release' + "400": + description: bad input parameter + /v3/checksum/version/{release_name}/{os}/{arch}/{image_type}/{jvm_impl}/{heap_size}/{vendor}: + get: + tags: + - Checksum + summary: Redirects to the checksum of the release that matches your current + query + description: Redirects to the checksum of the release that matches your current + query + operationId: getChecksumByVersion + parameters: + - name: arch + in: path + description: Architecture + required: true + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: heap_size + in: path + description: Heap Size + required: true + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: path + description: Image Type + required: true + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: path + description: JVM Implementation + required: true + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: os + in: path + description: Operating System + required: true + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: release_name + in: path + description: "\n

\n Name of the release as displayed in github or https://adoptopenjdk.net/ e.g\n\ + \ jdk-11.0.4+11, jdk8u172-b00-201807161800.\n

\n

\n\ + \ A list of release names can be obtained from \n https://api.adoptium.net/v3/info/release_names\n

\n" + required: true + schema: + default: jdk-11.0.6+10 + type: string + nullable: true + - name: vendor + in: path + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: true + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + responses: + "307": + description: link to checksum of the release that matches your current query + "400": + description: bad input parameter + "404": + description: No matching signature found + /v3/info/available_releases: + get: + tags: + - Release Info + summary: Returns information about available releases + operationId: getAvailableReleases + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ReleaseInfo' + /v3/info/release_names: + get: + tags: + - Release Info + summary: Returns a list of all release names + operationId: getReleaseNames + parameters: + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: heap_size + in: query + description: Heap Size + required: false + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: query + description: JVM Implementation + required: false + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: lts + in: query + description: Include only LTS releases + required: false + schema: + type: boolean + nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: page + in: query + description: Pagination page number + required: false + schema: + default: 0 + type: integer + nullable: true + - name: page_size + in: query + description: Pagination page size + required: false + schema: + default: 10 + maximum: 20 + type: integer + nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + - name: release_type + in: query + description: |2 + +

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/ReleaseType' + - nullable: true + - name: semver + in: query + description: Indicates that any version arguments provided in this request + were Adoptium semantic versions + required: false + schema: + default: false + type: boolean + nullable: true + - name: sort_method + in: query + description: Result sort method + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortMethod' + - nullable: true + - name: sort_order + in: query + description: Result sort order + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + - nullable: true + - name: vendor + in: query + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + - name: version + in: query + description: |2 + + Java version range (maven style) of versions to include. + + e.g: + * `11.0.4.1+11.1` + * `[1.0,2.0)` + * `(,1.0]` + + Details of maven version ranges can be found at + + required: false + schema: + type: string + nullable: true + responses: + "200": + description: A list of all release names + content: + application/json: + schema: + required: + - releases + type: object + properties: + releases: + type: array + items: + type: string + /v3/info/release_versions: + get: + tags: + - Release Info + summary: Returns a list of all release versions + operationId: getReleaseVersions + parameters: + - name: architecture + in: query + description: Architecture + required: false + schema: + allOf: + - $ref: '#/components/schemas/Architecture' + - nullable: true + - name: c_lib + in: query + description: "C Lib type, typically would imply image_type has been set to\ + \ staticlibs" + required: false + schema: + allOf: + - $ref: '#/components/schemas/CLib' + - nullable: true + - name: heap_size + in: query + description: Heap Size + required: false + schema: + allOf: + - $ref: '#/components/schemas/HeapSize' + - nullable: true + - name: image_type + in: query + description: Image Type + required: false + schema: + allOf: + - $ref: '#/components/schemas/ImageType' + - nullable: true + - name: jvm_impl + in: query + description: JVM Implementation + required: false + schema: + allOf: + - $ref: '#/components/schemas/JvmImpl' + - nullable: true + - name: lts + in: query + description: Include only LTS releases + required: false + schema: + type: boolean + nullable: true + - name: os + in: query + description: Operating System + required: false + schema: + allOf: + - $ref: '#/components/schemas/OperatingSystem' + - nullable: true + - name: page + in: query + description: Pagination page number + required: false + schema: + default: 0 + type: integer + nullable: true + - name: page_size + in: query + description: Pagination page size + required: false + schema: + default: 10 + maximum: 50 + type: integer + nullable: true + - name: project + in: query + description: Project + required: false + schema: + allOf: + - $ref: '#/components/schemas/project' + - nullable: true + - name: release_type + in: query + description: |2 + +

Type of release. Either a release version, known as General Availability(ga) or an Early Access(ea)

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/ReleaseType' + - nullable: true + - name: semver + in: query + description: Indicates that any version arguments provided in this request + were Adoptium semantic versions + required: false + schema: + default: false + type: boolean + nullable: true + - name: sort_method + in: query + description: Result sort method + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortMethod' + - nullable: true + - name: sort_order + in: query + description: Result sort order + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + - nullable: true + - name: vendor + in: query + description:

Vendor of the binary. This is the organisation that produced + the binary package.

+ required: false + schema: + allOf: + - $ref: '#/components/schemas/Vendor' + - nullable: true + - name: version + in: query + description: |2 + + Java version range (maven style) of versions to include. + + e.g: + * `11.0.4.1+11.1` + * `[1.0,2.0)` + * `(,1.0]` + + Details of maven version ranges can be found at + + required: false + schema: + type: string + nullable: true + responses: + "200": + description: A list of all release versions + content: + application/json: + schema: + required: + - versions + type: object + properties: + versions: + type: array + items: + $ref: '#/components/schemas/VersionData' + /v3/version/{version}: + get: + tags: + - Version + summary: Parses a java version string + description: Parses a java version string and returns that data in a structured + format + operationId: parseVersion + parameters: + - name: version + in: path + description: Version + required: true + schema: + type: string + nullable: true + responses: + "400": + description: bad input parameter +components: + schemas: + AdoptiumJvmImpl: + enum: + - hotspot + type: string + AdoptiumVendor: + default: eclipse + enum: + - eclipse + type: string + example: eclipse + Architecture: + enum: + - x64 + - x86 + - x32 + - ppc64 + - ppc64le + - s390x + - aarch64 + - arm + - sparcv9 + - riscv64 + type: string + Binary: + required: + - os + - architecture + - image_type + - jvm_impl + - heap_size + - updated_at + - project + type: object + properties: + os: + $ref: '#/components/schemas/OperatingSystem' + architecture: + $ref: '#/components/schemas/Architecture' + image_type: + $ref: '#/components/schemas/ImageType' + c_lib: + type: string + allOf: + - $ref: '#/components/schemas/CLib' + nullable: true + jvm_impl: + $ref: '#/components/schemas/JvmImpl' + package: + $ref: '#/components/schemas/Package' + installer: + $ref: '#/components/schemas/Installer' + heap_size: + $ref: '#/components/schemas/HeapSize' + download_count: + format: int64 + type: integer + example: 3899 + updated_at: + $ref: '#/components/schemas/DateTime' + scm_ref: + type: string + example: dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93 + nullable: true + project: + $ref: '#/components/schemas/project' + BinaryAssetView: + required: + - release_name + - release_link + type: object + properties: + binary: + $ref: '#/components/schemas/Binary' + release_name: + type: string + example: jdk8u162-b12_openj9-0.8.0 + release_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/tag/jdk8u162-b12_openj9-0.8.0 + vendor: + $ref: '#/components/schemas/Vendor' + version: + $ref: '#/components/schemas/VersionData' + CLib: + enum: + - musl + - glibc + type: string + DateTime: + description:

Date/time. When only a date is given the time is set to the + end of the given day.

  • 2020-01-21
  • 2020-01-21T10:15:30
  • +
  • 20200121
  • 2020-12-21T10:15:30Z
  • 2020-12-21+01:00
  • +

+ type: string + HeapSize: + enum: + - normal + - large + type: string + example: normal + ImageType: + enum: + - jdk + - jre + - testimage + - debugimage + - staticlibs + - sources + - sbom + type: string + example: jdk + Installer: + required: + - name + - link + type: object + properties: + name: + type: string + example: OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi + link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-binaries/ga/download/jdk8u212-b04/OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi + size: + format: int64 + type: integer + example: 82573385 + checksum: + type: string + example: dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93 + nullable: true + checksum_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.sha256.txt + nullable: true + signature_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk11-upstream-binaries/releases/download/jdk-11.0.5%2B10/OpenJDK11U-jdk_x64_linux_11.0.5_10.tar.gz.sign + nullable: true + download_count: + format: int64 + type: integer + example: 2 + metadata_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.json + nullable: true + JvmImpl: + $ref: '#/components/schemas/AdoptiumJvmImpl' + OperatingSystem: + enum: + - linux + - windows + - mac + - solaris + - aix + - alpine-linux + type: string + Package: + required: + - name + - link + type: object + properties: + name: + type: string + example: OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi + link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-binaries/ga/download/jdk8u212-b04/OpenJDK8U-jre_x86-32_windows_hotspot_8u212b04.msi + size: + format: int64 + type: integer + example: 82573385 + checksum: + type: string + example: dd28d6d2cde2b931caf94ac2422a2ad082ea62f0beee3bf7057317c53093de93 + nullable: true + checksum_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.sha256.txt + nullable: true + signature_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk11-upstream-binaries/releases/download/jdk-11.0.5%2B10/OpenJDK11U-jdk_x64_linux_11.0.5_10.tar.gz.sign + nullable: true + download_count: + format: int64 + type: integer + example: 2 + metadata_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/download/jdk8u162-b12_openj9-0.8.0/OpenJDK8-OPENJ9_x64_Linux_jdk8u162-b12_openj9-0.8.0.tar.gz.json + nullable: true + Release: + required: + - id + - release_link + - release_name + - timestamp + - updated_at + - binaries + - release_type + - vendor + - version_data + type: object + properties: + id: + type: string + example: VXNlci0xMA== + release_link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-openj9-releases/ga/tag/jdk8u162-b12_openj9-0.8.0 + release_name: + type: string + example: jdk8u162-b12_openj9-0.8.0 + timestamp: + $ref: '#/components/schemas/DateTime' + updated_at: + $ref: '#/components/schemas/DateTime' + binaries: + type: array + items: + $ref: '#/components/schemas/Binary' + download_count: + format: int64 + type: integer + example: 7128 + release_type: + $ref: '#/components/schemas/ReleaseType' + vendor: + $ref: '#/components/schemas/Vendor' + version_data: + $ref: '#/components/schemas/VersionData' + source: + type: object + allOf: + - $ref: '#/components/schemas/SourcePackage' + nullable: true + release_notes: + type: object + allOf: + - $ref: '#/components/schemas/ReleaseNotesPackage' + nullable: true + aqavit_results_link: + type: string + example: https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/AQAvitTapFiles.tar.gz + nullable: true + ReleaseInfo: + required: + - available_releases + - available_lts_releases + type: object + properties: + available_releases: + description: The versions for which adopt have produced a ga release + type: array + items: + format: int32 + type: integer + example: + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + available_lts_releases: + description: The LTS versions for which adopt have produced a ga release + type: array + items: + format: int32 + type: integer + example: + - 8 + - 11 + most_recent_lts: + format: int32 + description: The highest LTS version for which adopt have produced a ga + release + type: integer + example: 11 + most_recent_feature_release: + format: int32 + description: The highest version (LTS or not) for which adopt have produced + a ga release + type: integer + example: 13 + most_recent_feature_version: + format: int32 + description: "The highest version (LTS or not) for which we have produced\ + \ a build, this may be a version that has not yet produced a ga release" + type: integer + example: 15 + tip_version: + format: int32 + description: The version that is currently in development at openjdk + type: integer + example: 15 + ReleaseNote: + required: + - id + type: object + properties: + id: + type: string + title: + type: string + nullable: true + priority: + type: string + nullable: true + component: + type: string + nullable: true + subcomponent: + type: string + nullable: true + link: + type: string + nullable: true + type: + type: string + nullable: true + backportOf: + type: string + nullable: true + ReleaseNotesPackage: + required: + - name + - link + type: object + properties: + name: + type: string + example: OpenJDK8U-sources_8u232b09.tar.gz + link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u232-b09/OpenJDK8U-sources_8u232b09.tar.gz + size: + format: int64 + type: integer + example: 82573385 + ReleaseType: + default: ga + enum: + - ga + - ea + type: string + SortMethod: + description: "DEFAULT sort order is by: version, then date, then name, then\ + \ id. DATE sorts by date, then version, then name, then id." + default: DEFAULT + enum: + - DEFAULT + - DATE + type: string + SortOrder: + default: DESC + enum: + - ASC + - DESC + type: string + SourcePackage: + required: + - name + - link + type: object + properties: + name: + type: string + example: OpenJDK8U-sources_8u232b09.tar.gz + link: + type: string + example: https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u232-b09/OpenJDK8U-sources_8u232b09.tar.gz + size: + format: int64 + type: integer + example: 82573385 + StatsSource: + default: all + enum: + - github + - dockerhub + - all + type: string + example: all + Vendor: + $ref: '#/components/schemas/AdoptiumVendor' + VersionData: + required: + - semver + - openjdk_version + type: object + properties: + major: + format: int32 + type: integer + minor: + format: int32 + type: integer + security: + format: int32 + type: integer + patch: + format: int32 + type: integer + nullable: true + pre: + type: string + nullable: true + adopt_build_number: + format: int32 + type: integer + nullable: true + semver: + type: string + example: 11.0.0+28 + openjdk_version: + type: string + example: 11.0.4+10-201907081820 + build: + format: int32 + type: integer + optional: + type: string + nullable: true + project: + description: Project + default: jdk + enum: + - jdk + - valhalla + - metropolis + - jfr + - shenandoah + type: string diff --git a/java/samples/semantickernel-openapi-plugin/src/test/resources/petstore.yaml b/java/samples/semantickernel-openapi-plugin/src/test/resources/petstore.yaml new file mode 100644 index 0000000000..29a3432ce9 --- /dev/null +++ b/java/samples/semantickernel-openapi-plugin/src/test/resources/petstore.yaml @@ -0,0 +1,822 @@ +# Sample from: Swagger - https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml +# Under Apache 2.0 License +--- +openapi: 3.0.2 +servers: + - url: /v3 +info: + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about + Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! + You can now help us improve the API whether it's by making changes to the definition itself or to the code. + That way, with time, we can improve the API in general, and expose some of the new features in OAS3. + + Some useful links: + - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) + - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + version: 1.0.20-SNAPSHOT + title: Swagger Petstore - OpenAPI 3.0 + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: 'http://swagger.io' + - name: store + description: Access to Petstore orders + externalDocs: + description: Find out more about our store + url: 'http://swagger.io' + - name: user + description: Operations about user +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: Add a new pet to the store + operationId: addPet + responses: + '200': + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + description: Create a new pet in the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + put: + tags: + - pet + summary: Update an existing pet + description: Update an existing pet by Id + operationId: updatePet + responses: + '200': + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + description: Update an existent pet in the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: false + explode: true + schema: + type: string + enum: + - available + - pending + - sold + default: available + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: >- + Multiple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: false + explode: true + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + - petstore_auth: + - 'write:pets' + - 'read:pets' + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + - name: name + in: query + description: Name of pet that needs to be updated + schema: + type: string + - name: status + in: query + description: Status of pet that needs to be updated + schema: + type: string + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + parameters: + - name: api_key + in: header + description: '' + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + - name: additionalMetadata + in: query + description: Additional Metadata + required: false + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + x-swagger-router-controller: OrderController + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: Place a new order in the store + operationId: placeOrder + x-swagger-router-controller: OrderController + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + '405': + description: Invalid input + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Order' + '/store/order/{orderId}': + get: + tags: + - store + summary: Find purchase order by ID + x-swagger-router-controller: OrderController + description: >- + For valid response try integer IDs with value <= 5 or > 10. Other values + will generate exceptions. + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of order that needs to be fetched + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + x-swagger-router-controller: OrderController + description: >- + For valid response try integer IDs with value < 1000. Anything above + 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + responses: + default: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + description: Created user object + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: 'Creates list of users with given input array' + x-swagger-router-controller: UserController + operationId: createUsersWithListInput + responses: + '200': + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + default: + description: successful operation + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: false + schema: + type: string + - name: password + in: query + description: The password for login in clear text + required: false + schema: + type: string + responses: + '200': + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + parameters: [] + responses: + default: + description: successful operation + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing. ' + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Update user + x-swagger-router-controller: UserController + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that needs to be updated + required: true + schema: + type: string + responses: + default: + description: successful operation + requestBody: + description: Update an existent user in the store + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found +externalDocs: + description: Find out more about Swagger + url: 'http://swagger.io' +components: + schemas: + Order: + x-swagger-router-model: io.swagger.petstore.model.Order + properties: + id: + type: integer + format: int64 + example: 10 + petId: + type: integer + format: int64 + example: 198772 + quantity: + type: integer + format: int32 + example: 7 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + example: approved + complete: + type: boolean + xml: + name: order + type: object + Customer: + properties: + id: + type: integer + format: int64 + example: 100000 + username: + type: string + example: fehguy + address: + type: array + items: + $ref: '#/components/schemas/Address' + xml: + wrapped: true + name: addresses + xml: + name: customer + type: object + Address: + properties: + street: + type: string + example: 437 Lytton + city: + type: string + example: Palo Alto + state: + type: string + example: CA + zip: + type: string + example: 94301 + xml: + name: address + type: object + Category: + x-swagger-router-model: io.swagger.petstore.model.Category + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + xml: + name: category + type: object + User: + x-swagger-router-model: io.swagger.petstore.model.User + properties: + id: + type: integer + format: int64 + example: 10 + username: + type: string + example: theUser + firstName: + type: string + example: John + lastName: + type: string + example: James + email: + type: string + example: john@email.com + password: + type: string + example: 12345 + phone: + type: string + example: 12345 + userStatus: + type: integer + format: int32 + example: 1 + description: User Status + xml: + name: user + type: object + Tag: + x-swagger-router-model: io.swagger.petstore.model.Tag + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: tag + type: object + Pet: + x-swagger-router-model: io.swagger.petstore.model.Pet + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: '#/components/schemas/Category' + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/Tag' + xml: + name: tag + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + type: object + ApiResponse: + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + xml: + name: '##default' + type: object + requestBodies: + Pet: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + UserArray: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + description: List of user object + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: 'http://localhost:8123/oauth/authorize' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header diff --git a/java/semantickernel-api/pom.xml b/java/semantickernel-api/pom.xml index e4b05dd90e..7f2e21e157 100644 --- a/java/semantickernel-api/pom.xml +++ b/java/semantickernel-api/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java index 77ab211c2e..f8f4b939ec 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java @@ -61,6 +61,17 @@ public interface KernelHook extends Predicate { + + @Override + default boolean test(KernelHookEvent arguments) { + return PreToolCallEvent.class.isAssignableFrom(arguments.getClass()); + } + } + /** * A hook that accepts {@link PromptRenderedEvent} */ diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java index dd0669d0c6..f690fe26ba 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java @@ -5,6 +5,7 @@ import com.microsoft.semantickernel.hooks.KernelHook.FunctionInvokedHook; import com.microsoft.semantickernel.hooks.KernelHook.FunctionInvokingHook; import com.microsoft.semantickernel.hooks.KernelHook.PostChatCompletionHook; import com.microsoft.semantickernel.hooks.KernelHook.PreChatCompletionHook; +import com.microsoft.semantickernel.hooks.KernelHook.PreToolCallHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderedHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderingHook; import java.util.Collections; @@ -102,6 +103,17 @@ public class KernelHooks { return addHook((PreChatCompletionHook) function::apply); } + /** + * Add a {@link PreToolCallHook} to the collection of hooks. + * + * @param function the function to add + * @return the key of the hook in the collection + */ + public String addPreToolCallHook( + Function function) { + return addHook((PreToolCallHook) function::apply); + } + /** * Add a {@link PostChatCompletionEvent} to the collection of hooks. * diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java new file mode 100644 index 0000000000..848bcb4332 --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PreToolCallEvent.java @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.hooks; + +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; +import com.microsoft.semantickernel.semanticfunctions.KernelFunction; +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +/** + * Represents a KernelHookEvent that is raised before a tool call is invoked. + */ +public class PreToolCallEvent implements KernelHookEvent { + + private final ContextVariableTypes contextVariableTypes; + private final String functionName; + private final KernelFunctionArguments arguments; + private final KernelFunction function; + + /** + * Creates a new instance of the {@link PreToolCallEvent} class. + * + * @param functionName the name of the function + * @param arguments the arguments + * @param function the function + * @param contextVariableTypes the context variable types + */ + @SuppressFBWarnings("EI_EXPOSE_REP2") + public PreToolCallEvent( + String functionName, + KernelFunctionArguments arguments, + KernelFunction function, + ContextVariableTypes contextVariableTypes) { + this.functionName = functionName; + this.arguments = arguments; + this.function = function; + this.contextVariableTypes = contextVariableTypes; + } + + @SuppressFBWarnings("EI_EXPOSE_REP") + public KernelFunctionArguments getArguments() { + return arguments; + } + + @SuppressFBWarnings("EI_EXPOSE_REP2") + public KernelFunction getFunction() { + return function; + } +} diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java index bfeaa62bfd..2ee18f73d7 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/orchestration/FunctionInvocation.java @@ -13,6 +13,7 @@ import com.microsoft.semantickernel.hooks.KernelHooks.UnmodifiableKernelHooks; import com.microsoft.semantickernel.semanticfunctions.KernelFunction; import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.NoSuchElementException; import java.util.function.BiConsumer; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -109,6 +110,13 @@ public class FunctionInvocation extends Mono> { null, new InvocationContext(context)) .handle(convertToType(variableType)) + .onErrorResume(e -> { + if (e instanceof NoSuchElementException) { + return Mono.empty(); + } else { + return Mono.error(e); + } + }) .subscribe(coreSubscriber); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java index a36f4f8628..0f5a968ea9 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/plugin/KernelPluginFactory.java @@ -89,7 +89,27 @@ public class KernelPluginFactory { * @return The new plugin. */ public static KernelPlugin createFromObject(Object target, String pluginName) { - List> methods = Arrays.stream(target.getClass().getMethods()) + Class clazz = target.getClass(); + return KernelPluginFactory.createFromObject(clazz, target, pluginName); + } + + /** + * Creates a plugin that wraps the specified target object. Methods decorated with + * {@code {@literal @}DefineSKFunction} will be included in the plugin. + * + * @param clazz The class to be wrapped. + * @param target The instance of the class to be wrapped. + * @param pluginName Name of the plugin for function collection and prompt templates. If the + * value is {@code null}, a plugin name is derived from the type of the + * target. + * @return The new plugin. + */ + public static KernelPlugin createFromObject(Class clazz, Object target, String pluginName) { + if (!clazz.isInstance(target)) { + throw new SKException("Target object is not an instance of the provided class"); + } + + List> methods = Arrays.stream(clazz.getMethods()) .filter(method -> method.isAnnotationPresent(DefineKernelFunction.class)) .map(method -> { DefineKernelFunction annotation = method.getAnnotation(DefineKernelFunction.class); @@ -119,7 +139,15 @@ public class KernelPluginFactory { }).collect(ArrayList::new, (list, it) -> list.add(it), (a, b) -> a.addAll(b)); - return createFromFunctions(pluginName, methods); + KernelPlugin plugin = createFromFunctions(pluginName, methods); + + if (plugin.getFunctions().isEmpty()) { + LOGGER.warn( + "No functions found in class {}. This can be caused by DI frameworks that create proxies, or modules that are not making your methods visible. " + + "Try using: KernelPluginFactory.createFromObject(Class clazz, Object target, String pluginName).", + clazz.getName()); + } + return plugin; } private static Class getReturnType(DefineKernelFunction annotation, Method method) { @@ -270,11 +298,15 @@ public class KernelPluginFactory { KernelFunctionParameter annotation = parameter.getAnnotation( KernelFunctionParameter.class); + List enumValues = KernelFunctionFromMethod + .getEnumOptions(annotation.type()); + return InputVariable.build( annotation.name(), annotation.type(), annotation.description(), annotation.defaultValue(), + enumValues, annotation.required()); }).collect(Collectors.toList()); } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java index e0c6a1a62f..a819b2ee27 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/InputVariable.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.microsoft.semantickernel.plugin.KernelPluginFactory; +import java.util.Collections; +import java.util.List; import java.util.Objects; import javax.annotation.Nullable; @@ -20,6 +22,8 @@ public class InputVariable { private final String defaultValue; private final boolean isRequired; private final String type; + @Nullable + private final List enumValues; /** * Creates a new instance of {@link InputVariable}. @@ -29,6 +33,7 @@ public class InputVariable { * @param description the description of the input variable * @param defaultValue the default value of the input variable * @param isRequired whether the input variable is required + * @param enumValues the enum values of the input variable */ @JsonCreator public InputVariable( @@ -36,7 +41,8 @@ public class InputVariable { @JsonProperty("type") String type, @JsonProperty("description") @Nullable String description, @JsonProperty("default") @Nullable String defaultValue, - @JsonAlias({ "required", "is_required" }) @JsonProperty("is_required") boolean isRequired) { + @JsonAlias({ "required", "is_required" }) @JsonProperty("is_required") boolean isRequired, + @JsonProperty("enum") @Nullable List enumValues) { this.name = name; this.description = description; this.defaultValue = defaultValue; @@ -45,6 +51,11 @@ public class InputVariable { type = "java.lang.String"; } this.type = type; + if (enumValues != null) { + this.enumValues = Collections.unmodifiableList(enumValues); + } else { + this.enumValues = null; + } } /** @@ -58,6 +69,7 @@ public class InputVariable { this.description = null; this.defaultValue = null; this.isRequired = true; + this.enumValues = null; } /** @@ -75,8 +87,10 @@ public class InputVariable { Class type, @Nullable String description, @Nullable String defaultValue, + @Nullable List enumValues, boolean required) { - return new InputVariable(name, type.getName(), description, defaultValue, required); + return new InputVariable(name, type.getName(), description, defaultValue, required, + enumValues); } /** @@ -166,4 +180,13 @@ public class InputVariable { return isRequired == other.isRequired; } + /** + * Gets the possible enum values of the input variable. + * + * @return the possible enum values of the input variable + */ + @Nullable + public List getEnumValues() { + return enumValues; + } } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java index 9f30e2fae0..96e012fa05 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethod.java @@ -315,6 +315,13 @@ public class KernelFunctionFromMethod extends KernelFunction { e); } }) + .flatMap(it -> { + if (it == null) { + return Mono.empty(); + } else { + return Mono.just(it); + } + }) .subscribeOn(Schedulers.boundedElastic())); } @@ -557,14 +564,29 @@ public class KernelFunctionFromMethod extends KernelFunction { type = annotation.type(); } + List enumValues = getEnumOptions(type); + return InputVariable.build( name, type, description, defaultValue, + enumValues, isRequired); } + public static @Nullable List getEnumOptions(Class type) { + List enumValues = null; + if (type.isEnum()) { + enumValues = Arrays.stream(type.getEnumConstants()) + .map(it -> { + return it.toString(); + }) + .collect(Collectors.toList()); + } + return enumValues; + } + /** * A builder for {@link KernelFunction}. * diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java index 8ff2a5eb28..a163407897 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionFromMethodTest.java @@ -59,7 +59,7 @@ public class KernelFunctionFromMethodTest { String functionName = "concat"; String description = "concatenate two strings"; List parameters = Collections.singletonList(InputVariable.build( - "string1", String.class, "first string to concatenate", null, true)); + "string1", String.class, "first string to concatenate", null, null, true)); OutputVariable returnParameter = new OutputVariable<>( "concatenated strings", String.class); KernelFunction result = KernelFunctionFromMethod.create(method, "test", pluginName, @@ -81,7 +81,7 @@ public class KernelFunctionFromMethodTest { String description = "concatenate two strings"; List parameters = Collections .singletonList(new InputVariable("string1", - "java.lang.String", "first string to concatenate", null, true)); + "java.lang.String", "first string to concatenate", null, true, null)); OutputVariable returnParameter = new OutputVariable<>("java.lang.String", "concatenated strings"); KernelFunction result = new KernelFunctionFromPrompt.Builder() diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionYamlTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionYamlTest.java index 8f2219a2f8..513f4fd727 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionYamlTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/KernelFunctionYamlTest.java @@ -55,13 +55,15 @@ public class KernelFunctionYamlTest { "java.lang.String", "The topic of the story.", null, - true), + true, + null), new InputVariable( "length", "java.lang.String", "The number of sentences in the story.", null, - true))) + true, + null))) .withOutputVariable("The generated story.", "java.lang.String") .withExecutionSettings(new HashMap() { { diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfigTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfigTest.java index 98f1e1b37f..d4141dcf89 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfigTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateConfigTest.java @@ -19,7 +19,7 @@ public class PromptTemplateConfigTest { String template = "A template for testing {{plugin.function input}}"; List inputVariables = Arrays.asList( new InputVariable("input", "java.lang.String", "a description", "default value", - true)); + true, null)); OutputVariable outputVariable = new OutputVariable("java.lang.String", "a description"); @@ -61,7 +61,7 @@ public class PromptTemplateConfigTest { String templateFormat = "semantic-kernel"; List inputVariables = Arrays.asList( new InputVariable("input", "java.lang.String", "a description", "default value", - true)); + true, null)); OutputVariable outputVariable = new OutputVariable("java.lang.String", "a description"); HashMap executionSettings = new HashMap() { @@ -100,7 +100,7 @@ public class PromptTemplateConfigTest { String templateFormat = "semantic-kernel"; List inputVariables = Arrays.asList( new InputVariable("input", "java.lang.String", "a description", "default value", - true)); + true, null)); OutputVariable outputVariable = new OutputVariable("java.lang.String", "a description"); HashMap executionSettings = new HashMap() { diff --git a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactoryTest.java b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactoryTest.java index 6297e8fda2..f386084e4d 100644 --- a/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactoryTest.java +++ b/java/semantickernel-api/src/test/java/com/microsoft/semantickernel/semanticfunctions/PromptTemplateFactoryTest.java @@ -41,7 +41,7 @@ public class PromptTemplateFactoryTest { Method method = PromptTemplateFactoryTest.class.getMethod("function", String.class); List parameters = Collections.singletonList( new InputVariable("input", String.class.getName(), "the input", "borked", - true)); + true, null)); OutputVariable returnParameter = new OutputVariable<>( "the output", String.class); KernelFunction function = KernelFunctionFromMethod.create( @@ -72,7 +72,7 @@ public class PromptTemplateFactoryTest { List inputVariables = Arrays.asList( new InputVariable("input", "java.lang.String", "a description", "input from config", - true)); + true, null)); OutputVariable outputVariable = new OutputVariable("java.lang.String", "a description"); HashMap executionSettings = new HashMap() { diff --git a/java/semantickernel-bom/pom.xml b/java/semantickernel-bom/pom.xml index 9df589b64d..8eaf0e6593 100644 --- a/java/semantickernel-bom/pom.xml +++ b/java/semantickernel-bom/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 com.microsoft.semantic-kernel