cdotsanghvi's picture
add multi-head demo as 4th-6th tabs; restore Why Liquid + Integration
083b138
Raw
History Blame Contribute Delete
46.6 kB
"""HTML rendering for the inference demo UI.
Styling follows the Liquid AI design system from liquid-lfm-cloud:
- Monochrome primary scale (#171717 primary, #f5f5f5 background)
- Semantic accents: emerald (#10B981), amber (#F59E0B), red (#EF4444), blue (#3B82F6)
- JetBrains Mono for technical/metric elements, system sans-serif for body
- 16px rounded cards with subtle shadows
- Pill-shaped buttons and badges
"""
from __future__ import annotations
from typing import Callable
import numpy as np
from src.data.generator import AMOUNT_RANGE_LABELS
from src.data.schema import VALUES_START
from src.demo.decode import TransactionDecoder
from src.demo.merchant_catalog import DemoMerchantCatalog, MCC_NAMES
# Liquid design tokens — light mode (liquid-lfm-cloud design system)
_BG = "#f5f5f5"
_BG_CARD = "#ffffff"
_BG_CARD_ALT = "#fafafa"
_BORDER = "rgba(0,0,0,0.1)"
_BORDER_SUBTLE = "rgba(0,0,0,0.05)"
_TEXT = "#171717"
_TEXT_MUTED = "#525252"
_TEXT_DIM = "#737373"
_ACCENT_BLUE = "#3B82F6"
_ACCENT_GREEN = "#10B981"
_ACCENT_AMBER = "#F59E0B"
_ACCENT_RED = "#EF4444"
_RADIUS_CARD = "16px"
_RADIUS_SM = "8px"
_FONT_MONO = "JetBrains Mono, ui-monospace, SFMono-Regular, monospace"
_FONT_SANS = "-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif"
def format_fraud_score(prob: float) -> str:
"""Format fraud probability as colored gauge bar."""
pct = prob * 100
if pct < 20:
color = _ACCENT_GREEN
risk = "LOW RISK"
elif pct < 60:
color = _ACCENT_AMBER
risk = "MEDIUM RISK"
else:
color = _ACCENT_RED
risk = "HIGH RISK"
bar_width = max(2, min(100, int(pct)))
return f"""
<div style="margin: 8px 0;">
<div style="font-family: {_FONT_MONO}; font-size: 24px; font-weight: 600;
color: {color}; margin-bottom: 4px; letter-spacing: -0.02em;">
{pct:.1f}%
<span style="font-size: 12px; font-weight: 500; opacity: 0.8;
letter-spacing: 0.05em;">{risk}</span>
</div>
<div style="background: #e5e5e5; border-radius: 9999px; height: 8px;
width: 100%; overflow: hidden;">
<div style="background: {color}; height: 100%; width: {bar_width}%;
border-radius: 9999px; transition: width 0.3s ease;"></div>
</div>
</div>
"""
def format_topk_predictions(
probs: np.ndarray,
k: int,
label_fn: Callable[[int], str],
) -> str:
"""Format top-k predictions as styled rows with probability bars."""
top_indices = np.argsort(probs)[::-1][:k]
rows = ""
for i, idx in enumerate(top_indices):
p = probs[idx] * 100
label = label_fn(int(idx))
bar_width = max(2, int(p * 2.5))
opacity = 1.0 - i * 0.15
weight = "600" if i == 0 else "400"
# Show "<0.1%" for probabilities that round to 0.0% at 1 decimal place.
# This happens with high-cardinality heads (10K merchants) when the model
# hasn't learned a meaningful distribution (e.g. random-init baseline).
p_str = f"{p:.1f}%" if p >= 0.05 else "<0.1%"
rows += f"""
<div style="display: flex; align-items: center; gap: 8px; padding: 4px 0;">
<div style="flex: 1; font-size: 13px; font-weight: {weight};
color: {_TEXT}; opacity: {opacity};">{label}</div>
<div style="width: 50px; text-align: right; font-family: {_FONT_MONO};
font-size: 12px; color: {_TEXT_MUTED};">{p_str}</div>
<div style="width: 120px;">
<div style="background: {_ACCENT_BLUE}; height: 6px; width: {bar_width}%;
border-radius: 9999px; opacity: {opacity};"></div>
</div>
</div>
"""
return f'<div style="padding: 4px 0;">{rows}</div>'
def format_merchant_predictions(
probs: np.ndarray,
merchant_catalog: DemoMerchantCatalog,
k: int = 5,
) -> str:
"""Top-k merchant predictions with names and categories."""
def label_fn(idx: int) -> str:
if idx < VALUES_START:
return "[special]"
mid = idx - VALUES_START
info = merchant_catalog.get(mid)
return f"{info.name} ({info.category})"
return format_topk_predictions(probs, k, label_fn)
def format_amount_predictions(probs: np.ndarray, k: int = 5) -> str:
"""Top-k amount range predictions."""
def label_fn(idx: int) -> str:
return AMOUNT_RANGE_LABELS.get(idx, f"Range {idx}")
return format_topk_predictions(probs, k, label_fn)
def format_mcc_predictions(probs: np.ndarray, k: int = 5) -> str:
"""Top-k MCC predictions with category names."""
def label_fn(idx: int) -> str:
if idx < VALUES_START:
return "[special]"
mcc_val = idx - VALUES_START
return MCC_NAMES.get(mcc_val, f"MCC-{mcc_val}")
return format_topk_predictions(probs, k, label_fn)
def format_timeline(decoder: TransactionDecoder, token_ids: np.ndarray) -> str:
"""Render transaction sequence as a scrollable table."""
txns = decoder.decode_sequence(token_ids)
header = f"""
<div style="max-height: 380px; overflow-y: auto; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; background: {_BG_CARD};">
<table style="width: 100%; border-collapse: collapse; font-size: 12px;
font-family: {_FONT_MONO};">
<thead>
<tr style="border-bottom: 1px solid {_BORDER}; position: sticky; top: 0;
background: {_BG_CARD}; z-index: 1;">
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;">Tx</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">When</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">Merchant</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">Category</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">Amount</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">Method</th>
<th style="padding: 8px; text-align: left; color: {_TEXT_DIM};
font-size: 10px; text-transform: uppercase;">Country</th>
</tr>
</thead>
<tbody>
"""
rows = ""
for txn in txns:
when = f"{txn.dow} {txn.hour}"
highlight = f"background: rgba(59, 130, 246, 0.08);" if txn.index == 63 else ""
rows += f"""
<tr style="border-bottom: 1px solid {_BORDER_SUBTLE}; {highlight}">
<td style="padding: 6px 8px; color: {_TEXT_DIM};">{txn.index}</td>
<td style="padding: 6px 8px; color: {_TEXT_MUTED};">{when}</td>
<td style="padding: 6px 8px; color: {_TEXT}; font-weight: 500;">{txn.merchant_name}</td>
<td style="padding: 6px 8px; color: {_TEXT_DIM};">{txn.merchant_category}</td>
<td style="padding: 6px 8px; color: {_TEXT_MUTED};">{txn.amount_range}</td>
<td style="padding: 6px 8px; color: {_TEXT_DIM};">{txn.entry_mode}</td>
<td style="padding: 6px 8px; color: {_TEXT_DIM};">{txn.country}</td>
</tr>
"""
return header + rows + "</tbody></table></div>"
def render_production_architecture() -> str:
"""LFM2.5 production architecture and how it applies to payments."""
_purple = "#7c3aed"
_purple_bg = "rgba(124,58,237,0.08)"
_purple_border = "rgba(124,58,237,0.25)"
def _layer_cell(label: str, idx: int, is_attn: bool) -> str:
bg = _purple_bg if is_attn else "rgba(16,185,129,0.08)"
bc = _purple_border if is_attn else "rgba(16,185,129,0.25)"
color = _purple if is_attn else _ACCENT_GREEN
return f"""<div style="flex: 1; padding: 6px 2px; background: {bg};
border: 1px solid {bc}; border-radius: 4px; text-align: center; min-width: 0;">
<div style="font-family: {_FONT_MONO}; font-size: 8px; color: {color};
font-weight: 600;">{label}</div>
<div style="font-family: {_FONT_MONO}; font-size: 7px; color: {_TEXT_DIM};">L{idx}</div>
</div>"""
layers_1_2b = [
("C", 0, False), ("C", 1, False), ("A", 2, True), ("C", 3, False),
("C", 4, False), ("A", 5, True), ("C", 6, False), ("C", 7, False),
("A", 8, True), ("C", 9, False), ("A", 10, True), ("C", 11, False),
("A", 12, True), ("C", 13, False), ("A", 14, True), ("C", 15, False),
]
layer_cells = "".join(_layer_cell(l, i, a) for l, i, a in layers_1_2b)
return f"""
<div style="max-width: 1100px; margin: 0 auto; padding: 16px;">
<!-- Header -->
<h2 style="margin: 0 0 4px 0; color: {_TEXT}; font-size: 22px; font-weight: 700;
letter-spacing: -0.02em;">
LFM2.5 for Payment Sequences
</h2>
<p style="color: {_TEXT_DIM}; font-size: 13px; margin: 0 0 24px 0; line-height: 1.5;">
The published LFM2.5 architecture adapted for structured transaction data.
Same hybrid conv-attention backbone. New per-feature embedding layer.
</p>
<!-- Config comparison table -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 24px;">
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 10px;">
Production Target</div>
<table style="width: 100%; font-size: 12px; border-collapse: collapse;">
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Parameters</td>
<td style="padding: 3px 0; color: {_TEXT}; font-weight: 600; text-align: right;">1.2B</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Hidden dim</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">2048</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Layers</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">
<span style="color: {_ACCENT_GREEN};">10 conv</span> + <span style="color: {_purple};">6 attn</span></td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Attention</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">32Q / 8KV (GQA)</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">MLP</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">SwiGLU 12288</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Sequence</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">128 tx &times; 30 feat = 3,840</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Latency target</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_ACCENT_GREEN}; font-weight: 600; text-align: right;">&lt; 50ms (H100)</td></tr>
</table>
</div>
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 10px;">
This Demo (Reference)</div>
<table style="width: 100%; font-size: 12px; border-collapse: collapse;">
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Parameters</td>
<td style="padding: 3px 0; color: {_TEXT}; font-weight: 600; text-align: right;">9.8M</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Hidden dim</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">256</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Layers</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">
<span style="color: {_ACCENT_GREEN};">5 conv</span> + <span style="color: {_purple};">3 attn</span></td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Attention</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">4Q / 2KV (GQA)</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">MLP</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">SwiGLU 1024</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Sequence</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">64 tx &times; 15 feat = 960</td></tr>
<tr><td style="padding: 3px 0; color: {_TEXT_DIM};">Measured latency</td>
<td style="padding: 3px 0; font-family: {_FONT_MONO}; color: {_TEXT}; text-align: right;">&lt; 80ms (CPU)</td></tr>
</table>
</div>
</div>
<!-- LFM2.5-1.2B layer diagram -->
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 24px;">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 12px;">
LFM2.5-1.2B Layer Pattern (16 layers)</div>
<div style="display: flex; gap: 3px; margin-bottom: 8px;">
{layer_cells}
</div>
<div style="display: flex; gap: 16px; font-family: {_FONT_MONO}; font-size: 10px;">
<span style="color: {_ACCENT_GREEN};">&#9632; Conv (10): local patterns, k=3, O(n)</span>
<span style="color: {_purple};">&#9632; Attention (6): global context, GQA, O(n&sup2;)</span>
</div>
<div style="margin-top: 8px; font-size: 11px; color: {_TEXT_DIM}; line-height: 1.5;">
First and last layers are convolutional. Attention is densest mid-stack.
The LM head reads from a local-conv output, but 6 attention layers have already
encoded global context upstream.
</div>
</div>
<!-- Three key architectural adaptations -->
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; margin-bottom: 24px;">
<!-- Embedding adaptation -->
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="font-size: 14px; font-weight: 600; color: {_ACCENT_BLUE}; margin-bottom: 6px;">
Per-Feature Embedding</div>
<p style="font-size: 12px; color: {_TEXT_MUTED}; line-height: 1.5; margin: 0 0 8px 0;">
Text LFM2 has one embedding table. Payment LFM2 has one table per feature
(hour, merchant, MCC, amount, ...) plus a feature-type table. Summed to produce
the same (B, T, D) the backbone expects.</p>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
padding: 6px 8px; background: {_BG_CARD_ALT}; border-radius: 6px;">
value_tables[f](token) + type_table(f)</div>
</div>
<!-- KV-cache advantage -->
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="font-size: 14px; font-weight: 600; color: {_ACCENT_GREEN}; margin-bottom: 6px;">
KV-Cache Advantage</div>
<p style="font-size: 12px; color: {_TEXT_MUTED}; line-height: 1.5; margin: 0 0 8px 0;">
Only 6 of 16 layers need KV cache (attention layers only). A pure-attention
model at the same depth needs 16 layers of cache. At 3,840 token sequences
with 1,024 concurrent requests:</p>
<div style="font-family: {_FONT_MONO}; font-size: 11px;">
<span style="color: {_ACCENT_GREEN}; font-weight: 600;">LFM2: ~25 GB</span>
<span style="color: {_TEXT_DIM};"> vs </span>
<span style="color: {_ACCENT_RED};">Pure attn: ~64 GB</span>
</div>
</div>
<!-- Weight-tied heads -->
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="font-size: 14px; font-weight: 600; color: {_purple}; margin-bottom: 6px;">
Weight-Tied Heads</div>
<p style="font-size: 12px; color: {_TEXT_MUTED}; line-height: 1.5; margin: 0 0 8px 0;">
Downstream heads that predict pretrained features (next merchant, MCC)
project through the backbone's own embedding table. Zero extra parameters,
+50% accuracy vs fresh MLP heads.</p>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
padding: 6px 8px; background: {_BG_CARD_ALT}; border-radius: 6px;">
adapter(h) @ embedding.weight.T</div>
</div>
</div>
<!-- How it translates -->
<div style="padding: 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 16px;">
<div style="font-size: 14px; font-weight: 600; color: {_TEXT}; margin-bottom: 10px;">
How This Translates to Your Implementation</div>
<div style="display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; font-size: 12px;">
<div style="font-family: {_FONT_MONO}; color: {_ACCENT_BLUE}; font-weight: 600;">Schema</div>
<div style="color: {_TEXT_MUTED};">Your features, your vocab sizes, your ordering. We review the schema design.</div>
<div style="font-family: {_FONT_MONO}; color: {_ACCENT_BLUE}; font-weight: 600;">Pretrain</div>
<div style="color: {_TEXT_MUTED};">Self-supervised on your unlabeled transactions. No fraud labels needed.</div>
<div style="font-family: {_FONT_MONO}; color: {_ACCENT_BLUE}; font-weight: 600;">Fine-tune</div>
<div style="color: {_TEXT_MUTED};">Attach task heads (fraud, disputes, auth optimization). Multi-task with shared backbone.</div>
<div style="font-family: {_FONT_MONO}; color: {_ACCENT_BLUE}; font-weight: 600;">Deploy</div>
<div style="color: {_TEXT_MUTED};">Your infra, your GPUs, your compliance. Sub-100ms for real-time authorization decisioning.</div>
<div style="font-family: {_FONT_MONO}; color: {_ACCENT_BLUE}; font-weight: 600;">Own</div>
<div style="color: {_TEXT_MUTED};">No data leaves your infrastructure. No external API dependency. You own the model.</div>
</div>
</div>
<!-- Architecture source -->
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM}; text-align: center;">
Architecture: <a href="https://arxiv.org/abs/2511.23404" style="color: {_ACCENT_BLUE}; text-decoration: none;">
arXiv 2511.23404</a> &middot;
Weights: <a href="https://huggingface.co/LiquidAI" style="color: {_ACCENT_BLUE}; text-decoration: none;">
huggingface.co/LiquidAI</a>
</div>
</div>
"""
def render_comparison_header() -> str:
"""Header explaining the pretrained vs random-init comparison."""
return f"""
<div style="padding: 10px 14px; background: rgba(245,158,11,0.06);
border: 1px solid rgba(245,158,11,0.2); border-radius: {_RADIUS_SM};
margin-bottom: 12px; font-size: 12px; color: {_ACCENT_AMBER}; line-height: 1.5;">
<b>Pretrained vs Random Init:</b>
<span style="color: {_TEXT_MUTED};">Same architecture, same input, same fine-tuning data.
Left: pretrained on 200K unlabeled sequences first. Right: trained from scratch.
The difference is the value of self-supervised pretraining.</span>
</div>
"""
def render_why_liquid() -> str:
"""Render the 'Why Liquid AI' value proposition tab.
Content drawn from docs/architecture-walkthrough.md §14.
Uses measured reference-implementation numbers, not marketing claims.
"""
def _table_row(cells: list[str], bold_last: bool = False) -> str:
tds = ""
for i, c in enumerate(cells):
weight = "600" if (bold_last and i == len(cells) - 1) else "400"
align = "right" if i > 0 else "left"
tds += (
f'<td style="padding: 5px 10px; font-family: {_FONT_MONO}; font-size: 11px;'
f' color: {_TEXT}; font-weight: {weight}; text-align: {align};">{c}</td>'
)
return f"<tr style='border-bottom: 1px solid {_BORDER_SUBTLE};'>{tds}</tr>"
def _table_header(cols: list[str]) -> str:
ths = ""
for i, c in enumerate(cols):
align = "right" if i > 0 else "left"
ths += (
f'<th style="padding: 6px 10px; font-size: 10px; color: {_TEXT_DIM};'
f' text-transform: uppercase; letter-spacing: 0.05em; text-align: {align};'
f' font-weight: 600;">{c}</th>'
)
return f"<tr style='border-bottom: 1px solid {_BORDER};'>{ths}</tr>"
return f"""
<div style="max-width: 1100px; margin: 0 auto; padding: 16px;">
<h2 style="margin: 0 0 4px 0; color: {_TEXT}; font-size: 22px; font-weight: 700;
letter-spacing: -0.02em;">
Why LFM2.5 for Transaction Sequences
</h2>
<p style="color: {_TEXT_DIM}; font-size: 13px; margin: 0 0 24px 0; line-height: 1.5;">
Three claims, each backed by a different kind of evidence. Serving cost is arithmetic.
Label efficiency is measured. Architectural fit is a first-principles argument.
</p>
<!-- 1. Serving cost -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 12px;">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
1. Serving Cost Scales Better Than Pure Attention
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 13px; line-height: 1.6; margin: 0 0 12px 0;">
10 of 16 layers use O(n) conv instead of O(n&sup2;) attention. The gap is structural
and compounds as sequence length grows. Measured at matched parameter count on the same
hardware:
</p>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 8px;">
{_table_header(["Sequence", "Tokens", "Hybrid", "Pure Attn", "Speedup"])}
{_table_row(["64 tx &times; 15 feat", "960", "5.96 ms", "9.01 ms", "1.5x"])}
{_table_row(["64 tx &times; 30 feat", "1,920", "13.05 ms", "23.84 ms", "1.8x"])}
{_table_row(["128 tx &times; 30 feat", "3,840", "35.65 ms", "75.95 ms",
"<b>2.1x</b>"], bold_last=False)}
{_table_row(["256 tx &times; 30 feat", "7,680", "119 ms", "283 ms",
"<b>2.4x</b>"], bold_last=False)}
</table>
<p style="font-size: 11px; color: {_TEXT_DIM}; margin: 0; line-height: 1.5;">
At 3,840 tokens (production target), the FLOP analysis predicts 7.7% fewer operations.
The actual speedup is larger because conv layers achieve better hardware utilization.
At 7,680 tokens, savings reach 17% in FLOPs and 2.4x in wall-clock.
</p>
</div>
<!-- 2. Label scarcity -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 12px;">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
2. Pretraining Gets to 98% of Full-Data Quality with 10% of Labels
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 13px; line-height: 1.6; margin: 0 0 12px 0;">
Self-supervised pretraining on unlabeled transactions. No fraud labels needed.
Then fine-tune with whatever labels you have. The pretrained model at 10% labels
beats the random-init baseline at 100% labels.
</p>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 8px;">
{_table_header(["Labels", "Sequences", "Pretrained PR-AUC", "Baseline PR-AUC", "Delta"])}
{_table_row(["1%", "1,700", "<b>0.539</b>", "0.046", "<b>+0.493</b>"])}
{_table_row(["10%", "17,000", "<b>0.948</b>", "0.690", "<b>+0.258</b>"])}
{_table_row(["100%", "170,000", "<b>0.964</b>", "0.922", "+0.041"])}
</table>
<p style="font-size: 11px; color: {_TEXT_DIM}; margin: 0; line-height: 1.5;">
At 1% labels the baseline has learned nothing (0.046 is random guessing).
The pretrained model is already useful. This is the defining advantage for
institutions with billions of unlabeled transactions and limited fraud labels.
</p>
</div>
<!-- 3. Convergence + multi-head -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px;">
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
5x Convergence Speed
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 12px; line-height: 1.6; margin: 0 0 8px 0;">
The pretrained model hit 0.959 PR-AUC at step 1,000. The random-init baseline
never reached that level in 5,000 steps (peaked at 0.922).
</p>
<p style="color: {_TEXT_MUTED}; font-size: 12px; line-height: 1.6; margin: 0;">
For quarterly model refreshes, this cuts GPU-hours per cycle by 80%.
For weekly refreshes, it is the difference between feasible and infeasible.
</p>
</div>
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
One Backbone, Many Tasks
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 12px; line-height: 1.6; margin: 0 0 8px 0;">
A single forward pass serves fraud detection, next-merchant prediction, amount
forecasting, and merchant category classification. Add dispute prediction, default
risk, or authorization optimization as new heads without retraining the backbone.
</p>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
padding: 6px 8px; background: {_BG_CARD_ALT}; border-radius: 6px;">
Tied-embedding heads: +50% merchant accuracy at zero parameter cost
</div>
</div>
</div>
<!-- 4. Architectural fit -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 12px;">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
3. The Architecture Matches Transaction Data Structure
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 13px; line-height: 1.6; margin: 0 0 8px 0;">
Transaction data is not like text. Information density is concentrated locally
(within-transaction feature correlations, adjacent-transaction continuity) with sparse
global signal (behavioral baselines across the full history). LFM2.5 allocates
O(n) conv to the dense local patterns and O(n&sup2;) attention to the sparse
global patterns. A pure transformer allocates O(n&sup2;) compute uniformly
across all distances.
</p>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;">
<div style="padding: 10px; background: {_BG_CARD_ALT}; border-radius: 8px;">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT};
font-weight: 600; margin-bottom: 4px;">Within Transaction</div>
<div style="font-size: 11px; color: {_TEXT_DIM}; line-height: 1.4;">
Merchant determines MCC. Entry mode correlates with amount. Dense, local,
often deterministic. A 3-wide conv kernel captures this.
</div>
</div>
<div style="padding: 10px; background: {_BG_CARD_ALT}; border-radius: 8px;">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT};
font-weight: 600; margin-bottom: 4px;">Adjacent Transactions</div>
<div style="font-size: 11px; color: {_TEXT_DIM}; line-height: 1.4;">
Strong temporal continuity. A customer at Starbucks is likely at a similar
merchant next. The conditional distribution of t+1 given t is heavily peaked.
</div>
</div>
<div style="padding: 10px; background: {_BG_CARD_ALT}; border-radius: 8px;">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT};
font-weight: 600; margin-bottom: 4px;">Distant Transactions</div>
<div style="font-size: 11px; color: {_TEXT_DIM}; line-height: 1.4;">
Weak but non-zero signal. Behavioral profile matters for fraud baseline,
but per-position information density is thin. This is where attention earns its cost.
</div>
</div>
</div>
</div>
<!-- 5. Data ownership -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 12px;">
<h3 style="color: {_TEXT}; margin: 0 0 4px 0; font-size: 15px; font-weight: 600;">
Your Data, Your Model, Your Infrastructure
</h3>
<p style="color: {_TEXT_MUTED}; font-size: 13px; line-height: 1.6; margin: 0;">
Liquid licenses the architecture and training recipe. You train on your proprietary
data behind your firewall. No data leaves your infrastructure. No dependency on
external model APIs. The result is a foundation model you own, optimized for your
specific transaction patterns, deployed on your hardware.
</p>
</div>
<!-- What we claim / don't claim -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 16px;">
<h3 style="color: {_TEXT}; margin: 0 0 8px 0; font-size: 14px; font-weight: 600;">
What We Claim vs What We Don't
</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 12px;">
<div>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT};
font-weight: 600; margin-bottom: 6px; text-transform: uppercase;
letter-spacing: 0.05em;">We claim</div>
<ul style="margin: 0; padding-left: 14px; color: {_TEXT_MUTED}; line-height: 1.6;">
<li>Fewer FLOPs per forward pass above ~1,500 tokens (arithmetic)</li>
<li>Cost gap widens with sequence length (structural, measured 2.1-2.4x)</li>
<li>Pretraining dramatically improves low-label performance (measured)</li>
<li>Full pipeline works end-to-end on LFM2.5 (built it)</li>
</ul>
</div>
<div>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
font-weight: 600; margin-bottom: 6px; text-transform: uppercase;
letter-spacing: 0.05em;">We don't claim</div>
<ul style="margin: 0; padding-left: 14px; color: {_TEXT_MUTED}; line-height: 1.6;">
<li>Quality advantage over pure transformers at matched scale</li>
<li>Absolute latency numbers extrapolated from 10M to 1.2B</li>
<li>Superiority on all tasks (the advantage is specific to structured sequences)</li>
<li>Real-data validation (all results are on synthetic data)</li>
</ul>
</div>
</div>
</div>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM}; text-align: center;">
Source: <a href="https://arxiv.org/abs/2511.23404" style="color: {_TEXT_DIM};
text-decoration: underline;">arXiv 2511.23404</a> &middot;
Reference implementation measured at 9.85M params, Apple Silicon
</div>
</div>
"""
def render_integration_guide() -> str:
"""Render high-abstraction integration architecture flow.
Distills docs/integration-guide.md into a visual pipeline overview.
Monochrome design -- no rainbow pills or per-card color coding.
"""
def _phase_card(num: str, title: str, body: str, detail: str) -> str:
return f"""
<div style="padding: 14px 16px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD};">
<div style="display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px;">
<span style="font-family: {_FONT_MONO}; font-size: 11px; color: {_TEXT_DIM};
font-weight: 600;">{num}</span>
<span style="font-size: 14px; font-weight: 600; color: {_TEXT};">{title}</span>
</div>
<p style="font-size: 12px; color: {_TEXT_MUTED}; line-height: 1.5; margin: 0 0 8px 0;">
{body}</p>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
padding: 6px 8px; background: {_BG_CARD_ALT}; border-radius: 6px;
line-height: 1.5;">
{detail}</div>
</div>"""
def _gotcha(num: str, title: str, desc: str) -> str:
return f"""
<div style="display: flex; gap: 8px; padding: 5px 0;
border-bottom: 1px solid {_BORDER_SUBTLE};">
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM};
font-weight: 600; min-width: 18px;">{num}.</div>
<div>
<span style="font-size: 12px; font-weight: 600; color: {_TEXT};">{title}</span>
<span style="font-size: 12px; color: {_TEXT_MUTED};"> -- {desc}</span>
</div>
</div>"""
return f"""
<div style="max-width: 1100px; margin: 0 auto; padding: 16px;">
<h2 style="margin: 0 0 4px 0; color: {_TEXT}; font-size: 22px; font-weight: 700;
letter-spacing: -0.02em;">
Integration Architecture
</h2>
<p style="color: {_TEXT_DIM}; font-size: 13px; margin: 0 0 20px 0; line-height: 1.5;">
Seven phases from raw transaction data to production fraud scoring.
Each phase has a clear input, output, and set of decisions.
</p>
<!-- Pipeline flow -->
<div style="display: flex; align-items: center; justify-content: center; gap: 6px;
margin-bottom: 24px; padding: 10px 0; flex-wrap: wrap;">
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Schema</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Tokenize</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Embed</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Backbone</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Pretrain</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Heads</span>
<span style="color: {_TEXT_DIM}; font-size: 12px;">&rarr;</span>
<span style="padding: 5px 12px; background: {_TEXT}; color: #fff;
border-radius: 9999px; font-family: {_FONT_MONO};
font-size: 10px; font-weight: 600;">Deploy</span>
</div>
<!-- Phase cards -->
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px;">
{_phase_card("1", "Schema Design",
"Define your features, vocab sizes, and feature ordering. This is the contract "
"between your data team and your ML team. Three feature types: categorical "
"(direct index), ordinal (hour, day-of-week), and bucketed-continuous (amount "
"into quantile bins). Spend two weeks here.",
"3 reserved tokens per feature (MASK, OOV, NULL) &nbsp;|&nbsp; "
"Order features by semantic family for the conv window &nbsp;|&nbsp; "
"Embed schema fingerprint in checkpoint metadata")}
{_phase_card("2", "Tokenization",
"Convert raw transaction fields into integer token IDs. Categorical features "
"map directly to vocab indices. Continuous values (amount, days-since-last) get "
"quantile-bucketed into N bins. High-cardinality features (merchant_id) need the "
"long tail bucketed or factored into orthogonal features.",
"amount &rarr; 16-256 quantile bins &nbsp;|&nbsp; "
"merchant_id: top 10K distinct, rest into ~1K frequency buckets &nbsp;|&nbsp; "
"Unseen values at inference &rarr; OOV token (ID 1)")}
{_phase_card("3", "Structured Embedding",
"One embedding table per feature (sized to its vocab) plus a feature-type table. "
"Summed to produce the (B, T*F, D) tensor the backbone expects. "
"No raw continuous features -- everything goes through an embedding table.",
"value_tables[f](token) + type_table(f) &nbsp;|&nbsp; "
"High-cardinality features dominate param budget -- "
"bucket the merchant long tail")}
{_phase_card("4", "Backbone Config",
"Start from a published LFM2 scale point (350M, 700M, 1.2B, 2.6B). "
"Keep the conv-to-attention ratio, GQA config, SwiGLU MLP, and RoPE theta. "
"Do not deviate without a specific reason.",
"10:6 conv:attn at 1.2B &nbsp;|&nbsp; 32Q/8KV GQA &nbsp;|&nbsp; "
"QK-RMSNorm before RoPE &nbsp;|&nbsp; theta=1M &nbsp;|&nbsp; "
"First and last layers are conv")}
{_phase_card("5", "Pretraining",
"Self-supervised on your unlabeled transactions. No fraud labels needed. "
"Causal next-feature prediction or masked-transaction prediction. "
"Average per-feature losses (do NOT sum).",
"AdamW lr=3e-4, betas=(0.9, 0.95), wd=0.1 &nbsp;|&nbsp; "
"Cosine decay to 10% &nbsp;|&nbsp; BF16 (loss in FP32) &nbsp;|&nbsp; "
"Chinchilla: ~20 tokens per parameter")}
{_phase_card("6", "Downstream Heads",
"Attach task-specific heads. The critical decision: "
"TiedEmbeddingHead for features that were in pretraining vocab "
"(next_merchant, MCC). Fresh MLP for everything else (fraud, disputes).",
"Tied head: adapter(h) @ embedding.weight.T (+50% accuracy) &nbsp;|&nbsp; "
"Pool: last_tx_mean for sequence tasks, pre_last_tx for next-tx &nbsp;|&nbsp; "
"Dual LR: backbone 5e-5, heads 1e-3")}
</div>
<!-- Deployment gets its own full-width card -->
<div style="margin-bottom: 20px;">
{_phase_card("7", "Deployment",
"KV cache only on 6 attention layers (not 16). Dynamic batching with "
"5-10ms collection window. Conv-dominant models quantize cleanly to INT8. "
"Same model runs on GPU or CPU.",
"LFM2 @ 3,840 tokens: ~25 GB KV at 1K concurrent &nbsp;|&nbsp; "
"Pure attn: ~64 GB &nbsp;|&nbsp; "
"Sub-100ms on H100 for real-time auth decisioning")}
</div>
<!-- Gotchas -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 20px;">
<div style="font-size: 14px; font-weight: 600; color: {_TEXT}; margin-bottom: 8px;">
Top Gotchas
</div>
{_gotcha("1", "Per-feature loss summing",
"high-cardinality features dominate. Average, don't sum.")}
{_gotcha("2", "Fresh MLP for pretrained features",
"next_merchant stays at random. Use TiedEmbeddingHead.")}
{_gotcha("3", "Schema-checkpoint drift",
"training on schema v3, deploying on v2. Embed fingerprint in metadata.")}
{_gotcha("4", "Frozen backbone",
"PR-AUC drops from 0.96 to 0.16. Plan to fine-tune.")}
{_gotcha("5", "Pool strategy leak",
"last_tx_mean for next-tx prediction leaks the target. Use pre_last_tx.")}
</div>
<!-- Engagement model -->
<div style="padding: 16px 20px; background: {_BG_CARD}; border: 1px solid {_BORDER};
border-radius: {_RADIUS_CARD}; margin-bottom: 16px;">
<div style="font-size: 14px; font-weight: 600; color: {_TEXT}; margin-bottom: 10px;">
Typical Engagement
</div>
<table style="width: 100%; border-collapse: collapse;">
<tr style="border-bottom: 1px solid {_BORDER};">
<th style="padding: 6px 10px; text-align: left; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600;">Phase</th>
<th style="padding: 6px 10px; text-align: left; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; font-weight: 600;">Duration</th>
<th style="padding: 6px 10px; text-align: left; font-size: 10px; color: {_TEXT_DIM};
text-transform: uppercase; font-weight: 600;">What Happens</th>
</tr>
<tr style="border-bottom: 1px solid {_BORDER_SUBTLE};">
<td style="padding: 5px 10px; font-size: 12px; font-weight: 600;
color: {_TEXT};">Discovery</td>
<td style="padding: 5px 10px; font-family: {_FONT_MONO}; font-size: 11px;
color: {_TEXT_MUTED};">2-4 weeks</td>
<td style="padding: 5px 10px; font-size: 12px; color: {_TEXT_MUTED};">
Schema definition, data sample (~1M sequences), compliance review, architectural fit assessment</td>
</tr>
<tr style="border-bottom: 1px solid {_BORDER_SUBTLE};">
<td style="padding: 5px 10px; font-size: 12px; font-weight: 600;
color: {_TEXT};">POC</td>
<td style="padding: 5px 10px; font-family: {_FONT_MONO}; font-size: 11px;
color: {_TEXT_MUTED};">2 weeks</td>
<td style="padding: 5px 10px; font-size: 12px; color: {_TEXT_MUTED};">
Pretrain + fine-tune on your data sample, measurement report, go/no-go recommendation</td>
</tr>
<tr style="border-bottom: 1px solid {_BORDER_SUBTLE};">
<td style="padding: 5px 10px; font-size: 12px; font-weight: 600;
color: {_TEXT};">Production</td>
<td style="padding: 5px 10px; font-family: {_FONT_MONO}; font-size: 11px;
color: {_TEXT_MUTED};">3-6 months</td>
<td style="padding: 5px 10px; font-size: 12px; color: {_TEXT_MUTED};">
Engineering team builds, Liquid provides architectural support, weekly design review</td>
</tr>
<tr>
<td style="padding: 5px 10px; font-size: 12px; font-weight: 600;
color: {_TEXT};">Scale</td>
<td style="padding: 5px 10px; font-family: {_FONT_MONO}; font-size: 11px;
color: {_TEXT_MUTED};">Ongoing</td>
<td style="padding: 5px 10px; font-size: 12px; color: {_TEXT_MUTED};">
Operations, monitoring, retraining cadence, architecture evolution</td>
</tr>
</table>
</div>
<div style="font-family: {_FONT_MONO}; font-size: 10px; color: {_TEXT_DIM}; text-align: center;">
Full guide: docs/integration-guide.md &middot;
Architecture: <a href="https://arxiv.org/abs/2511.23404" style="color: {_TEXT_DIM};
text-decoration: underline;">arXiv 2511.23404</a>
</div>
</div>
"""