|
| 1 | +// Copyright (c) Microsoft. All rights reserved. |
| 2 | +package com.microsoft.semantickernel.samples.syntaxexamples.java; |
| 3 | + |
| 4 | +import com.azure.ai.openai.OpenAIAsyncClient; |
| 5 | +import com.azure.ai.openai.OpenAIClientBuilder; |
| 6 | +import com.azure.core.credential.AzureKeyCredential; |
| 7 | +import com.azure.core.credential.KeyCredential; |
| 8 | +import com.fasterxml.jackson.annotation.JsonCreator; |
| 9 | +import com.fasterxml.jackson.annotation.JsonProperty; |
| 10 | +import com.microsoft.semantickernel.Kernel; |
| 11 | +import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; |
| 12 | +import com.microsoft.semantickernel.contextvariables.ContextVariableTypeConverter; |
| 13 | +import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; |
| 14 | +import com.microsoft.semantickernel.contextvariables.converters.ContextVariableJacksonConverter; |
| 15 | +import com.microsoft.semantickernel.exceptions.ConfigurationException; |
| 16 | +import com.microsoft.semantickernel.semanticfunctions.KernelFunctionArguments; |
| 17 | +import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; |
| 18 | +import java.io.IOException; |
| 19 | +import java.util.Arrays; |
| 20 | +import java.util.Map; |
| 21 | +import java.util.function.Function; |
| 22 | +import java.util.stream.Collectors; |
| 23 | + |
| 24 | +public class CustomTypes_Example { |
| 25 | + |
| 26 | + private static final String CLIENT_KEY = System.getenv("CLIENT_KEY"); |
| 27 | + private static final String AZURE_CLIENT_KEY = System.getenv("AZURE_CLIENT_KEY"); |
| 28 | + |
| 29 | + // Only required if AZURE_CLIENT_KEY is set |
| 30 | + private static final String CLIENT_ENDPOINT = System.getenv("CLIENT_ENDPOINT"); |
| 31 | + private static final String MODEL_ID = System.getenv() |
| 32 | + .getOrDefault("MODEL_ID", "gpt-35-turbo-2"); |
| 33 | + |
| 34 | + public static void main(String[] args) throws ConfigurationException, IOException { |
| 35 | + |
| 36 | + OpenAIAsyncClient client; |
| 37 | + |
| 38 | + if (AZURE_CLIENT_KEY != null) { |
| 39 | + client = new OpenAIClientBuilder() |
| 40 | + .credential(new AzureKeyCredential(AZURE_CLIENT_KEY)) |
| 41 | + .endpoint(CLIENT_ENDPOINT) |
| 42 | + .buildAsyncClient(); |
| 43 | + } else { |
| 44 | + client = new OpenAIClientBuilder() |
| 45 | + .credential(new KeyCredential(CLIENT_KEY)) |
| 46 | + .buildAsyncClient(); |
| 47 | + } |
| 48 | + |
| 49 | + ChatCompletionService chatCompletionService = OpenAIChatCompletion.builder() |
| 50 | + .withOpenAIAsyncClient(client) |
| 51 | + .withModelId(MODEL_ID) |
| 52 | + .build(); |
| 53 | + |
| 54 | + exampleBuildingCustomConverter(chatCompletionService); |
| 55 | + exampleUsingJackson(chatCompletionService); |
| 56 | + exampleUsingGlobalTypes(chatCompletionService); |
| 57 | + } |
| 58 | + |
| 59 | + public record Pet(String name, int age, String species) { |
| 60 | + |
| 61 | + @JsonCreator |
| 62 | + public Pet( |
| 63 | + @JsonProperty("name") String name, |
| 64 | + @JsonProperty("age") int age, |
| 65 | + @JsonProperty("species") String species) { |
| 66 | + this.name = name; |
| 67 | + this.age = age; |
| 68 | + this.species = species; |
| 69 | + } |
| 70 | + |
| 71 | + @Override |
| 72 | + public String toString() { |
| 73 | + return name + " " + species + " " + age; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + private static void exampleBuildingCustomConverter( |
| 78 | + ChatCompletionService chatCompletionService) { |
| 79 | + Pet sandy = new Pet("Sandy", 3, "Dog"); |
| 80 | + |
| 81 | + Kernel kernel = Kernel.builder() |
| 82 | + .withAIService(ChatCompletionService.class, chatCompletionService) |
| 83 | + .build(); |
| 84 | + |
| 85 | + // Format: |
| 86 | + // name: Sandy |
| 87 | + // age: 3 |
| 88 | + // species: Dog |
| 89 | + |
| 90 | + // Custom serializer |
| 91 | + Function<Pet, String> petToString = pet -> "name: " + pet.name() + "\n" + |
| 92 | + "age: " + pet.age() + "\n" + |
| 93 | + "species: " + pet.species() + "\n"; |
| 94 | + |
| 95 | + // Custom deserializer |
| 96 | + Function<String, Pet> stringToPet = prompt -> { |
| 97 | + Map<String, String> properties = Arrays.stream(prompt.split("\n")) |
| 98 | + .collect(Collectors.toMap( |
| 99 | + line -> line.split(":")[0].trim(), |
| 100 | + line -> line.split(":")[1].trim())); |
| 101 | + |
| 102 | + return new Pet( |
| 103 | + properties.get("name"), |
| 104 | + Integer.parseInt(properties.get("age")), |
| 105 | + properties.get("species")); |
| 106 | + }; |
| 107 | + |
| 108 | + // create custom converter |
| 109 | + ContextVariableTypeConverter<Pet> typeConverter = ContextVariableTypeConverter.builder( |
| 110 | + Pet.class) |
| 111 | + .toPromptString(petToString) |
| 112 | + .fromPromptString(stringToPet) |
| 113 | + .build(); |
| 114 | + |
| 115 | + Pet updated = kernel.invokePromptAsync( |
| 116 | + "Change Sandy's name to Daisy:\n{{$Sandy}}", |
| 117 | + KernelFunctionArguments.builder() |
| 118 | + .withVariable("Sandy", sandy, typeConverter) |
| 119 | + .build()) |
| 120 | + .withTypeConverter(typeConverter) |
| 121 | + .withResultType(Pet.class) |
| 122 | + .block() |
| 123 | + .getResult(); |
| 124 | + |
| 125 | + System.out.println("Sandy's updated record: " + updated); |
| 126 | + } |
| 127 | + |
| 128 | + public static void exampleUsingJackson(ChatCompletionService chatCompletionService) { |
| 129 | + Pet sandy = new Pet("Sandy", 3, "Dog"); |
| 130 | + |
| 131 | + Kernel kernel = Kernel.builder() |
| 132 | + .withAIService(ChatCompletionService.class, chatCompletionService) |
| 133 | + .build(); |
| 134 | + |
| 135 | + // Create a converter that defaults to using jackson for serialization |
| 136 | + ContextVariableTypeConverter<Pet> typeConverter = ContextVariableJacksonConverter.create( |
| 137 | + Pet.class); |
| 138 | + |
| 139 | + // Invoke the prompt with the custom converter |
| 140 | + Pet updated = kernel.invokePromptAsync( |
| 141 | + "Increase Sandy's age by a year:\n{{$Sandy}}", |
| 142 | + KernelFunctionArguments.builder() |
| 143 | + .withVariable("Sandy", sandy, typeConverter) |
| 144 | + .build()) |
| 145 | + .withTypeConverter(typeConverter) |
| 146 | + .withResultType(Pet.class) |
| 147 | + .block() |
| 148 | + .getResult(); |
| 149 | + |
| 150 | + System.out.println("Sandy's updated record: " + updated); |
| 151 | + } |
| 152 | + |
| 153 | + public static void exampleUsingGlobalTypes(ChatCompletionService chatCompletionService) { |
| 154 | + Pet sandy = new Pet("Sandy", 3, "Dog"); |
| 155 | + |
| 156 | + Kernel kernel = Kernel.builder() |
| 157 | + .withAIService(ChatCompletionService.class, chatCompletionService) |
| 158 | + .build(); |
| 159 | + |
| 160 | + // Create a converter that defaults to using jackson for serialization |
| 161 | + ContextVariableTypeConverter<Pet> typeConverter = ContextVariableJacksonConverter.create( |
| 162 | + Pet.class); |
| 163 | + |
| 164 | + // Add converter to global types |
| 165 | + ContextVariableTypes.addGlobalConverter(typeConverter); |
| 166 | + |
| 167 | + // No need to explicitly tell the invocation how to convert the type |
| 168 | + Pet updated = kernel.invokePromptAsync( |
| 169 | + "Sandy's is actually a cat correct this:\n{{$Sandy}}", |
| 170 | + KernelFunctionArguments.builder() |
| 171 | + .withVariable("Sandy", sandy) |
| 172 | + .build()) |
| 173 | + .withResultType(Pet.class) |
| 174 | + .block() |
| 175 | + .getResult(); |
| 176 | + |
| 177 | + System.out.println("Sandy's updated record: " + updated); |
| 178 | + } |
| 179 | + |
| 180 | +} |
0 commit comments