Lambdua / openai4j

Java client library for OpenAI API.Full support for all OpenAI API models including Completions, Chat, Edits, Embeddings, Audio, Files, Assistants-v2, Images, Moderations, Batch, and Fine-tuning.
MIT License
331 stars 31 forks source link

Basic support for structured outputs #57

Closed bertilmuth closed 3 months ago

bertilmuth commented 3 months ago

This is a resolution to #54.

I tried to stick as close to the OpenAI API as possible. Note that for the JSON schema generation to work, all properties of the serialized classes must be marked with @NotNull or @JsonProperty(required = true)

Here's a test case that replicates the math tutor example from the OpenAI website:

@Test
    void createChatCompletionWithJsonSchema() throws JsonProcessingException {
        final List<ChatMessage> messages = new ArrayList<>();
        final ChatMessage systemMessage = new SystemMessage("You are a helpful math tutor. Guide the user through the solution step by step.");
        final ChatMessage userMessage = new UserMessage("how can I solve 8x + 7 = -23");
        messages.add(systemMessage);
        messages.add(userMessage);

        Class<MathReasoning> rootClass = MathReasoning.class;
        ChatResponseFormat responseFormat = ChatResponseFormat.jsonSchema(rootClass);

        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
                .builder()
                .model("gpt-4o-2024-08-06")
                .messages(messages)
                .responseFormat(responseFormat)
                .maxTokens(1000)
                .build();

        ChatCompletionChoice choice = service.createChatCompletion(chatCompletionRequest).getChoices().get(0);
        MathReasoning mathReasoning = choice.getMessage().parsed(rootClass);

        String finalAnswer = mathReasoning.getFinal_answer();
        assertTrue(finalAnswer.contains("x"));
        assertTrue(finalAnswer.contains("="));
    }

    @Data
    @NoArgsConstructor
    private static class MathReasoning {
        @NotNull private List<Step> steps;
        @NotNull private String final_answer;
    }

    @Data
    @NoArgsConstructor
    private static class Step {
        @NotNull private String explanation;
        @NotNull private String output;
    }