Generative AI library for .NET 9.0 with built-in OpenAI ChatGPT and Google Gemini API clients and support for C# function calling via reflection.
- Chat Completion
- Text Embedding
- Text-to-Speech
- Speech-to-Text
- Transcription
- Translation
- Moderation
- Response Streaming
- Function Calling
- Image Generation
- Assistants API
- Files API
- Chat Completion
- Function Calling
- Text Embedding
- Moderation
- Response Streaming
- Multi-Modal Requests
- Dependency Injection
- Time Awareness
- Message/Character Count Limiting
- Message Pinning
- Auto-Reattempt on Failure
- Token Counting
- XML Documentation
- Unit Tests
dotnet add package ChatAIze.GenerativeCS
Install-Package ChatAIze.GenerativeCS
using ChatAIze.GenerativeCS.Clients;
var openAIClient = new OpenAIClient("<OPENAI API KEY>");
var geminiClient = new GeminiClient("<GEMINI API KEY>");
using ChatAIze.GenerativeCS.Extensions;
builder.Services.AddOpenAIClient("<OPENAI API KEY>");
builder.Services.AddGeminiClient("<GEMINI API KEY>");
Note
By default, both OpenAIClient
and GeminiClient
services are registered as singleton. It's advised not to change global client options after the web application has already been launched. Use per-request options instead.
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
string response = await client.CompleteAsync("Write an article about Bitcoin.");
Console.WriteLine(response);
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
await foreach (string chunk in client.StreamCompletionAsync("Write an article about Bitcoin."))
{
Console.Write(chunk);
}
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Models;
var client = new OpenAIClient("<OPENAI API KEY>");
var chat = new Chat();
while (true)
{
string message = Console.ReadLine()!;
chat.FromUser(message);
string response = await client.CompleteAsync(chat);
Console.WriteLine(response);
}
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Models;
var client = new OpenAIClient("<OPENAI API KEY>");
var chat = new Chat();
while (true)
{
string message = Console.ReadLine()!;
chat.FromUser(message);
await foreach (string chunk in client.StreamCompletionAsync(chat))
{
Console.Write(chunk);
}
}
Note
Chatbot responses, function calls, and function results are automatically added to the chat.
You don't need to and should not call chat.FromAssistant(...)
manually, unless you want to inject custom messages (e.g. welcome message).
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
float[] vectorEmbedding = await client.GetEmbeddingAsync("The quick brown fox jumps over the lazy dog");
string base64Embedding = await client.GetBase64EmbeddingAsync("The quick brown fox jumps over the lazy dog");
var client = new OpenAIClient("<OPENAI API KEY>");
await client.SynthesizeSpeechAsync("The quick brown fox jumps over the lazy dog", "speech.mp3");
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
byte[] speech = await client.SynthesizeSpeechAsync("The quick brown fox jumps over the lazy dog");
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
string transcript = await client.TranscriptAsync("speech.mp3");
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
byte[] audio = await File.ReadAllBytesAsync("speech.mp3");
string transcript = await client.TranscriptAsync(audio);
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
string translation = await client.TranslateAsync("speech.mp3");
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
byte[] audio = await File.ReadAllBytesAsync("speech.mp3");
string translation = await client.TranslateAsync(audio);
using ChatAIze.GenerativeCS.Clients;
var client = new OpenAIClient("<OPENAI API KEY>");
var result = await client.ModerateAsync("I am going to blow up your house in Minecraft.");
Console.WriteLine(result.IsFlagged); // true
Console.WriteLine(result.IsViolence); // true
Console.WriteLine(result.ViolenceScore); // 0,908397912979126
Note
Per-request options take precedence over default client options.
Tip
If you use OpenAI client add:
using ChatAIze.GenerativeCS.Options.OpenAI;
If you use Gemini client add:
using ChatAIze.GenerativeCS.Options.Gemini;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Extensions;
builder.Services.AddOpenAIClient(configure =>
{
configure.ApiKey = "<OPENAI API KEY>";
configure.DefaultCompletionOptions = new ChatCompletionOptions()
{
Model = ChatCompletionModels.OpenAI.GPT4o,
Temperature = 1.0
// set other chat completion options here
};
configure.DefaultEmbeddingOptions = new EmbeddingOptions()
{
Model = EmbeddingModels.OpenAI.TextEmbedding3Large,
MaxAttempts = 5
// set other embeding options here
};
// set other options here
});
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Extensions;
builder.Services.AddGeminiClient(configure =>
{
configure.ApiKey = "<GEMINI API KEY>";
configure.DefaultCompletionOptions = new ChatCompletionOptions()
{
Model = ChatCompletionModels.Gemini.GeminiPro,
MessageLimit = 10
// set other chat completion options here
};
// set other options here
});
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Models;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new ChatCompletionOptions
{
Model = ChatCompletionModels.OpenAI.GPT4o,
UserTrackingId = "USER_ID_1234",
MaxAttempts = 5,
MaxOutputTokens = 2000,
MessageLimit = 10,
CharacterLimit = 20000,
Seed = 1234,
Temperature = 1.0,
TopP = 1,
FrequencyPenalty = 0.0,
PresencePenalty = 0.0,
IsJsonMode = false,
IsTimeAware = true,
StopWords = ["11.", "end"],
Functions = [new ChatFunction("ToggleDarkMode")],
DefaultFunctionCallback = async (name, arguments, cancellationToken) =>
{
await Console.Out.WriteLineAsync($"Function {name} called with arguments {arguments}");
return new { Success = true, Property1 = "ABC", Property2 = 123 };
},
AddMessageCallback = async (message) =>
{
// Called every time a new message is added, including function calls and results:
await Console.Out.WriteLineAsync($"Message added: {message}");
},
TimeCallback = () => DateTime.Now
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultCompletionOptions = options; // via property
// Set for single request:
string response = await client.CompleteAsync(prompt, options);
string response = await client.CompleteAsync(chat, options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Models;
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions
{
Model = ChatCompletionModels.Gemini.Gemini15Flash,
MaxAttempts = 5,
MessageLimit = 10,
CharacterLimit = 20000,
IsTimeAware = true,
Functions = [new ChatFunction("ToggleDarkMode")],
DefaultFunctionCallback = async (name, arguments, cancellationToken) =>
{
await Console.Out.WriteLineAsync($"Function {name} called with arguments {arguments}");
return new { Success = true, Property1 = "ABC", Property2 = 123 };
},
TimeCallback = () => DateTime.Now
};
// Set for entire client:
var client = new GeminiClient("<GEMINI API KEY>", options); // via constructor
client.DefaultCompletionOptions = options; // via property
// Set for single request:
string response = await client.CompleteAsync(prompt, options);
string response = await client.CompleteAsync(chat, options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new EmbeddingOptions
{
Model = EmbeddingModels.OpenAI.TextEmbedding3Large,
User = "USER_ID_1234",
MaxAttempts = 5
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultEmbeddingOptions = options; // via property
// Set for single request:
float[] embedding = await client.GetEmbeddingAsync("The quick brown fox jumps over the lazy dog", options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Enums;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new TextToSpeechOptions
{
Model = TextToSpeechModels.OpenAI.TTS1,
Voice = TextToSpeechVoice.Alloy,
Speed = 1.0,
MaxAttempts = 5,
ResponseFormat = VoiceResponseFormat.MP3
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultTextToSpeechOptions = options; // via property
// Set for single request:
await client.SynthesizeSpeechAsync("The quick brown fox jumps over the lazy dog", "speech.mp3", options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Enums;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new TranscriptionOptions
{
Model = SpeechRecognitionModels.OpenAI.Whisper1,
Language = "en",
Prompt = "ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, ...",
Temperature = 0.0,
MaxAttempts = 5,
ResponseFormat = TranscriptionResponseFormat.Text
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultTranscriptionOptions = options; // via property
// Set for single request:
string transcript = await client.TranscriptAsync("speech.mp3", options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Enums;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new TranslationOptions
{
Model = SpeechRecognitionModels.OpenAI.Whisper1,
Prompt = "ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, ...",
Temperature = 0.0,
MaxAttempts = 5,
ResponseFormat = TranscriptionResponseFormat.Text
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultTranslationOptions = options; // via property
// Set for single request:
string translation = await client.TranslateAsync("speech.mp3", options);
using ChatAIze.GenerativeCS.Clients;
using ChatAIze.GenerativeCS.Constants;
using ChatAIze.GenerativeCS.Options.OpenAI;
var options = new ModerationOptions
{
Model = ModerationModels.OpenAI.TextModerationStable,
MaxAttempts = 5
};
// Set for entire client:
var client = new OpenAIClient("<OPENAI API KEY>", options); // via constructor
client.DefaultModerationOptions = options; // via property
// Set for single request:
var result = await client.ModerateAsync("I am going going to blow up your house in Minecraft.", options);
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
void ToggleDarkMode(bool isOn)
{
Console.WriteLine($"Dark mode set to: {isOn}");
}
string GetCurrentWeather(string location)
{
return $"The weather in {location} is 72 degrees and sunny.";
}
async Task<object> SendEmailAsync(string recipient, string subject, string body)
{
await Task.Delay(3000);
return new { Success = true, Property1 = "ABC", Property2 = 123 };
}
var options = new ChatCompletionOptions();
options.AddFunction(ToggleDarkMode);
options.AddFunction(GetCurrentWeather);
options.AddFunction(SendEmailAsync);
using System.ComponentModel;
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions();
options.AddFunction(SmartHome.CheckFrontCamera);
options.AddFunction(SmartHome.SetFrontDoorLockAsync);
options.AddFunction(SmartHome.SetTemperature);
public static class SmartHome
{
[Description("Checks if there is someone waiting at the front door.")]
public static object CheckFrontCamera()
{
return new { Success = true, IsPersonDetected = true };
}
public static async Task SetFrontDoorLockAsync(bool isLocked)
{
await Task.Delay(3000);
Console.WriteLine($"Front door locked: {isLocked}");
}
public static void SetTemperature(string room, int temperature)
{
Console.WriteLine($"Temperature in {room} has been set to {temperature} degrees.");
}
}
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions();
var product = new Product();
options.AddFunction(product.GetDescription);
options.AddFunction(product.Rename);
options.AddFunction(product.Delete);
public class Product
{
public string? Name { get; set; }
public string GetDescription()
{
return $"This is a {Name}";
}
public void Rename(string name)
{
Name = name;
}
public void Delete()
{
Console.WriteLine($"Deleting product: {Name}");
}
}
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions();
options.AddFunction("GetCurrentWeather", (string location) =>
{
return "The current weather is sunny";
});
options.AddFunction("GetCurrentWeather", async () =>
{
await Task.Delay(3000);
return "The current weather is sunny";
});
options.AddFunction("GetCurrentWeather", "Gets the current weathe in default location.", async () =>
{
await Task.Delay(3000);
return new WeatherData(20, 50);
});
public record WeatherData(int Temperature, int Humidity);
using ChatAIze.GenerativeCS.Models;
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions();
options.AddFunction("GetUserLocation");
options.AddFunction("GetCurrentWeather", new FunctionParameter(typeof(string), "location"));
List<FunctionParameter> parameters = [new(typeof(string), "room"), new(typeof(int), "temperature")];
options.AddFunction("SetRoomTemperature", parameters);
options.DefaultFunctionCallback = async (name, parameters, cancellationToken) =>
{
if (name == "GetUserLocation")
{
return "London";
}
if (name == "GetCurrentWeather")
{
return new { Temperature = 20, Weather = "Sunny" };
}
if (name == "SetRoomTemperature")
{
await Task.Delay(3000, cancellationToken);
return new { IsSuccess = true };
}
return new { Error = $"Unknown function: {name}" };
};
You can configure both Gemini and OpenAI clients to be aware of the current date and time.
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions
{
IsTimeAware = true,
// other completion options
};
By default, GenerativeCS uses DateTime.Now
, but you can change the source of current time by specifying custom TimeCallback
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions
{
IsTimeAware = true,
TimeCallback = () => new DateTime(2024, 1, 14),
};
The maximum number of messages sent in a single chat completion request. The oldest messages will be removed one by one until the limit is satisfied.
- Pinned messages count toward the limit and have priority but are never truncated.
- The limit does include function calls and results.
- Function definitions are not considered messages.
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions
{
MessageLimit = 10,
};
The maximum number of characters sent in a single chat completion request. The oldest messages will be removed one by one until the limit is satisfied.
- Pinned messages count toward the limit and have priority but are never truncated.
- The limit does include function calls and results.
- Function definitions are not considered messages.
using ChatAIze.GenerativeCS.Options.OpenAI;
// or
using ChatAIze.GenerativeCS.Options.Gemini;
var options = new ChatCompletionOptions
{
CharacterLimit = 10,
};
Messages can be pinned to ensure they stay in the chat even when message and character limits are exceeded.
using ChatAIze.GenerativeCS.Enums;
using ChatAIze.GenerativeCS.Models;
var chat = new Chat();
chat.FromUser("This will always be the first message", PinLocation.Begin);
chat.FromSystem("This message will never be truncated due to limits.", PinLocation.Automatic);
chat.FromUser("This will always be the last (most recent) message", PinLocation.End);