Add C and C# portal pages, dotnet10/mono variants, c# symlink
This commit is contained in:
parent
44a3876db6
commit
c40f5e0bc0
8 changed files with 1215 additions and 0 deletions
1
public/languages/c#
Symbolic link
1
public/languages/c#
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
csharp
|
||||
266
public/languages/c/index.html
Normal file
266
public/languages/c/index.html
Normal 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 →</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 →</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 →</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>© 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>
|
||||
18
public/languages/csharp/dotnet10/Dockerfile
Normal file
18
public/languages/csharp/dotnet10/Dockerfile
Normal 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"]
|
||||
306
public/languages/csharp/dotnet10/Uncloseai.cs
Normal file
306
public/languages/csharp/dotnet10/Uncloseai.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
8
public/languages/csharp/dotnet10/uncloseai.csproj
Normal file
8
public/languages/csharp/dotnet10/uncloseai.csproj
Normal 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>
|
||||
298
public/languages/csharp/index.html
Normal file
298
public/languages/csharp/index.html
Normal 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 →</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 →</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 →</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 →</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>© 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>
|
||||
12
public/languages/csharp/mono/Dockerfile
Normal file
12
public/languages/csharp/mono/Dockerfile
Normal 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"]
|
||||
306
public/languages/csharp/mono/Uncloseai.cs
Normal file
306
public/languages/csharp/mono/Uncloseai.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue