@polycode-projects/the-mechanical-code-talker 6.0.20 → 6.0.21
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.21",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; indexes a repo on request (tmct index) or reads any producer's graph.",
|
|
@@ -29,6 +29,8 @@ export const MANIFEST_SORT_KEY = "manifest";
|
|
|
29
29
|
|
|
30
30
|
/** The bands this repo ships a build pipeline and a loader for. */
|
|
31
31
|
export const FIRST_CLASS_BANDS = Object.freeze([
|
|
32
|
+
"child",
|
|
33
|
+
"conceptnet",
|
|
32
34
|
"wordnet-complete",
|
|
33
35
|
]);
|
|
34
36
|
|
|
@@ -38,6 +40,10 @@ export const FIRST_CLASS_BANDS = Object.freeze([
|
|
|
38
40
|
* `notice` is a repo-relative path to the human-readable attribution file;
|
|
39
41
|
* null when the licence carries no attribution burden. */
|
|
40
42
|
export const BAND_LICENSES = Object.freeze({
|
|
43
|
+
// The child pack is ConceptNet-derived, so it carries ConceptNet's own
|
|
44
|
+
// share-alike terms rather than the maintainer-owned seed script's.
|
|
45
|
+
child: Object.freeze({ license: "CC-BY-SA-4.0", notice: "corpus/child/LICENSE-NOTICE" }),
|
|
46
|
+
conceptnet: Object.freeze({ license: "CC-BY-SA-4.0", notice: "corpus/conceptnet/LICENSE-NOTICE" }),
|
|
41
47
|
"wordnet-complete": Object.freeze({ license: "CC-BY-4.0", notice: "corpus/wordnet/LICENSE-NOTICE" }),
|
|
42
48
|
});
|
|
43
49
|
|
package/src/services/news.mjs
CHANGED
|
@@ -953,47 +953,67 @@ async function ingestResearchArticle(ctx, term, provider, article) {
|
|
|
953
953
|
return { facts: distinct.size, derived };
|
|
954
954
|
}
|
|
955
955
|
|
|
956
|
-
// The
|
|
957
|
-
//
|
|
958
|
-
// mentions in passing ("harbor", "senator"
|
|
959
|
-
// trip on those.
|
|
960
|
-
const
|
|
956
|
+
// The bands this fallback reads. All three are reference content — dictionary
|
|
957
|
+
// senses and everyday commonsense edges, not news entities — so they ground
|
|
958
|
+
// the ordinary nouns a headline mentions in passing ("harbor", "senator",
|
|
959
|
+
// "penguin") without ever costing a KB round trip on those.
|
|
960
|
+
const BAND_TERM_LOOKUP_BANDS = Object.freeze(["child", "conceptnet", "wordnet-complete"]);
|
|
961
961
|
const BAND_TERM_LOOKUP_LIMIT = 50;
|
|
962
962
|
// A local DynamoDB Query has no courtesy throttle and nothing external to
|
|
963
963
|
// protect, but it still has to return: the enrich cycle shares the same wall
|
|
964
964
|
// budget every other phase does, and this runs once per candidate term. A
|
|
965
965
|
// slow or hung Query loses the race and reads as a miss — a timeout is a
|
|
966
966
|
// miss, never a guess — rather than stalling the whole cycle behind it.
|
|
967
|
+
// The bands are read in parallel against one deadline, so the budget buys
|
|
968
|
+
// every band at once: each is a single-partition begins_with read of its own,
|
|
969
|
+
// and the worst case stays one timeout however many bands there are.
|
|
967
970
|
const BAND_TERM_LOOKUP_TIMEOUT_MS = 750;
|
|
968
971
|
|
|
969
|
-
/** `term`'s rows from
|
|
970
|
-
*
|
|
971
|
-
* band
|
|
972
|
-
*
|
|
973
|
-
|
|
974
|
-
* case here. */
|
|
975
|
-
async function groundTermFromBand(ctx, term) {
|
|
976
|
-
const queryBandTerm = ctx.providers?.queryBandTerm;
|
|
977
|
-
if (typeof queryBandTerm !== "function") return [];
|
|
978
|
-
const timeout = new Promise((resolve) => { setTimeout(() => resolve(null), BAND_TERM_LOOKUP_TIMEOUT_MS); });
|
|
972
|
+
/** `term`'s rows from one band, as `{subject, predicate, object, provenance}`
|
|
973
|
+
* triples — or `[]` when the band carries nothing for it or the Query
|
|
974
|
+
* throws. A band that fails is a band with nothing to say, so the others'
|
|
975
|
+
* rows survive it. */
|
|
976
|
+
async function bandTermFacts(queryBandTerm, band, term) {
|
|
979
977
|
let response;
|
|
980
978
|
try {
|
|
981
|
-
response = await
|
|
982
|
-
queryBandTerm({ band: BAND_TERM_LOOKUP_BAND, term, limit: BAND_TERM_LOOKUP_LIMIT }),
|
|
983
|
-
timeout,
|
|
984
|
-
]);
|
|
979
|
+
response = await queryBandTerm({ band, term, limit: BAND_TERM_LOOKUP_LIMIT });
|
|
985
980
|
} catch {
|
|
986
981
|
return [];
|
|
987
982
|
}
|
|
988
|
-
if (!response) return [];
|
|
989
983
|
const facts = [];
|
|
990
|
-
for (const row of response
|
|
984
|
+
for (const row of response?.rows || []) {
|
|
991
985
|
const fact = factFromBandRow(row);
|
|
992
986
|
if (fact) facts.push(fact);
|
|
993
987
|
}
|
|
994
988
|
return facts;
|
|
995
989
|
}
|
|
996
990
|
|
|
991
|
+
/** `term`'s rows from every band, as `{subject, predicate, object,
|
|
992
|
+
* provenance}` triples ready for `appendFacts` — or `[]` when no band
|
|
993
|
+
* carries it. Each band's Query races the SAME deadline, so a band that
|
|
994
|
+
* hangs costs the cycle one timeout rather than one per band, and the bands
|
|
995
|
+
* that answered inside it keep their rows. A fact both a band and the seed
|
|
996
|
+
* already hold arrives with the band's own provenance, so it corroborates
|
|
997
|
+
* rather than duplicating. `ctx.providers.queryBandTerm` is absent on the
|
|
998
|
+
* browser surface and in most tests, so this is a no-op there by
|
|
999
|
+
* construction, not a special case here. */
|
|
1000
|
+
async function groundTermFromBand(ctx, term) {
|
|
1001
|
+
const queryBandTerm = ctx.providers?.queryBandTerm;
|
|
1002
|
+
if (typeof queryBandTerm !== "function") return [];
|
|
1003
|
+
let deadlineTimer;
|
|
1004
|
+
const deadline = new Promise((resolve) => {
|
|
1005
|
+
deadlineTimer = setTimeout(() => resolve(null), BAND_TERM_LOOKUP_TIMEOUT_MS);
|
|
1006
|
+
});
|
|
1007
|
+
try {
|
|
1008
|
+
const perBand = await Promise.all(BAND_TERM_LOOKUP_BANDS.map(
|
|
1009
|
+
(band) => Promise.race([bandTermFacts(queryBandTerm, band, term), deadline]),
|
|
1010
|
+
));
|
|
1011
|
+
return perBand.flatMap((facts) => facts || []);
|
|
1012
|
+
} finally {
|
|
1013
|
+
clearTimeout(deadlineTimer);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
997
1017
|
/** Grounds `term` from `facts` — a band's own rows, already resolved
|
|
998
1018
|
* `{subject, predicate, object, provenance}` triples with no article prose
|
|
999
1019
|
* to walk, so this skips straight to append + syllogise rather than
|
|
@@ -1084,10 +1104,10 @@ export async function enrichTopTerms(ctx, { limit } = {}) {
|
|
|
1084
1104
|
if (aborted) { markTerm(ledger, entry.term, "pending", nowVal); break; }
|
|
1085
1105
|
if (!hit) {
|
|
1086
1106
|
// Every configured KB source came back empty (or none is configured) —
|
|
1087
|
-
// try the
|
|
1088
|
-
//
|
|
1107
|
+
// try the corpus bands before giving up. They're another source the
|
|
1108
|
+
// resolver can reach, not a replacement for the KB walk above: a
|
|
1089
1109
|
// headline's proper nouns still need a KB lookup, but its everyday
|
|
1090
|
-
// vocabulary is often already sitting in
|
|
1110
|
+
// vocabulary is often already sitting in a band.
|
|
1091
1111
|
const bandFacts = await groundTermFromBand(ctx, entry.term);
|
|
1092
1112
|
if (bandFacts.length) {
|
|
1093
1113
|
const res = await ingestBandFacts(ctx, bandFacts);
|