Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## vNext Release Notes

#### Breaking Changes

#### Namespace Relocations

Several types have been moved from `Devlooped.Extensions.AI` to `Devlooped.Extensions.AI.OpenAI`:

| Type | Old Namespace | New Namespace |
|------|---------------|---------------|
| `Verbosity` | `Devlooped.Extensions.AI` | `Devlooped.Extensions.AI.OpenAI` |
| `WebSearchTool` | `Devlooped.Extensions.AI` | `Devlooped.Extensions.AI.OpenAI` |

**Migration:** Update your `using` statements from `Devlooped.Extensions.AI` to `Devlooped.Extensions.AI.OpenAI` for these types.

#### Removed Types

The following types have been **removed entirely**:

| Type | Replacement |
|------|-------------|
| `AzureInferenceChatClient` | Use the built-in `IChatClientProvider` infrastructure with `AddChatClientProvider` |
| `AzureOpenAIChatClient` | Use the built-in `IChatClientProvider` infrastructure with `AddChatClientProvider` |
| `OpenAIChatClient` | Use the built-in `IChatClientProvider` infrastructure with `AddChatClientProvider` |
| `OpenAIWebSearchToolExtensions` | Properties (`City`, `Region`, `TimeZone`, `ContextSize`) are now directly on `WebSearchTool` |
| `ReasoningEffort` | Use `ChatOptions.ReasoningEffort` from `Microsoft.Extensions.AI` (built-in string-based property) |

#### WebSearchTool Changes

The `WebSearchTool` class has been significantly simplified:

- **Constructor:** Now accepts an optional `country` parameter (`string? country = null`) instead of a required one
- **Properties moved inline:** `City`, `Region`, and `TimeZone` are now properties directly on `WebSearchTool` (no longer extension properties)
- **New property:** `AllowedDomains` (`string[]?`) has been added
- **Removed:** The `ContextSize` extension property has been removed as it's no longer documented on OpenAI's official documentation

#### OpenAIChatOptions Changes

- The `ReasoningEffort` property has been removed from `OpenAIChatOptions`. Use the base `ChatOptions.ReasoningEffort` property directly instead (available in `Microsoft.Extensions.AI`).

#### ConfigurableChatClient Changes

- The `Options` property has been removed from `ConfigurableChatClient` since the new provider-based architecture does not require it
- A new constructor signature is now available with explicit parameters

#### Method Signature Changes

##### AddChatClients

The `AddChatClients` extension methods now include an additional optional parameter:

```csharp
// Old signature
AddChatClients(services, configuration, configurePipeline, configureClient, prefix);

// New signature
AddChatClients(services, configuration, configurePipeline, configureClient, prefix, useDefaultProviders: true);
```

The overload allows skipping the registration of default providers when set to `false`.

#### Removed Methods

- `OpenAIExtensions.ApplyExtensions(ChatOptions?)` has been removed since the new OpenAI-specific ChatOptions use a new mechanism based on lazy initialization of the `ChatOptions.RawRepresentationFactory` to apply the values.
- `ChatOptions.ReasoningEffort` extension property (from `OpenAIExtensions`) has been removed. The `ReasoningEffort` concept is now natively supported by the base `ChatOptions.ReasoningEffort` property in `Microsoft.Extensions.AI`.

---

### Migration Guide

1. **Update namespace imports** for `Verbosity` and `WebSearchTool` to use `Devlooped.Extensions.AI.OpenAI`

2. **Replace `ReasoningEffort` enum** with the built-in `ChatOptions.ReasoningEffort` string property from `Microsoft.Extensions.AI`. For example, use `options.ReasoningEffort = "medium"` instead of `options.ReasoningEffort = ReasoningEffort.Medium`.

3. **Replace custom chat clients** (`AzureInferenceChatClient`, `AzureOpenAIChatClient`, `OpenAIChatClient`) with the new provider-based architecture using `IChatClientProvider` and `AddChatClientProvider`

4. **WebSearchTool usage** is source-compatible once you import the correct OpenAI namespace.

5. **Use `OpenAIChatOptions`** for typed and binding-friendly configuration of OpenAI-specific options.
3 changes: 2 additions & 1 deletion src/Extensions/ChatClientProviders.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ClientModel;
using Azure;
using Devlooped.Extensions.AI.OpenAI;
using Microsoft.Extensions.AI;
Expand Down Expand Up @@ -28,7 +29,7 @@ public IChatClient Create(IConfigurationSection section)
Throw.IfNullOrEmpty(options.ApiKey, $"{section.Path}:apikey");
Throw.IfNullOrEmpty(options.ModelId, $"{section.Path}:modelid");

return new OpenAIChatClient(options.ApiKey, options.ModelId, options);
return new OpenAIClient(new ApiKeyCredential(options.ApiKey), options).GetChatClient(options.ModelId).AsIChatClient();
}

internal sealed class OpenAIProviderOptions : OpenAIClientOptions
Expand Down
14 changes: 7 additions & 7 deletions src/Extensions/Extensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
<PackageReference Include="Azure.AI.OpenAI" Version="2.7.0-beta.2" />
<PackageReference Include="Microsoft.Extensions.AI.AzureAIInference" Version="10.0.0-preview.1.25559.3" />
<PackageReference Include="NuGetizer" Version="1.4.7" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.AI" Version="10.2.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
<PackageReference Include="Microsoft.Extensions.AI" Version="10.3.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.3.0" />
<PackageReference Include="Spectre.Console" Version="0.54.0" />
<PackageReference Include="Spectre.Console.Json" Version="0.54.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.4" />
<PackageReference Include="xAI" Version="*" />
</ItemGroup>

Expand All @@ -44,4 +44,4 @@
<InternalsVisibleTo Include="Tests" />
</ItemGroup>

</Project>
</Project>
2 changes: 1 addition & 1 deletion src/Extensions/OpenAI/AzureOpenAIChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerabl
=> GetChatClient(options?.ModelId ?? modelId).GetStreamingResponseAsync(messages, options, cancellation);

IChatClient GetChatClient(string modelId) => clients.GetOrAdd(modelId, model
=> new PipelineClient(pipeline, endpoint, options).GetOpenAIResponseClient(modelId).AsIChatClient());
=> new PipelineClient(pipeline, endpoint, options).GetResponsesClient(modelId).AsIChatClient());

void IDisposable.Dispose() => GC.SuppressFinalize(this);

Expand Down
60 changes: 0 additions & 60 deletions src/Extensions/OpenAI/OpenAIChatClient.cs

This file was deleted.

12 changes: 0 additions & 12 deletions src/Extensions/OpenAI/OpenAIChatOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,6 @@ namespace Devlooped.Extensions.AI.OpenAI;
/// </remarks>
public class OpenAIChatOptions : ChatOptions
{
/// <summary>
/// Gets or sets the effort level for a reasoning AI model when generating responses.
/// </summary>
/// <remarks>
/// This property is specific to the OpenAI Responses API.
/// </remarks>
public ReasoningEffort? ReasoningEffort
{
get => ((ChatOptions)this).ReasoningEffort;
set => ((ChatOptions)this).ReasoningEffort = value;
}

/// <summary>
/// Gets or sets the verbosity level for a GPT-5+ model when generating responses.
/// </summary>
Expand Down
23 changes: 2 additions & 21 deletions src/Extensions/OpenAI/OpenAIExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,6 @@ public static class OpenAIExtensions
/// has been set to a non-OpenAI factory.</exception>
extension(ChatOptions options)
{
/// <summary>Controls how many reasoning tokens the model generates before producing a response.</summary>
public ReasoningEffort? ReasoningEffort
{
get => options.AdditionalProperties?.TryGetValue("reasoning_effort", out var value) == true && value is ReasoningEffort effort ? effort : null;
set
{
if (value is not null)
{
options.AdditionalProperties ??= [];
options.AdditionalProperties["reasoning_effort"] = value;
EnsureFactory(options);
}
else
{
options.AdditionalProperties?.Remove("reasoning_effort");
}
}
}

/// <summary>
/// Gets or sets the <see cref="AI.Verbosity"/> level for a GPT-5 model when generating responses.
/// </summary>
Expand Down Expand Up @@ -78,11 +59,11 @@ static void EnsureFactory(ChatOptions options)
options.RawRepresentationFactory.Target is not ResponseOptionsFactory)
{
throw new InvalidOperationException(
"Cannot use OpenAI Responses API extension properties (ReasoningEffort, Verbosity) when " +
"Cannot use OpenAI Responses API extension properties (i.e. Verbosity) when " +
"RawRepresentationFactory has already been set to a custom factory. These extension " +
"properties automatically configure the factory for the OpenAI Responses API.");
}

options.RawRepresentationFactory ??= new ResponseOptionsFactory(options).CreateResponseCreationOptions;
options.RawRepresentationFactory ??= new ResponseOptionsFactory(options).NewCreateResponseOptions;
}
}
37 changes: 0 additions & 37 deletions src/Extensions/OpenAI/ReasoningEffort.cs

This file was deleted.

17 changes: 2 additions & 15 deletions src/Extensions/OpenAI/ResponseOptionsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,16 @@ namespace Devlooped.Extensions.AI.OpenAI;

class ResponseOptionsFactory(ChatOptions options)
{
public ResponseCreationOptions CreateResponseCreationOptions(IChatClient client)
public CreateResponseOptions NewCreateResponseOptions(IChatClient client)
{
var creation = new ResponseCreationOptions();

if (options.ReasoningEffort is { } effort)
creation.ReasoningOptions = new ReasoningEffortOptions(effort);
var creation = new CreateResponseOptions();

if (options.Verbosity is { } verbosity)
creation.TextOptions = new VerbosityOptions(verbosity);

return creation;
}

class ReasoningEffortOptions(ReasoningEffort effort) : ResponseReasoningOptions
{
protected override void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options)
{
writer.WritePropertyName("effort"u8);
writer.WriteStringValue(effort.ToString().ToLowerInvariant());
base.JsonModelWriteCore(writer, options);
}
}

class VerbosityOptions(Verbosity verbosity) : ResponseTextOptions
{
protected override void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options)
Expand Down
Loading
Loading