@coffer-org/plugin-webchat 3.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ChatWidget-Wxy33-Ei.js → ChatWidget-Bs8zrQVZ.js} +382 -5779
- package/dist/index.js +6 -5
- package/dist/runtime/actions.d.ts +3 -1
- package/dist/runtime/actions.js +17 -2
- package/dist/runtime/chain-store.d.ts +8 -1
- package/dist/runtime/chain-store.js +15 -1
- package/dist/runtime/config.d.ts +1 -1
- package/dist/runtime/connector.d.ts +4 -2
- package/dist/runtime/connector.js +18 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/send.d.ts +1 -1
- package/dist/runtime/send.js +4 -3
- package/dist/schema.js +4488 -1578
- package/dist/web.js +2 -2
- package/locales/en.json +42 -0
- package/locales/ru.json +42 -0
- package/locales/uk.json +42 -0
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -4,19 +4,20 @@ import { field } from '@coffer-org/sdk/fields';
|
|
|
4
4
|
export default definePlugin({
|
|
5
5
|
id: 'webchat',
|
|
6
6
|
version: '1.0.0',
|
|
7
|
-
dependsOn: [
|
|
7
|
+
dependsOn: [],
|
|
8
8
|
settings: defineSettings({
|
|
9
9
|
label: 'webchat.settings.label',
|
|
10
|
-
fields:
|
|
11
|
-
field.
|
|
12
|
-
key: 'reasoning_display',
|
|
10
|
+
fields: {
|
|
11
|
+
reasoning_display: field.string({
|
|
13
12
|
label: 'webchat.settings.reasoning_display',
|
|
14
13
|
default: 'live',
|
|
14
|
+
strict: true,
|
|
15
|
+
view: { noSearch: true },
|
|
15
16
|
options: [
|
|
16
17
|
{ value: 'live', title: 'webchat.settings.reasoning_display_live' },
|
|
17
18
|
{ value: 'off', title: 'webchat.settings.reasoning_display_off' },
|
|
18
19
|
],
|
|
19
20
|
}),
|
|
20
|
-
|
|
21
|
+
},
|
|
21
22
|
}),
|
|
22
23
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ActionCaller } from '@coffer-org/server/plugin-hooks';
|
|
2
|
-
import { type AgentDescriptor } from '@coffer-org/
|
|
2
|
+
import { type AgentDescriptor } from '@coffer-org/server/orchestrator';
|
|
3
3
|
import { type ThreadSelection } from './chain-store.ts';
|
|
4
4
|
export interface ThreadSummary {
|
|
5
5
|
convId: string;
|
|
@@ -13,6 +13,7 @@ export interface HistoryMsg {
|
|
|
13
13
|
text: string;
|
|
14
14
|
ts: number;
|
|
15
15
|
reasoning?: string;
|
|
16
|
+
suggestions?: string[];
|
|
16
17
|
attachments?: {
|
|
17
18
|
name: string;
|
|
18
19
|
mime?: string;
|
|
@@ -32,6 +33,7 @@ export declare function agentsAction(body: Record<string, unknown>, caller: Acti
|
|
|
32
33
|
agents: AgentDescriptor[];
|
|
33
34
|
defaultAgentId: string | null;
|
|
34
35
|
selection: ThreadSelection;
|
|
36
|
+
starters: string[];
|
|
35
37
|
}>;
|
|
36
38
|
export declare function selectAgentAction(body: Record<string, unknown>, caller: ActionCaller): Promise<{
|
|
37
39
|
selection: ThreadSelection;
|
package/dist/runtime/actions.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { HttpError } from '@coffer-org/server/plugin-hooks';
|
|
2
|
-
import { listAgentCatalog, getDefaultAgentId, listRegisteredAgents, } from '@coffer-org/
|
|
2
|
+
import { listAgentCatalog, getDefaultAgentId, listRegisteredAgents, getConversationStarters, } from '@coffer-org/server/orchestrator';
|
|
3
3
|
import { chatIdFor, conversations, history, getSelection, setSelection } from "./chain-store.js";
|
|
4
4
|
const TITLE_MAX = 60;
|
|
5
|
+
function parseSuggestions(text) {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(text);
|
|
8
|
+
return Array.isArray(parsed) && parsed.every((v) => typeof v === 'string') ? parsed : null;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
5
14
|
function title(firstUserText) {
|
|
6
15
|
const t = firstUserText.trim().replace(/\s+/g, ' ');
|
|
7
16
|
return t.length <= TITLE_MAX ? t : `${t.slice(0, TITLE_MAX - 1)}…`;
|
|
@@ -28,7 +37,11 @@ export async function historyAction(body, caller) {
|
|
|
28
37
|
const convId = requireConvId(body);
|
|
29
38
|
const rows = await history(chatIdFor(caller.id, convId));
|
|
30
39
|
const reasoningByBot = new Map(rows.filter((m) => m.role === 'reasoning').map((m) => [m.msgId.slice(0, -2), m.text]));
|
|
31
|
-
const
|
|
40
|
+
const suggestionsByBot = new Map(rows
|
|
41
|
+
.filter((m) => m.role === 'suggestions')
|
|
42
|
+
.map((m) => [m.msgId.slice(0, -2), parseSuggestions(m.text)])
|
|
43
|
+
.filter((entry) => entry[1] !== null));
|
|
44
|
+
const visible = rows.filter((m) => m.role !== 'reasoning' && m.role !== 'suggestions');
|
|
32
45
|
return {
|
|
33
46
|
convId,
|
|
34
47
|
messages: visible.map((m) => ({
|
|
@@ -37,6 +50,7 @@ export async function historyAction(body, caller) {
|
|
|
37
50
|
text: m.text,
|
|
38
51
|
ts: m.ts,
|
|
39
52
|
...(reasoningByBot.has(m.msgId) ? { reasoning: reasoningByBot.get(m.msgId) } : {}),
|
|
53
|
+
...(suggestionsByBot.has(m.msgId) ? { suggestions: suggestionsByBot.get(m.msgId) } : {}),
|
|
40
54
|
...(m.attachments?.length ? { attachments: m.attachments } : {}),
|
|
41
55
|
})),
|
|
42
56
|
headMsgId: visible.at(-1)?.msgId ?? null,
|
|
@@ -48,6 +62,7 @@ export async function agentsAction(body, caller) {
|
|
|
48
62
|
agents: await listAgentCatalog(),
|
|
49
63
|
defaultAgentId: getDefaultAgentId() ?? null,
|
|
50
64
|
selection: await getSelection(chatIdFor(caller.id, convId)),
|
|
65
|
+
starters: await getConversationStarters(),
|
|
51
66
|
};
|
|
52
67
|
}
|
|
53
68
|
export async function selectAgentAction(body, caller) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ThreadChat, type StoredMsg } from '@coffer-org/server/thread-store';
|
|
2
2
|
import { type ThreadSelection } from '@coffer-org/server/thread-state';
|
|
3
|
-
import type { ConvMessage } from '@coffer-org/
|
|
3
|
+
import type { ConvMessage } from '@coffer-org/server/orchestrator';
|
|
4
4
|
export declare const CONNECTOR = "webchat";
|
|
5
5
|
export declare function chatIdFor(userId: string, convId: string): string;
|
|
6
6
|
export declare function chatPrefixFor(userId: string): string;
|
|
@@ -27,6 +27,13 @@ export declare function recordReasoning(m: {
|
|
|
27
27
|
text: string;
|
|
28
28
|
now: number;
|
|
29
29
|
}): Promise<boolean>;
|
|
30
|
+
export declare function recordSuggestions(m: {
|
|
31
|
+
chatId: string;
|
|
32
|
+
botMsgId: string;
|
|
33
|
+
parentMsgId: string | null;
|
|
34
|
+
suggestions: string[];
|
|
35
|
+
now: number;
|
|
36
|
+
}): Promise<boolean>;
|
|
30
37
|
export declare function buildChain(headMsgId: string, opts: {
|
|
31
38
|
chatId: string;
|
|
32
39
|
maxDepth?: number;
|
|
@@ -51,6 +51,20 @@ export async function recordReasoning(m) {
|
|
|
51
51
|
});
|
|
52
52
|
return true;
|
|
53
53
|
}
|
|
54
|
+
export async function recordSuggestions(m) {
|
|
55
|
+
if (!m.suggestions.length)
|
|
56
|
+
return false;
|
|
57
|
+
await putThreadMessage({
|
|
58
|
+
connector: CONNECTOR,
|
|
59
|
+
chatId: m.chatId,
|
|
60
|
+
msgId: `${m.botMsgId}~s`,
|
|
61
|
+
role: 'suggestions',
|
|
62
|
+
text: JSON.stringify(m.suggestions),
|
|
63
|
+
ts: m.now - 1,
|
|
64
|
+
replyToId: m.parentMsgId,
|
|
65
|
+
});
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
54
68
|
export async function buildChain(headMsgId, opts) {
|
|
55
69
|
const max = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
56
70
|
const acc = [];
|
|
@@ -61,7 +75,7 @@ export async function buildChain(headMsgId, opts) {
|
|
|
61
75
|
const stored = await getThreadMessage(CONNECTOR, opts.chatId, cur);
|
|
62
76
|
if (!stored)
|
|
63
77
|
break;
|
|
64
|
-
if (stored.role !== 'reasoning') {
|
|
78
|
+
if (stored.role !== 'reasoning' && stored.role !== 'suggestions') {
|
|
65
79
|
acc.push({
|
|
66
80
|
role: stored.role,
|
|
67
81
|
content: stored.text,
|
package/dist/runtime/config.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { GatePolicy } from '@coffer-org/
|
|
1
|
+
import type { GatePolicy } from '@coffer-org/server/orchestrator';
|
|
2
2
|
export declare function policy(): GatePolicy;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { Connector } from '@coffer-org/
|
|
2
|
-
import { recordAssistant as storeAssistant, recordReasoning as storeReasoning } from './chain-store.ts';
|
|
1
|
+
import type { Connector } from '@coffer-org/server/orchestrator';
|
|
2
|
+
import { recordAssistant as storeAssistant, recordReasoning as storeReasoning, recordSuggestions as storeSuggestions } from './chain-store.ts';
|
|
3
3
|
import type { ReasoningDisplay } from './settings.ts';
|
|
4
4
|
export interface StreamingConnector {
|
|
5
5
|
connector: Connector;
|
|
6
6
|
recorded(): string | null;
|
|
7
|
+
suggestions(): string[] | null;
|
|
7
8
|
}
|
|
8
9
|
export interface StreamingConnectorOpts {
|
|
9
10
|
chatId: string;
|
|
@@ -12,5 +13,6 @@ export interface StreamingConnectorOpts {
|
|
|
12
13
|
display: ReasoningDisplay;
|
|
13
14
|
record?: typeof storeAssistant;
|
|
14
15
|
recordReasoning?: typeof storeReasoning;
|
|
16
|
+
recordSuggestions?: typeof storeSuggestions;
|
|
15
17
|
}
|
|
16
18
|
export declare function makeStreamingConnector(opts: StreamingConnectorOpts): StreamingConnector;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { makeLiveChannel, plainRender } from '@coffer-org/
|
|
2
|
-
import { recordAssistant as storeAssistant, recordReasoning as storeReasoning } from "./chain-store.js";
|
|
1
|
+
import { makeLiveChannel, plainRender } from '@coffer-org/server/orchestrator';
|
|
2
|
+
import { recordAssistant as storeAssistant, recordReasoning as storeReasoning, recordSuggestions as storeSuggestions, } from "./chain-store.js";
|
|
3
3
|
const WEBCHAT_THROTTLE_MS = 250;
|
|
4
4
|
const WEBCHAT_MAX = 100_000;
|
|
5
5
|
export function makeStreamingConnector(opts) {
|
|
6
6
|
const record = opts.record ?? storeAssistant;
|
|
7
7
|
const recordReasoningFn = opts.recordReasoning ?? storeReasoning;
|
|
8
|
+
const recordSuggestionsFn = opts.recordSuggestions ?? storeSuggestions;
|
|
8
9
|
let recordedId = null;
|
|
10
|
+
let lastSuggestions = null;
|
|
9
11
|
const connector = {
|
|
10
12
|
id: 'webchat',
|
|
11
13
|
reply(_chatId, _ctx) {
|
|
@@ -25,6 +27,10 @@ export function makeStreamingConnector(opts) {
|
|
|
25
27
|
});
|
|
26
28
|
return {
|
|
27
29
|
...ch,
|
|
30
|
+
async finish(r) {
|
|
31
|
+
lastSuggestions = r.suggestions;
|
|
32
|
+
return ch.finish(r);
|
|
33
|
+
},
|
|
28
34
|
updateReasoning(acc) {
|
|
29
35
|
if (opts.display === 'off')
|
|
30
36
|
return;
|
|
@@ -51,10 +57,20 @@ export function makeStreamingConnector(opts) {
|
|
|
51
57
|
now,
|
|
52
58
|
});
|
|
53
59
|
}
|
|
60
|
+
if (wrote && lastSuggestions?.length && m.botMsgId) {
|
|
61
|
+
await recordSuggestionsFn({
|
|
62
|
+
chatId: opts.chatId,
|
|
63
|
+
botMsgId: m.botMsgId,
|
|
64
|
+
parentMsgId: m.parentMsgId,
|
|
65
|
+
suggestions: lastSuggestions,
|
|
66
|
+
now,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
54
69
|
},
|
|
55
70
|
};
|
|
56
71
|
return {
|
|
57
72
|
connector,
|
|
58
73
|
recorded: () => recordedId,
|
|
74
|
+
suggestions: () => lastSuggestions,
|
|
59
75
|
};
|
|
60
76
|
}
|
package/dist/runtime/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { pruneThreadState } from '@coffer-org/server/thread-state';
|
|
|
3
3
|
import { threadsAction, historyAction, agentsAction, selectAgentAction } from "./actions.js";
|
|
4
4
|
import { sendAction } from "./send.js";
|
|
5
5
|
import { CONNECTOR } from "./chain-store.js";
|
|
6
|
-
import { registerConnector } from '@coffer-org/
|
|
6
|
+
import { registerConnector } from '@coffer-org/server/orchestrator';
|
|
7
7
|
const THREAD_TTL_MS = Number(process.env['WEBCHAT_THREAD_TTL_MS'] ?? 30 * 86_400_000);
|
|
8
8
|
let unregisterConnector;
|
|
9
9
|
export { WEB_FORMAT } from "./format.js";
|
package/dist/runtime/send.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { handleIncoming } from '@coffer-org/
|
|
1
|
+
import { handleIncoming } from '@coffer-org/server/orchestrator';
|
|
2
2
|
import type { ActionCaller } from '@coffer-org/server/plugin-hooks';
|
|
3
3
|
export interface SendDeps {
|
|
4
4
|
handleIncoming?: typeof handleIncoming;
|
package/dist/runtime/send.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { handleIncoming, liveAgentId } from '@coffer-org/
|
|
2
|
+
import { handleIncoming, liveAgentId } from '@coffer-org/server/orchestrator';
|
|
3
3
|
import { chatIdFor, recordUser, buildChain, history, readSelectionForTurn } from "./chain-store.js";
|
|
4
4
|
import { policy } from "./config.js";
|
|
5
5
|
import { makeStreamingConnector } from "./connector.js";
|
|
@@ -41,7 +41,7 @@ export async function sendAction(body, ctx, deps = {}) {
|
|
|
41
41
|
const replyTo = explicitReplyTo ?? (await history(chatId, 1)).at(-1)?.msgId ?? null;
|
|
42
42
|
await recordUser({ chatId, msgId, sender: ctx.caller.id, text, attachments, ts: nowSec, replyToId: replyTo });
|
|
43
43
|
const messages = await buildChain(msgId, { chatId });
|
|
44
|
-
const { connector, recorded } = makeStreamingConnector({
|
|
44
|
+
const { connector, recorded, suggestions } = makeStreamingConnector({
|
|
45
45
|
chatId,
|
|
46
46
|
botMsgId,
|
|
47
47
|
emit: ctx.emit,
|
|
@@ -57,8 +57,9 @@ export async function sendAction(body, ctx, deps = {}) {
|
|
|
57
57
|
chatId,
|
|
58
58
|
sender: { id: ctx.caller.id },
|
|
59
59
|
messages,
|
|
60
|
+
supportsSuggestions: true,
|
|
60
61
|
}, { policy: policy() });
|
|
61
|
-
ctx.emit('done', { msgId: recorded(), parentMsgId: msgId });
|
|
62
|
+
ctx.emit('done', { msgId: recorded(), parentMsgId: msgId, suggestions: suggestions() });
|
|
62
63
|
}
|
|
63
64
|
function parseAttachments(value) {
|
|
64
65
|
if (!Array.isArray(value))
|