Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Builders are unnecessarily complex in some cases #53

Open
Rocky0102 opened this issue Dec 18, 2024 · 7 comments
Open

Builders are unnecessarily complex in some cases #53

Rocky0102 opened this issue Dec 18, 2024 · 7 comments

Comments

@Rocky0102
Copy link

Are those all kinds of builder necessary?

@Rocky0102
Copy link
Author

Since ChatCompletionUserMessageParam used, why role is not set automatically, it's very annoying.
`

public static void main(String[] args) {
    OpenAIClient client = OpenAIOkHttpClient.fromEnv();
    ChatCompletionCreateParams completionCreateParams = ChatCompletionCreateParams.builder()
            .model(ChatModel.GPT_3_5_TURBO)
            .maxTokens(1024)
            .addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
                    ChatCompletionUserMessageParam.builder()
                            .role(ChatCompletionUserMessageParam.Role.USER)
                            .content(ChatCompletionUserMessageParam.Content.ofTextContent(
                                    "Tell me a story about building the best SDK!"
                            ))
                            .build()
            ))
            .build();

`

@TomerAberbach
Copy link
Collaborator

Since ChatCompletionUserMessageParam used, why role is not set automatically, it's very annoying.

Yes, I agree! We are actually planning to update the SDK to automatically set this value for you.

Are those all kinds of builder necessary?

In Java, builders are the common approach to make construction of complex deeply nested objects readable and flexible. Otherwise you'd have to pass the parameters to a constructor in the right order, which is easy to get wrong.

Curious if you encountered any difficulties other than the role one you mentioned? We'd be eager to address those as well :)

@sahil28032005
Copy link

still good approach

@TomerAberbach
Copy link
Collaborator

still good approach

Are you referring to the builders or something else?

@sahil28032005
Copy link

ya i am talking regarding builder

@stargazer33
Copy link

stargazer33 commented Dec 31, 2024

In Java, builders are the common approach ...

Well, using builders make sense in SOME situations where you have 6-7 or more constructor arguments

Otherwise you'd have to pass the parameters to a constructor in the right order

I am capable to pass 3-4-5 parameters to constructor in right order, this is not a problem (and having Javadoc helps)
By the way, you can have multiple constructors with different number of parameters, kind of "syntax sugar"

In my code I have to do this (unfortunately):

messageParam = ofChatCompletionUserMessageParam(
    ChatCompletionUserMessageParam
    .builder()
    .role(ChatCompletionUserMessageParam.Role.USER)
    .content( ChatCompletionUserMessageParam.Content.ofTextContent(msg.msg) )
    .build() );

...

messageParam = ofChatCompletionAssistantMessageParam(
    ChatCompletionAssistantMessageParam
    .builder()
    .role(ChatCompletionAssistantMessageParam.Role.ASSISTANT)
    .content( ChatCompletionAssistantMessageParam.Content.ofTextContent( msg.msg ) )
    .build() );

This code above seriously over-engineered (because it use over-engineered API):
I have to use word "Assistant" 3 times (the same applies to "User"):

ChatCompletionAssistantMessageParam, 
ChatCompletionAssistantMessageParam.Role.ASSISTANT
ChatCompletionAssistantMessageParam.Content.ofTextContent

The question is WHY?
Remember, all we want to build is just this simple JSON (only ONE use of word "assistant"):

            {
                "role": "user",
                "content": "..."
            },
            {
                "role": "assistant",
                "content": "... "
            }

@TomerAberbach TomerAberbach changed the title Seems too much complicated design compared to python version Builders are unnecessarily complex in some cases Dec 31, 2024
@TomerAberbach
Copy link
Collaborator

In Java, builders are the common approach ...

Well, using builders make sense in SOME situations where you have 6-7 or more constructor arguments

Otherwise you'd have to pass the parameters to a constructor in the right order

I am capable to pass 3-4-5 parameters to constructor in right order, this is not a problem (and having Javadoc helps) By the way, you can have multiple constructors with different number of parameters, kind of "syntax sugar"

I hear you, but there are other benefits:

  • Builders scale better as the API grows. Even if we have relatively few parameters now, we may add more parameters in the future, which will make a constructor not ideal. At that point swapping to a builder would be a breaking change, and we care deeply about avoiding breaking changes. Starting with a builder avoids the problem.

  • Builders let you build up the object "over time" more easily (i.e. without many local variables, can pass the builder around across functions as a first-class value, etc.)

  • Understood that you could figure out how to pass parameters in the right order, but:

    • It would be easy to mess up, especially if many of the parameters are primitives. Everyone inevitably makes mistakes when writing code, and we want to decrease the likelihood of that happening! i.e. make it easy to fall into the Pit of Success.

    • The code would not be readable. For example, if we wrote new ChatCompletionCreateParams(ChatModel.GPT_3_5_TURBO, 500, ...), it's not clear what the 500 means without looking at the Javadoc. Of course you can read the Javadoc, but ideally the code is self-documenting and easy to skim.

  • Making a constructor for every possible ordering doesn't solve all of these problems, and scales ~exponentially with the number of parameters.

In my code I have to do this (unfortunately):

messageParam = ofChatCompletionUserMessageParam(
    ChatCompletionUserMessageParam
    .builder()
    .role(ChatCompletionUserMessageParam.Role.USER)
    .content( ChatCompletionUserMessageParam.Content.ofTextContent(msg.msg) )
    .build() );

...

messageParam = ofChatCompletionAssistantMessageParam(
    ChatCompletionAssistantMessageParam
    .builder()
    .role(ChatCompletionAssistantMessageParam.Role.ASSISTANT)
    .content( ChatCompletionAssistantMessageParam.Content.ofTextContent( msg.msg ) )
    .build() );

This code above seriously over-engineered (because it use over-engineered API): I have to use word "Assistant" 3 times (the same applies to "User"):

ChatCompletionAssistantMessageParam, 
ChatCompletionAssistantMessageParam.Role.ASSISTANT
ChatCompletionAssistantMessageParam.Content.ofTextContent

The question is WHY? Remember, all we want to build is just this simple JSON (only ONE use of word "assistant"):

            {
                "role": "user",
                "content": "..."
            },
            {
                "role": "assistant",
                "content": "... "
            }

I agree this code is not ideal!

As I mentioned above, we're working on improving this part. Here are some of the upcoming improvements that we're planning to make before the beta release (SDK is currently alpha):

  • Setting single value enums for you (e.g. no need to call role(...) since it's implied by ChatCompletionAssistantMessageParam)
  • Shortening some union factory function names (e.g. ofChatCompletionAssistantMessageParam is pretty verbose)
  • Letting you omit union factory functions in some cases with overloads (e.g. allow passing the message directly to addMessage without calling ofChatCompletionAssistantMessageParam, allow passing a string directly to content, etc.)
  • Perhaps some other things to make the API more delightful. If you feel the above improvements wouldn't address all your concerns, then definitely let me know. We want the API to be great :)

To answer "why" it's currently like this: we are generating this code from the OpenAI OpenAPI spec using our Stainless code generator and we're still iterating! I can assure you we'll work out all the issues before the GA release of the SDK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants