eaglelandsonce's picture
Rename index (2).html to index.html
29ac626 verified
Raw History Blame Contribute Delete
15.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ADK Multi-Agent Blog Simulation</title>
<style>
:root{
--bg:#f6f7fb;
--panel:#ffffff;
--card:#ffffff;
--text:#1f2937;
--muted:#6b7280;
--border:#d1d5db;
--accent:#2563eb;
--accent-bg:#dbeafe;
--ok:#16a34a;
--retry:#d97706;
}
*{box-sizing:border-box}
body{
margin:0;
font-family:Arial,Helvetica,sans-serif;
background:var(--bg);
color:var(--text);
}
.app{
max-width:1200px;
margin:0 auto;
padding:24px;
}
h1{margin:0 0 8px;font-size:28px}
.subtitle{color:var(--muted);margin-bottom:18px}
.controls{
display:flex;
flex-wrap:wrap;
gap:10px;
align-items:center;
margin-bottom:16px;
}
button,select{
font:inherit;
padding:10px 14px;
border-radius:8px;
border:1px solid var(--border);
background:#fff;
color:var(--text);
}
button{cursor:pointer}
button.primary{
background:var(--accent);
color:#fff;
border-color:var(--accent);
}
button:disabled{opacity:.5;cursor:not-allowed}
.spacer{flex:1}
.grid{
display:grid;
grid-template-columns:1fr 50px 1fr 50px 1fr;
gap:12px;
align-items:stretch;
}
.arrow{
display:flex;
align-items:center;
justify-content:center;
font-size:28px;
color:var(--muted);
}
.card,.panel{
background:var(--card);
border:2px solid var(--border);
border-radius:14px;
padding:16px;
transition:.2s ease;
}
.label{
font-size:12px;
text-transform:uppercase;
letter-spacing:.08em;
color:var(--muted);
font-weight:700;
}
.title{
margin-top:4px;
font-size:18px;
font-weight:700;
}
.node{
border:2px solid var(--border);
border-radius:10px;
padding:10px;
margin-top:10px;
background:#fff;
transition:.2s ease;
}
.down{
text-align:center;
color:var(--muted);
padding:6px 0 0;
}
.loop-note{
border:2px dashed var(--border);
border-radius:10px;
padding:9px;
margin-top:10px;
text-align:center;
font-size:12px;
color:var(--muted);
transition:.2s ease;
}
.status{
border:1px solid var(--border);
border-radius:9px;
padding:9px;
margin-top:12px;
font-size:14px;
}
.active{
border-color:var(--accent)!important;
background:var(--accent-bg)!important;
transform:translateY(-2px);
}
.ok{border-color:var(--ok)!important}
.retry{border-color:var(--retry)!important}
.pulse{animation:pulse .7s ease-in-out infinite alternate}
@keyframes pulse{from{opacity:.68}to{opacity:1}}
.panel{margin-top:14px;border-width:1px}
.panel-head{
display:flex;
gap:12px;
justify-content:space-between;
align-items:center;
flex-wrap:wrap;
}
.badge{
border:1px solid var(--border);
border-radius:999px;
padding:6px 10px;
font-size:13px;
}
.queue{
display:flex;
flex-wrap:wrap;
gap:8px;
margin-top:12px;
}
.topic{
border:1px solid var(--border);
border-radius:999px;
padding:6px 10px;
font-size:13px;
background:#fff;
}
.two-col{
display:grid;
grid-template-columns:1fr 1fr;
gap:14px;
margin-top:14px;
}
.state-row{
display:flex;
justify-content:space-between;
gap:16px;
border-bottom:1px solid var(--border);
padding:8px 0;
font-size:14px;
}
.state-row:last-child{border-bottom:0}
.mono{font-family:Consolas,monospace}
.muted{color:var(--muted)}
.log{
max-height:230px;
overflow:auto;
margin-top:10px;
font-size:14px;
line-height:1.5;
}
.final{margin-top:14px}
@media(max-width:850px){
.grid{grid-template-columns:1fr}
.arrow{transform:rotate(90deg)}
.two-col{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
.card,.node,.loop-note{transition:none}
.pulse{animation:none}
}
</style>
</head>
<body>
<div class="app" id="blogSim">
<h1>ADK Multi-Agent Blog Simulation</h1>
<div class="subtitle">Watch topics flow through the Blogger root agent, planner validation loop, writer validation loop, and final response.</div>
<div class="controls">
<button id="startBtn" class="primary" type="button">Start Simulation</button>
<button id="pauseBtn" type="button" disabled>Pause</button>
<button id="resetBtn" type="button">Reset</button>
<div class="spacer"></div>
<label>Speed
<select id="speed">
<option value="1400">Slow</option>
<option value="850" selected>Normal</option>
<option value="420">Fast</option>
</select>
</label>
</div>
<div class="grid">
<section id="rootCard" class="card">
<div class="label">Root Agent</div>
<div class="title">Blogger</div>
<p class="muted">Instruction-enforced sequence:</p>
<div class="mono">1. Planner → 2. Writer → 3. Final</div>
<div id="rootStatus" class="status">Waiting for topic</div>
</section>
<div class="arrow">→</div>
<section id="plannerCard" class="card">
<div class="label">LoopAgent · Max 3</div>
<div class="title">RobustBlogPlanner</div>
<div id="plannerAgent" class="node">BlogPlanner</div>
<div class="down">↓ blog_outline</div>
<div id="outlineValidator" class="node">OutlineValidationChecker</div>
<div id="plannerLoop" class="loop-note">retry ↻ until OK or 3 iterations</div>
</section>
<div class="arrow">→</div>
<section id="writerCard" class="card">
<div class="label">LoopAgent · Max 3</div>
<div class="title">RobustBlogWriter</div>
<div id="writerAgent" class="node">BlogWriter</div>
<div class="down">↓ blog_post</div>
<div id="postValidator" class="node">BlogPostValidationChecker</div>
<div id="writerLoop" class="loop-note">retry ↻ until OK or 3 iterations</div>
</section>
</div>
<section class="panel">
<div class="panel-head">
<div>
<div class="label">Incoming Topic Queue</div>
<div id="currentTopic" class="title">No topic active</div>
</div>
<div id="iterationBadge" class="badge">Idle</div>
</div>
<div id="topicQueue" class="queue"></div>
</section>
<div class="two-col">
<section class="panel" style="margin-top:0">
<div class="title">State / Output Keys</div>
<div class="state-row"><span class="mono">blog_outline</span><span id="outlineState" class="muted">empty</span></div>
<div class="state-row"><span class="mono">validation_result</span><span id="validationState" class="muted">empty</span></div>
<div class="state-row"><span class="mono">blog_post</span><span id="postState" class="muted">empty</span></div>
</section>
<section class="panel" style="margin-top:0">
<div class="title">Execution Log</div>
<div id="log" class="log"><div class="muted">Simulation ready.</div></div>
</section>
</div>
<section id="finalCard" class="panel final">
<div class="label">Final Response</div>
<div id="finalOutput" class="muted" style="margin-top:8px">Validated blog + 3 alternate titles + 2 tweet-length hooks will appear here.</div>
</section>
</div>
<script>
(() => {
const root = document.getElementById('blogSim');
const $ = id => document.getElementById(id);
const topics = [
'ADK Agent Patterns',
'Context Engineering',
'Vertex AI Memory',
'Agentic Coding Workflows',
'Responsible AI Guardrails'
];
let running = false;
let paused = false;
let stopped = false;
let topicIndex = 0;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const delay = () => Number($('speed').value) || 850;
async function waitStep(mult = 1) {
let remaining = delay() * mult;
while (remaining > 0 && !stopped) {
if (!paused) remaining -= 80;
await sleep(80);
}
}
function log(msg) {
const d = document.createElement('div');
d.textContent = msg;
$('log').appendChild(d);
$('log').scrollTop = $('log').scrollHeight;
}
function clearActive() {
root.querySelectorAll('.active,.pulse,.retry,.ok').forEach(el => {
el.classList.remove('active','pulse','retry','ok');
});
}
function activate(id, status) {
clearActive();
const el = $(id);
if (el) el.classList.add('active','pulse');
if (status) $('iterationBadge').textContent = status;
}
function renderQueue() {
$('topicQueue').innerHTML = '';
topics.forEach((t, i) => {
const span = document.createElement('span');
span.className = 'topic';
span.textContent = (i < topicIndex ? '✓ ' : '') + t;
if (i === topicIndex && running) span.classList.add('active');
$('topicQueue').appendChild(span);
});
}
async function planner(topic) {
const iterations = topicIndex % 2 === 0 ? 2 : 1;
for (let i = 1; i <= iterations; i++) {
activate('plannerAgent', `Planner iteration ${i}/3`);
$('rootStatus').textContent = 'Calling planner_tool';
log(`Planner: generating outline for “${topic}”`);
await waitStep();
$('outlineState').textContent = `outline v${i}`;
activate('outlineValidator', 'Validating outline');
log('OutlineValidationChecker: checking title, intro, 4–6 sections, conclusion');
await waitStep();
if (i < iterations) {
$('validationState').textContent = 'retry';
$('outlineValidator').classList.add('retry');
$('plannerLoop').classList.add('active');
log('Validation result: retry → LoopAgent starts next iteration');
await waitStep(.75);
} else {
$('validationState').textContent = 'ok';
$('outlineValidator').classList.add('ok');
log('Validation result: ok → planner returns control to Blogger');
await waitStep(.75);
}
}
}
async function writer(topic) {
const iterations = topicIndex % 3 === 0 ? 2 : 1;
for (let i = 1; i <= iterations; i++) {
activate('writerAgent', `Writer iteration ${i}/3`);
$('rootStatus').textContent = 'Calling writer_tool';
log('Writer: producing article from blog_outline');
await waitStep();
$('postState').textContent = `draft v${i}`;
activate('postValidator', 'Validating article');
log('BlogPostValidationChecker: checking structure and technical clarity');
await waitStep();
if (i < iterations) {
$('validationState').textContent = 'retry';
$('postValidator').classList.add('retry');
$('writerLoop').classList.add('active');
log('Validation result: retry → writer loop repeats');
await waitStep(.75);
} else {
$('validationState').textContent = 'ok';
$('postValidator').classList.add('ok');
log('Validation result: ok → writer returns control to Blogger');
await waitStep(.75);
}
}
}
async function processTopic(topic) {
$('currentTopic').textContent = topic;
$('outlineState').textContent = 'empty';
$('validationState').textContent = 'empty';
$('postState').textContent = 'empty';
$('finalOutput').textContent = 'Processing…';
activate('rootCard', 'Step 1 · Planner');
$('rootStatus').textContent = 'Topic received → planner first';
log(`Blogger received topic: “${topic}”`);
await waitStep(.8);
await planner(topic);
if (stopped) return;
activate('rootCard', 'Step 2 · Writer');
$('rootStatus').textContent = 'Planner complete → writer second';
log('Blogger: planner returned; invoking writer_tool');
await waitStep(.8);
await writer(topic);
if (stopped) return;
activate('finalCard', 'Step 3 · Final');
$('rootStatus').textContent = 'Writer complete → final response';
$('finalOutput').innerHTML =
`<strong>${topic}</strong><br>✓ Validated blog post<br>✓ 3 alternate titles<br>✓ 2 tweet-length hooks`;
log('Blogger: assembling final response with titles and hooks');
await waitStep();
clearActive();
}
async function run() {
if (running) return;
running = true;
paused = false;
stopped = false;
$('startBtn').disabled = true;
$('pauseBtn').disabled = false;
$('pauseBtn').textContent = 'Pause';
log('--- Simulation started ---');
renderQueue();
while (topicIndex < topics.length && !stopped) {
renderQueue();
await processTopic(topics[topicIndex]);
if (stopped) break;
topicIndex++;
renderQueue();
await waitStep(.5);
}
if (!stopped && topicIndex >= topics.length) {
$('currentTopic').textContent = 'All queued topics complete';
$('iterationBadge').textContent = 'Complete';
$('rootStatus').textContent = 'Waiting for next topic';
log('--- Queue complete ---');
}
running = false;
$('startBtn').disabled = false;
$('startBtn').textContent =
topicIndex >= topics.length ? 'Run Again' : 'Start Simulation';
$('pauseBtn').disabled = true;
}
$('startBtn').addEventListener('click', () => {
if (topicIndex >= topics.length) {
topicIndex = 0;
renderQueue();
}
run();
});
$('pauseBtn').addEventListener('click', () => {
paused = !paused;
$('pauseBtn').textContent = paused ? 'Resume' : 'Pause';
$('iterationBadge').textContent = paused ? 'Paused' : 'Running';
});
$('resetBtn').addEventListener('click', () => {
stopped = true;
paused = false;
running = false;
topicIndex = 0;
clearActive();
$('startBtn').disabled = false;
$('startBtn').textContent = 'Start Simulation';
$('pauseBtn').disabled = true;
$('pauseBtn').textContent = 'Pause';
$('currentTopic').textContent = 'No topic active';
$('iterationBadge').textContent = 'Idle';
$('rootStatus').textContent = 'Waiting for topic';
$('outlineState').textContent = 'empty';
$('validationState').textContent = 'empty';
$('postState').textContent = 'empty';
$('finalOutput').textContent =
'Validated blog + 3 alternate titles + 2 tweet-length hooks will appear here.';
$('log').innerHTML =
'<div class="muted">Simulation reset.</div>';
renderQueue();
});
renderQueue();
})();
</script>
</body>
</html>