mirror of https://github.com/dapr/dotnet-sdk.git
Add .NET client for LLM Conversations support (#1382)
* Updated prototype Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added Dapr.AI project and unit test project to contain the conversational building block (and potentially future other projects) Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Changed default values Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed unnecessary method Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added a few unit tests Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added example project Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added missing copyright headers Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Changed type name -> DaprLlmInput to DaprConversationInput Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Returning read only list Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Update to use IReadOnlyDictionary Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added method to abstract class Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Striving for consistency in how properties are specified on the record Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Refactored enum extensions out to Dapr.Common since it will be used in AI project Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added JSON converter for System.Text.Json to handle enum serialization based on the enum member attributes Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added unit tests to prove out generic enum JSON converter using EnumMember attributes Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added JSON converter to new enum for Dapr Conversation role Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Set up role to map to the string used in grpc call to sidecar Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * No need for the JSON converter after all Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added missing package version to fix build error Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed duplicate using statement breaking build Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed missing [Fact] annotation Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated proto types to reflect type name changes in https://github.com/dapr/dapr/pull/8250 Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added support for service lifetime Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Building out documentation for Dapr AI Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Simplified registration Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Tweaked package version Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Using IConfiguration to source DaprClient values if provided in service provider Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed Models.* directories, flattened into Conversation namespace Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Swapped out to use IReadOnlyDictionary Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added suggested optimization Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed bad using statement Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updates to use uniform method for standing up new Dapr clients Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed duplicate project reference Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed build error Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixing build errors Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed bad references Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed several build errors Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixing more build errors Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to fix several build errors Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed bad refernce Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixing more build errors Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Role is required when submitting conversation input Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed impossible path since the role cannot be nullable Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed impossible path from logic now that role cannot be null Signed-off-by: Whit Waldo <whit.waldo@innovian.net> --------- Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
This commit is contained in:
parent
cfd4fbee84
commit
7b5ca4fb6c
|
@ -1,52 +1,52 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
|
||||
<PackageVersion Include="FluentAssertions" Version="5.9.0" />
|
||||
<PackageVersion Include="GitHubActionsTestLogger" Version="1.1.2" />
|
||||
<PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.28.2" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.67.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
|
||||
<PackageVersion Include="MinVer" Version="2.3.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="protobuf-net.Grpc.AspNetCore" Version="1.2.2" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.10" />
|
||||
<PackageVersion Include="xunit" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
|
||||
<PackageVersion Include="FluentAssertions" Version="5.9.0" />
|
||||
<PackageVersion Include="GitHubActionsTestLogger" Version="1.1.2" />
|
||||
<PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.28.2" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.67.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
|
||||
<PackageVersion Include="MinVer" Version="2.3.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="protobuf-net.Grpc.AspNetCore" Version="1.2.2" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.10" />
|
||||
<PackageVersion Include="xunit" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
24
all.sln
24
all.sln
|
@ -119,6 +119,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Com
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common.Test", "test\Dapr.Common.Test\Dapr.Common.Test.csproj", "{CDB47863-BEBD-4841-A807-46D868962521}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.AI", "src\Dapr.AI\Dapr.AI.csproj", "{273F2527-1658-4CCF-8DC6-600E921188C5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.AI.Test", "test\Dapr.AI.Test\Dapr.AI.Test.csproj", "{2F3700EF-1CDA-4C15-AC88-360230000ECD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AI", "AI", "{3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConversationalAI", "examples\AI\ConversationalAI\ConversationalAI.csproj", "{11011FF8-77EA-4B25-96C0-29D4D486EF1C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowExternalInteraction", "examples\Workflow\WorkflowExternalInteraction\WorkflowExternalInteraction.csproj", "{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowMonitor", "examples\Workflow\WorkflowMonitor\WorkflowMonitor.csproj", "{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6}"
|
||||
|
@ -331,6 +339,18 @@ Global
|
|||
{CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -439,6 +459,10 @@ Global
|
|||
{DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{CDB47863-BEBD-4841-A807-46D868962521} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{273F2527-1658-4CCF-8DC6-600E921188C5} = {27C5D71D-0721-4221-9286-B94AB07B58CF}
|
||||
{2F3700EF-1CDA-4C15-AC88-360230000ECD} = {DD020B34-460F-455F-8D17-CF4A949F100B}
|
||||
{3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5} = {D687DDC4-66C5-4667-9E3A-FD8B78ECAA78}
|
||||
{11011FF8-77EA-4B25-96C0-29D4D486EF1C} = {3046DBF4-C2FF-4F3A-9176-E1C01E0A90E5}
|
||||
{43CB06A9-7E88-4C5F-BFB8-947E072CBC9F} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{7F73A3D8-FFC2-4E31-AA3D-A4840316A8C6} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{945DD3B7-94E5-435E-B3CB-796C20A652C7} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
|
|
|
@ -84,6 +84,13 @@ Put the Dapr .NET SDK to the test. Walk through the .NET quickstarts and tutoria
|
|||
<a href="{{< ref dotnet-jobs >}}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><b>AI</b></h5>
|
||||
<p class="card-text">Create and manage AI operations in .NET</p>
|
||||
<a href="{{< ref dotnet-ai >}}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## More information
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
_index.md
|
||||
|
||||
---
|
||||
type: docs
|
||||
title: "Getting started with the Dapr AI .NET SDK client"
|
||||
linkTitle: "AI"
|
||||
weight: 10000
|
||||
description: How to get up and running with the Dapr AI .NET SDK
|
||||
no_list: true
|
||||
---
|
||||
|
||||
The Dapr AI client package allows you to interact with the AI capabilities provided by the Dapr sidecar.
|
||||
|
||||
## Installation
|
||||
|
||||
To get started with the Dapr AI .NET SDK client, install the following package from NuGet:
|
||||
```sh
|
||||
dotnet add package Dapr.AI
|
||||
```
|
||||
|
||||
A `DaprConversationClient` holes access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar.
|
||||
|
||||
### Dependency Injection
|
||||
|
||||
The `AddDaprAiConversation()` method will register the Dapr client ASP.NET Core dependency injection and is the recommended approach
|
||||
for using this package. This method accepts an optional options delegate for configuring the `DaprConversationClient` and a
|
||||
`ServiceLifetime` argument, allowing you to specify a different lifetime for the registered services instead of the default `Singleton`
|
||||
value.
|
||||
|
||||
The following example assumes all default values are acceptable and is sufficient to register the `DaprConversationClient`:
|
||||
|
||||
```csharp
|
||||
services.AddDaprAiConversation();
|
||||
```
|
||||
|
||||
The optional configuration delegate is used to configure the `DaprConversationClient` by specifying options on the
|
||||
`DaprConversationClientBuilder` as in the following example:
|
||||
```csharp
|
||||
services.AddSingleton<DefaultOptionsProvider>();
|
||||
services.AddDaprAiConversation((serviceProvider, clientBuilder) => {
|
||||
//Inject a service to source a value from
|
||||
var optionsProvider = serviceProvider.GetRequiredService<DefaultOptionsProvider>();
|
||||
var standardTimeout = optionsProvider.GetStandardTimeout();
|
||||
|
||||
//Configure the value on the client builder
|
||||
clientBuilder.UseTimeout(standardTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Instantiation
|
||||
Rather than using dependency injection, a `DaprConversationClient` can also be built using the static client builder.
|
||||
|
||||
For best performance, create a single long-lived instance of `DaprConversationClient` and provide access to that shared instance throughout
|
||||
your application. `DaprConversationClient` instances are thread-safe and intended to be shared.
|
||||
|
||||
Avoid creating a `DaprConversationClient` per-operation.
|
||||
|
||||
A `DaprConversationClient` can be configured by invoking methods on the `DaprConversationClientBuilder` class before calling `.Build()`
|
||||
to create the client. The settings for each `DaprConversationClient` are separate and cannot be changed after calling `.Build()`.
|
||||
|
||||
```csharp
|
||||
var daprConversationClient = new DaprConversationClientBuilder()
|
||||
.UseJsonSerializerSettings( ... ) //Configure JSON serializer
|
||||
.Build();
|
||||
```
|
||||
|
||||
See the .NET [documentation here]({{< ref dotnet-client >}}) for more information about the options available when configuring the Dapr client via the builder.
|
||||
|
||||
## Try it out
|
||||
Put the Dapr AI .NET SDK to the test. Walk through the samples to see Dapr in action:
|
||||
|
||||
| SDK Samples | Description |
|
||||
| ----------- | ----------- |
|
||||
| [SDK samples](https://github.com/dapr/dotnet-sdk/tree/master/examples) | Clone the SDK repo to try out some examples and get started. |
|
||||
|
||||
## Building Blocks
|
||||
|
||||
This part of the .NET SDK allows you to interface with the Conversations API to send and receive messages from
|
||||
large language models.
|
||||
|
||||
### Send messages
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Best practices with the Dapr AI .NET SDK client"
|
||||
linkTitle: "Best Practices"
|
||||
weight: 100000
|
||||
description: How to get up and running with the Dapr .NET SDK
|
||||
---
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.AI\Dapr.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,23 @@
|
|||
using Dapr.AI.Conversation;
|
||||
using Dapr.AI.Conversation.Extensions;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprAiConversation();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var conversationClient = app.Services.GetRequiredService<DaprConversationClient>();
|
||||
var response = await conversationClient.ConverseAsync("conversation",
|
||||
new List<DaprConversationInput>
|
||||
{
|
||||
new DaprConversationInput(
|
||||
"Please write a witty haiku about the Dapr distributed programming framework at dapr.io",
|
||||
DaprConversationRole.Generic)
|
||||
});
|
||||
|
||||
Console.WriteLine("Received the following from the LLM:");
|
||||
foreach (var resp in response.Outputs)
|
||||
{
|
||||
Console.WriteLine($"\t{resp.Result}");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Dapr.AI.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to configure the conversation operation.
|
||||
/// </summary>
|
||||
/// <param name="ConversationId">The identifier of the conversation this is a continuation of.</param>
|
||||
public sealed record ConversationOptions(string? ConversationId = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// Temperature for the LLM to optimize for creativity or predictability.
|
||||
/// </summary>
|
||||
public double Temperature { get; init; } = default;
|
||||
/// <summary>
|
||||
/// Flag that indicates whether data that comes back from the LLM should be scrubbed of PII data.
|
||||
/// </summary>
|
||||
public bool ScrubPII { get; init; } = default;
|
||||
/// <summary>
|
||||
/// The metadata passing to the conversation components.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, string> Metadata { get; init; } = new Dictionary<string, string>();
|
||||
/// <summary>
|
||||
/// Parameters for all custom fields.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Any> Parameters { get; init; } = new Dictionary<string, Any>();
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.Common;
|
||||
using Dapr.Common.Extensions;
|
||||
using P = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// Used to interact with the Dapr conversation building block.
|
||||
/// </summary>
|
||||
public sealed class DaprConversationClient : DaprAIClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The HTTP client used by the client for calling the Dapr runtime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly HttpClient HttpClient;
|
||||
/// <summary>
|
||||
/// The Dapr API token value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly string? DaprApiToken;
|
||||
/// <summary>
|
||||
/// The autogenerated Dapr client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal P.Dapr.DaprClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to initialize a new instance of a <see cref="DaprConversationClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The Dapr client.</param>
|
||||
/// <param name="httpClient">The HTTP client used by the client for calling the Dapr runtime.</param>
|
||||
/// <param name="daprApiToken">An optional token required to send requests to the Dapr sidecar.</param>
|
||||
public DaprConversationClient(P.Dapr.DaprClient client,
|
||||
HttpClient httpClient,
|
||||
string? daprApiToken = null)
|
||||
{
|
||||
this.Client = client;
|
||||
this.HttpClient = httpClient;
|
||||
this.DaprApiToken = daprApiToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends various inputs to the large language model via the Conversational building block on the Dapr sidecar.
|
||||
/// </summary>
|
||||
/// <param name="daprConversationComponentName">The name of the Dapr conversation component.</param>
|
||||
/// <param name="inputs">The input values to send.</param>
|
||||
/// <param name="options">Optional options used to configure the conversation.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The response(s) provided by the LLM provider.</returns>
|
||||
public override async Task<DaprConversationResponse> ConverseAsync(string daprConversationComponentName, IReadOnlyList<DaprConversationInput> inputs, ConversationOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var request = new P.ConversationRequest
|
||||
{
|
||||
Name = daprConversationComponentName
|
||||
};
|
||||
|
||||
if (options is not null)
|
||||
{
|
||||
request.ContextID = options.ConversationId;
|
||||
request.ScrubPII = options.ScrubPII;
|
||||
|
||||
foreach (var (key, value) in options.Metadata)
|
||||
{
|
||||
request.Metadata.Add(key, value);
|
||||
}
|
||||
|
||||
foreach (var (key, value) in options.Parameters)
|
||||
{
|
||||
request.Parameters.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
request.Inputs.Add(new P.ConversationInput
|
||||
{
|
||||
ScrubPII = input.ScrubPII,
|
||||
Message = input.Message,
|
||||
Role = input.Role.GetValueFromEnumMember()
|
||||
});
|
||||
}
|
||||
|
||||
var grpCCallOptions =
|
||||
DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprConversationClient).Assembly, this.DaprApiToken,
|
||||
cancellationToken);
|
||||
|
||||
var result = await Client.ConverseAlpha1Async(request, grpCCallOptions).ConfigureAwait(false);
|
||||
var outputs = result.Outputs.Select(output => new DaprConversationResult(output.Result)
|
||||
{
|
||||
Parameters = output.Parameters.ToDictionary(kvp => kvp.Key, parameter => parameter.Value)
|
||||
}).ToList();
|
||||
|
||||
return new DaprConversationResponse(outputs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.Common;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1.Dapr;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// Used to create a new instance of a <see cref="DaprConversationClient"/>.
|
||||
/// </summary>
|
||||
public sealed class DaprConversationClientBuilder : DaprGenericClientBuilder<DaprConversationClient>
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to initialize a new instance of the <see cref="DaprConversationClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
public DaprConversationClientBuilder(IConfiguration? configuration = null) : base(configuration)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the client instance from the properties of the builder.
|
||||
/// </summary>
|
||||
/// <returns>The Dapr client instance.</returns>
|
||||
/// <summary>
|
||||
/// Builds the client instance from the properties of the builder.
|
||||
/// </summary>
|
||||
public override DaprConversationClient Build()
|
||||
{
|
||||
var daprClientDependencies = BuildDaprClientDependencies(typeof(DaprConversationClient).Assembly);
|
||||
var client = new Autogenerated.DaprClient(daprClientDependencies.channel);
|
||||
return new DaprConversationClient(client, daprClientDependencies.httpClient, daprClientDependencies.daprApiToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an input for the Dapr Conversational API.
|
||||
/// </summary>
|
||||
/// <param name="Message">The message to send to the LLM.</param>
|
||||
/// <param name="Role">The role indicating the entity providing the message.</param>
|
||||
/// <param name="ScrubPII">If true, scrubs the data that goes into the LLM.</param>
|
||||
public sealed record DaprConversationInput(string Message, DaprConversationRole Role, bool ScrubPII = false);
|
|
@ -0,0 +1,21 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// The response for a conversation.
|
||||
/// </summary>
|
||||
/// <param name="Outputs">The collection of conversation results.</param>
|
||||
/// <param name="ConversationId">The identifier of an existing or newly created conversation.</param>
|
||||
public record DaprConversationResponse(IReadOnlyList<DaprConversationResult> Outputs, string? ConversationId = null);
|
|
@ -0,0 +1,28 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// The result for a single conversational input.
|
||||
/// </summary>
|
||||
/// <param name="Result">The result for one conversation input.</param>
|
||||
public record DaprConversationResult(string Result)
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for all custom fields.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Any> Parameters { get; init; } = new Dictionary<string, Any>();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapr.Common.JsonConverters;
|
||||
|
||||
namespace Dapr.AI.Conversation;
|
||||
|
||||
/// <summary>
|
||||
/// Represents who
|
||||
/// </summary>
|
||||
public enum DaprConversationRole
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a message sent by an AI.
|
||||
/// </summary>
|
||||
[EnumMember(Value="ai")]
|
||||
AI,
|
||||
/// <summary>
|
||||
/// Represents a message sent by a human.
|
||||
/// </summary>
|
||||
[EnumMember(Value="human")]
|
||||
Human,
|
||||
/// <summary>
|
||||
/// Represents a message sent by the system.
|
||||
/// </summary>
|
||||
[EnumMember(Value="system")]
|
||||
System,
|
||||
/// <summary>
|
||||
/// Represents a message sent by a generic user.
|
||||
/// </summary>
|
||||
[EnumMember(Value="generic")]
|
||||
Generic,
|
||||
/// <summary>
|
||||
/// Represents a message sent by a function.
|
||||
/// </summary>
|
||||
[EnumMember(Value="function")]
|
||||
Function,
|
||||
/// <summary>
|
||||
/// Represents a message sent by a tool.
|
||||
/// </summary>
|
||||
[EnumMember(Value="tool")]
|
||||
Tool
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Dapr.AI.Conversation.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Used by the fluent registration builder to configure a Dapr AI conversational manager.
|
||||
/// </summary>
|
||||
public sealed class DaprAiConversationBuilder : IDaprAiConversationBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The registered services on the builder.
|
||||
/// </summary>
|
||||
public IServiceCollection Services { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to initialize a new <see cref="DaprAiConversationBuilder"/>.
|
||||
/// </summary>
|
||||
public DaprAiConversationBuilder(IServiceCollection services)
|
||||
{
|
||||
Services = services;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Dapr.AI.Conversation.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the dependency injection registration extensions for the Dapr AI Conversation operations.
|
||||
/// </summary>
|
||||
public static class DaprAiConversationBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the necessary functionality for the Dapr AI conversation functionality.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IDaprAiConversationBuilder AddDaprAiConversation(this IServiceCollection services, Action<IServiceProvider, DaprConversationClientBuilder>? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services, nameof(services));
|
||||
|
||||
services.AddHttpClient();
|
||||
|
||||
var registration = new Func<IServiceProvider, DaprConversationClient>(provider =>
|
||||
{
|
||||
var configuration = provider.GetService<IConfiguration>();
|
||||
var builder = new DaprConversationClientBuilder(configuration);
|
||||
|
||||
var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
|
||||
builder.UseHttpClientFactory(httpClientFactory);
|
||||
|
||||
configure?.Invoke(provider, builder);
|
||||
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
switch (lifetime)
|
||||
{
|
||||
case ServiceLifetime.Scoped:
|
||||
services.TryAddScoped(registration);
|
||||
break;
|
||||
case ServiceLifetime.Transient:
|
||||
services.TryAddTransient(registration);
|
||||
break;
|
||||
case ServiceLifetime.Singleton:
|
||||
default:
|
||||
services.TryAddSingleton(registration);
|
||||
break;
|
||||
}
|
||||
|
||||
return new DaprAiConversationBuilder(services);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.AI.Extensions;
|
||||
|
||||
namespace Dapr.AI.Conversation.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a root builder for the Dapr AI conversational functionality facilitating a more fluent-style registration.
|
||||
/// </summary>
|
||||
public interface IDaprAiConversationBuilder : IDaprAiServiceBuilder
|
||||
{
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6;net8</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PackageId>Dapr.AI</PackageId>
|
||||
<Title>Dapr AI SDK</Title>
|
||||
<Description>Dapr AI SDK for performing operations associated with artificial intelligence.</Description>
|
||||
<VersionSuffix>alpha</VersionSuffix>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dapr.Common\Dapr.Common.csproj" />
|
||||
<ProjectReference Include="..\Dapr.Protos\Dapr.Protos.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,34 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.AI.Conversation;
|
||||
|
||||
namespace Dapr.AI;
|
||||
|
||||
/// <summary>
|
||||
/// The base implementation of a Dapr AI client.
|
||||
/// </summary>
|
||||
public abstract class DaprAIClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends various inputs to the large language model via the Conversational building block on the Dapr sidecar.
|
||||
/// </summary>
|
||||
/// <param name="daprConversationComponentName">The name of the Dapr conversation component.</param>
|
||||
/// <param name="inputs">The input values to send.</param>
|
||||
/// <param name="options">Optional options used to configure the conversation.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The response(s) provided by the LLM provider.</returns>
|
||||
public abstract Task<DaprConversationResponse> ConverseAsync(string daprConversationComponentName,
|
||||
IReadOnlyList<DaprConversationInput> inputs, ConversationOptions? options = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Dapr.AI.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for registering Dapr AI service functionality.
|
||||
/// </summary>
|
||||
public interface IDaprAiServiceBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The registered services on the builder.
|
||||
/// </summary>
|
||||
public IServiceCollection Services { get; }
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,41 +0,0 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Dapr.Client
|
||||
{
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the value of an enum out of the attached <see cref="EnumMemberAttribute"/> attribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The enum.</typeparam>
|
||||
/// <param name="value">The value of the enum to pull the value for.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetValueFromEnumMember<T>(this T value) where T : Enum
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value, nameof(value));
|
||||
|
||||
var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||
if (memberInfo.Length <= 0)
|
||||
return value.ToString();
|
||||
|
||||
var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false);
|
||||
return (attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString()) ?? value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,11 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("Dapr.Actors, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Actors.Generators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AspNetCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Jobs, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Messaging, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Extensions.Configuration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Workflow, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
|
||||
|
@ -27,6 +29,7 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("Dapr.Actors.AspNetCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Actors.Generators.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Actors.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AI.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AspNetCore.IntegrationTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AspNetCore.IntegrationTest.App, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.AspNetCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
|
@ -40,3 +43,4 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("Dapr.E2E.Test.App.ReentrantActors, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Extensions.Configuration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Jobs.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
[assembly: InternalsVisibleTo("Dapr.Messaging.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b1f597635c44597fcecb493e2b1327033b29b1a98ac956a1a538664b68f87d45fbaada0438a15a6265e62864947cc067d8da3a7d93c5eb2fcbb850e396c8684dba74ea477d82a1bbb18932c0efb30b64ff1677f85ae833818707ac8b49ad8062ca01d2c89d8ab1843ae73e8ba9649cd28666b539444dcdee3639f95e2a099bb2")]
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using Grpc.Core;
|
||||
|
||||
namespace Dapr.Common;
|
||||
|
||||
internal static class DaprClientUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provisions the gRPC call options used to provision the various Dapr clients.
|
||||
/// </summary>
|
||||
/// <param name="daprApiToken">The Dapr API token, if any.</param>
|
||||
/// <param name="assembly">The assembly the user agent is built from.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The gRPC call options.</returns>
|
||||
internal static CallOptions ConfigureGrpcCallOptions(Assembly assembly, string? daprApiToken, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var callOptions = new CallOptions(headers: new Metadata(), cancellationToken: cancellationToken);
|
||||
|
||||
//Add the user-agent header to the gRPC call options
|
||||
var assemblyVersion = assembly
|
||||
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
||||
.FirstOrDefault()?
|
||||
.InformationalVersion;
|
||||
var userAgent = new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}").ToString();
|
||||
callOptions.Headers!.Add("User-Agent", userAgent);
|
||||
|
||||
//Add the API token to the headers as well if it's populated
|
||||
if (daprApiToken is not null)
|
||||
{
|
||||
var apiTokenHeader = GetDaprApiTokenHeader(daprApiToken);
|
||||
if (apiTokenHeader is not null)
|
||||
{
|
||||
callOptions.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return callOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to create the user-agent from the assembly attributes.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly the client is being built for.</param>
|
||||
/// <returns>The header value containing the user agent information.</returns>
|
||||
public static ProductInfoHeaderValue GetUserAgent(Assembly assembly)
|
||||
{
|
||||
var assemblyVersion = assembly
|
||||
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
||||
.FirstOrDefault()?
|
||||
.InformationalVersion;
|
||||
return new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to provision the header used for the Dapr API token on the HTTP or gRPC connection.
|
||||
/// </summary>
|
||||
/// <param name="daprApiToken">The value of the Dapr API token.</param>
|
||||
/// <returns>If a Dapr API token exists, the key/value pair to use for the header; otherwise null.</returns>
|
||||
public static KeyValuePair<string, string>? GetDaprApiTokenHeader(string? daprApiToken) =>
|
||||
string.IsNullOrWhiteSpace(daprApiToken)
|
||||
? null
|
||||
: new KeyValuePair<string, string>("dapr-api-token", daprApiToken);
|
||||
}
|
||||
|
|
@ -1,4 +1,18 @@
|
|||
using System.Text.Json;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Grpc.Net.Client;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
|
@ -170,8 +184,9 @@ public abstract class DaprGenericClientBuilder<TClientBuilder> where TClientBuil
|
|||
/// Builds out the inner DaprClient that provides the core shape of the
|
||||
/// runtime gRPC client used by the consuming package.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly the dependencies are being built for.</param>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
protected (GrpcChannel channel, HttpClient httpClient, Uri httpEndpoint) BuildDaprClientDependencies()
|
||||
protected (GrpcChannel channel, HttpClient httpClient, Uri httpEndpoint, string daprApiToken) BuildDaprClientDependencies(Assembly assembly)
|
||||
{
|
||||
var grpcEndpoint = new Uri(this.GrpcEndpoint);
|
||||
if (grpcEndpoint.Scheme != "http" && grpcEndpoint.Scheme != "https")
|
||||
|
@ -184,22 +199,48 @@ public abstract class DaprGenericClientBuilder<TClientBuilder> where TClientBuil
|
|||
// Set correct switch to make secure gRPC service calls. This switch must be set before creating the GrpcChannel.
|
||||
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
}
|
||||
|
||||
|
||||
var httpEndpoint = new Uri(this.HttpEndpoint);
|
||||
if (httpEndpoint.Scheme != "http" && httpEndpoint.Scheme != "https")
|
||||
{
|
||||
throw new InvalidOperationException("The HTTP endpoint must use http or https.");
|
||||
}
|
||||
|
||||
var channel = GrpcChannel.ForAddress(this.GrpcEndpoint, this.GrpcChannelOptions);
|
||||
//Configure the HTTP client
|
||||
var httpClient = ConfigureHttpClient(assembly);
|
||||
this.GrpcChannelOptions.HttpClient = httpClient;
|
||||
|
||||
var channel = GrpcChannel.ForAddress(this.GrpcEndpoint, this.GrpcChannelOptions);
|
||||
return (channel, httpClient, httpEndpoint, this.DaprApiToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the HTTP client.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly the user agent is built from.</param>
|
||||
/// <returns>The HTTP client to interact with the Dapr runtime with.</returns>
|
||||
private HttpClient ConfigureHttpClient(Assembly assembly)
|
||||
{
|
||||
var httpClient = HttpClientFactory is not null ? HttpClientFactory() : new HttpClient();
|
||||
|
||||
//Set the timeout as necessary
|
||||
if (this.Timeout > TimeSpan.Zero)
|
||||
{
|
||||
httpClient.Timeout = this.Timeout;
|
||||
}
|
||||
|
||||
//Set the user agent
|
||||
var userAgent = DaprClientUtilities.GetUserAgent(assembly);
|
||||
httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent.ToString());
|
||||
|
||||
//Set the API token
|
||||
var apiTokenHeader = DaprClientUtilities.GetDaprApiTokenHeader(this.DaprApiToken);
|
||||
if (apiTokenHeader is not null)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value);
|
||||
}
|
||||
|
||||
return (channel, httpClient, httpEndpoint);
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
using System.Reflection;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Dapr.Common.Extensions;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapr.Common.Extensions;
|
||||
|
||||
namespace Dapr.Common.JsonConverters;
|
||||
|
||||
/// <summary>
|
||||
/// A JsonConverter used to convert from an enum to a string and vice versa, but using the Enum extension written to pull
|
||||
/// the value from the [EnumMember] attribute, if present.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The enum type to convert.</typeparam>
|
||||
internal sealed class GenericEnumJsonConverter<T> : JsonConverter<T> where T : struct, Enum
|
||||
{
|
||||
private static readonly Dictionary<string, T> enumMemberCache = new();
|
||||
|
||||
static GenericEnumJsonConverter()
|
||||
{
|
||||
foreach (var enumValue in Enum.GetValues<T>())
|
||||
{
|
||||
var enumMemberValue = enumValue.GetValueFromEnumMember();
|
||||
enumMemberCache[enumMemberValue] = enumValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads and converts the JSON to type <typeparamref name="T" />.</summary>
|
||||
/// <param name="reader">The reader.</param>
|
||||
/// <param name="typeToConvert">The type to convert.</param>
|
||||
/// <param name="options">An object that specifies serialization options to use.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
//Get the string value from the JSON reader
|
||||
var value = reader.GetString();
|
||||
|
||||
//Try pulling the value from the cache
|
||||
if (value is not null && enumMemberCache.TryGetValue(value, out var enumValue))
|
||||
{
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
//If no match found, throw an exception
|
||||
throw new JsonException($"Invalid valid for {typeToConvert.Name}: {value}");
|
||||
}
|
||||
|
||||
/// <summary>Writes a specified value as JSON.</summary>
|
||||
/// <param name="writer">The writer to write to.</param>
|
||||
/// <param name="value">The value to convert to JSON.</param>
|
||||
/// <param name="options">An object that specifies serialization options to use.</param>
|
||||
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||
{
|
||||
//Get the value from the EnumMember attribute, if any
|
||||
var enumMemberValue = value.GetValueFromEnumMember();
|
||||
|
||||
//Write the value to the JSON writer
|
||||
writer.WriteStringValue(enumMemberValue);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.Common;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
||||
namespace Dapr.Jobs;
|
||||
|
@ -21,17 +22,22 @@ namespace Dapr.Jobs;
|
|||
/// </summary>
|
||||
public sealed class DaprJobsClientBuilder : DaprGenericClientBuilder<DaprJobsClient>
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to initialize a new instance of <see cref="IConfiguration"/>.
|
||||
/// </summary>
|
||||
/// <param name="configuration">An optional instance of <see cref="IConfiguration"/>.</param>
|
||||
public DaprJobsClientBuilder(IConfiguration? configuration = null) : base(configuration)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the client instance from the properties of the builder.
|
||||
/// </summary>
|
||||
/// <returns>The Dapr client instance.</returns>
|
||||
public override DaprJobsClient Build()
|
||||
{
|
||||
var daprClientDependencies = this.BuildDaprClientDependencies();
|
||||
|
||||
var daprClientDependencies = this.BuildDaprClientDependencies(typeof(DaprJobsClient).Assembly);
|
||||
var client = new Autogenerated.Dapr.DaprClient(daprClientDependencies.channel);
|
||||
var apiTokenHeader = this.DaprApiToken is not null ? DaprJobsClient.GetDaprApiTokenHeader(this.DaprApiToken) : null;
|
||||
|
||||
return new DaprJobsGrpcClient(client, daprClientDependencies.httpClient, apiTokenHeader);
|
||||
return new DaprJobsGrpcClient(client, daprClientDependencies.httpClient, daprClientDependencies.daprApiToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using Dapr.Common;
|
||||
using Dapr.Jobs.Models;
|
||||
using Dapr.Jobs.Models.Responses;
|
||||
using Google.Protobuf;
|
||||
|
@ -28,29 +27,35 @@ namespace Dapr.Jobs;
|
|||
internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Present only for testing purposes.
|
||||
/// The HTTP client used by the client for calling the Dapr runtime.
|
||||
/// </summary>
|
||||
internal readonly HttpClient httpClient;
|
||||
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly HttpClient HttpClient;
|
||||
/// <summary>
|
||||
/// Used to populate options headers with API token value.
|
||||
/// The Dapr API token value.
|
||||
/// </summary>
|
||||
internal readonly KeyValuePair<string, string>? apiTokenHeader;
|
||||
|
||||
private readonly Autogenerated.Dapr.DaprClient client;
|
||||
private readonly string userAgent = UserAgent().ToString();
|
||||
|
||||
// property exposed for testing purposes
|
||||
internal Autogenerated.Dapr.DaprClient Client => client;
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly string? DaprApiToken;
|
||||
/// <summary>
|
||||
/// The autogenerated Dapr client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal Autogenerated.Dapr.DaprClient Client { get; }
|
||||
|
||||
internal DaprJobsGrpcClient(
|
||||
Autogenerated.Dapr.DaprClient innerClient,
|
||||
HttpClient httpClient,
|
||||
KeyValuePair<string, string>? apiTokenHeader)
|
||||
string? daprApiToken)
|
||||
{
|
||||
this.client = innerClient;
|
||||
this.httpClient = httpClient;
|
||||
this.apiTokenHeader = apiTokenHeader;
|
||||
this.Client = innerClient;
|
||||
this.HttpClient = httpClient;
|
||||
this.DaprApiToken = daprApiToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -107,11 +112,11 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
|
||||
var envelope = new Autogenerated.ScheduleJobRequest { Job = job };
|
||||
|
||||
var callOptions = CreateCallOptions(headers: null, cancellationToken);
|
||||
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await client.ScheduleJobAlpha1Async(envelope, callOptions);
|
||||
await Client.ScheduleJobAlpha1Async(envelope, grpcCallOptions).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -146,8 +151,8 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
try
|
||||
{
|
||||
var envelope = new Autogenerated.GetJobRequest { Name = jobName };
|
||||
var callOptions = CreateCallOptions(headers: null, cancellationToken);
|
||||
var response = await client.GetJobAlpha1Async(envelope, callOptions);
|
||||
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
|
||||
var response = await Client.GetJobAlpha1Async(envelope, grpcCallOptions);
|
||||
return new DaprJobDetails(new DaprJobSchedule(response.Job.Schedule))
|
||||
{
|
||||
DueTime = response.Job.DueTime is not null ? DateTime.Parse(response.Job.DueTime) : null,
|
||||
|
@ -190,8 +195,8 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
try
|
||||
{
|
||||
var envelope = new Autogenerated.DeleteJobRequest { Name = jobName };
|
||||
var callOptions = CreateCallOptions(headers: null, cancellationToken);
|
||||
await client.DeleteJobAlpha1Async(envelope, callOptions);
|
||||
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
|
||||
await Client.DeleteJobAlpha1Async(envelope, grpcCallOptions);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -213,36 +218,7 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.httpClient.Dispose();
|
||||
this.HttpClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private CallOptions CreateCallOptions(Metadata? headers, CancellationToken cancellationToken)
|
||||
{
|
||||
var callOptions = new CallOptions(headers: headers ?? new Metadata(), cancellationToken: cancellationToken);
|
||||
|
||||
callOptions.Headers!.Add("User-Agent", this.userAgent);
|
||||
|
||||
if (apiTokenHeader is not null)
|
||||
{
|
||||
callOptions.Headers.Add(apiTokenHeader.Value.Key, apiTokenHeader.Value.Value);
|
||||
}
|
||||
|
||||
return callOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value for the User-Agent.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ProductInfoHeaderValue"/> containing the value to use for the User-Agent.</returns>
|
||||
private static ProductInfoHeaderValue UserAgent()
|
||||
{
|
||||
var assembly = typeof(DaprJobsClient).Assembly;
|
||||
var assemblyVersion = assembly
|
||||
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
||||
.FirstOrDefault()?
|
||||
.InformationalVersion;
|
||||
|
||||
return new ProductInfoHeaderValue("dapr-sdk-dotnet", $"v{assemblyVersion}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
|
@ -25,27 +26,29 @@ public static class DaprJobsServiceCollectionExtensions
|
|||
/// Adds Dapr Jobs client support to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprJobsClient"/>.</param>
|
||||
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprJobsClient"/> using injected services.</param>
|
||||
/// <param name="lifetime">The lifetime of the registered services.</param>
|
||||
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<DaprJobsClientBuilder>? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<IServiceProvider, DaprJobsClientBuilder>? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));
|
||||
|
||||
//Register the IHttpClientFactory implementation
|
||||
serviceCollection.AddHttpClient();
|
||||
|
||||
|
||||
var registration = new Func<IServiceProvider, DaprJobsClient>(serviceProvider =>
|
||||
{
|
||||
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
|
||||
var configuration = serviceProvider.GetService<IConfiguration>();
|
||||
|
||||
var builder = new DaprJobsClientBuilder();
|
||||
var builder = new DaprJobsClientBuilder(configuration);
|
||||
builder.UseHttpClientFactory(httpClientFactory);
|
||||
|
||||
configure?.Invoke(builder);
|
||||
configure?.Invoke(serviceProvider, builder);
|
||||
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
|
||||
switch (lifetime)
|
||||
{
|
||||
case ServiceLifetime.Scoped:
|
||||
|
@ -59,35 +62,6 @@ public static class DaprJobsServiceCollectionExtensions
|
|||
serviceCollection.TryAddSingleton(registration);
|
||||
break;
|
||||
}
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Dapr Jobs client support to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprJobsClient"/> using injected services.</param>
|
||||
/// <param name="lifetime">The lifetime of the registered services.</param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<IServiceProvider, DaprJobsClientBuilder>? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));
|
||||
|
||||
//Register the IHttpClientFactory implementation
|
||||
serviceCollection.AddHttpClient();
|
||||
|
||||
serviceCollection.TryAddSingleton(serviceProvider =>
|
||||
{
|
||||
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
|
||||
|
||||
var builder = new DaprJobsClientBuilder();
|
||||
builder.UseHttpClientFactory(httpClientFactory);
|
||||
|
||||
configure?.Invoke(serviceProvider, builder);
|
||||
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
|
|
@ -39,9 +39,8 @@ public sealed class DaprPublishSubscribeClientBuilder : DaprGenericClientBuilder
|
|||
/// </summary>
|
||||
public override DaprPublishSubscribeClient Build()
|
||||
{
|
||||
var daprClientDependencies = BuildDaprClientDependencies();
|
||||
var daprClientDependencies = BuildDaprClientDependencies(typeof(DaprPublishSubscribeClient).Assembly);
|
||||
var client = new Autogenerated.Dapr.DaprClient(daprClientDependencies.channel);
|
||||
|
||||
return new DaprPublishSubscribeGrpcClient(client);
|
||||
return new DaprPublishSubscribeGrpcClient(client, daprClientDependencies.httpClient, daprClientDependencies.daprApiToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,14 +20,36 @@ namespace Dapr.Messaging.PublishSubscribe;
|
|||
/// </summary>
|
||||
internal sealed class DaprPublishSubscribeGrpcClient : DaprPublishSubscribeClient
|
||||
{
|
||||
private readonly P.DaprClient daprClient;
|
||||
/// <summary>
|
||||
/// The HTTP client used by the client for calling the Dapr runtime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly HttpClient HttpClient;
|
||||
/// <summary>
|
||||
/// The Dapr API token value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
internal readonly string? DaprApiToken;
|
||||
/// <summary>
|
||||
/// The autogenerated Dapr client.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Property exposed for testing purposes.
|
||||
/// </remarks>
|
||||
private readonly P.DaprClient Client;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="DaprPublishSubscribeGrpcClient"/>
|
||||
/// </summary>
|
||||
public DaprPublishSubscribeGrpcClient(P.DaprClient client)
|
||||
public DaprPublishSubscribeGrpcClient(P.DaprClient client, HttpClient httpClient, string? daprApiToken)
|
||||
{
|
||||
daprClient = client;
|
||||
Client = client;
|
||||
this.HttpClient = httpClient;
|
||||
this.DaprApiToken = daprApiToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -41,7 +63,7 @@ internal sealed class DaprPublishSubscribeGrpcClient : DaprPublishSubscribeClien
|
|||
/// <returns></returns>
|
||||
public override async Task<IAsyncDisposable> SubscribeAsync(string pubSubName, string topicName, DaprSubscriptionOptions options, TopicMessageHandler messageHandler, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var receiver = new PublishSubscribeReceiver(pubSubName, topicName, options, messageHandler, daprClient);
|
||||
var receiver = new PublishSubscribeReceiver(pubSubName, topicName, options, messageHandler, Client);
|
||||
await receiver.SubscribeAsync(cancellationToken);
|
||||
return receiver;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Dapr.Messaging.PublishSubscribe.Extensions;
|
||||
|
@ -25,8 +26,9 @@ public static class PublishSubscribeServiceCollectionExtensions
|
|||
var registration = new Func<IServiceProvider, DaprPublishSubscribeClient>(serviceProvider =>
|
||||
{
|
||||
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
|
||||
var configuration = serviceProvider.GetService<IConfiguration>();
|
||||
|
||||
var builder = new DaprPublishSubscribeClientBuilder();
|
||||
var builder = new DaprPublishSubscribeClientBuilder(configuration);
|
||||
builder.UseHttpClientFactory(httpClientFactory);
|
||||
|
||||
configure?.Invoke(serviceProvider, builder);
|
||||
|
|
|
@ -157,4 +157,4 @@ message ConfigurationItem {
|
|||
|
||||
// the metadata which will be passed to/from configuration store component.
|
||||
map<string,string> metadata = 3;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -340,4 +340,4 @@ message ListInputBindingsResponse {
|
|||
|
||||
// HealthCheckResponse is the message with the response to the health check.
|
||||
// This message is currently empty as used as placeholder.
|
||||
message HealthCheckResponse {}
|
||||
message HealthCheckResponse {}
|
||||
|
|
|
@ -202,6 +202,9 @@ service Dapr {
|
|||
|
||||
// Delete a job
|
||||
rpc DeleteJobAlpha1(DeleteJobRequest) returns (DeleteJobResponse) {}
|
||||
|
||||
// Converse with a LLM service
|
||||
rpc ConverseAlpha1(ConversationRequest) returns (ConversationResponse) {}
|
||||
}
|
||||
|
||||
// InvokeServiceRequest represents the request message for Service invocation.
|
||||
|
@ -1206,7 +1209,7 @@ message Job {
|
|||
//
|
||||
// Systemd timer style cron accepts 6 fields:
|
||||
// seconds | minutes | hours | day of month | month | day of week
|
||||
// 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-7/sun-sat
|
||||
// 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-6/sun-sat
|
||||
//
|
||||
// "0 30 * * * *" - every hour on the half hour
|
||||
// "0 15 3 * * *" - every day at 03:15
|
||||
|
@ -1274,4 +1277,56 @@ message DeleteJobRequest {
|
|||
// DeleteJobResponse is the message response to delete the job by name.
|
||||
message DeleteJobResponse {
|
||||
// Empty
|
||||
}
|
||||
|
||||
// ConversationRequest is the request object for Conversation.
|
||||
message ConversationRequest {
|
||||
// The name of Conversation component
|
||||
string name = 1;
|
||||
|
||||
// The ID of an existing chat (like in ChatGPT)
|
||||
optional string contextID = 2;
|
||||
|
||||
// Inputs for the conversation, support multiple input in one time.
|
||||
repeated ConversationInput inputs = 3;
|
||||
|
||||
// Parameters for all custom fields.
|
||||
map<string, google.protobuf.Any> parameters = 4;
|
||||
|
||||
// The metadata passing to conversation components.
|
||||
map<string, string> metadata = 5;
|
||||
|
||||
// Scrub PII data that comes back from the LLM
|
||||
optional bool scrubPII = 6;
|
||||
|
||||
// Temperature for the LLM to optimize for creativity or predictability
|
||||
optional double temperature = 7;
|
||||
}
|
||||
|
||||
message ConversationInput {
|
||||
// The message to send to the llm
|
||||
string message = 1;
|
||||
|
||||
// The role to set for the message
|
||||
optional string role = 2;
|
||||
|
||||
// Scrub PII data that goes into the LLM
|
||||
optional bool scrubPII = 3;
|
||||
}
|
||||
|
||||
// ConversationResult is the result for one input.
|
||||
message ConversationResult {
|
||||
// Result for the one conversation input.
|
||||
string result = 1;
|
||||
// Parameters for all custom fields.
|
||||
map<string, google.protobuf.Any> parameters = 2;
|
||||
}
|
||||
|
||||
// ConversationResponse is the response for Conversation.
|
||||
message ConversationResponse {
|
||||
// The ID of an existing chat (like in ChatGPT)
|
||||
optional string contextID = 1;
|
||||
|
||||
// An array of results.
|
||||
repeated ConversationResult outputs = 2;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.AI.Conversation;
|
||||
|
||||
namespace Dapr.AI.Test.Conversation;
|
||||
|
||||
public class DaprConversationClientBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_WithDefaultConfiguration_ShouldReturnNewInstanceOfDaprConversationClient()
|
||||
{
|
||||
// Arrange
|
||||
var conversationClientBuilder = new DaprConversationClientBuilder();
|
||||
|
||||
// Act
|
||||
var client = conversationClientBuilder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(client);
|
||||
Assert.IsType<DaprConversationClient>(client);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2024 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Dapr.AI.Conversation;
|
||||
using Dapr.AI.Conversation.Extensions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Dapr.AI.Test.Conversation.Extensions;
|
||||
|
||||
public class DaprAiConversationBuilderExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void AddDaprConversationClient_FromIConfiguration()
|
||||
{
|
||||
const string apiToken = "abc123";
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string> { { "DAPR_API_TOKEN", apiToken } })
|
||||
.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
|
||||
services.AddDaprAiConversation();
|
||||
|
||||
var app = services.BuildServiceProvider();
|
||||
|
||||
var conversationClient = app.GetRequiredService<DaprConversationClient>() as DaprConversationClient;
|
||||
|
||||
Assert.NotNull(conversationClient!.DaprApiToken);
|
||||
Assert.Equal(apiToken, conversationClient.DaprApiToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddDaprAiConversation_WithoutConfigure_ShouldAddServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddDaprAiConversation();
|
||||
Assert.NotNull(builder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddDaprAiConversation_RegistersIHttpClientFactory()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddDaprAiConversation();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
|
||||
Assert.NotNull(httpClientFactory);
|
||||
|
||||
var daprConversationClient = serviceProvider.GetService<DaprConversationClient>();
|
||||
Assert.NotNull(daprConversationClient);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddDaprAiConversation_NullServices_ShouldThrowException()
|
||||
{
|
||||
IServiceCollection services = null;
|
||||
Assert.Throws<ArgumentNullException>(() => services.AddDaprAiConversation());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="coverlet.msbuild">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Dapr.AI\Dapr.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,38 +0,0 @@
|
|||
using System.Runtime.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Client.Test.Extensions
|
||||
{
|
||||
public class EnumExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_RedResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Red.GetValueFromEnumMember();
|
||||
Assert.Equal("red", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_YellowResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Yellow.GetValueFromEnumMember();
|
||||
Assert.Equal("YELLOW", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_BlueResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Blue.GetValueFromEnumMember();
|
||||
Assert.Equal("Blue", value);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
[EnumMember(Value = "red")]
|
||||
Red,
|
||||
[EnumMember(Value = "YELLOW")]
|
||||
Yellow,
|
||||
Blue
|
||||
}
|
||||
}
|
|
@ -12,14 +12,14 @@ public class EnumExtensionTest
|
|||
var value = TestEnum.Red.GetValueFromEnumMember();
|
||||
Assert.Equal("red", value);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_YellowResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Yellow.GetValueFromEnumMember();
|
||||
Assert.Equal("YELLOW", value);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_BlueResolvesAsExpected()
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ public class EnumExtensionTest
|
|||
Assert.Equal("Blue", value);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
[EnumMember(Value = "red")]
|
||||
|
@ -35,4 +36,3 @@ public enum TestEnum
|
|||
Yellow,
|
||||
Blue
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
using System.Runtime.Serialization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapr.Common.JsonConverters;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Common.Test.JsonConverters;
|
||||
|
||||
public class GenericEnumJsonConverterTest
|
||||
{
|
||||
[Fact]
|
||||
public void ShouldSerializeWithEnumMemberAttribute()
|
||||
{
|
||||
var testValue = new TestType("ColorTest", Color.Red);
|
||||
var serializedValue = JsonSerializer.Serialize(testValue);
|
||||
Assert.Equal("{\"Name\":\"ColorTest\",\"Color\":\"definitely-not-red\"}", serializedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSerializeWithoutEnumMemberAttribute()
|
||||
{
|
||||
var testValue = new TestType("ColorTest", Color.Green);
|
||||
var serializedValue = JsonSerializer.Serialize(testValue);
|
||||
Assert.Equal("{\"Name\":\"ColorTest\",\"Color\":\"Green\"}", serializedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDeserializeWithEnumMemberAttribute()
|
||||
{
|
||||
const string json = "{\"Name\":\"ColorTest\",\"Color\":\"definitely-not-red\"}";
|
||||
var deserializedValue = JsonSerializer.Deserialize<TestType>(json);
|
||||
Assert.Equal("ColorTest", deserializedValue.Name);
|
||||
Assert.Equal(Color.Red, deserializedValue.Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDeserializeWithoutEnumMemberAttribute()
|
||||
{
|
||||
const string json = "{\"Name\":\"ColorTest\",\"Color\":\"Green\"}";
|
||||
var deserializedValue = JsonSerializer.Deserialize<TestType>(json);
|
||||
Assert.Equal("ColorTest", deserializedValue.Name);
|
||||
Assert.Equal(Color.Green, deserializedValue.Color);
|
||||
}
|
||||
|
||||
private record TestType(string Name, Color Color);
|
||||
|
||||
[JsonConverter(typeof(GenericEnumJsonConverter<Color>))]
|
||||
private enum Color {
|
||||
[EnumMember(Value="definitely-not-red")]
|
||||
Red,
|
||||
Green };
|
||||
}
|
|
@ -12,31 +12,57 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Jobs.Extensions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Dapr.Jobs.Test.Extensions;
|
||||
|
||||
public class DaprJobsServiceCollectionExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void AddDaprJobsClient_FromIConfiguration()
|
||||
{
|
||||
const string apiToken = "abc123";
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string> { { "DAPR_API_TOKEN", apiToken } })
|
||||
.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
|
||||
services.AddDaprJobsClient();
|
||||
|
||||
var app = services.BuildServiceProvider();
|
||||
|
||||
var jobsClient = app.GetRequiredService<DaprJobsClient>() as DaprJobsGrpcClient;
|
||||
|
||||
Assert.NotNull(jobsClient!.DaprApiToken);
|
||||
Assert.Equal(apiToken, jobsClient.DaprApiToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddDaprJobsClient_RegistersDaprClientOnlyOnce()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
var clientBuilder = new Action<DaprJobsClientBuilder>(builder =>
|
||||
builder.UseDaprApiToken("abc"));
|
||||
var clientBuilder = new Action<IServiceProvider, DaprJobsClientBuilder>((sp, builder) =>
|
||||
{
|
||||
builder.UseDaprApiToken("abc");
|
||||
});
|
||||
|
||||
services.AddDaprJobsClient(); //Sets a default API token value of an empty string
|
||||
services.AddDaprJobsClient(clientBuilder); //Sets the API token value
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
var daprJobClient = serviceProvider.GetService<DaprJobsClient>() as DaprJobsGrpcClient;
|
||||
|
||||
Assert.Null(daprJobClient!.apiTokenHeader);
|
||||
Assert.False(daprJobClient.httpClient.DefaultRequestHeaders.TryGetValues("dapr-api-token", out var _));
|
||||
|
||||
Assert.NotNull(daprJobClient!.HttpClient);
|
||||
Assert.False(daprJobClient.HttpClient.DefaultRequestHeaders.TryGetValues("dapr-api-token", out var _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -63,8 +89,8 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
services.AddDaprJobsClient((provider, builder) =>
|
||||
{
|
||||
var configProvider = provider.GetRequiredService<TestSecretRetriever>();
|
||||
var daprApiToken = configProvider.GetApiTokenValue();
|
||||
builder.UseDaprApiToken(daprApiToken);
|
||||
var apiToken = TestSecretRetriever.GetApiTokenValue();
|
||||
builder.UseDaprApiToken(apiToken);
|
||||
});
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
@ -72,10 +98,15 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
|
||||
//Validate it's set on the GrpcClient - note that it doesn't get set on the HttpClient
|
||||
Assert.NotNull(client);
|
||||
Assert.NotNull(client.apiTokenHeader);
|
||||
Assert.True(client.apiTokenHeader.HasValue);
|
||||
Assert.Equal("dapr-api-token", client.apiTokenHeader.Value.Key);
|
||||
Assert.Equal("abcdef", client.apiTokenHeader.Value.Value);
|
||||
Assert.NotNull(client.DaprApiToken);
|
||||
Assert.Equal("abcdef", client.DaprApiToken);
|
||||
Assert.NotNull(client.HttpClient);
|
||||
|
||||
if (!client.HttpClient.DefaultRequestHeaders.TryGetValues("dapr-api-token", out var daprApiToken))
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
Assert.Equal("abcdef", daprApiToken.FirstOrDefault());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -83,7 +114,7 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddDaprJobsClient(options => { }, ServiceLifetime.Singleton);
|
||||
services.AddDaprJobsClient((serviceProvider, options) => { }, ServiceLifetime.Singleton);
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var daprJobsClient1 = serviceProvider.GetService<DaprJobsClient>();
|
||||
|
@ -100,7 +131,7 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddDaprJobsClient(options => { }, ServiceLifetime.Scoped);
|
||||
services.AddDaprJobsClient((serviceProvider, options) => { }, ServiceLifetime.Scoped);
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
await using var scope1 = serviceProvider.CreateAsyncScope();
|
||||
|
@ -119,7 +150,7 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddDaprJobsClient(options => { }, ServiceLifetime.Transient);
|
||||
services.AddDaprJobsClient((serviceProvider, options) => { }, ServiceLifetime.Transient);
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var daprJobsClient1 = serviceProvider.GetService<DaprJobsClient>();
|
||||
|
@ -132,6 +163,6 @@ public class DaprJobsServiceCollectionExtensionsTest
|
|||
|
||||
private class TestSecretRetriever
|
||||
{
|
||||
public string GetApiTokenValue() => "abcdef";
|
||||
public static string GetApiTokenValue() => "abcdef";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue