Select your language

How It Works (Technical Overview)

Audience: developers, integrators and reviewers who want to understand the data flow, the knowledge pipeline and the security model.

Overview

mod_bearsamppai is a Joomla site module that renders a floating chat widget. The widget is inert markup until a visitor asks a question; at that point the browser POSTs the question to a com_ajax endpoint, the server assembles a knowledge context from published site content, calls an OpenAI-compatible chat completions API and returns the answer as JSON. The API key stays on the server; the knowledge context is assembled per request (and cached) server-side.

Files

FileRole
mod_bearsamppai.xmlManifest: metadata, dependencies (PHP 8.1, Joomla 5.4), the Knowledge Base Content / AI Chat / Advanced parameter fields, update server.
mod_bearsamppai.phpEntry point: boots the module through the DI dispatcher.
services/provider.phpDI service provider: registers the dispatcher, the site helper and the module.
src/Dispatcher/Dispatcher.phpCollects layout data (params, module, plus article and forum lists) and loads the layout.
src/Helper/ModuleHelper.phpContent queries used by the dispatcher (recent articles, FAQ items, Kunena topics).
src/Helper/BearsamppaiHelper.phpThe chat logic: knowledge context assembly, the AI request, the fallback model and the ping check.
helper.phpLegacy com_ajax bridge that delegates to the namespaced helper.
tmpl/default.php, tmpl/chat.phpWrapper and chat widget markup; registers the CSS and JS web assets.
media/js/chat.js, media/css/chat.cssWidget behaviour and styling (styles are driven by CSS custom properties under dark/light schemes).
language/en-GB/*.iniTranslatable strings.
updates.xmlJoomla Update System feed.

Rendering the widget

The dispatcher returns layout data, the layout (tmpl/default.php) wraps tmpl/chat.php, and the widget is registered through the Joomla Web Asset Manager. The assets ship inside the module folder, so they are addressed through it (/modules/mod_bearsamppai/media/...) rather than the shared /media tree. An explicit light or dark choice is emitted up front as data-theme-scheme so the correct palette applies before the deferred script runs; auto is resolved in JavaScript from the prefers-color-scheme media query.

The dispatcher also loads article and forum lists into the layout, but the widget markup does not consume them - the answer path always rebuilds the knowledge context server-side during the AJAX request.

The com_ajax endpoint

The widget's endpoint is:

index.php?option=com_ajax&module=bearsamppai&method=ask&format=json

Messages are sent as a POST form body with module_id and message. The connection-status indicator, when enabled, uses method=ping (a GET that returns {"success":true,...} without calling the AI).

com_ajax resolves the namespaced helper through the module HelperFactory; if that path is unavailable it falls back to the legacy helper.php bridge (ModBearsamppaiHelper::askAjax()), which delegates to the same class. The logic stays in one place either way.

The helper reads the module's own parameters by querying #__modules directly rather than using ModuleHelper::getModuleById(). That core method lists modules for the current menu item, and a com_ajax request is not a menu page (Itemid 0), so a module assigned to a single menu item - and therefore its API key - would otherwise read back empty.

Building the knowledge context

BearsamppaiHelper assembles a single context string from three sources, reduced to plain text (tags stripped, entities decoded, whitespace collapsed):

  1. FAQ articles (when a FAQ category is set). The article title becomes FAQ question: ... and the text FAQ answer: ....
  2. Articles from the selected categories, or every published article when none are selected.
  3. Kunena forum topics (only when Include Kunena forum topics is Yes).

Only published content is read, at the access level Joomla's content configuration allows. If the forum switch is off the Kunena tables are never queried, so there is no forum dependency at all.

Ordering and the character budget

Two orderings are available (Context ordering):

  • relevance (default) - every candidate is scored against the visitor's question and the best matches are selected. Title matches weigh more than body matches, and repeated body hits have diminishing returns. Non-matching candidates are kept but sorted last, so an uncovered question still receives some context rather than none. The same corpus yields a different context per question, so this path is not served from the shared context cache.
  • newest - content is walked in source order (FAQ first, then articles, then forum) and the newest items fill the budget first.

In the relevance path the FAQ is scored like any other source (a strong FAQ match can outrank everything). In the newest path the FAQ is loaded first but capped at half the overall budget, so a large article selection cannot starve the FAQ, while every source still gets a turn and items that do not fit are skipped whole rather than truncated mid-sentence.

The overall budget is Knowledge context limit (characters) (1000–200000, default 20000); separators between parts are charged against it so the configured maximum is the actual maximum sent.

Source links

When ordering is relevance, candidates that scored above zero and have a resolvable article URL are returned as up to five sources entries; the widget renders them as links under the answer. Forum posts have no article URL and are not listed. In newest ordering the sources list is empty.

Context cache

The assembled context is cached server-side through Joomla's cache (group mod_bearsamppai), compressed, and keyed by a signature that combines the relevant parameters with a content fingerprint (highest modification time and row count of the articles in scope, plus the forum fingerprint when enabled). Publishing, editing or deleting content changes the fingerprint, so the cache rebuilds without any explicit invalidation. The relevance path cannot reuse this cache because its context depends on the question.

The AI request

The helper sends the OpenAI-compatible request with a Bearer token:

POST <endpoint>
Authorization: Bearer <api key>
Content-Type: application/json

{ "model": "...", "messages": [ {system}, {user} ], "max_tokens": ..., "temperature": ... }

The system message instructs the model to answer only from the <kb> context, not to use prior knowledge or browse the web, to prefer the most specific source, and to treat the FAQ answer as authoritative if sources conflict. The visitor's question is the user message. The reply is choices[0].message.content.

If no knowledge context can be built, the helper returns the configured Fallback answer with kb: false and does not call the API at all. A failure while building the context is non-fatal: it degrades to an empty context (and therefore the fallback answer) rather than erroring the request.

Fallback model

Distinct from the fallback answer, the Fallback model is retried once when the primary request times out, is rate limited (HTTP 429) or returns a 5xx. It uses its own timeout, so a slow primary plus fallback can take up to both timeouts combined. Other 4xx statuses (bad key, bad model, malformed payload) are not retried.

Client side

media/js/chat.js runs on DOMContentLoaded and wires up the toggle, form, theme, status and copy features for each widget instance:

  • Form - POSTs module_id and message, appends a typing placeholder, then renders the answer. Assistant replies run through a small Markdown renderer (fenced code, inline code, http/https links, bold, italic); user text is escaped. Source links, when present, are appended under the bubble and open in a new tab with rel="noopener noreferrer".
  • Theme - resolves auto from prefers-color-scheme and listens for changes; the runtime toggle writes the choice to localStorage under mod_bearsamppai_theme.
  • Status - polls method=ping on the configured interval (and on tab visibility).
  • Copy - builds a timestamped transcript of the messages and copies it with the Clipboard API (with an execCommand fallback).
  • Keyboard - Escape closes the panel and returns focus to the toggle; Ctrl+/ (or Cmd+/) opens the first widget.

Security and privacy

  • Key handling: the API key is stored in the module parameters and used only server-side; it is never output to the browser.
  • Data egress: the only outbound data is the visitor's question and the selected knowledge-base context, sent from your server to the AI endpoint you configure. The extension adds no analytics, tracking or telemetry and does not store conversations.
  • Content scope: only published content is read; the forum source is opt-in and never queried when disabled.
  • Prompt grounding: the model is instructed not to use prior knowledge, browse the web or guess, and to return the fallback answer when the knowledge base does not cover the question.
  • Failure containment: cache and context-building failures are swallowed (logged at most) and degrade gracefully rather than breaking the chat.

Failure handling

The helper returns a structured error that the widget shows in an assistant bubble. Notable messages: missing module_id, an unknown module, an empty message, a missing API key or model, AI API request failed (status NNN) with any provider detail, a generic busy message after a failed fallback attempt, an unexpected-response error, and a "taking too long" message when the fallback also times out. Diagnostics are written to the mod_bearsamppai.php log (context size, which sources were used, fallback usage, ordering).

Distribution and updates

Releases are produced by the repository's packaging workflow (Joomla Packager) on each merged PR to main. Versions are date-based, and updates.xml advertises the latest version to the Joomla Update System with a download URL pointing at the release ZIP on GitHub.

Our Supporters

Sorry, this website uses features that your browser doesn't support. Upgrade to a newer version of Firefox, Chrome, Safari, or Edge and you'll be all set.