Reference

Developer Reference

Notes for developers extending the module or integrating with it.

Events the module listens to

Identifier Events Effect
Omeka\Api\Adapter\ItemAdapter api.create.post, api.update.post, api.delete.post Queue an item index job, or delete its chunks
Omeka\Api\Adapter\MediaAdapter same Queue a media index job, or delete its chunks
Omeka\Api\Adapter\SitePageAdapter same Queue a site page index job, or delete its chunks
Blog blog.post.create, blog.post.update, blog.post.delete with a post_id parameter Queue a blog post index job, or delete its chunks
Omeka\Controller\Admin\Item view.browse.after Add the re-index options to the batch actions dropdown
* view.layout Inject the global widget on public site pages

Any module that triggers the Blog events with a post_id parameter gets its posts indexed, provided the blog_post and blog_post_site tables exist.

Jobs

Job Arguments Purpose
Chatbot\Job\IndexItemJob action (index or delete), resource_type (item, media, blog_post, site_page), resource_id Index or remove one resource
Chatbot\Job\BulkIndexJob item_ids (array, or null for all), resource_types (array, default ['item']) Rebuild whole content types
Chatbot\Job\PurgeChatbotLog none Delete log entries older than the retention period

Dispatch them with Omeka\Job\Dispatcher like any other Omeka job.

Services

All are registered in the service manager under their class names.

Service Role
Chatbot\Service\TursoService HTTP client for the Turso pipeline API: insert, delete, search, index rebuild, table rebuild, chunk count
Chatbot\Service\IndexService Text extraction, chunking, embedding and storage: indexItem(), indexMedia(), indexBlogPost(), indexSitePage(), deleteResource()
Chatbot\Service\ChatService chat() and semanticSearch()
Chatbot\Service\ChatbotLogger Writes to module_chatbot_log: info(), warning(), error() with an optional context array
Chatbot\Service\RateLimitService Per-IP fixed-window counter in module_chatbot_rate_limit
Chatbot\Service\EncryptionService AES-256-CBC encrypt and decrypt with a key derived from chatbot.encryption_key
Chatbot\Provider\OpenAiProvider Embeddings and chat completions over the Laminas HTTP client; retries once after 5 seconds on HTTP 429

Provider interfaces

OpenAiProvider implements both interfaces. To add another backend, implement them, register a factory in config/module.config.php under service_manager.factories, and change ChatServiceFactory and IndexServiceFactory to fetch your provider instead of OpenAiProvider.

interface EmbeddingsProviderInterface
{
    /** @return float[] */
    public function embed(string $text, int $dimensions = 0): array;
    public function getName(): string;
}

interface LLMProviderInterface
{
    public function complete(array $messages, string $model, float $temperature = 0.2, int $maxTokens = 1024): string;
    public function getName(): string;
}

Turso schema

Created automatically on first connection and dropped by Purge All Index Data:

CREATE TABLE chunks (
    id            INTEGER PRIMARY KEY AUTOINCREMENT,
    resource_type TEXT    NOT NULL,
    resource_id   INTEGER NOT NULL,
    chunk_id      INTEGER NOT NULL,
    chunk_text    TEXT    NOT NULL,
    metadata      TEXT,            -- JSON: title, resource_type, site_ids, item_set_ids (items and media only)
    url           TEXT,
    created_at    TEXT    NOT NULL,
    embedding     F32_BLOB(3072)   -- size follows the Embedding dimensions setting
);
CREATE INDEX chunks_vec_idx ON chunks (libsql_vector_idx(embedding));

Searches use vector_top_k('chunks_vec_idx', vector32('[...]'), k) and order by vector_distance_cos. The DISKANN index does not see rows inserted after it was built, so the module drops and recreates it after each single-resource index and once at the end of a bulk job.

MySQL tables

  • module_chatbot_log: id, level, event, message, context (JSON), created_at
  • module_chatbot_rate_limit: ip_address, window_start, request_count

Front-end scripts

Both scripts are plain JavaScript with no dependencies, so a theme can mount them in custom markup.

  • ChatbotWidget.init({ apiUrl, mode: 'widget' | 'inline', container, systemPrompt, heading, siteId, siteSlug }) from asset/js/chatbot.js. mode: 'widget' appends a floating button and panel to the body; mode: 'inline' renders the panel inside container.
  • SemanticSearch.init({ apiUrl, container, perPage, placeholder, siteId, siteSlug }) from asset/js/semantic-search.js.

Get the endpoint URLs from the chatbot-api-chat and chatbot-api-search routes, for example $this->url('chatbot-api-chat') in a view.

Tests

Unit tests use PHPUnit mocks and need no database or API keys. From the Omeka S root:

vendor/bin/phpunit -c modules/Chatbot/test/phpunit.xml
Log in for Support