176 lines
5.5 KiB
D
176 lines
5.5 KiB
D
#!/usr/bin/env rdmd
|
|
// PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
|
|
// Copyright 2025 TimeHexOn & foxhop & russell@unturf
|
|
// https://www.permacomputer.com
|
|
|
|
|
|
// uncloseai. - D Language Client
|
|
// Multiple AI model examples with streaming and non-streaming chat, plus TTS
|
|
|
|
import std.stdio;
|
|
import std.net.curl;
|
|
import std.json;
|
|
import std.string;
|
|
import std.algorithm;
|
|
import std.array;
|
|
import std.file;
|
|
import std.process;
|
|
import std.conv;
|
|
|
|
void main()
|
|
{
|
|
writeln("=== uncloseai. D Language Client ===\n");
|
|
|
|
// Discover endpoints from environment variables
|
|
string modelEndpoint1 = environment.get("MODEL_ENDPOINT_1", "");
|
|
string modelEndpoint2 = environment.get("MODEL_ENDPOINT_2", "");
|
|
string ttsEndpoint1 = environment.get("TTS_ENDPOINT_1", "");
|
|
|
|
if (modelEndpoint1 == "" || modelEndpoint2 == "" || ttsEndpoint1 == "")
|
|
{
|
|
writeln("ERROR: No models discovered. Set environment variables:");
|
|
writeln(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, TTS_ENDPOINT_1");
|
|
return;
|
|
}
|
|
|
|
// Discover models from endpoint 1
|
|
writeln("Discovering models from ", modelEndpoint1, "...");
|
|
auto response1 = get(modelEndpoint1 ~ "/models");
|
|
auto models1 = parseJSON(response1);
|
|
string model1Id = models1["data"][0]["id"].str;
|
|
writeln("Model 1: ", model1Id, "\n");
|
|
|
|
// Discover models from endpoint 2
|
|
writeln("Discovering models from ", modelEndpoint2, "...");
|
|
auto response2 = get(modelEndpoint2 ~ "/models");
|
|
auto models2 = parseJSON(response2);
|
|
string model2Id = models2["data"][0]["id"].str;
|
|
writeln("Model 2: ", model2Id, "\n");
|
|
|
|
// Non-streaming chat with Model 1
|
|
writeln("=== Non-Streaming Chat (Model 1) ===");
|
|
try
|
|
{
|
|
JSONValue payload1;
|
|
payload1["model"] = model1Id;
|
|
payload1["messages"] = [
|
|
JSONValue([
|
|
"role": JSONValue("user"),
|
|
"content": JSONValue("Give a Python Fizzbuzz solution in one line of code?")
|
|
])
|
|
];
|
|
payload1["temperature"] = 0.5;
|
|
payload1["max_tokens"] = 150;
|
|
payload1["stream"] = false;
|
|
|
|
auto http1 = HTTP();
|
|
http1.addRequestHeader("Content-Type", "application/json");
|
|
auto chatResponse1 = post(modelEndpoint1 ~ "/chat/completions", payload1.toString(), http1);
|
|
auto jsonResponse1 = parseJSON(chatResponse1);
|
|
auto content1 = jsonResponse1["choices"][0]["message"]["content"].str;
|
|
writeln("Response: ", content1, "\n");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
writeln("Error: ", e.msg, "\n");
|
|
}
|
|
|
|
// Streaming chat with Model 2
|
|
writeln("=== Streaming Chat (Model 2) ===");
|
|
write("Response: ");
|
|
stdout.flush();
|
|
|
|
try
|
|
{
|
|
JSONValue payload2;
|
|
payload2["model"] = model2Id;
|
|
payload2["messages"] = [
|
|
JSONValue([
|
|
"role": JSONValue("user"),
|
|
"content": JSONValue("Give a Python Fizzbuzz solution in one line of code?")
|
|
])
|
|
];
|
|
payload2["temperature"] = 0.5;
|
|
payload2["max_tokens"] = 150;
|
|
payload2["stream"] = true;
|
|
|
|
auto http2 = HTTP();
|
|
http2.addRequestHeader("Content-Type", "application/json");
|
|
http2.method = HTTP.Method.post;
|
|
http2.url = modelEndpoint2 ~ "/chat/completions";
|
|
http2.postData = payload2.toString();
|
|
|
|
string buffer = "";
|
|
|
|
// Process chunks as they arrive
|
|
http2.onReceive = (ubyte[] data)
|
|
{
|
|
buffer ~= cast(string)data;
|
|
|
|
// Process complete lines
|
|
while (true)
|
|
{
|
|
auto idx = buffer.indexOf("\n");
|
|
if (idx == -1) break;
|
|
|
|
auto line = buffer[0..idx].strip();
|
|
buffer = buffer[idx+1..$];
|
|
|
|
// Process SSE data lines
|
|
if (line.startsWith("data: "))
|
|
{
|
|
auto jsonData = line[6..$];
|
|
if (jsonData == "[DONE]") continue;
|
|
|
|
try
|
|
{
|
|
auto parsed = parseJSON(jsonData);
|
|
if ("choices" in parsed && parsed["choices"].array.length > 0)
|
|
{
|
|
auto delta = parsed["choices"][0]["delta"];
|
|
if ("content" in delta)
|
|
{
|
|
write(delta["content"].str);
|
|
stdout.flush();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception) {}
|
|
}
|
|
}
|
|
|
|
return data.length;
|
|
};
|
|
|
|
http2.perform();
|
|
writeln("\n");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
writeln("\nError: ", e.msg, "\n");
|
|
}
|
|
|
|
// TTS example
|
|
writeln("=== TTS Speech Generation ===");
|
|
try
|
|
{
|
|
JSONValue ttsPayload;
|
|
ttsPayload["model"] = "tts-1";
|
|
ttsPayload["voice"] = "alloy";
|
|
ttsPayload["speed"] = 0.9;
|
|
ttsPayload["input"] = "I think so therefore, Today is a wonderful day to grow something people love!";
|
|
|
|
auto httpTts = HTTP();
|
|
httpTts.addRequestHeader("Content-Type", "application/json");
|
|
auto ttsResponse = post(ttsEndpoint1 ~ "/audio/speech", ttsPayload.toString(), httpTts);
|
|
|
|
std.file.write("speech.mp3", ttsResponse);
|
|
writeln("[OK] Speech file created: speech.mp3 (", ttsResponse.length, " bytes)\n");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
writeln("[ERROR] TTS failed: ", e.msg, "\n");
|
|
}
|
|
|
|
writeln("=== Examples Complete ===");
|
|
}
|