Skip to content

Conversation

@JM-Lab
Copy link
Contributor

@JM-Lab JM-Lab commented Dec 17, 2023

@tzolov, I took a close look at the code and ran the beanStreamOutputParserRecords test in OpenAIClientIT. I'm sorry for the delayed response; I ran into an issue and needed some time to address it.

I made some changes, including defining the Delta for ChatCompletionChunk within the class to handle OpenAI's SSE for ChatCompletionChunk objects. I also adjusted the ObjectMapper for generating response JSON to align with the one used in RestClient.

Additionally, I refactored the code related to ending the Flux Stream and put it into a separate commit.

Please check out the draft pull request, and feel free to reach out if you have any questions or need further clarification.

Copy link
Contributor

@tzolov tzolov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @JM-Lab !
See my comments. I will pick the Predicate and have to check about the object converter.

private static final String DEFAULT_BASE_URL = "https://api.openai.com";
private static final String DEFAULT_EMBEDDING_MODEL = "text-embedding-ada-002";
private static final String SSE_DONE = "[DONE]";
private static final Predicate<String> SSE_DONE_PREDICATE = "[DONE]"::equals;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume that the Predicate is just a code style improvement, not due to issues with the existing conditions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation at https://platform.openai.com/docs/api-reference/chat/create explicitly states that it ends with '[DONE]', so in my opinion, using 'equals' would be clearer than 'contains.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the predicate.

@JsonProperty("delta") Delta delta
) {
// The delta used in ChatCompletionChunk is not identical to ChatCompletion's ChatCompletionMessage, so a Delta record needs to be defined separately.
public record Delta(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid that this is incorrect. The message format is the same in both the chat completion object and the chat completion chunk object and it includes the tool_calls hierarchy as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misunderstood. Thank you for letting me know.


this.objectMapper = new ObjectMapper();
// Use the same ObjectMapper for WebClient's response as the one used in RestClient.
this.objectMapper = Jackson2ObjectMapperBuilder.json().build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, need to dig a bit more.
The recent streaming response parsing failure was due to the unexpected (for me) appearance of the LogProbs in the chunk response. Not sure if i missed it or OpenAI introduced it silently. Hope it is the former.
I deliberately didn't configure the objectmapper to ignore unknown fields so i can catch changes. But didn't expect they will come without announcement and also didn't consider that the webclient and restclient converters are likely ignoring such fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I encountered an error during code review. It appears that LogProbs were added at some point. After checking OpenAI's API specification at https://platform.openai.com/docs/api-reference/chat/object, I noticed the presence of LogProbs. However, in the response for the ChatCompletion - Choice record when the stream is false, LogProbs are not included. To address this issue, I realized that a similar approach is needed for WebClient, similar to the ObjectMapper in RestClient, to ignore unknown fields. Therefore, I added the same configuration in the commit.

Instead of adding LogProbs to ChatCompletion - Choice and ChatCompletionChunk record, considering the continuous changes and potential additions or deprecations in OpenAI's spec, I believe it's more beneficial for developers using Spring AI to configure the ObjectMapper to ignore unknown fields. This way, they won't have to deal with too many specific options. Hence, I implemented the ObjectMapper configuration in the current commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already added the LogProbs in another commit.
But with this PR I'll configure the object mapper to tolerate unknown properties.
Would live out the Jackson2ObjectMapperBuilder for now.

@tzolov
Copy link
Contributor

tzolov commented Dec 19, 2023

Rebased, squashed and merged at b54bb8b

@tzolov tzolov closed this Dec 19, 2023
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

Successfully merging this pull request may close these issues.

2 participants