銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

OpenFeature .NET SDKを試してみた(4) 1.5で導入されたInMemoryProviderを試してみる

しばらく触っていない間に v1.5.1 までリリースされていました。v1.5 で InMemoryProvider が追加され、ようやくflagdなどのProviderを別途用意せずに検証できるようになりました。

github.com

利用方法はドキュメントにもあるようにこれだけなのですが、これだけでは何もフラグ定義がはいっていないので、追加する必要があります。

await Api.Instance.SetProviderAsync(new InMemoryProvider());

フラグ定義はDictionary<string, Flag>型でコンストラクタかUpdateFlagsメソッドの引数で指定します。文字列型のフラグであれば次のようになります。

var flags = new Dictionary<string, Flag>
{
    {
        "myStringFlag", new Flag<string>(
            variants: new Dictionary<string, string>{
                { "key1", "val1" },
                { "key2", "val2" },
                { "key3", "val3" }
            },
            defaultVariant: "key1"
        )
    }
};

await Api.Instance.SetProviderAsync(new InMemoryProvider(flags));

//フラグを取得するとき
var strValue = await client.GetStringValue("myStringFlag", "foo");

Flagを定義する際、variantsとdefaultVariantが必須で、任意でcontextEvaluatorを指定できます。contextEvaluatorについてはContextを試す際にあわせて使ってみようと思います。

さて、数値型、ブーリアン型は文字列型と同様にstringをdoubleやboolに変えれば利用できます。OpenFeature .NETはこの他にDateTimeと今まで挙げた型を要素にするリストと辞書を格納できるようです。これらはobject型として取り扱うようになっていました。

まず、DateTimeのときです。variantsを定義するときに、Dictionary<string, Value>として、ValueのコンストラクタにDateTimeを指定します。ValueではなくDateTimeを直接指定すると、フラグを取り出すときにSDK内で行う型チェックで値が取得できなくなりました。 フラグを取り出すときは、GetObjectValue(もしくはGetObjectDetails)を呼び出し、object型で返ってきた値をAsDateTimeでDateTimeとして取り出します。ここはInMemoryProviderに限らず同じ処理になります。

var flags = new Dictionary<string, Flag>
{
    {
        "myDateTimeFlag", new Flag<Value>(
            variants: new Dictionary<string, Value>{
                {
                    "foo", new Value(DateTime.Now)
                },
                {
                    "bar", new Value(DateTime.MaxValue)
                }
            },
            defaultVariant: "foo"
        )
    }
};

await Api.Instance.SetProviderAsync(new InMemoryProvider(flags));

//フラグを取得するとき
var dateTimeValue = (await client.GetObjectValue("myDateTimeFlag", null)).AsDateTime;

リストや辞書の場合は次のようになります。

var flags = new Dictionary<string, Flag>
{
    {
        "myListFlag", new Flag<Value>(
            variants: new Dictionary<string, Value>{
                {"foo", new Value([new Value("foo1"), new Value("foo1")])},
                {"bar", new Value([new Value("bar1"), new Value("bar1")])}
            },
            defaultVariant: "foo"
        )
    },
    {
        "myStructureFlag", new Flag<Value>(
            variants: new Dictionary<string, Value>{
                {
                    "foo", new Value(new Structure(new Dictionary<string, Value>
                    {
                        { "foo1", new Value("foo1") },
                        { "foo2", new Value("foo2") }
                    }))
                },
                {
                    "bar", new Value(new Structure(new Dictionary<string, Value>
                    {
                        { "bar1", new Value("bar1") },
                        { "bar2", new Value("bar2") }
                    }))
                }
            },
            defaultVariant: "foo"
        )
    }
};

await Api.Instance.SetProviderAsync(new InMemoryProvider(flags));

//フラグを取得するとき
var listValue = (await client.GetObjectValue("myListFlag", null)).AsList;
var structureValue = (await client.GetObjectValue("myStructureFlag", null)).AsStructure;
foreach (var (k, v) in structureValue)
{
    //vがstringの場合
    Console.WriteLine($"{k}={v.AsString}");
}

まずリストの場合はValueのコンストラクタにIListもしくはIImmutableListを指定します。実装を見るとなぜこの2つなのかがわかります。上のコードだとコンストラクタが用意されているIListC#12で導入されたコレクション式で生成していることになります。

github.com

辞書型の場合は、辞書のデータをDictionary<string, Value>で用意してStructureのコンストラクタに与えて、StructureをValueのコンストラクタに与えます。マトリョーシカのような構造ですが、これで辞書型を格納したフラグを定義できました。取り出すときは AsStructure プロパティでStructureが取れて、IEnumerable<KeyValuePair<string, Value>>を実装しているのでforeach文などで取り出せます。

ちょっと定義に仕方にくせがある気がしますが、こんな感じになります。

MS LearnのAOAIのDALL-E 3を使用して画像を生成するサンプルを動かす方法

Azure OpenAI Serviceを試しているのですが、以下のページのDALL-E 3を使うC#のサンプルコードが動かなかったので、2024年2月25日現在で動かした手順を載せておきます。フィードバック済みです。

learn.microsoft.com

まず前提条件が足りていません。DALL-E 3モデルをデプロイしておく必要があるのですが、このためにはOpenAI serviceリソースをSweden Centralに作成しておく必要があります*1

OpenAI serviceを作成したら、ドキュメントの手順通りエンドポイントとキーをメモっておきます。

次にAzure OpenAI Studioに移動して、管理>デプロイメニューを開きます。ここで、dall-e-3 モデルのモデルがデプロイされていることを確認します。自分は最初からデプロイされていましたが、ない場合は新規にデプロイすればよいと思われます。ここで、デプロイ名をメモっておきます。

そして動いたサンプルコードは以下の通りです。

using Azure.AI.OpenAI;
using Azure;

var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY");

OpenAIClient client = new(new Uri(endpoint), new AzureKeyCredential(key));

var imageGenerations = await client.GetImageGenerationsAsync(
    new ImageGenerationOptions()
    {
        Prompt = "a happy monkey eating a banana, in watercolor",
        Size = ImageSize.Size1024x1024,
        DeploymentName = "Dalle3"
    });

// Image Generations responses provide URLs you can use to retrieve requested images
Uri imageUri = imageGenerations.Value.Data[0].Url;

// Print the image URI to console:
Console.WriteLine(imageUri);

Environment.GetEnvironmentVariable でないと動かないのは比較的わかりやすいですが、ImageGenerationOptions のプロパティに DeploymentName が指定されていないと例外がスローされます。値は先ほどメモっておいたdall-e-3モデルのデプロイ名です。また自分が試したときは Sizeプロパティが ImageSize.Size256x256 だとエラーになりました。

以上の手順でドキュメント通り、生成された画像のURLが表示されました。

*1:もしかしたらこのドキュメントはDALL-E 2向けかもしれませんが、いずれにせよこのドキュメントのままでは動きませんでした

.NET Aspire プレビュー3が公開されました

.NET Aspireの最新の機能などをまとめてみます。 現在はpreview3が最新です。 learn.microsoft.com

アップデートと破壊的変更

Aspireの開発ツールのアップデートは、Visual Studioを使う場合はVisual Studioをアップデートすれば自動的にアップデートされました。CLIを使う場合は手順に従ってコマンドを実行します。 既存のApisreアプリはNuGetの参照を更新することでアップデートできますが、すでに9.0.0系列のパッケージが公開されているため、最新ではなく 8.0.0-preview.3.24105.21 にアップデートするようにします。

また、builder.WithServiceBinding(...) APIが非推奨になり builder.WithEndpoint(...) に置き換える必要があります。次のプレビューで廃止される予定です。

ダッシュボード

ダッシュボードが独立した実行ファイルとして実行できるようになりました。開発時は今までとおなじように使えますが、運用環境に展開したあとにダッシュボードを独立して動作させることができるようになります。

learn.microsoft.com

その結果、ダッシュボード単体でOTLPのバックエンドとして動作し、メトリクス、トレース、ログを表示することが可能になりました。Aspire以外の、ほかの言語のアプリからであっても動作する見込みです。

github.com

また、ダッシュボードの機能そのものもローカリゼーションやアクセシビリティの改善、OTEL関連の改善などが加えられています。

コンポーネント

以下のコンポーネントが追加されました。

  • Azure AI OpenAI
  • Azure Kafka
  • Entity Frameworkを利用したOracle
  • Entity Frameworkを利用したMySQL

また以下のホスティングがサポートされAspireからプロビジョニングできるようになりました。

  • MySQL
  • CosmosDB

そのほか

Orleansをサポートしました。サンプルアプリが公開されています。そのほかにもサンプルアプリが追加されています。

github.com

コンテナランタイムとしてDockerに加えてPodmanもサポートしました。

github.com

Aspire上でNatsが動作するようになりました。Natsは分散システムのための接続テクノロジーで、M:N接続を容易に実現できるといったメリットがあります。