84 lines
2.4 KiB
PHP
84 lines
2.4 KiB
PHP
<?php
|
||
/**
|
||
* ambre-llm-semaphore.php · 6σ Lean server-side throttle for LLM cascade :4000
|
||
* Prevents > 5 simultaneous LLM calls to protect cascade from burst overload.
|
||
* Used transparently by tools that call LLM.
|
||
*/
|
||
|
||
class AmbreLLMSemaphore {
|
||
const DIR = "/var/tmp/ambre-llm-sem";
|
||
const MAX_CONCURRENT = 5;
|
||
const MAX_WAIT_MS = 20000; // 20s max wait in queue
|
||
const STALE_LOCK_SEC = 60; // kill locks older than 60s
|
||
|
||
public static function init() {
|
||
if (!is_dir(self::DIR)) @mkdir(self::DIR, 0777, true);
|
||
}
|
||
|
||
/** Clean stale locks (older than 60s) */
|
||
public static function cleanup() {
|
||
self::init();
|
||
$now = time();
|
||
foreach (glob(self::DIR . "/*.lock") as $f) {
|
||
if (($now - @filemtime($f)) > self::STALE_LOCK_SEC) @unlink($f);
|
||
}
|
||
}
|
||
|
||
/** Count active locks */
|
||
public static function count_active() {
|
||
self::cleanup();
|
||
return count(glob(self::DIR . "/*.lock"));
|
||
}
|
||
|
||
/** Acquire a slot (blocks up to MAX_WAIT_MS) */
|
||
public static function acquire() {
|
||
self::init();
|
||
$id = bin2hex(random_bytes(6)) . "-" . getmypid();
|
||
$start = microtime(true);
|
||
|
||
while (true) {
|
||
if (self::count_active() < self::MAX_CONCURRENT) {
|
||
@file_put_contents(self::DIR . "/$id.lock", date("c"));
|
||
return $id;
|
||
}
|
||
// Wait 200ms then retry
|
||
if ((microtime(true) - $start) * 1000 > self::MAX_WAIT_MS) {
|
||
return null; // timeout - caller should handle
|
||
}
|
||
usleep(200000);
|
||
}
|
||
}
|
||
|
||
/** Release a slot */
|
||
public static function release($id) {
|
||
if (!$id) return;
|
||
@unlink(self::DIR . "/$id.lock");
|
||
}
|
||
|
||
/** Wrap a callable with semaphore protection */
|
||
public static function guarded($callable) {
|
||
$id = self::acquire();
|
||
try {
|
||
$result = $callable($id);
|
||
} finally {
|
||
self::release($id);
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
/** Stats for monitoring */
|
||
public static function stats() {
|
||
return [
|
||
"active" => self::count_active(),
|
||
"max" => self::MAX_CONCURRENT,
|
||
"dir" => self::DIR,
|
||
];
|
||
}
|
||
}
|
||
|
||
// Expose as endpoint for stats
|
||
if (basename($_SERVER["SCRIPT_NAME"]) === "ambre-llm-semaphore.php") {
|
||
header("Content-Type: application/json");
|
||
echo json_encode(AmbreLLMSemaphore::stats());
|
||
}
|