@semiont/jobs 0.5.8 → 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 +146 -184
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +153 -191
- 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';
|
|
@@ -3765,9 +3765,9 @@ var require_mapOneOrManyArgs = __commonJS({
|
|
|
3765
3765
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3766
3766
|
exports.mapOneOrManyArgs = void 0;
|
|
3767
3767
|
var map_1 = require_map();
|
|
3768
|
-
var
|
|
3768
|
+
var isArray2 = Array.isArray;
|
|
3769
3769
|
function callOrApply(fn, args) {
|
|
3770
|
-
return
|
|
3770
|
+
return isArray2(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args);
|
|
3771
3771
|
}
|
|
3772
3772
|
function mapOneOrManyArgs(fn) {
|
|
3773
3773
|
return map_1.map(function(args) {
|
|
@@ -3912,14 +3912,14 @@ var require_argsArgArrayOrObject = __commonJS({
|
|
|
3912
3912
|
"../../node_modules/rxjs/dist/cjs/internal/util/argsArgArrayOrObject.js"(exports) {
|
|
3913
3913
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3914
3914
|
exports.argsArgArrayOrObject = void 0;
|
|
3915
|
-
var
|
|
3915
|
+
var isArray2 = Array.isArray;
|
|
3916
3916
|
var getPrototypeOf = Object.getPrototypeOf;
|
|
3917
3917
|
var objectProto = Object.prototype;
|
|
3918
3918
|
var getKeys = Object.keys;
|
|
3919
3919
|
function argsArgArrayOrObject(args) {
|
|
3920
3920
|
if (args.length === 1) {
|
|
3921
3921
|
var first_1 = args[0];
|
|
3922
|
-
if (
|
|
3922
|
+
if (isArray2(first_1)) {
|
|
3923
3923
|
return { args: first_1, keys: null };
|
|
3924
3924
|
}
|
|
3925
3925
|
if (isPOJO(first_1)) {
|
|
@@ -4667,9 +4667,9 @@ var require_argsOrArgArray = __commonJS({
|
|
|
4667
4667
|
"../../node_modules/rxjs/dist/cjs/internal/util/argsOrArgArray.js"(exports) {
|
|
4668
4668
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4669
4669
|
exports.argsOrArgArray = void 0;
|
|
4670
|
-
var
|
|
4670
|
+
var isArray2 = Array.isArray;
|
|
4671
4671
|
function argsOrArgArray(args) {
|
|
4672
|
-
return args.length === 1 &&
|
|
4672
|
+
return args.length === 1 && isArray2(args[0]) ? args[0] : args;
|
|
4673
4673
|
}
|
|
4674
4674
|
exports.argsOrArgArray = argsOrArgArray;
|
|
4675
4675
|
}
|
|
@@ -9587,197 +9587,144 @@ Example format:
|
|
|
9587
9587
|
return prompt;
|
|
9588
9588
|
}
|
|
9589
9589
|
};
|
|
9590
|
-
function
|
|
9591
|
-
let
|
|
9592
|
-
if (cleaned.startsWith("```")) {
|
|
9593
|
-
cleaned = cleaned.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "");
|
|
9594
|
-
}
|
|
9590
|
+
function parseJsonArray(response, motivation) {
|
|
9591
|
+
let parsed;
|
|
9595
9592
|
try {
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
if (start === -1) return [];
|
|
9602
|
-
const endBracket = cleaned.lastIndexOf("]");
|
|
9603
|
-
const end = endBracket > start ? endBracket : cleaned.length;
|
|
9604
|
-
const inner = cleaned.slice(start + 1, end);
|
|
9605
|
-
const objects = [];
|
|
9606
|
-
let depth = 0;
|
|
9607
|
-
let objStart = -1;
|
|
9608
|
-
let inString = false;
|
|
9609
|
-
let escape = false;
|
|
9610
|
-
for (let i = 0; i < inner.length; i++) {
|
|
9611
|
-
const ch = inner[i];
|
|
9612
|
-
if (escape) {
|
|
9613
|
-
escape = false;
|
|
9614
|
-
continue;
|
|
9615
|
-
}
|
|
9616
|
-
if (ch === "\\") {
|
|
9617
|
-
escape = true;
|
|
9618
|
-
continue;
|
|
9619
|
-
}
|
|
9620
|
-
if (ch === '"') {
|
|
9621
|
-
inString = !inString;
|
|
9622
|
-
continue;
|
|
9623
|
-
}
|
|
9624
|
-
if (inString) continue;
|
|
9625
|
-
if (ch === "{") {
|
|
9626
|
-
if (depth === 0) objStart = i;
|
|
9627
|
-
depth++;
|
|
9628
|
-
} else if (ch === "}") {
|
|
9629
|
-
depth--;
|
|
9630
|
-
if (depth === 0 && objStart !== -1) {
|
|
9631
|
-
try {
|
|
9632
|
-
objects.push(JSON.parse(inner.slice(objStart, i + 1)));
|
|
9633
|
-
} catch {
|
|
9634
|
-
}
|
|
9635
|
-
objStart = -1;
|
|
9636
|
-
}
|
|
9637
|
-
}
|
|
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));
|
|
9638
9598
|
}
|
|
9639
|
-
|
|
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}`);
|
|
9602
|
+
}
|
|
9603
|
+
return parsed;
|
|
9640
9604
|
}
|
|
9641
9605
|
var MotivationParsers = class {
|
|
9642
9606
|
/**
|
|
9643
9607
|
* Parse and validate AI response for comment detection
|
|
9644
9608
|
*
|
|
9645
|
-
* @param response - Raw AI response
|
|
9609
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9646
9610
|
* @param content - Original content to validate offsets against
|
|
9647
9611
|
* @returns Array of validated comment matches
|
|
9612
|
+
* @throws if the response is not a parseable JSON array
|
|
9648
9613
|
*/
|
|
9649
9614
|
static parseComments(response, content) {
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
continue;
|
|
9666
|
-
}
|
|
9667
|
-
logAnchorMethod("comment", comment.exact, reconciled.anchorMethod);
|
|
9668
|
-
validatedComments.push({
|
|
9669
|
-
comment: comment.comment,
|
|
9670
|
-
exact: reconciled.exact,
|
|
9671
|
-
start: reconciled.start,
|
|
9672
|
-
end: reconciled.end,
|
|
9673
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9674
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9675
|
-
});
|
|
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;
|
|
9676
9630
|
}
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
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
|
+
});
|
|
9681
9640
|
}
|
|
9641
|
+
return validatedComments;
|
|
9682
9642
|
}
|
|
9683
9643
|
/**
|
|
9684
9644
|
* Parse and validate AI response for highlight detection
|
|
9685
9645
|
*
|
|
9686
|
-
* @param response - Raw AI response
|
|
9646
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9687
9647
|
* @param content - Original content to validate offsets against
|
|
9688
9648
|
* @returns Array of validated highlight matches
|
|
9649
|
+
* @throws if the response is not a parseable JSON array
|
|
9689
9650
|
*/
|
|
9690
9651
|
static parseHighlights(response, content) {
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
continue;
|
|
9706
|
-
}
|
|
9707
|
-
logAnchorMethod("highlight", highlight.exact, reconciled.anchorMethod);
|
|
9708
|
-
validatedHighlights.push({
|
|
9709
|
-
exact: reconciled.exact,
|
|
9710
|
-
start: reconciled.start,
|
|
9711
|
-
end: reconciled.end,
|
|
9712
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9713
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9714
|
-
});
|
|
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;
|
|
9715
9666
|
}
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
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
|
+
});
|
|
9721
9675
|
}
|
|
9676
|
+
return validatedHighlights;
|
|
9722
9677
|
}
|
|
9723
9678
|
/**
|
|
9724
9679
|
* Parse and validate AI response for assessment detection
|
|
9725
9680
|
*
|
|
9726
|
-
* @param response - Raw AI response
|
|
9681
|
+
* @param response - Raw AI response text (a JSON array)
|
|
9727
9682
|
* @param content - Original content to validate offsets against
|
|
9728
9683
|
* @returns Array of validated assessment matches
|
|
9684
|
+
* @throws if the response is not a parseable JSON array
|
|
9729
9685
|
*/
|
|
9730
9686
|
static parseAssessments(response, content) {
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
continue;
|
|
9746
|
-
}
|
|
9747
|
-
logAnchorMethod("assessment", assessment.exact, reconciled.anchorMethod);
|
|
9748
|
-
validatedAssessments.push({
|
|
9749
|
-
assessment: assessment.assessment,
|
|
9750
|
-
exact: reconciled.exact,
|
|
9751
|
-
start: reconciled.start,
|
|
9752
|
-
end: reconciled.end,
|
|
9753
|
-
...reconciled.prefix !== void 0 ? { prefix: reconciled.prefix } : {},
|
|
9754
|
-
...reconciled.suffix !== void 0 ? { suffix: reconciled.suffix } : {}
|
|
9755
|
-
});
|
|
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;
|
|
9756
9701
|
}
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
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
|
+
});
|
|
9762
9711
|
}
|
|
9712
|
+
return validatedAssessments;
|
|
9763
9713
|
}
|
|
9764
9714
|
/**
|
|
9765
9715
|
* Parse the LLM's tag response into raw, pre-reconciliation tag inputs.
|
|
9766
9716
|
* Reconciliation happens in `validateTagOffsets`, which adds `start`/`end`
|
|
9767
9717
|
* by anchoring `exact` against the source content.
|
|
9718
|
+
*
|
|
9719
|
+
* @throws if the response is not a parseable JSON array
|
|
9768
9720
|
*/
|
|
9769
9721
|
static parseTags(response) {
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
return valid;
|
|
9777
|
-
} catch (error) {
|
|
9778
|
-
console.error("[MotivationParsers] Failed to parse AI tag response:", error);
|
|
9779
|
-
return [];
|
|
9780
|
-
}
|
|
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;
|
|
9781
9728
|
}
|
|
9782
9729
|
/**
|
|
9783
9730
|
* Anchor raw tag inputs against source content and add category.
|
|
@@ -9814,6 +9761,11 @@ function logAnchorMethod(motivation, exact, anchorMethod) {
|
|
|
9814
9761
|
}
|
|
9815
9762
|
|
|
9816
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
|
+
}
|
|
9817
9769
|
var AnnotationDetection = class {
|
|
9818
9770
|
/**
|
|
9819
9771
|
* Detect comments in content.
|
|
@@ -9825,8 +9777,9 @@ var AnnotationDetection = class {
|
|
|
9825
9777
|
*/
|
|
9826
9778
|
static async detectComments(content, client, instructions, tone, density, language, sourceLanguage) {
|
|
9827
9779
|
const prompt = MotivationPrompts.buildCommentPrompt(content, instructions, tone, density, language, sourceLanguage);
|
|
9828
|
-
const response = await client.
|
|
9829
|
-
|
|
9780
|
+
const response = await client.generateTextWithMetadata(prompt, 3e3, 0.4, { format: "json" });
|
|
9781
|
+
assertNotTruncated(response, "comment");
|
|
9782
|
+
return MotivationParsers.parseComments(response.text, content);
|
|
9830
9783
|
}
|
|
9831
9784
|
/**
|
|
9832
9785
|
* Detect highlights in content.
|
|
@@ -9837,8 +9790,9 @@ var AnnotationDetection = class {
|
|
|
9837
9790
|
*/
|
|
9838
9791
|
static async detectHighlights(content, client, instructions, density, sourceLanguage) {
|
|
9839
9792
|
const prompt = MotivationPrompts.buildHighlightPrompt(content, instructions, density, sourceLanguage);
|
|
9840
|
-
const response = await client.
|
|
9841
|
-
|
|
9793
|
+
const response = await client.generateTextWithMetadata(prompt, 2e3, 0.3, { format: "json" });
|
|
9794
|
+
assertNotTruncated(response, "highlight");
|
|
9795
|
+
return MotivationParsers.parseHighlights(response.text, content);
|
|
9842
9796
|
}
|
|
9843
9797
|
/**
|
|
9844
9798
|
* Detect assessments in content.
|
|
@@ -9849,8 +9803,9 @@ var AnnotationDetection = class {
|
|
|
9849
9803
|
*/
|
|
9850
9804
|
static async detectAssessments(content, client, instructions, tone, density, language, sourceLanguage) {
|
|
9851
9805
|
const prompt = MotivationPrompts.buildAssessmentPrompt(content, instructions, tone, density, language, sourceLanguage);
|
|
9852
|
-
const response = await client.
|
|
9853
|
-
|
|
9806
|
+
const response = await client.generateTextWithMetadata(prompt, 3e3, 0.3, { format: "json" });
|
|
9807
|
+
assertNotTruncated(response, "assessment");
|
|
9808
|
+
return MotivationParsers.parseAssessments(response.text, content);
|
|
9854
9809
|
}
|
|
9855
9810
|
/**
|
|
9856
9811
|
* Detect tags in content for a specific category.
|
|
@@ -9879,8 +9834,9 @@ var AnnotationDetection = class {
|
|
|
9879
9834
|
categoryInfo.examples,
|
|
9880
9835
|
sourceLanguage
|
|
9881
9836
|
);
|
|
9882
|
-
const response = await client.
|
|
9883
|
-
|
|
9837
|
+
const response = await client.generateTextWithMetadata(prompt, 4e3, 0.2, { format: "json" });
|
|
9838
|
+
assertNotTruncated(response, "tag");
|
|
9839
|
+
const parsedTags = MotivationParsers.parseTags(response.text);
|
|
9884
9840
|
return MotivationParsers.validateTagOffsets(parsedTags, content, category);
|
|
9885
9841
|
}
|
|
9886
9842
|
};
|
|
@@ -9949,36 +9905,42 @@ Example output:
|
|
|
9949
9905
|
{ format: "json" }
|
|
9950
9906
|
);
|
|
9951
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;
|
|
9952
9914
|
try {
|
|
9953
|
-
|
|
9954
|
-
if (jsonStr.startsWith("```")) {
|
|
9955
|
-
jsonStr = jsonStr.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
|
|
9956
|
-
}
|
|
9957
|
-
const entities = JSON.parse(jsonStr);
|
|
9958
|
-
logger2.debug("Parsed entities from AI response", { count: entities.length });
|
|
9959
|
-
if (response.stopReason === "max_tokens") {
|
|
9960
|
-
const errorMsg = `AI response truncated: Found ${entities.length} entities but response hit max_tokens limit. Increase max_tokens or reduce resource size.`;
|
|
9961
|
-
logger2.error(errorMsg);
|
|
9962
|
-
throw new Error(errorMsg);
|
|
9963
|
-
}
|
|
9964
|
-
return entities.filter((e) => {
|
|
9965
|
-
const ok = e && typeof e === "object" && typeof e.exact === "string" && typeof e.entityType === "string";
|
|
9966
|
-
if (!ok) {
|
|
9967
|
-
logger2.debug("Dropped malformed LLM entity", { entity: e });
|
|
9968
|
-
}
|
|
9969
|
-
return ok;
|
|
9970
|
-
}).map((entity) => ({
|
|
9971
|
-
exact: entity.exact,
|
|
9972
|
-
entityType: entity.entityType,
|
|
9973
|
-
...typeof entity.prefix === "string" ? { prefix: entity.prefix } : {},
|
|
9974
|
-
...typeof entity.suffix === "string" ? { suffix: entity.suffix } : {}
|
|
9975
|
-
}));
|
|
9915
|
+
entities = JSON.parse(response.text.trim());
|
|
9976
9916
|
} catch (error) {
|
|
9977
9917
|
logger2.error("Failed to parse entity extraction response", {
|
|
9978
|
-
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))
|
|
9979
9923
|
});
|
|
9980
|
-
return [];
|
|
9981
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
|
+
}));
|
|
9982
9944
|
}
|
|
9983
9945
|
function getLanguageName(locale) {
|
|
9984
9946
|
return getLocaleEnglishName(locale) || locale;
|