From c40f5e0bc06b2651be42fddca6ed051f6936a01a Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 26 Jan 2026 11:08:14 -0500 Subject: [PATCH] Add C and C# portal pages, dotnet10/mono variants, c# symlink --- public/languages/c# | 1 + public/languages/c/index.html | 266 +++++++++++++++ public/languages/csharp/dotnet10/Dockerfile | 18 ++ public/languages/csharp/dotnet10/Uncloseai.cs | 306 ++++++++++++++++++ .../csharp/dotnet10/uncloseai.csproj | 8 + public/languages/csharp/index.html | 298 +++++++++++++++++ public/languages/csharp/mono/Dockerfile | 12 + public/languages/csharp/mono/Uncloseai.cs | 306 ++++++++++++++++++ 8 files changed, 1215 insertions(+) create mode 120000 public/languages/c# create mode 100644 public/languages/c/index.html create mode 100644 public/languages/csharp/dotnet10/Dockerfile create mode 100644 public/languages/csharp/dotnet10/Uncloseai.cs create mode 100644 public/languages/csharp/dotnet10/uncloseai.csproj create mode 100644 public/languages/csharp/index.html create mode 100644 public/languages/csharp/mono/Dockerfile create mode 100644 public/languages/csharp/mono/Uncloseai.cs diff --git a/public/languages/c# b/public/languages/c# new file mode 120000 index 0000000..289a854 --- /dev/null +++ b/public/languages/c# @@ -0,0 +1 @@ +csharp \ No newline at end of file diff --git a/public/languages/c/index.html b/public/languages/c/index.html new file mode 100644 index 0000000..73ab965 --- /dev/null +++ b/public/languages/c/index.html @@ -0,0 +1,266 @@ + + + + + + + + + C Implementations | uncloseai.com + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

uncloseai.

+

C Implementations - OpenAI-Compatible API Clients

+
+ +
+ +

C Implementations

+

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.

+ +

Available Endpoints:

+ + +

Available Implementations

+ +
+

libcurl

+

Recommended for most use cases. Uses the widely-available libcurl library for HTTP requests. Well-documented, portable, and supports SSL/TLS out of the box.

+
    +
  • Library: libcurl
  • +
  • Pros: Most portable, excellent documentation, widely available
  • +
  • Cons: Callback-based API can be verbose
  • +
+

View Source Code →

+
# 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
+
+ +
+

libsoup (GNOME)

+

Uses GNOME's libsoup HTTP library. Integrates well with GLib-based applications and provides a cleaner API for async operations.

+
    +
  • Library: libsoup
  • +
  • Pros: Clean GLib-style API, good async support, GNOME ecosystem integration
  • +
  • Cons: Heavier dependency, mainly for GTK/GNOME apps
  • +
+

View Source Code →

+
# 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
+
+ +
+

nghttp2 (HTTP/2)

+

Uses nghttp2 for native HTTP/2 support. Ideal for high-performance applications that benefit from HTTP/2 multiplexing and header compression.

+
    +
  • Library: nghttp2
  • +
  • Pros: Native HTTP/2, excellent performance, multiplexing support
  • +
  • Cons: More complex setup, HTTP/2 specific
  • +
+

View Source Code →

+
# 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
+
+ +

Common Features

+

All three implementations share these features:

+ + +

API Structure

+
// 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);
+ +

Source Code

+

View the full implementations in the git repository:

+ + +

See the uncloseai. Machine Learning Reference Guide for complete documentation.

+ + + + + + +
+ + + + + + diff --git a/public/languages/csharp/dotnet10/Dockerfile b/public/languages/csharp/dotnet10/Dockerfile new file mode 100644 index 0000000..7dec4a6 --- /dev/null +++ b/public/languages/csharp/dotnet10/Dockerfile @@ -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"] diff --git a/public/languages/csharp/dotnet10/Uncloseai.cs b/public/languages/csharp/dotnet10/Uncloseai.cs new file mode 100644 index 0000000..327f1f7 --- /dev/null +++ b/public/languages/csharp/dotnet10/Uncloseai.cs @@ -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 models = new List(); + static readonly List ttsEndpoints = new List(); + + 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 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 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(); + } +} diff --git a/public/languages/csharp/dotnet10/uncloseai.csproj b/public/languages/csharp/dotnet10/uncloseai.csproj new file mode 100644 index 0000000..bad583f --- /dev/null +++ b/public/languages/csharp/dotnet10/uncloseai.csproj @@ -0,0 +1,8 @@ + + + Exe + net10.0 + enable + enable + + diff --git a/public/languages/csharp/index.html b/public/languages/csharp/index.html new file mode 100644 index 0000000..5a45722 --- /dev/null +++ b/public/languages/csharp/index.html @@ -0,0 +1,298 @@ + + + + + + + + + C# Implementations | uncloseai.com + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

uncloseai.

+

C# Implementations - OpenAI-Compatible API Clients

+
+ +
+ +

C# Implementations

+

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.

+ +

URL Aliases: This directory is accessible via both /languages/csharp/ and /languages/c#/

+ +

Available Endpoints:

+
    +
  • Hermes: https://hermes.ai.unturf.com/v1 - General purpose conversational AI
  • +
  • Qwen 3 Coder: https://qwen.ai.unturf.com/v1 - Specialized coding model
  • +
  • TTS: https://speech.ai.unturf.com/v1 - Text-to-speech generation
  • +
+ +

Available Implementations

+ +
+

HttpClient (.NET 9)

+

Recommended for most use cases. Uses .NET's built-in HttpClient with System.Text.Json for direct HTTP API calls. No external dependencies required.

+
    +
  • Runtime: .NET 9.0
  • +
  • Pros: No external packages, lightweight, full control over HTTP
  • +
  • Cons: More verbose than SDK approach
  • +
+

View Source Code →

+
# 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
+
+ +
+

OpenAI SDK (.NET 9)

+

Uses the official OpenAI .NET SDK for a higher-level API. Simpler code with built-in types for messages, completions, and audio.

+
    +
  • Runtime: .NET 9.0
  • +
  • Package: OpenAI NuGet package
  • +
  • Pros: Clean API, type-safe, official SDK support
  • +
  • Cons: External dependency, SDK updates may lag API features
  • +
+

View Source Code →

+
# 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
+
+ +
+

.NET 10 Preview

+

Uses the latest .NET 10 preview runtime. Ideal for testing new language features and runtime improvements before general availability.

+
    +
  • Runtime: .NET 10.0 Preview
  • +
  • Pros: Latest C# features, performance improvements
  • +
  • Cons: Preview/unstable, may have breaking changes
  • +
+

View Source Code →

+
# 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
+
+ +
+

Mono

+

Uses the Mono runtime for cross-platform compatibility. Ideal for environments where .NET Core/5+ isn't available or for legacy system integration.

+
    +
  • Runtime: Mono 6.12
  • +
  • Pros: Wide platform support, mature runtime, works on older systems
  • +
  • Cons: Slower than .NET Core, fewer modern features
  • +
+

View Source Code →

+
# 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
+
+ +

Common Features

+

All implementations share these features:

+
    +
  • Dynamic Model Discovery: Reads MODEL_ENDPOINT_1 through MODEL_ENDPOINT_9999 environment variables
  • +
  • Streaming Support: Real-time response streaming via SSE parsing
  • +
  • Text-to-Speech: Audio generation via TTS_ENDPOINT_* variables
  • +
  • OpenAI Compatible: Works with vLLM, Ollama, and any OpenAI-compatible endpoint
  • +
+ +

Choosing an Implementation

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ImplementationBest ForDependencies
HttpClientProduction, minimal dependenciesNone (built-in)
OpenAI SDKRapid development, type safetyOpenAI NuGet
.NET 10Testing new featuresNone (built-in)
MonoLegacy systems, wide platform supportNone (built-in)
+ +

Source Code

+

View the full implementations in the git repository:

+ + +

See the uncloseai. Machine Learning Reference Guide for complete documentation.

+ + + + + + +
+ + + + + + diff --git a/public/languages/csharp/mono/Dockerfile b/public/languages/csharp/mono/Dockerfile new file mode 100644 index 0000000..9ec7466 --- /dev/null +++ b/public/languages/csharp/mono/Dockerfile @@ -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"] diff --git a/public/languages/csharp/mono/Uncloseai.cs b/public/languages/csharp/mono/Uncloseai.cs new file mode 100644 index 0000000..327f1f7 --- /dev/null +++ b/public/languages/csharp/mono/Uncloseai.cs @@ -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 models = new List(); + static readonly List ttsEndpoints = new List(); + + 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 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 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(); + } +}