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-thinking-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()// 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);
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 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{Value2=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{Value2=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{Value2=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{Value2=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 9101112131415161718192021
usingvarclient=newOpenAiClient(apiKey);usingvarmemoryStream=newMemoryStream();awaitforeach(varstreamEventinclient.Audio.CreateSpeechAsync(model:CreateSpeechRequestModel.Gpt4oMiniTts,input:"Hello! This is a text-to-speech test.",voice:(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.");
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}");