@folotoy/folotoy-openclaw-plugin 0.4.1 → 0.5.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/README.md +28 -7
- package/dist/cli/install.js +12 -2
- package/dist/cli/install.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -30
- package/dist/index.js.map +1 -1
- package/dist/mqtt.d.ts.map +1 -1
- package/dist/mqtt.js +14 -2
- package/dist/mqtt.js.map +1 -1
- package/dist/soothing.d.ts +9 -0
- package/dist/soothing.d.ts.map +1 -0
- package/dist/soothing.js +154 -0
- package/dist/soothing.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/soothing.test.ts +47 -0
- package/src/cli/install.ts +13 -2
- package/src/config.ts +4 -0
- package/src/index.ts +60 -34
- package/src/mqtt.ts +17 -2
- package/src/soothing.ts +157 -0
package/README.md
CHANGED
|
@@ -33,6 +33,8 @@ The plugin supports two authentication flows. All fields are configured as flat
|
|
|
33
33
|
| `toy_key` | Toy key (used as MQTT password) |
|
|
34
34
|
| `mqtt_host` | MQTT broker host (default: `198.19.249.25`) |
|
|
35
35
|
| `mqtt_port` | MQTT broker port (default: `1883`) |
|
|
36
|
+
| `summary_enabled` | Enable reply summarization (default: `true`) |
|
|
37
|
+
| `summary_max_chars` | Character threshold for summarization (default: `200`) |
|
|
36
38
|
|
|
37
39
|
### Flow 1: HTTP API Login
|
|
38
40
|
|
|
@@ -56,7 +58,9 @@ Example `openclaw.json`:
|
|
|
56
58
|
"flow": "direct",
|
|
57
59
|
"toy_sn": "your-toy-sn",
|
|
58
60
|
"toy_key": "your-toy-key",
|
|
59
|
-
"mqtt_host": "198.19.249.25"
|
|
61
|
+
"mqtt_host": "198.19.249.25",
|
|
62
|
+
"summary_enabled": true,
|
|
63
|
+
"summary_max_chars": 200
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
}
|
|
@@ -71,13 +75,16 @@ Inbound (Toy → Plugin): /openapi/folotoy/{sn}/thing/command/call
|
|
|
71
75
|
Outbound (Plugin → Toy): /openapi/folotoy/{sn}/thing/command/callAck
|
|
72
76
|
```
|
|
73
77
|
|
|
74
|
-
The plugin connects with an `openapi:` prefix on the
|
|
78
|
+
The plugin connects with an `openapi:` prefix on the `clientId` to distinguish itself from the toy's own connection:
|
|
75
79
|
|
|
76
80
|
```
|
|
77
|
-
|
|
81
|
+
clientId: openapi:{toy_sn}
|
|
82
|
+
username: {toy_sn}
|
|
78
83
|
password: {toy_key}
|
|
79
84
|
```
|
|
80
85
|
|
|
86
|
+
Connection failures trigger exponential backoff reconnection (1s → 2s → 4s → ... → 60s max), resetting on successful connect.
|
|
87
|
+
|
|
81
88
|
## Message Format
|
|
82
89
|
|
|
83
90
|
**Toy → Plugin (inbound)**
|
|
@@ -95,7 +102,7 @@ password: {toy_key}
|
|
|
95
102
|
|
|
96
103
|
**Plugin → Toy (outbound)**
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
Reply is buffered, then summarized if it exceeds `summary_max_chars`. Sent as a single message with auto-incrementing `order`, followed by a finish message:
|
|
99
106
|
|
|
100
107
|
```json
|
|
101
108
|
{
|
|
@@ -137,12 +144,26 @@ Finish message (`is_finished: true`, empty content):
|
|
|
137
144
|
|
|
138
145
|
Switch environments via the `FOLOTOY_MQTT_HOST` environment variable or `mqtt_host` config field.
|
|
139
146
|
|
|
147
|
+
## Features
|
|
148
|
+
|
|
149
|
+
### Reply Summarization
|
|
150
|
+
|
|
151
|
+
When AI reply exceeds `summary_max_chars` (default 200), the plugin uses the primary model to generate a concise summary before sending to the toy. This avoids excessively long voice playback. Falls back to truncation on failure. Disable with `summary_enabled: false`.
|
|
152
|
+
|
|
153
|
+
### Soothing Acknowledgment
|
|
154
|
+
|
|
155
|
+
Immediately sends a transitional reply (e.g., "Let me think...") upon receiving a message, providing instant feedback while the AI processes.
|
|
156
|
+
|
|
157
|
+
### Exponential Backoff Reconnection
|
|
158
|
+
|
|
159
|
+
MQTT connection failures trigger automatic reconnection with exponential backoff (1s → 60s cap), resetting on success.
|
|
160
|
+
|
|
140
161
|
## Development
|
|
141
162
|
|
|
142
163
|
```bash
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
164
|
+
pnpm install
|
|
165
|
+
pnpm test
|
|
166
|
+
pnpm build
|
|
146
167
|
```
|
|
147
168
|
|
|
148
169
|
## License
|
package/dist/cli/install.js
CHANGED
|
@@ -65,6 +65,14 @@ async function pollSession(sessionId) {
|
|
|
65
65
|
}
|
|
66
66
|
throw new Error('Pairing timed out after 5 minutes.');
|
|
67
67
|
}
|
|
68
|
+
function restartGateway() {
|
|
69
|
+
try {
|
|
70
|
+
execSync('openclaw gateway restart', { stdio: 'inherit' });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
console.warn('⚠ Failed to restart gateway. You can restart manually: openclaw gateway restart');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
68
76
|
function writeConfig(result) {
|
|
69
77
|
execSync(`openclaw config set channels.folotoy.flow direct`, { stdio: 'pipe' });
|
|
70
78
|
execSync(`openclaw config set channels.folotoy.toy_sn ${result.toy_sn}`, { stdio: 'pipe' });
|
|
@@ -100,11 +108,13 @@ async function main() {
|
|
|
100
108
|
// Step 6: write config
|
|
101
109
|
console.log('\nWriting configuration...');
|
|
102
110
|
writeConfig(result);
|
|
103
|
-
// Step 7:
|
|
111
|
+
// Step 7: restart gateway
|
|
112
|
+
console.log('\nRestarting gateway...');
|
|
113
|
+
restartGateway();
|
|
114
|
+
// Step 8: done
|
|
104
115
|
console.log('\n\x1b[32m✓ FoloToy plugin installed and configured!\x1b[0m');
|
|
105
116
|
console.log(` Toy SN: ${result.toy_sn}`);
|
|
106
117
|
console.log(` MQTT Host: ${result.mqtt_host ?? DEFAULT_MQTT_HOST}`);
|
|
107
|
-
console.log('\nRestart the gateway to apply: openclaw gateway start --force');
|
|
108
118
|
}
|
|
109
119
|
main().catch((err) => {
|
|
110
120
|
console.error(`\n\x1b[31mError:\x1b[0m ${err instanceof Error ? err.message : String(err)}`);
|
package/dist/cli/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -25,9 +25,13 @@ export type FlatChannelConfig = {
|
|
|
25
25
|
api_key?: string;
|
|
26
26
|
mqtt_host?: string;
|
|
27
27
|
mqtt_port?: number;
|
|
28
|
+
summary_enabled?: boolean;
|
|
29
|
+
summary_max_chars?: number;
|
|
28
30
|
};
|
|
29
31
|
export declare const DEFAULT_API_URL = "https://api.folotoy.cn";
|
|
30
32
|
export declare const DEFAULT_MQTT_HOST: string;
|
|
31
33
|
export declare const DEFAULT_MQTT_PORT = 1883;
|
|
34
|
+
export declare const DEFAULT_SUMMARY_ENABLED = true;
|
|
35
|
+
export declare const DEFAULT_SUMMARY_MAX_CHARS = 200;
|
|
32
36
|
export declare function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig;
|
|
33
37
|
//# 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;
|
|
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;CAC3B,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;AAE5C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAaxE"}
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const DEFAULT_API_URL = 'https://api.folotoy.cn';
|
|
2
2
|
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.qrc92.cn';
|
|
3
3
|
export const DEFAULT_MQTT_PORT = 1883;
|
|
4
|
+
export const DEFAULT_SUMMARY_ENABLED = true;
|
|
5
|
+
export const DEFAULT_SUMMARY_MAX_CHARS = 200;
|
|
4
6
|
export function flatToPluginConfig(flat) {
|
|
5
7
|
const flow = flat.flow ?? 'direct';
|
|
6
8
|
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":"AAkCA,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;AAE5C,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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAA;AAyP/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,28 +1,10 @@
|
|
|
1
1
|
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic, buildNotificationTopic } from './mqtt.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
function pickSoothingReply(text) {
|
|
5
|
-
const t = text.toLowerCase();
|
|
6
|
-
if (/难过|伤心|哭|不开心|sad|upset|cry/.test(t))
|
|
7
|
-
return '抱抱你,我在听呢,让我想想怎么帮你。';
|
|
8
|
-
if (/害怕|恐惧|怕|scared|afraid/.test(t))
|
|
9
|
-
return '别怕,有我在呢,让我想一想。';
|
|
10
|
-
if (/生气|愤怒|烦|angry|mad/.test(t))
|
|
11
|
-
return '我理解你的感受,让我来帮你想想办法。';
|
|
12
|
-
if (/累|疲|困|tired|exhausted/.test(t))
|
|
13
|
-
return '辛苦了,休息一下,我来帮你想。';
|
|
14
|
-
if (/无聊|没意思|bored/.test(t))
|
|
15
|
-
return '我来陪你聊聊吧,让我想想。';
|
|
16
|
-
if (/谢|感谢|thank/.test(t))
|
|
17
|
-
return '不客气呀,让我想想还能帮你什么。';
|
|
18
|
-
if (/你好|嗨|hello|hi|hey/.test(t))
|
|
19
|
-
return '你好呀!让我想想怎么回答你。';
|
|
20
|
-
if (/帮|help|怎么办/.test(t))
|
|
21
|
-
return '没问题,让我帮你想想办法。';
|
|
22
|
-
return '好的,让我想一想,马上回复你。';
|
|
23
|
-
}
|
|
2
|
+
import { pickSoothingReply } from './soothing.js';
|
|
3
|
+
import { DEFAULT_MQTT_HOST, DEFAULT_MQTT_PORT, DEFAULT_SUMMARY_ENABLED, DEFAULT_SUMMARY_MAX_CHARS, flatToPluginConfig } from './config.js';
|
|
24
4
|
// Per-account MQTT clients and msgId counters
|
|
25
5
|
const activeClients = new Map();
|
|
6
|
+
// Subagent reference, set at plugin registration
|
|
7
|
+
let subagent;
|
|
26
8
|
const folotoyChannel = {
|
|
27
9
|
id: 'folotoy',
|
|
28
10
|
meta: {
|
|
@@ -46,6 +28,8 @@ const folotoyChannel = {
|
|
|
46
28
|
api_key: { type: 'string' },
|
|
47
29
|
mqtt_host: { type: 'string', default: DEFAULT_MQTT_HOST },
|
|
48
30
|
mqtt_port: { type: 'number', default: DEFAULT_MQTT_PORT },
|
|
31
|
+
summary_enabled: { type: 'boolean', default: DEFAULT_SUMMARY_ENABLED },
|
|
32
|
+
summary_max_chars: { type: 'number', default: DEFAULT_SUMMARY_MAX_CHARS },
|
|
49
33
|
},
|
|
50
34
|
},
|
|
51
35
|
uiHints: {
|
|
@@ -56,6 +40,8 @@ const folotoyChannel = {
|
|
|
56
40
|
api_key: { label: 'API Key', sensitive: true },
|
|
57
41
|
mqtt_host: { label: 'MQTT Host', placeholder: DEFAULT_MQTT_HOST },
|
|
58
42
|
mqtt_port: { label: 'MQTT Port' },
|
|
43
|
+
summary_enabled: { label: 'Enable Summary' },
|
|
44
|
+
summary_max_chars: { label: 'Summary Max Characters' },
|
|
59
45
|
},
|
|
60
46
|
},
|
|
61
47
|
config: {
|
|
@@ -93,6 +79,8 @@ const folotoyChannel = {
|
|
|
93
79
|
if (err)
|
|
94
80
|
log?.error?.(`Failed to subscribe: ${err.message}`);
|
|
95
81
|
});
|
|
82
|
+
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED;
|
|
83
|
+
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS;
|
|
96
84
|
client.on('message', (_topic, payload) => {
|
|
97
85
|
let msg;
|
|
98
86
|
try {
|
|
@@ -121,8 +109,9 @@ const folotoyChannel = {
|
|
|
121
109
|
AccountId: accountId,
|
|
122
110
|
Provider: 'folotoy',
|
|
123
111
|
});
|
|
124
|
-
// dispatch
|
|
112
|
+
// dispatch, optionally summarize, then send finish message
|
|
125
113
|
void (async () => {
|
|
114
|
+
const replyChunks = [];
|
|
126
115
|
try {
|
|
127
116
|
await channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
128
117
|
ctx: inboundCtx,
|
|
@@ -131,17 +120,53 @@ const folotoyChannel = {
|
|
|
131
120
|
deliver: async (replyPayload) => {
|
|
132
121
|
if (!replyPayload.text)
|
|
133
122
|
return;
|
|
134
|
-
|
|
135
|
-
const outMsg = {
|
|
136
|
-
msgId,
|
|
137
|
-
identifier: 'chat_output',
|
|
138
|
-
outParams: { content: replyPayload.text, recording_id, order, is_finished: false },
|
|
139
|
-
};
|
|
140
|
-
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
123
|
+
replyChunks.push(replyPayload.text);
|
|
141
124
|
},
|
|
142
125
|
onError: (err) => log?.error?.(`Dispatch error: ${String(err)}`),
|
|
143
126
|
},
|
|
144
127
|
});
|
|
128
|
+
let finalText = replyChunks.join('');
|
|
129
|
+
// Summarize if enabled and text exceeds threshold
|
|
130
|
+
if (summaryEnabled && subagent && finalText.length > summaryMaxChars) {
|
|
131
|
+
try {
|
|
132
|
+
const sessionKey = `folotoy-summary-${accountId}-${Date.now()}`;
|
|
133
|
+
const { runId } = await subagent.run({
|
|
134
|
+
sessionKey,
|
|
135
|
+
message: [
|
|
136
|
+
`You are an assistant that summarizes texts concisely while keeping the most important information.`,
|
|
137
|
+
`Summarize the text to approximately ${summaryMaxChars} characters.`,
|
|
138
|
+
`Maintain the original tone and style. Reply only with the summary, without additional explanations.`,
|
|
139
|
+
``,
|
|
140
|
+
`<text_to_summarize>`,
|
|
141
|
+
finalText,
|
|
142
|
+
`</text_to_summarize>`,
|
|
143
|
+
].join('\n'),
|
|
144
|
+
deliver: false,
|
|
145
|
+
});
|
|
146
|
+
const result = await subagent.waitForRun({ runId, timeoutMs: 30_000 });
|
|
147
|
+
if (result.status === 'ok') {
|
|
148
|
+
const { messages } = await subagent.getSessionMessages({ sessionKey, limit: 1 });
|
|
149
|
+
const lastMsg = messages[messages.length - 1];
|
|
150
|
+
const summaryText = lastMsg?.content ?? lastMsg?.text;
|
|
151
|
+
if (summaryText)
|
|
152
|
+
finalText = summaryText;
|
|
153
|
+
}
|
|
154
|
+
await subagent.deleteSession({ sessionKey }).catch(() => { });
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
log?.warn?.(`Summary failed, truncating text: ${String(err)}`);
|
|
158
|
+
finalText = `${finalText.slice(0, summaryMaxChars - 3)}...`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (finalText) {
|
|
162
|
+
order++;
|
|
163
|
+
const outMsg = {
|
|
164
|
+
msgId,
|
|
165
|
+
identifier: 'chat_output',
|
|
166
|
+
outParams: { content: finalText, recording_id, order, is_finished: false },
|
|
167
|
+
};
|
|
168
|
+
client.publish(outboundTopic, JSON.stringify(outMsg));
|
|
169
|
+
}
|
|
145
170
|
}
|
|
146
171
|
finally {
|
|
147
172
|
order++;
|
|
@@ -203,6 +228,7 @@ export function sendNotification({ text, accountId }) {
|
|
|
203
228
|
return { channel: 'folotoy', messageId: String(msgId) };
|
|
204
229
|
}
|
|
205
230
|
export default (api) => {
|
|
231
|
+
subagent = api.runtime.subagent;
|
|
206
232
|
api.registerChannel({ plugin: folotoyChannel });
|
|
207
233
|
};
|
|
208
234
|
//# sourceMappingURL=index.js.map
|
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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mqtt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mqtt.d.ts","sourceRoot":"","sources":["../src/mqtt.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AACvC,OAAO,EAAoC,YAAY,EAAE,MAAM,aAAa,CAAA;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAgCD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAKvF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;
|
|
1
|
+
{"version":3,"file":"mqtt.d.ts","sourceRoot":"","sources":["../src/mqtt.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AACvC,OAAO,EAAoC,YAAY,EAAE,MAAM,aAAa,CAAA;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAgCD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAKvF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAKD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B9G"}
|
package/dist/mqtt.js
CHANGED
|
@@ -40,16 +40,28 @@ export function buildOutboundTopic(toy_sn) {
|
|
|
40
40
|
export function buildNotificationTopic(toy_sn) {
|
|
41
41
|
return `/openapi/folotoy/${toy_sn}/thing/event/post`;
|
|
42
42
|
}
|
|
43
|
+
const INITIAL_RECONNECT_MS = 1000;
|
|
44
|
+
const MAX_RECONNECT_MS = 60000;
|
|
43
45
|
export async function createMqttClient(config, credentials) {
|
|
44
46
|
const { host, port } = config.mqtt;
|
|
45
47
|
const { username, password } = credentials;
|
|
46
48
|
return new Promise((resolve, reject) => {
|
|
47
|
-
const clientId = `openapi:${
|
|
49
|
+
const clientId = `openapi:${credentials.toy_sn}`;
|
|
48
50
|
const client = mqtt.connect(`mqtt://${host}:${port}`, {
|
|
49
51
|
clientId,
|
|
50
|
-
username
|
|
52
|
+
username,
|
|
51
53
|
password,
|
|
52
54
|
clean: true,
|
|
55
|
+
reconnectPeriod: INITIAL_RECONNECT_MS,
|
|
56
|
+
});
|
|
57
|
+
// Exponential backoff: increase reconnectPeriod on each failed attempt,
|
|
58
|
+
// reset on successful connection.
|
|
59
|
+
client.on('reconnect', () => {
|
|
60
|
+
const current = client.options.reconnectPeriod ?? INITIAL_RECONNECT_MS;
|
|
61
|
+
client.options.reconnectPeriod = Math.min(current * 2, MAX_RECONNECT_MS);
|
|
62
|
+
});
|
|
63
|
+
client.on('connect', () => {
|
|
64
|
+
client.options.reconnectPeriod = INITIAL_RECONNECT_MS;
|
|
53
65
|
});
|
|
54
66
|
client.once('connect', () => resolve(client));
|
|
55
67
|
client.once('error', reject);
|
package/dist/mqtt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mqtt.js","sourceRoot":"","sources":["../src/mqtt.ts"],"names":[],"mappings":"AAAA,OAAO,IAAoB,MAAM,MAAM,CAAA;AASvC,KAAK,UAAU,gBAAgB,CAAC,IAAqB;IACnD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,+BAA+B,EAAE;QACtE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,eAAe,EAAE,UAAU,IAAI,CAAC,OAAO,EAAE;YACzC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;KAC9C,CAAC,CAAA;IAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IAChF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA4C,CAAA;IACvE,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAqB;IAC9C,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,MAAM;QACrB,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAoB;IAC3D,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,oBAAoB,MAAM,qBAAqB,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,oBAAoB,MAAM,mBAAmB,CAAA;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAoB,EAAE,WAA4B;IACvF,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;IAClC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAA;IAE1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,QAAQ,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"mqtt.js","sourceRoot":"","sources":["../src/mqtt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soothing acknowledgment replies for FoloToy.
|
|
3
|
+
*
|
|
4
|
+
* 12 intent categories, 5 candidates each — one is chosen at random.
|
|
5
|
+
* Principles: convey "I heard you + I'm working on it", no time promises.
|
|
6
|
+
*/
|
|
7
|
+
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
8
|
+
export declare function pickSoothingReply(text: string): string;
|
|
9
|
+
//# sourceMappingURL=soothing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"soothing.d.ts","sourceRoot":"","sources":["../src/soothing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+IH,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOtD"}
|
package/dist/soothing.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soothing acknowledgment replies for FoloToy.
|
|
3
|
+
*
|
|
4
|
+
* 12 intent categories, 5 candidates each — one is chosen at random.
|
|
5
|
+
* Principles: convey "I heard you + I'm working on it", no time promises.
|
|
6
|
+
*/
|
|
7
|
+
const INTENT_RULES = [
|
|
8
|
+
{
|
|
9
|
+
// 1. 难过类
|
|
10
|
+
pattern: /难过|伤心|哭|不开心|委屈/,
|
|
11
|
+
candidates: [
|
|
12
|
+
'抱抱你,我在认真听呢,让我想想怎么说...',
|
|
13
|
+
'别难过,我陪着你,让我想一会儿...',
|
|
14
|
+
'嗯,我听到了,给我点时间好好想想怎么帮你',
|
|
15
|
+
'你说的我都记住了,让我想想...',
|
|
16
|
+
'我在认真想呢,你先缓缓',
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
// 2. 生气类
|
|
21
|
+
pattern: /生气|愤怒|烦|讨厌|气死/,
|
|
22
|
+
candidates: [
|
|
23
|
+
'我理解你,让我想想怎么帮你...',
|
|
24
|
+
'消消气,我在认真想办法呢',
|
|
25
|
+
'听到了,让我好好想一下...',
|
|
26
|
+
'别气别气,我来帮你想想,给我一点时间',
|
|
27
|
+
'嗯,你说的我记住了,我想一下',
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
// 3. 累/怕类
|
|
32
|
+
pattern: /累|疲|困|害怕|怕/,
|
|
33
|
+
candidates: [
|
|
34
|
+
'辛苦了,让我想想怎么帮你...',
|
|
35
|
+
'别怕,有我在呢,我想一下',
|
|
36
|
+
'你先歇歇,让我帮你想想办法...',
|
|
37
|
+
'我在想办法呢,你先放松一下',
|
|
38
|
+
'嗯嗯,我在认真想怎么帮你...',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
// 4. 故事类
|
|
43
|
+
pattern: /故事|story/i,
|
|
44
|
+
candidates: [
|
|
45
|
+
'让我好好想一个故事...',
|
|
46
|
+
'嗯,我在想一个好听的故事呢',
|
|
47
|
+
'让我想想讲什么好...',
|
|
48
|
+
'我在脑子里翻故事书呢,等等我',
|
|
49
|
+
'故事嘛,得好好想一个才行...',
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
// 5. 唱歌/音乐类
|
|
54
|
+
pattern: /唱|歌|音乐|播放|儿歌/,
|
|
55
|
+
candidates: [
|
|
56
|
+
'让我想想唱什么好...',
|
|
57
|
+
'嗯,我在选歌呢,等我一下',
|
|
58
|
+
'我清清嗓子,想想唱什么...',
|
|
59
|
+
'让我找一首好听的...',
|
|
60
|
+
'好嘞,让我想想唱哪首',
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
// 6. 笑话类
|
|
65
|
+
pattern: /笑话|joke|搞笑|好笑/i,
|
|
66
|
+
candidates: [
|
|
67
|
+
'让我想一个好笑的...',
|
|
68
|
+
'嗯,我在想笑话呢,先憋住',
|
|
69
|
+
'好嘞,让我想一个逗你乐的...',
|
|
70
|
+
'笑话嘛,得想一个真正好笑的才行...',
|
|
71
|
+
'让我翻翻我的笑话本...',
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
// 7. 知识/古诗类
|
|
76
|
+
pattern: /为什么|怎么|什么是|古诗|诗|背/,
|
|
77
|
+
candidates: [
|
|
78
|
+
'好问题,让我好好想想...',
|
|
79
|
+
'嗯,这个问题我得想一下',
|
|
80
|
+
'让我仔细想想怎么跟你解释...',
|
|
81
|
+
'这个嘛,让我想一想...',
|
|
82
|
+
'嗯,让我好好想想这个',
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
// 8. 继续/追问类
|
|
87
|
+
pattern: /继续|接着|然后呢|后来|下一个/,
|
|
88
|
+
candidates: [
|
|
89
|
+
'让我接着往下想...',
|
|
90
|
+
'嗯,后面的我得好好想想',
|
|
91
|
+
'让我理理思路...',
|
|
92
|
+
'好嘞,让我想想后面怎么接',
|
|
93
|
+
'我在想呢,等一下',
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
// 9. 打招呼/呼唤类
|
|
98
|
+
pattern: /你好|嗨|hello|hi\b|hey|哈喽/i,
|
|
99
|
+
candidates: [
|
|
100
|
+
'来啦,让我想想说什么...',
|
|
101
|
+
'嘿,你来啦,让我想想...',
|
|
102
|
+
'我在呢,让我想想跟你说什么好',
|
|
103
|
+
'哎,来了,给我一点时间想想',
|
|
104
|
+
'哈,你来啦,让我想一下...',
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
// 10. 告别/睡觉类
|
|
109
|
+
pattern: /关机|睡|拜拜|再见|bye|晚安/i,
|
|
110
|
+
candidates: ['好嘞~', '嗯嗯~', '好的~', '哦~', '嗯~'],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
// 11. 配网/技术类
|
|
114
|
+
pattern: /网络|配网|wifi|蓝牙|连接/i,
|
|
115
|
+
candidates: [
|
|
116
|
+
'让我想想怎么帮你...',
|
|
117
|
+
'嗯,这个我得想一下',
|
|
118
|
+
'让我看看怎么办...',
|
|
119
|
+
'我在想呢,等一下',
|
|
120
|
+
'嗯,给我一点时间想想',
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
// 12. 查询类
|
|
125
|
+
pattern: /天气|新闻|时间|几点|日期|查|搜/,
|
|
126
|
+
candidates: [
|
|
127
|
+
'我去帮你查一查...',
|
|
128
|
+
'好的,我来查一下',
|
|
129
|
+
'稍等,我帮你看看',
|
|
130
|
+
'我去查一下,等我一秒',
|
|
131
|
+
'让我来查查看',
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
const DEFAULT_CANDIDATES = [
|
|
136
|
+
'让我想一想...',
|
|
137
|
+
'嗯嗯,让我想想怎么回答你',
|
|
138
|
+
'好嘞,我想一下',
|
|
139
|
+
'嗯,给我一点时间想想',
|
|
140
|
+
'让我好好想想...',
|
|
141
|
+
];
|
|
142
|
+
function pickRandom(arr) {
|
|
143
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
144
|
+
}
|
|
145
|
+
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
146
|
+
export function pickSoothingReply(text) {
|
|
147
|
+
for (const rule of INTENT_RULES) {
|
|
148
|
+
if (rule.pattern.test(text)) {
|
|
149
|
+
return pickRandom(rule.candidates);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return pickRandom(DEFAULT_CANDIDATES);
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=soothing.js.map
|
|
@@ -0,0 +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,UAAU,CAAI,GAAQ;IAC7B,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAM,CAAA;AACzD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAA;AACvC,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { pickSoothingReply } from '../soothing.js'
|
|
3
|
+
|
|
4
|
+
const CATEGORIES = [
|
|
5
|
+
{ input: '我今天好难过', label: '难过类' },
|
|
6
|
+
{ input: '我很生气', label: '生气类' },
|
|
7
|
+
{ input: '我好累', label: '累/怕类' },
|
|
8
|
+
{ input: '给我讲个故事', label: '故事类' },
|
|
9
|
+
{ input: '唱首儿歌', label: '唱歌类' },
|
|
10
|
+
{ input: '讲个笑话', label: '笑话类' },
|
|
11
|
+
{ input: '为什么天是蓝的', label: '知识类' },
|
|
12
|
+
{ input: '然后呢', label: '继续类' },
|
|
13
|
+
{ input: '你好', label: '打招呼类' },
|
|
14
|
+
{ input: '晚安', label: '告别类' },
|
|
15
|
+
{ input: '帮我连接wifi', label: '配网类' },
|
|
16
|
+
{ input: '帮我查天气', label: '查询类' },
|
|
17
|
+
{ input: '随便说点什么', label: '兜底' },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
describe('pickSoothingReply', () => {
|
|
21
|
+
it.each(CATEGORIES)('$label — returns a non-empty string', ({ input }) => {
|
|
22
|
+
const reply = pickSoothingReply(input)
|
|
23
|
+
expect(typeof reply).toBe('string')
|
|
24
|
+
expect(reply.length).toBeGreaterThan(0)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it.each(CATEGORIES)('$label — never contains 马上', ({ input }) => {
|
|
28
|
+
// Run multiple times to cover random selection
|
|
29
|
+
for (let i = 0; i < 20; i++) {
|
|
30
|
+
expect(pickSoothingReply(input)).not.toContain('马上')
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('randomly selects from candidates (not always the same reply)', () => {
|
|
35
|
+
const results = new Set(
|
|
36
|
+
Array.from({ length: 40 }, () => pickSoothingReply('我今天好难过'))
|
|
37
|
+
)
|
|
38
|
+
expect(results.size).toBeGreaterThan(1)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('告别类 returns only short affirmatives', () => {
|
|
42
|
+
const allowed = new Set(['好嘞~', '嗯嗯~', '好的~', '哦~', '嗯~'])
|
|
43
|
+
for (let i = 0; i < 20; i++) {
|
|
44
|
+
expect(allowed.has(pickSoothingReply('晚安'))).toBe(true)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
})
|
package/src/cli/install.ts
CHANGED
|
@@ -87,6 +87,14 @@ async function pollSession(sessionId: string): Promise<PollResponse & { status:
|
|
|
87
87
|
throw new Error('Pairing timed out after 5 minutes.')
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function restartGateway(): void {
|
|
91
|
+
try {
|
|
92
|
+
execSync('openclaw gateway restart', { stdio: 'inherit' })
|
|
93
|
+
} catch {
|
|
94
|
+
console.warn('⚠ Failed to restart gateway. You can restart manually: openclaw gateway restart')
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
function writeConfig(result: { toy_sn: string; toy_key: string; mqtt_host?: string; mqtt_port?: number }): void {
|
|
91
99
|
execSync(`openclaw config set channels.folotoy.flow direct`, { stdio: 'pipe' })
|
|
92
100
|
execSync(`openclaw config set channels.folotoy.toy_sn ${result.toy_sn}`, { stdio: 'pipe' })
|
|
@@ -134,11 +142,14 @@ async function main() {
|
|
|
134
142
|
console.log('\nWriting configuration...')
|
|
135
143
|
writeConfig(result)
|
|
136
144
|
|
|
137
|
-
// Step 7:
|
|
145
|
+
// Step 7: restart gateway
|
|
146
|
+
console.log('\nRestarting gateway...')
|
|
147
|
+
restartGateway()
|
|
148
|
+
|
|
149
|
+
// Step 8: done
|
|
138
150
|
console.log('\n\x1b[32m✓ FoloToy plugin installed and configured!\x1b[0m')
|
|
139
151
|
console.log(` Toy SN: ${result.toy_sn}`)
|
|
140
152
|
console.log(` MQTT Host: ${result.mqtt_host ?? DEFAULT_MQTT_HOST}`)
|
|
141
|
-
console.log('\nRestart the gateway to apply: openclaw gateway start --force')
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
main().catch((err) => {
|
package/src/config.ts
CHANGED
|
@@ -28,11 +28,15 @@ export type FlatChannelConfig = {
|
|
|
28
28
|
api_key?: string
|
|
29
29
|
mqtt_host?: string
|
|
30
30
|
mqtt_port?: number
|
|
31
|
+
summary_enabled?: boolean
|
|
32
|
+
summary_max_chars?: number
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export const DEFAULT_API_URL = 'https://api.folotoy.cn'
|
|
34
36
|
export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.qrc92.cn'
|
|
35
37
|
export const DEFAULT_MQTT_PORT = 1883
|
|
38
|
+
export const DEFAULT_SUMMARY_ENABLED = true
|
|
39
|
+
export const DEFAULT_SUMMARY_MAX_CHARS = 200
|
|
36
40
|
|
|
37
41
|
export function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig {
|
|
38
42
|
const flow = flat.flow ?? 'direct'
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { OpenClawPluginApi, ChannelPlugin } from 'openclaw/plugin-sdk/core'
|
|
1
|
+
import type { OpenClawPluginApi, ChannelPlugin, PluginRuntime } from 'openclaw/plugin-sdk/core'
|
|
2
2
|
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic, buildNotificationTopic } from './mqtt.js'
|
|
3
|
-
import {
|
|
3
|
+
import { pickSoothingReply } from './soothing.js'
|
|
4
|
+
import { DEFAULT_MQTT_HOST, DEFAULT_MQTT_PORT, DEFAULT_SUMMARY_ENABLED, DEFAULT_SUMMARY_MAX_CHARS, flatToPluginConfig } from './config.js'
|
|
4
5
|
import type { FlatChannelConfig } from './config.js'
|
|
5
6
|
import type { MqttClient } from 'mqtt'
|
|
6
7
|
|
|
@@ -22,33 +23,12 @@ type NotificationMessage = {
|
|
|
22
23
|
outParams: { text: string }
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/** Pick a soothing acknowledgment that loosely matches the input. */
|
|
26
|
-
function pickSoothingReply(text: string): string {
|
|
27
|
-
const t = text.toLowerCase()
|
|
28
|
-
|
|
29
|
-
if (/难过|伤心|哭|不开心|sad|upset|cry/.test(t))
|
|
30
|
-
return '抱抱你,我在听呢,让我想想怎么帮你。'
|
|
31
|
-
if (/害怕|恐惧|怕|scared|afraid/.test(t))
|
|
32
|
-
return '别怕,有我在呢,让我想一想。'
|
|
33
|
-
if (/生气|愤怒|烦|angry|mad/.test(t))
|
|
34
|
-
return '我理解你的感受,让我来帮你想想办法。'
|
|
35
|
-
if (/累|疲|困|tired|exhausted/.test(t))
|
|
36
|
-
return '辛苦了,休息一下,我来帮你想。'
|
|
37
|
-
if (/无聊|没意思|bored/.test(t))
|
|
38
|
-
return '我来陪你聊聊吧,让我想想。'
|
|
39
|
-
if (/谢|感谢|thank/.test(t))
|
|
40
|
-
return '不客气呀,让我想想还能帮你什么。'
|
|
41
|
-
if (/你好|嗨|hello|hi|hey/.test(t))
|
|
42
|
-
return '你好呀!让我想想怎么回答你。'
|
|
43
|
-
if (/帮|help|怎么办/.test(t))
|
|
44
|
-
return '没问题,让我帮你想想办法。'
|
|
45
|
-
|
|
46
|
-
return '好的,让我想一想,马上回复你。'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
26
|
// Per-account MQTT clients and msgId counters
|
|
50
27
|
const activeClients = new Map<string, { client: MqttClient; toy_sn: string; nextMsgId: number }>()
|
|
51
28
|
|
|
29
|
+
// Subagent reference, set at plugin registration
|
|
30
|
+
let subagent: PluginRuntime['subagent'] | undefined
|
|
31
|
+
|
|
52
32
|
const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
53
33
|
id: 'folotoy',
|
|
54
34
|
meta: {
|
|
@@ -72,6 +52,8 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
72
52
|
api_key: { type: 'string' },
|
|
73
53
|
mqtt_host: { type: 'string', default: DEFAULT_MQTT_HOST },
|
|
74
54
|
mqtt_port: { type: 'number', default: DEFAULT_MQTT_PORT },
|
|
55
|
+
summary_enabled: { type: 'boolean', default: DEFAULT_SUMMARY_ENABLED },
|
|
56
|
+
summary_max_chars: { type: 'number', default: DEFAULT_SUMMARY_MAX_CHARS },
|
|
75
57
|
},
|
|
76
58
|
},
|
|
77
59
|
uiHints: {
|
|
@@ -82,6 +64,8 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
82
64
|
api_key: { label: 'API Key', sensitive: true },
|
|
83
65
|
mqtt_host: { label: 'MQTT Host', placeholder: DEFAULT_MQTT_HOST },
|
|
84
66
|
mqtt_port: { label: 'MQTT Port' },
|
|
67
|
+
summary_enabled: { label: 'Enable Summary' },
|
|
68
|
+
summary_max_chars: { label: 'Summary Max Characters' },
|
|
85
69
|
},
|
|
86
70
|
},
|
|
87
71
|
config: {
|
|
@@ -124,6 +108,9 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
124
108
|
if (err) log?.error?.(`Failed to subscribe: ${err.message}`)
|
|
125
109
|
})
|
|
126
110
|
|
|
111
|
+
const summaryEnabled = account.summary_enabled ?? DEFAULT_SUMMARY_ENABLED
|
|
112
|
+
const summaryMaxChars = account.summary_max_chars ?? DEFAULT_SUMMARY_MAX_CHARS
|
|
113
|
+
|
|
127
114
|
client.on('message', (_topic, payload) => {
|
|
128
115
|
let msg: InboundMessage
|
|
129
116
|
try {
|
|
@@ -154,8 +141,9 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
154
141
|
Provider: 'folotoy',
|
|
155
142
|
})
|
|
156
143
|
|
|
157
|
-
// dispatch
|
|
144
|
+
// dispatch, optionally summarize, then send finish message
|
|
158
145
|
void (async () => {
|
|
146
|
+
const replyChunks: string[] = []
|
|
159
147
|
try {
|
|
160
148
|
await channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
161
149
|
ctx: inboundCtx,
|
|
@@ -163,17 +151,54 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
163
151
|
dispatcherOptions: {
|
|
164
152
|
deliver: async (replyPayload) => {
|
|
165
153
|
if (!replyPayload.text) return
|
|
166
|
-
|
|
167
|
-
const outMsg: OutboundMessage = {
|
|
168
|
-
msgId,
|
|
169
|
-
identifier: 'chat_output',
|
|
170
|
-
outParams: { content: replyPayload.text, recording_id, order, is_finished: false },
|
|
171
|
-
}
|
|
172
|
-
client.publish(outboundTopic, JSON.stringify(outMsg))
|
|
154
|
+
replyChunks.push(replyPayload.text)
|
|
173
155
|
},
|
|
174
156
|
onError: (err) => log?.error?.(`Dispatch error: ${String(err)}`),
|
|
175
157
|
},
|
|
176
158
|
})
|
|
159
|
+
|
|
160
|
+
let finalText = replyChunks.join('')
|
|
161
|
+
|
|
162
|
+
// Summarize if enabled and text exceeds threshold
|
|
163
|
+
if (summaryEnabled && subagent && finalText.length > summaryMaxChars) {
|
|
164
|
+
try {
|
|
165
|
+
const sessionKey = `folotoy-summary-${accountId}-${Date.now()}`
|
|
166
|
+
const { runId } = await subagent.run({
|
|
167
|
+
sessionKey,
|
|
168
|
+
message: [
|
|
169
|
+
`You are an assistant that summarizes texts concisely while keeping the most important information.`,
|
|
170
|
+
`Summarize the text to approximately ${summaryMaxChars} characters.`,
|
|
171
|
+
`Maintain the original tone and style. Reply only with the summary, without additional explanations.`,
|
|
172
|
+
``,
|
|
173
|
+
`<text_to_summarize>`,
|
|
174
|
+
finalText,
|
|
175
|
+
`</text_to_summarize>`,
|
|
176
|
+
].join('\n'),
|
|
177
|
+
deliver: false,
|
|
178
|
+
})
|
|
179
|
+
const result = await subagent.waitForRun({ runId, timeoutMs: 30_000 })
|
|
180
|
+
if (result.status === 'ok') {
|
|
181
|
+
const { messages } = await subagent.getSessionMessages({ sessionKey, limit: 1 })
|
|
182
|
+
const lastMsg = messages[messages.length - 1] as { content?: string; text?: string } | undefined
|
|
183
|
+
const summaryText = lastMsg?.content ?? lastMsg?.text
|
|
184
|
+
if (summaryText) finalText = summaryText
|
|
185
|
+
}
|
|
186
|
+
await subagent.deleteSession({ sessionKey }).catch(() => {})
|
|
187
|
+
} catch (err) {
|
|
188
|
+
log?.warn?.(`Summary failed, truncating text: ${String(err)}`)
|
|
189
|
+
finalText = `${finalText.slice(0, summaryMaxChars - 3)}...`
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (finalText) {
|
|
194
|
+
order++
|
|
195
|
+
const outMsg: OutboundMessage = {
|
|
196
|
+
msgId,
|
|
197
|
+
identifier: 'chat_output',
|
|
198
|
+
outParams: { content: finalText, recording_id, order, is_finished: false },
|
|
199
|
+
}
|
|
200
|
+
client.publish(outboundTopic, JSON.stringify(outMsg))
|
|
201
|
+
}
|
|
177
202
|
} finally {
|
|
178
203
|
order++
|
|
179
204
|
const finishMsg: OutboundMessage = {
|
|
@@ -239,5 +264,6 @@ export function sendNotification({ text, accountId }: { text: string; accountId?
|
|
|
239
264
|
}
|
|
240
265
|
|
|
241
266
|
export default (api: OpenClawPluginApi) => {
|
|
267
|
+
subagent = api.runtime.subagent
|
|
242
268
|
api.registerChannel({ plugin: folotoyChannel })
|
|
243
269
|
}
|
package/src/mqtt.ts
CHANGED
|
@@ -56,17 +56,32 @@ export function buildNotificationTopic(toy_sn: string): string {
|
|
|
56
56
|
return `/openapi/folotoy/${toy_sn}/thing/event/post`
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const INITIAL_RECONNECT_MS = 1000
|
|
60
|
+
const MAX_RECONNECT_MS = 60000
|
|
61
|
+
|
|
59
62
|
export async function createMqttClient(config: PluginConfig, credentials: MqttCredentials): Promise<MqttClient> {
|
|
60
63
|
const { host, port } = config.mqtt
|
|
61
64
|
const { username, password } = credentials
|
|
62
65
|
|
|
63
66
|
return new Promise((resolve, reject) => {
|
|
64
|
-
const clientId = `openapi:${
|
|
67
|
+
const clientId = `openapi:${credentials.toy_sn}`
|
|
65
68
|
const client = mqtt.connect(`mqtt://${host}:${port}`, {
|
|
66
69
|
clientId,
|
|
67
|
-
username
|
|
70
|
+
username,
|
|
68
71
|
password,
|
|
69
72
|
clean: true,
|
|
73
|
+
reconnectPeriod: INITIAL_RECONNECT_MS,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Exponential backoff: increase reconnectPeriod on each failed attempt,
|
|
77
|
+
// reset on successful connection.
|
|
78
|
+
client.on('reconnect', () => {
|
|
79
|
+
const current = client.options.reconnectPeriod ?? INITIAL_RECONNECT_MS
|
|
80
|
+
client.options.reconnectPeriod = Math.min(current * 2, MAX_RECONNECT_MS)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
client.on('connect', () => {
|
|
84
|
+
client.options.reconnectPeriod = INITIAL_RECONNECT_MS
|
|
70
85
|
})
|
|
71
86
|
|
|
72
87
|
client.once('connect', () => resolve(client))
|
package/src/soothing.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soothing acknowledgment replies for FoloToy.
|
|
3
|
+
*
|
|
4
|
+
* 12 intent categories, 5 candidates each — one is chosen at random.
|
|
5
|
+
* Principles: convey "I heard you + I'm working on it", no time promises.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const INTENT_RULES: Array<{ pattern: RegExp; candidates: string[] }> = [
|
|
9
|
+
{
|
|
10
|
+
// 1. 难过类
|
|
11
|
+
pattern: /难过|伤心|哭|不开心|委屈/,
|
|
12
|
+
candidates: [
|
|
13
|
+
'抱抱你,我在认真听呢,让我想想怎么说...',
|
|
14
|
+
'别难过,我陪着你,让我想一会儿...',
|
|
15
|
+
'嗯,我听到了,给我点时间好好想想怎么帮你',
|
|
16
|
+
'你说的我都记住了,让我想想...',
|
|
17
|
+
'我在认真想呢,你先缓缓',
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// 2. 生气类
|
|
22
|
+
pattern: /生气|愤怒|烦|讨厌|气死/,
|
|
23
|
+
candidates: [
|
|
24
|
+
'我理解你,让我想想怎么帮你...',
|
|
25
|
+
'消消气,我在认真想办法呢',
|
|
26
|
+
'听到了,让我好好想一下...',
|
|
27
|
+
'别气别气,我来帮你想想,给我一点时间',
|
|
28
|
+
'嗯,你说的我记住了,我想一下',
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
// 3. 累/怕类
|
|
33
|
+
pattern: /累|疲|困|害怕|怕/,
|
|
34
|
+
candidates: [
|
|
35
|
+
'辛苦了,让我想想怎么帮你...',
|
|
36
|
+
'别怕,有我在呢,我想一下',
|
|
37
|
+
'你先歇歇,让我帮你想想办法...',
|
|
38
|
+
'我在想办法呢,你先放松一下',
|
|
39
|
+
'嗯嗯,我在认真想怎么帮你...',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
// 4. 故事类
|
|
44
|
+
pattern: /故事|story/i,
|
|
45
|
+
candidates: [
|
|
46
|
+
'让我好好想一个故事...',
|
|
47
|
+
'嗯,我在想一个好听的故事呢',
|
|
48
|
+
'让我想想讲什么好...',
|
|
49
|
+
'我在脑子里翻故事书呢,等等我',
|
|
50
|
+
'故事嘛,得好好想一个才行...',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
// 5. 唱歌/音乐类
|
|
55
|
+
pattern: /唱|歌|音乐|播放|儿歌/,
|
|
56
|
+
candidates: [
|
|
57
|
+
'让我想想唱什么好...',
|
|
58
|
+
'嗯,我在选歌呢,等我一下',
|
|
59
|
+
'我清清嗓子,想想唱什么...',
|
|
60
|
+
'让我找一首好听的...',
|
|
61
|
+
'好嘞,让我想想唱哪首',
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
// 6. 笑话类
|
|
66
|
+
pattern: /笑话|joke|搞笑|好笑/i,
|
|
67
|
+
candidates: [
|
|
68
|
+
'让我想一个好笑的...',
|
|
69
|
+
'嗯,我在想笑话呢,先憋住',
|
|
70
|
+
'好嘞,让我想一个逗你乐的...',
|
|
71
|
+
'笑话嘛,得想一个真正好笑的才行...',
|
|
72
|
+
'让我翻翻我的笑话本...',
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
// 7. 知识/古诗类
|
|
77
|
+
pattern: /为什么|怎么|什么是|古诗|诗|背/,
|
|
78
|
+
candidates: [
|
|
79
|
+
'好问题,让我好好想想...',
|
|
80
|
+
'嗯,这个问题我得想一下',
|
|
81
|
+
'让我仔细想想怎么跟你解释...',
|
|
82
|
+
'这个嘛,让我想一想...',
|
|
83
|
+
'嗯,让我好好想想这个',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
// 8. 继续/追问类
|
|
88
|
+
pattern: /继续|接着|然后呢|后来|下一个/,
|
|
89
|
+
candidates: [
|
|
90
|
+
'让我接着往下想...',
|
|
91
|
+
'嗯,后面的我得好好想想',
|
|
92
|
+
'让我理理思路...',
|
|
93
|
+
'好嘞,让我想想后面怎么接',
|
|
94
|
+
'我在想呢,等一下',
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
// 9. 打招呼/呼唤类
|
|
99
|
+
pattern: /你好|嗨|hello|hi\b|hey|哈喽/i,
|
|
100
|
+
candidates: [
|
|
101
|
+
'来啦,让我想想说什么...',
|
|
102
|
+
'嘿,你来啦,让我想想...',
|
|
103
|
+
'我在呢,让我想想跟你说什么好',
|
|
104
|
+
'哎,来了,给我一点时间想想',
|
|
105
|
+
'哈,你来啦,让我想一下...',
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
// 10. 告别/睡觉类
|
|
110
|
+
pattern: /关机|睡|拜拜|再见|bye|晚安/i,
|
|
111
|
+
candidates: ['好嘞~', '嗯嗯~', '好的~', '哦~', '嗯~'],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
// 11. 配网/技术类
|
|
115
|
+
pattern: /网络|配网|wifi|蓝牙|连接/i,
|
|
116
|
+
candidates: [
|
|
117
|
+
'让我想想怎么帮你...',
|
|
118
|
+
'嗯,这个我得想一下',
|
|
119
|
+
'让我看看怎么办...',
|
|
120
|
+
'我在想呢,等一下',
|
|
121
|
+
'嗯,给我一点时间想想',
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
// 12. 查询类
|
|
126
|
+
pattern: /天气|新闻|时间|几点|日期|查|搜/,
|
|
127
|
+
candidates: [
|
|
128
|
+
'我去帮你查一查...',
|
|
129
|
+
'好的,我来查一下',
|
|
130
|
+
'稍等,我帮你看看',
|
|
131
|
+
'我去查一下,等我一秒',
|
|
132
|
+
'让我来查查看',
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
const DEFAULT_CANDIDATES = [
|
|
138
|
+
'让我想一想...',
|
|
139
|
+
'嗯嗯,让我想想怎么回答你',
|
|
140
|
+
'好嘞,我想一下',
|
|
141
|
+
'嗯,给我一点时间想想',
|
|
142
|
+
'让我好好想想...',
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
function pickRandom<T>(arr: T[]): T {
|
|
146
|
+
return arr[Math.floor(Math.random() * arr.length)] as T
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Returns one randomly chosen soothing reply matching the input intent. */
|
|
150
|
+
export function pickSoothingReply(text: string): string {
|
|
151
|
+
for (const rule of INTENT_RULES) {
|
|
152
|
+
if (rule.pattern.test(text)) {
|
|
153
|
+
return pickRandom(rule.candidates)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return pickRandom(DEFAULT_CANDIDATES)
|
|
157
|
+
}
|