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
[WIP] Add Chat endpoint #56
Conversation
I'm not sure why the Lists didn't work before. It might have been some other issue with the API.
good stuff. i was working on this in parallel. i'll take a look, i already wrote some simple tests. so i may be able to use them with yours if you followed the same format as the prior code. |
@megalon -- can you submit this as a PR to a new branch pls? it will make it easier for me to do code review and comments/edits. |
This is already on a separate branch named "feature/chat" on my repo. Is that what you mean? |
(Nice work by the way :) ) |
streaming seems not to be working? For example, given the following code (and a file Auth.cs, containing open api key),
(10-gptnpc) Hughs-MacBook-Air:cs_prot hugh$ bin/Debug/net7.0/cs_prot Abort trap: 6
|
I feel like the streaming responses from openai, e.g.
don't match the structure of ChatResult ChatChoice class?
|
|
change ChatChoice, and adding ChatChoiceDelta as follows fixes this issue:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi there! I hope you don't mind some drive-by feedback from a user of the library. 😄
OpenAI_API/Chat/ChatEndpoint.cs
Outdated
int? max_tokens = null, | ||
double? frequencyPenalty = null, | ||
double? presencePenalty = null, | ||
Dictionary<string, float> logitBias = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same point as above: I'd recommend making this an IReadOnlyDictionary<string, float>
which supports more ways of calling this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be an IReadOnlyDictionary
or a IDictionary
?
Since this is in the outgoing request, logitBias
is something the user has created themselves.
Shouldn't it be a regular dictionary since the user is the one making it?
I noted it in the PR, but I do not actually understand what logit_bias is used for, so I am not sure about it's implementation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good question. The TL;DR is by taking the least derived type, an IReadOnlyDictionary<>
in this case, you make the intention of your method clearer and it's more broadly callable. The caller doesn't need to create a specific Dictionary<>
, they can pass any type that implements the interface (which includes Dictionary<>
, so you lose nothing)
As the Framework Design Guidelines ponit out, method arguments should be the least derived type needed by the method. This makes it more broadly callable. For example, the caller of the method might not have a Dictionary<>
handy. They might have something that implements the interface though.
IReadOnlyDictionary<string, float> biases = GetBiases(...);
Now then they want to pass the biases
into your method, they should be able to without having to call ToDictionary()
.
Since this is in the outgoing request
I'd argue that ChatRequest.LogitBias
(the outgoing request) could also be an IReadOnlyDictionary<string, float>
(or IReadOnlyDictionary<string, int>
, but it's not clear from the documentation). That will get serialized properly.
The only time your method needs to take in a Dictionary<>
is if it's supposed to modify the passed in dictionary, which should be rare. And if your method. modifies the dictionary, it should be very clear to the caller that's what it does.
As a caller of an API, I'd be worried about passing in a concrete Dictionary<>
to a method because maybe the method modifies it and I don't realize it, which could lead to bugs in my code. If I saw a method argument of Dictionary<>
, I'd probably create a copy of my dictionary first via ToDictionary()
to be safe so that anything your method does won't affect my dictionary.
By making the argument IReadOnlyDictionary<>
, you let the caller know that you don't intend to modify the dictionary, but only read its values. Advertising your intentions is good API design. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue that
ChatRequest.LogitBias
(the outgoing request) could also be anIReadOnlyDictionary<string, float>
(orIReadOnlyDictionary<string, int>
, but it's not clear from the documentation). That will get serialized properly.
I should be clear, if the ChatRequest
object is using a builder pattern, then it's fine if the property is Dictionary<>
. But if it's meant to be created all at once, it could be a read only dictionary. This one is not that important to me. My focus is more on the method arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a caller of an API, I'd be worried about passing in a concrete
Dictionary<>
to a method because maybe the method modifies it and I don't realize it, which could lead to bugs in my code. If I saw a method argument ofDictionary<>
, I'd probably create a copy of my dictionary first viaToDictionary()
to be safe so that anything your method does won't affect my dictionary.By making the argument
IReadOnlyDictionary<>
, you let the caller know that you don't intend to modify the dictionary, but only read its values. Advertising your intentions is good API design. 😄
That makes sense to me!
I'd argue that ChatRequest.LogitBias (the outgoing request) could also be an IReadOnlyDictionary<string, float> (or IReadOnlyDictionary<string, int>, but it's not clear from the documentation). That will get serialized properly.
Yeah, it's not clear to me from the documentation either. I might have to look at some other implementations and see what they do. I was hoping someone else would chime in here with the correct answer eventually.
Hey, thanks for the review! All of those changes sound good to me. |
It looks like you're right about this. In the Chat docs "deltas" are just briefly noted in the "stream" description. I missed it because it's not in their example response! It looks like instead of using a new class, I could just use the ChatMessage, since it has the same properties. |
Oh great! Oh right, good point :D |
For anyone updating this from my original PR, I have also added the |
Could we also make I admit that for Chat, it's a little bit ambiguous whether such text should also add the role into such text, i.e. Edit: ok this is what Completions OpenAI-API-dotnet/OpenAI_API/Completions/CompletionResult.cs Lines 83 to 89 in e949bd4
(We'd probably want an |
I'm going to go ahead an merge this, and then commit some additional fixes afterwards, including tests, readme updates, and an alternate |
Wish we have a |
This adds the
chat/completions
endpoint, as specified by the OpenAI API docs.I copied the style of the completions and embeds endpoints, so it should fit in with the project's style.
I've tested the async functions in my own project and it seems to work just fine.
Potential issues
I do not understand what the logit_bias is in the ChatRequest.
In the docs it is listed as a "map" and:
It does not specify what data types to use for this "map", so I interpreted this as a
Dictionary<string, float>
where the key is the ID and the float value is the bias.I am not sure if this is correct.
I have also not tested any of the Streaming functions.
TODO:
Closes #54