using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
namespace SynorCompute;
///
/// Synor Compute SDK - C# Client
///
/// Access distributed heterogeneous compute resources (CPU, GPU, TPU, NPU, LPU, FPGA, DSP)
/// for AI/ML workloads at 90% cost reduction compared to traditional cloud.
///
///
///
/// // Create client
/// using var client = new SynorComputeClient("your-api-key");
///
/// // Matrix multiplication on GPU
/// var a = Tensor.Rand(512, 512);
/// var b = Tensor.Rand(512, 512);
/// var result = await client.MatMulAsync(a, b, new MatMulOptions
/// {
/// Processor = ProcessorType.Gpu,
/// Precision = Precision.Fp16
/// });
///
/// if (result.IsSuccess)
/// {
/// Console.WriteLine($"Time: {result.ExecutionTimeMs}ms");
/// }
///
/// // LLM inference
/// var response = await client.InferenceAsync("llama-3-70b", "Explain quantum computing");
/// Console.WriteLine(response.Result);
///
/// // Streaming inference
/// await foreach (var token in client.InferenceStreamAsync("llama-3-70b", "Write a poem"))
/// {
/// Console.Write(token);
/// }
///
///
public sealed class SynorComputeClient : IDisposable
{
public const string Version = "0.1.0";
private readonly SynorConfig _config;
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonOptions;
private bool _disposed;
public SynorComputeClient(string apiKey)
: this(new SynorConfig { ApiKey = apiKey })
{
}
public SynorComputeClient(SynorConfig config)
{
_config = config;
_httpClient = new HttpClient
{
BaseAddress = new Uri(config.BaseUrl),
Timeout = TimeSpan.FromMilliseconds(config.TimeoutMs)
};
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {config.ApiKey}");
_httpClient.DefaultRequestHeaders.Add("X-SDK-Version", $"csharp/{Version}");
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
PropertyNameCaseInsensitive = true
};
}
// ==================== Matrix Operations ====================
public Task> MatMulAsync(Tensor a, Tensor b, CancellationToken ct = default)
=> MatMulAsync(a, b, new MatMulOptions(), ct);
public async Task> MatMulAsync(
Tensor a,
Tensor b,
MatMulOptions options,
CancellationToken ct = default)
{
CheckDisposed();
var body = new
{
operation = "matmul",
a = TensorToDict(a),
b = TensorToDict(b),
precision = options.Precision.ToString().ToLower(),
processor = options.Processor.ToString().ToLower(),
priority = options.Priority.ToString().ToLower()
};
return await PostAsync>("/compute", body, ct);
}
public async Task> Conv2dAsync(
Tensor input,
Tensor kernel,
Conv2dOptions? options = null,
CancellationToken ct = default)
{
CheckDisposed();
options ??= new Conv2dOptions();
var body = new
{
operation = "conv2d",
input = TensorToDict(input),
kernel = TensorToDict(kernel),
stride = new[] { options.Stride.Item1, options.Stride.Item2 },
padding = new[] { options.Padding.Item1, options.Padding.Item2 },
precision = options.Precision.ToString().ToLower()
};
return await PostAsync>("/compute", body, ct);
}
public async Task> AttentionAsync(
Tensor query,
Tensor key,
Tensor value,
AttentionOptions? options = null,
CancellationToken ct = default)
{
CheckDisposed();
options ??= new AttentionOptions();
var body = new
{
operation = "attention",
query = TensorToDict(query),
key = TensorToDict(key),
value = TensorToDict(value),
num_heads = options.NumHeads,
flash = options.Flash,
precision = options.Precision.ToString().ToLower()
};
return await PostAsync>("/compute", body, ct);
}
// ==================== LLM Inference ====================
public Task> InferenceAsync(string model, string prompt, CancellationToken ct = default)
=> InferenceAsync(model, prompt, new InferenceOptions(), ct);
public async Task> InferenceAsync(
string model,
string prompt,
InferenceOptions options,
CancellationToken ct = default)
{
CheckDisposed();
var body = new Dictionary
{
["operation"] = "inference",
["model"] = model,
["prompt"] = prompt,
["max_tokens"] = options.MaxTokens,
["temperature"] = options.Temperature,
["top_p"] = options.TopP,
["top_k"] = options.TopK
};
if (options.Processor.HasValue)
{
body["processor"] = options.Processor.Value.ToString().ToLower();
}
return await PostAsync>("/inference", body, ct);
}
public async IAsyncEnumerable InferenceStreamAsync(
string model,
string prompt,
InferenceOptions? options = null,
[EnumeratorCancellation] CancellationToken ct = default)
{
CheckDisposed();
options ??= new InferenceOptions();
var body = new Dictionary
{
["operation"] = "inference",
["model"] = model,
["prompt"] = prompt,
["max_tokens"] = options.MaxTokens,
["temperature"] = options.Temperature,
["stream"] = true
};
var request = new HttpRequestMessage(HttpMethod.Post, "/inference/stream")
{
Content = JsonContent.Create(body, options: _jsonOptions)
};
using var response = await _httpClient.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead,
ct);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(ct);
using var reader = new StreamReader(stream);
while (!reader.EndOfStream && !ct.IsCancellationRequested)
{
var line = await reader.ReadLineAsync(ct);
if (line == null) break;
if (line.StartsWith("data: "))
{
var data = line[6..];
if (data == "[DONE]") yield break;
try
{
var json = JsonSerializer.Deserialize>(data, _jsonOptions);
if (json?.TryGetValue("token", out var token) == true)
{
yield return token.GetString() ?? "";
}
}
catch (JsonException)
{
// Skip malformed JSON
}
}
}
}
// ==================== Model Registry ====================
public async Task> ListModelsAsync(
ModelCategory? category = null,
CancellationToken ct = default)
{
CheckDisposed();
var url = category.HasValue
? $"/models?category={category.Value.ToString().ToLower()}"
: "/models";
var response = await GetAsync(url, ct);
var models = response.GetProperty("models");
return models.Deserialize>(_jsonOptions) ?? new List();
}
public async Task GetModelAsync(string modelId, CancellationToken ct = default)
{
CheckDisposed();
return await GetAsync($"/models/{modelId}", ct);
}
public async Task> SearchModelsAsync(string query, CancellationToken ct = default)
{
CheckDisposed();
var response = await GetAsync($"/models/search?q={Uri.EscapeDataString(query)}", ct);
var models = response.GetProperty("models");
return models.Deserialize>(_jsonOptions) ?? new List();
}
// ==================== Pricing & Usage ====================
public async Task> GetPricingAsync(CancellationToken ct = default)
{
CheckDisposed();
var response = await GetAsync("/pricing", ct);
var pricing = response.GetProperty("pricing");
return pricing.Deserialize>(_jsonOptions) ?? new List();
}
public async Task GetUsageAsync(CancellationToken ct = default)
{
CheckDisposed();
return await GetAsync("/usage", ct);
}
// ==================== Health Check ====================
public async Task HealthCheckAsync(CancellationToken ct = default)
{
try
{
var response = await GetAsync("/health", ct);
return response.GetProperty("status").GetString() == "healthy";
}
catch
{
return false;
}
}
// ==================== Internal Methods ====================
private async Task GetAsync(string path, CancellationToken ct)
{
var response = await _httpClient.GetAsync(path, ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync(_jsonOptions, ct)
?? throw new SynorException("Failed to deserialize response");
}
private async Task PostAsync(string path, object body, CancellationToken ct)
{
var response = await _httpClient.PostAsJsonAsync(path, body, _jsonOptions, ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync(_jsonOptions, ct)
?? throw new SynorException("Failed to deserialize response");
}
private static object TensorToDict(Tensor tensor) => new
{
shape = tensor.Shape,
data = tensor.Data,
dtype = tensor.Dtype.ToString().ToLower()
};
private void CheckDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_httpClient.Dispose();
}
}
}