@semiont/jobs 0.5.7 → 0.5.9
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/index.d.ts +10 -0
- package/dist/index.js +156 -185
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +168 -193
- package/dist/worker-main.js.map +1 -1
- package/package.json +8 -8
package/dist/worker-main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTomlConfigLoader, softwareToAgent, baseUrl, getPrimaryMediaType, textExtractionOf, reconcileSelector, didToAgent, getLocaleEnglishName } from '@semiont/core';
|
|
1
|
+
import { createTomlConfigLoader, softwareToAgent, baseUrl, getPrimaryMediaType, textExtractionOf, reconcileSelector, didToAgent, getLocaleEnglishName, isArray, isObject, isString } from '@semiont/core';
|
|
2
2
|
import { deriveStorageUri } from '@semiont/content';
|
|
3
3
|
import { withSpan, SpanKind, recordJobOutcome } from '@semiont/observability';
|
|
4
4
|
import { generateAnnotationId } from '@semiont/event-sourcing';
|
|
@@ -18,7 +18,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
18
18
|
var __getProtoOf = Object.getPrototypeOf;
|
|
19
19
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
20
20
|
var __commonJS = (cb, mod) => function __require() {
|
|
21
|
-
|
|
21
|
+
try {
|
|
22
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw mod = 0, e;
|
|
25
|
+
}
|
|
22
26
|
};
|
|
23
27
|
var __copyProps = (to, from, except, desc) => {
|
|
24
28
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -3761,9 +3765,9 @@ var require_mapOneOrManyArgs = __commonJS({
|
|
|
3761
3765
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3762
3766
|
exports.mapOneOrManyArgs = void 0;
|
|
3763
3767
|
var map_1 = require_map();
|
|
3764
|
-
var
|
|
3768
|
+
var isArray2 = Array.isArray;
|
|
3765
3769
|
function callOrApply(fn, args) {
|
|
3766
|
-
return
|
|
3770
|
+
return isArray2(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args);
|
|
3767
3771
|
}
|
|
3768
3772
|
function mapOneOrManyArgs(fn) {
|
|
3769
3773
|
return map_1.map(function(args) {
|
|
@@ -3908,14 +3912,14 @@ var require_argsArgArrayOrObject = __commonJS({
|
|
|
3908
3912
|
"../../node_modules/rxjs/dist/cjs/internal/util/argsArgArrayOrObject.js"(exports) {
|
|
3909
3913
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3910
3914
|
exports.argsArgArrayOrObject = void 0;
|
|
3911
|
-
var
|
|
3915
|
+
var isArray2 = Array.isArray;
|
|
3912
3916
|
var getPrototypeOf = Object.getPrototypeOf;
|
|
3913
3917
|
var objectProto = Object.prototype;
|
|
3914
3918
|
var getKeys = Object.keys;
|
|
3915
3919
|
function argsArgArrayOrObject(args) {
|
|
3916
3920
|
if (args.length === 1) {
|
|
3917
3921
|
var first_1 = args[0];
|
|
3918
|
-
if (
|
|
3922
|
+
if (isArray2(first_1)) {
|
|
3919
3923
|
return { args: first_1, keys: null };
|
|
3920
3924
|
}
|
|
3921
3925
|
if (isPOJO(first_1)) {
|
|
@@ -4663,9 +4667,9 @@ var require_argsOrArgArray = __commonJS({
|
|
|
4663
4667
|
"../../node_modules/rxjs/dist/cjs/internal/util/argsOrArgArray.js"(exports) {
|
|
4664
4668
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4665
4669
|
exports.argsOrArgArray = void 0;
|
|
4666
|
-
var
|
|
4670
|
+
var isArray2 = Array.isArray;
|
|
4667
4671
|
function argsOrArgArray(args) {
|
|
4668
|
-
return args.length === 1 &&
|
|
4672
|
+
return args.length === 1 && isArray2(args[0]) ? args[0] : args;
|
|
4669
4673
|
}
|
|
4670
4674
|
exports.argsOrArgArray = argsOrArgArray;
|
|
4671
4675
|
}
|
|
@@ -9583,197 +9587,144 @@ Example format:
|
|
|
9583
9587
|
return prompt;
|
|
9584
9588
|
}
|
|
9585
9589
|
};
|
|
9586
|
-
function
|
|
9587
|
-
let
|
|
9588
|
-
if (cleaned.startsWith("```")) {
|
|
9589
|
-
cleaned = cleaned.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "");
|
|
9590
|
-
}
|
|
9590
|
+
function parseJsonArray(response, motivation) {
|
|
9591
|
+
let parsed;
|
|
9591
9592
|
try {
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
const objects = [];
|
|
9602
|
-
let depth = 0;
|
|
9603
|
-
let objStart = -1;
|
|
9604
|
-
let inString = false;
|
|
9605
|
-
let escape = false;
|
|
9606
|
-
for (let i = 0; i < inner.length; i++) {
|
|
9607
|
-
const ch = inner[i];
|
|
9608
|
-
if (escape) {
|
|
9609
|
-
escape = false;
|
|
9610
|
-
continue;
|
|
9611
|
-
}
|
|
9612
|
-
if (ch === "\\") {
|
|
9613
|
-
escape = true;
|
|
9614
|
-
continue;
|
|
9615
|
-
}
|
|
9616
|
-
if (ch === '"') {
|
|
9617
|
-
inString = !inString;
|
|
9618
|
-
continue;
|
|
9619
|
-
}
|
|
9620
|
-
if (inString) continue;
|
|
9621
|
-
if (ch === "{") {
|
|
9622
|
-
if (depth === 0) objStart = i;
|
|
9623
|
-
depth++;
|
|
9624
|
-
} else if (ch === "}") {
|
|
9625
|
-
depth--;
|
|
9626
|
-
if (depth === 0 && objStart !== -1) {
|
|
9627
|
-
try {
|
|
9628
|
-
objects.push(JSON.parse(inner.slice(objStart, i + 1)));
|
|
9629
|
-
} catch {
|
|
9630
|
-
}
|
|
9631
|
-
objStart = -1;
|
|
9632
|
-
}
|
|
9633
|
-
}
|
|
9593
|
+
parsed = JSON.parse(response.trim());
|
|
9594
|
+
} catch (error) {
|
|
9595
|
+
console.error(`[MotivationParsers] Failed to parse AI ${motivation} response:`, error);
|
|
9596
|
+
console.error("Raw response:", response);
|
|
9597
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
9598
|
+
}
|
|
9599
|
+
if (!Array.isArray(parsed)) {
|
|
9600
|
+
console.error(`[MotivationParsers] Expected a JSON array for ${motivation} detection, got ${typeof parsed}:`, response);
|
|
9601
|
+
throw new Error(`Expected a JSON array for ${motivation} detection, got ${typeof parsed}`);
|
|
9634
9602
|
}
|
|
9635
|
-
return
|
|
9603
|
+
return parsed;
|
|
9636
9604
|
}
|
|
9637
9605
|
var MotivationParsers = class {
|
|
9638
9606
|
/**
|
|
9639
9607
|
* Parse and validate AI response for comment detection
|
|
9640
9608
|
*
|
|
9641
|
-
* @param response - Raw AI response
|
|
9609
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9642
9610
|
* @param content - Original content to validate offsets against
|
|
9643
9611
|
* @returns Array of validated comment matches
|
|
9612
|
+
* @throws if the response is not a parseable JSON array
|
|
9644
9613
|
*/
|
|
9645
9614
|
static parseComments(response, content) {
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
continue;
|
|
9662
|
-
}
|
|
9663
|
-
logAnchorMethod("comment", comment.exact, reconciled.anchorMethod);
|
|
9664
|
-
validatedComments.push({
|
|
9665
|
-
comment: comment.comment,
|
|
9666
|
-
exact: reconciled.exact,
|
|
9667
|
-
start: reconciled.start,
|
|
9668
|
-
end: reconciled.end,
|
|
9669
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9670
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9671
|
-
});
|
|
9615
|
+
const parsed = parseJsonArray(response, "comment");
|
|
9616
|
+
const valid = parsed.filter(
|
|
9617
|
+
(c) => isObject(c) && isString(c.exact) && isString(c.comment) && c.comment.trim().length > 0
|
|
9618
|
+
);
|
|
9619
|
+
console.log(`[MotivationParsers] Parsed ${valid.length} valid comments from ${parsed.length} total`);
|
|
9620
|
+
const validatedComments = [];
|
|
9621
|
+
for (const comment of valid) {
|
|
9622
|
+
const reconciled = reconcileSelector(content, {
|
|
9623
|
+
exact: comment.exact,
|
|
9624
|
+
...typeof comment.prefix === "string" ? { prefix: comment.prefix } : {},
|
|
9625
|
+
...typeof comment.suffix === "string" ? { suffix: comment.suffix } : {}
|
|
9626
|
+
});
|
|
9627
|
+
if (!reconciled) {
|
|
9628
|
+
console.warn(`[MotivationParsers] Dropped hallucinated comment "${comment.exact}"`);
|
|
9629
|
+
continue;
|
|
9672
9630
|
}
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9631
|
+
logAnchorMethod("comment", comment.exact, reconciled.anchorMethod);
|
|
9632
|
+
validatedComments.push({
|
|
9633
|
+
comment: comment.comment,
|
|
9634
|
+
exact: reconciled.exact,
|
|
9635
|
+
start: reconciled.start,
|
|
9636
|
+
end: reconciled.end,
|
|
9637
|
+
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9638
|
+
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9639
|
+
});
|
|
9677
9640
|
}
|
|
9641
|
+
return validatedComments;
|
|
9678
9642
|
}
|
|
9679
9643
|
/**
|
|
9680
9644
|
* Parse and validate AI response for highlight detection
|
|
9681
9645
|
*
|
|
9682
|
-
* @param response - Raw AI response
|
|
9646
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9683
9647
|
* @param content - Original content to validate offsets against
|
|
9684
9648
|
* @returns Array of validated highlight matches
|
|
9649
|
+
* @throws if the response is not a parseable JSON array
|
|
9685
9650
|
*/
|
|
9686
9651
|
static parseHighlights(response, content) {
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
continue;
|
|
9702
|
-
}
|
|
9703
|
-
logAnchorMethod("highlight", highlight.exact, reconciled.anchorMethod);
|
|
9704
|
-
validatedHighlights.push({
|
|
9705
|
-
exact: reconciled.exact,
|
|
9706
|
-
start: reconciled.start,
|
|
9707
|
-
end: reconciled.end,
|
|
9708
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9709
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9710
|
-
});
|
|
9652
|
+
const parsed = parseJsonArray(response, "highlight");
|
|
9653
|
+
const highlights = parsed.filter(
|
|
9654
|
+
(h) => isObject(h) && isString(h.exact)
|
|
9655
|
+
);
|
|
9656
|
+
const validatedHighlights = [];
|
|
9657
|
+
for (const highlight of highlights) {
|
|
9658
|
+
const reconciled = reconcileSelector(content, {
|
|
9659
|
+
exact: highlight.exact,
|
|
9660
|
+
...typeof highlight.prefix === "string" ? { prefix: highlight.prefix } : {},
|
|
9661
|
+
...typeof highlight.suffix === "string" ? { suffix: highlight.suffix } : {}
|
|
9662
|
+
});
|
|
9663
|
+
if (!reconciled) {
|
|
9664
|
+
console.warn(`[MotivationParsers] Dropped hallucinated highlight "${highlight.exact}"`);
|
|
9665
|
+
continue;
|
|
9711
9666
|
}
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9667
|
+
logAnchorMethod("highlight", highlight.exact, reconciled.anchorMethod);
|
|
9668
|
+
validatedHighlights.push({
|
|
9669
|
+
exact: reconciled.exact,
|
|
9670
|
+
start: reconciled.start,
|
|
9671
|
+
end: reconciled.end,
|
|
9672
|
+
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9673
|
+
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9674
|
+
});
|
|
9717
9675
|
}
|
|
9676
|
+
return validatedHighlights;
|
|
9718
9677
|
}
|
|
9719
9678
|
/**
|
|
9720
9679
|
* Parse and validate AI response for assessment detection
|
|
9721
9680
|
*
|
|
9722
|
-
* @param response - Raw AI response
|
|
9681
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9723
9682
|
* @param content - Original content to validate offsets against
|
|
9724
9683
|
* @returns Array of validated assessment matches
|
|
9684
|
+
* @throws if the response is not a parseable JSON array
|
|
9725
9685
|
*/
|
|
9726
9686
|
static parseAssessments(response, content) {
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
continue;
|
|
9742
|
-
}
|
|
9743
|
-
logAnchorMethod("assessment", assessment.exact, reconciled.anchorMethod);
|
|
9744
|
-
validatedAssessments.push({
|
|
9745
|
-
assessment: assessment.assessment,
|
|
9746
|
-
exact: reconciled.exact,
|
|
9747
|
-
start: reconciled.start,
|
|
9748
|
-
end: reconciled.end,
|
|
9749
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9750
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9751
|
-
});
|
|
9687
|
+
const parsed = parseJsonArray(response, "assessment");
|
|
9688
|
+
const assessments = parsed.filter(
|
|
9689
|
+
(a) => isObject(a) && isString(a.exact) && isString(a.assessment)
|
|
9690
|
+
);
|
|
9691
|
+
const validatedAssessments = [];
|
|
9692
|
+
for (const assessment of assessments) {
|
|
9693
|
+
const reconciled = reconcileSelector(content, {
|
|
9694
|
+
exact: assessment.exact,
|
|
9695
|
+
...typeof assessment.prefix === "string" ? { prefix: assessment.prefix } : {},
|
|
9696
|
+
...typeof assessment.suffix === "string" ? { suffix: assessment.suffix } : {}
|
|
9697
|
+
});
|
|
9698
|
+
if (!reconciled) {
|
|
9699
|
+
console.warn(`[MotivationParsers] Dropped hallucinated assessment "${assessment.exact}"`);
|
|
9700
|
+
continue;
|
|
9752
9701
|
}
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9702
|
+
logAnchorMethod("assessment", assessment.exact, reconciled.anchorMethod);
|
|
9703
|
+
validatedAssessments.push({
|
|
9704
|
+
assessment: assessment.assessment,
|
|
9705
|
+
exact: reconciled.exact,
|
|
9706
|
+
start: reconciled.start,
|
|
9707
|
+
end: reconciled.end,
|
|
9708
|
+
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9709
|
+
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9710
|
+
});
|
|
9758
9711
|
}
|
|
9712
|
+
return validatedAssessments;
|
|
9759
9713
|
}
|
|
9760
9714
|
/**
|
|
9761
9715
|
* Parse the LLM's tag response into raw, pre-reconciliation tag inputs.
|
|
9762
9716
|
* Reconciliation happens in `validateTagOffsets`, which adds `start`/`end`
|
|
9763
9717
|
* by anchoring `exact` against the source content.
|
|
9718
|
+
*
|
|
9719
|
+
* @throws if the response is not a parseable JSON array
|
|
9764
9720
|
*/
|
|
9765
9721
|
static parseTags(response) {
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
return valid;
|
|
9773
|
-
} catch (error) {
|
|
9774
|
-
console.error("[MotivationParsers] Failed to parse AI tag response:", error);
|
|
9775
|
-
return [];
|
|
9776
|
-
}
|
|
9722
|
+
const parsed = parseJsonArray(response, "tag");
|
|
9723
|
+
const valid = parsed.filter(
|
|
9724
|
+
(t) => isObject(t) && isString(t.exact) && t.exact.trim().length > 0
|
|
9725
|
+
);
|
|
9726
|
+
console.log(`[MotivationParsers] Parsed ${valid.length} valid tags from ${parsed.length} total`);
|
|
9727
|
+
return valid;
|
|
9777
9728
|
}
|
|
9778
9729
|
/**
|
|
9779
9730
|
* Anchor raw tag inputs against source content and add category.
|
|
@@ -9810,6 +9761,11 @@ function logAnchorMethod(motivation, exact, anchorMethod) {
|
|
|
9810
9761
|
}
|
|
9811
9762
|
|
|
9812
9763
|
// src/workers/annotation-detection.ts
|
|
9764
|
+
function assertNotTruncated(response, motivation) {
|
|
9765
|
+
if (response.stopReason === "max_tokens") {
|
|
9766
|
+
throw new Error(`${motivation} detection response truncated (max_tokens) \u2014 increase max_tokens or reduce resource size; failing the job rather than under-reporting annotations.`);
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9813
9769
|
var AnnotationDetection = class {
|
|
9814
9770
|
/**
|
|
9815
9771
|
* Detect comments in content.
|
|
@@ -9821,8 +9777,9 @@ var AnnotationDetection = class {
|
|
|
9821
9777
|
*/
|
|
9822
9778
|
static async detectComments(content, client, instructions, tone, density, language, sourceLanguage) {
|
|
9823
9779
|
const prompt = MotivationPrompts.buildCommentPrompt(content, instructions, tone, density, language, sourceLanguage);
|
|
9824
|
-
const response = await client.
|
|
9825
|
-
|
|
9780
|
+
const response = await client.generateTextWithMetadata(prompt, 3e3, 0.4, { format: "json" });
|
|
9781
|
+
assertNotTruncated(response, "comment");
|
|
9782
|
+
return MotivationParsers.parseComments(response.text, content);
|
|
9826
9783
|
}
|
|
9827
9784
|
/**
|
|
9828
9785
|
* Detect highlights in content.
|
|
@@ -9833,8 +9790,9 @@ var AnnotationDetection = class {
|
|
|
9833
9790
|
*/
|
|
9834
9791
|
static async detectHighlights(content, client, instructions, density, sourceLanguage) {
|
|
9835
9792
|
const prompt = MotivationPrompts.buildHighlightPrompt(content, instructions, density, sourceLanguage);
|
|
9836
|
-
const response = await client.
|
|
9837
|
-
|
|
9793
|
+
const response = await client.generateTextWithMetadata(prompt, 2e3, 0.3, { format: "json" });
|
|
9794
|
+
assertNotTruncated(response, "highlight");
|
|
9795
|
+
return MotivationParsers.parseHighlights(response.text, content);
|
|
9838
9796
|
}
|
|
9839
9797
|
/**
|
|
9840
9798
|
* Detect assessments in content.
|
|
@@ -9845,8 +9803,9 @@ var AnnotationDetection = class {
|
|
|
9845
9803
|
*/
|
|
9846
9804
|
static async detectAssessments(content, client, instructions, tone, density, language, sourceLanguage) {
|
|
9847
9805
|
const prompt = MotivationPrompts.buildAssessmentPrompt(content, instructions, tone, density, language, sourceLanguage);
|
|
9848
|
-
const response = await client.
|
|
9849
|
-
|
|
9806
|
+
const response = await client.generateTextWithMetadata(prompt, 3e3, 0.3, { format: "json" });
|
|
9807
|
+
assertNotTruncated(response, "assessment");
|
|
9808
|
+
return MotivationParsers.parseAssessments(response.text, content);
|
|
9850
9809
|
}
|
|
9851
9810
|
/**
|
|
9852
9811
|
* Detect tags in content for a specific category.
|
|
@@ -9875,8 +9834,9 @@ var AnnotationDetection = class {
|
|
|
9875
9834
|
categoryInfo.examples,
|
|
9876
9835
|
sourceLanguage
|
|
9877
9836
|
);
|
|
9878
|
-
const response = await client.
|
|
9879
|
-
|
|
9837
|
+
const response = await client.generateTextWithMetadata(prompt, 4e3, 0.2, { format: "json" });
|
|
9838
|
+
assertNotTruncated(response, "tag");
|
|
9839
|
+
const parsedTags = MotivationParsers.parseTags(response.text);
|
|
9880
9840
|
return MotivationParsers.validateTagOffsets(parsedTags, content, category);
|
|
9881
9841
|
}
|
|
9882
9842
|
};
|
|
@@ -9945,36 +9905,42 @@ Example output:
|
|
|
9945
9905
|
{ format: "json" }
|
|
9946
9906
|
);
|
|
9947
9907
|
logger2.debug("Got entity extraction response", { responseLength: response.text.length });
|
|
9908
|
+
if (response.stopReason === "max_tokens") {
|
|
9909
|
+
const errorMsg = "Entity extraction response truncated (max_tokens) \u2014 increase max_tokens or reduce resource size; failing the job rather than dropping annotations.";
|
|
9910
|
+
logger2.error(errorMsg, { responseLength: response.text.length });
|
|
9911
|
+
throw new Error(errorMsg);
|
|
9912
|
+
}
|
|
9913
|
+
let entities;
|
|
9948
9914
|
try {
|
|
9949
|
-
|
|
9950
|
-
if (jsonStr.startsWith("```")) {
|
|
9951
|
-
jsonStr = jsonStr.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
|
|
9952
|
-
}
|
|
9953
|
-
const entities = JSON.parse(jsonStr);
|
|
9954
|
-
logger2.debug("Parsed entities from AI response", { count: entities.length });
|
|
9955
|
-
if (response.stopReason === "max_tokens") {
|
|
9956
|
-
const errorMsg = `AI response truncated: Found ${entities.length} entities but response hit max_tokens limit. Increase max_tokens or reduce resource size.`;
|
|
9957
|
-
logger2.error(errorMsg);
|
|
9958
|
-
throw new Error(errorMsg);
|
|
9959
|
-
}
|
|
9960
|
-
return entities.filter((e) => {
|
|
9961
|
-
const ok = e && typeof e === "object" && typeof e.exact === "string" && typeof e.entityType === "string";
|
|
9962
|
-
if (!ok) {
|
|
9963
|
-
logger2.debug("Dropped malformed LLM entity", { entity: e });
|
|
9964
|
-
}
|
|
9965
|
-
return ok;
|
|
9966
|
-
}).map((entity) => ({
|
|
9967
|
-
exact: entity.exact,
|
|
9968
|
-
entityType: entity.entityType,
|
|
9969
|
-
...typeof entity.prefix === "string" ? { prefix: entity.prefix } : {},
|
|
9970
|
-
...typeof entity.suffix === "string" ? { suffix: entity.suffix } : {}
|
|
9971
|
-
}));
|
|
9915
|
+
entities = JSON.parse(response.text.trim());
|
|
9972
9916
|
} catch (error) {
|
|
9973
9917
|
logger2.error("Failed to parse entity extraction response", {
|
|
9974
|
-
error: error instanceof Error ? error.message : String(error)
|
|
9918
|
+
error: error instanceof Error ? error.message : String(error),
|
|
9919
|
+
response: response.text.slice(0, 500)
|
|
9920
|
+
});
|
|
9921
|
+
throw new Error("Failed to parse entity extraction response", {
|
|
9922
|
+
cause: error instanceof Error ? error : new Error(String(error))
|
|
9975
9923
|
});
|
|
9976
|
-
return [];
|
|
9977
9924
|
}
|
|
9925
|
+
if (!isArray(entities)) {
|
|
9926
|
+
logger2.error("Failed to parse entity extraction response: expected a JSON array", {
|
|
9927
|
+
response: response.text.slice(0, 500)
|
|
9928
|
+
});
|
|
9929
|
+
throw new Error("Failed to parse entity extraction response: expected a JSON array");
|
|
9930
|
+
}
|
|
9931
|
+
logger2.debug("Parsed entities from AI response", { count: entities.length });
|
|
9932
|
+
return entities.filter((e) => {
|
|
9933
|
+
const ok = isObject(e) && isString(e.exact) && isString(e.entityType);
|
|
9934
|
+
if (!ok) {
|
|
9935
|
+
logger2.debug("Dropped malformed LLM entity", { entity: e });
|
|
9936
|
+
}
|
|
9937
|
+
return ok;
|
|
9938
|
+
}).map((entity) => ({
|
|
9939
|
+
exact: entity.exact,
|
|
9940
|
+
entityType: entity.entityType,
|
|
9941
|
+
...isString(entity.prefix) ? { prefix: entity.prefix } : {},
|
|
9942
|
+
...isString(entity.suffix) ? { suffix: entity.suffix } : {}
|
|
9943
|
+
}));
|
|
9978
9944
|
}
|
|
9979
9945
|
function getLanguageName(locale) {
|
|
9980
9946
|
return getLocaleEnglishName(locale) || locale;
|
|
@@ -10056,10 +10022,19 @@ Knowledge graph context:
|
|
|
10056
10022
|
${parts.join("\n")}`;
|
|
10057
10023
|
}
|
|
10058
10024
|
}
|
|
10025
|
+
let semanticContextSection = "";
|
|
10026
|
+
const similar = context?.semanticContext?.similar ?? [];
|
|
10027
|
+
if (similar.length > 0) {
|
|
10028
|
+
const lines = [...similar].sort((a, b) => b.score - a.score).slice(0, 3).map((m) => `- (${m.score.toFixed(2)}) ${m.text.slice(0, 240)}`);
|
|
10029
|
+
semanticContextSection = `
|
|
10030
|
+
|
|
10031
|
+
Related passages from the knowledge base:
|
|
10032
|
+
${lines.join("\n")}`;
|
|
10033
|
+
}
|
|
10059
10034
|
const structureGuidance = finalMaxTokens >= 1e3 ? "organized into titled sections (## Section) with well-structured paragraphs" : "organized into well-structured paragraphs";
|
|
10060
10035
|
const prompt = `Generate a concise, informative resource about "${topic}".
|
|
10061
10036
|
${entityTypes.length > 0 ? `Focus on these entity types: ${entityTypes.join(", ")}.` : ""}
|
|
10062
|
-
${userPrompt ? `Additional context: ${userPrompt}` : ""}${annotationSection}${contextSection}${graphContextSection}${sourceLanguageInstruction}${languageInstruction}
|
|
10037
|
+
${userPrompt ? `Additional context: ${userPrompt}` : ""}${annotationSection}${contextSection}${graphContextSection}${semanticContextSection}${sourceLanguageInstruction}${languageInstruction}
|
|
10063
10038
|
|
|
10064
10039
|
Requirements:
|
|
10065
10040
|
- Start with a clear heading (# Title)
|