dsh-plugin-soulbridge 0.0.111 → 0.0.112
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/index.js +44 -8
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1098,6 +1098,17 @@ function bumpExtractAttempt(turnId) {
|
|
|
1098
1098
|
);
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
+
/**
|
|
1102
|
+
* After a thread was extracted, every queued turn of that thread is already covered.
|
|
1103
|
+
* Leaving them would extract the same topic again, turn by turn (0025).
|
|
1104
|
+
*/
|
|
1105
|
+
function removeExtractItemsOfThread(threadId, turnIds) {
|
|
1106
|
+
if (!threadId) return;
|
|
1107
|
+
writeExtractQueue(
|
|
1108
|
+
readExtractQueue().filter((item) => !turnIds.includes(item.turnId)),
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1101
1112
|
/**
|
|
1102
1113
|
* Add one turn to the extraction queue.
|
|
1103
1114
|
*
|
|
@@ -1131,7 +1142,7 @@ function hasSubstance(text) {
|
|
|
1131
1142
|
return !/^(好的?|嗯+|哦+|可以|行|ok|okay|收到|明白|继续|好)[。!!.\s]*$/i.test(trimmed);
|
|
1132
1143
|
}
|
|
1133
1144
|
|
|
1134
|
-
async function runExtraction(text, turnId, turnNo, origin = 'live') {
|
|
1145
|
+
async function runExtraction(text, turnId, turnNo, origin = 'live', thread = null) {
|
|
1135
1146
|
const config = readConfig();
|
|
1136
1147
|
if (config.extract === 'off') return 'skipped-off';
|
|
1137
1148
|
if (typeof text !== 'string' || !hasSubstance(text)) {
|
|
@@ -1153,6 +1164,10 @@ async function runExtraction(text, turnId, turnNo, origin = 'live') {
|
|
|
1153
1164
|
const previousThreadId = soul.threadBefore(turnRow.sessionId, turnNo);
|
|
1154
1165
|
if (previousThreadId) context.previousThreadId = previousThreadId;
|
|
1155
1166
|
}
|
|
1167
|
+
// 0025: the unit is the whole topic when the turn belongs to one. The turns array is
|
|
1168
|
+
// numbered the way the prompt numbers them, and each fact may name its own turn.
|
|
1169
|
+
const turns = thread?.turns ?? null;
|
|
1170
|
+
const turnIds = turns ? Object.fromEntries(turns.map((entry) => [entry.turnNo, entry.id])) : undefined;
|
|
1156
1171
|
const draft = await soul.extract(text, {
|
|
1157
1172
|
llm: useLlm ? injectedLlm : undefined,
|
|
1158
1173
|
mode: useLlm ? 'auto' : 'rules',
|
|
@@ -1162,8 +1177,9 @@ async function runExtraction(text, turnId, turnNo, origin = 'live') {
|
|
|
1162
1177
|
// The exchange, not the message (ADR 0035): my reply is where promises and
|
|
1163
1178
|
// decisions live, and it is the only way "我的承诺" can ever be extracted.
|
|
1164
1179
|
assistantText: turnRow?.assistantText ?? undefined,
|
|
1180
|
+
turns: turns ?? undefined,
|
|
1165
1181
|
});
|
|
1166
|
-
const result = soul.commitDrafts(draft, { turnId });
|
|
1182
|
+
const result = soul.commitDrafts(draft, { turnId, turnIds });
|
|
1167
1183
|
// Attribution is written even when nothing was extracted: a turn that says
|
|
1168
1184
|
// "无" (nothing worth remembering) can still be part of a thread.
|
|
1169
1185
|
const attribution = soul.applyThread(draft.thread, {
|
|
@@ -1183,6 +1199,7 @@ async function runExtraction(text, turnId, turnNo, origin = 'live') {
|
|
|
1183
1199
|
` gaps=${result.gaps.length} skipped=${result.skipped.length}` +
|
|
1184
1200
|
` slots->text=${result.droppedSlots.length} holder->world=${result.holderFallbacks.length}` +
|
|
1185
1201
|
` inferred=${inferred}` +
|
|
1202
|
+
` unit=${turns ? `${turns.length}turns` : '1turn'} threadId=${thread?.id ?? '-'}` +
|
|
1186
1203
|
` ms=${ms} turn=${turnNo} origin=${origin}` +
|
|
1187
1204
|
` thread=${attribution.threadId}${attribution.created ? '(new)' : ''}` +
|
|
1188
1205
|
` join=${attribution.joinReason} threadLine=${draft.threadLine ?? 'rules'}` +
|
|
@@ -1276,9 +1293,21 @@ export async function drainExtractionQueue() {
|
|
|
1276
1293
|
try {
|
|
1277
1294
|
const item = pickExtractionItem(queue);
|
|
1278
1295
|
let text = '';
|
|
1296
|
+
let thread = null;
|
|
1279
1297
|
try {
|
|
1280
|
-
const
|
|
1298
|
+
const soul = ensureSoul();
|
|
1299
|
+
const turn = soul.turn(item.turnId);
|
|
1281
1300
|
text = turn?.userText ?? '';
|
|
1301
|
+
// A turn is a trigger, the topic is the unit (0025). When the turn is already in a
|
|
1302
|
+
// thread, extract that thread's turns instead of this message alone.
|
|
1303
|
+
const threadId = soul.turnThreadId(item.turnId);
|
|
1304
|
+
if (threadId) {
|
|
1305
|
+
const turns = soul.threadTurns(threadId);
|
|
1306
|
+
if (turns.length > 0) {
|
|
1307
|
+
thread = { id: threadId, turns };
|
|
1308
|
+
text = turns[turns.length - 1].userText;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1282
1311
|
} catch {
|
|
1283
1312
|
text = '';
|
|
1284
1313
|
}
|
|
@@ -1288,14 +1317,21 @@ export async function drainExtractionQueue() {
|
|
|
1288
1317
|
return;
|
|
1289
1318
|
}
|
|
1290
1319
|
|
|
1291
|
-
const outcome = await runExtraction(text, item.turnId, item.turnNo, item.origin ?? 'live');
|
|
1320
|
+
const outcome = await runExtraction(text, item.turnId, item.turnNo, item.origin ?? 'live', thread);
|
|
1292
1321
|
const ok =
|
|
1293
1322
|
outcome === 'ok' || outcome === 'skipped-off' || outcome === 'skipped-empty' || outcome === 'rules-mode';
|
|
1294
1323
|
if (ok) {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1324
|
+
if (thread) {
|
|
1325
|
+
removeExtractItemsOfThread(thread.id, thread.turns.map((entry) => entry.id));
|
|
1326
|
+
// Promotion is decided after the facts exist; a whole thread can hold several
|
|
1327
|
+
// corrected turns, so every turn is offered.
|
|
1328
|
+
for (const entry of thread.turns) feedClassifyFromTurn(entry.id);
|
|
1329
|
+
} else {
|
|
1330
|
+
removeExtractItem(item.turnId);
|
|
1331
|
+
// Promotion is decided after the facts exist: a corrected turn's lessons enter
|
|
1332
|
+
// the classification queue here, which is the only live producer it has.
|
|
1333
|
+
feedClassifyFromTurn(item.turnId);
|
|
1334
|
+
}
|
|
1299
1335
|
trace({ event: 'extract-queue', drained: 1, remaining: readExtractQueue().length, outcome });
|
|
1300
1336
|
return true;
|
|
1301
1337
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-soulbridge",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.112",
|
|
4
4
|
"description": "Inject soulbridge context into DSH sessions and feed user turns back as observations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"setup": "node scripts/install-hooks.mjs"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"soulbridge": ">=0.0.
|
|
45
|
+
"soulbridge": ">=0.0.112 <0.1.0"
|
|
46
46
|
}
|
|
47
47
|
}
|