139 lines
4.8 KiB
PHP
139 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
use app\controller\api\Chat;
|
|
use app\model\UploadFile;
|
|
use app\service\ComfyJobDeferredException;
|
|
use app\service\ComfyUIService;
|
|
use app\service\OpenAIService;
|
|
|
|
$app = new think\App(dirname(__DIR__) . DIRECTORY_SEPARATOR);
|
|
$app->initialize();
|
|
|
|
$model = OpenAIService::getImageModel();
|
|
$source = null;
|
|
foreach (UploadFile::where('file_type', 'image')->order('id', 'desc')->limit(50)->select() as $candidate) {
|
|
$path = Chat::resolveStoredPath('/api/uploads/' . rawurlencode((string) $candidate->stored_name));
|
|
if ($path !== null && @getimagesize($path)) {
|
|
$source = ['record' => $candidate, 'path' => $path];
|
|
break;
|
|
}
|
|
}
|
|
if ($source === null) {
|
|
fwrite(STDERR, "No stored source image is available for integration testing.\n");
|
|
exit(2);
|
|
}
|
|
|
|
$createdAttachments = [];
|
|
$maskPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'comfy_inpaint_mask_' . bin2hex(random_bytes(6)) . '.png';
|
|
|
|
$cleanup = static function () use (&$createdAttachments, &$maskPath): void {
|
|
foreach ($createdAttachments as $attachment) {
|
|
$filename = urldecode(basename(parse_url((string) ($attachment['url'] ?? ''), PHP_URL_PATH) ?: ''));
|
|
if ($filename === '') {
|
|
continue;
|
|
}
|
|
$record = UploadFile::where('stored_name', $filename)->find();
|
|
if (!$record) {
|
|
continue;
|
|
}
|
|
$path = Chat::resolveStoredPath('/api/uploads/' . rawurlencode($filename));
|
|
if ($path !== null && is_file($path)) {
|
|
@unlink($path);
|
|
}
|
|
$record->delete();
|
|
}
|
|
if (is_file($maskPath)) {
|
|
@unlink($maskPath);
|
|
}
|
|
};
|
|
register_shutdown_function($cleanup);
|
|
|
|
/**
|
|
* Wait for a ComfyUI task until it finishes, even if the first request is
|
|
* intentionally deferred into the background by the HTTP budget guard.
|
|
*
|
|
* @return array<int, array{type:string,url:string,name:string,mime:string,size:int}>
|
|
*/
|
|
$waitForFinalOutputs = static function (
|
|
$model,
|
|
string $promptId,
|
|
int $userId,
|
|
int $httpBudgetSeconds = 120,
|
|
int $pollTimeoutSeconds = 900
|
|
): array {
|
|
try {
|
|
return ComfyUIService::waitAndCollect($model, $promptId, $userId, null, $httpBudgetSeconds);
|
|
} catch (ComfyJobDeferredException $exception) {
|
|
$deadline = time() + $pollTimeoutSeconds;
|
|
while (time() < $deadline) {
|
|
$status = ComfyUIService::inspect($model, $promptId);
|
|
if (($status['state'] ?? '') === 'done' && !empty($status['images'])) {
|
|
return ComfyUIService::storeInspectImages($model, $status['images'], $userId);
|
|
}
|
|
if (($status['state'] ?? '') === 'error') {
|
|
throw new RuntimeException((string) ($status['message'] ?? 'ComfyUI task failed'));
|
|
}
|
|
sleep(2);
|
|
}
|
|
|
|
throw new RuntimeException('ComfyUI task did not finish before the integration timeout');
|
|
}
|
|
};
|
|
|
|
try {
|
|
$userId = (int) $source['record']->user_id;
|
|
$imgPromptId = ComfyUIService::submitEdit(
|
|
$model,
|
|
'Preserve the exact source composition and subjects, improve natural lighting and fine detail, photorealistic.',
|
|
$source['path'],
|
|
null,
|
|
'img2img'
|
|
);
|
|
$imgOutputs = $waitForFinalOutputs($model, $imgPromptId, $userId);
|
|
$createdAttachments = array_merge($createdAttachments, $imgOutputs);
|
|
if ($imgOutputs === []) {
|
|
throw new RuntimeException('img2img returned no output');
|
|
}
|
|
echo 'IMG2IMG PASS prompt_id=' . $imgPromptId . PHP_EOL;
|
|
|
|
[$width, $height] = getimagesize($source['path']);
|
|
$mask = imagecreatetruecolor($width, $height);
|
|
$black = imagecolorallocate($mask, 0, 0, 0);
|
|
$white = imagecolorallocate($mask, 255, 255, 255);
|
|
imagefill($mask, 0, 0, $black);
|
|
$boxWidth = max(16, (int) round($width * 0.12));
|
|
$boxHeight = max(16, (int) round($height * 0.08));
|
|
imagefilledrectangle(
|
|
$mask,
|
|
max(0, $width - $boxWidth - 8),
|
|
max(0, $height - $boxHeight - 8),
|
|
max(0, $width - 8),
|
|
max(0, $height - 8),
|
|
$white
|
|
);
|
|
imagepng($mask, $maskPath);
|
|
imagedestroy($mask);
|
|
|
|
$inpaintPromptId = ComfyUIService::submitEdit(
|
|
$model,
|
|
'Reconstruct the white masked region using the surrounding background texture, seamless and natural.',
|
|
$source['path'],
|
|
$maskPath,
|
|
'inpaint'
|
|
);
|
|
$inpaintOutputs = $waitForFinalOutputs($model, $inpaintPromptId, $userId);
|
|
$createdAttachments = array_merge($createdAttachments, $inpaintOutputs);
|
|
if ($inpaintOutputs === []) {
|
|
throw new RuntimeException('inpaint returned no output');
|
|
}
|
|
echo 'INPAINT PASS prompt_id=' . $inpaintPromptId . PHP_EOL;
|
|
echo 'RESULT 2/2 integration tasks passed' . PHP_EOL;
|
|
} catch (Throwable $exception) {
|
|
fwrite(STDERR, get_class($exception) . ': ' . $exception->getMessage() . PHP_EOL);
|
|
exit(1);
|
|
}
|