{"id":21733,"date":"2026-08-22T18:43:36","date_gmt":"2026-08-22T16:43:36","guid":{"rendered":"https:\/\/www.juust.org\/?p=21733"},"modified":"2026-08-22T18:57:44","modified_gmt":"2026-08-22T16:57:44","slug":"contextual-bandit-linucb-vs-vector-native","status":"publish","type":"post","link":"https:\/\/www.juust.org\/index.php\/contextual-bandit-linucb-vs-vector-native\/2026\/08\/","title":{"rendered":"Contextual bandit : LinUCB vs. Vector-Native"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/www.juust.org\/index.php\/category\/optimisation\/\">Optimize<\/a> for Fast Decisions or Fast Outcomes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/><strong>Github repo: <a href=\"https:\/\/github.com\/juustesout\/contextual-vector-bandit-in-typescript\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/juustesout\/contextual-vector-bandit-in-typescript<\/a><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We built and evaluated a standalone contextual bandit system to answer a practical product question: when should you prefer a classic per-arm approach like LinUCB, and when does a vector-native shared model create better outcomes?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The short answer is nuanced and useful:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB is often stronger when each arm behaves independently and there is little transferable structure between actions.<br \/>A vector-native bandit is often stronger when actions share meaningful context or embedding similarity, because each update can generalize to nearby actions.<br \/>If you optimize only for per-decision compute latency, classic methods can look better.<br \/>If you optimize for time-to-outcome (for example, how many interactions it takes to reach a successful event), vector-native methods can become compelling even with higher compute per decision.<br \/>The main conclusion is not \u201cone algorithm wins.\u201d<br \/>The main conclusion is: choose the algorithm that matches the structure of your action space and the business objective you actually care about.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why We Ran This Evaluation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In real recommendation and decision systems, teams often default to one of two narratives:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u201cUse a simple bandit, keep inference cheap.\u201d<br \/>\u201cUse embeddings everywhere, transfer learning across actions.\u201d<br \/>Both narratives can be right, both can be wrong, and both are incomplete without context.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most teams also benchmark with narrow metrics, usually average inference time and aggregate reward. Those are useful, but insufficient for decision-making. A model can be slower per call and still deliver better business outcomes if it needs fewer interactions to reach success.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So we set up the evaluation around a broader question:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">How does each approach behave across different environment structures and different objective lenses, including outcome-oriented metrics?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What We Compared<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We compared:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Classic LinUCB (disjoint, per-arm model)<br \/>Vector-native bandit (shared model over joint context-action representation)<br \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB keeps independent statistics per arm. It excels when each arm is truly distinct and you cannot transfer much from one arm to another.<br \/>Vector-native modeling shares one model over context-action features, so feedback on one action can update confidence around nearby actions.<br \/>We also tested variants and implementation profiles, but the central narrative is this LinUCB vs shared vector-native trade-off.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fairness in the Methodology<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>A lot of benchmark claims break because the protocol is not fair. We explicitly addressed this.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The evaluation uses a fair harness with these guardrails:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Shared contexts across algorithms in the same run.<br \/>Shared candidate sets across algorithms in the same run.<br \/>Common random noise policy so stochastic effects are matched.<br \/>Multi-seed studies, not single-seed storytelling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>This matters because otherwise differences can come from data ordering, RNG drift, or retrieval variance, not from algorithm quality.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We also compare across multiple environment types instead of one synthetic world, because bandit behavior is environment-dependent by design.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Environment Lens: Where Each Model Is Strong<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The most important finding is structural: performance is tightly coupled to environment assumptions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1) Per-arm independent structure<\/strong><br \/>When actions are effectively independent and there is weak cross-arm similarity structure, LinUCB tends to perform better.<br \/>This is exactly where per-arm local modeling is a strength, not a limitation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Interpretation:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB\u2019s disjoint updates are not \u201cwasted capacity\u201d in this setting; they are aligned with the true data-generating process.<br \/>A shared vector model can over-generalize and smear signal between arms that should stay separate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><br \/>2) Shared contextual structure<\/strong><br \/>When arms share meaningful latent structure (for example, embedding neighborhoods that reflect similar response behavior), vector-native modeling tends to perform better.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Interpretation:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Shared representation unlocks sample efficiency.<br \/>One update can inform nearby actions.<br \/>The model climbs faster in environments where transfer is valid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/><strong>3) Mixed structure<\/strong><br \/>In mixed environments, behavior is hybrid:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vector-native approaches can still show strong gains in outcome metrics when shared structure dominates enough of the interaction stream.<br \/>LinUCB can remain competitive or superior in slices where local arm idiosyncrasy dominates.<br \/>This is often the closest to production reality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Time-to-Decision vs Time-to-Outcome<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>One of the most important outcomes of this work is metric clarity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Time-to-decision<br \/>This is model compute latency per interaction.<br \/>It answers: how fast can I score one step?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB can look excellent here, especially in disjoint setups and smaller dimensions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Time-to-outcome<br \/>This is interaction efficiency and end-to-end progress toward business success.<br \/>It answers: how many attempts and how much elapsed decision effort does it take before I get a useful result?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This can favor vector-native methods in shared-structure worlds, even if per-step compute is higher. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If someone visits webpages with ads, and clicks every 10 seconds, and the vector bands picks a good offer and makes the sale in 3 tries, I get a lot more sales than the normal bandit with 5 or 6 tries, and on that timescale, it doesn&#8217;t matter that the linUCB function takes 1ms and the vector function 15ms. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That distinction is operationally critical. A few milliseconds of extra scoring time can be irrelevant if it reduces failed interactions and reaches success much earlier from a user or revenue perspective.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What We Added to Make This Measurable<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>To avoid hand-wavy claims, we added explicit outcome metrics in the benchmark protocol.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The study now tracks episode-level and outcome-level metrics such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Success rate under a defined success threshold<\/li>\n\n\n\n<li>Average turns to success (for successful episodes)<\/li>\n\n\n\n<li>Average turns to outcome under censoring rules<\/li>\n\n\n\n<li>Decision-time accumulation to outcome<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This makes statements like \u201cmodel A reaches useful outcomes in fewer turns\u201d testable, not rhetorical.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It also makes the benchmark directly useful for product planning, because these metrics can be mapped to interaction costs and value-per-conversion assumptions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Practical Read of Findings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>An objective read should avoid universal winners. Here is the practical framing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When LinUCB is the better default<br \/>Choose LinUCB-first if:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Arms are mostly independent.<\/li>\n\n\n\n<li>You have low confidence in embedding quality or relevance.<\/li>\n\n\n\n<li>You need very low computational overhead per call.<\/li>\n\n\n\n<li>You are in a regime where transfer between actions is likely harmful.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>Why this is coherent:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB\u2019s disjoint structure is robust and interpretable in locally heterogeneous arm landscapes.<br \/>It can be simpler to reason about under strict real-time constraints.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When vector-native is the better default<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Choose vector-native-first if:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Action embeddings are meaningful and stable.<\/li>\n\n\n\n<li>You expect substantial behavioral similarity across neighboring actions.<\/li>\n\n\n\n<li>You need sample efficiency in large catalogs.<\/li>\n\n\n\n<li>You care about reducing decision count to reach success.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Why this is coherent:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Shared representation captures transferable structure.<br \/>The model can improve faster in sparse-feedback settings.<br \/>Outcome-oriented metrics can favor this path strongly in the right environment.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When to run both in production<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>Use a dual-path strategy if:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your traffic is structurally mixed.<\/li>\n\n\n\n<li>You can segment users, contexts, or verticals.<\/li>\n\n\n\n<li>You can route by confidence or observed environment signatures.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>A common strategy<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start with conservative LinUCB on segments suspected to be per-arm.<\/li>\n\n\n\n<li>Use vector-native on segments with embedding-trust and demonstrated transfer benefits.<\/li>\n\n\n\n<li>Continuously re-evaluate routing policy from live outcomes.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>Interpreting the \u201cPer-Arm vs Shared\u201d Result Correctly<br \/>It is tempting to over-simplify:<br \/>\u201cLinUCB wins per-arm, vector wins contextual.\u201d<br \/>That is directionally true, but still too coarse.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nuances that matter:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Embedding quality is a first-order variable. Poor embeddings can erase vector-native advantages.<br \/>Retrieval strategy affects candidate quality and thus downstream bandit behavior.<br \/>Exploration tuning can flip local rankings.<br \/>Noise models and reward clipping policies can mask or amplify differences.<br \/>Mixed environments can produce non-linear crossover behavior.<br \/>So treat environment labels as structural anchors, not deterministic guarantees.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations and What We Did Not Claim<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>Objective review means being explicit about limits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We did not claim:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Universal superiority of vector-native methods.<\/li>\n\n\n\n<li>Universal inferiority of LinUCB in modern stacks.<\/li>\n\n\n\n<li>One benchmark run is enough for production rollout.<\/li>\n\n\n\n<li>Synthetic environments directly equal real-world business outcomes.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>We also acknowledge:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Synthetic benchmarks approximate but do not replace online A\/B outcomes.<\/li>\n\n\n\n<li>Distribution shift can change winner identities.<\/li>\n\n\n\n<li>Operational constraints (SLOs, infra cost, deployment risk) matter as much as offline gains.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>What the evidence supports:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">LinUCB is strong and often better when action behavior is disjoint.<br \/>Vector-native modeling is strong and often better when similarity structure is real and usable.<br \/>Outcome-oriented metrics reveal advantages that per-decision latency alone can hide.<br \/>Robust benchmarking requires fairness controls and multi-seed reporting.<br \/>Production decisions should be segmented, iterative, and tied to business outcomes.<br \/>If you force a universal answer, you will likely deploy the wrong model in half your traffic.<br \/>If you accept structural nuance and evaluate on time-to-outcome as well as time-to-decision, you get a practical strategy that scales.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is the real finding.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Github repo: <a href=\"https:\/\/github.com\/juustesout\/contextual-vector-bandit-in-typescript\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/juustesout\/contextual-vector-bandit-in-typescript<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Protocol (Reproducible)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Seeds: 20<\/li>\n\n\n\n<li>Rounds: 2000<\/li>\n\n\n\n<li>Actions: 200<\/li>\n\n\n\n<li>Candidates per round: 25<\/li>\n\n\n\n<li>Dimension: 16<\/li>\n\n\n\n<li>Joint dimension: 64<\/li>\n\n\n\n<li>Outcome protocol: max turns 10, success threshold 0.8<\/li>\n\n\n\n<li>Tuning: per-algorithm alpha tuning over grid [0, 0.25, 0.5, 1, 2]<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Fairness controls: shared contexts, shared candidate sets, and deterministic common-random-noise across algorithms.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Main Quality Results (20 Seeds, Tuned)<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Environment<\/th><th>Regret % vs LinUCB (Vector)<\/th><th>Reward Delta (Vector &#8211; LinUCB)<\/th><th>Inference Ratio (Vector \/ LinUCB)<\/th><th>Winner<\/th><\/tr><\/thead><tbody><tr><td>Bilinear (shared structure)<\/td><td>+65.30%<\/td><td>+0.1025<\/td><td>14.46x<\/td><td>Vector-native<\/td><\/tr><tr><td>Per-arm (independent arms)<\/td><td>-29.25%<\/td><td>-0.0490<\/td><td>11.47x<\/td><td>LinUCB<\/td><\/tr><tr><td>Mixed<\/td><td>+53.98%<\/td><td>+0.0748<\/td><td>14.25x<\/td><td>Vector-native<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Outcome Results (20 Seeds, Tuned)<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Environment<\/th><th>LinUCB Success Rate<\/th><th>Vector Success Rate<\/th><th>Success Lift (pp)<\/th><th>LinUCB Avg Turns to Outcome<\/th><th>Vector Avg Turns to Outcome<\/th><th>Turns Reduction %<\/th><\/tr><\/thead><tbody><tr><td>Bilinear<\/td><td>0.3840<\/td><td>0.7383<\/td><td>+35.43<\/td><td>8.101<\/td><td>5.754<\/td><td>28.97%<\/td><\/tr><tr><td>Per-arm<\/td><td>0.3976<\/td><td>0.2391<\/td><td>-15.85<\/td><td>8.041<\/td><td>8.856<\/td><td>-10.13%<\/td><\/tr><tr><td>Mixed<\/td><td>0.2353<\/td><td>0.5081<\/td><td>+27.29<\/td><td>8.890<\/td><td>7.339<\/td><td>17.44%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">1) LinUCB is stronger in truly per-arm regimes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In the per-arm environment, LinUCB wins both quality and outcome metrics. This is expected and important: when arms are genuinely independent, disjoint per-arm learning is exactly the right inductive bias.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2) Vector-native is stronger where structure is shared<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In bilinear and mixed environments, the vector-native shared model materially outperforms LinUCB on regret, reward, success rate, and turns-to-outcome. This supports the transfer-learning hypothesis: updates from one action inform nearby actions when embeddings capture real similarity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3) Compute latency and outcome latency are different objectives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Vector has higher per-decision compute in this benchmark, but still wins in time-to-outcome in shared-structure regimes because it needs fewer interactions before success. For product systems, this distinction often matters more than micro-latency alone.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Important nuance.<\/strong> The outcomeTimeReductionPct numbers in the raw API are based on model compute-time only and can be negative for Vector because Vector does more math per decision. That does not contradict fewer turns-to-outcome. It means: if your real-world interaction loop is dominated by user\/network\/business latency per turn, fewer turns can still be the bigger win.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Speed Profile Snapshot (Serving Path)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">From the latency matrix sweep in this repo (same app, k in {25,100,250}).<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>k<\/th><th>Best Total Latency Profile<\/th><th>Best Total (ms)<\/th><th>Best Retrieval Profile<\/th><th>Best Select Profile<\/th><\/tr><\/thead><tbody><tr><td>25<\/td><td>factored_nocache<\/td><td>4.293<\/td><td>dense<\/td><td>factored_cache<\/td><\/tr><tr><td>100<\/td><td>factored_cache<\/td><td>5.956<\/td><td>factored_cache<\/td><td>factored_cache<\/td><\/tr><tr><td>250<\/td><td>factored_nocache<\/td><td>10.015<\/td><td>factored_nocache<\/td><td>factored_cache<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Objective Conclusion<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">The right claim is not: one algorithm is universally best. The right claim is: model choice should follow environment structure and business objective.<\/p>\n<\/blockquote>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If your domain is per-arm and independent, LinUCB is a strong default.<\/li>\n\n\n\n<li>If your domain has reliable contextual similarity, a vector-native shared model can deliver better outcomes in fewer interactions.<\/li>\n\n\n\n<li>For decision systems that monetize successful outcomes, optimize for time-to-outcome, not only time-to-decision.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Does a contextual vector bandit work, and how does it compare to a linUCB bandit, some research<\/p>\n","protected":false},"author":5796,"featured_media":21634,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[484,479,305],"tags":[483],"class_list":["post-21733","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-javascript","category-programming","tag-ai"],"_links":{"self":[{"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/posts\/21733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/users\/5796"}],"replies":[{"embeddable":true,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/comments?post=21733"}],"version-history":[{"count":2,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/posts\/21733\/revisions"}],"predecessor-version":[{"id":21737,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/posts\/21733\/revisions\/21737"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/media\/21634"}],"wp:attachment":[{"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/media?parent=21733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/categories?post=21733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.juust.org\/index.php\/wp-json\/wp\/v2\/tags?post=21733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}