dvcrn / ex_openai

Auto-generated Elixir SDK for OpenAI APIs with full typespecs, docs and streaming support
MIT License
71 stars 27 forks source link

Can't parse new OpenAI OpenAPI spec #10

Closed nallwhy closed 4 days ago

nallwhy commented 8 months ago

https://github.com/openai/openai-openapi/blob/master/openapi.yaml

== Compilation error in file lib/ex_openai.ex ==
** (FunctionClauseError) no function clause matching in ExOpenAI.Codegen.parse_component_schema/1    

    The following arguments were given to ExOpenAI.Codegen.parse_component_schema/1:

        # 1
        %{"oneOf" => [%{"$ref" => "#/components/schemas/ChatCompletionRequestMessageContentPartText"}, %{"$ref" => "#/components/schemas/ChatCompletionRequestMessageContentPartImage"}], "x-oaiExpandable" => true}

    lib/ex_openai/codegen.ex:394: ExOpenAI.Codegen.parse_component_schema/1
    lib/ex_openai/codegen.ex:625: anonymous fn/2 in ExOpenAI.Codegen.get_documentation/0
    (stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
    lib/ex_openai/codegen.ex:624: ExOpenAI.Codegen.get_documentation/0
    lib/ex_openai.ex:31: (file)
dvcrn commented 8 months ago

Looks like they changed the format slightly, there’s now a OneOf type for 2 different components 😅

Need to add handling for that

dvcrn commented 8 months ago

Culprit:

    ChatCompletionRequestMessageContentPart:
      oneOf:
        - $ref: "#/components/schemas/ChatCompletionRequestMessageContentPartText"
        - $ref: "#/components/schemas/ChatCompletionRequestMessageContentPartImage"
      x-oaiExpandable: true

and

    ChatCompletionRequestMessage:
      oneOf:
        - $ref: "#/components/schemas/ChatCompletionRequestSystemMessage"
        - $ref: "#/components/schemas/ChatCompletionRequestUserMessage"
        - $ref: "#/components/schemas/ChatCompletionRequestAssistantMessage"
        - $ref: "#/components/schemas/ChatCompletionRequestToolMessage"
        - $ref: "#/components/schemas/ChatCompletionRequestFunctionMessage"
      x-oaiExpandable: true

So this is a component that can be oneOf many different components. Need to figure out a good way to represent those

dvcrn commented 8 months ago

Hrrmmm so the issue here is that those new components aren't representable as structs in Elixir. Previously each of those had properties so it was easy. What we could do is, represent them as empty structs (%{}), but then use the @type to map them to the others, like this:

iex(3)> t ExOpenAI.Components.ChatCompletionRequestMessage
@type t() ::
        ExOpenAI.Components.ChatCompletionRequestFunctionMessage.t()
        | ExOpenAI.Components.ChatCompletionRequestToolMessage.t()
        | ExOpenAI.Components.ChatCompletionRequestAssistantMessage.t()
        | ExOpenAI.Components.ChatCompletionRequestUserMessage.t()
        | ExOpenAI.Components.ChatCompletionRequestSystemMessage.t()

Then when a function references them, it would do ExOpenAI.Components.ChatCompletionRequestMessage.t() which maps to the others

dvcrn commented 8 months ago

Done, please give latest HEAD a try. It changes component handling a bit so I don't want to release it immediately, need to test a bit