usingvarapi=newOpenAiApi("API_KEY");stringresponse=awaitapi.Chat.CreateChatCompletionAsync(messages:["Generate five random words."],model:ModelIdsSharedEnum.Gpt4oMini);Console.WriteLine(response);// "apple, banana, cherry, date, elderberry"varenumerable=api.Chat.CreateChatCompletionAsStreamAsync(messages:["Generate five random words."],model:ModelIdsSharedEnum.Gpt4oMini);awaitforeach(stringresponseinenumerable){Console.WriteLine(response);}
It uses three implicit conversions:
- from string to ChatCompletionRequestUserMessage. It will always be converted to the user message.
- from ChatCompletionResponseMessage to string . It will always contain the first choice message content.
- from CreateChatCompletionStreamResponse to string . It will always contain the first delta content.
You still can use the full response objects if you need more information, just replace string response to var response.
usingOpenAI;usingCSharpToJsonSchema;publicenumUnit{Celsius,Fahrenheit,}publicclassWeather{publicstringLocation{get;set;}=string.Empty;publicdoubleTemperature{get;set;}publicUnitUnit{get;set;}publicstringDescription{get;set;}=string.Empty;}[GenerateJsonSchema(Strict = true)]// false by default. You can't use parameters with default values in Strict mode.publicinterfaceIWeatherFunctions{[Description("Get the current weather in a given location")]publicTask<Weather>GetCurrentWeatherAsync([Description("The city and state, e.g. San Francisco, CA")]stringlocation,Unitunit,CancellationTokencancellationToken=default);}publicclassWeatherService:IWeatherFunctions{publicTask<Weather>GetCurrentWeatherAsync(stringlocation,Unitunit=Unit.Celsius,CancellationTokencancellationToken=default){returnTask.FromResult(newWeather{Location=location,Temperature=22.0,Unit=unit,Description="Sunny",});}}usingvarapi=newOpenAiApi("API_KEY");varservice=newWeatherService();vartools=service.AsTools().AsOpenAiTools();varmessages=newList<ChatCompletionRequestMessage>{"You are a helpful weather assistant.".AsSystemMessage(),"What is the current temperature in Dubai, UAE in Celsius?".AsUserMessage(),};varmodel=ModelIdsSharedEnum.Gpt4oMini;varresult=awaitapi.Chat.CreateChatCompletionAsync(messages,model:model,tools:tools);varresultMessage=result.Choices.First().Message;messages.Add(resultMessage.AsRequestMessage());foreach(varcallinresultMessage.ToolCalls){varjson=awaitservice.CallAsync(functionName:call.Function.Name,argumentsAsJson:call.Function.Arguments);messages.Add(json.AsToolMessage(call.Id));}varresult=awaitapi.Chat.CreateChatCompletionAsync(messages,model:model,tools:tools);varresultMessage=result.Choices.First().Message;messages.Add(resultMessage.AsRequestMessage());
1 2 3 4 5 6 7 8 91011
> System:
You are a helpful weather assistant.
> User:
What is the current temperature in Dubai, UAE in Celsius?
> Assistant:
call_3sptsiHzKnaxF8bs8BWxPo0B:
GetCurrentWeather({"location":"Dubai, UAE","unit":"celsius"})
> Tool(call_3sptsiHzKnaxF8bs8BWxPo0B):
{"location":"Dubai, UAE","temperature":22,"unit":"celsius","description":"Sunny"}
> Assistant:
The current temperature in Dubai, UAE is 22°C with sunny weather.
Structured Outputs
1 2 3 4 5 6 7 8 910111213141516171819
usingOpenAI;usingvarapi=newOpenAiApi("API_KEY");varresponse=awaitapi.Chat.CreateChatCompletionAsAsync<Weather>(messages:["Generate random weather."],model:ModelIdsSharedEnum.Gpt4oMini,jsonSerializerOptions:newJsonSerializerOptions{Converters={newJsonStringEnumConverter()},});// or (if you need trimmable/NativeAOT version)varresponse=awaitapi.Chat.CreateChatCompletionAsAsync(jsonTypeInfo:SourceGeneratedContext.Default.Weather,messages:["Generate random weather."],model:ModelIdsSharedEnum.Gpt4oMini);// response.Value1 contains the structured output// response.Value2 contains the CreateChatCompletionResponse object
1234567
Weather:
Location: San Francisco, CA
Temperature: 65
Unit: Fahrenheit
Description: Partly cloudy with a light breeze and occasional sunshine.
Raw Response:
{"Location":"San Francisco, CA","Temperature":65,"Unit":"Fahrenheit","Description":"Partly cloudy with a light breeze and occasional sunshine."}
usingOpenAI;usingMicrosoft.Extensions.AI;// Works with OpenAI and all CustomProviders (Azure, DeepSeek, Groq, etc.)usingvarclient=newOpenAiClient("API_KEY");// or: using var client = CustomProviders.Groq("API_KEY");// IChatClientIChatClientchatClient=client;varresponse=awaitchatClient.GetResponseAsync("Say hello!",newChatOptions{ModelId="gpt-4o-mini"});Console.WriteLine(response.Messages[0].Text);// Streamingawaitforeach(varupdateinchatClient.GetStreamingResponseAsync("Count to 5.",newChatOptions{ModelId="gpt-4o-mini"})){Console.Write(string.Concat(update.Contents.OfType<TextContent>().Select(c=>c.Text)));}// IEmbeddingGeneratorIEmbeddingGenerator<string,Embedding<float>>generator=client;varembeddings=awaitgenerator.GenerateAsync(["Hello, world!"],newEmbeddingGenerationOptions{ModelId="text-embedding-3-small"});
Routed chat client
For OpenAI-compatible providers, you can build one routed IChatClient with aliases like smart, smart-any, fast, and cheap.
usingMicrosoft.Extensions.AI;usingtryAGI.OpenAI;usingvarrouted=newOpenAiRoutedChatClientBuilder().AddProvider("cerebras",CustomProviders.Cerebras("CEREBRAS_API_KEY"),provider=>provider.AddModel("qwen-3-235b-a22b-instruct-2507",model=>model.AsSmart(priority:100).AsSmartAny(priority:100).SupportsToolCalls().SupportsStructuredOutputs().IsRecurringFree())).AddProvider("groq",CustomProviders.Groq("GROQ_API_KEY"),provider=>provider.AddModel("llama-3.3-70b-versatile",model=>model.AsFast(priority:100).AsCheap(priority:70).AsSmartAny(priority:60).SupportsToolCalls())).AddProvider("openrouter",CustomProviders.OpenRouter("OPENROUTER_API_KEY"),provider=>provider.WithRateLimitCooldown(TimeSpan.FromMinutes(2)).AddModel("openrouter/free",model=>model.AsCheap(priority:100).AsSmartAny(priority:40))).Build();IChatClientchatClient=routed;varresponse=awaitchatClient.GetResponseAsync("Explain the tradeoffs of vector search vs keyword search.",newChatOptions{ModelId=OpenAiModelAliases.Smart});Console.WriteLine(response.Messages[0].Text);
Notes:
- This router is for providers exposed through CustomProviders, i.e. OpenAI-compatible endpoints.
- Provider cooldowns are tracked automatically from 429 responses and common rate-limit headers.
- If smart is exhausted, the router also considers models tagged with smart-any.
FreeLLM
FreeLLM is a separate package in this repo. It depends on tryAGI.OpenAI and Google_Gemini, and gives you one routed chat client across OpenAI-compatible providers and Gemini.
usingMicrosoft.Extensions.AI;usingFreeLLM;usingtryAGI.OpenAI;usingvarclient=newFreeLlmClientBuilder().WithCatalogRefresh(TimeSpan.FromHours(6),routeDiscoveredModels:true)// Curated defaults are applied automatically for popular providers..AddCerebras("CEREBRAS_API_KEY").AddGemini("GEMINI_API_KEY",provider=>provider.WithPriority(320).AddModel("gemini-2.5-flash",model=>model.AsSmart(priority:190).AsSmartAny(priority:190).AsFast(priority:140)).AddModel("gemini-2.5-flash-lite",model=>model.AsCheap(priority:220))).AddOpenRouter("OPENROUTER_API_KEY",provider=>provider.WithPriority(90).RemoveModel("openrouter/free").AddModel("openrouter/free",model=>model.AsCheap(priority:250))).Build();// OpenAI-compatible chat completions APIvarraw=awaitclient.Chat.CreateChatCompletionAsync(newCreateChatCompletionRequest{Value2=newCreateChatCompletionRequestVariant2{Model=FreeLlmModelAliases.Smart,Messages=["Explain vector search vs keyword search."],},});// Microsoft.Extensions.AI APIIChatClientchatClient=client;varmeai=awaitchatClient.GetResponseAsync("Explain vector search vs keyword search.",newChatOptions{ModelId=FreeLlmModelAliases.SmartAny});Console.WriteLine(raw.Choices[0].Message.Content);Console.WriteLine(meai.Messages[0].Text);varcatalogs=client.GetProviderCatalogs();foreach(varcatalogincatalogs){Console.WriteLine($"{catalog.ProviderId}: refreshed={catalog.RefreshedAt:O}, missing={string.Join(",", catalog.MissingConfiguredModelIds)}");}
Notes:
- FreeLlmModelAliases includes smart, smart-any, fast, and cheap.
- Convenience methods for Gemini, Cerebras, SambaNova, OpenRouter, GitHub Models, Groq, and NVIDIA register curated default models and priorities.
- Use provider.WithPriority(...) to bias whole providers, and model.AsSmart(...), model.AsCheap(...), model.AsFast(...), and model.AsSmartAny(...) to tune alias-specific model priority.
- Use builder.WithCatalogRefresh(...) or provider.WithCatalogRefresh(...) to periodically call provider model catalogs, mark configured models as missing when they disappear, and optionally route newly discovered models when routeDiscoveredModels: true.
- Use client.RefreshProviderCatalogsAsync() to force a catalog refresh and client.GetProviderCatalogs() to inspect configured, discovered, present, missing, and routable models per provider.
- Use provider.ClearModels() or pass useDefaultModels: false to a convenience method if you want a fully manual model list.
- provider.AddModel("existing-model", ...) updates preset models in place, so you can override defaults without duplicating registrations.
- client.Chat preserves raw OpenAI-compatible requests for OpenAI-compatible providers and translates supported chat-completions requests to Gemini when a Gemini model wins routing.
- Gemini translation currently supports CreateChatCompletionRequestVariant2, single-choice text chat, JSON response formats, and data-URI images.
- Raw OpenAI tool schemas/functions, audio/modalities, logprobs, web search, prediction, and remote image URLs are not translated to Gemini; use the MEAI surface for Gemini tool calling.
- Provider cooldowns and last-seen rate-limit data are available through client.GetProviderStatuses().
Constants
All tryGetXXX methods return null if the value is not found.
There also non-try methods that throw an exception if the value is not found.
usingOpenAI;// You can try to get the enum from string using:varmodel=ModelIdsSharedEnumExtensions.ToEnum("gpt-4o")??thrownewException("Invalid model");// Chatvarmodel=ModelIdsSharedEnum.Gpt4oMini;double?priceInUsd=model.TryGetPriceInUsd(inputTokens:500,outputTokens:500)double?priceInUsd=model.TryGetFineTunePriceInUsd(trainingTokens:500,inputTokens:500,outputTokens:500)intcontextLength=model.TryGetContextLength()// 128_000intoutputLength=model.TryGetOutputLength()// 16_000// Embeddingsvarmodel=CreateEmbeddingRequestModel.TextEmbedding3Small;int?maxInputTokens=model.TryGetMaxInputTokens()// 8191double?priceInUsd=model.TryGetPriceInUsd(tokens:500)// Imagesdouble?priceInUsd=CreateImageRequestModel.DallE3.TryGetPriceInUsd(size:CreateImageRequestSize.x1024x1024,quality:CreateImageRequestQuality.Hd)// Speech to Textdouble?priceInUsd=CreateTranscriptionRequestModel.Whisper1.TryGetPriceInUsd(seconds:60)// Text to Speechdouble?priceInUsd=CreateSpeechRequestModel.Tts1Hd.TryGetPriceInUsd(characters:1000)
Chat Completion
Send a simple chat completion request.
1 2 3 4 5 6 7 8 910111213
usingvarclient=newOpenAiClient(apiKey);stringresponse=awaitclient.Chat.CreateChatCompletionAsync(newCreateChatCompletionRequest{CreateChatCompletionRequestVariant2=newCreateChatCompletionRequestVariant2{Messages=["Generate five random words."],Model="gpt-4o-mini",}});Console.WriteLine(response);
Chat Completion Streaming
Stream a chat completion response token by token.
1 2 3 4 5 6 7 8 910111213141516
usingvarclient=newOpenAiClient(apiKey);varenumerable=client.Chat.CreateChatCompletionAsStreamAsync(newCreateChatCompletionRequest{CreateChatCompletionRequestVariant2=newCreateChatCompletionRequestVariant2{Messages=["Generate five random words."],Model="gpt-4o-mini",}});awaitforeach(stringresponseinenumerable){Console.Write(response);}
Chat With Vision
Send an image to the model for analysis.
1 2 3 4 5 6 7 8 910111213141516
usingvarclient=newOpenAiClient(apiKey);CreateChatCompletionResponseresponse=awaitclient.Chat.CreateChatCompletionAsync(newCreateChatCompletionRequest{CreateChatCompletionRequestVariant2=newCreateChatCompletionRequestVariant2{Messages=["Please describe the following image.",H.Resources.images_dog_and_cat_png.AsBytes().AsUserMessage(mimeType:"image/png"),],Model="gpt-4o-mini",}});Console.WriteLine(response.Choices[0].Message.Content);
JSON Response Format
Request a response in JSON format.
1 2 3 4 5 6 7 8 91011121314151617
usingvarclient=newOpenAiClient(apiKey);stringresponse=awaitclient.Chat.CreateChatCompletionAsync(newCreateChatCompletionRequest{CreateChatCompletionRequestVariant2=newCreateChatCompletionRequestVariant2{Messages=["Generate five random words as json."],Model="gpt-4o-mini",ResponseFormat=newResponseFormatJsonObject{Type=ResponseFormatJsonObjectType.JsonObject,},}});Console.WriteLine(response);
Structured Outputs
Get structured JSON responses using a C# type as the schema.
1 2 3 4 5 6 7 8 91011
usingvarclient=newOpenAiClient(apiKey);varresponse=awaitclient.Chat.CreateChatCompletionAsAsync<WordsResponse>(messages:["Generate five random words as json."],model:"gpt-4o-mini");Console.WriteLine("Words:");foreach(varwordinresponse.Value1!.Words){Console.WriteLine(word);}
Structured Outputs (AOT)
Get structured JSON responses using a JsonTypeInfo for AOT/trimming compatibility.
1 2 3 4 5 6 7 8 9101112
usingvarclient=newOpenAiClient(apiKey);varresponse=awaitclient.Chat.CreateChatCompletionAsAsync(jsonTypeInfo:SourceGeneratedContext.Default.WordsResponse,messages:["Generate five random words."],model:"gpt-4o-mini");Console.WriteLine("Words:");foreach(varwordinresponse.Value1!.Words){Console.WriteLine(word);}
usingvarclient=newOpenAiClient(apiKey);varresponse=awaitclient.Images.CreateImageAsync(prompt:"a white siamese cat",model:CreateImageRequestModel.GptImage1Mini,n:1,quality:CreateImageRequestQuality.Low,size:CreateImageRequestSize.x1024x1024,outputFormat:CreateImageRequestOutputFormat.Png);varbase64=response.Data?.ElementAt(0).B64Json;Console.WriteLine($"Generated image ({base64?.Length} base64 chars)");
Text To Speech
Convert text to speech audio using streaming.
1 2 3 4 5 6 7 8 910111213141516171819202122
usingvarclient=newOpenAiClient(apiKey);usingvarmemoryStream=newMemoryStream();awaitforeach(varstreamEventinclient.Audio.CreateSpeechAsEventStreamAsync(model:CreateSpeechRequestModel.Gpt4oMiniTts,input:"Hello! This is a text-to-speech test.",voice:AnyOf<AnyOf<VoiceIdsShared?,CreateSpeechRequestVoice?>?,CreateSpeechRequestVoice2>.FromValue1(AnyOf<VoiceIdsShared?,CreateSpeechRequestVoice?>.FromValue1((VoiceIdsShared)VoiceIdsSharedEnum.Alloy)),responseFormat:CreateSpeechRequestResponseFormat.Mp3,speed:1.0,streamFormat:CreateSpeechRequestStreamFormat.Sse)){if(streamEvent.SpeechAudioDeltais{}delta){byte[]chunk=Convert.FromBase64String(delta.Audio);memoryStream.Write(chunk,0,chunk.Length);}}byte[]audio=memoryStream.ToArray();Console.WriteLine($"Generated {audio.Length} bytes of audio.");
Realtime
Connect to the Realtime API with the generated WebSocket client and wait for the initial session event.
varapiKey=Environment.GetEnvironmentVariable("OPENAI_API_KEY")is{Length:>0}apiKeyValue?apiKeyValue:thrownewAssertInconclusiveException("OPENAI_API_KEY environment variable is not found.");varmodel=Environment.GetEnvironmentVariable("OPENAI_REALTIME_MODEL")is{Length:>0}modelValue?modelValue:"gpt-4o-realtime-preview";usingvarclient=newOpenAiRealtimeClient(apiKey);usingvarcancellationTokenSource=newCancellationTokenSource(TimeSpan.FromSeconds(30));varcancellationToken=cancellationTokenSource.Token;awaitclient.ConnectAsync(model:model,cancellationToken:cancellationToken);awaitforeach(varserverEventinclient.ReceiveUpdatesAsync(cancellationToken)){if(serverEvent.IsSessionCreated){Console.WriteLine(serverEvent.SessionCreated!.EventId);return;}if(serverEvent.IsError){if(serverEvent.Error!.Error.Message.Contains("Realtime Beta API is no longer supported",StringComparison.OrdinalIgnoreCase)){thrownewAssertInconclusiveException("The generated Realtime beta client no longer matches the live OpenAI GA Realtime endpoint.");}thrownewAssertFailedException($"Realtime connection returned an error: {serverEvent.Error!.Error.Message}");}}thrownewAssertFailedException("Realtime connection did not produce a session.created event before timing out.");
Use the Microsoft.Extensions.AI IChatClient interface for chat completions.
1 2 3 4 5 6 7 8 9101112131415
usingvarclient=newOpenAiClient(apiKey);// using Meai = Microsoft.Extensions.AI;Meai.IChatClientchatClient=client;varmessages=newList<Meai.ChatMessage>{new(Meai.ChatRole.User,"Say hello in exactly 3 words."),};varresponse=awaitchatClient.GetResponseAsync(messages,newMeai.ChatOptions{ModelId="gpt-4o-mini"});Console.WriteLine(response.Messages[0].Text);
MEAI Chat Streaming
Stream a chat completion using the Microsoft.Extensions.AI IChatClient interface.
1 2 3 4 5 6 7 8 91011121314151617181920
usingvarclient=newOpenAiClient(apiKey);// using Meai = Microsoft.Extensions.AI;Meai.IChatClientchatClient=client;varmessages=newList<Meai.ChatMessage>{new(Meai.ChatRole.User,"Count from 1 to 5."),};awaitforeach(varupdateinchatClient.GetStreamingResponseAsync(messages,newMeai.ChatOptions{ModelId="gpt-4o-mini"})){vartext=string.Concat(update.Contents.OfType<Meai.TextContent>().Select(c=>c.Text));if(!string.IsNullOrEmpty(text)){Console.Write(text);}}
MEAI Tool Calling
Use function/tool calling via the Microsoft.Extensions.AI IChatClient interface.
usingvarclient=newOpenAiClient(apiKey);// using Meai = Microsoft.Extensions.AI;Meai.IChatClientchatClient=client;vartool=Meai.AIFunctionFactory.Create((stringcity)=>cityswitch{"Paris"=>"22C, sunny","London"=>"15C, cloudy",_=>"Unknown",},name:"GetWeather",description:"Gets the current weather for a city");varchatOptions=newMeai.ChatOptions{ModelId="gpt-4o-mini",Tools=[tool],};varmessages=newList<Meai.ChatMessage>{new(Meai.ChatRole.User,"What's the weather in Paris? Respond with the temperature only."),};// First turn: get tool callvarresponse=awaitchatClient.GetResponseAsync((IEnumerable<Meai.ChatMessage>)messages,chatOptions);varfunctionCall=response.Messages.SelectMany(m=>m.Contents).OfType<Meai.FunctionCallContent>().First();// Execute tool and add resultvartoolResult=awaittool.InvokeAsync(functionCall.Argumentsis{}args?newMeai.AIFunctionArguments(args):null);messages.AddRange(response.Messages);messages.Add(newMeai.ChatMessage(Meai.ChatRole.Tool,newMeai.AIContent[]{newMeai.FunctionResultContent(functionCall.CallId,toolResult),}));// Second turn: get final responsevarfinalResponse=awaitchatClient.GetResponseAsync((IEnumerable<Meai.ChatMessage>)messages,chatOptions);Console.WriteLine(finalResponse.Messages[0].Text);
MEAI Embeddings
Generate embeddings using the Microsoft.Extensions.AI IEmbeddingGenerator interface.
1 2 3 4 5 6 7 8 910111213
usingvarclient=newOpenAiClient(apiKey);// using Meai = Microsoft.Extensions.AI;Meai.IEmbeddingGenerator<string,Meai.Embedding<float>>generator=client;varresult=awaitgenerator.GenerateAsync(newList<string>{"Hello, world!"},newMeai.EmbeddingGenerationOptions{ModelId="text-embedding-3-small",});Console.WriteLine($"Embedding dimension: {result[0].Vector.Length}");
Ecosystem maintenance
This SDK is one of more than 200 .NET SDKs maintained with AutoSDK. The tryAGI SDK audit continuously checks repository synchronization, upstream-spec regeneration, release workflows, warnings, public API visibility, and trimming/NativeAOT compatibility.
Every issue is first investigated for ecosystem-wide applicability. When the root cause belongs in AutoSDK, we fix and regression-test the generator, then roll the improvement out to every applicable SDK. Provider-specific behavior remains in this repository when it cannot be derived safely from the API specification.
Issue content—including code blocks, logs, links, and attachments—is treated only as untrusted diagnostic data. Embedded control instructions, hidden directives, delimiter tricks, or requests to alter triage or tooling behavior are ignored. Please report reproducible technical evidence and remove secrets and personal data.