Skip to content

Commit 4e3831c

Browse files
- Added support for gpt-5 and latest API changes
- Added ConverstaionsEndpoint
1 parent 7efb937 commit 4e3831c

15 files changed

Lines changed: 524 additions & 33 deletions

OpenAI-DotNet/Common/ReasoningEffort.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ namespace OpenAI
66
{
77
/// <summary>
88
/// Constrains the effort of reasoning for <see href="https://platform.openai.com/docs/guides/reasoning">Reasoning Models</see>.<br/>
9-
/// Currently supported values are: Low, Medium, High. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning response.
9+
/// Currently supported values are: Minimal, Low, Medium, High. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning response.
1010
/// </summary>
1111
/// <remarks>
1212
/// <b>Reasoning models only!</b>
1313
/// </remarks>
1414
public enum ReasoningEffort
1515
{
16+
[EnumMember(Value = "minimal")]
17+
Minimal = 1,
1618
[EnumMember(Value = "low")]
17-
Low = 1,
19+
Low,
1820
[EnumMember(Value = "medium")]
1921
Medium,
2022
[EnumMember(Value = "high")]

OpenAI-DotNet/Extensions/ResponseContentConverter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public override IResponseContent Read(ref Utf8JsonReader reader, Type typeToConv
2525
"input_image" => root.Deserialize<ImageContent>(options),
2626
"input_file" => root.Deserialize<FileContent>(options),
2727
"refusal" => root.Deserialize<RefusalContent>(options),
28+
"reasoning_text" => root.Deserialize<ReasoningContent>(options),
2829
_ => throw new NotImplementedException($"Unknown response content type: {type}")
2930
};
3031
}

OpenAI-DotNet/OpenAI-DotNet.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Version 8.8.2
3535
- Add file_url for responses api
3636
- Added NoiseReductionSettings for RealtimeConfiguration
3737
- Fix ConversationItemTruncateRequest.ContentIndex default serialization
38+
- Added support for gpt-5 and latest API changes
39+
- Added ConverstaionsEndpoint
3840
Version 8.8.1
3941
- Updated realtime audio transcription settings properties
4042
Version 8.8.0

OpenAI-DotNet/OpenAIClient.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public OpenAIClient(OpenAIAuthentication openAIAuthentication = null, OpenAISett
7575
VectorStoresEndpoint = new VectorStoresEndpoint(this);
7676
RealtimeEndpoint = new RealtimeEndpoint(this);
7777
ResponsesEndpoint = new ResponsesEndpoint(this);
78+
ConversationsEndpoint = new ConversationsEndpoint(this);
7879
}
7980

8081
~OpenAIClient() => Dispose(false);
@@ -238,6 +239,12 @@ private void Dispose(bool disposing)
238239
/// </summary>
239240
public ResponsesEndpoint ResponsesEndpoint { get; }
240241

242+
/// <summary>
243+
/// Create and manage conversations to store and retrieve conversation state across Response API calls.
244+
/// <see href="https://platform.openai.com/docs/api-reference/conversations"/>
245+
/// </summary>
246+
public ConversationsEndpoint ConversationsEndpoint { get; }
247+
241248
#endregion Endpoints
242249

243250
private HttpClient SetupHttpClient(HttpClient client = null)

OpenAI-DotNet/Realtime/RealtimeContentType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public enum RealtimeContentType
1515
[EnumMember(Value = "input_audio")]
1616
InputAudio,
1717
[EnumMember(Value = "item_reference")]
18-
ItemReference
18+
ItemReference,
1919
}
2020
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed under the MIT License. See LICENSE in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Text.Json.Serialization;
6+
7+
namespace OpenAI.Responses
8+
{
9+
public sealed class Conversation
10+
{
11+
[JsonInclude]
12+
[JsonPropertyName("created_at")]
13+
public long CreatedAtUnixTimeSeconds { get; private set; }
14+
15+
[JsonIgnore]
16+
public DateTime CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnixTimeSeconds).UtcDateTime;
17+
18+
[JsonInclude]
19+
[JsonPropertyName("id")]
20+
public string Id { get; private set; }
21+
22+
[JsonInclude]
23+
[JsonPropertyName("metadata")]
24+
public IReadOnlyDictionary<string, string> Metadata { get; private set; }
25+
26+
public override string ToString() => Id;
27+
28+
public static implicit operator string(Conversation conversation) => conversation?.Id;
29+
}
30+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// Licensed under the MIT License. See LICENSE in the project root for license information.
2+
3+
using OpenAI.Extensions;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text.Json;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace OpenAI.Responses
11+
{
12+
public sealed class ConversationsEndpoint : OpenAIBaseEndpoint
13+
{
14+
public ConversationsEndpoint(OpenAIClient client) : base(client) { }
15+
16+
protected override string Root => "conversations";
17+
18+
/// <summary>
19+
/// Create a conversation.
20+
/// </summary>
21+
/// <param name="request"><see cref="CreateConversationRequest"/>.</param>
22+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
23+
/// <returns><see cref="Conversation"/>.</returns>
24+
public async Task<Conversation> CreateConversationAsync(CreateConversationRequest request, CancellationToken cancellationToken = default)
25+
{
26+
var payload = JsonSerializer.Serialize(request, OpenAIClient.JsonSerializationOptions).ToJsonStringContent();
27+
var response = await HttpClient.PostAsync(GetUrl(), payload, cancellationToken).ConfigureAwait(false);
28+
return await response.DeserializeAsync<Conversation>(EnableDebug, payload, client, cancellationToken).ConfigureAwait(false);
29+
}
30+
31+
32+
/// <summary>
33+
/// Get a conversation.
34+
/// </summary>
35+
/// <param name="conversationId">The id of the conversation to retrieve.</param>
36+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
37+
/// <returns><see cref="Conversation"/>.</returns>
38+
public async Task<Conversation> GetConversationAsync(string conversationId, CancellationToken cancellationToken = default)
39+
{
40+
if (string.IsNullOrWhiteSpace(conversationId))
41+
{
42+
throw new ArgumentNullException(nameof(conversationId));
43+
}
44+
45+
var response = await HttpClient.GetAsync(GetUrl($"/{conversationId}"), cancellationToken).ConfigureAwait(false);
46+
return await response.DeserializeAsync<Conversation>(EnableDebug, client, cancellationToken).ConfigureAwait(false);
47+
}
48+
49+
50+
/// <summary>
51+
/// Update a conversation.
52+
/// </summary>
53+
/// <param name="conversationId">
54+
/// The id of the conversation to retrieve.
55+
/// </param>
56+
/// <param name="metadata">
57+
/// Set of 16 key-value pairs that can be attached to an object.
58+
/// This can be useful for storing additional information about the object in a structured format,
59+
/// and querying for objects via API or the dashboard.
60+
/// Keys are strings with a maximum length of 64 characters.Values are strings with a maximum length of 512 characters.
61+
/// </param>
62+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
63+
/// <returns><see cref="Conversation"/>.</returns>
64+
public async Task<Conversation> UpdateConversationAsync(string conversationId, IReadOnlyDictionary<string, string> metadata, CancellationToken cancellationToken = default)
65+
{
66+
if (string.IsNullOrWhiteSpace(conversationId))
67+
{
68+
throw new ArgumentNullException(nameof(conversationId));
69+
}
70+
71+
var payload = JsonSerializer.Serialize(new { metadata }, OpenAIClient.JsonSerializationOptions).ToJsonStringContent();
72+
var response = await HttpClient.PatchAsync(GetUrl($"/{conversationId}"), payload, cancellationToken).ConfigureAwait(false);
73+
return await response.DeserializeAsync<Conversation>(EnableDebug, payload, client, cancellationToken).ConfigureAwait(false);
74+
}
75+
76+
77+
/// <summary>
78+
/// Delete a conversation.
79+
/// </summary>
80+
/// <remarks>
81+
/// Items in the conversation will not be deleted.
82+
/// </remarks>
83+
/// <param name="conversationId">The id of the conversation to retrieve.</param>
84+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
85+
/// <returns>True, if the <see cref="Conversation"/> was deleted successfully, otherwise False.</returns>
86+
public async Task<bool> DeleteConversationAsync(string conversationId, CancellationToken cancellationToken = default)
87+
{
88+
if (string.IsNullOrWhiteSpace(conversationId))
89+
{
90+
throw new ArgumentNullException(nameof(conversationId));
91+
}
92+
93+
var response = await HttpClient.DeleteAsync(GetUrl($"/{conversationId}"), cancellationToken).ConfigureAwait(false);
94+
var result = await response.DeserializeAsync<DeletedResponse>(EnableDebug, client, cancellationToken).ConfigureAwait(false);
95+
return result.Deleted;
96+
}
97+
98+
#region Conversation Items
99+
100+
/// <summary>
101+
/// List all items for a conversation with the given ID.
102+
/// </summary>
103+
/// <param name="conversationId">The ID of the conversation to list items for.</param>
104+
/// <param name="query">Optional, <see cref="ListQuery"/>.</param>
105+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
106+
/// <returns><see cref="ListResponse{IResponseItem}"/>.</returns>
107+
public async Task<ListResponse<IResponseItem>> ListConversationItemsAsync(string conversationId, ListQuery query = null, CancellationToken cancellationToken = default)
108+
{
109+
if (string.IsNullOrWhiteSpace(conversationId))
110+
{
111+
throw new ArgumentNullException(nameof(conversationId));
112+
}
113+
114+
var response = await HttpClient.GetAsync(GetUrl($"/{conversationId}/items", query), cancellationToken).ConfigureAwait(false);
115+
return await response.DeserializeAsync<ListResponse<IResponseItem>>(EnableDebug, client, cancellationToken).ConfigureAwait(false);
116+
}
117+
118+
119+
/// <summary>
120+
/// Create items in a conversation with the given ID.
121+
/// </summary>
122+
/// <param name="conversationId">The ID of the conversation to add the item to.</param>
123+
/// <param name="items">The items to add to the conversation. You may add up to 20 items at a time.</param>
124+
/// <param name="include">Optional, Additional fields to include in the response.</param>
125+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
126+
/// <returns><see cref="ListResponse{IResponseItem}"/>.</returns>
127+
public async Task<ListResponse<IResponseItem>> CreateConversationItemsAsync(string conversationId, IEnumerable<IResponseItem> items, string[] include = null, CancellationToken cancellationToken = default)
128+
{
129+
if (string.IsNullOrWhiteSpace(conversationId))
130+
{
131+
throw new ArgumentNullException(nameof(conversationId));
132+
}
133+
134+
if (items == null)
135+
{
136+
throw new ArgumentNullException(nameof(items));
137+
}
138+
139+
var payload = JsonSerializer.Serialize(new { items }, OpenAIClient.JsonSerializationOptions).ToJsonStringContent();
140+
Dictionary<string, string> query = null;
141+
142+
if (include is { Length: > 0 })
143+
{
144+
query = new Dictionary<string, string>
145+
{
146+
{ "include", string.Join(",", include) }
147+
};
148+
}
149+
150+
var response = await HttpClient.PostAsync(GetUrl($"/{conversationId}/items", query), payload, cancellationToken).ConfigureAwait(false);
151+
return await response.DeserializeAsync<ListResponse<IResponseItem>>(EnableDebug, payload, client, cancellationToken).ConfigureAwait(false);
152+
}
153+
154+
/// <summary>
155+
/// Retrieve an item from a conversation.
156+
/// </summary>
157+
/// <param name="conversationId">The ID of the conversation that contains the item.</param>
158+
/// <param name="itemId">The ID of the item to retrieve.</param>
159+
/// <param name="include">Optional, Additional fields to include in the response.</param>
160+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
161+
/// <returns><see cref="IResponseItem"/>.</returns>
162+
public async Task<IResponseItem> GetConversationItemAsync(string conversationId, string itemId, string[] include = null, CancellationToken cancellationToken = default)
163+
{
164+
if (string.IsNullOrWhiteSpace(conversationId))
165+
{
166+
throw new ArgumentNullException(nameof(conversationId));
167+
}
168+
169+
if (string.IsNullOrWhiteSpace(itemId))
170+
{
171+
throw new ArgumentNullException(nameof(itemId));
172+
}
173+
174+
Dictionary<string, string> query = null;
175+
176+
if (include is { Length: > 0 })
177+
{
178+
query = new Dictionary<string, string>
179+
{
180+
{ "include", string.Join(",", include) }
181+
};
182+
}
183+
184+
var response = await HttpClient.GetAsync(GetUrl($"/{conversationId}/items/{itemId}", query), cancellationToken).ConfigureAwait(false);
185+
return await response.DeserializeAsync<IResponseItem>(EnableDebug, client, cancellationToken).ConfigureAwait(false);
186+
}
187+
188+
189+
/// <summary>
190+
/// Delete an item from a conversation with the given IDs.
191+
/// </summary>
192+
/// <param name="conversationId">The ID of the conversation that contains the item.</param>
193+
/// <param name="itemId">The ID of the item to delete.</param>
194+
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
195+
/// <returns>Returns the updated <see cref="Conversation"/>>.</returns>
196+
public async Task<Conversation> DeleteConversationItemAsync(string conversationId, string itemId, CancellationToken cancellationToken = default)
197+
{
198+
if (string.IsNullOrWhiteSpace(conversationId))
199+
{
200+
throw new ArgumentNullException(nameof(conversationId));
201+
}
202+
203+
if (string.IsNullOrWhiteSpace(itemId))
204+
{
205+
throw new ArgumentNullException(nameof(itemId));
206+
}
207+
208+
var response = await HttpClient.DeleteAsync(GetUrl($"/{conversationId}/items/{itemId}"), cancellationToken).ConfigureAwait(false);
209+
return await response.DeserializeAsync<Conversation>(EnableDebug, client, cancellationToken).ConfigureAwait(false);
210+
}
211+
212+
#endregion Conversation Items
213+
}
214+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed under the MIT License. See LICENSE in the project root for license information.
2+
3+
using System.Collections.Generic;
4+
using System.Text.Json.Serialization;
5+
6+
namespace OpenAI.Responses
7+
{
8+
public sealed class CreateConversationRequest
9+
{
10+
public CreateConversationRequest() { }
11+
12+
public CreateConversationRequest(IResponseItem item, IReadOnlyDictionary<string, string> metadata = null)
13+
: this([item], metadata)
14+
{
15+
}
16+
17+
public CreateConversationRequest(IEnumerable<IResponseItem> items, IReadOnlyDictionary<string, string> metadata = null)
18+
{
19+
Items = items;
20+
Metadata = metadata;
21+
}
22+
23+
/// <summary>
24+
/// Initial items to include in the conversation context. You may add up to 20 items at a time.
25+
/// </summary>
26+
[JsonInclude]
27+
[JsonPropertyName("items")]
28+
public IEnumerable<IResponseItem> Items { get; private set; }
29+
30+
/// <summary>
31+
/// Set of 16 key-value pairs that can be attached to an object.
32+
/// This can be useful for storing additional information about the object in a structured format,
33+
/// and querying for objects via API or the dashboard. Keys are strings with a maximum length of 64 characters.
34+
/// Values are strings with a maximum length of 512 characters.
35+
/// </summary>
36+
[JsonInclude]
37+
[JsonPropertyName("metadata")]
38+
public IReadOnlyDictionary<string, string> Metadata { get; private set; }
39+
}
40+
}

0 commit comments

Comments
 (0)