@folotoy/folotoy-openclaw-plugin 0.5.9 → 0.6.2
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/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +167 -42
- package/dist/index.js.map +1 -1
- package/dist/soothing.d.ts +11 -1
- package/dist/soothing.d.ts.map +1 -1
- package/dist/soothing.js +36 -8
- package/dist/soothing.js.map +1 -1
- package/dist/strip-markdown.d.ts +10 -0
- package/dist/strip-markdown.d.ts.map +1 -0
- package/dist/strip-markdown.js +43 -0
- package/dist/strip-markdown.js.map +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/test-message.mjs +48 -5
- package/src/config.ts +9 -1
- package/src/index.ts +162 -40
- package/src/soothing.ts +38 -8
- package/src/strip-markdown.ts +44 -0
package/dist/config.d.ts
CHANGED
|
@@ -27,11 +27,19 @@ export type FlatChannelConfig = {
|
|
|
27
27
|
mqtt_port?: number;
|
|
28
28
|
summary_enabled?: boolean;
|
|
29
29
|
summary_max_chars?: number;
|
|
30
|
+
sentence_split_enabled?: boolean;
|
|
31
|
+
sentence_split_delimiters?: string;
|
|
32
|
+
soothing_loop_enabled?: boolean;
|
|
33
|
+
soothing_loop_interval_ms?: number;
|
|
30
34
|
};
|
|
31
35
|
export declare const DEFAULT_API_URL = "https://api.folotoy.cn";
|
|
32
36
|
export declare const DEFAULT_MQTT_HOST: string;
|
|
33
37
|
export declare const DEFAULT_MQTT_PORT = 1883;
|
|
34
38
|
export declare const DEFAULT_SUMMARY_ENABLED = true;
|
|
35
39
|
export declare const DEFAULT_SUMMARY_MAX_CHARS = 200;
|
|
40
|
+
export declare const DEFAULT_SENTENCE_SPLIT_ENABLED = true;
|
|
41
|
+
export declare const DEFAULT_SENTENCE_SPLIT_DELIMITERS = "\uFF01\u3002\uFF1F\uFF1B!.?;~";
|
|
42
|
+
export declare const DEFAULT_SOOTHING_LOOP_ENABLED = true;
|
|
43
|
+
export declare const DEFAULT_SOOTHING_LOOP_INTERVAL_MS = 3000;
|
|
36
44
|
export declare function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig;
|
|
37
45
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,eAAe,GAAG,eAAe,CAAA;IACvC,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF,CAAA;AAED,8DAA8D;AAC9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,eAAe,GAAG,eAAe,CAAA;IACvC,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF,CAAA;AAED,8DAA8D;AAC9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,eAAe,2BAA2B,CAAA;AACvD,eAAO,MAAM,iBAAiB,QAAkD,CAAA;AAChF,eAAO,MAAM,iBAAiB,OAAO,CAAA;AACrC,eAAO,MAAM,uBAAuB,OAAO,CAAA;AAC3C,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAC5C,eAAO,MAAM,8BAA8B,OAAO,CAAA;AAClD,eAAO,MAAM,iCAAiC,kCAAc,CAAA;AAC5D,eAAO,MAAM,6BAA6B,OAAO,CAAA;AACjD,eAAO,MAAM,iCAAiC,OAAO,CAAA;AAErD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAaxE"}
|
package/dist/config.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export const DEFAULT_API_URL = 'https://api.folotoy.cn';
|
|
2
|
-
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.
|
|
2
|
+
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.folotoy.cn';
|
|
3
3
|
export const DEFAULT_MQTT_PORT = 1883;
|
|
4
4
|
export const DEFAULT_SUMMARY_ENABLED = true;
|
|
5
5
|
export const DEFAULT_SUMMARY_MAX_CHARS = 200;
|
|
6
|
+
export const DEFAULT_SENTENCE_SPLIT_ENABLED = true;
|
|
7
|
+
export const DEFAULT_SENTENCE_SPLIT_DELIMITERS = '!。?;!.?;~';
|
|
8
|
+
export const DEFAULT_SOOTHING_LOOP_ENABLED = true;
|
|
9
|
+
export const DEFAULT_SOOTHING_LOOP_INTERVAL_MS = 3000;
|
|
6
10
|
export function flatToPluginConfig(flat) {
|
|
7
11
|
const flow = flat.flow ?? 'direct';
|
|
8
12
|
const auth = flow === 'api'
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsCA,MAAM,CAAC,MAAM,eAAe,GAAG,wBAAwB,CAAA;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,cAAc,CAAA;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAA;AACrC,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAA;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAC5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAA;AAClD,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAA;AAC5D,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAA;AACjD,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAA;AAErD,MAAM,UAAU,kBAAkB,CAAC,IAAuB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAA;IAClC,MAAM,IAAI,GAAG,IAAI,KAAK,KAAK;QACzB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAc,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE;QAC5H,CAAC,CAAC,EAAE,IAAI,EAAE,QAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAA;IAEvF,OAAO;QACL,IAAI;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,iBAAiB;YACzC,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,iBAAiB;SAC1C;KACF,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAA;AAyb/F,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;;;EAczF;yBAEe,KAAK,iBAAiB;AAAtC,wBAGC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic, buildNotificationTopic } from './mqtt.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createSoothingPicker } from './soothing.js';
|
|
3
|
+
import { stripMarkdown } from './strip-markdown.js';
|
|
4
|
+
import { DEFAULT_MQTT_HOST, DEFAULT_MQTT_PORT, DEFAULT_SUMMARY_ENABLED, DEFAULT_SUMMARY_MAX_CHARS, DEFAULT_SENTENCE_SPLIT_ENABLED, DEFAULT_SENTENCE_SPLIT_DELIMITERS, DEFAULT_SOOTHING_LOOP_ENABLED, DEFAULT_SOOTHING_LOOP_INTERVAL_MS, flatToPluginConfig } from './config.js';
|
|
4
5
|
// Per-account MQTT clients and msgId counters
|
|
5
6
|
const activeClients = new Map();
|
|
6
7
|
// Subagent reference, set at plugin registration
|
|
@@ -30,6 +31,10 @@ const folotoyChannel = {
|
|
|
30
31
|
mqtt_port: { type: 'number', default: DEFAULT_MQTT_PORT },
|
|
31
32
|
summary_enabled: { type: 'boolean', default: DEFAULT_SUMMARY_ENABLED },
|
|
32
33
|
summary_max_chars: { type: 'number', default: DEFAULT_SUMMARY_MAX_CHARS },
|
|
34
|
+
sentence_split_enabled: { type: 'boolean', default: DEFAULT_SENTENCE_SPLIT_ENABLED },
|
|
35
|
+
sentence_split_delimiters: { type: 'string', default: DEFAULT_SENTENCE_SPLIT_DELIMITERS },
|
|
36
|
+
soothing_loop_enabled: { type: 'boolean', default: DEFAULT_SOOTHING_LOOP_ENABLED },
|
|
37
|
+
soothing_loop_interval_ms: { type: 'number', default: DEFAULT_SOOTHING_LOOP_INTERVAL_MS },
|
|
33
38
|
},
|
|
34
39
|
},
|
|
35
40
|
uiHints: {
|
|
@@ -42,6 +47,10 @@ const folotoyChannel = {
|
|
|
42
47
|
mqtt_port: { label: 'MQTT Port' },
|
|
43
48
|
summary_enabled: { label: 'Enable Summary' },
|
|
44
49
|
summary_max_chars: { label: 'Summary Max Characters' },
|
|
50
|
+
sentence_split_enabled: { label: 'Enable Sentence Splitting' },
|
|
51
|
+
sentence_split_delimiters: { label: 'Sentence Delimiters' },
|
|
52
|
+
soothing_loop_enabled: { label: 'Enable Soothing Loop' },
|
|
53
|
+
soothing_loop_interval_ms: { label: 'Soothing Loop Interval (ms)' },
|
|
45
54
|
},
|
|
46
55
|
},
|
|
47
56
|
config: {
|
|
@@ -86,6 +95,10 @@ const folotoyChannel = {
|
|
|
86
95
|
});
|
|
87
96
|
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED;
|
|
88
97
|
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS;
|
|
98
|
+
const sentenceSplitEnabled = account.sentence_split_enabled ?? DEFAULT_SENTENCE_SPLIT_ENABLED;
|
|
99
|
+
const sentenceSplitDelimiters = account.sentence_split_delimiters ?? DEFAULT_SENTENCE_SPLIT_DELIMITERS;
|
|
100
|
+
const soothingLoopEnabled = account.soothing_loop_enabled ?? DEFAULT_SOOTHING_LOOP_ENABLED;
|
|
101
|
+
const soothingLoopIntervalMs = account.soothing_loop_interval_ms ?? DEFAULT_SOOTHING_LOOP_INTERVAL_MS;
|
|
89
102
|
client.on('message', (_topic, payload) => {
|
|
90
103
|
let msg;
|
|
91
104
|
try {
|
|
@@ -98,12 +111,14 @@ const folotoyChannel = {
|
|
|
98
111
|
return;
|
|
99
112
|
const { msgId, inputParams: { text, recording_id } } = msg;
|
|
100
113
|
let order = 1;
|
|
114
|
+
// Create a per-message picker that cycles through candidates without repeats
|
|
115
|
+
const soothingPick = createSoothingPicker(text);
|
|
101
116
|
// Send a quick soothing acknowledgment before AI processing (order=1).
|
|
102
117
|
// AI replies continue from order=2.
|
|
103
118
|
const ackMsg = {
|
|
104
119
|
msgId,
|
|
105
120
|
identifier: 'chat_output',
|
|
106
|
-
outParams: { content:
|
|
121
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
107
122
|
};
|
|
108
123
|
client.publish(outboundTopic, JSON.stringify(ackMsg));
|
|
109
124
|
const peer = { kind: 'direct', id: credentials.toy_sn };
|
|
@@ -127,13 +142,120 @@ const folotoyChannel = {
|
|
|
127
142
|
});
|
|
128
143
|
// dispatch using dispatchReplyFromConfig (full agent capabilities including tools)
|
|
129
144
|
void (async () => {
|
|
130
|
-
|
|
145
|
+
// Sentence-level streaming: buffer incoming text and flush complete
|
|
146
|
+
// sentences (delimited by punctuation) to the toy immediately.
|
|
147
|
+
const sentenceDelimiterRe = sentenceSplitEnabled
|
|
148
|
+
? new RegExp(`[${sentenceSplitDelimiters.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')}]`)
|
|
149
|
+
: null;
|
|
150
|
+
let sentenceBuffer = '';
|
|
151
|
+
let totalSentChars = 0;
|
|
152
|
+
let firstSentenceFlushed = false;
|
|
153
|
+
let llmStarted = false;
|
|
154
|
+
// Soothing loop: check every 200ms if LLM has started responding.
|
|
155
|
+
// If not, send a soothing reply at a pace matching TTS playback
|
|
156
|
+
// (controlled by soothingLoopIntervalMs). Keeps running until LLM
|
|
157
|
+
// returns its first chunk — no fixed max count.
|
|
158
|
+
let soothingCheckTimer = null;
|
|
159
|
+
let lastSoothingSentAt = 0;
|
|
160
|
+
const stopSoothingLoop = () => {
|
|
161
|
+
if (soothingCheckTimer) {
|
|
162
|
+
clearInterval(soothingCheckTimer);
|
|
163
|
+
soothingCheckTimer = null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
if (soothingLoopEnabled) {
|
|
167
|
+
soothingCheckTimer = setInterval(() => {
|
|
168
|
+
if (llmStarted) {
|
|
169
|
+
stopSoothingLoop();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
if (now - lastSoothingSentAt >= soothingLoopIntervalMs) {
|
|
174
|
+
lastSoothingSentAt = now;
|
|
175
|
+
order++;
|
|
176
|
+
const extraAck = {
|
|
177
|
+
msgId,
|
|
178
|
+
identifier: 'chat_output',
|
|
179
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
180
|
+
};
|
|
181
|
+
client.publish(outboundTopic, JSON.stringify(extraAck));
|
|
182
|
+
}
|
|
183
|
+
}, 200);
|
|
184
|
+
}
|
|
185
|
+
// First sentence: split purely by punctuation (fast first response).
|
|
186
|
+
// Subsequent sentences: require a minimum length that grows with each
|
|
187
|
+
// sentence (base 20 chars, +10 per sentence), so later chunks are longer.
|
|
188
|
+
let sentenceCount = 0;
|
|
189
|
+
const minLenForSentence = () => {
|
|
190
|
+
if (sentenceCount === 0)
|
|
191
|
+
return 0; // first sentence: no minimum
|
|
192
|
+
return Math.min(100, 20 + (sentenceCount - 1) * 10);
|
|
193
|
+
};
|
|
194
|
+
const FORCE_FLUSH_LEN = 200; // force flush if buffer has no delimiter for this many chars
|
|
195
|
+
const publishSentence = (sentence) => {
|
|
196
|
+
sentenceCount++;
|
|
197
|
+
if (!firstSentenceFlushed)
|
|
198
|
+
firstSentenceFlushed = true;
|
|
199
|
+
order++;
|
|
200
|
+
totalSentChars += sentence.length;
|
|
201
|
+
const outMsg = {
|
|
202
|
+
msgId,
|
|
203
|
+
identifier: 'chat_output',
|
|
204
|
+
outParams: { content: sentence, recording_id, order, is_finished: false },
|
|
205
|
+
};
|
|
206
|
+
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
207
|
+
};
|
|
208
|
+
const flushSentences = () => {
|
|
209
|
+
if (!sentenceDelimiterRe)
|
|
210
|
+
return;
|
|
211
|
+
while (true) {
|
|
212
|
+
const minLen = minLenForSentence();
|
|
213
|
+
// Search for a delimiter that is at or beyond the minimum length
|
|
214
|
+
let idx = -1;
|
|
215
|
+
const searchFrom = Math.max(0, minLen - 1);
|
|
216
|
+
if (searchFrom < sentenceBuffer.length) {
|
|
217
|
+
const sub = sentenceBuffer.slice(searchFrom);
|
|
218
|
+
const match = sub.search(sentenceDelimiterRe);
|
|
219
|
+
if (match !== -1)
|
|
220
|
+
idx = searchFrom + match;
|
|
221
|
+
}
|
|
222
|
+
if (idx !== -1) {
|
|
223
|
+
const sentence = sentenceBuffer.slice(0, idx + 1).trim();
|
|
224
|
+
sentenceBuffer = sentenceBuffer.slice(idx + 1);
|
|
225
|
+
if (sentence)
|
|
226
|
+
publishSentence(sentence);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
// No delimiter found — force flush if buffer exceeds limit
|
|
230
|
+
if (sentenceBuffer.length >= FORCE_FLUSH_LEN) {
|
|
231
|
+
// Try to break at the last space/comma for a cleaner cut
|
|
232
|
+
let cutIdx = FORCE_FLUSH_LEN;
|
|
233
|
+
const lastSpace = sentenceBuffer.lastIndexOf(' ', FORCE_FLUSH_LEN);
|
|
234
|
+
const lastComma = sentenceBuffer.lastIndexOf(',', FORCE_FLUSH_LEN);
|
|
235
|
+
const lastBreak = Math.max(lastSpace, lastComma);
|
|
236
|
+
if (lastBreak > FORCE_FLUSH_LEN / 2)
|
|
237
|
+
cutIdx = lastBreak + 1;
|
|
238
|
+
const sentence = sentenceBuffer.slice(0, cutIdx).trim();
|
|
239
|
+
sentenceBuffer = sentenceBuffer.slice(cutIdx);
|
|
240
|
+
if (sentence)
|
|
241
|
+
publishSentence(sentence);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
131
247
|
const dispatcher = {
|
|
132
248
|
sendToolResult: () => true,
|
|
133
249
|
sendBlockReply: () => true,
|
|
134
250
|
sendFinalReply: (payload) => {
|
|
135
|
-
if (payload.text)
|
|
136
|
-
|
|
251
|
+
if (payload.text) {
|
|
252
|
+
if (!llmStarted) {
|
|
253
|
+
llmStarted = true;
|
|
254
|
+
stopSoothingLoop();
|
|
255
|
+
}
|
|
256
|
+
sentenceBuffer += stripMarkdown(payload.text);
|
|
257
|
+
flushSentences();
|
|
258
|
+
}
|
|
137
259
|
return true;
|
|
138
260
|
},
|
|
139
261
|
waitForIdle: async () => { },
|
|
@@ -149,50 +271,53 @@ const folotoyChannel = {
|
|
|
149
271
|
dispatcher,
|
|
150
272
|
}),
|
|
151
273
|
});
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
274
|
+
// Flush remaining buffer (text after the last punctuation)
|
|
275
|
+
let remaining = sentenceBuffer.trim();
|
|
276
|
+
if (remaining) {
|
|
277
|
+
// Summarize remaining text if enabled and total response exceeds threshold
|
|
278
|
+
if (summaryEnabled && subagent && (totalSentChars + remaining.length) > summaryMaxChars) {
|
|
279
|
+
try {
|
|
280
|
+
const summarySessionKey = `folotoy-summary-${accountId}-${Date.now()}`;
|
|
281
|
+
const { runId } = await subagent.run({
|
|
282
|
+
sessionKey: summarySessionKey,
|
|
283
|
+
message: [
|
|
284
|
+
`You are an assistant that summarizes texts concisely while keeping the most important information.`,
|
|
285
|
+
`Summarize the text to approximately ${Math.max(50, summaryMaxChars - totalSentChars)} characters.`,
|
|
286
|
+
`Maintain the original tone and style. Reply only with the summary, without additional explanations.`,
|
|
287
|
+
``,
|
|
288
|
+
`<text_to_summarize>`,
|
|
289
|
+
remaining,
|
|
290
|
+
`</text_to_summarize>`,
|
|
291
|
+
].join('\n'),
|
|
292
|
+
deliver: false,
|
|
293
|
+
});
|
|
294
|
+
const result = await subagent.waitForRun({ runId, timeoutMs: 30_000 });
|
|
295
|
+
if (result.status === 'ok') {
|
|
296
|
+
const { messages } = await subagent.getSessionMessages({ sessionKey: summarySessionKey, limit: 1 });
|
|
297
|
+
const lastMsg = messages[messages.length - 1];
|
|
298
|
+
const summaryText = lastMsg?.content ?? lastMsg?.text;
|
|
299
|
+
if (summaryText)
|
|
300
|
+
remaining = summaryText;
|
|
301
|
+
}
|
|
302
|
+
await subagent.deleteSession({ sessionKey: summarySessionKey }).catch(() => { });
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
log?.warn?.(`Summary failed, truncating text: ${String(err)}`);
|
|
306
|
+
const maxRemaining = Math.max(50, summaryMaxChars - totalSentChars);
|
|
307
|
+
remaining = `${remaining.slice(0, maxRemaining - 3)}...`;
|
|
177
308
|
}
|
|
178
|
-
await subagent.deleteSession({ sessionKey }).catch(() => { });
|
|
179
|
-
}
|
|
180
|
-
catch (err) {
|
|
181
|
-
log?.warn?.(`Summary failed, truncating text: ${String(err)}`);
|
|
182
|
-
finalText = `${finalText.slice(0, summaryMaxChars - 3)}...`;
|
|
183
309
|
}
|
|
184
|
-
}
|
|
185
|
-
if (finalText) {
|
|
186
310
|
order++;
|
|
187
311
|
const outMsg = {
|
|
188
312
|
msgId,
|
|
189
313
|
identifier: 'chat_output',
|
|
190
|
-
outParams: { content:
|
|
314
|
+
outParams: { content: remaining, recording_id, order, is_finished: false },
|
|
191
315
|
};
|
|
192
316
|
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
193
317
|
}
|
|
194
318
|
}
|
|
195
319
|
finally {
|
|
320
|
+
stopSoothingLoop();
|
|
196
321
|
order++;
|
|
197
322
|
const finishMsg = {
|
|
198
323
|
msgId,
|
|
@@ -232,7 +357,7 @@ const folotoyChannel = {
|
|
|
232
357
|
const pad = (n) => String(n).padStart(2, '0');
|
|
233
358
|
const nowLocal = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${tzSuffix}`;
|
|
234
359
|
return [
|
|
235
|
-
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}). Current time: ${nowLocal} (${tz}).`,
|
|
360
|
+
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}). Current time: ${nowLocal} (${tz}). IMPORTANT: Your reply will be converted to speech (TTS). Use plain text only — no markdown, no bullet points, no numbered lists, no code blocks, no URLs. Write in a natural, conversational tone.`,
|
|
236
361
|
[
|
|
237
362
|
`To set reminders/timers, use the cron tool with action="add". IMPORTANT:`,
|
|
238
363
|
`- schedule.at MUST use the same timezone offset as current time (${tzSuffix}), NEVER use "Z"`,
|
|
@@ -272,7 +397,7 @@ const folotoyChannel = {
|
|
|
272
397
|
const notifMsg = {
|
|
273
398
|
msgId,
|
|
274
399
|
identifier: 'send_notification',
|
|
275
|
-
outParams: { text },
|
|
400
|
+
outParams: { text: stripMarkdown(text) },
|
|
276
401
|
};
|
|
277
402
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg));
|
|
278
403
|
return { channel: 'folotoy', messageId: String(msgId) };
|
|
@@ -289,7 +414,7 @@ export function sendNotification({ text, accountId }) {
|
|
|
289
414
|
const notifMsg = {
|
|
290
415
|
msgId,
|
|
291
416
|
identifier: 'send_notification',
|
|
292
|
-
outParams: { text },
|
|
417
|
+
outParams: { text: stripMarkdown(text) },
|
|
293
418
|
};
|
|
294
419
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg));
|
|
295
420
|
return { channel: 'folotoy', messageId: String(msgId) };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAC/H,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAsB1I,8CAA8C;AAC9C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqE,CAAA;AAElG,iDAAiD;AACjD,IAAI,QAA+C,CAAA;AAEnD,MAAM,cAAc,GAAqC;IACvD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE;QACJ,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;QACzB,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,qDAAqD;KAC7D;IACD,YAAY,EAAE;QACZ,SAAS,EAAE,CAAC,QAAQ,CAAC;KACtB;IACD,YAAY,EAAE;QACZ,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE;gBACpE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wBAAwB,EAAE;gBAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,EAAE;gBACtE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,yBAAyB,EAAE;aAC1E;SACF;QACD,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YAC5B,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;YAC3B,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,yBAAyB,EAAE;YACrE,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;YACjE,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YACjC,eAAe,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;YAC5C,iBAAiB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;SACvD;KACF;IACD,MAAM,EAAE;QACN,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACnC,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;YAClC,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,IAAK,EAAwB,CAAA;QAC7C,CAAC;QACD,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YAC5B,MAAM,EAAE,GAAI,GAAgF;gBAC1F,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAA;YAC7B,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC9B,CAAC;KACF;IACD,OAAO,EAAE;QACP,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;YAEzE,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,yDAAyD,CAAC,CAAA;gBACtE,OAAM;YACR,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,kDAAkD,CAAC,CAAA;gBAC/D,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;YAC9C,GAAG,EAAE,IAAI,EAAE,CAAC,6BAA6B,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAA;YAC3F,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAA;YACxD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;YAC9D,MAAM,YAAY,GAAG,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAC1D,MAAM,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAE5D,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;YAClF,GAAG,EAAE,IAAI,EAAE,CAAC,2CAA2C,YAAY,EAAE,CAAC,CAAA;YAEtE,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;gBACrC,IAAI,GAAG;oBAAE,GAAG,EAAE,KAAK,EAAE,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YAC9D,CAAC,CAAC,CAAA;YAEF,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,IAAI,uBAAuB,CAAA;YACzE,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,IAAI,yBAAyB,CAAA;YAE9E,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;gBACvC,IAAI,GAAmB,CAAA;gBACvB,IAAI,CAAC;oBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAmB,CAAA;gBACxD,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAM;gBACR,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,KAAK,YAAY,IAAI,OAAO,GAAG,CAAC,WAAW,EAAE,IAAI,KAAK,QAAQ;oBAAE,OAAM;gBAExF,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,GAAG,CAAA;gBAC1D,IAAI,KAAK,GAAG,CAAC,CAAA;gBAEb,uEAAuE;gBACvE,oCAAoC;gBACpC,MAAM,MAAM,GAAoB;oBAC9B,KAAK;oBACL,UAAU,EAAE,aAAa;oBACzB,SAAS,EAAE,EAAE,OAAO,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;iBACzF,CAAA;gBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;gBAErD,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,QAAiB,EAAE,EAAE,EAAE,WAAW,CAAC,MAAM,EAAE,CAAA;gBAChE,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,oBAAoB,CAAC;oBAC7D,OAAO,EAAE,MAAM;oBACf,OAAO,EAAE,SAAS;oBAClB,SAAS;oBACT,IAAI;oBACJ,OAAO,EAAE,kBAAkB;iBAC5B,CAAC,CAAA;gBAEF,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,sBAAsB,CAAC;oBAC7D,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,WAAW,CAAC,MAAM;oBACxB,EAAE,EAAE,WAAW,CAAC,MAAM;oBACtB,UAAU,EAAE,UAAU;oBACtB,SAAS,EAAE,SAAS;oBACpB,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,SAAS;oBAClB,kBAAkB,EAAE,SAAS;oBAC7B,aAAa,EAAE,WAAW,CAAC,MAAM;iBAClC,CAAC,CAAA;gBAEF,mFAAmF;gBACnF,KAAK,CAAC,KAAK,IAAI,EAAE;oBACf,MAAM,WAAW,GAAa,EAAE,CAAA;oBAChC,MAAM,UAAU,GAAG;wBACjB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;wBAC1B,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;wBAC1B,cAAc,EAAE,CAAC,OAA0B,EAAE,EAAE;4BAC7C,IAAI,OAAO,CAAC,IAAI;gCAAE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;4BAChD,OAAO,IAAI,CAAA;wBACb,CAAC;wBACD,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;wBAC3B,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;wBACxD,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;qBACvB,CAAA;oBACD,IAAI,CAAC;wBACH,MAAM,cAAc,CAAC,KAAK,CAAC,mBAAmB,CAAC;4BAC7C,UAAU;4BACV,GAAG,EAAE,GAAG,EAAE,CACR,cAAc,CAAC,KAAK,CAAC,uBAAuB,CAAC;gCAC3C,GAAG,EAAE,UAAU;gCACf,GAAG;gCACH,UAAU;6BACX,CAAC;yBACL,CAAC,CAAA;wBAEF,IAAI,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;wBAEpC,kDAAkD;wBAClD,IAAI,cAAc,IAAI,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;4BACrE,IAAI,CAAC;gCACH,MAAM,UAAU,GAAG,mBAAmB,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;gCAC/D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC;oCACnC,UAAU;oCACV,OAAO,EAAE;wCACP,oGAAoG;wCACpG,uCAAuC,eAAe,cAAc;wCACpE,qGAAqG;wCACrG,EAAE;wCACF,qBAAqB;wCACrB,SAAS;wCACT,sBAAsB;qCACvB,CAAC,IAAI,CAAC,IAAI,CAAC;oCACZ,OAAO,EAAE,KAAK;iCACf,CAAC,CAAA;gCACF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;gCACtE,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;oCAC3B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;oCAChF,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAoD,CAAA;oCAChG,MAAM,WAAW,GAAG,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,IAAI,CAAA;oCACrD,IAAI,WAAW;wCAAE,SAAS,GAAG,WAAW,CAAA;gCAC1C,CAAC;gCACD,MAAM,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;4BAC9D,CAAC;4BAAC,OAAO,GAAG,EAAE,CAAC;gCACb,GAAG,EAAE,IAAI,EAAE,CAAC,oCAAoC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gCAC9D,SAAS,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,GAAG,CAAC,CAAC,KAAK,CAAA;4BAC7D,CAAC;wBACH,CAAC;wBAED,IAAI,SAAS,EAAE,CAAC;4BACd,KAAK,EAAE,CAAA;4BACP,MAAM,MAAM,GAAoB;gCAC9B,KAAK;gCACL,UAAU,EAAE,aAAa;gCACzB,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;6BAC3E,CAAA;4BACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;wBACvD,CAAC;oBACH,CAAC;4BAAS,CAAC;wBACT,KAAK,EAAE,CAAA;wBACP,MAAM,SAAS,GAAoB;4BACjC,KAAK;4BACL,UAAU,EAAE,aAAa;4BACzB,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE;yBACnE,CAAA;wBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA;oBAC1D,CAAC;gBACH,CAAC,CAAC,EAAE,CAAA;YACN,CAAC,CAAC,CAAA;YAEF,uCAAuC;YACvC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBACnC,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;oBACzC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;oBAC/B,MAAM,CAAC,GAAG,EAAE,CAAA;oBACZ,GAAG,EAAE,IAAI,EAAE,CAAC,0BAA0B,CAAC,CAAA;oBACvC,OAAO,EAAE,CAAA;gBACX,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC1B,0DAA0D;QAC5D,CAAC;KACF;IAED,WAAW,EAAE;QACX,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YAC5B,MAAM,OAAO,GAAI,GAAgF;gBAC/F,EAAE,QAAQ,EAAE,OAAO,CAAA;YACrB,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,CAAA;YACxC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;YACtB,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ,CAAA;YAC3D,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAA;YAC5C,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;YAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAC3E,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAC/D,MAAM,QAAQ,GAAG,GAAG,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;YACzC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACrD,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAA;YAC5K,OAAO;gBACL,qDAAqD,EAAE,oBAAoB,QAAQ,KAAK,EAAE,IAAI;gBAC9F;oBACE,0EAA0E;oBAC1E,oEAAoE,QAAQ,kBAAkB;oBAC9F,yDAAyD,QAAQ,EAAE;oBACnE,0FAA0F;oBAC1F,oCAAoC;oBACpC,oEAAoE,EAAE,0BAA0B;oBAChG,kGAAkG,QAAQ,8IAA8I,EAAE,2CAA2C;iBACtS,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAA;QACH,CAAC;KACF;IAED,SAAS,EAAE;QACT,cAAc,EAAE;YACd,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;YACvB,IAAI,EAAE,oCAAoC;SAC3C;KACF;IAGD,QAAQ,EAAE;QACR,YAAY,EAAE,QAAQ;QACtB,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YAC7B,IAAI,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;YAC/B,MAAM,OAAO,GAAI,GAAgF;gBAC/F,EAAE,QAAQ,EAAE,OAAO,CAAA;YACrB,IAAI,OAAO,EAAE,MAAM;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;YAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE,CAAA;QAC5E,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;YACtC,MAAM,GAAG,GAAG,SAAS,IAAI,SAAS,CAAA;YAClC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YAC3E,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAA;YAEzE,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;YAC/B,MAAM,QAAQ,GAAwB;gBACpC,KAAK;gBACL,UAAU,EAAE,mBAAmB;gBAC/B,SAAS,EAAE,EAAE,IAAI,EAAE;aACpB,CAAA;YACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;YACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;QACzD,CAAC;KACF;CACF,CAAA;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAwC;IACxF,MAAM,GAAG,GAAG,SAAS,IAAI,SAAS,CAAA;IAClC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAA;IAEzE,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;IAC/B,MAAM,QAAQ,GAAwB;QACpC,KAAK;QACL,UAAU,EAAE,mBAAmB;QAC/B,SAAS,EAAE,EAAE,IAAI,EAAE;KACpB,CAAA;IACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;IACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AACzD,CAAC;AAED,eAAe,CAAC,GAAsB,EAAE,EAAE;IACxC,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAA;IAC/B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;AACjD,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAC/H,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,iCAAiC,EAAE,6BAA6B,EAAE,iCAAiC,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAsB/Q,8CAA8C;AAC9C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqE,CAAA;AAElG,iDAAiD;AACjD,IAAI,QAA+C,CAAA;AAEnD,MAAM,cAAc,GAAqC;IACvD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE;QACJ,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;QACzB,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,qDAAqD;KAC7D;IACD,YAAY,EAAE;QACZ,SAAS,EAAE,CAAC,QAAQ,CAAC;KACtB;IACD,YAAY,EAAE;QACZ,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE;gBACpE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wBAAwB,EAAE;gBAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,EAAE;gBACtE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,yBAAyB,EAAE;gBACzE,sBAAsB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,8BAA8B,EAAE;gBACpF,yBAAyB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iCAAiC,EAAE;gBACzF,qBAAqB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,6BAA6B,EAAE;gBAClF,yBAAyB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iCAAiC,EAAE;aAC1F;SACF;QACD,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YAC5B,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;YAC3B,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,yBAAyB,EAAE;YACrE,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;YACjE,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YACjC,eAAe,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;YAC5C,iBAAiB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;YACtD,sBAAsB,EAAE,EAAE,KAAK,EAAE,2BAA2B,EAAE;YAC9D,yBAAyB,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE;YAC3D,qBAAqB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;YACxD,yBAAyB,EAAE,EAAE,KAAK,EAAE,6BAA6B,EAAE;SACpE;KACF;IACD,MAAM,EAAE;QACN,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACnC,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;YAClC,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,IAAK,EAAwB,CAAA;QAC7C,CAAC;QACD,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YAC5B,MAAM,EAAE,GAAI,GAAgF;gBAC1F,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAA;YAC7B,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC9B,CAAC;KACF;IACD,OAAO,EAAE;QACP,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;YAEzE,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,yDAAyD,CAAC,CAAA;gBACtE,OAAM;YACR,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,kDAAkD,CAAC,CAAA;gBAC/D,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;YAC9C,GAAG,EAAE,IAAI,EAAE,CAAC,6BAA6B,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAA;YAC3F,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAA;YACxD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;YAC9D,MAAM,YAAY,GAAG,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAC1D,MAAM,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAE5D,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;YAClF,GAAG,EAAE,IAAI,EAAE,CAAC,2CAA2C,YAAY,EAAE,CAAC,CAAA;YAEtE,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;gBACrC,IAAI,GAAG;oBAAE,GAAG,EAAE,KAAK,EAAE,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YAC9D,CAAC,CAAC,CAAA;YAEF,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,IAAI,uBAAuB,CAAA;YACzE,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,IAAI,yBAAyB,CAAA;YAC9E,MAAM,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,IAAI,8BAA8B,CAAA;YAC7F,MAAM,uBAAuB,GAAG,OAAO,CAAC,yBAAyB,IAAI,iCAAiC,CAAA;YACtG,MAAM,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,IAAI,6BAA6B,CAAA;YAC1F,MAAM,sBAAsB,GAAG,OAAO,CAAC,yBAAyB,IAAI,iCAAiC,CAAA;YAErG,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;gBACvC,IAAI,GAAmB,CAAA;gBACvB,IAAI,CAAC;oBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAmB,CAAA;gBACxD,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAM;gBACR,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,KAAK,YAAY,IAAI,OAAO,GAAG,CAAC,WAAW,EAAE,IAAI,KAAK,QAAQ;oBAAE,OAAM;gBAExF,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,GAAG,CAAA;gBAC1D,IAAI,KAAK,GAAG,CAAC,CAAA;gBAEb,6EAA6E;gBAC7E,MAAM,YAAY,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAA;gBAE/C,uEAAuE;gBACvE,oCAAoC;gBACpC,MAAM,MAAM,GAAoB;oBAC9B,KAAK;oBACL,UAAU,EAAE,aAAa;oBACzB,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;iBAChF,CAAA;gBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;gBAErD,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,QAAiB,EAAE,EAAE,EAAE,WAAW,CAAC,MAAM,EAAE,CAAA;gBAChE,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,oBAAoB,CAAC;oBAC7D,OAAO,EAAE,MAAM;oBACf,OAAO,EAAE,SAAS;oBAClB,SAAS;oBACT,IAAI;oBACJ,OAAO,EAAE,kBAAkB;iBAC5B,CAAC,CAAA;gBAEF,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,sBAAsB,CAAC;oBAC7D,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,WAAW,CAAC,MAAM;oBACxB,EAAE,EAAE,WAAW,CAAC,MAAM;oBACtB,UAAU,EAAE,UAAU;oBACtB,SAAS,EAAE,SAAS;oBACpB,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,SAAS;oBAClB,kBAAkB,EAAE,SAAS;oBAC7B,aAAa,EAAE,WAAW,CAAC,MAAM;iBAClC,CAAC,CAAA;gBAEF,mFAAmF;gBACnF,KAAK,CAAC,KAAK,IAAI,EAAE;oBACf,oEAAoE;oBACpE,+DAA+D;oBAC/D,MAAM,mBAAmB,GAAG,oBAAoB;wBAC9C,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,uBAAuB,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,GAAG,CAAC;wBACrF,CAAC,CAAC,IAAI,CAAA;oBACR,IAAI,cAAc,GAAG,EAAE,CAAA;oBACvB,IAAI,cAAc,GAAG,CAAC,CAAA;oBACtB,IAAI,oBAAoB,GAAG,KAAK,CAAA;oBAChC,IAAI,UAAU,GAAG,KAAK,CAAA;oBAEtB,kEAAkE;oBAClE,gEAAgE;oBAChE,kEAAkE;oBAClE,gDAAgD;oBAChD,IAAI,kBAAkB,GAA0C,IAAI,CAAA;oBACpE,IAAI,kBAAkB,GAAG,CAAC,CAAA;oBAC1B,MAAM,gBAAgB,GAAG,GAAG,EAAE;wBAC5B,IAAI,kBAAkB,EAAE,CAAC;4BAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;4BAAC,kBAAkB,GAAG,IAAI,CAAA;wBAAC,CAAC;oBAC1F,CAAC,CAAA;oBACD,IAAI,mBAAmB,EAAE,CAAC;wBACxB,kBAAkB,GAAG,WAAW,CAAC,GAAG,EAAE;4BACpC,IAAI,UAAU,EAAE,CAAC;gCACf,gBAAgB,EAAE,CAAA;gCAClB,OAAM;4BACR,CAAC;4BACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;4BACtB,IAAI,GAAG,GAAG,kBAAkB,IAAI,sBAAsB,EAAE,CAAC;gCACvD,kBAAkB,GAAG,GAAG,CAAA;gCACxB,KAAK,EAAE,CAAA;gCACP,MAAM,QAAQ,GAAoB;oCAChC,KAAK;oCACL,UAAU,EAAE,aAAa;oCACzB,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;iCAChF,CAAA;gCACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;4BACzD,CAAC;wBACH,CAAC,EAAE,GAAG,CAAC,CAAA;oBACT,CAAC;oBAED,qEAAqE;oBACrE,sEAAsE;oBACtE,0EAA0E;oBAC1E,IAAI,aAAa,GAAG,CAAC,CAAA;oBACrB,MAAM,iBAAiB,GAAG,GAAG,EAAE;wBAC7B,IAAI,aAAa,KAAK,CAAC;4BAAE,OAAO,CAAC,CAAA,CAAC,6BAA6B;wBAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAA;oBACrD,CAAC,CAAA;oBAED,MAAM,eAAe,GAAG,GAAG,CAAA,CAAC,6DAA6D;oBAEzF,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAE,EAAE;wBAC3C,aAAa,EAAE,CAAA;wBACf,IAAI,CAAC,oBAAoB;4BAAE,oBAAoB,GAAG,IAAI,CAAA;wBACtD,KAAK,EAAE,CAAA;wBACP,cAAc,IAAI,QAAQ,CAAC,MAAM,CAAA;wBACjC,MAAM,MAAM,GAAoB;4BAC9B,KAAK;4BACL,UAAU,EAAE,aAAa;4BACzB,SAAS,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;yBAC1E,CAAA;wBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;oBACvD,CAAC,CAAA;oBAED,MAAM,cAAc,GAAG,GAAG,EAAE;wBAC1B,IAAI,CAAC,mBAAmB;4BAAE,OAAM;wBAChC,OAAO,IAAI,EAAE,CAAC;4BACZ,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;4BAClC,iEAAiE;4BACjE,IAAI,GAAG,GAAG,CAAC,CAAC,CAAA;4BACZ,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAA;4BAC1C,IAAI,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC;gCACvC,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;gCAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;gCAC7C,IAAI,KAAK,KAAK,CAAC,CAAC;oCAAE,GAAG,GAAG,UAAU,GAAG,KAAK,CAAA;4BAC5C,CAAC;4BACD,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;gCACf,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;gCACxD,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;gCAC9C,IAAI,QAAQ;oCAAE,eAAe,CAAC,QAAQ,CAAC,CAAA;gCACvC,SAAQ;4BACV,CAAC;4BACD,2DAA2D;4BAC3D,IAAI,cAAc,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;gCAC7C,yDAAyD;gCACzD,IAAI,MAAM,GAAG,eAAe,CAAA;gCAC5B,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;gCAClE,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;gCAClE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;gCAChD,IAAI,SAAS,GAAG,eAAe,GAAG,CAAC;oCAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAAA;gCAC3D,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;gCACvD,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;gCAC7C,IAAI,QAAQ;oCAAE,eAAe,CAAC,QAAQ,CAAC,CAAA;gCACvC,SAAQ;4BACV,CAAC;4BACD,MAAK;wBACP,CAAC;oBACH,CAAC,CAAA;oBAED,MAAM,UAAU,GAAG;wBACjB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;wBAC1B,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;wBAC1B,cAAc,EAAE,CAAC,OAA0B,EAAE,EAAE;4BAC7C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gCACjB,IAAI,CAAC,UAAU,EAAE,CAAC;oCAChB,UAAU,GAAG,IAAI,CAAA;oCACjB,gBAAgB,EAAE,CAAA;gCACpB,CAAC;gCACD,cAAc,IAAI,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gCAC7C,cAAc,EAAE,CAAA;4BAClB,CAAC;4BACD,OAAO,IAAI,CAAA;wBACb,CAAC;wBACD,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;wBAC3B,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;wBACxD,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;qBACvB,CAAA;oBACD,IAAI,CAAC;wBACH,MAAM,cAAc,CAAC,KAAK,CAAC,mBAAmB,CAAC;4BAC7C,UAAU;4BACV,GAAG,EAAE,GAAG,EAAE,CACR,cAAc,CAAC,KAAK,CAAC,uBAAuB,CAAC;gCAC3C,GAAG,EAAE,UAAU;gCACf,GAAG;gCACH,UAAU;6BACX,CAAC;yBACL,CAAC,CAAA;wBAEF,2DAA2D;wBAC3D,IAAI,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,CAAA;wBACrC,IAAI,SAAS,EAAE,CAAC;4BACd,2EAA2E;4BAC3E,IAAI,cAAc,IAAI,QAAQ,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,eAAe,EAAE,CAAC;gCACxF,IAAI,CAAC;oCACH,MAAM,iBAAiB,GAAG,mBAAmB,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;oCACtE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC;wCACnC,UAAU,EAAE,iBAAiB;wCAC7B,OAAO,EAAE;4CACP,oGAAoG;4CACpG,uCAAuC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,GAAG,cAAc,CAAC,cAAc;4CACnG,qGAAqG;4CACrG,EAAE;4CACF,qBAAqB;4CACrB,SAAS;4CACT,sBAAsB;yCACvB,CAAC,IAAI,CAAC,IAAI,CAAC;wCACZ,OAAO,EAAE,KAAK;qCACf,CAAC,CAAA;oCACF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;oCACtE,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;wCAC3B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;wCACnG,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAoD,CAAA;wCAChG,MAAM,WAAW,GAAG,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,IAAI,CAAA;wCACrD,IAAI,WAAW;4CAAE,SAAS,GAAG,WAAW,CAAA;oCAC1C,CAAC;oCACD,MAAM,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;gCACjF,CAAC;gCAAC,OAAO,GAAG,EAAE,CAAC;oCACb,GAAG,EAAE,IAAI,EAAE,CAAC,oCAAoC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;oCAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,GAAG,cAAc,CAAC,CAAA;oCACnE,SAAS,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC,KAAK,CAAA;gCAC1D,CAAC;4BACH,CAAC;4BAED,KAAK,EAAE,CAAA;4BACP,MAAM,MAAM,GAAoB;gCAC9B,KAAK;gCACL,UAAU,EAAE,aAAa;gCACzB,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;6BAC3E,CAAA;4BACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;wBACvD,CAAC;oBACH,CAAC;4BAAS,CAAC;wBACT,gBAAgB,EAAE,CAAA;wBAClB,KAAK,EAAE,CAAA;wBACP,MAAM,SAAS,GAAoB;4BACjC,KAAK;4BACL,UAAU,EAAE,aAAa;4BACzB,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE;yBACnE,CAAA;wBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA;oBAC1D,CAAC;gBACH,CAAC,CAAC,EAAE,CAAA;YACN,CAAC,CAAC,CAAA;YAEF,uCAAuC;YACvC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBACnC,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;oBACzC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;oBAC/B,MAAM,CAAC,GAAG,EAAE,CAAA;oBACZ,GAAG,EAAE,IAAI,EAAE,CAAC,0BAA0B,CAAC,CAAA;oBACvC,OAAO,EAAE,CAAA;gBACX,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC1B,0DAA0D;QAC5D,CAAC;KACF;IAED,WAAW,EAAE;QACX,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YAC5B,MAAM,OAAO,GAAI,GAAgF;gBAC/F,EAAE,QAAQ,EAAE,OAAO,CAAA;YACrB,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,CAAA;YACxC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;YACtB,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ,CAAA;YAC3D,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAA;YAC5C,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;YAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAC3E,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAC/D,MAAM,QAAQ,GAAG,GAAG,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;YACzC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACrD,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAA;YAC5K,OAAO;gBACL,qDAAqD,EAAE,oBAAoB,QAAQ,KAAK,EAAE,uMAAuM;gBACjS;oBACE,0EAA0E;oBAC1E,oEAAoE,QAAQ,kBAAkB;oBAC9F,yDAAyD,QAAQ,EAAE;oBACnE,0FAA0F;oBAC1F,oCAAoC;oBACpC,oEAAoE,EAAE,0BAA0B;oBAChG,kGAAkG,QAAQ,8IAA8I,EAAE,2CAA2C;iBACtS,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAA;QACH,CAAC;KACF;IAED,SAAS,EAAE;QACT,cAAc,EAAE;YACd,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;YACvB,IAAI,EAAE,oCAAoC;SAC3C;KACF;IAGD,QAAQ,EAAE;QACR,YAAY,EAAE,QAAQ;QACtB,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YAC7B,IAAI,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;YAC/B,MAAM,OAAO,GAAI,GAAgF;gBAC/F,EAAE,QAAQ,EAAE,OAAO,CAAA;YACrB,IAAI,OAAO,EAAE,MAAM;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;YAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE,CAAA;QAC5E,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;YACtC,MAAM,GAAG,GAAG,SAAS,IAAI,SAAS,CAAA;YAClC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YAC3E,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAA;YAEzE,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;YAC/B,MAAM,QAAQ,GAAwB;gBACpC,KAAK;gBACL,UAAU,EAAE,mBAAmB;gBAC/B,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE;aACzC,CAAA;YACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;YACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;QACzD,CAAC;KACF;CACF,CAAA;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAwC;IACxF,MAAM,GAAG,GAAG,SAAS,IAAI,SAAS,CAAA;IAClC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAA;IAEzE,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;IAC/B,MAAM,QAAQ,GAAwB;QACpC,KAAK;QACL,UAAU,EAAE,mBAAmB;QAC/B,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE;KACzC,CAAA;IACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;IACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AACzD,CAAC;AAED,eAAe,CAAC,GAAsB,EAAE,EAAE;IACxC,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAA;IAC/B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;AACjD,CAAC,CAAA"}
|
package/dist/soothing.d.ts
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
* 12 intent categories, 5 candidates each — one is chosen at random.
|
|
5
5
|
* Principles: convey "I heard you + I'm working on it", no time promises.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Creates a soothing reply picker that guarantees no repeats until all
|
|
9
|
+
* candidates in the matched category are exhausted, then reshuffles.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const picker = createSoothingPicker(text)
|
|
13
|
+
* picker() // first reply (no repeat)
|
|
14
|
+
* picker() // second reply (different from first)
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSoothingPicker(text?: string): () => string;
|
|
7
17
|
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
8
|
-
export declare function pickSoothingReply(text
|
|
18
|
+
export declare function pickSoothingReply(text?: string): string;
|
|
9
19
|
//# sourceMappingURL=soothing.d.ts.map
|
package/dist/soothing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soothing.d.ts","sourceRoot":"","sources":["../src/soothing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"soothing.d.ts","sourceRoot":"","sources":["../src/soothing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoJH;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,MAAM,CAmBhE;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
package/dist/soothing.js
CHANGED
|
@@ -139,16 +139,44 @@ const DEFAULT_CANDIDATES = [
|
|
|
139
139
|
'嗯,给我一点时间想想',
|
|
140
140
|
'让我好好想想...',
|
|
141
141
|
];
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
/** Fisher-Yates shuffle (in-place) */
|
|
143
|
+
function shuffle(arr) {
|
|
144
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
145
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
146
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
147
|
+
}
|
|
148
|
+
return arr;
|
|
144
149
|
}
|
|
145
|
-
/**
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Creates a soothing reply picker that guarantees no repeats until all
|
|
152
|
+
* candidates in the matched category are exhausted, then reshuffles.
|
|
153
|
+
*
|
|
154
|
+
* Usage:
|
|
155
|
+
* const picker = createSoothingPicker(text)
|
|
156
|
+
* picker() // first reply (no repeat)
|
|
157
|
+
* picker() // second reply (different from first)
|
|
158
|
+
*/
|
|
159
|
+
export function createSoothingPicker(text) {
|
|
160
|
+
let candidates;
|
|
161
|
+
if (text) {
|
|
162
|
+
for (const rule of INTENT_RULES) {
|
|
163
|
+
if (rule.pattern.test(text)) {
|
|
164
|
+
candidates = rule.candidates;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
150
167
|
}
|
|
151
168
|
}
|
|
152
|
-
|
|
169
|
+
const pool = candidates ?? DEFAULT_CANDIDATES;
|
|
170
|
+
let queue = [];
|
|
171
|
+
return () => {
|
|
172
|
+
if (queue.length === 0) {
|
|
173
|
+
queue = shuffle([...pool]);
|
|
174
|
+
}
|
|
175
|
+
return queue.pop();
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
179
|
+
export function pickSoothingReply(text) {
|
|
180
|
+
return createSoothingPicker(text)();
|
|
153
181
|
}
|
|
154
182
|
//# sourceMappingURL=soothing.js.map
|
package/dist/soothing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soothing.js","sourceRoot":"","sources":["../src/soothing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,YAAY,GAAqD;IACrE;QACE,SAAS;QACT,OAAO,EAAE,gBAAgB;QACzB,UAAU,EAAE;YACV,uBAAuB;YACvB,oBAAoB;YACpB,sBAAsB;YACtB,kBAAkB;YAClB,aAAa;SACd;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE;YACV,kBAAkB;YAClB,cAAc;YACd,gBAAgB;YAChB,oBAAoB;YACpB,gBAAgB;SACjB;KACF;IACD;QACE,UAAU;QACV,OAAO,EAAE,YAAY;QACrB,UAAU,EAAE;YACV,iBAAiB;YACjB,cAAc;YACd,kBAAkB;YAClB,eAAe;YACf,iBAAiB;SAClB;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,WAAW;QACpB,UAAU,EAAE;YACV,cAAc;YACd,eAAe;YACf,aAAa;YACb,gBAAgB;YAChB,iBAAiB;SAClB;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,cAAc;QACvB,UAAU,EAAE;YACV,aAAa;YACb,cAAc;YACd,gBAAgB;YAChB,aAAa;YACb,YAAY;SACb;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,gBAAgB;QACzB,UAAU,EAAE;YACV,aAAa;YACb,cAAc;YACd,iBAAiB;YACjB,oBAAoB;YACpB,cAAc;SACf;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,mBAAmB;QAC5B,UAAU,EAAE;YACV,eAAe;YACf,aAAa;YACb,iBAAiB;YACjB,cAAc;YACd,YAAY;SACb;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,kBAAkB;QAC3B,UAAU,EAAE;YACV,YAAY;YACZ,aAAa;YACb,WAAW;YACX,cAAc;YACd,UAAU;SACX;KACF;IACD;QACE,aAAa;QACb,OAAO,EAAE,yBAAyB;QAClC,UAAU,EAAE;YACV,eAAe;YACf,eAAe;YACf,gBAAgB;YAChB,eAAe;YACf,gBAAgB;SACjB;KACF;IACD;QACE,aAAa;QACb,OAAO,EAAE,oBAAoB;QAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;KAC9C;IACD;QACE,aAAa;QACb,OAAO,EAAE,mBAAmB;QAC5B,UAAU,EAAE;YACV,aAAa;YACb,WAAW;YACX,YAAY;YACZ,UAAU;YACV,YAAY;SACb;KACF;IACD;QACE,UAAU;QACV,OAAO,EAAE,oBAAoB;QAC7B,UAAU,EAAE;YACV,YAAY;YACZ,UAAU;YACV,UAAU;YACV,YAAY;YACZ,QAAQ;SACT;KACF;CACF,CAAA;AAED,MAAM,kBAAkB,GAAG;IACzB,UAAU;IACV,cAAc;IACd,SAAS;IACT,YAAY;IACZ,WAAW;CACZ,CAAA;AAED,SAAS,
|
|
1
|
+
{"version":3,"file":"soothing.js","sourceRoot":"","sources":["../src/soothing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,YAAY,GAAqD;IACrE;QACE,SAAS;QACT,OAAO,EAAE,gBAAgB;QACzB,UAAU,EAAE;YACV,uBAAuB;YACvB,oBAAoB;YACpB,sBAAsB;YACtB,kBAAkB;YAClB,aAAa;SACd;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,eAAe;QACxB,UAAU,EAAE;YACV,kBAAkB;YAClB,cAAc;YACd,gBAAgB;YAChB,oBAAoB;YACpB,gBAAgB;SACjB;KACF;IACD;QACE,UAAU;QACV,OAAO,EAAE,YAAY;QACrB,UAAU,EAAE;YACV,iBAAiB;YACjB,cAAc;YACd,kBAAkB;YAClB,eAAe;YACf,iBAAiB;SAClB;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,WAAW;QACpB,UAAU,EAAE;YACV,cAAc;YACd,eAAe;YACf,aAAa;YACb,gBAAgB;YAChB,iBAAiB;SAClB;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,cAAc;QACvB,UAAU,EAAE;YACV,aAAa;YACb,cAAc;YACd,gBAAgB;YAChB,aAAa;YACb,YAAY;SACb;KACF;IACD;QACE,SAAS;QACT,OAAO,EAAE,gBAAgB;QACzB,UAAU,EAAE;YACV,aAAa;YACb,cAAc;YACd,iBAAiB;YACjB,oBAAoB;YACpB,cAAc;SACf;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,mBAAmB;QAC5B,UAAU,EAAE;YACV,eAAe;YACf,aAAa;YACb,iBAAiB;YACjB,cAAc;YACd,YAAY;SACb;KACF;IACD;QACE,YAAY;QACZ,OAAO,EAAE,kBAAkB;QAC3B,UAAU,EAAE;YACV,YAAY;YACZ,aAAa;YACb,WAAW;YACX,cAAc;YACd,UAAU;SACX;KACF;IACD;QACE,aAAa;QACb,OAAO,EAAE,yBAAyB;QAClC,UAAU,EAAE;YACV,eAAe;YACf,eAAe;YACf,gBAAgB;YAChB,eAAe;YACf,gBAAgB;SACjB;KACF;IACD;QACE,aAAa;QACb,OAAO,EAAE,oBAAoB;QAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;KAC9C;IACD;QACE,aAAa;QACb,OAAO,EAAE,mBAAmB;QAC5B,UAAU,EAAE;YACV,aAAa;YACb,WAAW;YACX,YAAY;YACZ,UAAU;YACV,YAAY;SACb;KACF;IACD;QACE,UAAU;QACV,OAAO,EAAE,oBAAoB;QAC7B,UAAU,EAAE;YACV,YAAY;YACZ,UAAU;YACV,UAAU;YACV,YAAY;YACZ,QAAQ;SACT;KACF;CACF,CAAA;AAED,MAAM,kBAAkB,GAAG;IACzB,UAAU;IACV,cAAc;IACd,SAAS;IACT,YAAY;IACZ,WAAW;CACZ,CAAA;AAED,sCAAsC;AACtC,SAAS,OAAO,CAAI,GAAQ;IAC1B,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAC5C;QAAA,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,EAAE,GAAG,CAAC,CAAC,CAAE,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,IAAI,UAAgC,CAAA;IACpC,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;gBAC5B,MAAK;YACP,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,IAAI,kBAAkB,CAAA;IAE7C,IAAI,KAAK,GAAa,EAAE,CAAA;IACxB,OAAO,GAAG,EAAE;QACV,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;QAC5B,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,EAAG,CAAA;IACrB,CAAC,CAAA;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,IAAa;IAC7C,OAAO,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAA;AACrC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip Markdown / rich-text formatting for TTS.
|
|
3
|
+
*
|
|
4
|
+
* Designed to be safe for streaming chunks — removes syntax characters
|
|
5
|
+
* individually rather than matching complete open/close patterns,
|
|
6
|
+
* so partial markdown across chunks is handled correctly.
|
|
7
|
+
* Plain text without markdown passes through unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export declare function stripMarkdown(text: string): string;
|
|
10
|
+
//# sourceMappingURL=strip-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-markdown.d.ts","sourceRoot":"","sources":["../src/strip-markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmClD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip Markdown / rich-text formatting for TTS.
|
|
3
|
+
*
|
|
4
|
+
* Designed to be safe for streaming chunks — removes syntax characters
|
|
5
|
+
* individually rather than matching complete open/close patterns,
|
|
6
|
+
* so partial markdown across chunks is handled correctly.
|
|
7
|
+
* Plain text without markdown passes through unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export function stripMarkdown(text) {
|
|
10
|
+
return (text
|
|
11
|
+
// Code blocks (``` ... ```) — remove fences and content when complete
|
|
12
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
13
|
+
// Remaining fences (incomplete code block in a chunk)
|
|
14
|
+
.replace(/```/g, '')
|
|
15
|
+
// Inline code backticks
|
|
16
|
+
.replace(/`/g, '')
|
|
17
|
+
// Images  — keep alt text
|
|
18
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
19
|
+
// Links [text](url) — keep text
|
|
20
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
21
|
+
// Bare URLs (https://...)
|
|
22
|
+
.replace(/https?:\/\/\S+/g, '')
|
|
23
|
+
// Heading markers
|
|
24
|
+
.replace(/^#{1,6}\s*/gm, '')
|
|
25
|
+
// Bold/italic/strikethrough markers (*, _, ~)
|
|
26
|
+
.replace(/[*_~]+/g, '')
|
|
27
|
+
// Blockquote markers
|
|
28
|
+
.replace(/^>\s?/gm, '')
|
|
29
|
+
// Horizontal rules (---, ***, ___)
|
|
30
|
+
.replace(/^[-]{3,}\s*$/gm, '')
|
|
31
|
+
// Unordered list markers (- / + at line start, * already removed above)
|
|
32
|
+
.replace(/^[\s]*[-+]\s+/gm, '')
|
|
33
|
+
// Ordered list markers (1. / 2. ...)
|
|
34
|
+
.replace(/^[\s]*\d+\.\s+/gm, '')
|
|
35
|
+
// HTML tags
|
|
36
|
+
.replace(/<[^>]+>/g, '')
|
|
37
|
+
// Collapse multiple spaces
|
|
38
|
+
.replace(/ {2,}/g, ' ')
|
|
39
|
+
// Collapse multiple blank lines
|
|
40
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
41
|
+
.trim());
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=strip-markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-markdown.js","sourceRoot":"","sources":["../src/strip-markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,CACL,IAAI;QACF,sEAAsE;SACrE,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC/B,sDAAsD;SACrD,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpB,wBAAwB;SACvB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,qCAAqC;SACpC,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC;QACzC,gCAAgC;SAC/B,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC;QACxC,0BAA0B;SACzB,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC/B,kBAAkB;SACjB,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;QAC5B,8CAA8C;SAC7C,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;QACvB,qBAAqB;SACpB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;QACvB,mCAAmC;SAClC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC9B,wEAAwE;SACvE,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC/B,qCAAqC;SACpC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;QAChC,YAAY;SACX,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QACxB,2BAA2B;SAC1B,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;QACvB,gCAAgC;SAC/B,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CACV,CAAA;AACH,CAAC"}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -55,6 +55,11 @@ let gotSoothing = false
|
|
|
55
55
|
let gotReply = false
|
|
56
56
|
let gotFinish = false
|
|
57
57
|
let gotNotification = false
|
|
58
|
+
let soothingCount = 0
|
|
59
|
+
let firstReplyAt = 0
|
|
60
|
+
const soothingTimestamps = []
|
|
61
|
+
const replyTimestamps = []
|
|
62
|
+
const startTime = Date.now()
|
|
58
63
|
|
|
59
64
|
function validateOutboundMessage(msg) {
|
|
60
65
|
if (msg.identifier !== 'chat_output') {
|
|
@@ -90,8 +95,8 @@ function printSummary() {
|
|
|
90
95
|
console.log('='.repeat(60))
|
|
91
96
|
|
|
92
97
|
const checks = [
|
|
93
|
-
['Soothing ack
|
|
94
|
-
['AI reply
|
|
98
|
+
['Soothing ack received', gotSoothing],
|
|
99
|
+
['AI reply received', gotReply],
|
|
95
100
|
['Finish message (is_finished=true)', gotFinish],
|
|
96
101
|
]
|
|
97
102
|
|
|
@@ -99,6 +104,27 @@ function printSummary() {
|
|
|
99
104
|
console.log(` ${ok ? '✅' : '❌'} ${label}`)
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
console.log('\n Soothing stats:')
|
|
108
|
+
console.log(` Total soothing messages: ${soothingCount}`)
|
|
109
|
+
if (soothingTimestamps.length > 0) {
|
|
110
|
+
console.log(` Soothing timestamps: ${soothingTimestamps.map(t => `+${t}ms`).join(', ')}`)
|
|
111
|
+
if (soothingTimestamps.length > 1) {
|
|
112
|
+
const intervals = soothingTimestamps.slice(1).map((t, i) => t - soothingTimestamps[i])
|
|
113
|
+
console.log(` Intervals between soothing: ${intervals.map(t => `${t}ms`).join(', ')}`)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (firstReplyAt) {
|
|
117
|
+
console.log(` First LLM reply at: +${firstReplyAt}ms`)
|
|
118
|
+
const lastSoothing = soothingTimestamps[soothingTimestamps.length - 1] || 0
|
|
119
|
+
if (lastSoothing && firstReplyAt > lastSoothing) {
|
|
120
|
+
console.log(` Gap (last soothing → first reply): ${firstReplyAt - lastSoothing}ms`)
|
|
121
|
+
}
|
|
122
|
+
console.log(` No soothing after first LLM reply: ${soothingTimestamps.every(t => t < firstReplyAt) ? '✅ YES' : '❌ NO'}`)
|
|
123
|
+
}
|
|
124
|
+
if (replyTimestamps.length > 0) {
|
|
125
|
+
console.log(` Total LLM reply chunks: ${replyTimestamps.length}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
102
128
|
if (mode === 'reminder') {
|
|
103
129
|
console.log(` ${gotNotification ? '✅' : 'ℹ️ '} Notification on event/post${gotNotification ? '' : ' (not received within timeout — may arrive later)'}`)
|
|
104
130
|
}
|
|
@@ -157,12 +183,29 @@ client.on('message', (topic, payload) => {
|
|
|
157
183
|
|
|
158
184
|
if (topic === outboundTopic) {
|
|
159
185
|
const p = msg.outParams
|
|
186
|
+
const elapsed = Date.now() - startTime
|
|
160
187
|
const label = p?.is_finished ? 'FINISH' : `REPLY(order=${p?.order})`
|
|
161
|
-
console.log(`\n[OUTBOUND ${label}] ${p?.content || '(empty)'}`)
|
|
188
|
+
console.log(`\n[OUTBOUND ${label}] +${elapsed}ms | ${p?.content || '(empty)'}`)
|
|
162
189
|
validateOutboundMessage(msg)
|
|
163
190
|
|
|
164
|
-
if (p?.
|
|
165
|
-
|
|
191
|
+
if (!p?.is_finished && p?.content) {
|
|
192
|
+
// Detect soothing vs LLM reply: soothing messages arrive before LLM starts
|
|
193
|
+
if (!firstReplyAt) {
|
|
194
|
+
// All messages before the first LLM sentence are soothing
|
|
195
|
+
// We'll determine this retrospectively, for now track all
|
|
196
|
+
}
|
|
197
|
+
// Heuristic: soothing messages are short Chinese phrases ending with ... or ~
|
|
198
|
+
const isSoothing = /[.…~]$/.test(p.content) && p.content.length < 30
|
|
199
|
+
if (isSoothing) {
|
|
200
|
+
soothingCount++
|
|
201
|
+
soothingTimestamps.push(elapsed)
|
|
202
|
+
gotSoothing = true
|
|
203
|
+
} else {
|
|
204
|
+
if (!firstReplyAt) firstReplyAt = elapsed
|
|
205
|
+
replyTimestamps.push(elapsed)
|
|
206
|
+
gotReply = true
|
|
207
|
+
}
|
|
208
|
+
}
|
|
166
209
|
if (p?.is_finished) {
|
|
167
210
|
gotFinish = true
|
|
168
211
|
if (mode === 'chat') {
|
package/src/config.ts
CHANGED
|
@@ -30,13 +30,21 @@ export type FlatChannelConfig = {
|
|
|
30
30
|
mqtt_port?: number
|
|
31
31
|
summary_enabled?: boolean
|
|
32
32
|
summary_max_chars?: number
|
|
33
|
+
sentence_split_enabled?: boolean
|
|
34
|
+
sentence_split_delimiters?: string
|
|
35
|
+
soothing_loop_enabled?: boolean
|
|
36
|
+
soothing_loop_interval_ms?: number
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export const DEFAULT_API_URL = 'https://api.folotoy.cn'
|
|
36
|
-
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.
|
|
40
|
+
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.folotoy.cn'
|
|
37
41
|
export const DEFAULT_MQTT_PORT = 1883
|
|
38
42
|
export const DEFAULT_SUMMARY_ENABLED = true
|
|
39
43
|
export const DEFAULT_SUMMARY_MAX_CHARS = 200
|
|
44
|
+
export const DEFAULT_SENTENCE_SPLIT_ENABLED = true
|
|
45
|
+
export const DEFAULT_SENTENCE_SPLIT_DELIMITERS = '!。?;!.?;~'
|
|
46
|
+
export const DEFAULT_SOOTHING_LOOP_ENABLED = true
|
|
47
|
+
export const DEFAULT_SOOTHING_LOOP_INTERVAL_MS = 3000
|
|
40
48
|
|
|
41
49
|
export function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig {
|
|
42
50
|
const flow = flat.flow ?? 'direct'
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { OpenClawPluginApi, ChannelPlugin, PluginRuntime } from 'openclaw/plugin-sdk/core'
|
|
2
2
|
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic, buildNotificationTopic } from './mqtt.js'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createSoothingPicker } from './soothing.js'
|
|
4
|
+
import { stripMarkdown } from './strip-markdown.js'
|
|
5
|
+
import { DEFAULT_MQTT_HOST, DEFAULT_MQTT_PORT, DEFAULT_SUMMARY_ENABLED, DEFAULT_SUMMARY_MAX_CHARS, DEFAULT_SENTENCE_SPLIT_ENABLED, DEFAULT_SENTENCE_SPLIT_DELIMITERS, DEFAULT_SOOTHING_LOOP_ENABLED, DEFAULT_SOOTHING_LOOP_INTERVAL_MS, flatToPluginConfig } from './config.js'
|
|
5
6
|
import type { FlatChannelConfig } from './config.js'
|
|
6
7
|
import type { MqttClient } from 'mqtt'
|
|
7
8
|
|
|
@@ -54,6 +55,10 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
54
55
|
mqtt_port: { type: 'number', default: DEFAULT_MQTT_PORT },
|
|
55
56
|
summary_enabled: { type: 'boolean', default: DEFAULT_SUMMARY_ENABLED },
|
|
56
57
|
summary_max_chars: { type: 'number', default: DEFAULT_SUMMARY_MAX_CHARS },
|
|
58
|
+
sentence_split_enabled: { type: 'boolean', default: DEFAULT_SENTENCE_SPLIT_ENABLED },
|
|
59
|
+
sentence_split_delimiters: { type: 'string', default: DEFAULT_SENTENCE_SPLIT_DELIMITERS },
|
|
60
|
+
soothing_loop_enabled: { type: 'boolean', default: DEFAULT_SOOTHING_LOOP_ENABLED },
|
|
61
|
+
soothing_loop_interval_ms: { type: 'number', default: DEFAULT_SOOTHING_LOOP_INTERVAL_MS },
|
|
57
62
|
},
|
|
58
63
|
},
|
|
59
64
|
uiHints: {
|
|
@@ -66,6 +71,10 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
66
71
|
mqtt_port: { label: 'MQTT Port' },
|
|
67
72
|
summary_enabled: { label: 'Enable Summary' },
|
|
68
73
|
summary_max_chars: { label: 'Summary Max Characters' },
|
|
74
|
+
sentence_split_enabled: { label: 'Enable Sentence Splitting' },
|
|
75
|
+
sentence_split_delimiters: { label: 'Sentence Delimiters' },
|
|
76
|
+
soothing_loop_enabled: { label: 'Enable Soothing Loop' },
|
|
77
|
+
soothing_loop_interval_ms: { label: 'Soothing Loop Interval (ms)' },
|
|
69
78
|
},
|
|
70
79
|
},
|
|
71
80
|
config: {
|
|
@@ -115,6 +124,10 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
115
124
|
|
|
116
125
|
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED
|
|
117
126
|
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS
|
|
127
|
+
const sentenceSplitEnabled = account.sentence_split_enabled ?? DEFAULT_SENTENCE_SPLIT_ENABLED
|
|
128
|
+
const sentenceSplitDelimiters = account.sentence_split_delimiters ?? DEFAULT_SENTENCE_SPLIT_DELIMITERS
|
|
129
|
+
const soothingLoopEnabled = account.soothing_loop_enabled ?? DEFAULT_SOOTHING_LOOP_ENABLED
|
|
130
|
+
const soothingLoopIntervalMs = account.soothing_loop_interval_ms ?? DEFAULT_SOOTHING_LOOP_INTERVAL_MS
|
|
118
131
|
|
|
119
132
|
client.on('message', (_topic, payload) => {
|
|
120
133
|
let msg: InboundMessage
|
|
@@ -128,12 +141,15 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
128
141
|
const { msgId, inputParams: { text, recording_id } } = msg
|
|
129
142
|
let order = 1
|
|
130
143
|
|
|
144
|
+
// Create a per-message picker that cycles through candidates without repeats
|
|
145
|
+
const soothingPick = createSoothingPicker(text)
|
|
146
|
+
|
|
131
147
|
// Send a quick soothing acknowledgment before AI processing (order=1).
|
|
132
148
|
// AI replies continue from order=2.
|
|
133
149
|
const ackMsg: OutboundMessage = {
|
|
134
150
|
msgId,
|
|
135
151
|
identifier: 'chat_output',
|
|
136
|
-
outParams: { content:
|
|
152
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
137
153
|
}
|
|
138
154
|
client.publish(outboundTopic, JSON.stringify(ackMsg))
|
|
139
155
|
|
|
@@ -160,12 +176,116 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
160
176
|
|
|
161
177
|
// dispatch using dispatchReplyFromConfig (full agent capabilities including tools)
|
|
162
178
|
void (async () => {
|
|
163
|
-
|
|
179
|
+
// Sentence-level streaming: buffer incoming text and flush complete
|
|
180
|
+
// sentences (delimited by punctuation) to the toy immediately.
|
|
181
|
+
const sentenceDelimiterRe = sentenceSplitEnabled
|
|
182
|
+
? new RegExp(`[${sentenceSplitDelimiters.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')}]`)
|
|
183
|
+
: null
|
|
184
|
+
let sentenceBuffer = ''
|
|
185
|
+
let totalSentChars = 0
|
|
186
|
+
let firstSentenceFlushed = false
|
|
187
|
+
let llmStarted = false
|
|
188
|
+
|
|
189
|
+
// Soothing loop: check every 200ms if LLM has started responding.
|
|
190
|
+
// If not, send a soothing reply at a pace matching TTS playback
|
|
191
|
+
// (controlled by soothingLoopIntervalMs). Keeps running until LLM
|
|
192
|
+
// returns its first chunk — no fixed max count.
|
|
193
|
+
let soothingCheckTimer: ReturnType<typeof setInterval> | null = null
|
|
194
|
+
let lastSoothingSentAt = 0
|
|
195
|
+
const stopSoothingLoop = () => {
|
|
196
|
+
if (soothingCheckTimer) { clearInterval(soothingCheckTimer); soothingCheckTimer = null }
|
|
197
|
+
}
|
|
198
|
+
if (soothingLoopEnabled) {
|
|
199
|
+
soothingCheckTimer = setInterval(() => {
|
|
200
|
+
if (llmStarted) {
|
|
201
|
+
stopSoothingLoop()
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
const now = Date.now()
|
|
205
|
+
if (now - lastSoothingSentAt >= soothingLoopIntervalMs) {
|
|
206
|
+
lastSoothingSentAt = now
|
|
207
|
+
order++
|
|
208
|
+
const extraAck: OutboundMessage = {
|
|
209
|
+
msgId,
|
|
210
|
+
identifier: 'chat_output',
|
|
211
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
212
|
+
}
|
|
213
|
+
client.publish(outboundTopic, JSON.stringify(extraAck))
|
|
214
|
+
}
|
|
215
|
+
}, 200)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// First sentence: split purely by punctuation (fast first response).
|
|
219
|
+
// Subsequent sentences: require a minimum length that grows with each
|
|
220
|
+
// sentence (base 20 chars, +10 per sentence), so later chunks are longer.
|
|
221
|
+
let sentenceCount = 0
|
|
222
|
+
const minLenForSentence = () => {
|
|
223
|
+
if (sentenceCount === 0) return 0 // first sentence: no minimum
|
|
224
|
+
return Math.min(100, 20 + (sentenceCount - 1) * 10)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const FORCE_FLUSH_LEN = 200 // force flush if buffer has no delimiter for this many chars
|
|
228
|
+
|
|
229
|
+
const publishSentence = (sentence: string) => {
|
|
230
|
+
sentenceCount++
|
|
231
|
+
if (!firstSentenceFlushed) firstSentenceFlushed = true
|
|
232
|
+
order++
|
|
233
|
+
totalSentChars += sentence.length
|
|
234
|
+
const outMsg: OutboundMessage = {
|
|
235
|
+
msgId,
|
|
236
|
+
identifier: 'chat_output',
|
|
237
|
+
outParams: { content: sentence, recording_id, order, is_finished: false },
|
|
238
|
+
}
|
|
239
|
+
client.publish(outboundTopic, JSON.stringify(outMsg))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const flushSentences = () => {
|
|
243
|
+
if (!sentenceDelimiterRe) return
|
|
244
|
+
while (true) {
|
|
245
|
+
const minLen = minLenForSentence()
|
|
246
|
+
// Search for a delimiter that is at or beyond the minimum length
|
|
247
|
+
let idx = -1
|
|
248
|
+
const searchFrom = Math.max(0, minLen - 1)
|
|
249
|
+
if (searchFrom < sentenceBuffer.length) {
|
|
250
|
+
const sub = sentenceBuffer.slice(searchFrom)
|
|
251
|
+
const match = sub.search(sentenceDelimiterRe)
|
|
252
|
+
if (match !== -1) idx = searchFrom + match
|
|
253
|
+
}
|
|
254
|
+
if (idx !== -1) {
|
|
255
|
+
const sentence = sentenceBuffer.slice(0, idx + 1).trim()
|
|
256
|
+
sentenceBuffer = sentenceBuffer.slice(idx + 1)
|
|
257
|
+
if (sentence) publishSentence(sentence)
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
// No delimiter found — force flush if buffer exceeds limit
|
|
261
|
+
if (sentenceBuffer.length >= FORCE_FLUSH_LEN) {
|
|
262
|
+
// Try to break at the last space/comma for a cleaner cut
|
|
263
|
+
let cutIdx = FORCE_FLUSH_LEN
|
|
264
|
+
const lastSpace = sentenceBuffer.lastIndexOf(' ', FORCE_FLUSH_LEN)
|
|
265
|
+
const lastComma = sentenceBuffer.lastIndexOf(',', FORCE_FLUSH_LEN)
|
|
266
|
+
const lastBreak = Math.max(lastSpace, lastComma)
|
|
267
|
+
if (lastBreak > FORCE_FLUSH_LEN / 2) cutIdx = lastBreak + 1
|
|
268
|
+
const sentence = sentenceBuffer.slice(0, cutIdx).trim()
|
|
269
|
+
sentenceBuffer = sentenceBuffer.slice(cutIdx)
|
|
270
|
+
if (sentence) publishSentence(sentence)
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
273
|
+
break
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
164
277
|
const dispatcher = {
|
|
165
278
|
sendToolResult: () => true,
|
|
166
279
|
sendBlockReply: () => true,
|
|
167
280
|
sendFinalReply: (payload: { text?: string }) => {
|
|
168
|
-
if (payload.text)
|
|
281
|
+
if (payload.text) {
|
|
282
|
+
if (!llmStarted) {
|
|
283
|
+
llmStarted = true
|
|
284
|
+
stopSoothingLoop()
|
|
285
|
+
}
|
|
286
|
+
sentenceBuffer += stripMarkdown(payload.text)
|
|
287
|
+
flushSentences()
|
|
288
|
+
}
|
|
169
289
|
return true
|
|
170
290
|
},
|
|
171
291
|
waitForIdle: async () => {},
|
|
@@ -183,49 +303,51 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
183
303
|
}),
|
|
184
304
|
})
|
|
185
305
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
306
|
+
// Flush remaining buffer (text after the last punctuation)
|
|
307
|
+
let remaining = sentenceBuffer.trim()
|
|
308
|
+
if (remaining) {
|
|
309
|
+
// Summarize remaining text if enabled and total response exceeds threshold
|
|
310
|
+
if (summaryEnabled && subagent && (totalSentChars + remaining.length) > summaryMaxChars) {
|
|
311
|
+
try {
|
|
312
|
+
const summarySessionKey = `folotoy-summary-${accountId}-${Date.now()}`
|
|
313
|
+
const { runId } = await subagent.run({
|
|
314
|
+
sessionKey: summarySessionKey,
|
|
315
|
+
message: [
|
|
316
|
+
`You are an assistant that summarizes texts concisely while keeping the most important information.`,
|
|
317
|
+
`Summarize the text to approximately ${Math.max(50, summaryMaxChars - totalSentChars)} characters.`,
|
|
318
|
+
`Maintain the original tone and style. Reply only with the summary, without additional explanations.`,
|
|
319
|
+
``,
|
|
320
|
+
`<text_to_summarize>`,
|
|
321
|
+
remaining,
|
|
322
|
+
`</text_to_summarize>`,
|
|
323
|
+
].join('\n'),
|
|
324
|
+
deliver: false,
|
|
325
|
+
})
|
|
326
|
+
const result = await subagent.waitForRun({ runId, timeoutMs: 30_000 })
|
|
327
|
+
if (result.status === 'ok') {
|
|
328
|
+
const { messages } = await subagent.getSessionMessages({ sessionKey: summarySessionKey, limit: 1 })
|
|
329
|
+
const lastMsg = messages[messages.length - 1] as { content?: string; text?: string } | undefined
|
|
330
|
+
const summaryText = lastMsg?.content ?? lastMsg?.text
|
|
331
|
+
if (summaryText) remaining = summaryText
|
|
332
|
+
}
|
|
333
|
+
await subagent.deleteSession({ sessionKey: summarySessionKey }).catch(() => {})
|
|
334
|
+
} catch (err) {
|
|
335
|
+
log?.warn?.(`Summary failed, truncating text: ${String(err)}`)
|
|
336
|
+
const maxRemaining = Math.max(50, summaryMaxChars - totalSentChars)
|
|
337
|
+
remaining = `${remaining.slice(0, maxRemaining - 3)}...`
|
|
211
338
|
}
|
|
212
|
-
await subagent.deleteSession({ sessionKey }).catch(() => {})
|
|
213
|
-
} catch (err) {
|
|
214
|
-
log?.warn?.(`Summary failed, truncating text: ${String(err)}`)
|
|
215
|
-
finalText = `${finalText.slice(0, summaryMaxChars - 3)}...`
|
|
216
339
|
}
|
|
217
|
-
}
|
|
218
340
|
|
|
219
|
-
if (finalText) {
|
|
220
341
|
order++
|
|
221
342
|
const outMsg: OutboundMessage = {
|
|
222
343
|
msgId,
|
|
223
344
|
identifier: 'chat_output',
|
|
224
|
-
outParams: { content:
|
|
345
|
+
outParams: { content: remaining, recording_id, order, is_finished: false },
|
|
225
346
|
}
|
|
226
347
|
client.publish(outboundTopic, JSON.stringify(outMsg))
|
|
227
348
|
}
|
|
228
349
|
} finally {
|
|
350
|
+
stopSoothingLoop()
|
|
229
351
|
order++
|
|
230
352
|
const finishMsg: OutboundMessage = {
|
|
231
353
|
msgId,
|
|
@@ -268,7 +390,7 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
268
390
|
const pad = (n: number) => String(n).padStart(2, '0')
|
|
269
391
|
const nowLocal = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${tzSuffix}`
|
|
270
392
|
return [
|
|
271
|
-
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}). Current time: ${nowLocal} (${tz}).`,
|
|
393
|
+
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}). Current time: ${nowLocal} (${tz}). IMPORTANT: Your reply will be converted to speech (TTS). Use plain text only — no markdown, no bullet points, no numbered lists, no code blocks, no URLs. Write in a natural, conversational tone.`,
|
|
272
394
|
[
|
|
273
395
|
`To set reminders/timers, use the cron tool with action="add". IMPORTANT:`,
|
|
274
396
|
`- schedule.at MUST use the same timezone offset as current time (${tzSuffix}), NEVER use "Z"`,
|
|
@@ -309,7 +431,7 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
309
431
|
const notifMsg: NotificationMessage = {
|
|
310
432
|
msgId,
|
|
311
433
|
identifier: 'send_notification',
|
|
312
|
-
outParams: { text },
|
|
434
|
+
outParams: { text: stripMarkdown(text) },
|
|
313
435
|
}
|
|
314
436
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg))
|
|
315
437
|
return { channel: 'folotoy', messageId: String(msgId) }
|
|
@@ -327,7 +449,7 @@ export function sendNotification({ text, accountId }: { text: string; accountId?
|
|
|
327
449
|
const notifMsg: NotificationMessage = {
|
|
328
450
|
msgId,
|
|
329
451
|
identifier: 'send_notification',
|
|
330
|
-
outParams: { text },
|
|
452
|
+
outParams: { text: stripMarkdown(text) },
|
|
331
453
|
}
|
|
332
454
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg))
|
|
333
455
|
return { channel: 'folotoy', messageId: String(msgId) }
|
package/src/soothing.ts
CHANGED
|
@@ -142,16 +142,46 @@ const DEFAULT_CANDIDATES = [
|
|
|
142
142
|
'让我好好想想...',
|
|
143
143
|
]
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
/** Fisher-Yates shuffle (in-place) */
|
|
146
|
+
function shuffle<T>(arr: T[]): T[] {
|
|
147
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
148
|
+
const j = Math.floor(Math.random() * (i + 1))
|
|
149
|
+
;[arr[i], arr[j]] = [arr[j]!, arr[i]!]
|
|
150
|
+
}
|
|
151
|
+
return arr
|
|
147
152
|
}
|
|
148
153
|
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Creates a soothing reply picker that guarantees no repeats until all
|
|
156
|
+
* candidates in the matched category are exhausted, then reshuffles.
|
|
157
|
+
*
|
|
158
|
+
* Usage:
|
|
159
|
+
* const picker = createSoothingPicker(text)
|
|
160
|
+
* picker() // first reply (no repeat)
|
|
161
|
+
* picker() // second reply (different from first)
|
|
162
|
+
*/
|
|
163
|
+
export function createSoothingPicker(text?: string): () => string {
|
|
164
|
+
let candidates: string[] | undefined
|
|
165
|
+
if (text) {
|
|
166
|
+
for (const rule of INTENT_RULES) {
|
|
167
|
+
if (rule.pattern.test(text)) {
|
|
168
|
+
candidates = rule.candidates
|
|
169
|
+
break
|
|
170
|
+
}
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
|
-
|
|
173
|
+
const pool = candidates ?? DEFAULT_CANDIDATES
|
|
174
|
+
|
|
175
|
+
let queue: string[] = []
|
|
176
|
+
return () => {
|
|
177
|
+
if (queue.length === 0) {
|
|
178
|
+
queue = shuffle([...pool])
|
|
179
|
+
}
|
|
180
|
+
return queue.pop()!
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
185
|
+
export function pickSoothingReply(text?: string): string {
|
|
186
|
+
return createSoothingPicker(text)()
|
|
157
187
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip Markdown / rich-text formatting for TTS.
|
|
3
|
+
*
|
|
4
|
+
* Designed to be safe for streaming chunks — removes syntax characters
|
|
5
|
+
* individually rather than matching complete open/close patterns,
|
|
6
|
+
* so partial markdown across chunks is handled correctly.
|
|
7
|
+
* Plain text without markdown passes through unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export function stripMarkdown(text: string): string {
|
|
10
|
+
return (
|
|
11
|
+
text
|
|
12
|
+
// Code blocks (``` ... ```) — remove fences and content when complete
|
|
13
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
14
|
+
// Remaining fences (incomplete code block in a chunk)
|
|
15
|
+
.replace(/```/g, '')
|
|
16
|
+
// Inline code backticks
|
|
17
|
+
.replace(/`/g, '')
|
|
18
|
+
// Images  — keep alt text
|
|
19
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
20
|
+
// Links [text](url) — keep text
|
|
21
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
22
|
+
// Bare URLs (https://...)
|
|
23
|
+
.replace(/https?:\/\/\S+/g, '')
|
|
24
|
+
// Heading markers
|
|
25
|
+
.replace(/^#{1,6}\s*/gm, '')
|
|
26
|
+
// Bold/italic/strikethrough markers (*, _, ~)
|
|
27
|
+
.replace(/[*_~]+/g, '')
|
|
28
|
+
// Blockquote markers
|
|
29
|
+
.replace(/^>\s?/gm, '')
|
|
30
|
+
// Horizontal rules (---, ***, ___)
|
|
31
|
+
.replace(/^[-]{3,}\s*$/gm, '')
|
|
32
|
+
// Unordered list markers (- / + at line start, * already removed above)
|
|
33
|
+
.replace(/^[\s]*[-+]\s+/gm, '')
|
|
34
|
+
// Ordered list markers (1. / 2. ...)
|
|
35
|
+
.replace(/^[\s]*\d+\.\s+/gm, '')
|
|
36
|
+
// HTML tags
|
|
37
|
+
.replace(/<[^>]+>/g, '')
|
|
38
|
+
// Collapse multiple spaces
|
|
39
|
+
.replace(/ {2,}/g, ' ')
|
|
40
|
+
// Collapse multiple blank lines
|
|
41
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
42
|
+
.trim()
|
|
43
|
+
)
|
|
44
|
+
}
|