Skip to content

com_cloudurable_jai_model_text_completion_chat

Rick Hightower edited this page Jul 16, 2023 · 1 revision

com.cloudurable.jai.model.text.completion.chat

class diagram

Message

The Message class represents a message in a chat system.

ChatRequest

The ChatRequest class extends from CommonCompletionRequest and represents a chat request in a chat system. It is used to generate a response from the model based on a provided chat conversation. The class has various properties that can be set to control the behavior of the chat completion, such as the model to be used, the list of messages forming the conversation, the role of the message author, the content of the message, and additional options like temperature, top_p, completionCount, stream, stop, maxTokens, presencePenalty, frequencyPenalty, and logitBias. By configuring these properties, the class allows for customizable chat completions in a chat system.

ChatChoice

ChatChoice is a class that extends the Choice class. It represents a chat choice and contains information such as an index, message, and a finish reason.

ChatResponse

The ChatResponse class is a subclass of TextResponse that represents a chat response. It includes the ID, object, creation time, chat choices, and usage statistics.

ChatResponseDeserializer

The ChatResponseDeserializer class provides deserialization functionality for the Chat Response for the Open AI API. It takes a string representation of the JSON response and converts it into a ChatResponse object, which contains the deserialized information.

The class has several helper methods, including deserializeUsage(), deserializeChoice(), deserializeMessage(), deserializeRole(), and deserializeFinishReason(), which are used to deserialize specific parts of the JSON response.

Overall, the ChatResponseDeserializer class provides a convenient way to deserialize the OpenAI API's chat response JSON into a structured Java object, making it easier to work with and access the information contained in the response.

public static ChatResponse deserialize(final String jsonBody)

public static ChatResponse deserialize(final String jsonBody) {
    //ystem.out.println(jsonBody);
    final JsonParser parser = JsonParserBuilder.builder().build();
    final ObjectNode objectNode = parser.parse(jsonBody).asObject();
    final String id = objectNode.getString("id");
    final String object = objectNode.getString("object");
    final int createdTime = objectNode.getInt("created");
    final Usage usage = DeserializerUtils.deserializeUsage(objectNode.getObjectNode("usage"));
    final ArrayNode choicesNode = objectNode.getArrayNode("choices");
    final List<ChatChoice> chatChoices = choicesNode.mapObjectNode(ChatResponseDeserializer::deserializeChoice);
    return ChatResponse.builder().choices(chatChoices).id(id).usage(usage).object(object).created(Instant.ofEpochSecond(createdTime)).build();
}

The method deserialize in class com.cloudurable.jai.model.text.completion.chat.ChatResponseDeserializer is responsible for deserializing a JSON string into a ChatResponse object.

Here is a step-by-step description of what the method does:

  1. It takes a JSON string jsonBody as input.

  2. It creates a JsonParser object using JsonParserBuilder.builder().build().

  3. It parses the jsonBody using the parse method of the JsonParser, and then casts the result as an ObjectNode.

  4. It retrieves the value of the "id" field from the objectNode and assigns it to the variable id.

  5. It retrieves the value of the "object" field from the objectNode and assigns it to the variable object.

  6. It retrieves the value of the "created" field from the objectNode and assigns it to the variable createdTime.

  7. It calls the static method deserializeUsage from the DeserializerUtils class and passes the ObjectNode representing the "usage" field of objectNode as an argument. The returned Usage object is assigned to the variable usage.

  8. It retrieves an ArrayNode representing the "choices" field from the objectNode.

  9. It maps each element in the choicesNode using the mapObjectNode method, passing the deserializeChoice method from the same class as the mapping function. The result is a list of ChatChoice objects, which is assigned to the variable chatChoices.

  10. It constructs and returns a new ChatResponse object using the builder pattern. It sets the choices field to the chatChoices list, the id field to id, the usage field to usage, the object field to object, and the created field to an Instant object created from the createdTime value.

That's it! The method deserialize deserializes the JSON string and returns a ChatResponse object. sequence diagram

private static Message deserializeMessage(ObjectNode message)

private static Message deserializeMessage(ObjectNode message) {
    Message.Builder builder = Message.builder();
    if (message.get("content") != null) {
        builder.content(message.getString("content"));
    }
    if (message.get("name") != null) {
        builder.name(message.getString("name"));
    }
    builder.role(deserializeRole(message.getString("role")));
    if (message.get("function_call") != null) {
        ObjectNode functionCallNode = message.getObjectNode("function_call");
        FunctionalCall.Builder funcBuilder = FunctionalCall.builder().name(functionCallNode.getString("name"));
        if (functionCallNode.getString("arguments") != null) {
            funcBuilder.arguments(JsonParserBuilder.builder().build().parse(functionCallNode.getString("arguments")).getObjectNode());
        }
        builder.functionCall(funcBuilder.build());
    }
    return builder.build();
}

The deserializeMessage method in the ChatResponseDeserializer class is used to deserialize a JSON object into a Message object. Here is a step-by-step description of what the method is doing based on the provided code:

  1. Create a new Message.Builder object.
  2. Check if the JSON object has a field named "content". If it does, get the string value of the "content" field and set it as the content of the message using the builder.content method.
  3. Check if the JSON object has a field named "name". If it does, get the string value of the "name" field and set it as the name of the message using the builder.name method.
  4. Deserialize the "role" field of the JSON object into a Role object using the deserializeRole method and set it as the role of the message using the builder.role method.
  5. Check if the JSON object has a field named "function_call". If it does, get the JSON object value of the "function_call" field and assign it to the functionCallNode variable.
  6. Create a new FunctionalCall.Builder object and set its name as the string value of the "name" field of the functionCallNode using the FunctionalCall.builder().name method.
  7. Check if the "arguments" field of the functionCallNode is not null. If it is not null, get its string value, parse it as JSON using JsonParserBuilder.builder().build().parse method, and get the resulting JSON object. Set this JSON object as the arguments of the functional call using the funcBuilder.arguments method.
  8. Build the functional call object using the funcBuilder.build() method.
  9. Set the functional call object as the function call of the message using the builder.functionCall method.
  10. Build the message object using the builder.build() method.
  11. Return the built message object.

This method takes a JSON object as input and uses various fields of the JSON object to construct a Message object with optional content, name, role, and function call. sequence diagram

private static Role deserializeRole(final String role)

private static Role deserializeRole(final String role) {
    switch(role) {
        case "system":
            return Role.SYSTEM;
        case "user":
            return Role.USER;
        case "assistant":
            return Role.ASSISTANT;
        case "function":
            return Role.FUNCTION;
        default:
            return Role.OTHER;
    }
}

The method deserializeRole is defined as a private static method in the class com.cloudurable.jai.model.text.completion.chat.ChatResponseDeserializer. It takes a parameter role of type String, and its return type is Role.

Here is a step-by-step description of what the deserializeRole method does based on its body:

  1. The method starts with a switch statement that checks the value of the role parameter.

  2. If the value of role is "system", the switch case for "system" is executed. It returns the value Role.SYSTEM.

  3. If the value of role is "user", the switch case for "user" is executed. It returns the value Role.USER.

  4. If the value of role is "assistant", the switch case for "assistant" is executed. It returns the value Role.ASSISTANT.

  5. If the value of role is "function", the switch case for "function" is executed. It returns the value Role.FUNCTION.

  6. If none of the above switch cases match the value of role, the default case is executed. It returns the value Role.OTHER.

  7. The method ends after the switch statement with the default case.

In summary, the deserializeRole method takes a string input role and returns a Role object based on the value of role. It maps specific string values to corresponding Role enum values, and for any other string value, it returns Role.OTHER. sequence diagram

ChatRequestSerializer

The ChatRequestSerializer class provides serialization functionality for ChatRequest objects. It is responsible for converting ChatRequest objects into a JSON string representation compatible with the OpenAI API. The class contains a single public static method serialize, which takes a ChatRequest object as input and returns the serialized JSON string representation.

During the serialization process, the class constructs a JSON body by appending key-value pairs based on the properties of the ChatRequest object. The StringBuilder is used for efficient string construction. The method handles different data types and applies appropriate formatting for each property, including messages, functions, and logit biases.

In summary, ChatRequestSerializer enables the conversion of ChatRequest objects into JSON strings that can be sent to the OpenAI API for chat completion requests.

public static String serialize(ChatRequest chatRequest)

public static String serialize(ChatRequest chatRequest) {
    final JsonSerializer jsonBodyBuilder = new JsonSerializer();
    // start JSON request body for an open ai API chat request
    jsonBodyBuilder.startObject();
    SerializerUtils.outputModel(chatRequest, jsonBodyBuilder);
    final List<Message> messages = chatRequest.getMessages();
    // start JSON list body for messages
    jsonBodyBuilder.startNestedArrayAttribute("messages");
    for (Message message : messages) {
        jsonBodyBuilder.startNestedObjectElement();
        jsonBodyBuilder.addAttribute("role", message.getRole().toString().toLowerCase());
        jsonBodyBuilder.addAttribute("content", message.getContent());
        if (message.getName() != null) {
            jsonBodyBuilder.addAttribute("name", message.getName());
        }
        jsonBodyBuilder.endObject();
    }
    jsonBodyBuilder.endArray();
    SerializerUtils.outputTextParams(chatRequest, jsonBodyBuilder);
    if (chatRequest.getFunctionalCall() == ChatRequest.AUTO) {
        jsonBodyBuilder.addAttribute("function_call", "auto");
    } else if (chatRequest.getFunctionalCall() == ChatRequest.NONE) {
        jsonBodyBuilder.addAttribute("function_call", "none");
    } else if (chatRequest.getFunctionalCall() != null) {
        final FunctionalCall functionalCall = chatRequest.getFunctionalCall();
        jsonBodyBuilder.startNestedObjectAttribute("function_call");
        jsonBodyBuilder.addAttribute("name", functionalCall.getName());
        if (functionalCall.getArguments() != null) {
            jsonBodyBuilder.addAttribute("arguments", functionalCall.getArguments().toString());
        }
        jsonBodyBuilder.endObject();
    }
    final List<FunctionDef> functions = chatRequest.getFunctions();
    if (functions != null && !functions.isEmpty()) {
        jsonBodyBuilder.startNestedArrayAttribute("functions");
        for (FunctionDef function : functions) {
            jsonBodyBuilder.startNestedObjectElement();
            jsonBodyBuilder.addAttribute("name", function.getName());
            ObjectParameter parameters = function.getParameters();
            writeObjectParameter(jsonBodyBuilder, parameters);
            jsonBodyBuilder.endObject();
            jsonBodyBuilder.endObject();
        }
        jsonBodyBuilder.endArray();
    }
    SerializerUtils.outputCompletionParams(chatRequest, jsonBodyBuilder);
    // end JSON request body for an open ai API chat request
    jsonBodyBuilder.endObject();
    String json = jsonBodyBuilder.toString();
    //ystem.out.println(json);
    return json;
}

The serialize method in class com.cloudurable.jai.model.text.completion.chat.ChatRequestSerializer is responsible for converting a ChatRequest object into a JSON string.

Here is a step-by-step description of what this method does based on its body:

  1. Create a new JsonSerializer object named jsonBodyBuilder.
  2. Start the JSON request body for an open AI API chat request by calling startObject() on jsonBodyBuilder.
  3. Use the SerializerUtils.outputModel method to output the chatRequest object to the jsonBodyBuilder.
  4. Get the list of messages from the chatRequest object.
  5. Start the JSON list body for messages by calling startNestedArrayAttribute("messages") on jsonBodyBuilder.
  6. Iterate over each message in the messages list.
    • Start a nested JSON object element by calling startNestedObjectElement() on jsonBodyBuilder.
    • Add the "role" attribute with the lowercase value of message.getRole().toString() by calling addAttribute("role", message.getRole().toString().toLowerCase()) on jsonBodyBuilder.
    • Add the "content" attribute with the value of message.getContent() by calling addAttribute("content", message.getContent()) on jsonBodyBuilder.
    • If the message has a name, add the "name" attribute with its value by calling addAttribute("name", message.getName()) on jsonBodyBuilder.
    • End the nested JSON object by calling endObject() on jsonBodyBuilder.
  7. End the JSON list body for messages by calling endArray() on jsonBodyBuilder.
  8. Use the SerializerUtils.outputTextParams method to output text parameters from the chatRequest object to the jsonBodyBuilder.
  9. Check if the functionalCall of the chatRequest is "AUTO", "NONE", or not null.
    • If the functionalCall is "AUTO", add the "function_call" attribute with the value "auto" by calling addAttribute("function_call", "auto") on jsonBodyBuilder.
    • If the functionalCall is "NONE", add the "function_call" attribute with the value "none" by calling addAttribute("function_call", "none") on jsonBodyBuilder.
    • If the functionalCall is not null, handle the functionalCall object by doing the following:
      • Start a nested JSON object attribute for "function_call" by calling startNestedObjectAttribute("function_call") on jsonBodyBuilder.
      • Add the "name" attribute with the value of functionalCall.getName() by calling addAttribute("name", functionalCall.getName()) on jsonBodyBuilder.
      • If the functionalCall has arguments, add the "arguments" attribute with the string representation of the arguments by calling addAttribute("arguments", functionalCall.getArguments().toString()) on jsonBodyBuilder.
      • End the nested JSON object by calling endObject() on jsonBodyBuilder.
  10. Get the list of functions from the chatRequest object.
  11. Check if the functions list is not null and not empty.
  • If true, start the nested array attribute for "functions" by calling startNestedArrayAttribute("functions") on jsonBodyBuilder.
  • Iterate over each function in the functions list.
    • Start a nested JSON object element by calling startNestedObjectElement() on jsonBodyBuilder.
    • Add the "name" attribute with the value of function.getName() by calling addAttribute("name", function.getName()) on jsonBodyBuilder.
    • Get the parameters object of the function.
    • Use the writeObjectParameter method to write the parameters to the jsonBodyBuilder.
    • End the nested JSON object by calling endObject() on jsonBodyBuilder.
  • End the nested array by calling endArray() on jsonBodyBuilder.
  1. Use the SerializerUtils.outputCompletionParams method to output completion parameters from the chatRequest object to the jsonBodyBuilder.
  2. End the JSON request body for an open AI API chat request by calling endObject() on jsonBodyBuilder.
  3. Convert the JSON body to a string by calling toString() on jsonBodyBuilder and assign it to the json variable.
  4. Return the json string.

Please note that there is commented-out code in the method that prints the JSON, but it is not part of the serialized output. sequence diagram

public static void writeObjectParameter(JsonSerializer jsonBodyBuilder, ObjectParameter op)

public static void writeObjectParameter(JsonSerializer jsonBodyBuilder, ObjectParameter op) {
    jsonBodyBuilder.startNestedObjectAttribute("parameters");
    jsonBodyBuilder.addAttribute("type", op.getType().toString().toLowerCase());
    jsonBodyBuilder.startNestedObjectAttribute("properties");
    for (Parameter parameter : op.getProperties()) {
        jsonBodyBuilder.startNestedObjectAttribute(parameter.getName());
        jsonBodyBuilder.addAttribute("type", parameter.getType().toString().toLowerCase());
        if (parameter instanceof EnumParameter) {
            jsonBodyBuilder.startNestedArrayAttribute("enum");
            for (String enumValue : ((EnumParameter) parameter).getEnumValues()) {
                jsonBodyBuilder.addElement(enumValue);
            }
            jsonBodyBuilder.endArray();
        }
        jsonBodyBuilder.endObject();
    }
    jsonBodyBuilder.endObject();
    jsonBodyBuilder.startNestedArrayAttribute("required");
    for (String req : op.getRequired()) {
        jsonBodyBuilder.addElement(req);
    }
    jsonBodyBuilder.endArray();
}

The writeObjectParameter method in the ChatRequestSerializer class is responsible for serializing an ObjectParameter object and writing its attributes to a JSON body.

Here is the step-by-step description of what this method is doing:

  1. Start building the nested object attribute "parameters" in the JSON body.
  2. Add the "type" attribute to indicate the type of the ObjectParameter object.
  3. Start building the nested object attribute "properties" in the JSON body.
  4. Iterate through each Parameter object in the op.getProperties() list. a. Start building the nested object attribute with the parameter name. b. Add the "type" attribute to indicate the type of the parameter. c. If the parameter is an instance of EnumParameter, start building the nested array attribute "enum". d. Iterate through each enum value in the getEnumValues() list of the EnumParameter. e. Add each enum value as an element in the "enum" array. f. End the object attribute for the parameter.
  5. End the object attribute for "properties".
  6. Start building the nested array attribute "required".
  7. Iterate through each required attribute in the op.getRequired() list. a. Add each required attribute as an element in the "required" array.
  8. End the array attribute for "required".

By following these steps, the writeObjectParameter method will produce a JSON body that represents the attributes of the ObjectParameter object. sequence diagram