# Data

The **Data** page of a chatbot names it and decides what it knows. Everything
the chatbot knows reaches the model in its instruction, the system message
sent with every visitor message. You build that instruction either with the
wizard or by hand from variables.

The **General** card holds the chatbot's **Name** and **Delete chatbot**,
which removes the chatbot, its settings and any files it synchronized.

## The wizard

**Use the wizard** is on for a new chatbot. The wizard lists the site's post
types; switch on each one the chatbot should know about and choose what to
**Include** of its posts: **Title**, **Content**, and **Post meta** where the
post type has meta fields. **Current user** gives the chatbot the signed-in
visitor's display name, email and roles, so it can greet them by name.

**System instruction** is your own text, placed before what the wizard
generates. Use it for the chatbot's role and tone:

```txt
You are a helpful assistant for our website. Be friendly and concise.
If you don't know the answer, say so.
```

**Generated instruction** shows the complete instruction the chatbot will
use, read-only. The wizard builds it from variables: one query variable per
post type, named `query_` and the post type, and a `currentUser` variable.
Each post type's template gives every post its public URL and its type beside
what you include, so the chatbot can link the posts it recommends.
Switching the wizard off keeps those variables and the instruction, so you can
start from them by hand.

## Variables

With the wizard off, **Variables** lists the chatbot's variables and
**Instruction** holds its instruction. A variable is a named value that
replaces `{name}` in the instruction when a visitor sends a message. Select
**Add variable**, choose its **Source**, and give it a name in **Variable**:

```txt
You are an assistant for the page {pageTitle}.
The visitor's name is {visitorName}.
```

The sources are:

| Source                   | Value                                                           |
| ------------------------ | --------------------------------------------------------------- |
| Text                     | Fixed text you enter.                                           |
| Query                    | Posts from a WordPress query, each rendered through a template. |
| Post title, Post content | The title or content of a post.                                 |
| Post meta                | A post's meta.                                                  |
| Taxonomy, Term           | A taxonomy or a term.                                           |
| User, User meta          | A user's account or their meta.                                 |

Plugins can add their own sources; see [Custom sources](#custom-sources).

For the post, taxonomy, term and user sources, enter the ID to read, or switch
on **Use the current value from the page** to read the post, term or visitor
of the page the chatbot is on. **Meta key** picks one value out of the result
with dot notation; for example, `data.display_name` on the User source gives
the user's display name. Without a meta key, the whole result is sent as JSON;
for the User source that is the display name, email and roles, and a key such
as `data.user_login` reads any other field.
Below a variable with an ID or a query, **Preview** shows the value it
currently resolves to.

## Query variables

A query variable lists posts. **Post types** shows the post types it reads,
which the wizard sets; a query you add yourself reads posts. **Search value**
limits it to posts that match a search, and **Match all terms** requires a post
to match every term the query names rather than any. **Raw query arguments**
replaces all of that with your own `WP_Query` arguments as JSON:

```json
{ "post_type": "product", "posts_per_page": 20, "orderby": "date" }
```

Raw arguments run as written, so set `posts_per_page`: without it, WordPress
returns five posts.

**Template** is rendered once for every post, one per line. Write post fields
in curly brackets, such as `{ID}`, `{post_title}`, `{post_excerpt}` or
`{post_content}`, and reach into the post's meta with a dot, as in
`{post_meta.price}`. `{permalink}` is the post's public URL; `{guid}` is the
GUID WordPress stored when the post was created, which is not always a working
address:

```txt
Product: {post_title}
URL: {permalink}
Price: {post_meta.price}
```

Without raw arguments a query has no limit, so a large post type makes a long
instruction, and every visitor message pays for it.

## Synchronizing to OpenAI file search

For large queries, **Enable file synchronization** uploads the rendered posts
to OpenAI's file search instead of putting them in the instruction. The model
then looks up what it needs for each question. It needs an OpenAI model on the
chatbot's [Model](/docs/ai/chatbots/model.md) page; with any other model the
switch is disabled and says so. A synchronization switched on before the
model changed can still be switched off.

**Synchronization interval** sets how often the file is refreshed: every
minute, every 10 minutes, hourly, daily, weekly or monthly. Saving the chatbot
uploads the file at once and schedules the refreshes, which run as the
`sleek_ai_chatbot_file_sync` action in Action Scheduler.
`wp sleek ai chatbot sync` uploads again on demand, and
`wp sleek ai chatbot sync-status` shows whether each file is ready.

Until a file is ready, visitors' messages are refused with "Synchronized
chatbot data is not available yet. Sync the data and try again." rather than
sending the whole query inline, which could cost far more than expected. The
editor's preview uses the inline data instead, so you can try the chatbot
while the file uploads.

## Retrieval priority

With file synchronization, OpenAI's file search decides which content a reply
reads. **Retrieval priority** on the Data page lets you weigh that choice
instead, so that, for example, your documentation pages win over blog posts
that mention the same thing. It needs an OpenAI model and at least one
synchronized query; without them the switch is disabled and says what it
needs, though a priority switched on before can still be switched off.

With it on, every synchronized post is uploaded as a file of its own, with its
title, URL and type, into one search index for the chatbot. For each visitor
message SleekAI searches that index itself, with the visitor's two previous
questions as context, and ranks what it finds: relevance first, raised or
lowered by each post's priority. The best eight posts reach the model as
reference data, each with its title and URL, so the chatbot links the posts it
names.

Each synchronized query gets a priority, **Low**, **Normal** or **High**, or an
exact weight from 0.5 to 1.5 beside it; Normal is 1. Below the queries, each
post type the queries read can take its own priority, which takes precedence
over its query's; **Use query priority** leaves it to the query. A priority
moves a post past others that are about as relevant, never past a much better
match, and never brings in content that does not match at all.

Saving the chatbot rebuilds the index; unchanged posts are not uploaded again.
Until the first index is ready, visitors' messages are refused as with file
synchronization, and a failed rebuild keeps the previous index. Turning the
priority off keeps the index, which the chatbot then searches through OpenAI's
file search; moving the chatbot to another provider removes it, and moving back
builds it again.

The `sleek_ai_chatbot_retrieval_weight` filter sets the weight of single posts,
for example to raise one product page:

```php
add_filter('sleek_ai_chatbot_retrieval_weight', function ($weight, $postId) {
    return $postId === 42 ? 1.5 : $weight;
}, 10, 2);
```

The [hooks reference](/docs/ai/reference/hooks.md) describes the filter's
arguments.

## Custom sources

The `sleekAi/chatbot/sources` filter adds sources to the list. Each entry has
an `id`, a `label` and a string `value`:

```php
add_filter('sleekAi/chatbot/sources', function ($sources) {
    $sources[] = [
        'id' => 'openingHours',
        'label' => 'Opening hours',
        'value' => get_option('my_opening_hours', 'Mon-Fri 9:00-17:00'),
    ];

    return $sources;
});
```

A variable that uses the source is replaced with its value. SleekAI reads the
value while it answers a visitor's message, in a REST request, so the value
cannot depend on the page the visitor is on; use the built-in sources for page
data. The [hooks reference](/docs/ai/reference/hooks.md) describes the filter's
arguments.
