Rekhu Assistant
Conversation history
Keep signed-in users' conversations, show them in a list next to the chat, and serve the list from your own tables if you like.
What it is for
Users come back to a question days later. With the conversation history, a signed-in user sees their earlier conversations in a list next to the chat. They open one and go on where they left off, search, rename or delete.
- The list is newest first, with when each conversation was last used and how many questions it holds.
- New starts a new conversation; the earlier one stays in the list.
- On a phone the list covers the chat and closes once a conversation is picked. In Arabic it reads right to left.
- The public chat for visitors has no list; only signed-in users have one.
The web widget and the Flutter chat show the list by themselves when the server keeps conversations. The server says so with "history": true in its answer.
Set it up
- Keep conversations on the server. In Laravel, run the migration the package brings. In Node.js, nothing to turn on: conversations are kept in the assistant's store.
- Choose how long. A conversation stays in the list for 90 days after its last message, by default.
- Let old ones go. In Laravel, the package removes conversations past their days once a day through your scheduler. In Node.js, the store's expiry does it.
- Share the store on more than one server. In Node.js, the memory store forgets everything when the process restarts; give a shared store, such as Redis.
- Show the list. The widget (0.8 or newer) and Flutter (0.2 or newer) show it. A chat of your own uses the client calls below.
php artisan migrate
Then set the days. In Laravel, in config/rekhu-assistant.php or .env; in Node.js, on createAssistant:
'conversations' => [
'store' => env('REKHU_ASSISTANT_CONVERSATIONS', 'auto'), // auto, database or cache
'keep_days' => (int) env('REKHU_ASSISTANT_KEEP_DAYS', 90),
],
createAssistant({
// …
conversations: { keepDays: 30 },
});
In Laravel, make sure the scheduler runs (php artisan schedule:work, or the usual cron line). To clean up by hand:
php artisan rekhu-assistant:prune
Without the table, Laravel keeps conversations in your cache for conversation_minutes (1440). In Node.js, when conversationMinutes is longer than keepDays, that counts instead.
The routes
Both server packages answer the same routes, next to the chat route:
| Request | Answer |
|---|---|
GET …/chat/conversations?limit=&before=&q= |
a page of the user's conversations, newest first, with next for the following page; q searches titles and the user's own questions |
GET …/chat?conversation={id} |
opens one: what the user saw |
PATCH …/chat/conversations/{id} with {"title": "…"} |
renames it; an empty title goes back to the first question |
DELETE …/chat/conversations/{id} |
deletes it for good, with the files attached to it |
Each conversation in the list has id, title, scope, updated_at and questions. limit is 30 by default; send the next you got as before for the next page. A conversation that is not the user's answers 404; another user's id changes nothing.
To go on in a conversation, send its conversation with the next message. A new chat is a message without conversation.
A chat of your own
The web client and the Flutter client have the same calls:
import { createClient } from '@rekhu/assistant-widget';
const chat = createClient({ endpoint: '/api/rekhu/chat' });
const { conversations, next } = await chat.conversations({ query: 'invoice' });
await chat.open(conversations[0].id); // its history; the next send() goes on in it
await chat.rename(conversations[0].id, 'October invoices');
await chat.remove(conversations[0].id);
chat.newChat(); // the next send() starts a new one
if (chat.history) {
await chat.loadConversations(query: 'invoice'); // chat.conversations, chat.conversationsNext
await chat.moreConversations(); // the next page
await chat.open(chat.conversations.first.id); // the next message goes on in it
await chat.renameConversation(id, 'October invoices');
await chat.deleteConversation(id);
await chat.newChat();
}
In Flutter, chat is a RekhuChatController; it fetches the list again after each answer, so the open conversation moves to the top. With header: false, open the list from your own app bar with controller.showConversations(true). To hide the list in the web widget, mount it with history: false.
Conversations in your own tables
To keep conversations in tables of your own, give the package a store of your own.
In Laravel, implement Rekhu\Assistant\Contracts\ConversationStore (get, put, forget, list), and Rekhu\Assistant\Contracts\ConversationHistory (page, rename) for paging, search and renaming. In Node.js, add page and rename to the store's four methods:
use Rekhu\Assistant\RekhuAssistant;
RekhuAssistant::conversationsUsing(App\Assistant\DatabaseConversations::class);
createAssistant({
// …
store: {
// get, set, delete, increment, and:
async page(owner, scope, limit, before, query) {
// owner is "user:12"; newest first; query searches titles and questions
return { conversations: [{ id, title, scope, updated_at, questions }], next: null };
},
async rename(owner, id, title) {
// null when the owner has no such conversation (404)
return { id, title, scope, updated_at, questions };
},
},
});
Every conversation your store is given carries what those tables need: who it belongs to, its title (the first question), how many questions it has, its scope and when it changed. In Laravel, put() also gets the user's questions as one text, for the search.
Note: in a multi-tenant Laravel app with
tenantUsing(), a conversation belongs to the tenant and the user together. User 3 of one tenant never sees the conversations of user 3 of another.
Privacy
A conversation belongs to the user who started it. It is never listed, opened, renamed or deleted for another user. Deleting is for good: the conversation and its attached files are gone. Users are identified to Rekhu by their id only. See Privacy and data region.
Next
- Actions and the scan: what the assistant may do in a conversation.
- The web widget: every option of the chat in the browser.