@folotoy/folotoy-openclaw-plugin 0.5.7 → 0.6.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/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +191 -43
- 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/config.ts +10 -0
- package/src/index.ts +180 -41
- package/src/soothing.ts +38 -8
- package/src/strip-markdown.ts +44 -0
package/dist/config.d.ts
CHANGED
|
@@ -27,11 +27,21 @@ 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;
|
|
34
|
+
soothing_loop_max_count?: number;
|
|
30
35
|
};
|
|
31
36
|
export declare const DEFAULT_API_URL = "https://api.folotoy.cn";
|
|
32
37
|
export declare const DEFAULT_MQTT_HOST: string;
|
|
33
38
|
export declare const DEFAULT_MQTT_PORT = 1883;
|
|
34
39
|
export declare const DEFAULT_SUMMARY_ENABLED = true;
|
|
35
40
|
export declare const DEFAULT_SUMMARY_MAX_CHARS = 200;
|
|
41
|
+
export declare const DEFAULT_SENTENCE_SPLIT_ENABLED = true;
|
|
42
|
+
export declare const DEFAULT_SENTENCE_SPLIT_DELIMITERS = "\uFF01\u3002\uFF1F\uFF1B!.?;~";
|
|
43
|
+
export declare const DEFAULT_SOOTHING_LOOP_ENABLED = true;
|
|
44
|
+
export declare const DEFAULT_SOOTHING_LOOP_INTERVAL_MS = 200;
|
|
45
|
+
export declare const DEFAULT_SOOTHING_LOOP_MAX_COUNT = 3;
|
|
36
46
|
export declare function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig;
|
|
37
47
|
//# 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;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC,CAAA;AAED,eAAO,MAAM,eAAe,2BAA2B,CAAA;AACvD,eAAO,MAAM,iBAAiB,QAAgD,CAAA;AAC9E,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,MAAM,CAAA;AACpD,eAAO,MAAM,+BAA+B,IAAI,CAAA;AAEhD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAaxE"}
|
package/dist/config.js
CHANGED
|
@@ -3,6 +3,11 @@ export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.qrc92.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 = 200;
|
|
10
|
+
export const DEFAULT_SOOTHING_LOOP_MAX_COUNT = 3;
|
|
6
11
|
export function flatToPluginConfig(flat) {
|
|
7
12
|
const flow = flat.flow ?? 'direct';
|
|
8
13
|
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":"AAuCA,MAAM,CAAC,MAAM,eAAe,GAAG,wBAAwB,CAAA;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,YAAY,CAAA;AAC9E,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,GAAG,CAAA;AACpD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAA;AAEhD,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, DEFAULT_SOOTHING_LOOP_MAX_COUNT, 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,11 @@ 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 },
|
|
38
|
+
soothing_loop_max_count: { type: 'number', default: DEFAULT_SOOTHING_LOOP_MAX_COUNT },
|
|
33
39
|
},
|
|
34
40
|
},
|
|
35
41
|
uiHints: {
|
|
@@ -42,6 +48,11 @@ const folotoyChannel = {
|
|
|
42
48
|
mqtt_port: { label: 'MQTT Port' },
|
|
43
49
|
summary_enabled: { label: 'Enable Summary' },
|
|
44
50
|
summary_max_chars: { label: 'Summary Max Characters' },
|
|
51
|
+
sentence_split_enabled: { label: 'Enable Sentence Splitting' },
|
|
52
|
+
sentence_split_delimiters: { label: 'Sentence Delimiters' },
|
|
53
|
+
soothing_loop_enabled: { label: 'Enable Soothing Loop' },
|
|
54
|
+
soothing_loop_interval_ms: { label: 'Soothing Loop Interval (ms)' },
|
|
55
|
+
soothing_loop_max_count: { label: 'Soothing Loop Max Count' },
|
|
45
56
|
},
|
|
46
57
|
},
|
|
47
58
|
config: {
|
|
@@ -86,6 +97,11 @@ const folotoyChannel = {
|
|
|
86
97
|
});
|
|
87
98
|
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED;
|
|
88
99
|
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS;
|
|
100
|
+
const sentenceSplitEnabled = account.sentence_split_enabled ?? DEFAULT_SENTENCE_SPLIT_ENABLED;
|
|
101
|
+
const sentenceSplitDelimiters = account.sentence_split_delimiters ?? DEFAULT_SENTENCE_SPLIT_DELIMITERS;
|
|
102
|
+
const soothingLoopEnabled = account.soothing_loop_enabled ?? DEFAULT_SOOTHING_LOOP_ENABLED;
|
|
103
|
+
const soothingLoopIntervalMs = account.soothing_loop_interval_ms ?? DEFAULT_SOOTHING_LOOP_INTERVAL_MS;
|
|
104
|
+
const soothingLoopMaxCount = account.soothing_loop_max_count ?? DEFAULT_SOOTHING_LOOP_MAX_COUNT;
|
|
89
105
|
client.on('message', (_topic, payload) => {
|
|
90
106
|
let msg;
|
|
91
107
|
try {
|
|
@@ -98,12 +114,14 @@ const folotoyChannel = {
|
|
|
98
114
|
return;
|
|
99
115
|
const { msgId, inputParams: { text, recording_id } } = msg;
|
|
100
116
|
let order = 1;
|
|
117
|
+
// Create a per-message picker that cycles through candidates without repeats
|
|
118
|
+
const soothingPick = createSoothingPicker(text);
|
|
101
119
|
// Send a quick soothing acknowledgment before AI processing (order=1).
|
|
102
120
|
// AI replies continue from order=2.
|
|
103
121
|
const ackMsg = {
|
|
104
122
|
msgId,
|
|
105
123
|
identifier: 'chat_output',
|
|
106
|
-
outParams: { content:
|
|
124
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
107
125
|
};
|
|
108
126
|
client.publish(outboundTopic, JSON.stringify(ackMsg));
|
|
109
127
|
const peer = { kind: 'direct', id: credentials.toy_sn };
|
|
@@ -127,13 +145,120 @@ const folotoyChannel = {
|
|
|
127
145
|
});
|
|
128
146
|
// dispatch using dispatchReplyFromConfig (full agent capabilities including tools)
|
|
129
147
|
void (async () => {
|
|
130
|
-
|
|
148
|
+
// Sentence-level streaming: buffer incoming text and flush complete
|
|
149
|
+
// sentences (delimited by punctuation) to the toy immediately.
|
|
150
|
+
const sentenceDelimiterRe = sentenceSplitEnabled
|
|
151
|
+
? new RegExp(`[${sentenceSplitDelimiters.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')}]`)
|
|
152
|
+
: null;
|
|
153
|
+
let sentenceBuffer = '';
|
|
154
|
+
let totalSentChars = 0;
|
|
155
|
+
let firstSentenceFlushed = false;
|
|
156
|
+
let llmStarted = false;
|
|
157
|
+
// Send soothing replies while waiting for the LLM to start responding.
|
|
158
|
+
// Once any LLM chunk arrives, stop immediately. Max 5 soothing messages.
|
|
159
|
+
let soothingTimer = null;
|
|
160
|
+
let soothingCount = 0;
|
|
161
|
+
const stopSoothingLoop = () => {
|
|
162
|
+
if (soothingTimer) {
|
|
163
|
+
clearTimeout(soothingTimer);
|
|
164
|
+
soothingTimer = null;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const resetSoothingTimer = () => {
|
|
168
|
+
if (!soothingLoopEnabled || llmStarted || soothingCount >= soothingLoopMaxCount)
|
|
169
|
+
return;
|
|
170
|
+
if (soothingTimer)
|
|
171
|
+
clearTimeout(soothingTimer);
|
|
172
|
+
soothingTimer = setTimeout(() => {
|
|
173
|
+
if (llmStarted || soothingCount >= soothingLoopMaxCount)
|
|
174
|
+
return;
|
|
175
|
+
soothingCount++;
|
|
176
|
+
order++;
|
|
177
|
+
const extraAck = {
|
|
178
|
+
msgId,
|
|
179
|
+
identifier: 'chat_output',
|
|
180
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
181
|
+
};
|
|
182
|
+
client.publish(outboundTopic, JSON.stringify(extraAck));
|
|
183
|
+
// Restart timer in case LLM stays silent even longer
|
|
184
|
+
resetSoothingTimer();
|
|
185
|
+
}, soothingLoopIntervalMs);
|
|
186
|
+
};
|
|
187
|
+
resetSoothingTimer();
|
|
188
|
+
// First sentence: split purely by punctuation (fast first response).
|
|
189
|
+
// Subsequent sentences: require a minimum length that grows with each
|
|
190
|
+
// sentence (base 20 chars, +10 per sentence), so later chunks are longer.
|
|
191
|
+
let sentenceCount = 0;
|
|
192
|
+
const minLenForSentence = () => {
|
|
193
|
+
if (sentenceCount === 0)
|
|
194
|
+
return 0; // first sentence: no minimum
|
|
195
|
+
return Math.min(100, 20 + (sentenceCount - 1) * 10);
|
|
196
|
+
};
|
|
197
|
+
const FORCE_FLUSH_LEN = 200; // force flush if buffer has no delimiter for this many chars
|
|
198
|
+
const publishSentence = (sentence) => {
|
|
199
|
+
sentenceCount++;
|
|
200
|
+
if (!firstSentenceFlushed)
|
|
201
|
+
firstSentenceFlushed = true;
|
|
202
|
+
order++;
|
|
203
|
+
totalSentChars += sentence.length;
|
|
204
|
+
const outMsg = {
|
|
205
|
+
msgId,
|
|
206
|
+
identifier: 'chat_output',
|
|
207
|
+
outParams: { content: sentence, recording_id, order, is_finished: false },
|
|
208
|
+
};
|
|
209
|
+
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
210
|
+
};
|
|
211
|
+
const flushSentences = () => {
|
|
212
|
+
if (!sentenceDelimiterRe)
|
|
213
|
+
return;
|
|
214
|
+
while (true) {
|
|
215
|
+
const minLen = minLenForSentence();
|
|
216
|
+
// Search for a delimiter that is at or beyond the minimum length
|
|
217
|
+
let idx = -1;
|
|
218
|
+
const searchFrom = Math.max(0, minLen - 1);
|
|
219
|
+
if (searchFrom < sentenceBuffer.length) {
|
|
220
|
+
const sub = sentenceBuffer.slice(searchFrom);
|
|
221
|
+
const match = sub.search(sentenceDelimiterRe);
|
|
222
|
+
if (match !== -1)
|
|
223
|
+
idx = searchFrom + match;
|
|
224
|
+
}
|
|
225
|
+
if (idx !== -1) {
|
|
226
|
+
const sentence = sentenceBuffer.slice(0, idx + 1).trim();
|
|
227
|
+
sentenceBuffer = sentenceBuffer.slice(idx + 1);
|
|
228
|
+
if (sentence)
|
|
229
|
+
publishSentence(sentence);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
// No delimiter found — force flush if buffer exceeds limit
|
|
233
|
+
if (sentenceBuffer.length >= FORCE_FLUSH_LEN) {
|
|
234
|
+
// Try to break at the last space/comma for a cleaner cut
|
|
235
|
+
let cutIdx = FORCE_FLUSH_LEN;
|
|
236
|
+
const lastSpace = sentenceBuffer.lastIndexOf(' ', FORCE_FLUSH_LEN);
|
|
237
|
+
const lastComma = sentenceBuffer.lastIndexOf(',', FORCE_FLUSH_LEN);
|
|
238
|
+
const lastBreak = Math.max(lastSpace, lastComma);
|
|
239
|
+
if (lastBreak > FORCE_FLUSH_LEN / 2)
|
|
240
|
+
cutIdx = lastBreak + 1;
|
|
241
|
+
const sentence = sentenceBuffer.slice(0, cutIdx).trim();
|
|
242
|
+
sentenceBuffer = sentenceBuffer.slice(cutIdx);
|
|
243
|
+
if (sentence)
|
|
244
|
+
publishSentence(sentence);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
131
250
|
const dispatcher = {
|
|
132
251
|
sendToolResult: () => true,
|
|
133
252
|
sendBlockReply: () => true,
|
|
134
253
|
sendFinalReply: (payload) => {
|
|
135
|
-
if (payload.text)
|
|
136
|
-
|
|
254
|
+
if (payload.text) {
|
|
255
|
+
if (!llmStarted) {
|
|
256
|
+
llmStarted = true;
|
|
257
|
+
stopSoothingLoop();
|
|
258
|
+
}
|
|
259
|
+
sentenceBuffer += stripMarkdown(payload.text);
|
|
260
|
+
flushSentences();
|
|
261
|
+
}
|
|
137
262
|
return true;
|
|
138
263
|
},
|
|
139
264
|
waitForIdle: async () => { },
|
|
@@ -149,50 +274,56 @@ const folotoyChannel = {
|
|
|
149
274
|
dispatcher,
|
|
150
275
|
}),
|
|
151
276
|
});
|
|
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
|
-
|
|
277
|
+
// Flush remaining buffer (text after the last punctuation)
|
|
278
|
+
let remaining = sentenceBuffer.trim();
|
|
279
|
+
if (remaining) {
|
|
280
|
+
// Summarize remaining text if enabled and total response exceeds threshold
|
|
281
|
+
if (summaryEnabled && subagent && (totalSentChars + remaining.length) > summaryMaxChars) {
|
|
282
|
+
try {
|
|
283
|
+
const summarySessionKey = `folotoy-summary-${accountId}-${Date.now()}`;
|
|
284
|
+
const { runId } = await subagent.run({
|
|
285
|
+
sessionKey: summarySessionKey,
|
|
286
|
+
message: [
|
|
287
|
+
`You are an assistant that summarizes texts concisely while keeping the most important information.`,
|
|
288
|
+
`Summarize the text to approximately ${Math.max(50, summaryMaxChars - totalSentChars)} characters.`,
|
|
289
|
+
`Maintain the original tone and style. Reply only with the summary, without additional explanations.`,
|
|
290
|
+
``,
|
|
291
|
+
`<text_to_summarize>`,
|
|
292
|
+
remaining,
|
|
293
|
+
`</text_to_summarize>`,
|
|
294
|
+
].join('\n'),
|
|
295
|
+
deliver: false,
|
|
296
|
+
});
|
|
297
|
+
const result = await subagent.waitForRun({ runId, timeoutMs: 30_000 });
|
|
298
|
+
if (result.status === 'ok') {
|
|
299
|
+
const { messages } = await subagent.getSessionMessages({ sessionKey: summarySessionKey, limit: 1 });
|
|
300
|
+
const lastMsg = messages[messages.length - 1];
|
|
301
|
+
const summaryText = lastMsg?.content ?? lastMsg?.text;
|
|
302
|
+
if (summaryText)
|
|
303
|
+
remaining = summaryText;
|
|
304
|
+
}
|
|
305
|
+
await subagent.deleteSession({ sessionKey: summarySessionKey }).catch(() => { });
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
log?.warn?.(`Summary failed, truncating text: ${String(err)}`);
|
|
309
|
+
const maxRemaining = Math.max(50, summaryMaxChars - totalSentChars);
|
|
310
|
+
remaining = `${remaining.slice(0, maxRemaining - 3)}...`;
|
|
177
311
|
}
|
|
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
312
|
}
|
|
184
|
-
}
|
|
185
|
-
if (finalText) {
|
|
186
313
|
order++;
|
|
187
314
|
const outMsg = {
|
|
188
315
|
msgId,
|
|
189
316
|
identifier: 'chat_output',
|
|
190
|
-
outParams: { content:
|
|
317
|
+
outParams: { content: remaining, recording_id, order, is_finished: false },
|
|
191
318
|
};
|
|
192
319
|
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
193
320
|
}
|
|
194
321
|
}
|
|
195
322
|
finally {
|
|
323
|
+
if (soothingTimer) {
|
|
324
|
+
clearTimeout(soothingTimer);
|
|
325
|
+
soothingTimer = null;
|
|
326
|
+
}
|
|
196
327
|
order++;
|
|
197
328
|
const finishMsg = {
|
|
198
329
|
msgId,
|
|
@@ -222,9 +353,26 @@ const folotoyChannel = {
|
|
|
222
353
|
const folotoy = cfg
|
|
223
354
|
?.channels?.folotoy;
|
|
224
355
|
const sn = folotoy?.toy_sn ?? '<toy_sn>';
|
|
356
|
+
const now = new Date();
|
|
357
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
358
|
+
const tzOffsetMin = -now.getTimezoneOffset();
|
|
359
|
+
const tzSign = tzOffsetMin >= 0 ? '+' : '-';
|
|
360
|
+
const tzH = String(Math.floor(Math.abs(tzOffsetMin) / 60)).padStart(2, '0');
|
|
361
|
+
const tzM = String(Math.abs(tzOffsetMin) % 60).padStart(2, '0');
|
|
362
|
+
const tzSuffix = `${tzSign}${tzH}:${tzM}`;
|
|
363
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
364
|
+
const nowLocal = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${tzSuffix}`;
|
|
225
365
|
return [
|
|
226
|
-
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}).`,
|
|
227
|
-
|
|
366
|
+
`[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.`,
|
|
367
|
+
[
|
|
368
|
+
`To set reminders/timers, use the cron tool with action="add". IMPORTANT:`,
|
|
369
|
+
`- schedule.at MUST use the same timezone offset as current time (${tzSuffix}), NEVER use "Z"`,
|
|
370
|
+
`- For "N分钟后" reminders, add N minutes to current time ${nowLocal}`,
|
|
371
|
+
`- payload.kind MUST be "systemEvent" with a "text" field containing the reminder message`,
|
|
372
|
+
`- sessionTarget MUST be "isolated"`,
|
|
373
|
+
`- delivery MUST be: {"mode":"announce","channel":"folotoy","to":"${sn}","accountId":"default"}`,
|
|
374
|
+
`Example: {"action":"add","job":{"name":"喝水提醒","schedule":{"kind":"at","at":"2026-03-27T21:03:00${tzSuffix}"},"payload":{"kind":"systemEvent","text":"时间到啦,该喝水了!"},"sessionTarget":"isolated","delivery":{"mode":"announce","channel":"folotoy","to":"${sn}","accountId":"default"},"enabled":true}}`,
|
|
375
|
+
].join('\n'),
|
|
228
376
|
];
|
|
229
377
|
},
|
|
230
378
|
},
|
|
@@ -255,7 +403,7 @@ const folotoyChannel = {
|
|
|
255
403
|
const notifMsg = {
|
|
256
404
|
msgId,
|
|
257
405
|
identifier: 'send_notification',
|
|
258
|
-
outParams: { text },
|
|
406
|
+
outParams: { text: stripMarkdown(text) },
|
|
259
407
|
};
|
|
260
408
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg));
|
|
261
409
|
return { channel: 'folotoy', messageId: String(msgId) };
|
|
@@ -272,7 +420,7 @@ export function sendNotification({ text, accountId }) {
|
|
|
272
420
|
const notifMsg = {
|
|
273
421
|
msgId,
|
|
274
422
|
identifier: 'send_notification',
|
|
275
|
-
outParams: { text },
|
|
423
|
+
outParams: { text: stripMarkdown(text) },
|
|
276
424
|
};
|
|
277
425
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg));
|
|
278
426
|
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,OAAO;gBACL,qDAAqD,EAAE,IAAI;gBAC3D,2KAA2K,EAAE,kCAAkC;aAChN,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,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAsBhT,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;gBACzF,uBAAuB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,+BAA+B,EAAE;aACtF;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;YACnE,uBAAuB,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE;SAC9D;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;YACrG,MAAM,oBAAoB,GAAG,OAAO,CAAC,uBAAuB,IAAI,+BAA+B,CAAA;YAE/F,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,uEAAuE;oBACvE,yEAAyE;oBACzE,IAAI,aAAa,GAAyC,IAAI,CAAA;oBAC9D,IAAI,aAAa,GAAG,CAAC,CAAA;oBACrB,MAAM,gBAAgB,GAAG,GAAG,EAAE;wBAC5B,IAAI,aAAa,EAAE,CAAC;4BAAC,YAAY,CAAC,aAAa,CAAC,CAAC;4BAAC,aAAa,GAAG,IAAI,CAAA;wBAAC,CAAC;oBAC1E,CAAC,CAAA;oBACD,MAAM,kBAAkB,GAAG,GAAG,EAAE;wBAC9B,IAAI,CAAC,mBAAmB,IAAI,UAAU,IAAI,aAAa,IAAI,oBAAoB;4BAAE,OAAM;wBACvF,IAAI,aAAa;4BAAE,YAAY,CAAC,aAAa,CAAC,CAAA;wBAC9C,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;4BAC9B,IAAI,UAAU,IAAI,aAAa,IAAI,oBAAoB;gCAAE,OAAM;4BAC/D,aAAa,EAAE,CAAA;4BACf,KAAK,EAAE,CAAA;4BACP,MAAM,QAAQ,GAAoB;gCAChC,KAAK;gCACL,UAAU,EAAE,aAAa;gCACzB,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;6BAChF,CAAA;4BACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;4BACvD,qDAAqD;4BACrD,kBAAkB,EAAE,CAAA;wBACtB,CAAC,EAAE,sBAAsB,CAAC,CAAA;oBAC5B,CAAC,CAAA;oBACD,kBAAkB,EAAE,CAAA;oBAEpB,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,IAAI,aAAa,EAAE,CAAC;4BAAC,YAAY,CAAC,aAAa,CAAC,CAAC;4BAAC,aAAa,GAAG,IAAI,CAAA;wBAAC,CAAC;wBACxE,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":"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"}
|
|
@@ -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
package/src/config.ts
CHANGED
|
@@ -30,6 +30,11 @@ 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
|
|
37
|
+
soothing_loop_max_count?: number
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
export const DEFAULT_API_URL = 'https://api.folotoy.cn'
|
|
@@ -37,6 +42,11 @@ export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.qrc92.cn'
|
|
|
37
42
|
export const DEFAULT_MQTT_PORT = 1883
|
|
38
43
|
export const DEFAULT_SUMMARY_ENABLED = true
|
|
39
44
|
export const DEFAULT_SUMMARY_MAX_CHARS = 200
|
|
45
|
+
export const DEFAULT_SENTENCE_SPLIT_ENABLED = true
|
|
46
|
+
export const DEFAULT_SENTENCE_SPLIT_DELIMITERS = '!。?;!.?;~'
|
|
47
|
+
export const DEFAULT_SOOTHING_LOOP_ENABLED = true
|
|
48
|
+
export const DEFAULT_SOOTHING_LOOP_INTERVAL_MS = 200
|
|
49
|
+
export const DEFAULT_SOOTHING_LOOP_MAX_COUNT = 3
|
|
40
50
|
|
|
41
51
|
export function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig {
|
|
42
52
|
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, DEFAULT_SOOTHING_LOOP_MAX_COUNT, flatToPluginConfig } from './config.js'
|
|
5
6
|
import type { FlatChannelConfig } from './config.js'
|
|
6
7
|
import type { MqttClient } from 'mqtt'
|
|
7
8
|
|
|
@@ -54,6 +55,11 @@ 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 },
|
|
62
|
+
soothing_loop_max_count: { type: 'number', default: DEFAULT_SOOTHING_LOOP_MAX_COUNT },
|
|
57
63
|
},
|
|
58
64
|
},
|
|
59
65
|
uiHints: {
|
|
@@ -66,6 +72,11 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
66
72
|
mqtt_port: { label: 'MQTT Port' },
|
|
67
73
|
summary_enabled: { label: 'Enable Summary' },
|
|
68
74
|
summary_max_chars: { label: 'Summary Max Characters' },
|
|
75
|
+
sentence_split_enabled: { label: 'Enable Sentence Splitting' },
|
|
76
|
+
sentence_split_delimiters: { label: 'Sentence Delimiters' },
|
|
77
|
+
soothing_loop_enabled: { label: 'Enable Soothing Loop' },
|
|
78
|
+
soothing_loop_interval_ms: { label: 'Soothing Loop Interval (ms)' },
|
|
79
|
+
soothing_loop_max_count: { label: 'Soothing Loop Max Count' },
|
|
69
80
|
},
|
|
70
81
|
},
|
|
71
82
|
config: {
|
|
@@ -115,6 +126,11 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
115
126
|
|
|
116
127
|
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED
|
|
117
128
|
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS
|
|
129
|
+
const sentenceSplitEnabled = account.sentence_split_enabled ?? DEFAULT_SENTENCE_SPLIT_ENABLED
|
|
130
|
+
const sentenceSplitDelimiters = account.sentence_split_delimiters ?? DEFAULT_SENTENCE_SPLIT_DELIMITERS
|
|
131
|
+
const soothingLoopEnabled = account.soothing_loop_enabled ?? DEFAULT_SOOTHING_LOOP_ENABLED
|
|
132
|
+
const soothingLoopIntervalMs = account.soothing_loop_interval_ms ?? DEFAULT_SOOTHING_LOOP_INTERVAL_MS
|
|
133
|
+
const soothingLoopMaxCount = account.soothing_loop_max_count ?? DEFAULT_SOOTHING_LOOP_MAX_COUNT
|
|
118
134
|
|
|
119
135
|
client.on('message', (_topic, payload) => {
|
|
120
136
|
let msg: InboundMessage
|
|
@@ -128,12 +144,15 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
128
144
|
const { msgId, inputParams: { text, recording_id } } = msg
|
|
129
145
|
let order = 1
|
|
130
146
|
|
|
147
|
+
// Create a per-message picker that cycles through candidates without repeats
|
|
148
|
+
const soothingPick = createSoothingPicker(text)
|
|
149
|
+
|
|
131
150
|
// Send a quick soothing acknowledgment before AI processing (order=1).
|
|
132
151
|
// AI replies continue from order=2.
|
|
133
152
|
const ackMsg: OutboundMessage = {
|
|
134
153
|
msgId,
|
|
135
154
|
identifier: 'chat_output',
|
|
136
|
-
outParams: { content:
|
|
155
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
137
156
|
}
|
|
138
157
|
client.publish(outboundTopic, JSON.stringify(ackMsg))
|
|
139
158
|
|
|
@@ -160,12 +179,113 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
160
179
|
|
|
161
180
|
// dispatch using dispatchReplyFromConfig (full agent capabilities including tools)
|
|
162
181
|
void (async () => {
|
|
163
|
-
|
|
182
|
+
// Sentence-level streaming: buffer incoming text and flush complete
|
|
183
|
+
// sentences (delimited by punctuation) to the toy immediately.
|
|
184
|
+
const sentenceDelimiterRe = sentenceSplitEnabled
|
|
185
|
+
? new RegExp(`[${sentenceSplitDelimiters.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')}]`)
|
|
186
|
+
: null
|
|
187
|
+
let sentenceBuffer = ''
|
|
188
|
+
let totalSentChars = 0
|
|
189
|
+
let firstSentenceFlushed = false
|
|
190
|
+
let llmStarted = false
|
|
191
|
+
|
|
192
|
+
// Send soothing replies while waiting for the LLM to start responding.
|
|
193
|
+
// Once any LLM chunk arrives, stop immediately. Max 5 soothing messages.
|
|
194
|
+
let soothingTimer: ReturnType<typeof setTimeout> | null = null
|
|
195
|
+
let soothingCount = 0
|
|
196
|
+
const stopSoothingLoop = () => {
|
|
197
|
+
if (soothingTimer) { clearTimeout(soothingTimer); soothingTimer = null }
|
|
198
|
+
}
|
|
199
|
+
const resetSoothingTimer = () => {
|
|
200
|
+
if (!soothingLoopEnabled || llmStarted || soothingCount >= soothingLoopMaxCount) return
|
|
201
|
+
if (soothingTimer) clearTimeout(soothingTimer)
|
|
202
|
+
soothingTimer = setTimeout(() => {
|
|
203
|
+
if (llmStarted || soothingCount >= soothingLoopMaxCount) return
|
|
204
|
+
soothingCount++
|
|
205
|
+
order++
|
|
206
|
+
const extraAck: OutboundMessage = {
|
|
207
|
+
msgId,
|
|
208
|
+
identifier: 'chat_output',
|
|
209
|
+
outParams: { content: soothingPick(), recording_id, order, is_finished: false },
|
|
210
|
+
}
|
|
211
|
+
client.publish(outboundTopic, JSON.stringify(extraAck))
|
|
212
|
+
// Restart timer in case LLM stays silent even longer
|
|
213
|
+
resetSoothingTimer()
|
|
214
|
+
}, soothingLoopIntervalMs)
|
|
215
|
+
}
|
|
216
|
+
resetSoothingTimer()
|
|
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
|
+
if (soothingTimer) { clearTimeout(soothingTimer); soothingTimer = null }
|
|
229
351
|
order++
|
|
230
352
|
const finishMsg: OutboundMessage = {
|
|
231
353
|
msgId,
|
|
@@ -258,9 +380,26 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
258
380
|
const folotoy = (cfg as Record<string, unknown> & { channels?: { folotoy?: FlatChannelConfig } })
|
|
259
381
|
?.channels?.folotoy
|
|
260
382
|
const sn = folotoy?.toy_sn ?? '<toy_sn>'
|
|
383
|
+
const now = new Date()
|
|
384
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
385
|
+
const tzOffsetMin = -now.getTimezoneOffset()
|
|
386
|
+
const tzSign = tzOffsetMin >= 0 ? '+' : '-'
|
|
387
|
+
const tzH = String(Math.floor(Math.abs(tzOffsetMin) / 60)).padStart(2, '0')
|
|
388
|
+
const tzM = String(Math.abs(tzOffsetMin) % 60).padStart(2, '0')
|
|
389
|
+
const tzSuffix = `${tzSign}${tzH}:${tzM}`
|
|
390
|
+
const pad = (n: number) => String(n).padStart(2, '0')
|
|
391
|
+
const nowLocal = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${tzSuffix}`
|
|
261
392
|
return [
|
|
262
|
-
`[FoloToy] This message is from a FoloToy toy (SN: ${sn}).`,
|
|
263
|
-
|
|
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.`,
|
|
394
|
+
[
|
|
395
|
+
`To set reminders/timers, use the cron tool with action="add". IMPORTANT:`,
|
|
396
|
+
`- schedule.at MUST use the same timezone offset as current time (${tzSuffix}), NEVER use "Z"`,
|
|
397
|
+
`- For "N分钟后" reminders, add N minutes to current time ${nowLocal}`,
|
|
398
|
+
`- payload.kind MUST be "systemEvent" with a "text" field containing the reminder message`,
|
|
399
|
+
`- sessionTarget MUST be "isolated"`,
|
|
400
|
+
`- delivery MUST be: {"mode":"announce","channel":"folotoy","to":"${sn}","accountId":"default"}`,
|
|
401
|
+
`Example: {"action":"add","job":{"name":"喝水提醒","schedule":{"kind":"at","at":"2026-03-27T21:03:00${tzSuffix}"},"payload":{"kind":"systemEvent","text":"时间到啦,该喝水了!"},"sessionTarget":"isolated","delivery":{"mode":"announce","channel":"folotoy","to":"${sn}","accountId":"default"},"enabled":true}}`,
|
|
402
|
+
].join('\n'),
|
|
264
403
|
]
|
|
265
404
|
},
|
|
266
405
|
},
|
|
@@ -292,7 +431,7 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
292
431
|
const notifMsg: NotificationMessage = {
|
|
293
432
|
msgId,
|
|
294
433
|
identifier: 'send_notification',
|
|
295
|
-
outParams: { text },
|
|
434
|
+
outParams: { text: stripMarkdown(text) },
|
|
296
435
|
}
|
|
297
436
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg))
|
|
298
437
|
return { channel: 'folotoy', messageId: String(msgId) }
|
|
@@ -310,7 +449,7 @@ export function sendNotification({ text, accountId }: { text: string; accountId?
|
|
|
310
449
|
const notifMsg: NotificationMessage = {
|
|
311
450
|
msgId,
|
|
312
451
|
identifier: 'send_notification',
|
|
313
|
-
outParams: { text },
|
|
452
|
+
outParams: { text: stripMarkdown(text) },
|
|
314
453
|
}
|
|
315
454
|
entry.client.publish(notificationTopic, JSON.stringify(notifMsg))
|
|
316
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
|
+
}
|