73 lines
4.7 KiB
PHP
73 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace app\common\service\prescriptionai {
|
|
final class PrescriptionAiAccess {
|
|
public static bool $active = true;
|
|
public static function actor(int $id): ?array { return self::$active ? ['id' => $id] : null; }
|
|
}
|
|
}
|
|
namespace app\common\service\followupaudio {
|
|
final class FollowupAudioStore {
|
|
public static bool $enabled = true;
|
|
public static bool $verified = true;
|
|
public static array $events = [];
|
|
public static bool $lease = true;
|
|
public static function enabled(): bool { return self::$enabled; }
|
|
public static function verified(?string $profile = null): bool { return self::$verified; }
|
|
public static function claim(): ?array { self::$events[] = 'claim'; return ['id' => 1, 'actor_id' => 1, 'diagnosis_id' => 1, 'lease_token' => 'test', 'model_key' => 'qwen']; }
|
|
public static function heartbeat(int $id, string $token): bool { self::$events[] = 'heartbeat'; return self::$lease; }
|
|
public static function checkpoint(int $id, string $token, array $fields): bool { self::$events[] = ['checkpoint' => $fields]; return self::$lease; }
|
|
public static function complete(int $id, string $token, array $extraction): bool { self::$events[] = ['complete' => $extraction]; return self::$lease; }
|
|
public static function fail(int $id, string $token, string $code, string $message, bool $uncertain): bool { self::$events[] = ['fail' => $code, 'uncertain' => $uncertain, 'message' => $message]; return true; }
|
|
}
|
|
final class FollowupAudioAccess {
|
|
public static bool $allowed = true;
|
|
public static function task(int $id, int $actor, array $info): array { if (!self::$allowed) { throw new \RuntimeException('private-patient'); } return ['id' => $id]; }
|
|
}
|
|
final class FollowupAudioDify {
|
|
public static string $mode = 'success';
|
|
public function analyze(array $task, callable $heartbeat): array {
|
|
if ($heartbeat(['upstream_started_at' => 1, 'stage' => 'uploading']) === false) { throw new FollowupAudioException('LEASE_LOST'); }
|
|
if (self::$mode === 'uncertain') { throw new FollowupAudioException('UPSTREAM_UNCERTAIN', true); }
|
|
if (self::$mode === 'internal') { throw new \RuntimeException('private-patient'); }
|
|
if (self::$mode === 'revoke') { \app\common\service\prescriptionai\PrescriptionAiAccess::$active = false; }
|
|
return ['summary' => 'synthetic', 'items' => []];
|
|
}
|
|
}
|
|
}
|
|
namespace {
|
|
require dirname(__DIR__) . '/app/common/service/followupaudio/FollowupAudioException.php';
|
|
require dirname(__DIR__) . '/app/common/service/followupaudio/FollowupAudioWorker.php';
|
|
use app\common\service\followupaudio\FollowupAudioStore as Store;
|
|
use app\common\service\followupaudio\FollowupAudioDify as Dify;
|
|
use app\common\service\followupaudio\FollowupAudioWorker as Worker;
|
|
use app\common\service\followupaudio\FollowupAudioAccess as Access;
|
|
use app\common\service\prescriptionai\PrescriptionAiAccess as Actors;
|
|
$checks = 0;
|
|
$expect = static function (bool $ok, string $why) use (&$checks): void { if (!$ok) { throw new \RuntimeException($why); } $checks++; };
|
|
$worker = new Worker(new Dify());
|
|
Store::$enabled = false;
|
|
$expect(!$worker->runOnce() && Store::$events === [], 'disabled before claim');
|
|
Store::$enabled = true; Store::$verified = false;
|
|
$expect(!$worker->runOnce() && Store::$events === [], 'unverified before claim');
|
|
Store::$verified = true;
|
|
$expect($worker->runOnce(), 'success consumed task');
|
|
$expect(isset(Store::$events[count(Store::$events)-1]['complete']), 'success completed after authorization');
|
|
foreach (['uncertain' => 'UPSTREAM_UNCERTAIN', 'internal' => 'INTERNAL_ERROR', 'revoke' => 'LEASE_LOST'] as $mode => $code) {
|
|
Dify::$mode = $mode; Store::$events = []; Actors::$active = true;
|
|
$expect($worker->runOnce(), $mode . ' consumed');
|
|
$last = end(Store::$events);
|
|
$expect($last['fail'] === $code && $last['uncertain'] === true, $mode . ' reconciliation required');
|
|
$expect(!str_contains($last['message'], 'private-patient'), 'sanitized ' . $mode);
|
|
}
|
|
Actors::$active = false; Store::$events = [];
|
|
$worker->runOnce(); $last = end(Store::$events);
|
|
$expect($last['fail'] === 'ACCESS_REVOKED' && !$last['uncertain'], 'inactive actor before upstream');
|
|
Actors::$active = true; Access::$allowed = false; Store::$events = [];
|
|
$worker->runOnce(); $last = end(Store::$events);
|
|
$expect($last['fail'] === 'INTERNAL_ERROR' && !$last['uncertain'], 'scope denied before upstream');
|
|
echo 'FOLLOWUP_AUDIO_WORKER assertions=' . $checks . ' PASS gate=1 actor_recheck=1 reconciliation=1' . PHP_EOL;
|
|
}
|