Resource icon

LLMO SEO Indexing AI Addon 2.1.4 (my last try)

No permission to download

Roiarthur

Active member
Roiarthur submitted a new resource:

LLMO SEO Indexing AI Addon - Addon for AI SEO

What’s changed compared to 3.1.0
  • No hashes.json → no more “N files are missing” during install.
  • PHP fixes in the controller and service (proper . concatenation, array appends, .=, correct ternaries).
  • Single namespace Sylvain\LlmoproV311 to avoid collisions with previous versions.
  • Minimal, valid routes.xml (avoids “Please enter a valid value”).
  • Clean Setup (tables xf_llmopro_document & xf_llmopro_log).
Structure...

Read more about this resource...
 
Roiarthur updated LLMO SEO Indexing AI Addon with a new update entry:

LLMO_Pro_V3_3.1.3_patch1 (Fix Listener)

You will need to go into PhpMyAdmin to execute the following query in order to ensure there are no duplicate entries in the Listener after installing the Add-on, and to make sure the correct Listener is used in XenForo.

Open your database and click on the SQL tab. Then copy and paste the following query into the field labeled "Run SQL query/queries on server 'localhost'", and click the Execute button:

Code:
DELETE FROM xf_code_event_listener
WHERE event_id = 'template_post_render'
  AND...

Read the rest of this update entry...
 
Hi, what is this addon? The description doesn't detail what it does?
 
The attached ZIP is not a valid Add-on ZIP, please read how to properly build an Add-on:

Files
  • _data/
    • options.xml
    • phrases.xml
    • template_modifications.xml
    • routes.xml
  • Entity/Document.php
  • Entity/Log.php
  • Service/Generator.php
  • Util/RateLimiter.php
  • Pub/Controller/Index.php
  • Pub/View/Json.php
  • Cron/Regenerate.php
  • Job/WebhookPing.php
as mentioned in the RM Add-on description are missing in the ZIP.

Furthermore, your code (at leat the listenere stuff) seems to be pretty inefficient and doesn't use framework features as it probably should:
  • Avoid frequently called using code event listeners without hints (unless absolutely required)
  • Avoid manipulating full HTML unless absolutely necessary
  • Avoid custom magic like
    PHP:
    $text = trim($app->stringFormatter()->stripBbCode((string)$first->message));
    if (mb_strlen($text) > 1500) $text = mb_substr($text, 0, 1499) . '…';
    There are framework features for things like this, epecifically take a look at XF\Str\Formatter::snippetString() and templater function snippet()
  • Don't include a Setup.php that does basically nothing
 
Why does this addon come with more instructions than an Ikea kids wardrobe? This doesn't seem to be packaged properly.
 
Because i am not a professional, i am only webmaster, it is my first addon and my last one if someone want to finish the job go ahead.

Mister well known member, if you can do better than me, go ahead, make my day !

About the difference between LLMO SEO and XenForo’s stock schema markup

LLMO (Large Language Model Optimization) SEO goes beyond traditional structured data. It’s designed for the AI era, focusing on how large language models like ChatGPT, Gemini, or Claude interpret and retrieve content semantically.


XenForo’s built-in schema markup (the “stock XF schema”) uses JSON-LD microdata to describe your pages for search engines such as Google — things like discussions, articles, breadcrumbs, etc. It helps search bots index your site and generate rich snippets.

LLMO markup, on the other hand, is meant for AI understanding, not just search ranking:
  • It emphasizes contextual meaning rather than keyword tagging.
  • It may use entity-based or graph-style metadata to express relationships and intent.
  • It’s more flexible, allowing for custom contexts or hybrid vocabularies that go beyond Schema.org.

In short:

XenForo schema = for Google’s structured indexing.

LLMO SEO = for AI comprehension and reasoning.

To put it simply: XenForo’s schema tells Google what’s on the page, while LLMO SEO tells AI systems why that content matters and how it connects to the bigger picture.


And just to add — I decided to drop the idea of developing the LLMO add-on myself; it turned out to be too complex for my current development skills.
 
The attached ZIP is not a valid Add-on ZIP, please read how to properly build an Add-on:

Files
  • _data/
    • options.xml
    • phrases.xml
    • template_modifications.xml
    • routes.xml
  • Entity/Document.php
  • Entity/Log.php
  • Service/Generator.php
  • Util/RateLimiter.php
  • Pub/Controller/Index.php
  • Pub/View/Json.php
  • Cron/Regenerate.php
  • Job/WebhookPing.php
as mentioned in the RM Add-on description are missing in the ZIP.

Furthermore, your code (at leat the listenere stuff) seems to be pretty inefficient and doesn't use framework features as it probably should:
  • Avoid frequently called using code event listeners without hints (unless absolutely required)
  • Avoid manipulating full HTML unless absolutely necessary
  • Avoid custom magic like
    PHP:
    $text = trim($app->stringFormatter()->stripBbCode((string)$first->message));
    if (mb_strlen($text) > 1500) $text = mb_substr($text, 0, 1499) . '…';
    There are framework features for things like this, epecifically take a look at XF\Str\Formatter::snippetString() and templater function snippet()
  • Don't include a Setup.php that does basically nothing

It would be great if someone could improve these details, as the author allows it.
 
Hi Phineas,

I actually spent about five days trying to fix the existing issues in the add-on. Every time I solved one problem, another one appeared. That’s why the ZIP is incomplete, XenForo simply refused to install it in its current state.

The project itself already has a solid foundation, and I tested it thoroughly with PowerShell after each update. Unfortunately, there was always something that caused a new conflict or error.

Because of that, I decided to stop working on it and leave it to a more experienced developer. I really believe this idea could benefit the XenForo community, since it’s a step forward for anyone who wants to optimize their sites for AI-driven search, which is becoming more and more important as Google-style search slowly becomes a thing of the past.
 

Why the Listener Is Important When Designing the LLMO SEO Add-on


The listener is essentially the invisible backbone of any well-designed SEO add-on — and it becomes even more critical when dealing with LLMO (Large Language Model Optimization).

A listener in XenForo is an event hook: it allows your add-on to listen to what the XenForo core is doing at specific points in its execution cycle for example, when a page loads, a template is rendered, or content is saved to the database.
When a relevant event is triggered, the listener can intercept, modify, or inject data before the final output is delivered to the user or to search engines.


In other words, it’s the bridge between XenForo’s core engine and your SEO logic.

Why It Matters for LLMO SEO


Unlike classic SEO (which only adds static JSON-LD or meta tags), LLMO optimization depends on contextual and dynamic metadata — data that reflects what the page really means rather than just what it contains.
To generate that kind of smart metadata, your add-on needs to:
  • Detect the type of content being displayed (thread, resource, article, etc.).
  • Capture user context and semantic signals at runtime.
  • Inject or modify the LLMO JSON-LD block before the final HTML output.

All of that requires hooking into XenForo’s internal events — typically via listeners like templater_setup, app_pub_start, or thread_view.


Without listeners, your add-on would be blind and static.
With listeners, it becomes adaptive, capable of generating semantic SEO data in real time, tailored for AI models and next-gen search systems.

To put it simply:

Without a listener, your LLMO SEO add-on can’t understand XenForo’s page lifecycle — it can only decorate it.
With a listener, it can think with XenForo and inject meaningful intelligence at the right moment.
 
The Structural Foundations of the LLMO SEO Add-on

For LLMO (Large Language Model Optimization) to work properly, the add-on can’t just inject static schema markup. It needs a dynamic architecture that adapts metadata and context for every page type.
This is what separates AI-oriented SEO from conventional keyword or title-based SEO like Google’s.

1. Context-Aware Architecture

Each page in XenForo threads, resources, user profiles, nodes, tags carries a different semantic purpose.
The add-on must detect and describe that purpose automatically. For example:

A thread represents a “conversation” with an author, date, and conclusion.

A resource represents a “knowledge object” with versioning and updates.

A user profile represents an “expert entity.”

The listener system and contextual handlers allow the module to generate specific JSON-LD and AI-structured metadata for each of these, giving large language models clear semantic signals rather than generic tags.


2. Dynamic JSON-LD Generation

Instead of a fixed schema file, the add-on should dynamically build the JSON-LD block based on:

Thread tags, post sentiment, and author reputation.

Relationships between content (e.g. “This thread answers question X”).

Topical clusters and entities extracted from the text itself.

This dynamic model transforms the page into a knowledge node, ready to be integrated into AI reasoning graphs — something Google’s conventional index can’t do effectively because it relies on surface signals like titles, keywords, and backlinks.


3. Semantic Relationship Layer

Traditional search engines look at strings; LLMO systems look at meanings.
That’s why the add-on needs to express:

Entity relationships (author ↔ subject ↔ conclusion).

Intent indicators (instructional, comparative, troubleshooting, etc.).

Content dependencies (this page extends or references another).

By embedding these relationships in metadata, the add-on helps AI models link related ideas together. The result is that when someone asks an AI about a topic, your forum’s content is more likely to be chosen — because it already fits the model’s reasoning structure.


4. Adaptive Output Layer

Finally, LLMO SEO must be contextually fluid:
It should modify the markup depending on where the page appears — desktop, mobile, AMP, or even embedded previews — ensuring AI crawlers always receive a clean, interpretable dataset.

Where Google search sees “titles and keywords,” LLMO sees semantic vectors — meaning, your content can “rank” in AI responses even when users never perform a search query.


The Big Difference

Traditional SEO = optimization for retrieval (Google needs to find you).
LLMO SEO = optimization for reasoning (AI needs to understand you).

That’s the paradigm shift: Google indexes pages; AI systems ingest knowledge.
An LLMO-optimized XenForo add-on ensures each page becomes part of that living knowledge graph — not just another search result.
 
Project structure
Code:
src/
  addons/
    Vendor/LlmoSeo/
      addon.json
      _data/
        phrases/
          llmoseo_explain.json
        templates/
          public/llmoseo_jsonld_thread.json
          public/llmoseo_jsonld_resource.json
          public/llmoseo_jsonld_forum.json
          public/llmoseo_jsonld_user.json
      Setup.php
      Listener/
        AppPubStart.php
        TemplaterSetup.php
        ThreadView.php
        ResourceView.php
        ForumView.php
        UserView.php
      Service/
        Context/
          PageContextResolver.php
          EntityExtractor.php
          IntentClassifier.php
        JsonLd/
          Builder.php
          Types/
            ThreadType.php
            ResourceType.php
            ForumType.php
            UserType.php
        Cache/
          CacheKey.php
      Repository/
        TopicGraph.php
      XFExt/
        Entity/
          Thread.php   (extend)
          ResourceItem.php (extend)
      Cli/
        Command/ValidateLlmo.php
      Util/
        Sanitizer.php
        Logger.php



Addon.json

Code:
{
  "title": "LLMO SEO",
  "version_id": 1000010,
  "version_string": "1.0.1",
  "dev": "Vendor",
  "require": { "XF": [2030070, "XenForo 2.3.7+"] },
  "listeners": {
    "app_pub_start": ["Vendor\\LlmoSeo\\Listener\\AppPubStart", "run"],
    "templater_setup": ["Vendor\\LlmoSeo\\Listener\\TemplaterSetup", "run"],
    "thread_view": ["Vendor\\LlmoSeo\\Listener\\ThreadView", "run"],
    "resource_view": ["Vendor\\LlmoSeo\\Listener\\ResourceView", "run"],
    "node_view": ["Vendor\\LlmoSeo\\Listener\\ForumView", "run"],
    "member_view": ["Vendor\\LlmoSeo\\Listener\\UserView", "run"]
  },
  "options": [
    "llmoseo_enable",
    "llmoseo_debug",
    "llmoseo_strip_pii",
    "llmoseo_cache_ttl"
  ],
  "cli_commands": ["Vendor\\LlmoSeo\\Cli\\Command\\ValidateLlmo"]
}

Runtime Flow

1. AppPubStart.php

Initializes environment, loads options, adds debug headers.

2. TemplaterSetup.php
Injects the JSON-LD partial template at the end of the <head>.

3. Page Listeners (ThreadView, ResourceView, etc.)
Resolve context (type, entities, relations, intent) → Build JSON-LD → Pass to $viewParams['llmoSeoJson'].

4. Template Partial
Renders the LLMO script block when data exists.


Page Context Resolver

Code:
public function resolve(\XF\Mvc\Reply\View $reply): PageContext {
  $route = $this->app->request()->getRoutePath();
  // detect type (thread/resource/forum/user)
  // extract entities and intent
  return new PageContext([...]);
}


JSON-LD Builder
Code:
interface LlmoType {
  public function supports(PageContext $ctx): bool;
  public function build(PageContext $ctx): array;
}

Code:
public function build(PageContext $ctx): string {
  $type = $this->pickType($ctx);
  $payload = $type->build($ctx);

  $payload['@context'] = [
    "https://schema.org",
    "llmo" => "https://llmo.vendor.dev/context/v1"
  ];
  $payload['llmo:reasoningHints'] = [
    "intent" => $ctx->intent,
    "entities" => $ctx->entities,
    "relations" => $ctx->relations
  ];

  return json_encode($this->sanitizer->clean($payload), JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
}

Template Partial

Code:
<xf:if is="$llmoSeoJson">
<script id="llmo-seo" type="application/ld+json">{$llmoSeoJson|raw}</script>
</xf:if>


Listener Examples

TemplaterSetup.php
Code:
public static function run(\XF\Container $c, \XF\Template\Templater $t) {
  $t->addDefaultParam('llmoSeoJson', '');
}



ThreadView.php
Code:
public static function run(\XF\Mvc\Reply\View $reply, \XF\Mvc\ParameterBag $params) {
  $resolver = \XF::app()->service('Vendor\LlmoSeo:Context\PageContextResolver');
  $ctx = $resolver->resolve($reply);

  $builder = \XF::app()->service('Vendor\LlmoSeo:JsonLd\Builder');
  $json = $builder->build($ctx);

  $reply->setParam('llmoSeoJson', $json);
}

CLI Validation Command

Code:
protected function execute(InputInterface $in, OutputInterface $out) {
  $url = $in->getArgument('url');
  $html = $this->http->get($url)->getBody();

  if (!preg_match('#<script[^>]+id="llmo-seo"[^>]*>(.*?)</script>#si', $html, $m)) {
    $out->writeln('<error>No LLMO JSON-LD found.</error>');
    return 1;
  }

  $json = json_decode(html_entity_decode($m[1]), true);
  $out->writeln('<info>LLMO JSON-LD looks valid.</info>');
  return 0;
}



Admin Options
  • llmoseo_enable
  • llmoseo_debug
  • llmoseo_strip_pii
  • llmoseo_cache_ttl
  • Whitelist of allowed routes


Security and Performance
  • llmoseo_strip_pii: removes emails, phones, IPs.
  • Sanitizer: trims & validates JSON-LD (max 50 KB).
  • Cache key built on (type, id, last_update, locale, device-class).


Semantic Strategy
  • Clear intent: howTo, troubleshooting, reference, comparison.
  • Normalize entities (OS, versions, CVE).
  • Express relationships: answers, extends, dependsOn, etc.
  • Include llmo:AnswerSummary, llmo:KeyFindings, and llmo:Env.


Roadmap
  • Local entity extraction (spaCy-like or API).
  • Compact AI-ready snippets (facts + sources).
  • Trust/reproducibility scoring.
  • Multilingual entity alignment.
 
Back
Top Bottom