// architecture-section.jsx — radial mindmap: agent-runtime at the core. const ArchitectureSection = () => { const ref = React.useRef(null); const inView = useInView(ref, { threshold: 0.15, once: true }); const [t, setT] = React.useState(0); useRaf((s) => setT(s), inView); // Canvas 1000 x 780, core at (500, 400) const CX = 500, CY = 400; // Nodes: each with polar position (angle in deg, radius) for layout clarity, // then converted to x/y. Angle 0 = right, 90 = down. // Three rings: // r=0 -> agent-runtime (core) // r=200 -> internal cluster pods it talks to // r=340 -> data substrate + LLM apis + external agents const polar = (deg, r) => { const rad = (deg - 90) * Math.PI / 180; // 0 deg = top return { x: CX + Math.cos(rad) * r, y: CY + Math.sin(rad) * r }; }; const nodes = [ // CORE · Agno Agents hub (replaces the box) { id: 'agent', kind: 'coreHub', label: 'Agno', sub: 'AGENTS', ...polar(0, 0), group: 'core', isCoreHub: true }, // INNER RING · cluster pods (angles chosen so links don't cross) { id: 'web', kind: 'pod', label: 'web', sub: 'Next.js 16 · Payload', tags: ['HPA 1-5', ':3000'], ...polar(320, 240), w: 200, h: 90, group: 'app' }, { id: 'mcp', kind: 'pod', label: 'mcp', sub: 'Bun · Typesense proxy', tags: [':3001'], ...polar(40, 240), w: 200, h: 82, group: 'app' }, { id: 'keycloak', kind: 'pod', label: 'keycloak', sub: 'OIDC · realm-job', tags: [':8080'], ...polar(180, 200), w: 200, h: 82, group: 'app' }, // OUTER RING · state / data (minio adjacent to postgres) { id: 'postgres', kind: 'db', label: 'postgres', sub: 'cloudnativepg', tags: ['payload', 'agno'], ...polar(265, 400), w: 180, h: 82, group: 'data' }, { id: 'minio', kind: 'db', label: 'minio', sub: 'StatefulSet · S3', tags: [':9000'], ...polar(245, 390), w: 170, h: 74, group: 'data' }, { id: 'typesense', kind: 'db', label: 'typesense', sub: 'hybrid search + RAG', tags: [':8108'], ...polar(80, 380), w: 200, h: 82, group: 'data' }, // OUTER RING · external LLM providers { id: 'anthropic', kind: 'ext', label: 'Anthropic', sub: 'chat models', ...polar(135, 360), w: 150, h: 60, group: 'ext' }, { id: 'openai', kind: 'ext', label: 'OpenAI', sub: 'embeddings', ...polar(110, 380), w: 150, h: 60, group: 'ext' }, // OUTER · external MCP clients (hub) { id: 'agents-hub', kind: 'hub', label: 'EXTERNAL', sub: 'CLAUDE CODE · CHATGPT', ...polar(50, 430), group: 'agents', isHub: true }, ]; const find = (id) => nodes.find(n => n.id === id); // Offset node bbox so x,y is the visual center nodes.forEach(n => { if (n.isHub || n.isCoreHub) { n._cx = n.x; n._cy = n.y; } else { n._cx = n.x; n._cy = n.y; n._left = n.x - n.w / 2; n._top = n.y - n.h / 2; } }); // Edges — ONLY real functional connections. No redundant postgres lines. const edges = [ // Core (Agno) → inner cluster { from: 'agent', to: 'web', label: 'PAYLOAD_URL', delay: 0.3 }, { from: 'agent', to: 'mcp', label: 'MCP_URL', delay: 0.5, accent: true }, // Core → its own state { from: 'agent', to: 'postgres', label: 'schema=agno', delay: 0.7, accent: true }, // Core → external LLMs { from: 'agent', to: 'anthropic', label: 'chat', delay: 0.9, dashed: true }, // Keycloak provides sessions to web, mcp and agent-runtime { from: 'keycloak', to: 'web', label: 'session', delay: 1.0, dashed: true }, { from: 'keycloak', to: 'mcp', label: 'session', delay: 1.1, dashed: true }, { from: 'keycloak', to: 'agent', label: 'session', delay: 1.2, dashed: true }, // App-level essentials (one line each) { from: 'web', to: 'postgres', label: 'payload', delay: 1.35 }, { from: 'web', to: 'minio', label: 'uploads', delay: 1.45, dashed: true }, { from: 'mcp', to: 'typesense', label: 'query', delay: 1.55 }, { from: 'typesense', to: 'openai', label: 'embed', delay: 1.7, dashed: true }, // External agents → mcp { from: 'agents-hub', to: 'mcp', label: 'stdio / sse', delay: 1.85, reverse: true }, ]; const revealT = Math.min(t, 4.0); // Compute edge geometry: from node center to node center, but stop at the node border. const edgePath = (a, b) => { // From center of a toward center of b, clipped to node rect or circle borders. const clipToRect = (node, tx, ty) => { if (node.isHub) { const dx = tx - node._cx, dy = ty - node._cy; const len = Math.hypot(dx, dy) || 1; return { x: node._cx + (dx / len) * 36, y: node._cy + (dy / len) * 36 }; } if (node.isCoreHub) { const dx = tx - node._cx, dy = ty - node._cy; const len = Math.hypot(dx, dy) || 1; return { x: node._cx + (dx / len) * 62, y: node._cy + (dy / len) * 62 }; } const hw = node.w / 2, hh = node.h / 2; const dx = tx - node._cx, dy = ty - node._cy; if (dx === 0 && dy === 0) return { x: node._cx, y: node._cy }; const tx1 = Math.abs(dx) > 0 ? hw / Math.abs(dx) : Infinity; const ty1 = Math.abs(dy) > 0 ? hh / Math.abs(dy) : Infinity; const tt = Math.min(tx1, ty1); return { x: node._cx + dx * tt, y: node._cy + dy * tt }; }; const p1 = clipToRect(a, b._cx, b._cy); const p2 = clipToRect(b, a._cx, a._cy); const mx = (p1.x + p2.x) / 2; const my = (p1.y + p2.y) / 2; return { d: `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`, p1, p2, mx, my }; }; return (
03 · Arquitectura · MINDMAP

El runtime al centro.
Agno orquesta,
el resto sirve.

Agno Agents orquesta el sistema: llama a web para Payload, a mcp para búsqueda, y persiste en el schema agno. keycloak provee sesión a los tres. Cada línea es una conexión real.

nexus · topology runtime-centric 12 nodes · 12 edges
{/* Concentric guide rings (very subtle) */} {[200, 340, 430].map((r, i) => ( ))} {/* Namespace label */} NAMESPACE · NEXUS {/* Core halo */} {/* Edges */} {edges.map((e, i) => { const srcId = e.reverse ? e.to : e.from; const dstId = e.reverse ? e.from : e.to; const src = find(srcId), dst = find(dstId); if (!src || !dst) return null; const { d, p1, p2, mx, my } = edgePath(src, dst); const len = Math.hypot(p2.x - p1.x, p2.y - p1.y); const appearT = Math.max(0, Math.min(1, (revealT - e.delay) / 0.55)); const phase = ((t * 0.32 + i * 0.17) % 1); const solidColor = e.accent ? '#0d0d0d' : 'rgba(13,13,13,0.45)'; // Place label at 38% of the line from the source (near core side), // then push it perpendicular so it doesn't sit on top of the edge. const t38 = 0.38; const baseX = p1.x + (p2.x - p1.x) * t38; const baseY = p1.y + (p2.y - p1.y) * t38; const vx = p2.x - p1.x, vy = p2.y - p1.y; const vlen = Math.hypot(vx, vy) || 1; // Perpendicular, chosen to push away from canvas center let nx = -vy / vlen, ny = vx / vlen; if ((baseX - CX) * nx + (baseY - CY) * ny < 0) { nx = -nx; ny = -ny; } const labX = baseX + nx * 10; const labY = baseY + ny * 10; return ( {appearT > 0.9 && !e.dashed && ( )} {e.label && appearT > 0.95 && ( {e.label} )} ); })} {/* Nodes */} {nodes.map((n) => { const groupDelay = ({ core: 0, app: 0.35, data: 0.9, ext: 1.1, agents: 1.5 })[n.group]; const appear = Math.max(0, Math.min(1, (revealT - groupDelay) / 0.4)); if (n.isCoreHub) { const pulse = 1 + Math.sin(t * 1.6) * 0.06; return ( Agno AGENTS FASTAPI · SCHEMA=AGNO ); } if (n.isHub) { const pulse = 1 + Math.sin(t * 2) * 0.08; return ( EXTERNAL CLAUDE CODE · CHATGPT ); } // Palette per kind let bg, fg, stroke, accent, tagBg, tagFg, kindLabel; if (n.kind === 'db') { bg = '#fff'; fg = '#0d0d0d'; stroke = 'rgba(13,13,13,0.5)'; accent = '#0d0d0d'; tagBg = 'rgba(13,13,13,0.06)'; tagFg = 'rgba(13,13,13,0.75)'; kindLabel = n.id === 'postgres' ? 'STATEFUL' : n.id === 'minio' ? 'STATEFULSET' : 'SERVICE'; } else if (n.kind === 'ext') { bg = 'rgba(13,13,13,0.04)'; fg = '#0d0d0d'; stroke = 'rgba(13,13,13,0.2)'; accent = 'rgba(13,13,13,0.3)'; tagBg = 'transparent'; tagFg = 'rgba(13,13,13,0.5)'; kindLabel = 'EXTERNAL'; } else { bg = '#0d0d0d'; fg = '#f9f9f9'; stroke = '#0d0d0d'; accent = '#a1f75e'; tagBg = 'rgba(161,247,94,0.12)'; tagFg = '#a1f75e'; kindLabel = 'POD'; } return ( {/* kind badge */} {kindLabel} {n.label} {n.sub} {/* tag chips */} {n.tags && n.tags.map((tag, j) => { const chipW = tag.length * 7.2 + 18; const perRow = Math.max(1, Math.floor((n.w - 18) / (chipW + 8))); const col = j % perRow; const row = Math.floor(j / perRow); const xOffset = 14 + col * (chipW + 8); const yOffset = (n.kind === 'ext' ? 44 : 60) + row * 18; return ( {tag} ); })} ); })}
{/* Legend footer */}
core runtime cluster pod state optional / async helm/nexus-template · argocd
{/* Pillars */}
{[ { label: 'AGNO AGENTS', text: 'El runtime en el centro. Habla con mcp, web y persiste en el schema agno. Llama a Anthropic para chat.', hl: true }, { label: 'WEB', text: 'Next.js 16 + Payload CMS. Fuente de verdad editorial y metadatos del producto.' }, { label: 'MCP', text: 'Wrapper Bun sobre Typesense. Expone tools de búsqueda híbrida + RAG a agentes externos.' }, { label: 'KEYCLOAK', text: 'OIDC centralizado. Provee sesión a web, mcp y al runtime de agentes. Realm-job de bootstrap.' }, ].map((p, i) => (
{p.label}

{p.text}

))}
); }; // Packet moving along an SVG path const PacketOnPath = ({ d, phase, accent }) => { const pathRef = React.useRef(null); const [pt, setPt] = React.useState({ x: 0, y: 0 }); React.useEffect(() => { if (!pathRef.current) return; const path = pathRef.current; const len = path.getTotalLength(); const p = path.getPointAtLength(phase * len); setPt({ x: p.x, y: p.y }); }, [phase, d]); return ( ); }; Object.assign(window, { ArchitectureSection });