152 lines
5.8 KiB
PHP
152 lines
5.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
header('Content-Type: text/html; charset=UTF-8');
|
|
|
|
$startedAt = microtime(true);
|
|
|
|
$apiKey = getenv('LLAMA_API_KEY') ?: '';
|
|
$model = getenv('LLAMA_MODEL') ?: 'local-model';
|
|
$maxTokens = (int) (getenv('LLAMA_MAX_TOKENS') ?: 4096);
|
|
$reasoningEffort = getenv('LLAMA_REASONING_EFFORT') ?: 'none';
|
|
|
|
$topicHint = isset($_GET['topic']) ? trim((string) $_GET['topic']) : '';
|
|
$topicHint = mb_substr($topicHint, 0, 120);
|
|
|
|
$seed = sprintf('seed-%08x%08x', random_int(0, 0xffffffff), random_int(0, 0xffffffff));
|
|
header('HX-Redirect: /' . $seed);
|
|
|
|
$topicCategories = [
|
|
'local restaurant landing page',
|
|
'fitness coaching brand site',
|
|
'travel destination mini-guide',
|
|
'event festival one-page promo',
|
|
'indie game launch page',
|
|
'architect portfolio page',
|
|
'music artist release page',
|
|
'bookstore seasonal campaign',
|
|
'pet adoption center homepage',
|
|
'artisan coffee roaster website',
|
|
'online course product page',
|
|
'nonprofit donation campaign page',
|
|
];
|
|
$selectedCategory = $topicCategories[array_rand($topicCategories)];
|
|
|
|
$bannedTerms = [
|
|
'chrono',
|
|
'temporal',
|
|
'chrono-',
|
|
'timewarp',
|
|
'timescape',
|
|
'timeshift',
|
|
'quantum',
|
|
'aether',
|
|
'epoch',
|
|
'singularity',
|
|
];
|
|
$bannedTermsList = implode(', ', $bannedTerms);
|
|
|
|
$factory = OpenAI::factory()->withBaseUri('http://localhost:8080/v1');
|
|
if ($apiKey !== '') {
|
|
$factory = $factory->withApiKey($apiKey);
|
|
}
|
|
|
|
$client = $factory->make();
|
|
|
|
$systemPrompt = <<<PROMPT
|
|
You are a senior creative web designer and front-end engineer.
|
|
|
|
Task:
|
|
- On every run, first choose a unique page concept with a specific topic, visual theme, and style direction.
|
|
- Then generate one complete, production-ready HTML document from scratch.
|
|
- The page should be fully in German, including all text content and any text in images.
|
|
|
|
Hard output rules:
|
|
- Return only raw HTML. No markdown. No code fences. No explanations.
|
|
- Output must begin with <!doctype html> and include full <html>, <head>, and <body>.
|
|
- Use Tailwind CSS via CDN in the same HTML document.
|
|
- Do not rely on local/external project files. Everything required must be inside this single HTML file.
|
|
- Inline any custom CSS and JavaScript in <style> and <script> tags.
|
|
- Make the page responsive and fully usable on mobile and desktop.
|
|
- Include accessible semantic structure and visible focus states.
|
|
|
|
Creative direction:
|
|
- Avoid generic templates.
|
|
- Use bold, intentional typography and layout.
|
|
- Include subtle but meaningful animation/interaction.
|
|
- Provide complete content, not placeholder lorem ipsum.
|
|
|
|
Diversity constraints:
|
|
- Do not make a sci-fi, futuristic, or time-themed website unless the user explicitly asks for it.
|
|
- Avoid these words in brand names, title, headings, and body copy: {$bannedTermsList}
|
|
- If no topic is provided by user, choose a grounded real-world business/topic from the requested category.
|
|
PROMPT;
|
|
|
|
$userPrompt = 'Create a complete one-page website now. Use random token: ' . $seed . '.';
|
|
if ($topicHint === '') {
|
|
$userPrompt .= ' Required category for this run: ' . $selectedCategory . '.';
|
|
}
|
|
if ($topicHint !== '') {
|
|
$userPrompt .= ' Topic request: ' . $topicHint . '.';
|
|
}
|
|
|
|
$request = [
|
|
'model' => $model,
|
|
'temperature' => 0.95,
|
|
'top_p' => 0.95,
|
|
'max_tokens' => $maxTokens,
|
|
'messages' => [
|
|
['role' => 'system', 'content' => $systemPrompt],
|
|
['role' => 'user', 'content' => $userPrompt],
|
|
],
|
|
'reasoning_effort' => $reasoningEffort,
|
|
];
|
|
|
|
try {
|
|
$response = $client->chat()->create($request);
|
|
$html = trim((string) ($response->choices[0]->message->content ?? ''));
|
|
|
|
if ($topicHint === '' && preg_match('/\b(?:chrono\w*|temporal\w*|quantum\w*|aether\w*|epoch\w*|singularity\w*|timeshift\w*|timescape\w*|timewarp\w*)\b/i', $html) === 1) {
|
|
$request['messages'][] = [
|
|
'role' => 'system',
|
|
'content' => 'Retry with a completely different, non-sci-fi, non-time-themed concept. Do not use banned words.',
|
|
];
|
|
$response = $client->chat()->create($request);
|
|
$html = trim((string) ($response->choices[0]->message->content ?? ''));
|
|
}
|
|
|
|
$elapsedSeconds = number_format(microtime(true) - $startedAt, 2);
|
|
|
|
// If the model wraps output in a markdown fence, unwrap it safely.
|
|
if (preg_match('/^```(?:html)?\s*(.*?)\s*```$/is', $html, $matches) === 1) {
|
|
$html = $matches[1];
|
|
}
|
|
|
|
if ($html === '' || stripos($html, '<html') === false) {
|
|
throw new RuntimeException('Model did not return a full HTML document.');
|
|
}
|
|
|
|
$timingBlock = '<div style="position:fixed;right:12px;bottom:12px;z-index:2147483647;background:rgba(17,24,39,.92);color:#fff;padding:8px 10px;border-radius:10px;font:12px/1.3 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;box-shadow:0 8px 20px rgba(0,0,0,.28)">Generated in ' . $elapsedSeconds . 's</div>';
|
|
if (stripos($html, '</body>') !== false) {
|
|
$html = preg_replace('/<\/body>/i', $timingBlock . '</body>', $html, 1) ?? ($html . $timingBlock);
|
|
} else {
|
|
$html .= $timingBlock;
|
|
}
|
|
|
|
//echo $html;
|
|
|
|
file_put_contents(__DIR__ . '/../pages/' . $seed . '.html', $html);
|
|
|
|
} catch (Throwable $e) {
|
|
http_response_code(500);
|
|
$message = htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
echo '<!doctype html><html><head><meta charset="UTF-8"><title>Generation Error</title>';
|
|
echo '<script src="https://cdn.tailwindcss.com"></script></head><body class="min-h-screen bg-zinc-950 text-zinc-100 p-8">';
|
|
echo '<h1 class="text-2xl font-bold mb-4">Page generation failed</h1>';
|
|
echo '<p class="text-zinc-300 mb-3">' . $message . '</p>';
|
|
echo '<p class="text-zinc-400 text-sm">Tip: set LLAMA_MODEL to a loaded model and optionally pass ?topic=your-idea in the URL.</p>';
|
|
echo '</body></html>';
|
|
} |