sashirestela / simple-openai

A Java library to use the OpenAI Api in the simplest possible way.
MIT License
182 stars 17 forks source link

provide a way to customize JsonSchemaUtil #159

Closed asafbennatan closed 4 months ago

asafbennatan commented 4 months ago

currently i dont see a way to customize the outputted schema that will be used for functions . specifically i need a way to output additionalProperties=true for some type. any suggestion? would a PR be accepted here ?

additionally i suspect that setting Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES by default would be reasonable , if this is done then i wont need a customization for the JsonSchemaUtil at all but i suspect this is a broader issue than that

sashirestela commented 4 months ago

Hi @asafbennatan thanks for using simple-openai!

As far as I know, OpenAI is not expecting any "additionalProperties" field in the function's structure. I'm afraid I'm not following you, could you elaborate more here and explain what you are trying to do, please?

asafbennatan commented 4 months ago

Hi @sashirestela , thanks for creating this library. yes figured the same later , but something similar still is required . i have a scenario where the property is an object but without a known schema - the schema is dynamically derived from somewhere else in the chat.

i wanted that when a Map is encountered we will add the missing properties:{} to it. (same as i see that is being done to the top level object). here is how i did that (but this required me to fork the library ):

class MapAdditionalPropertiesModule implements Module {
    @Override
    public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
        builder.forTypesInGeneral().withTypeAttributeOverride(new TypeAttributeOverrideV2() {
            @Override
            public void overrideTypeAttributes(ObjectNode collectedTypeAttributes, TypeScope scope, SchemaGenerationContext context) {
                if (scope.getType().isInstanceOf(Map.class)) {
                    collectedTypeAttributes.putObject("properties" );

                }
            }
        });
    }
}

//JsonSchemaUtil

   public static JsonNode classToJsonSchema(Class<?> clazz) {
        JsonNode jsonSchema = null;
        try {
            var jacksonModule = new JacksonModule(JacksonOption.RESPECT_JSONPROPERTY_REQUIRED,
                    JacksonOption.RESPECT_JSONPROPERTY_ORDER);
            var configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12,
                    OptionPreset.PLAIN_JSON)
                    .with(jacksonModule)
                    .with(new MapAdditionalPropertiesModule()) //this line here
.......

for reference here is an example of my use case :

    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    static class MapTestClass {

        @JsonProperty(required = true)
        public Map<String,Object> first;

    }

the desired output is :

{"type":"object","properties":{"first":{"type":"object","properties":{}}},"required":["first"]}

currently it will output:

{"type":"object","properties":{"first":{"type":"object"}},"required":["first"]}

there might be a better way to achieve this but in general i assume there can be other use cases which require the customization of the schema generation process .

my suggestion is to have the users of the library to optionally provide custom schema generation logic via some interface. i can create some solution like that if you are are willing to accept such PR. Thanks.

sashirestela commented 4 months ago

I'm not sure if OpenAI is going to work with this "dynamic structure", but I'm curious about it. You will need to provide some running mechanism or extend the FunctionExecutor too. I could have a better understanding of your requirement in code, so you could create a PR.

asafbennatan commented 4 months ago

this is working already, let me work on a PR

asafbennatan commented 4 months ago

hi @sashirestela , this is done - please check pr #160

sashirestela commented 4 months ago

@asafbennatan This was deployed as part of the 3.6.0 release. Thanks for your contribution.

If you find this project valuable there are a few ways you can show us your love, preferably all of them 🙂: