diff --git a/api/src/main/java/com/theokanning/openai/function/FunctionDefinition.java b/api/src/main/java/com/theokanning/openai/function/FunctionDefinition.java index 5294599..ac30097 100644 --- a/api/src/main/java/com/theokanning/openai/function/FunctionDefinition.java +++ b/api/src/main/java/com/theokanning/openai/function/FunctionDefinition.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle; import lombok.Getter; import lombok.NonNull; @@ -40,6 +41,7 @@ private FunctionDefinition() { /** * The parameters the functions accepts.Choose between this parameter and {@link #parametersDefinitionClass} + * This parameter requires you to implement the serialization/deserialization logic of the JSON schema yourself **/ private Object parametersDefinition; @@ -94,9 +96,6 @@ public FunctionDefinition build() { if (name == null) { throw new IllegalArgumentException("name can't be null"); } - if (parametersDefinitionClass == null && parametersDefinition == null) { - throw new IllegalArgumentException("parametersDefinitionClass and parametersDefinition can't be null at the same time,please set one of them"); - } if (parametersDefinition != null && parametersDefinitionClass != null) { throw new IllegalArgumentException("parametersDefinitionClass and parametersDefinition can't be set at the same time,please set one of them"); } diff --git a/api/src/main/java/com/theokanning/openai/function/FunctionParametersSerializer.java b/api/src/main/java/com/theokanning/openai/function/FunctionParametersSerializer.java index 9caa962..4bddc0a 100644 --- a/api/src/main/java/com/theokanning/openai/function/FunctionParametersSerializer.java +++ b/api/src/main/java/com/theokanning/openai/function/FunctionParametersSerializer.java @@ -1,13 +1,18 @@ package com.theokanning.openai.function; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.kjetland.jackson.jsonSchema.JsonSchemaConfig; +import com.kjetland.jackson.jsonSchema.JsonSchemaDraft; import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator; import java.io.IOException; +import java.util.Collections; +import java.util.Optional; public class FunctionParametersSerializer extends JsonSerializer { @@ -22,7 +27,11 @@ public void serialize(FunctionDefinition value, JsonGenerator gen, SerializerPro gen.writeStringField("description", value.getDescription()); if (value.getParametersDefinitionClass() != null) { gen.writeFieldName("parameters"); - gen.writeRawValue(mapper.writeValueAsString(jsonSchemaGenerator.generateJsonSchema(value.getParametersDefinitionClass()))); + ObjectNode parameterSchema = (ObjectNode) jsonSchemaGenerator.generateJsonSchema(value.getParametersDefinitionClass()); + parameterSchema.remove("$schema"); + parameterSchema.remove("title"); + parameterSchema.remove("additionalProperties"); + gen.writeRawValue(mapper.writeValueAsString(parameterSchema)); } else { gen.writeFieldName("parameters"); gen.writeRawValue(mapper.writeValueAsString(value.getParametersDefinition())); diff --git a/example/src/main/java/example/ComplexFunctionDefineUseExample.java b/example/src/main/java/example/ComplexFunctionDefineUseExample.java new file mode 100644 index 0000000..e624f07 --- /dev/null +++ b/example/src/main/java/example/ComplexFunctionDefineUseExample.java @@ -0,0 +1,141 @@ +package example; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDefault; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaDescription; +import com.theokanning.openai.completion.chat.*; +import com.theokanning.openai.function.FunctionDefinition; +import com.theokanning.openai.function.FunctionExecutorManager; +import com.theokanning.openai.service.OpenAiService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author LiangTao + * @date 2024年05月13 11:28 + **/ +public class ComplexFunctionDefineUseExample { + public static class ComponentSearch { + + @JsonSchemaDescription("The logical relationship of query conditions") + @JsonProperty(required = true) + public MergeType mergeType; + + @JsonSchemaDescription("query criteria list collection") + @JsonProperty(required = true) + public List queryConditionList; + + + public static class QueryCondition { + @JsonSchemaDescription("the name of the queried attribute") + @JsonSchemaDefault("family and type") + @JsonProperty(required = true) + public String propertyName; + + @JsonSchemaDescription("comparator") + @JsonProperty(required = true) + public Operate operate; + + @JsonSchemaDescription("attribute value") + @JsonProperty(required = true) + public String value; + + + public enum Operate { + EQUAL, + NOT_EQUAL, + GREATER_THAN, + LESS_THAN, + GREATER_THAN_OR_EQUAL, + LESS_THAN_OR_EQUAL, + CONTAIN, + NOT_CONTAIN; + } + } + + public enum MergeType { + AND, + OR; + } + + } + + public static void main(String[] args) { + // complexFunctionExample(); + noParameterFunctionExample(); + } + + private static void noParameterFunctionExample() { + OpenAiService openAiService = new OpenAiService(); + FunctionDefinition fd = FunctionDefinition.builder() + .name("get_current_time") + .description("Get the current time") + .executor(c -> System.currentTimeMillis()) + .build(); + FunctionExecutorManager functionExecutorManager = new FunctionExecutorManager(); + functionExecutorManager.addFunctionDefinition(fd); + + List messages = new ArrayList<>(); + messages.add(new SystemMessage("You are responsible for assisting users in solving problems, and you can complete tasks based on the different abilities provided to you\n" + + "Your answer is limited to the following abilities. You can choose the appropriate ability to answer the user's question based on their input\n" + + "# your abilities:\n" + + "- Get the current time:`get_current_time`\n" + + "\n")); + + messages.add(new UserMessage("What is the current time?")); + + ChatCompletionResult chatCompletion = openAiService.createChatCompletion(ChatCompletionRequest.builder() + .model("gpt-3.5-turbo") + .messages(messages) + .tools(Arrays.asList(new ChatTool(fd))) + .temperature(1D) + .build()); + ChatCompletionChoice choice = chatCompletion.getChoices().get(0); + System.out.println(choice.getFinishReason()); + List toolCalls = choice.getMessage().getToolCalls(); + ChatToolCall chatToolCall = toolCalls.get(0); + ToolMessage toolMessage = functionExecutorManager.executeAndConvertToChatMessage(chatToolCall.getFunction().getName(), chatToolCall.getFunction().getArguments(), chatToolCall.getId()); + System.out.println(toolMessage); + + } + + private static void complexFunctionExample() { + OpenAiService openAiService = new OpenAiService(); + FunctionDefinition fd = FunctionDefinition.builder() + .name("get_component_set") + .description("Obtain the component set information of the model based on the query conditions provided by the user") + .parametersDefinitionByClass(ComponentSearch.class) + .executor(c -> c) + .build(); + FunctionExecutorManager functionExecutorManager = new FunctionExecutorManager(); + functionExecutorManager.addFunctionDefinition(fd); + + List messages = new ArrayList<>(); + messages.add(new SystemMessage( + "You are responsible for assisting users in solving problems, and you can complete tasks based on the different abilities provided to you\n" + + "Your answer is limited to the following abilities. You can choose the appropriate ability to answer the user's question based on their input\n" + + "# your abilities:\n" + + "- Based on the component query language provided by the user, analyze and extract the query conditions to obtain the component set information of the model:`get_component_set`\n" + + "\n")); + messages.add(new UserMessage("When calling component queries, you should select the most suitable attribute name class from this collection for component queries," + + "This is a collection of component attribute names for the model: [\"A1\",\"b\",\"B1\",\"b1\",\"b2\",\"h\",\"H1\",\"h1\",\"h2\",\"h3\",\"h4\",\"name\",\",\"seismicGrade\",\"visibleLightTransmittance\",\"width\",\"widthCoefficient\"," + + "\"familyAndType]")); + + messages.add(new UserMessage("Search for components with a width of 12 and a name of hot return water pipe")); + + ChatCompletionResult chatCompletion = openAiService.createChatCompletion(ChatCompletionRequest.builder() + .model("gpt-3.5-turbo") + .messages(messages) + .tools(Arrays.asList(new ChatTool(fd))) + .temperature(1D) + .build()); + ChatCompletionChoice choice = chatCompletion.getChoices().get(0); + System.out.println(choice.getFinishReason()); + List toolCalls = choice.getMessage().getToolCalls(); + ChatToolCall chatToolCall = toolCalls.get(0); + ToolMessage toolMessage = functionExecutorManager.executeAndConvertToChatMessage(chatToolCall.getFunction().getName(), chatToolCall.getFunction().getArguments(), chatToolCall.getId()); + System.out.println(toolMessage); + } +} diff --git a/example/src/main/java/example/TikTokensExample.java b/example/src/main/java/example/TikTokensExample.java index 803a23c..0d82886 100644 --- a/example/src/main/java/example/TikTokensExample.java +++ b/example/src/main/java/example/TikTokensExample.java @@ -13,7 +13,7 @@ public static void main(String... args) { List messages = new ArrayList<>(); messages.add(new SystemMessage("Hello OpenAI 1.")); messages.add(new SystemMessage("Hello OpenAI 2. ")); - messages.add(new UserMessage(Arrays.asList(new ImageContent("text", "", new ImageUrl("dddd"))))); + messages.add(new UserMessage(Arrays.asList(new ImageContent("textContent")))); int tokens_1 = TikTokensUtil.tokens(TikTokensUtil.ModelEnum.GPT_3_5_TURBO.getName(), messages); int tokens_2 = TikTokensUtil.tokens(TikTokensUtil.ModelEnum.GPT_3_5_TURBO.getName(), "Hello OpenAI 1."); int tokens_3 = TikTokensUtil.tokens(TikTokensUtil.ModelEnum.GPT_4_TURBO.getName(), messages);