Add C and C# portal pages, dotnet10/mono variants, c# symlink

This commit is contained in:
russell@unturf.com 2026-01-26 11:08:14 -05:00
parent 44a3876db6
commit c40f5e0bc0
8 changed files with 1215 additions and 0 deletions

1
public/languages/c# Symbolic link
View file

@ -0,0 +1 @@
csharp

View file

@ -0,0 +1,266 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#43a047">
<meta name="color-scheme" content="light dark">
<title>C Implementations | uncloseai.com</title>
<!-- PicoCSS -->
<link rel="stylesheet" href="/css/pico.classless.min.css">
<!-- ChunkFive Font -->
<link rel="stylesheet" href="/css/chunkfive/stylesheet.css" type="text/css" charset="utf-8" />
<!-- Sidebar Theme -->
<link rel="stylesheet" href="/css/sidebar-theme.css">
<!-- Highlight.js for syntax highlighting -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/a11y-dark.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<!-- Theme Switcher Script -->
<script>
function switchTheme(theme) {
if (theme === "auto") {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
}
// Disable custom styling for uncloseai.js - use PicoCSS instead
window.UNCLOSEAI_CUSTOM_STYLING = false;
</script>
<script src="https://uncloseai.com/uncloseai.js" type="module"></script>
</head>
<body>
<!-- Mobile menu toggle -->
<button class="sidebar-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')">
</button>
<!-- Left sidebar - Main Navigation -->
<aside class="sidebar">
<div class="table-of-contents">
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/languages/c/" class="active">C Implementations</a></li>
<li><a href="/csharp-examples.html">C# Examples</a></li>
<li><a href="/dart-examples.html">Dart Examples</a></li>
<li><a href="/go-examples.html">Go Examples</a></li>
<li><a href="/java-examples.html">Java Examples</a></li>
<li><a href="/kotlin-examples.html">Kotlin Examples</a></li>
<li><a href="/nodejs-examples.html">Node.js Examples</a></li>
<li><a href="/php-examples.html">PHP Examples</a></li>
<li><a href="/python-examples.html">Python Examples</a></li>
<li><a href="/ruby-examples.html">Ruby Examples</a></li>
<li><a href="/rust-examples.html">Rust Examples</a></li>
<li><a href="/uncloseai-js.html">uncloseai.js Docs</a></li>
<li><a href="/inference.html">Inference Setup</a></li>
<li><a href="/text-to-speech.html">Text-to-Speech</a></li>
<li><a href="/languages" target="_blank">All Languages</a></li>
<li><a href="https://shop.unturf.com/p/8486f492-a93e-11f0-b477-02dfe05770ee/uncloseai-machine-learning-reference-guide-to-inference-clients" target="_blank">Book</a></li>
</ul>
</nav>
</div>
</aside>
<!-- Main content -->
<main>
<header>
<hgroup>
<a href="https://uncloseai.com"><h1 class="unturf" style="font-family: 'ChunkFiveRegular';">uncloseai.</h1></a>
<p>C Implementations - OpenAI-Compatible API Clients</p>
</hgroup>
<nav>
<ul>
<li><a href="#" onclick="switchTheme('auto')">Auto</a></li>
<li><a href="#" onclick="switchTheme('light')">Light</a></li>
<li><a href="#" onclick="switchTheme('dark')">Dark</a></li>
</ul>
</nav>
</header>
<h2 id="c-intro">C Implementations</h2>
<p>Three different C implementations of the OpenAI-compatible API client, each using a different HTTP library. All implementations support streaming, dynamic model discovery via environment variables, and text-to-speech generation.</p>
<p><strong>Available Endpoints:</strong></p>
<ul>
<li><strong>Hermes:</strong> <code>https://hermes.ai.unturf.com/v1</code> - General purpose conversational AI</li>
<li><strong>Qwen 3 Coder:</strong> <code>https://qwen.ai.unturf.com/v1</code> - Specialized coding model</li>
<li><strong>TTS:</strong> <code>https://speech.ai.unturf.com/v1</code> - Text-to-speech generation</li>
</ul>
<h2 id="implementations">Available Implementations</h2>
<article>
<h3 id="curl">libcurl</h3>
<p><strong>Recommended for most use cases.</strong> Uses the widely-available libcurl library for HTTP requests. Well-documented, portable, and supports SSL/TLS out of the box.</p>
<ul>
<li><strong>Library:</strong> <a href="https://curl.se/libcurl/" target="_blank">libcurl</a></li>
<li><strong>Pros:</strong> Most portable, excellent documentation, widely available</li>
<li><strong>Cons:</strong> Callback-based API can be verbose</li>
</ul>
<p><a href="curl/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-c-curl languages/c/curl/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-c-curl</code></pre>
</article>
<article>
<h3 id="libsoup">libsoup (GNOME)</h3>
<p>Uses GNOME's libsoup HTTP library. Integrates well with GLib-based applications and provides a cleaner API for async operations.</p>
<ul>
<li><strong>Library:</strong> <a href="https://libsoup.org/" target="_blank">libsoup</a></li>
<li><strong>Pros:</strong> Clean GLib-style API, good async support, GNOME ecosystem integration</li>
<li><strong>Cons:</strong> Heavier dependency, mainly for GTK/GNOME apps</li>
</ul>
<p><a href="libsoup/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-c-libsoup languages/c/libsoup/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-c-libsoup</code></pre>
</article>
<article>
<h3 id="nghttp2">nghttp2 (HTTP/2)</h3>
<p>Uses nghttp2 for native HTTP/2 support. Ideal for high-performance applications that benefit from HTTP/2 multiplexing and header compression.</p>
<ul>
<li><strong>Library:</strong> <a href="https://nghttp2.org/" target="_blank">nghttp2</a></li>
<li><strong>Pros:</strong> Native HTTP/2, excellent performance, multiplexing support</li>
<li><strong>Cons:</strong> More complex setup, HTTP/2 specific</li>
</ul>
<p><a href="nghttp2/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-c-nghttp2 languages/c/nghttp2/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-c-nghttp2</code></pre>
</article>
<h2 id="features">Common Features</h2>
<p>All three implementations share these features:</p>
<ul>
<li><strong>Dynamic Model Discovery:</strong> Reads <code>MODEL_ENDPOINT_1</code> through <code>MODEL_ENDPOINT_9999</code> environment variables</li>
<li><strong>Streaming Support:</strong> Real-time response streaming via SSE parsing</li>
<li><strong>Text-to-Speech:</strong> Audio generation via <code>TTS_ENDPOINT_*</code> variables</li>
<li><strong>OpenAI Compatible:</strong> Works with vLLM, Ollama, and any OpenAI-compatible endpoint</li>
</ul>
<h2 id="api">API Structure</h2>
<pre><code class="c">// Client initialization
UncloseAIClient* uncloseai_init(void);
// Model discovery
int uncloseai_discover_models(UncloseAIClient *client);
// Chat completion (non-streaming)
char* uncloseai_chat(UncloseAIClient *client, int model_idx,
const char *system_msg, const char *user_msg,
int max_tokens);
// Chat completion (streaming)
int uncloseai_chat_stream(UncloseAIClient *client, int model_idx,
const char *system_msg, const char *user_msg,
int max_tokens, StreamCallback callback,
void *userdata);
// Text-to-speech
int uncloseai_tts(UncloseAIClient *client, const char *text,
const char *voice, const char *output_file);
// Cleanup
void uncloseai_free(UncloseAIClient *client);</code></pre>
<h2 id="source">Source Code</h2>
<p>View the full implementations in the git repository:</p>
<ul>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/c/curl" target="_blank">C with libcurl</a></li>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/c/libsoup" target="_blank">C with libsoup</a></li>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/c/nghttp2" target="_blank">C with nghttp2</a></li>
</ul>
<p>See the <strong>uncloseai. Machine Learning Reference Guide</strong> for complete documentation.</p>
<script>hljs.highlightAll();</script>
<footer>
<small>&copy; uncloseai. 2025</small>
<br>
<small>Stylesheets by <a href="https://picocss.com" target="_blank">PicoCSS</a></small>
<small>& <a href="https://highlightjs.org/" target="_blank">highlight.js</a></small>
<br>
<small><a href="/privacy-policy.html">Privacy Policy</a> | <a href="/terms-of-use.html">Terms of Use</a></small>
</footer>
<script>
// Simple active link highlighting
document.querySelectorAll('.table-of-contents a').forEach(link => {
link.addEventListener('click', function() {
document.querySelectorAll('.table-of-contents a').forEach(a => a.classList.remove('active'));
this.classList.add('active');
});
});
// Highlight current section on scroll
const sections = document.querySelectorAll('h2[id], h3[id]');
const navLinks = document.querySelectorAll('.table-of-contents a');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (window.scrollY >= sectionTop - 100) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + current) {
link.classList.add('active');
}
});
});
</script>
</main>
<!-- Right sidebar - Page TOC -->
<aside class="sidebar-right">
<div class="table-of-contents">
<nav>
<ul>
<li><a href="#c-intro" class="active">Overview</a></li>
<li><a href="#implementations">Implementations</a>
<ul>
<li><a href="#curl">libcurl</a></li>
<li><a href="#libsoup">libsoup</a></li>
<li><a href="#nghttp2">nghttp2</a></li>
</ul>
</li>
<li><a href="#features">Features</a></li>
<li><a href="#api">API Structure</a></li>
<li><a href="#source">Source Code</a></li>
</ul>
</nav>
</div>
</aside>
</body>
</html>

View file

@ -0,0 +1,18 @@
# .NET 10 Preview (checked 2026-01-26: mcr.microsoft.com/dotnet/sdk:10.0-preview is latest)
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview-alpine AS builder
WORKDIR /app
COPY uncloseai.csproj .
COPY Uncloseai.cs .
# Build the application
RUN dotnet build -c Release -o out
FROM mcr.microsoft.com/dotnet/runtime:10.0-preview-alpine
RUN apk add --no-cache ca-certificates
WORKDIR /app
COPY --from=builder /app/out .
CMD ["dotnet", "uncloseai.dll"]

View file

@ -0,0 +1,306 @@
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
class ModelInfo
{
public string Id { get; set; } = "";
public string Endpoint { get; set; } = "";
public int MaxTokens { get; set; } = 8192;
}
class Uncloseai
{
static readonly HttpClient client = new HttpClient();
static readonly List<ModelInfo> models = new List<ModelInfo>();
static readonly List<string> ttsEndpoints = new List<string>();
static async Task DiscoverModelsFromEndpoint(string endpoint)
{
Console.WriteLine($"Discovering models from: {endpoint}");
try
{
var response = await client.GetAsync($"{endpoint}/models");
var body = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
if (root.TryGetProperty("data", out var data))
{
foreach (var model in data.EnumerateArray())
{
var modelId = model.GetProperty("id").GetString() ?? "";
var maxTokens = 8192;
if (model.TryGetProperty("max_model_len", out var maxModelLen))
{
maxTokens = maxModelLen.GetInt32();
}
models.Add(new ModelInfo
{
Id = modelId,
Endpoint = endpoint,
MaxTokens = maxTokens
});
Console.WriteLine($" - Discovered: {modelId}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task DiscoverAllModels()
{
Console.WriteLine("=== Model Discovery ===");
// Discover chat/code models
for (int i = 1; i <= 9999; i++)
{
var endpoint = Environment.GetEnvironmentVariable($"MODEL_ENDPOINT_{i}");
if (string.IsNullOrEmpty(endpoint)) break;
await DiscoverModelsFromEndpoint(endpoint);
}
// Discover TTS endpoints
for (int i = 1; i <= 9999; i++)
{
var endpoint = Environment.GetEnvironmentVariable($"TTS_ENDPOINT_{i}");
if (string.IsNullOrEmpty(endpoint)) break;
Console.WriteLine($"Discovering TTS from: {endpoint}");
ttsEndpoints.Add(endpoint);
}
Console.WriteLine($"\n{models.Count} model(s) discovered");
Console.WriteLine($"{ttsEndpoints.Count} TTS endpoint(s) discovered\n");
}
static async Task<string> MakeChatRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
{
var model = models[modelIdx];
var url = $"{model.Endpoint}/chat/completions";
var payload = new
{
model = model.Id,
messages = new[]
{
new { role = "system", content = systemMsg },
new { role = "user", content = userMsg }
},
max_tokens = maxTokens
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
var body = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
return root.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString() ?? "";
}
static async Task MakeChatStreamRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
{
var model = models[modelIdx];
var url = $"{model.Endpoint}/chat/completions";
var payload = new
{
model = model.Id,
messages = new[]
{
new { role = "system", content = systemMsg },
new { role = "user", content = userMsg }
},
max_tokens = maxTokens,
stream = true
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = content;
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
Console.Write("Response: ");
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("data: "))
{
var data = line.Substring(6);
if (data == "[DONE]") break;
try
{
using var doc = JsonDocument.Parse(data);
var root = doc.RootElement;
if (root.TryGetProperty("choices", out var choices) && choices.GetArrayLength() > 0)
{
var choice = choices[0];
if (choice.TryGetProperty("delta", out var delta))
{
if (delta.TryGetProperty("content", out var contentProp))
{
var contentStr = contentProp.GetString();
if (!string.IsNullOrEmpty(contentStr))
{
Console.Write(contentStr);
}
}
}
}
}
catch
{
// Ignore parse errors
}
}
}
Console.WriteLine();
}
static async Task<string> MakeTtsRequest(string text)
{
if (ttsEndpoints.Count == 0)
return "ERROR: No TTS endpoints available";
var endpoint = ttsEndpoints[0];
var url = $"{endpoint}/audio/speech";
var payload = new
{
model = "tts-1",
voice = "alloy",
input = text
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
var audioData = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.mp3", audioData);
return $"Audio saved to output.mp3 ({audioData.Length} bytes)";
}
static async Task HermesExample()
{
Console.WriteLine("\n=== Non-Streaming Chat (using first discovered model) ===");
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models available");
return;
}
var model = models[0];
Console.WriteLine($"Model: {model.Id}");
Console.WriteLine($"Endpoint: {model.Endpoint}\n");
try
{
var response = await MakeChatRequest(
0,
"You are Hermes, a helpful AI assistant from Nous Research.",
"Explain quantum computing in one sentence.",
100
);
Console.WriteLine($"Response: {response}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task QwenStreamExample()
{
Console.WriteLine("\n=== Streaming Chat (using second or first model) ===");
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models available");
return;
}
var modelIdx = models.Count >= 2 ? 1 : 0;
var model = models[modelIdx];
Console.WriteLine($"Model: {model.Id}");
Console.WriteLine($"Endpoint: {model.Endpoint}\n");
try
{
await MakeChatStreamRequest(
modelIdx,
"You are Qwen, a coding assistant specialized in software development.",
"Write a hello world function in C#.",
200
);
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task TtsExample()
{
Console.WriteLine("\n=== TTS Speech Generation Example ===");
if (ttsEndpoints.Count == 0)
{
Console.WriteLine("ERROR: No TTS endpoints available");
return;
}
Console.WriteLine($"Endpoint: {ttsEndpoints[0]}\n");
try
{
var result = await MakeTtsRequest("Hello from C#! This is a text to speech example.");
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task Main(string[] args)
{
Console.WriteLine("C# AI API Examples (Dynamic Model Discovery)");
Console.WriteLine("=============================================");
Console.WriteLine();
await DiscoverAllModels();
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models discovered. Check environment variables:");
Console.WriteLine(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.");
Environment.Exit(1);
}
await HermesExample();
await QwenStreamExample();
await TtsExample();
}
}

View file

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,298 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#43a047">
<meta name="color-scheme" content="light dark">
<title>C# Implementations | uncloseai.com</title>
<!-- PicoCSS -->
<link rel="stylesheet" href="/css/pico.classless.min.css">
<!-- ChunkFive Font -->
<link rel="stylesheet" href="/css/chunkfive/stylesheet.css" type="text/css" charset="utf-8" />
<!-- Sidebar Theme -->
<link rel="stylesheet" href="/css/sidebar-theme.css">
<!-- Highlight.js for syntax highlighting -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/a11y-dark.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<!-- Theme Switcher Script -->
<script>
function switchTheme(theme) {
if (theme === "auto") {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
}
// Disable custom styling for uncloseai.js - use PicoCSS instead
window.UNCLOSEAI_CUSTOM_STYLING = false;
</script>
<script src="https://uncloseai.com/uncloseai.js" type="module"></script>
</head>
<body>
<!-- Mobile menu toggle -->
<button class="sidebar-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')">
</button>
<!-- Left sidebar - Main Navigation -->
<aside class="sidebar">
<div class="table-of-contents">
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/languages/c/">C Implementations</a></li>
<li><a href="/languages/csharp/" class="active">C# Implementations</a></li>
<li><a href="/csharp-examples.html">C# Examples</a></li>
<li><a href="/dart-examples.html">Dart Examples</a></li>
<li><a href="/go-examples.html">Go Examples</a></li>
<li><a href="/java-examples.html">Java Examples</a></li>
<li><a href="/kotlin-examples.html">Kotlin Examples</a></li>
<li><a href="/nodejs-examples.html">Node.js Examples</a></li>
<li><a href="/php-examples.html">PHP Examples</a></li>
<li><a href="/python-examples.html">Python Examples</a></li>
<li><a href="/ruby-examples.html">Ruby Examples</a></li>
<li><a href="/rust-examples.html">Rust Examples</a></li>
<li><a href="/uncloseai-js.html">uncloseai.js Docs</a></li>
<li><a href="/inference.html">Inference Setup</a></li>
<li><a href="/text-to-speech.html">Text-to-Speech</a></li>
<li><a href="/languages" target="_blank">All Languages</a></li>
<li><a href="https://shop.unturf.com/p/8486f492-a93e-11f0-b477-02dfe05770ee/uncloseai-machine-learning-reference-guide-to-inference-clients" target="_blank">Book</a></li>
</ul>
</nav>
</div>
</aside>
<!-- Main content -->
<main>
<header>
<hgroup>
<a href="https://uncloseai.com"><h1 class="unturf" style="font-family: 'ChunkFiveRegular';">uncloseai.</h1></a>
<p>C# Implementations - OpenAI-Compatible API Clients</p>
</hgroup>
<nav>
<ul>
<li><a href="#" onclick="switchTheme('auto')">Auto</a></li>
<li><a href="#" onclick="switchTheme('light')">Light</a></li>
<li><a href="#" onclick="switchTheme('dark')">Dark</a></li>
</ul>
</nav>
</header>
<h2 id="csharp-intro">C# Implementations</h2>
<p>Multiple C# implementations of the OpenAI-compatible API client, supporting different runtimes and SDK approaches. All implementations support streaming, dynamic model discovery via environment variables, and text-to-speech generation.</p>
<p><strong>URL Aliases:</strong> This directory is accessible via both <code>/languages/csharp/</code> and <code>/languages/c#/</code></p>
<p><strong>Available Endpoints:</strong></p>
<ul>
<li><strong>Hermes:</strong> <code>https://hermes.ai.unturf.com/v1</code> - General purpose conversational AI</li>
<li><strong>Qwen 3 Coder:</strong> <code>https://qwen.ai.unturf.com/v1</code> - Specialized coding model</li>
<li><strong>TTS:</strong> <code>https://speech.ai.unturf.com/v1</code> - Text-to-speech generation</li>
</ul>
<h2 id="implementations">Available Implementations</h2>
<article>
<h3 id="http">HttpClient (.NET 9)</h3>
<p><strong>Recommended for most use cases.</strong> Uses .NET's built-in HttpClient with System.Text.Json for direct HTTP API calls. No external dependencies required.</p>
<ul>
<li><strong>Runtime:</strong> .NET 9.0</li>
<li><strong>Pros:</strong> No external packages, lightweight, full control over HTTP</li>
<li><strong>Cons:</strong> More verbose than SDK approach</li>
</ul>
<p><a href="http/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-csharp-http languages/csharp/http/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-csharp-http</code></pre>
</article>
<article>
<h3 id="openai">OpenAI SDK (.NET 9)</h3>
<p>Uses the official OpenAI .NET SDK for a higher-level API. Simpler code with built-in types for messages, completions, and audio.</p>
<ul>
<li><strong>Runtime:</strong> .NET 9.0</li>
<li><strong>Package:</strong> <code>OpenAI</code> NuGet package</li>
<li><strong>Pros:</strong> Clean API, type-safe, official SDK support</li>
<li><strong>Cons:</strong> External dependency, SDK updates may lag API features</li>
</ul>
<p><a href="openai/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-csharp-openai languages/csharp/openai/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-csharp-openai</code></pre>
</article>
<article>
<h3 id="dotnet10">.NET 10 Preview</h3>
<p>Uses the latest .NET 10 preview runtime. Ideal for testing new language features and runtime improvements before general availability.</p>
<ul>
<li><strong>Runtime:</strong> .NET 10.0 Preview</li>
<li><strong>Pros:</strong> Latest C# features, performance improvements</li>
<li><strong>Cons:</strong> Preview/unstable, may have breaking changes</li>
</ul>
<p><a href="dotnet10/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-csharp-dotnet10 languages/csharp/dotnet10/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-csharp-dotnet10</code></pre>
</article>
<article>
<h3 id="mono">Mono</h3>
<p>Uses the Mono runtime for cross-platform compatibility. Ideal for environments where .NET Core/5+ isn't available or for legacy system integration.</p>
<ul>
<li><strong>Runtime:</strong> Mono 6.12</li>
<li><strong>Pros:</strong> Wide platform support, mature runtime, works on older systems</li>
<li><strong>Cons:</strong> Slower than .NET Core, fewer modern features</li>
</ul>
<p><a href="mono/">View Source Code &rarr;</a></p>
<pre><code class="bash"># Build with Docker
docker build -t uncloseai-csharp-mono languages/csharp/mono/
# Run
docker run -e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
uncloseai-csharp-mono</code></pre>
</article>
<h2 id="features">Common Features</h2>
<p>All implementations share these features:</p>
<ul>
<li><strong>Dynamic Model Discovery:</strong> Reads <code>MODEL_ENDPOINT_1</code> through <code>MODEL_ENDPOINT_9999</code> environment variables</li>
<li><strong>Streaming Support:</strong> Real-time response streaming via SSE parsing</li>
<li><strong>Text-to-Speech:</strong> Audio generation via <code>TTS_ENDPOINT_*</code> variables</li>
<li><strong>OpenAI Compatible:</strong> Works with vLLM, Ollama, and any OpenAI-compatible endpoint</li>
</ul>
<h2 id="choosing">Choosing an Implementation</h2>
<table>
<thead>
<tr>
<th>Implementation</th>
<th>Best For</th>
<th>Dependencies</th>
</tr>
</thead>
<tbody>
<tr>
<td>HttpClient</td>
<td>Production, minimal dependencies</td>
<td>None (built-in)</td>
</tr>
<tr>
<td>OpenAI SDK</td>
<td>Rapid development, type safety</td>
<td>OpenAI NuGet</td>
</tr>
<tr>
<td>.NET 10</td>
<td>Testing new features</td>
<td>None (built-in)</td>
</tr>
<tr>
<td>Mono</td>
<td>Legacy systems, wide platform support</td>
<td>None (built-in)</td>
</tr>
</tbody>
</table>
<h2 id="source">Source Code</h2>
<p>View the full implementations in the git repository:</p>
<ul>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/csharp/http" target="_blank">C# HttpClient (.NET 9)</a></li>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/csharp/openai" target="_blank">C# OpenAI SDK (.NET 9)</a></li>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/csharp/dotnet10" target="_blank">C# .NET 10 Preview</a></li>
<li><a href="https://git.unturf.com/engineering/unturf/uncloseai.com/-/tree/master/public/languages/csharp/mono" target="_blank">C# Mono</a></li>
</ul>
<p>See the <strong>uncloseai. Machine Learning Reference Guide</strong> for complete documentation.</p>
<script>hljs.highlightAll();</script>
<footer>
<small>&copy; uncloseai. 2025</small>
<br>
<small>Stylesheets by <a href="https://picocss.com" target="_blank">PicoCSS</a></small>
<small>& <a href="https://highlightjs.org/" target="_blank">highlight.js</a></small>
<br>
<small><a href="/privacy-policy.html">Privacy Policy</a> | <a href="/terms-of-use.html">Terms of Use</a></small>
</footer>
<script>
// Simple active link highlighting
document.querySelectorAll('.table-of-contents a').forEach(link => {
link.addEventListener('click', function() {
document.querySelectorAll('.table-of-contents a').forEach(a => a.classList.remove('active'));
this.classList.add('active');
});
});
// Highlight current section on scroll
const sections = document.querySelectorAll('h2[id], h3[id]');
const navLinks = document.querySelectorAll('.table-of-contents a');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (window.scrollY >= sectionTop - 100) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + current) {
link.classList.add('active');
}
});
});
</script>
</main>
<!-- Right sidebar - Page TOC -->
<aside class="sidebar-right">
<div class="table-of-contents">
<nav>
<ul>
<li><a href="#csharp-intro" class="active">Overview</a></li>
<li><a href="#implementations">Implementations</a>
<ul>
<li><a href="#http">HttpClient</a></li>
<li><a href="#openai">OpenAI SDK</a></li>
<li><a href="#dotnet10">.NET 10</a></li>
<li><a href="#mono">Mono</a></li>
</ul>
</li>
<li><a href="#features">Features</a></li>
<li><a href="#choosing">Choosing</a></li>
<li><a href="#source">Source Code</a></li>
</ul>
</nav>
</div>
</aside>
</body>
</html>

View file

@ -0,0 +1,12 @@
# Mono C# (checked 2026-01-26: mono:latest is 6.12)
FROM mono:latest
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY Uncloseai.cs .
# Compile with Mono's mcs compiler
RUN mcs -out:uncloseai.exe Uncloseai.cs -r:System.Net.Http.dll
CMD ["mono", "uncloseai.exe"]

View file

@ -0,0 +1,306 @@
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
class ModelInfo
{
public string Id { get; set; } = "";
public string Endpoint { get; set; } = "";
public int MaxTokens { get; set; } = 8192;
}
class Uncloseai
{
static readonly HttpClient client = new HttpClient();
static readonly List<ModelInfo> models = new List<ModelInfo>();
static readonly List<string> ttsEndpoints = new List<string>();
static async Task DiscoverModelsFromEndpoint(string endpoint)
{
Console.WriteLine($"Discovering models from: {endpoint}");
try
{
var response = await client.GetAsync($"{endpoint}/models");
var body = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
if (root.TryGetProperty("data", out var data))
{
foreach (var model in data.EnumerateArray())
{
var modelId = model.GetProperty("id").GetString() ?? "";
var maxTokens = 8192;
if (model.TryGetProperty("max_model_len", out var maxModelLen))
{
maxTokens = maxModelLen.GetInt32();
}
models.Add(new ModelInfo
{
Id = modelId,
Endpoint = endpoint,
MaxTokens = maxTokens
});
Console.WriteLine($" - Discovered: {modelId}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($" Error: {ex.Message}");
}
}
static async Task DiscoverAllModels()
{
Console.WriteLine("=== Model Discovery ===");
// Discover chat/code models
for (int i = 1; i <= 9999; i++)
{
var endpoint = Environment.GetEnvironmentVariable($"MODEL_ENDPOINT_{i}");
if (string.IsNullOrEmpty(endpoint)) break;
await DiscoverModelsFromEndpoint(endpoint);
}
// Discover TTS endpoints
for (int i = 1; i <= 9999; i++)
{
var endpoint = Environment.GetEnvironmentVariable($"TTS_ENDPOINT_{i}");
if (string.IsNullOrEmpty(endpoint)) break;
Console.WriteLine($"Discovering TTS from: {endpoint}");
ttsEndpoints.Add(endpoint);
}
Console.WriteLine($"\n{models.Count} model(s) discovered");
Console.WriteLine($"{ttsEndpoints.Count} TTS endpoint(s) discovered\n");
}
static async Task<string> MakeChatRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
{
var model = models[modelIdx];
var url = $"{model.Endpoint}/chat/completions";
var payload = new
{
model = model.Id,
messages = new[]
{
new { role = "system", content = systemMsg },
new { role = "user", content = userMsg }
},
max_tokens = maxTokens
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
var body = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
return root.GetProperty("choices")[0].GetProperty("message").GetProperty("content").GetString() ?? "";
}
static async Task MakeChatStreamRequest(int modelIdx, string systemMsg, string userMsg, int maxTokens)
{
var model = models[modelIdx];
var url = $"{model.Endpoint}/chat/completions";
var payload = new
{
model = model.Id,
messages = new[]
{
new { role = "system", content = systemMsg },
new { role = "user", content = userMsg }
},
max_tokens = maxTokens,
stream = true
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = content;
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
Console.Write("Response: ");
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("data: "))
{
var data = line.Substring(6);
if (data == "[DONE]") break;
try
{
using var doc = JsonDocument.Parse(data);
var root = doc.RootElement;
if (root.TryGetProperty("choices", out var choices) && choices.GetArrayLength() > 0)
{
var choice = choices[0];
if (choice.TryGetProperty("delta", out var delta))
{
if (delta.TryGetProperty("content", out var contentProp))
{
var contentStr = contentProp.GetString();
if (!string.IsNullOrEmpty(contentStr))
{
Console.Write(contentStr);
}
}
}
}
}
catch
{
// Ignore parse errors
}
}
}
Console.WriteLine();
}
static async Task<string> MakeTtsRequest(string text)
{
if (ttsEndpoints.Count == 0)
return "ERROR: No TTS endpoints available";
var endpoint = ttsEndpoints[0];
var url = $"{endpoint}/audio/speech";
var payload = new
{
model = "tts-1",
voice = "alloy",
input = text
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
var audioData = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.mp3", audioData);
return $"Audio saved to output.mp3 ({audioData.Length} bytes)";
}
static async Task HermesExample()
{
Console.WriteLine("\n=== Non-Streaming Chat (using first discovered model) ===");
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models available");
return;
}
var model = models[0];
Console.WriteLine($"Model: {model.Id}");
Console.WriteLine($"Endpoint: {model.Endpoint}\n");
try
{
var response = await MakeChatRequest(
0,
"You are Hermes, a helpful AI assistant from Nous Research.",
"Explain quantum computing in one sentence.",
100
);
Console.WriteLine($"Response: {response}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task QwenStreamExample()
{
Console.WriteLine("\n=== Streaming Chat (using second or first model) ===");
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models available");
return;
}
var modelIdx = models.Count >= 2 ? 1 : 0;
var model = models[modelIdx];
Console.WriteLine($"Model: {model.Id}");
Console.WriteLine($"Endpoint: {model.Endpoint}\n");
try
{
await MakeChatStreamRequest(
modelIdx,
"You are Qwen, a coding assistant specialized in software development.",
"Write a hello world function in C#.",
200
);
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task TtsExample()
{
Console.WriteLine("\n=== TTS Speech Generation Example ===");
if (ttsEndpoints.Count == 0)
{
Console.WriteLine("ERROR: No TTS endpoints available");
return;
}
Console.WriteLine($"Endpoint: {ttsEndpoints[0]}\n");
try
{
var result = await MakeTtsRequest("Hello from C#! This is a text to speech example.");
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static async Task Main(string[] args)
{
Console.WriteLine("C# AI API Examples (Dynamic Model Discovery)");
Console.WriteLine("=============================================");
Console.WriteLine();
await DiscoverAllModels();
if (models.Count == 0)
{
Console.WriteLine("ERROR: No models discovered. Check environment variables:");
Console.WriteLine(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.");
Environment.Exit(1);
}
await HermesExample();
await QwenStreamExample();
await TtsExample();
}
}