uncloseai.com/languages/php/uncloseai.php
Russell Ballestrini 9e3eccb1b9 modified: .gitignore
modified:   CLAUDE.md
	modified:   Makefile
	new file:   languages/awk/Dockerfile
	new file:   languages/awk/uncloseai.awk
	modified:   languages/bash/Dockerfile
	deleted:    languages/bash/examples.sh
	new file:   languages/bash/uncloseai.sh
	new file:   languages/c/curl/Dockerfile
	new file:   languages/c/curl/Makefile
	new file:   languages/c/curl/index.html
	new file:   languages/c/curl/uncloseai.c
	new file:   languages/c/libh2o/Dockerfile
	new file:   languages/c/libh2o/Makefile
	new file:   languages/c/libh2o/uncloseai.c
	new file:   languages/c/nghttp2/Dockerfile
	new file:   languages/c/nghttp2/Makefile
	new file:   languages/c/nghttp2/uncloseai.c
	new file:   languages/clojure/Dockerfile
	new file:   languages/clojure/deps.edn
	new file:   languages/clojure/uncloseai.clj
	new file:   languages/cobol/Dockerfile
	new file:   languages/cobol/discover.sh
	new file:   languages/cobol/hermes.sh
	new file:   languages/cobol/qwen.sh
	new file:   languages/cobol/tts.sh
	new file:   languages/cobol/uncloseai.cob
	new file:   languages/cpp/boost-beast/Dockerfile
	new file:   languages/cpp/boost-beast/Makefile
	new file:   languages/cpp/boost-beast/uncloseai.cpp
	new file:   languages/cpp/cpp-httplib/Dockerfile
	new file:   languages/cpp/cpp-httplib/Makefile
	new file:   languages/cpp/cpp-httplib/uncloseai.cpp
	new file:   languages/cpp/libcurl/Dockerfile
	new file:   languages/cpp/libcurl/Makefile
	new file:   languages/cpp/libcurl/uncloseai.cpp
	new file:   languages/crystal/Dockerfile
	new file:   languages/crystal/uncloseai.cr
	new file:   languages/csharp/Dockerfile
	new file:   languages/csharp/Uncloseai.cs
	new file:   languages/csharp/csharp.csproj
	new file:   languages/dart/Dockerfile
	new file:   languages/dart/bin/uncloseai.dart
	new file:   languages/dart/pubspec.yaml
	new file:   languages/deno/Dockerfile
	new file:   languages/deno/uncloseai.ts
	new file:   languages/elixir/Dockerfile
	new file:   languages/elixir/lib/uncloseai.ex
	new file:   languages/elixir/mix.exs
	new file:   languages/elixir/run.exs
	new file:   languages/erlang/Dockerfile
	new file:   languages/erlang/rebar.config
	new file:   languages/erlang/src/uncloseai.app.src
	new file:   languages/erlang/src/uncloseai.erl
	new file:   languages/fortran/Dockerfile
	new file:   languages/fortran/uncloseai.f90
	new file:   languages/fsharp/Dockerfile
	new file:   languages/fsharp/Uncloseai.fs
	new file:   languages/fsharp/fsharp.fsproj
	new file:   languages/go/Dockerfile
	new file:   languages/go/README.md
	new file:   languages/go/examples/basic.go
	new file:   languages/go/go.mod
	new file:   languages/go/uncloseai.go
	new file:   languages/go/uncloseai/uncloseai.go
	new file:   languages/haskell/Dockerfile
	new file:   languages/haskell/UncloseAI.hs
	new file:   languages/haskell/uncloseai.cabal
	new file:   languages/java/Dockerfile
	new file:   languages/java/UncloseAI.java
	new file:   languages/javascript/bun/Dockerfile
	new file:   languages/javascript/bun/uncloseai.ts
	new file:   languages/javascript/nodejs/Dockerfile
	new file:   languages/javascript/nodejs/README.md
	new file:   languages/javascript/nodejs/package.json
	new file:   languages/javascript/nodejs/uncloseai.js
	new file:   languages/javascript/typescript/Dockerfile
	new file:   languages/javascript/typescript/package.json
	new file:   languages/javascript/typescript/tsconfig.json
	new file:   languages/javascript/typescript/uncloseai.ts
	new file:   languages/javascript/vanilla/Dockerfile
	new file:   languages/javascript/vanilla/uncloseai.html
	new file:   languages/julia/Dockerfile
	new file:   languages/julia/Project.toml
	new file:   languages/julia/src/uncloseai.jl
	new file:   languages/kotlin/Dockerfile
	new file:   languages/kotlin/build.gradle.kts
	new file:   languages/kotlin/src/main/kotlin/UncloseAI.kt
	new file:   languages/kotlin/uncloseai.kt
	new file:   languages/lua/Dockerfile
	new file:   languages/lua/uncloseai.lua
	new file:   languages/nim/Dockerfile
	new file:   languages/nim/uncloseai.nim
	new file:   languages/ocaml/Dockerfile
	new file:   languages/ocaml/dune
	new file:   languages/ocaml/dune-project
	new file:   languages/ocaml/uncloseai.ml
	new file:   languages/odin/Dockerfile
	new file:   languages/odin/uncloseai.odin
	new file:   languages/perl/Dockerfile
	new file:   languages/perl/uncloseai.pl
	new file:   languages/php/Dockerfile
	new file:   languages/php/uncloseai.php
	new file:   languages/powershell/Dockerfile
	new file:   languages/powershell/uncloseai.ps1
	new file:   languages/prolog/Dockerfile
	new file:   languages/prolog/uncloseai.pl
	new file:   languages/python/aiohttp/Dockerfile
	new file:   languages/python/aiohttp/requirements.txt
	new file:   languages/python/aiohttp/uncloseai.py
	modified:   languages/python/httpx-async/Dockerfile
	deleted:    languages/python/httpx-async/examples.py
	new file:   languages/python/httpx-async/uncloseai.py
	modified:   languages/python/openai-client/Dockerfile
	deleted:    languages/python/openai-client/examples.py
	new file:   languages/python/openai-client/uncloseai.py
	modified:   languages/python/requests/Dockerfile
	deleted:    languages/python/requests/examples.py
	new file:   languages/python/requests/uncloseai.py
	new file:   languages/r/Dockerfile
	new file:   languages/r/uncloseai.R
	new file:   languages/ruby/Dockerfile
	new file:   languages/ruby/README.md
	new file:   languages/ruby/uncloseai.rb
	new file:   languages/rust/Cargo.toml
	new file:   languages/rust/Dockerfile
	new file:   languages/rust/README.md
	new file:   languages/rust/examples/basic.rs
	new file:   languages/rust/src/lib.rs
	new file:   languages/rust/src/uncloseai.rs
	new file:   languages/scala/Dockerfile
	new file:   languages/scala/build.sbt
	new file:   languages/scala/project/build.properties
	new file:   languages/scala/project/plugins.sbt
	new file:   languages/scala/src/main/scala/UncloseAI.scala
	new file:   languages/tcl/Dockerfile
	new file:   languages/tcl/uncloseai.tcl
	new file:   languages/v/Dockerfile
	new file:   languages/v/uncloseai.v
	new file:   languages/v/v.mod
	new file:   languages/vbnet/Dockerfile
	new file:   languages/vbnet/UncloseAI.vb
	new file:   languages/vbnet/UncloseAI.vbproj
	modified:   languages/zig/Dockerfile
	modified:   languages/zig/build.zig
	deleted:    languages/zig/src/main.zig
	new file:   languages/zig/src/uncloseai.zig
2025-10-14 10:24:16 -04:00

254 lines
8.1 KiB
PHP

#!/usr/bin/env php
<?php
/**
* UncloseAI - PHP client for OpenAI-compatible APIs with streaming support
*/
class UncloseAI {
private $models = [];
private $tts_endpoints = [];
private $api_key;
private $timeout;
private $debug;
public function __construct($config = []) {
$this->api_key = $config['api_key'] ?? null;
$this->timeout = $config['timeout'] ?? 30;
$this->debug = $config['debug'] ?? false;
$endpoints = $config['endpoints'] ?? $this->discoverEndpointsFromEnv('MODEL_ENDPOINT');
$ttsEndpoints = $config['tts_endpoints'] ?? $this->discoverEndpointsFromEnv('TTS_ENDPOINT');
if ($this->debug) {
echo "[DEBUG] Initialized with " . count($endpoints) . " endpoint(s)\n";
}
$this->discoverModels($endpoints);
$this->tts_endpoints = $ttsEndpoints;
}
public function listModels() {
return $this->models;
}
public function chat($messages, $options = []) {
$model = $options['model'] ?? null;
$modelInfo = $this->resolveModel($model);
$payload = [
'model' => $modelInfo['id'],
'messages' => $messages,
'max_tokens' => $options['max_tokens'] ?? 100,
'temperature' => $options['temperature'] ?? 0.7
];
$response = $this->httpRequest($modelInfo['endpoint'] . '/chat/completions', 'POST', $payload);
return json_decode($response, true);
}
public function chatStream($messages, $options, $callback) {
$model = $options['model'] ?? null;
$modelInfo = $this->resolveModel($model);
$payload = [
'model' => $modelInfo['id'],
'messages' => $messages,
'max_tokens' => $options['max_tokens'] ?? 500,
'temperature' => $options['temperature'] ?? 0.7,
'stream' => true
];
$url = $modelInfo['endpoint'] . '/chat/completions';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
$buffer = '';
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($callback, &$buffer) {
$buffer .= $data;
$lines = explode("\n", $buffer);
$buffer = array_pop($lines);
foreach ($lines as $line) {
if (strpos($line, 'data: ') === 0) {
$json = substr($line, 6);
if (trim($json) === '[DONE]') {
return strlen($data);
}
$chunk = json_decode($json, true);
if ($chunk) {
call_user_func($callback, $chunk);
}
}
}
return strlen($data);
});
curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
}
public function tts($text, $voice = 'alloy', $model = 'tts-1') {
if (empty($this->tts_endpoints)) {
throw new Exception('No TTS endpoints available');
}
$payload = [
'model' => $model,
'voice' => $voice,
'input' => $text
];
return $this->httpRequest($this->tts_endpoints[0] . '/audio/speech', 'POST', $payload);
}
private function discoverEndpointsFromEnv($prefix) {
$endpoints = [];
for ($i = 1; $i < 10000; $i++) {
$endpoint = getenv("{$prefix}_{$i}");
if ($endpoint === false) break;
$endpoints[] = $endpoint;
}
return $endpoints;
}
private function discoverModels($endpoints) {
foreach ($endpoints as $endpoint) {
if ($this->debug) {
echo "[DEBUG] Discovering from: $endpoint\n";
}
try {
$response = $this->httpRequest("$endpoint/models", 'GET');
$data = json_decode($response, true);
foreach ($data['data'] as $model) {
$maxTokens = $model['max_model_len'] ?? 8192;
$this->models[] = [
'id' => $model['id'],
'endpoint' => $endpoint,
'max_tokens' => $maxTokens
];
if ($this->debug) {
echo "[DEBUG] Discovered: {$model['id']}\n";
}
}
} catch (Exception $e) {
if ($this->debug) {
echo "[DEBUG] Error: {$e->getMessage()}\n";
}
}
}
}
private function resolveModel($model) {
if (empty($this->models)) {
throw new Exception('No models available');
}
if ($model === null) {
return $this->models[0];
}
foreach ($this->models as $m) {
if ($m['id'] === $model) {
return $m;
}
}
throw new Exception("Model '$model' not found");
}
private function httpRequest($url, $method, $payload = null) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
if ($method === 'POST' && $payload) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
}
if ($this->api_key) {
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
"Authorization: Bearer {$this->api_key}"
]);
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return $response;
}
}
// Demo when run as script
if (basename(__FILE__) === basename($_SERVER['PHP_SELF'])) {
echo "=== UncloseAI PHP Client (with Streaming) ===\n\n";
$client = new UncloseAI(['debug' => true]);
if (empty($client->listModels())) {
echo "ERROR: No models discovered. Set environment variables:\n";
echo " MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.\n";
exit(1);
}
$models = $client->listModels();
echo "\nDiscovered " . count($models) . " model(s):\n";
foreach ($models as $m) {
echo " - {$m['id']} (max_tokens: {$m['max_tokens']})\n";
}
echo "\n";
// Non-streaming chat
echo "=== Non-Streaming Chat ===\n";
$response = $client->chat([
['role' => 'system', 'content' => 'You are a helpful AI assistant.'],
['role' => 'user', 'content' => 'Explain quantum computing in one sentence.']
]);
echo "Response: " . $response['choices'][0]['message']['content'] . "\n\n";
// Streaming chat
echo "=== Streaming Chat ===\n";
$modelId = count($models) > 1 ? $models[1]['id'] : null;
echo "Model: " . ($modelId ?? $models[0]['id']) . "\n";
echo "Response: ";
$client->chatStream([
['role' => 'system', 'content' => 'You are a coding assistant.'],
['role' => 'user', 'content' => 'Write a PHP function to check if a number is prime']
], ['model' => $modelId, 'max_tokens' => 200], function($chunk) {
if (isset($chunk['choices'][0]['delta']['content'])) {
echo $chunk['choices'][0]['delta']['content'];
}
});
echo "\n\n";
// TTS
if (!empty($client->listModels())) {
echo "=== TTS Speech Generation ===\n";
try {
$audioData = $client->tts('Hello from UncloseAI PHP client! This demonstrates streaming support.');
file_put_contents('speech.mp3', $audioData);
echo "✓ Speech file created: speech.mp3 (" . strlen($audioData) . " bytes)\n\n";
} catch (Exception $e) {
echo "✗ TTS Error: " . $e->getMessage() . "\n\n";
}
}
echo "=== Examples Complete ===\n";
}