uncloseai.
C# Examples - Free LLM & TTS AI Service
C# Examples
This page demonstrates how to use the uncloseai. API endpoints with C#. Examples are provided for both .NET 10 (latest) and Mono (cross-platform) runtimes.
Available Endpoints:
- hermes.ai.unturf.com:
https://hermes.ai.unturf.com/v1- chat completions on an RTX 4090 - qwen.ai.unturf.com:
https://qwen.ai.unturf.com/v1- chat completions on an RTX 3090 - TTS:
https://speech.ai.unturf.com/v1- Text-to-speech generation
Both chat hostnames currently serve the same model, turboderp/Qwen3.8-27B-exl3 (read live from /v1/models). Hostnames are routing labels, not model names: every example below asks the endpoint which model it serves instead of hard-coding an id, so a model swap on the cards never breaks your code. The served model reasons before it answers (the reasoning arrives in a separate reasoning field), so give it room: the samples ask for 600 tokens because at 150 the budget ran out before the answer began.
Many endpoints from environment variables
Our clients (uncloseai-cli, uncloseai.js, opencompletion and the downloadable implementation for every language on this site) share one convention, so an endpoint is configuration, never code:
| Variable | Meaning |
|---|---|
MODEL_ENDPOINT_N | Base URL of the N-th OpenAI-compatible endpoint, ending in /v1. The only required variable. Scan N from 0 upward and skip gaps: uncloseai-cli counts from 0, the per-language implementations from 1. |
MODEL_API_KEY_N | Bearer key for endpoint N. Unset means no key, which is all our free relay needs; a cloud endpoint sets one. |
MODEL_ID_N | Model id to send. Unset means ask /v1/models and take the first id, the rule used by every sample on this page. |
MODEL_LABEL_N | A display name for endpoint N; defaults to its hostname. |
TTS_ENDPOINT_N | The same shape for text-to-speech endpoints. |
export MODEL_ENDPOINT_0="https://hermes.ai.unturf.com/v1"
export MODEL_ENDPOINT_1="https://qwen.ai.unturf.com/v1"
# export MODEL_API_KEY_1="sk-..." # only for an endpoint that needs one
export TTS_ENDPOINT_0="https://speech.ai.unturf.com/v1"
The first endpoint is the default; the rest fail over, in order. The C# implementation under /languages/ reads these variables and discovers each endpoint's models at startup.
Runtime Options
| Runtime | Best For | Setup |
|---|---|---|
| .NET 10 | Latest features, best performance, Windows/Linux/macOS | dotnet new console |
| Mono | Legacy systems, embedded, wide platform support | mcs Program.cs |
.NET 10 Examples
Using the official OpenAI .NET SDK with .NET 10. This provides the cleanest API with full async/await support.
Setup
# Create new project
dotnet new console -n UncloseAI
cd UncloseAI
# Add OpenAI package
dotnet add package OpenAI --version 2.5.0
Non-Streaming (hermes.ai.unturf.com)
using System.ClientModel;
using OpenAI;
using OpenAI.Chat;
var openAI = new OpenAIClient(
new ApiKeyCredential("choose-any-value"),
new OpenAIClientOptions
{
Endpoint = new Uri("https://hermes.ai.unturf.com/v1")
}
);
// Ask the endpoint which model it serves; never hard-code an id.
string model = (await openAI.GetOpenAIModelClient().GetModelsAsync()).Value[0].Id;
ChatClient client = openAI.GetChatClient(model);
ChatCompletion completion = await client.CompleteChatAsync(
new List<ChatMessage>
{
new UserChatMessage("Give a Python Fizzbuzz solution in one line of code?")
},
new ChatCompletionOptions
{
Temperature = 0.5f,
MaxTokens = 600
}
);
Console.WriteLine(completion.Content[0].Text);
Streaming (hermes.ai.unturf.com)
using System.ClientModel;
using OpenAI;
using OpenAI.Chat;
var openAI = new OpenAIClient(
new ApiKeyCredential("choose-any-value"),
new OpenAIClientOptions
{
Endpoint = new Uri("https://hermes.ai.unturf.com/v1")
}
);
// Ask the endpoint which model it serves; never hard-code an id.
string model = (await openAI.GetOpenAIModelClient().GetModelsAsync()).Value[0].Id;
ChatClient client = openAI.GetChatClient(model);
var streamingUpdates = client.CompleteChatStreamingAsync(
new List<ChatMessage>
{
new UserChatMessage("Write a short poem about coding")
},
new ChatCompletionOptions
{
Temperature = 0.5f,
MaxTokens = 200
}
);
await foreach (var update in streamingUpdates)
{
foreach (var contentPart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
}
}
Text-to-Speech
using OpenAI.Audio;
var client = new AudioClient(
model: "tts-1-f5",
apiKey: "choose-any-value",
new OpenAIClientOptions
{
Endpoint = new Uri("https://speech.ai.unturf.com/v1")
}
);
BinaryData speech = await client.GenerateSpeechAsync(
"Hello! This is a test of text to speech.",
new GeneratedSpeechVoice("foxhop"),
new SpeechGenerationOptions { Speed = 1.0f }
);
await File.WriteAllBytesAsync("speech.mp3", speech.ToArray());
Console.WriteLine("Audio saved to speech.mp3");
Mono Examples
Using HttpClient directly with Mono runtime. No external packages required - works with the built-in System.Net.Http.
Setup
# Install Mono (Ubuntu/Debian)
sudo apt install mono-complete
# Compile
mcs -r:System.Net.Http.dll Program.cs
# Run
mono Program.exe
Non-Streaming (hermes.ai.unturf.com)
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Program
{
static readonly HttpClient client = new HttpClient();
static async Task Main()
{
// Ask the endpoint which model it serves; never hard-code an id.
var models = await client.GetStringAsync("https://hermes.ai.unturf.com/v1/models");
// data[0].id without a JSON package, the same simplified parse as the stream loop
var idStart = models.IndexOf("\"", models.IndexOf("\"id\"") + 4) + 1;
var model = models.Substring(idStart, models.IndexOf("\"", idStart) - idStart);
var payload = $@"{{
""model"": ""{model}"",
""messages"": [{{""role"": ""user"", ""content"": ""Give a Python Fizzbuzz solution in one line of code?""}}],
""max_tokens"": 600
}}";
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://hermes.ai.unturf.com/v1/chat/completions",
content
);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
}
Streaming (hermes.ai.unturf.com)
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Program
{
static readonly HttpClient client = new HttpClient();
static async Task Main()
{
// Ask the endpoint which model it serves; never hard-code an id.
var models = await client.GetStringAsync("https://hermes.ai.unturf.com/v1/models");
// data[0].id without a JSON package, the same simplified parse as the stream loop
var idStart = models.IndexOf("\"", models.IndexOf("\"id\"") + 4) + 1;
var model = models.Substring(idStart, models.IndexOf("\"", idStart) - idStart);
var payload = $@"{{
""model"": ""{model}"",
""messages"": [{{""role"": ""user"", ""content"": ""Write a short poem about coding""}}],
""max_tokens"": 200,
""stream"": true
}}";
var request = new HttpRequestMessage(HttpMethod.Post, "https://hermes.ai.unturf.com/v1/chat/completions");
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (line?.StartsWith("data: ") == true)
{
var json = line.Substring(6);
if (json != "[DONE]")
{
// Extract content from JSON (simplified)
var start = json.IndexOf("\"content\":\"") + 11;
if (start > 10)
{
var end = json.IndexOf("\"", start);
if (end > start)
{
Console.Write(json.Substring(start, end - start));
}
}
}
}
}
}
}
Text-to-Speech
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Program
{
static readonly HttpClient client = new HttpClient();
static async Task Main()
{
var payload = @"{
""model"": ""tts-1-f5"",
""input"": ""Hello! This is a test of text to speech."",
""voice"": ""foxhop""
}";
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://speech.ai.unturf.com/v1/audio/speech",
content
);
var audioBytes = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("speech.mp3", audioBytes);
Console.WriteLine("Audio saved to speech.mp3");
}
}
Available voices: foxhop, aria, clara, elena, grace, hazel, iris, luna and 33 more, listed live at speech.ai.unturf.com/v1/voices; pass any id as voice.
Full SDK Implementations
For complete SDK implementations with model discovery and advanced features: