.NET is Microsoft’s battle-tested framework trusted by Fortune 500 companies. It’s now easier than ever to build LLM apps. You get:
Battle-Tested Security
Built-in identity management, secret rotation, and compliance standards
Production Performance
High-throughput processing with advanced memory management
Azure Integration
Seamless Azure OpenAI and Active Directory support
Combined with Portkey’s enterprise features, you get everything needed for mission-critical LLM deployments. Monitor costs, ensure reliability, maintain compliance, and scale with confidence.
The OpenAI package does not support directly modifying the base URL or passing additional headers. So, we write a simple function to extend OpenAI’s ChatClient or EmbeddingClient to create a new PortkeyClient.
using OpenAI;using OpenAI.Chat;using System.ClientModel;using System.ClientModel.Primitives;public static class PortkeyClient{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static OpenAIClient CreateClient(Dictionary<string, string> headers) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options); } public static ChatClient CreateChatClient(Dictionary<string, string> headers, string model) { var client = CreateClient(headers); return client.GetChatClient(model); }}
using OpenAI;using OpenAI.Chat;using System.ClientModel;using System.ClientModel.Primitives;public static class PortkeyClient{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static OpenAIClient CreateClient(Dictionary<string, string> headers) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options); } public static ChatClient CreateChatClient(Dictionary<string, string> headers, string model) { var client = CreateClient(headers); return client.GetChatClient(model); }}
using OpenAI;using OpenAI.Embeddings;using System.ClientModel;using System.ClientModel.Primitives;public static class PortkeyClient{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static EmbeddingClient CreateEmbeddingClient(Dictionary<string, string> headers, string model) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options).GetEmbeddingClient(model); }}
After creating the extension above, you can pass any Portkey supported headers directly while creating the new client.
// Define Portkey headersvar headers = new Dictionary<string, string> { // Required headers { "x-portkey-api-key", "..." }, // Your Portkey API key { "x-portkey-virtual-key", "..." }, // Virtual key for provider // Optional headers { "x-portkey-trace-id", "my-app" }, // Custom trace identifier { "x-portkey-config", "..." }, // Send Config ID // Add any other Portkey headers as needed};// Create clientvar client = PortkeyClient.CreateChatClient( headers: headers, model: "gpt-4");// Make requestvar response = client.CompleteChat(new UserChatMessage("Yellow!"));Console.WriteLine(response.Value.Content[0].Text);
// Define Portkey headersvar headers = new Dictionary<string, string> { // Required headers { "x-portkey-api-key", "..." }, // Your Portkey API key { "x-portkey-virtual-key", "..." }, // Virtual key for provider // Optional headers { "x-portkey-trace-id", "my-app" }, // Custom trace identifier { "x-portkey-config", "..." }, // Send Config ID // Add any other Portkey headers as needed};// Create clientvar client = PortkeyClient.CreateChatClient( headers: headers, model: "gpt-4");// Make requestvar response = client.CompleteChat(new UserChatMessage("Yellow!"));Console.WriteLine(response.Value.Content[0].Text);
// Define Portkey headersvar headers = new Dictionary<string, string> { // Required headers { "x-portkey-api-key", "..." }, // Your Portkey API key { "x-portkey-virtual-key", "..." }, // Virtual key for provider // Optional headers { "x-portkey-trace-id", "..." }, // Custom trace identifier { "x-portkey-config", "..." }, // Send Config ID // Add any other Portkey headers as needed};// Create embedding client through Portkeyvar client = PortkeyClient.CreateEmbeddingClient( headers: headers, model: "text-embedding-3-large");// Text that we want to embedstring description = "Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa," + " and a really helpful concierge. The location is perfect -- right downtown, close to all the tourist" + " attractions. We highly recommend this hotel.";// Generate embeddingvar embeddingResult = client.GenerateEmbedding(description);var vector = embeddingResult.Value.ToFloats();Console.WriteLine($"Full embedding dimensions: {vector.Length}");
While we show common headers here, you can pass any Portkey-supported headers to enable features like custom metadata, fallbacks, caching, retries, and more.
using OpenAI;using OpenAI.Embeddings;using System.ClientModel;using System.ClientModel.Primitives;public static class PortkeyClient{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static EmbeddingClient CreateEmbeddingClient(Dictionary<string, string> headers, string model) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options).GetEmbeddingClient(model); }}class Program{ static void Main() { // Define Portkey headers var headers = new Dictionary<string, string> { // Required headers { "x-portkey-api-key", "..." }, // Your Portkey API key { "x-portkey-virtual-key", "..." }, // Virtual key for provider // Optional headers { "x-portkey-trace-id", "..." }, // Custom trace identifier { "x-portkey-config", "..." }, // Send Config ID // Add any other Portkey headers as needed }; // Create embedding client through Portkey var client = PortkeyClient.CreateEmbeddingClient( headers: headers, model: "text-embedding-3-large" ); // Text that we want to embed string description = "Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa," + " and a really helpful concierge. The location is perfect -- right downtown, close to all the tourist" + " attractions. We highly recommend this hotel."; // Generate embedding var embeddingResult = client.GenerateEmbedding(description); var vector = embeddingResult.Value.ToFloats(); Console.WriteLine($"Full embedding dimensions: {vector.Length}"); }}
We can make use of the Portkey client we created above to initialize the Semantic Kernel.
(Please make use of the CreateClient method and not CreateChatClient method to create the client)
using Microsoft.SemanticKernel;using Microsoft.SemanticKernel.ChatCompletion;public class Program{ public static async Task Main() { var headers = new Dictionary<string, string> { // Required headers { "x-portkey-api-key", "..." }, // Your Portkey API key { "x-portkey-virtual-key", "..." }, // Virtual key for provider // Optional headers // { "x-portkey-trace-id", "my-app" }, // Custom trace identifier // { "x-portkey-config", "..." }, // Send Config ID // Add any other Portkey headers as needed }; // Create client var client = PortkeyClient.CreateClient(headers); var builder = Kernel.CreateBuilder().AddOpenAIChatCompletion("gpt-4", client); Kernel kernel = builder.Build(); var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); var history = new ChatHistory(); // Initiate a back-and-forth chat string? userInput; do { // Collect user input Console.Write("User > "); userInput = Console.ReadLine(); // Add user input history.AddUserMessage(userInput); // Get the response from the AI var result = await chatCompletionService.GetChatMessageContentAsync( history, null, kernel: kernel); // Print the results Console.WriteLine("Assistant > " + result); // Add the message from the agent to the chat history history.AddMessage(result.Role, result.Content ?? string.Empty); } while (userInput is not null); }}
You can also use the PortkeyClient to send Async requests:
var completion = await client.CompleteChatAsync(new UserChatMessage("Hello!"));Console.WriteLine(completion.Value.Content[0].Text);
You can also use the PortkeyClient to send Async requests:
var completion = await client.CompleteChatAsync(new UserChatMessage("Hello!"));Console.WriteLine(completion.Value.Content[0].Text);
Use the SystemChatMessage and UserChatMessage properties from the OpenAI package to create multi-turn conversations:
var messages = new List<ChatMessage>{ new SystemChatMessage("You are a helpful assistant."), new UserChatMessage("What is the capital of France?")};var completion = client.CompleteChat(messages);messages.Add(new AssistantChatMessage(completion));
Switching providers is just a matter of swapping out your virtual key. Change the virtual key to Anthropic, set the model name, and start making requests to Anthropic from the OpenAI .NET library.
using OpenAI;using OpenAI.Chat;using System.ClientModel;using System.ClientModel.Primitives;public static class Portkey{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static ChatClient CreateChatClient(Dictionary<string, string> headers, string model) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options).GetChatClient(model); }}public class Program{ public static void Main() { var client = Portkey.CreateChatClient( headers: new Dictionary<string, string> { { "x-portkey-api-key", "PORTKEY API KEY" }, { "x-portkey-virtual-key", "ANTHROPIC VIRTUAL KEY" }, { "x-portkey-trace-id", "dotnet" } }, model: "claude-3-5-sonnet-20240620" ); Console.WriteLine(client.CompleteChat(new UserChatMessage("1729")).Value.Content[0].Text); }}
Similarly, just change your virtual key to Vertex virtual key:
using OpenAI;using OpenAI.Chat;using System.ClientModel;using System.ClientModel.Primitives;public static class Portkey{ private class HeaderPolicy : PipelinePolicy { private readonly Dictionary<string, string> _headers; public HeaderPolicy(Dictionary<string, string> headers) => _headers = headers; public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { foreach (var header in _headers) message.Request.Headers.Set(header.Key, header.Value); if (index < pipeline.Count) pipeline[index].Process(message, pipeline, index + 1); } public override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int index) { Process(message, pipeline, index); return ValueTask.CompletedTask; } } public static ChatClient CreateChatClient(Dictionary<string, string> headers, string model) { var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.portkey.ai/v1") }; options.AddPolicy(new HeaderPolicy(headers), PipelinePosition.PerCall); return new OpenAIClient(new ApiKeyCredential("dummy"), options).GetChatClient(model); }}public class Program{ public static void Main() { var client = Portkey.CreateChatClient( headers: new Dictionary<string, string> { { "x-portkey-api-key", "PORTKEY API KEY" }, { "x-portkey-virtual-key", "VERTEX AI VIRTUAL KEY" }, { "x-portkey-trace-id", "dotnet" } }, model: "gemini-1.5-pro-002" ); Console.WriteLine(client.CompleteChat(new UserChatMessage("1729")).Value.Content[0].Text); }}