@sembl/core 0.3.0 → 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/dist/index.cjs +463 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +234 -38
- package/dist/index.d.ts +234 -38
- package/dist/index.js +455 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
Constrain: () => Constrain,
|
|
27
27
|
Describe: () => Describe,
|
|
28
28
|
EnumResolutionError: () => EnumResolutionError,
|
|
29
|
+
FIELD_FORMATS: () => FIELD_FORMATS,
|
|
29
30
|
PROVENANCE_INSTRUCTIONS: () => PROVENANCE_INSTRUCTIONS,
|
|
30
31
|
SOURCE_INSTRUCTIONS: () => SOURCE_INSTRUCTIONS,
|
|
31
32
|
Schema: () => Schema,
|
|
@@ -35,19 +36,25 @@ __export(index_exports, {
|
|
|
35
36
|
ValuesFrom: () => ValuesFrom,
|
|
36
37
|
budgetSources: () => budgetSources,
|
|
37
38
|
buildPrompt: () => buildPrompt,
|
|
39
|
+
buildRepairCorrection: () => buildRepairCorrection,
|
|
38
40
|
buildRepairInput: () => buildRepairInput,
|
|
39
41
|
bundleOf: () => bundleOf,
|
|
40
42
|
coerce: () => coerce,
|
|
43
|
+
coerceDetailed: () => coerceDetailed,
|
|
41
44
|
coerceMany: () => coerceMany,
|
|
42
45
|
coerceWithProvenance: () => coerceWithProvenance,
|
|
43
46
|
collectEnumSources: () => collectEnumSources,
|
|
44
47
|
defineSchema: () => defineSchema,
|
|
48
|
+
describeFormat: () => describeFormat,
|
|
45
49
|
field: () => field,
|
|
50
|
+
formatToJsonSchema: () => formatToJsonSchema,
|
|
46
51
|
isCoerceInput: () => isCoerceInput,
|
|
47
52
|
isSource: () => isSource,
|
|
48
53
|
normalizeInstructions: () => normalizeInstructions,
|
|
49
54
|
partialCoerce: () => partialCoerce,
|
|
55
|
+
partialCoerceDetailed: () => partialCoerceDetailed,
|
|
50
56
|
partialCoerceWithProvenance: () => partialCoerceWithProvenance,
|
|
57
|
+
primeCache: () => primeCache,
|
|
51
58
|
provenanceInstructions: () => provenanceInstructions,
|
|
52
59
|
renderSources: () => renderSources,
|
|
53
60
|
resolveEnumSources: () => resolveEnumSources,
|
|
@@ -58,11 +65,164 @@ __export(index_exports, {
|
|
|
58
65
|
toOpenAIJsonSchema: () => toOpenAIJsonSchema,
|
|
59
66
|
toProvenanceSchema: () => toProvenanceSchema,
|
|
60
67
|
toSources: () => toSources,
|
|
68
|
+
validateFormat: () => validateFormat,
|
|
61
69
|
validatePartial: () => validatePartial,
|
|
62
70
|
validateStrict: () => validateStrict
|
|
63
71
|
});
|
|
64
72
|
module.exports = __toCommonJS(index_exports);
|
|
65
73
|
|
|
74
|
+
// src/schema/formats.ts
|
|
75
|
+
var FIELD_FORMATS = [
|
|
76
|
+
"url",
|
|
77
|
+
"email",
|
|
78
|
+
"date",
|
|
79
|
+
"datetime",
|
|
80
|
+
"iso-country",
|
|
81
|
+
"us-state",
|
|
82
|
+
"us-state-name",
|
|
83
|
+
"currency"
|
|
84
|
+
];
|
|
85
|
+
var ISO_COUNTRIES = new Set(
|
|
86
|
+
"AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW".split(" ")
|
|
87
|
+
);
|
|
88
|
+
var US_STATES = {
|
|
89
|
+
AL: "Alabama",
|
|
90
|
+
AK: "Alaska",
|
|
91
|
+
AZ: "Arizona",
|
|
92
|
+
AR: "Arkansas",
|
|
93
|
+
CA: "California",
|
|
94
|
+
CO: "Colorado",
|
|
95
|
+
CT: "Connecticut",
|
|
96
|
+
DE: "Delaware",
|
|
97
|
+
FL: "Florida",
|
|
98
|
+
GA: "Georgia",
|
|
99
|
+
HI: "Hawaii",
|
|
100
|
+
ID: "Idaho",
|
|
101
|
+
IL: "Illinois",
|
|
102
|
+
IN: "Indiana",
|
|
103
|
+
IA: "Iowa",
|
|
104
|
+
KS: "Kansas",
|
|
105
|
+
KY: "Kentucky",
|
|
106
|
+
LA: "Louisiana",
|
|
107
|
+
ME: "Maine",
|
|
108
|
+
MD: "Maryland",
|
|
109
|
+
MA: "Massachusetts",
|
|
110
|
+
MI: "Michigan",
|
|
111
|
+
MN: "Minnesota",
|
|
112
|
+
MS: "Mississippi",
|
|
113
|
+
MO: "Missouri",
|
|
114
|
+
MT: "Montana",
|
|
115
|
+
NE: "Nebraska",
|
|
116
|
+
NV: "Nevada",
|
|
117
|
+
NH: "New Hampshire",
|
|
118
|
+
NJ: "New Jersey",
|
|
119
|
+
NM: "New Mexico",
|
|
120
|
+
NY: "New York",
|
|
121
|
+
NC: "North Carolina",
|
|
122
|
+
ND: "North Dakota",
|
|
123
|
+
OH: "Ohio",
|
|
124
|
+
OK: "Oklahoma",
|
|
125
|
+
OR: "Oregon",
|
|
126
|
+
PA: "Pennsylvania",
|
|
127
|
+
RI: "Rhode Island",
|
|
128
|
+
SC: "South Carolina",
|
|
129
|
+
SD: "South Dakota",
|
|
130
|
+
TN: "Tennessee",
|
|
131
|
+
TX: "Texas",
|
|
132
|
+
UT: "Utah",
|
|
133
|
+
VT: "Vermont",
|
|
134
|
+
VA: "Virginia",
|
|
135
|
+
WA: "Washington",
|
|
136
|
+
WV: "West Virginia",
|
|
137
|
+
WI: "Wisconsin",
|
|
138
|
+
WY: "Wyoming",
|
|
139
|
+
DC: "District of Columbia",
|
|
140
|
+
PR: "Puerto Rico",
|
|
141
|
+
GU: "Guam",
|
|
142
|
+
VI: "U.S. Virgin Islands",
|
|
143
|
+
AS: "American Samoa",
|
|
144
|
+
MP: "Northern Mariana Islands"
|
|
145
|
+
};
|
|
146
|
+
var US_STATE_NAMES = new Set(Object.values(US_STATES));
|
|
147
|
+
var CURRENCIES = new Set(
|
|
148
|
+
"AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BRL BSD BTN BWP BYN BZD CAD CDF CHF CLP CNY COP CRC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD UYU UZS VES VND VUV WST XAF XCD XOF XPF YER ZAR ZMW ZWG".split(" ")
|
|
149
|
+
);
|
|
150
|
+
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
151
|
+
var DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
152
|
+
var DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?$/;
|
|
153
|
+
function isCalendarDate(y, m, d) {
|
|
154
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
155
|
+
return date.getUTCFullYear() === y && date.getUTCMonth() === m - 1 && date.getUTCDate() === d;
|
|
156
|
+
}
|
|
157
|
+
function validateFormat(value, format) {
|
|
158
|
+
switch (format) {
|
|
159
|
+
case "url": {
|
|
160
|
+
try {
|
|
161
|
+
const url = new URL(value);
|
|
162
|
+
if (url.protocol === "http:" || url.protocol === "https:") return void 0;
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
return `Expected an absolute http(s) URL, got ${JSON.stringify(value)}`;
|
|
166
|
+
}
|
|
167
|
+
case "email":
|
|
168
|
+
return EMAIL.test(value) ? void 0 : `Expected an email address, got ${JSON.stringify(value)}`;
|
|
169
|
+
case "date": {
|
|
170
|
+
const m = DATE.exec(value);
|
|
171
|
+
if (m && isCalendarDate(Number(m[1]), Number(m[2]), Number(m[3]))) return void 0;
|
|
172
|
+
return `Expected a calendar date as YYYY-MM-DD, got ${JSON.stringify(value)}`;
|
|
173
|
+
}
|
|
174
|
+
case "datetime":
|
|
175
|
+
return DATETIME.test(value) && !Number.isNaN(Date.parse(value)) ? void 0 : `Expected an ISO 8601 timestamp, got ${JSON.stringify(value)}`;
|
|
176
|
+
case "iso-country":
|
|
177
|
+
return ISO_COUNTRIES.has(value) ? void 0 : `Expected an ISO 3166-1 alpha-2 country code such as US or DE, got ${JSON.stringify(value)}`;
|
|
178
|
+
case "us-state":
|
|
179
|
+
return value in US_STATES ? void 0 : `Expected a two-letter USPS state code such as CA or NY, got ${JSON.stringify(value)}`;
|
|
180
|
+
case "us-state-name":
|
|
181
|
+
return US_STATE_NAMES.has(value) ? void 0 : `Expected a US state's full name such as California, got ${JSON.stringify(value)}`;
|
|
182
|
+
case "currency":
|
|
183
|
+
return CURRENCIES.has(value) ? void 0 : `Expected an ISO 4217 currency code such as USD or EUR, got ${JSON.stringify(value)}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function describeFormat(format) {
|
|
187
|
+
switch (format) {
|
|
188
|
+
case "url":
|
|
189
|
+
return "an absolute http(s) URL";
|
|
190
|
+
case "email":
|
|
191
|
+
return "an email address";
|
|
192
|
+
case "date":
|
|
193
|
+
return "a calendar date as YYYY-MM-DD";
|
|
194
|
+
case "datetime":
|
|
195
|
+
return "an ISO 8601 timestamp (e.g. 2026-09-05T14:30:00Z)";
|
|
196
|
+
case "iso-country":
|
|
197
|
+
return "an ISO 3166-1 alpha-2 country code (e.g. US, DE, PT), never a country name";
|
|
198
|
+
case "us-state":
|
|
199
|
+
return "a two-letter USPS state code (e.g. CA, NY), never the state's name";
|
|
200
|
+
case "us-state-name":
|
|
201
|
+
return "a US state's full name (e.g. California, New York), never its abbreviation";
|
|
202
|
+
case "currency":
|
|
203
|
+
return "an ISO 4217 currency code (e.g. USD, EUR, GBP), never a symbol or a word";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function formatToJsonSchema(format) {
|
|
207
|
+
switch (format) {
|
|
208
|
+
case "url":
|
|
209
|
+
return { format: "uri" };
|
|
210
|
+
case "email":
|
|
211
|
+
return { format: "email" };
|
|
212
|
+
case "date":
|
|
213
|
+
return { format: "date" };
|
|
214
|
+
case "datetime":
|
|
215
|
+
return { format: "date-time" };
|
|
216
|
+
case "iso-country":
|
|
217
|
+
case "us-state":
|
|
218
|
+
return { pattern: "^[A-Z]{2}$" };
|
|
219
|
+
case "currency":
|
|
220
|
+
return { pattern: "^[A-Z]{3}$" };
|
|
221
|
+
case "us-state-name":
|
|
222
|
+
return {};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
66
226
|
// src/schema/json-schema.ts
|
|
67
227
|
var CONSTRAINT_KEYWORDS = [
|
|
68
228
|
"maxLength",
|
|
@@ -79,6 +239,9 @@ function constraintsToJsonSchema(constraints, dialect) {
|
|
|
79
239
|
return {};
|
|
80
240
|
}
|
|
81
241
|
const out = {};
|
|
242
|
+
if (constraints.format !== void 0) {
|
|
243
|
+
Object.assign(out, formatToJsonSchema(constraints.format));
|
|
244
|
+
}
|
|
82
245
|
for (const keyword of CONSTRAINT_KEYWORDS) {
|
|
83
246
|
const value = constraints[keyword];
|
|
84
247
|
if (value !== void 0) {
|
|
@@ -228,7 +391,7 @@ async function resolveEnumSources(schema, resolver, bundle) {
|
|
|
228
391
|
await Promise.all(
|
|
229
392
|
[...usages].map(async ([sourceId, usage]) => {
|
|
230
393
|
try {
|
|
231
|
-
const values = await resolver(sourceId);
|
|
394
|
+
const values = await resolver(sourceId, { sourceId, schema, ...usage });
|
|
232
395
|
if (!values || values.length === 0) {
|
|
233
396
|
failures.push({ sourceId, reason: "empty", ...usage });
|
|
234
397
|
return;
|
|
@@ -463,7 +626,7 @@ ${summary}`);
|
|
|
463
626
|
// src/coerce/sources.ts
|
|
464
627
|
var SOURCE_TAG = "source";
|
|
465
628
|
function isSource(value) {
|
|
466
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.text === "string" && (value.label === void 0 || typeof value.label === "string");
|
|
629
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.text === "string" && (value.label === void 0 || typeof value.label === "string") && (value.maxChars === void 0 || typeof value.maxChars === "number");
|
|
467
630
|
}
|
|
468
631
|
function isCoerceInput(value) {
|
|
469
632
|
return typeof value === "string" || isSource(value) || Array.isArray(value) && value.every(isSource);
|
|
@@ -483,7 +646,9 @@ function toSources(input) {
|
|
|
483
646
|
}
|
|
484
647
|
function cleanLabel(source) {
|
|
485
648
|
const label = source.label?.trim();
|
|
486
|
-
|
|
649
|
+
const cleaned = label ? { label, text: source.text } : { text: source.text };
|
|
650
|
+
if (source.maxChars !== void 0) cleaned.maxChars = source.maxChars;
|
|
651
|
+
return cleaned;
|
|
487
652
|
}
|
|
488
653
|
function escapeText(text) {
|
|
489
654
|
return text.replace(new RegExp(`</(\\s*${SOURCE_TAG}\\b)`, "gi"), "<\\/$1");
|
|
@@ -521,7 +686,10 @@ function describeConstraints(constraints) {
|
|
|
521
686
|
return [];
|
|
522
687
|
}
|
|
523
688
|
const phrases = [];
|
|
524
|
-
const { minLength, maxLength, minimum, maximum, minItems, maxItems, pattern } = constraints;
|
|
689
|
+
const { minLength, maxLength, minimum, maximum, minItems, maxItems, pattern, format } = constraints;
|
|
690
|
+
if (format !== void 0) {
|
|
691
|
+
phrases.push(describeFormat(format));
|
|
692
|
+
}
|
|
525
693
|
if (minLength !== void 0 && maxLength !== void 0) {
|
|
526
694
|
phrases.push(`between ${minLength} and ${maxLength} characters`);
|
|
527
695
|
} else if (maxLength !== void 0) {
|
|
@@ -634,6 +802,8 @@ function buildPrompt(schema, bundle, options = {}) {
|
|
|
634
802
|
lines.push("Instructions:");
|
|
635
803
|
lines.push("- Extract values from the sources that match the schema fields.");
|
|
636
804
|
lines.push("- Use null for optional fields that cannot be determined from the input.");
|
|
805
|
+
lines.push("- A value the input states is never omitted because it looks like a default: return 1, 0, false or an empty list when that is what the input says.");
|
|
806
|
+
lines.push("- Never return an empty object when the input states values for any field.");
|
|
637
807
|
lines.push("- Required fields must always have a valid, non-null value.");
|
|
638
808
|
lines.push("- Interpret the user's input semantically \u2014 infer meaning, don't just pattern match.");
|
|
639
809
|
lines.push("- Respect every stated limit exactly; truncate or drop lower-priority content to stay within it.");
|
|
@@ -659,7 +829,7 @@ function renderReceived(received) {
|
|
|
659
829
|
return text.length > MAX_RECEIVED_LENGTH ? `${text.slice(0, MAX_RECEIVED_LENGTH)}\u2026 (truncated)` : text;
|
|
660
830
|
}
|
|
661
831
|
function buildRepairInput(originalInput, rejected, issues) {
|
|
662
|
-
|
|
832
|
+
return [
|
|
663
833
|
originalInput,
|
|
664
834
|
"",
|
|
665
835
|
"---",
|
|
@@ -668,9 +838,11 @@ function buildRepairInput(originalInput, rejected, issues) {
|
|
|
668
838
|
"",
|
|
669
839
|
JSON.stringify(rejected, null, 2),
|
|
670
840
|
"",
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
841
|
+
buildRepairCorrection(issues)
|
|
842
|
+
].join("\n");
|
|
843
|
+
}
|
|
844
|
+
function buildRepairCorrection(issues) {
|
|
845
|
+
const lines = ["The output was rejected because:", ""];
|
|
674
846
|
for (const issue of issues) {
|
|
675
847
|
lines.push(`- ${issue.path}: ${issue.message} (received: ${renderReceived(issue.received)})`);
|
|
676
848
|
}
|
|
@@ -699,11 +871,31 @@ var PROVENANCE_INSTRUCTIONS = [
|
|
|
699
871
|
"- Judge each field on its own. A confident value next to a guessed one is",
|
|
700
872
|
" normal, and marking the guess honestly is more useful than looking sure."
|
|
701
873
|
].join("\n");
|
|
874
|
+
function provenanceFieldNames(schema, fields) {
|
|
875
|
+
if (fields === void 0) return new Set(schema.fields.map((f) => f.name));
|
|
876
|
+
const known = new Set(schema.fields.map((f) => f.name));
|
|
877
|
+
for (const name of fields) {
|
|
878
|
+
if (!known.has(name)) {
|
|
879
|
+
throw new RangeError(
|
|
880
|
+
`provenance field "${name}" is not a field of schema "${schema.id}" (fields: ${[...known].join(", ")})`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return new Set(fields);
|
|
885
|
+
}
|
|
702
886
|
function provenanceInstructions(options = {}) {
|
|
703
887
|
const labels = options.sourceLabels ?? [];
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
888
|
+
let text = PROVENANCE_INSTRUCTIONS;
|
|
889
|
+
if (options.fields !== void 0) {
|
|
890
|
+
text = text.replace(
|
|
891
|
+
"- Every field is wrapped as an object: put the extracted value in `value`.",
|
|
892
|
+
`- Only these fields are wrapped as objects, with the extracted value in \`value\`: ${options.fields.join(", ")}. Every other field is a plain value.`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
if (labels.length >= 2) {
|
|
896
|
+
text += "\n- Always set `source` to the label of the source the value was read from. When several agree, name the one quoted in `evidence`.";
|
|
897
|
+
}
|
|
898
|
+
return text;
|
|
707
899
|
}
|
|
708
900
|
function annotationSchema(parentId, field2, sourceLabels) {
|
|
709
901
|
const valueField = {
|
|
@@ -735,7 +927,7 @@ function annotationSchema(parentId, field2, sourceLabels) {
|
|
|
735
927
|
name: "source",
|
|
736
928
|
description: "The label of the source this value was read from.",
|
|
737
929
|
type: { kind: "enum", values: [...sourceLabels] },
|
|
738
|
-
required:
|
|
930
|
+
required: true
|
|
739
931
|
}
|
|
740
932
|
] : []
|
|
741
933
|
]
|
|
@@ -745,7 +937,12 @@ function toProvenanceSchema(schema, bundle, options = {}) {
|
|
|
745
937
|
const schemas = { ...bundle?.schemas ?? {} };
|
|
746
938
|
const fields = [];
|
|
747
939
|
const sourceLabels = options.sourceLabels ?? [];
|
|
940
|
+
const wrapped = provenanceFieldNames(schema, options.fields);
|
|
748
941
|
for (const field2 of schema.fields) {
|
|
942
|
+
if (!wrapped.has(field2.name)) {
|
|
943
|
+
fields.push(field2);
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
749
946
|
const annotation = annotationSchema(schema.id, field2, sourceLabels);
|
|
750
947
|
schemas[annotation.id] = annotation;
|
|
751
948
|
fields.push({
|
|
@@ -821,10 +1018,30 @@ ${tailRoom > 0 ? text.slice(text.length - tailRoom) : ""}`;
|
|
|
821
1018
|
}
|
|
822
1019
|
}
|
|
823
1020
|
function budgetSources(sources, maxChars, policy = "tail") {
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
1021
|
+
const records = /* @__PURE__ */ new Map();
|
|
1022
|
+
const record = (index, source, text) => {
|
|
1023
|
+
const existing = records.get(index);
|
|
1024
|
+
if (existing) {
|
|
1025
|
+
existing.keptLength = text.length;
|
|
1026
|
+
} else {
|
|
1027
|
+
records.set(index, {
|
|
1028
|
+
...source.label !== void 0 ? { label: source.label } : {},
|
|
1029
|
+
originalLength: source.text.length,
|
|
1030
|
+
keptLength: text.length
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
const capped = sources.map((source, index) => {
|
|
1035
|
+
if (source.maxChars === void 0 || source.text.length <= source.maxChars) return source;
|
|
1036
|
+
const text = truncateText(source.text, source.maxChars, policy);
|
|
1037
|
+
record(index, source, text);
|
|
1038
|
+
return { ...source, text };
|
|
1039
|
+
});
|
|
1040
|
+
const total = capped.reduce((sum, s) => sum + s.text.length, 0);
|
|
1041
|
+
if (maxChars === void 0 || total <= maxChars) {
|
|
1042
|
+
return { sources: capped, truncated: [...records.values()] };
|
|
827
1043
|
}
|
|
1044
|
+
sources = capped;
|
|
828
1045
|
const allowance = /* @__PURE__ */ new Map();
|
|
829
1046
|
const order = sources.map((s, i) => i).sort((a, b) => sources[a].text.length - sources[b].text.length);
|
|
830
1047
|
let remaining = maxChars;
|
|
@@ -834,19 +1051,14 @@ function budgetSources(sources, maxChars, policy = "tail") {
|
|
|
834
1051
|
allowance.set(index, granted);
|
|
835
1052
|
remaining -= granted;
|
|
836
1053
|
});
|
|
837
|
-
const truncated = [];
|
|
838
1054
|
const budgeted = sources.map((source, index) => {
|
|
839
1055
|
const limit = allowance.get(index) ?? 0;
|
|
840
1056
|
if (source.text.length <= limit) return source;
|
|
841
1057
|
const text = truncateText(source.text, limit, policy);
|
|
842
|
-
|
|
843
|
-
...source.label !== void 0 ? { label: source.label } : {},
|
|
844
|
-
originalLength: source.text.length,
|
|
845
|
-
keptLength: text.length
|
|
846
|
-
});
|
|
1058
|
+
record(index, source, text);
|
|
847
1059
|
return { ...source, text };
|
|
848
1060
|
});
|
|
849
|
-
return { sources: budgeted, truncated };
|
|
1061
|
+
return { sources: budgeted, truncated: [...records.values()] };
|
|
850
1062
|
}
|
|
851
1063
|
|
|
852
1064
|
// src/coerce/validator.ts
|
|
@@ -862,7 +1074,7 @@ function entries(count) {
|
|
|
862
1074
|
return `${count} ${count === 1 ? "entry" : "entries"}`;
|
|
863
1075
|
}
|
|
864
1076
|
function validateConstraints(value, constraints, path, issues) {
|
|
865
|
-
const { minLength, maxLength, minimum, maximum, minItems, maxItems, pattern } = constraints;
|
|
1077
|
+
const { minLength, maxLength, minimum, maximum, minItems, maxItems, pattern, format } = constraints;
|
|
866
1078
|
if (Array.isArray(value)) {
|
|
867
1079
|
if (minItems !== void 0 && value.length < minItems) {
|
|
868
1080
|
issues.push({
|
|
@@ -906,6 +1118,10 @@ function validateConstraints(value, constraints, path, issues) {
|
|
|
906
1118
|
received: value
|
|
907
1119
|
});
|
|
908
1120
|
}
|
|
1121
|
+
if (format !== void 0) {
|
|
1122
|
+
const message = validateFormat(value, format);
|
|
1123
|
+
if (message) issues.push({ path, message, received: value });
|
|
1124
|
+
}
|
|
909
1125
|
return;
|
|
910
1126
|
}
|
|
911
1127
|
if (typeof value === "number") {
|
|
@@ -1247,8 +1463,15 @@ function generateSpanId() {
|
|
|
1247
1463
|
}
|
|
1248
1464
|
var Tracer = class {
|
|
1249
1465
|
sinks;
|
|
1250
|
-
|
|
1466
|
+
baseAttributes;
|
|
1467
|
+
/**
|
|
1468
|
+
* `baseAttributes` are merged into every span this tracer opens — how a
|
|
1469
|
+
* batch stamps `itemIndex` on the spans of each item, so a sink can tell
|
|
1470
|
+
* whose `llmCall` it is looking at under concurrency.
|
|
1471
|
+
*/
|
|
1472
|
+
constructor(sinks, baseAttributes) {
|
|
1251
1473
|
this.sinks = sinks ?? [];
|
|
1474
|
+
this.baseAttributes = baseAttributes;
|
|
1252
1475
|
}
|
|
1253
1476
|
startSpan(name, attributes, parent) {
|
|
1254
1477
|
return {
|
|
@@ -1256,7 +1479,7 @@ var Tracer = class {
|
|
|
1256
1479
|
name,
|
|
1257
1480
|
startTime: Date.now(),
|
|
1258
1481
|
events: [],
|
|
1259
|
-
attributes,
|
|
1482
|
+
attributes: this.baseAttributes ? { ...this.baseAttributes, ...attributes } : attributes,
|
|
1260
1483
|
parentId: parent?.id
|
|
1261
1484
|
};
|
|
1262
1485
|
}
|
|
@@ -1311,15 +1534,31 @@ async function resolveEnums(schema, bundle, enumResolver, tracer, parent) {
|
|
|
1311
1534
|
tracer.endSpan(span);
|
|
1312
1535
|
}
|
|
1313
1536
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1537
|
+
function emptyUsage() {
|
|
1538
|
+
return { calls: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
|
|
1539
|
+
}
|
|
1540
|
+
function addUsage(into, usage) {
|
|
1541
|
+
into.calls += 1;
|
|
1542
|
+
if (!usage) return;
|
|
1543
|
+
into.promptTokens += usage.promptTokens;
|
|
1544
|
+
into.completionTokens += usage.completionTokens;
|
|
1545
|
+
into.totalTokens += usage.totalTokens;
|
|
1546
|
+
into.cacheReadTokens += usage.cacheReadTokens ?? 0;
|
|
1547
|
+
into.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
|
|
1548
|
+
}
|
|
1549
|
+
function checkOptions(options) {
|
|
1317
1550
|
const maxRepairAttempts = options.maxRepairAttempts ?? 0;
|
|
1318
1551
|
if (!Number.isInteger(maxRepairAttempts) || maxRepairAttempts < 0) {
|
|
1319
1552
|
throw new RangeError(
|
|
1320
1553
|
`maxRepairAttempts must be a non-negative integer, got ${String(options.maxRepairAttempts)}`
|
|
1321
1554
|
);
|
|
1322
1555
|
}
|
|
1556
|
+
const retryOnEmpty = options.retryOnEmpty ?? 0;
|
|
1557
|
+
if (!Number.isInteger(retryOnEmpty) || retryOnEmpty < 0) {
|
|
1558
|
+
throw new RangeError(
|
|
1559
|
+
`retryOnEmpty must be a non-negative integer, got ${String(options.retryOnEmpty)}`
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1323
1562
|
const instructions = normalizeInstructions(options.instructions);
|
|
1324
1563
|
const onInvalidField = options.onInvalidField ?? "throw";
|
|
1325
1564
|
if (!INVALID_FIELD_POLICIES.includes(onInvalidField)) {
|
|
@@ -1332,61 +1571,106 @@ async function runCoercion(input, options, { mode, provenance }) {
|
|
|
1332
1571
|
`maxInputChars must be a positive integer, got ${String(options.maxInputChars)}`
|
|
1333
1572
|
);
|
|
1334
1573
|
}
|
|
1574
|
+
return { maxRepairAttempts, retryOnEmpty, onInvalidField, instructions };
|
|
1575
|
+
}
|
|
1576
|
+
async function prepareRequest(options, { mode, provenance }, instructions, sourceLabels, tracer, rootSpan) {
|
|
1577
|
+
const { schema, enumResolver } = options;
|
|
1578
|
+
const bundle = options.bundle ?? bundleOf(schema);
|
|
1579
|
+
const resolvedEnums = await resolveEnums(schema, bundle, enumResolver, tracer, rootSpan);
|
|
1580
|
+
const promptSpan = tracer.startSpan("buildPrompt", {}, rootSpan);
|
|
1581
|
+
const basePrompt = buildPrompt(schema, bundle, { resolvedEnums, instructions });
|
|
1582
|
+
const provenanceOptions = { sourceLabels, fields: options.provenanceFields };
|
|
1583
|
+
const systemPrompt = provenance ? `${basePrompt}
|
|
1584
|
+
${provenanceInstructions(provenanceOptions)}` : basePrompt;
|
|
1585
|
+
tracer.addEvent(promptSpan, "promptBuilt", {
|
|
1586
|
+
promptLength: systemPrompt.length,
|
|
1587
|
+
instructionCount: instructions.length,
|
|
1588
|
+
mode
|
|
1589
|
+
});
|
|
1590
|
+
tracer.endSpan(promptSpan);
|
|
1591
|
+
const request = provenance ? toProvenanceSchema(schema, bundle, provenanceOptions) : { schema, bundle };
|
|
1592
|
+
const schemaSpan = tracer.startSpan("buildJsonSchema", {}, rootSpan);
|
|
1593
|
+
const jsonSchema = runtimeSchemaToJsonSchema(request.schema, request.bundle, {
|
|
1594
|
+
resolvedEnums
|
|
1595
|
+
});
|
|
1596
|
+
tracer.endSpan(schemaSpan);
|
|
1597
|
+
return { systemPrompt, jsonSchema, schema: request.schema, bundle: request.bundle, resolvedEnums };
|
|
1598
|
+
}
|
|
1599
|
+
function isEmptyResult(data) {
|
|
1600
|
+
return Object.values(data).every((value) => value === null || value === void 0);
|
|
1601
|
+
}
|
|
1602
|
+
var EMPTY_RETRY_NOTE = "A previous attempt at this extraction returned no fields. The sources above do state values for at least some fields; read them again and return every value that is stated, leaving out only what the sources genuinely do not say.";
|
|
1603
|
+
async function runCoercion(input, options, { mode, provenance, traceAttributes }) {
|
|
1604
|
+
const { provider, schema, traceSinks } = options;
|
|
1605
|
+
const bundle = options.bundle ?? bundleOf(schema);
|
|
1606
|
+
const { maxRepairAttempts, retryOnEmpty, onInvalidField, instructions } = checkOptions(options);
|
|
1335
1607
|
const rawSources = toSources(input);
|
|
1336
|
-
const tracer = new Tracer(traceSinks);
|
|
1608
|
+
const tracer = new Tracer(traceSinks, traceAttributes);
|
|
1337
1609
|
const rootSpan = tracer.startSpan(mode, {
|
|
1338
1610
|
schemaId: schema.id,
|
|
1339
1611
|
provenance,
|
|
1340
1612
|
onInvalidField,
|
|
1341
1613
|
sourceCount: rawSources.length
|
|
1342
1614
|
});
|
|
1615
|
+
const usage = emptyUsage();
|
|
1343
1616
|
try {
|
|
1344
1617
|
const sources = await prepareSources(rawSources, options, tracer, rootSpan);
|
|
1345
1618
|
const sourceLabels = sources.length > 1 ? sources.map((s) => s.label ?? "") : [];
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1619
|
+
const prepared = await prepareRequest(
|
|
1620
|
+
options,
|
|
1621
|
+
{ mode, provenance },
|
|
1622
|
+
instructions,
|
|
1623
|
+
sourceLabels,
|
|
1350
1624
|
tracer,
|
|
1351
1625
|
rootSpan
|
|
1352
1626
|
);
|
|
1353
|
-
const
|
|
1354
|
-
const basePrompt = buildPrompt(schema, bundle, { resolvedEnums, instructions });
|
|
1355
|
-
const systemPrompt = provenance ? `${basePrompt}
|
|
1356
|
-
${provenanceInstructions({ sourceLabels })}` : basePrompt;
|
|
1357
|
-
tracer.addEvent(promptSpan, "promptBuilt", {
|
|
1358
|
-
promptLength: systemPrompt.length,
|
|
1359
|
-
instructionCount: instructions.length
|
|
1360
|
-
});
|
|
1361
|
-
tracer.endSpan(promptSpan);
|
|
1362
|
-
const request = provenance ? toProvenanceSchema(schema, bundle, { sourceLabels }) : { schema, bundle };
|
|
1363
|
-
const schemaSpan = tracer.startSpan("buildJsonSchema", {}, rootSpan);
|
|
1364
|
-
const jsonSchema = runtimeSchemaToJsonSchema(request.schema, request.bundle, {
|
|
1365
|
-
resolvedEnums
|
|
1366
|
-
});
|
|
1367
|
-
tracer.endSpan(schemaSpan);
|
|
1627
|
+
const { systemPrompt, jsonSchema, resolvedEnums } = prepared;
|
|
1368
1628
|
const validate = mode === "coerce" ? validateStrict : validatePartial;
|
|
1369
1629
|
const renderedInput = renderSources(sources);
|
|
1630
|
+
const hasInput = sources.some((s) => s.text.trim().length > 0);
|
|
1370
1631
|
tracer.addEvent(rootSpan, "inputRendered", {
|
|
1371
1632
|
sourceCount: sources.length,
|
|
1372
1633
|
inputLength: renderedInput.length
|
|
1373
1634
|
});
|
|
1374
1635
|
let userInput = renderedInput;
|
|
1375
1636
|
let issues = [];
|
|
1376
|
-
let run = { data: {}, provenance: {}, issues: [] };
|
|
1637
|
+
let run = { data: {}, provenance: {}, issues: [], usage };
|
|
1638
|
+
let emptyRetries = 0;
|
|
1639
|
+
const multiTurn = provider.supportsHistory === true;
|
|
1640
|
+
const history = [];
|
|
1641
|
+
const followUp = (rejected, text, folded) => {
|
|
1642
|
+
if (multiTurn) {
|
|
1643
|
+
history.push({ role: "assistant", data: rejected }, { role: "user", text });
|
|
1644
|
+
} else {
|
|
1645
|
+
userInput = folded;
|
|
1646
|
+
}
|
|
1647
|
+
};
|
|
1377
1648
|
for (let attempt = 0; attempt <= maxRepairAttempts; attempt++) {
|
|
1378
|
-
const llmSpan = tracer.startSpan("llmCall", { attempt }, rootSpan);
|
|
1649
|
+
const llmSpan = tracer.startSpan("llmCall", { attempt, turns: history.length }, rootSpan);
|
|
1379
1650
|
const response = await provider.complete({
|
|
1380
1651
|
systemPrompt,
|
|
1381
1652
|
userInput,
|
|
1653
|
+
...history.length > 0 ? { history: [...history] } : {},
|
|
1382
1654
|
jsonSchema,
|
|
1383
|
-
schema:
|
|
1384
|
-
bundle:
|
|
1655
|
+
schema: prepared.schema,
|
|
1656
|
+
bundle: prepared.bundle,
|
|
1385
1657
|
resolvedEnums
|
|
1386
1658
|
});
|
|
1659
|
+
addUsage(usage, response.usage);
|
|
1387
1660
|
tracer.addEvent(llmSpan, "responseReceived", { usage: response.usage });
|
|
1388
1661
|
tracer.endSpan(llmSpan);
|
|
1389
|
-
run = provenance ? { ...splitProvenance(response.data, schema), issues: [] } : { data: response.data, provenance: {}, issues: [] };
|
|
1662
|
+
run = provenance ? { ...splitProvenance(response.data, schema), issues: [], usage } : { data: response.data, provenance: {}, issues: [], usage };
|
|
1663
|
+
if (hasInput && emptyRetries < retryOnEmpty && isEmptyResult(run.data)) {
|
|
1664
|
+
emptyRetries += 1;
|
|
1665
|
+
tracer.addEvent(rootSpan, "emptyRetry", { retry: emptyRetries });
|
|
1666
|
+
followUp(response.data, EMPTY_RETRY_NOTE, `${renderedInput}
|
|
1667
|
+
|
|
1668
|
+
---
|
|
1669
|
+
|
|
1670
|
+
${EMPTY_RETRY_NOTE}`);
|
|
1671
|
+
attempt -= 1;
|
|
1672
|
+
continue;
|
|
1673
|
+
}
|
|
1390
1674
|
const validationSpan = tracer.startSpan("validate", { attempt }, rootSpan);
|
|
1391
1675
|
issues = validate(run.data, schema, bundle, { resolvedEnums });
|
|
1392
1676
|
tracer.addEvent(validationSpan, "validated", { issueCount: issues.length });
|
|
@@ -1412,7 +1696,8 @@ ${provenanceInstructions({ sourceLabels })}` : basePrompt;
|
|
|
1412
1696
|
return {
|
|
1413
1697
|
data: outcome.data,
|
|
1414
1698
|
provenance: pruneProvenance(run.provenance, outcome.resolved),
|
|
1415
|
-
issues: outcome.resolved
|
|
1699
|
+
issues: outcome.resolved,
|
|
1700
|
+
usage
|
|
1416
1701
|
};
|
|
1417
1702
|
}
|
|
1418
1703
|
}
|
|
@@ -1423,7 +1708,11 @@ ${provenanceInstructions({ sourceLabels })}` : basePrompt;
|
|
|
1423
1708
|
issueCount: issues.length,
|
|
1424
1709
|
paths: issues.map((issue) => issue.path)
|
|
1425
1710
|
});
|
|
1426
|
-
|
|
1711
|
+
followUp(
|
|
1712
|
+
response.data,
|
|
1713
|
+
buildRepairCorrection(issues),
|
|
1714
|
+
buildRepairInput(renderedInput, run.data, issues)
|
|
1715
|
+
);
|
|
1427
1716
|
}
|
|
1428
1717
|
}
|
|
1429
1718
|
throw new CoerceError(issues);
|
|
@@ -1433,7 +1722,8 @@ ${provenanceInstructions({ sourceLabels })}` : basePrompt;
|
|
|
1433
1722
|
}
|
|
1434
1723
|
async function prepareSources(sources, options, tracer, parent) {
|
|
1435
1724
|
const { preprocess, maxInputChars, truncate } = options;
|
|
1436
|
-
|
|
1725
|
+
const anyCapped = sources.some((s) => s.maxChars !== void 0);
|
|
1726
|
+
if (!preprocess && maxInputChars === void 0 && !anyCapped) {
|
|
1437
1727
|
return [...sources];
|
|
1438
1728
|
}
|
|
1439
1729
|
const span = tracer.startSpan("prepareInput", {}, parent);
|
|
@@ -1450,7 +1740,7 @@ async function prepareSources(sources, options, tracer, parent) {
|
|
|
1450
1740
|
lengths: prepared.map((s) => s.text.length)
|
|
1451
1741
|
});
|
|
1452
1742
|
}
|
|
1453
|
-
if (maxInputChars !== void 0) {
|
|
1743
|
+
if (maxInputChars !== void 0 || prepared.some((s) => s.maxChars !== void 0)) {
|
|
1454
1744
|
const budgeted = budgetSources(prepared, maxInputChars, truncate);
|
|
1455
1745
|
prepared = budgeted.sources;
|
|
1456
1746
|
if (budgeted.truncated.length > 0) {
|
|
@@ -1498,19 +1788,59 @@ async function partialCoerce(input, options) {
|
|
|
1498
1788
|
});
|
|
1499
1789
|
return stripNulls(data);
|
|
1500
1790
|
}
|
|
1791
|
+
async function coerceDetailed(input, options) {
|
|
1792
|
+
const { data, issues, usage } = await runCoercion(input, options, {
|
|
1793
|
+
mode: "coerce",
|
|
1794
|
+
provenance: false
|
|
1795
|
+
});
|
|
1796
|
+
return { data, issues, usage };
|
|
1797
|
+
}
|
|
1798
|
+
async function partialCoerceDetailed(input, options) {
|
|
1799
|
+
const { data, issues, usage } = await runCoercion(input, options, {
|
|
1800
|
+
mode: "partialCoerce",
|
|
1801
|
+
provenance: false
|
|
1802
|
+
});
|
|
1803
|
+
return { data: stripNulls(data), issues, usage };
|
|
1804
|
+
}
|
|
1805
|
+
var PRIME_INPUT = "Cache warm-up. There is no input to extract from; return an object with every field null.";
|
|
1806
|
+
async function primeCache(options) {
|
|
1807
|
+
const { mode = "coerce", provenance = false, ...coerceOptions } = options;
|
|
1808
|
+
const { instructions } = checkOptions(coerceOptions);
|
|
1809
|
+
const tracer = new Tracer(coerceOptions.traceSinks);
|
|
1810
|
+
const rootSpan = tracer.startSpan("primeCache", { schemaId: coerceOptions.schema.id, mode, provenance });
|
|
1811
|
+
const usage = emptyUsage();
|
|
1812
|
+
try {
|
|
1813
|
+
const prepared = await prepareRequest(coerceOptions, { mode, provenance }, instructions, [], tracer, rootSpan);
|
|
1814
|
+
const llmSpan = tracer.startSpan("llmCall", { attempt: 0, warmup: true }, rootSpan);
|
|
1815
|
+
const response = await coerceOptions.provider.complete({
|
|
1816
|
+
systemPrompt: prepared.systemPrompt,
|
|
1817
|
+
userInput: renderSources(toSources(PRIME_INPUT)),
|
|
1818
|
+
jsonSchema: prepared.jsonSchema,
|
|
1819
|
+
schema: prepared.schema,
|
|
1820
|
+
bundle: prepared.bundle,
|
|
1821
|
+
resolvedEnums: prepared.resolvedEnums
|
|
1822
|
+
});
|
|
1823
|
+
addUsage(usage, response.usage);
|
|
1824
|
+
tracer.addEvent(llmSpan, "responseReceived", { usage: response.usage });
|
|
1825
|
+
tracer.endSpan(llmSpan);
|
|
1826
|
+
return { schemaId: coerceOptions.schema.id, mode, provenance, usage, primedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1827
|
+
} finally {
|
|
1828
|
+
tracer.endSpan(rootSpan);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1501
1831
|
async function coerceWithProvenance(input, options) {
|
|
1502
|
-
const { data, provenance, issues } = await runCoercion(input, options, {
|
|
1832
|
+
const { data, provenance, issues, usage } = await runCoercion(input, options, {
|
|
1503
1833
|
mode: "coerce",
|
|
1504
1834
|
provenance: true
|
|
1505
1835
|
});
|
|
1506
|
-
return { data, provenance, issues };
|
|
1836
|
+
return { data, provenance, issues, usage };
|
|
1507
1837
|
}
|
|
1508
1838
|
async function partialCoerceWithProvenance(input, options) {
|
|
1509
|
-
const { data, provenance, issues } = await runCoercion(input, options, {
|
|
1839
|
+
const { data, provenance, issues, usage } = await runCoercion(input, options, {
|
|
1510
1840
|
mode: "partialCoerce",
|
|
1511
1841
|
provenance: true
|
|
1512
1842
|
});
|
|
1513
|
-
return { data: stripNulls(data), provenance, issues };
|
|
1843
|
+
return { data: stripNulls(data), provenance, issues, usage };
|
|
1514
1844
|
}
|
|
1515
1845
|
|
|
1516
1846
|
// src/coerce/coerce-many.ts
|
|
@@ -1551,60 +1881,107 @@ var BackoffGate = class {
|
|
|
1551
1881
|
this.streak = 0;
|
|
1552
1882
|
}
|
|
1553
1883
|
};
|
|
1884
|
+
var InputQueue = class {
|
|
1885
|
+
iterator;
|
|
1886
|
+
pulling = Promise.resolve();
|
|
1887
|
+
index = 0;
|
|
1888
|
+
constructor(inputs) {
|
|
1889
|
+
this.iterator = Symbol.asyncIterator in inputs ? inputs[Symbol.asyncIterator]() : inputs[Symbol.iterator]();
|
|
1890
|
+
}
|
|
1891
|
+
next() {
|
|
1892
|
+
const pull = this.pulling.then(async () => {
|
|
1893
|
+
const result = await this.iterator.next();
|
|
1894
|
+
if (result.done) return void 0;
|
|
1895
|
+
return { index: this.index++, input: result.value };
|
|
1896
|
+
});
|
|
1897
|
+
this.pulling = pull.catch(() => void 0);
|
|
1898
|
+
return pull;
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
function labelOf(input) {
|
|
1902
|
+
if (typeof input === "string") return void 0;
|
|
1903
|
+
if (isSource(input)) return input.label;
|
|
1904
|
+
return input[0]?.label;
|
|
1905
|
+
}
|
|
1554
1906
|
async function coerceMany(inputs, options) {
|
|
1555
1907
|
const {
|
|
1556
1908
|
concurrency = DEFAULT_CONCURRENCY,
|
|
1557
1909
|
mode = "coerce",
|
|
1558
|
-
provenance = false,
|
|
1559
|
-
primeCache = true,
|
|
1910
|
+
provenance: provenanceOption = false,
|
|
1911
|
+
primeCache: primeCache2 = true,
|
|
1912
|
+
primed,
|
|
1560
1913
|
onItem,
|
|
1561
1914
|
signal,
|
|
1915
|
+
retry: retryOptions,
|
|
1562
1916
|
...coerceOptions
|
|
1563
1917
|
} = options;
|
|
1564
|
-
const retry = { ...DEFAULT_RETRY, ...
|
|
1918
|
+
const retry = { ...DEFAULT_RETRY, ...retryOptions };
|
|
1919
|
+
const provenance = provenanceOption !== false;
|
|
1920
|
+
if (Array.isArray(provenanceOption)) {
|
|
1921
|
+
coerceOptions.provenanceFields = provenanceOption;
|
|
1922
|
+
}
|
|
1565
1923
|
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
1566
1924
|
throw new RangeError(`concurrency must be a positive integer, got ${String(concurrency)}`);
|
|
1567
1925
|
}
|
|
1568
1926
|
if (!Number.isInteger(retry.attempts) || retry.attempts < 0) {
|
|
1569
1927
|
throw new RangeError(`retry.attempts must be a non-negative integer, got ${String(retry.attempts)}`);
|
|
1570
1928
|
}
|
|
1571
|
-
const results =
|
|
1929
|
+
const results = [];
|
|
1572
1930
|
const gate = new BackoffGate(retry);
|
|
1573
|
-
|
|
1931
|
+
const queue = new InputQueue(inputs);
|
|
1932
|
+
async function runOne(index, input) {
|
|
1574
1933
|
let attempts = 0;
|
|
1575
1934
|
let result;
|
|
1935
|
+
let usage = emptyUsage();
|
|
1936
|
+
const label = labelOf(input);
|
|
1937
|
+
const traceAttributes = { itemIndex: index, ...label !== void 0 ? { itemLabel: label } : {} };
|
|
1576
1938
|
for (; ; ) {
|
|
1577
1939
|
if (signal?.aborted) {
|
|
1578
|
-
result = { ok: false, index, error: signal.reason ?? new Error("Batch aborted"), attempts };
|
|
1940
|
+
result = { ok: false, index, error: signal.reason ?? new Error("Batch aborted"), usage, attempts };
|
|
1579
1941
|
break;
|
|
1580
1942
|
}
|
|
1581
1943
|
await gate.wait();
|
|
1582
1944
|
attempts += 1;
|
|
1583
1945
|
try {
|
|
1584
|
-
const run = await runCoercion(
|
|
1946
|
+
const run = await runCoercion(input, coerceOptions, { mode, provenance, traceAttributes });
|
|
1585
1947
|
const data = mode === "partialCoerce" ? stripNulls(run.data) : run.data;
|
|
1586
1948
|
gate.succeeded();
|
|
1587
|
-
result = { ok: true, index, data, provenance: run.provenance, issues: run.issues, attempts };
|
|
1949
|
+
result = { ok: true, index, data, provenance: run.provenance, issues: run.issues, usage: run.usage, attempts };
|
|
1588
1950
|
break;
|
|
1589
1951
|
} catch (error) {
|
|
1952
|
+
usage = emptyUsage();
|
|
1590
1953
|
if (isRetryable(error) && attempts <= retry.attempts) {
|
|
1591
1954
|
gate.failed();
|
|
1592
1955
|
continue;
|
|
1593
1956
|
}
|
|
1594
|
-
result = { ok: false, index, error, attempts };
|
|
1957
|
+
result = { ok: false, index, error, usage, attempts };
|
|
1595
1958
|
break;
|
|
1596
1959
|
}
|
|
1597
1960
|
}
|
|
1598
1961
|
results[index] = result;
|
|
1599
1962
|
onItem?.(result);
|
|
1600
1963
|
}
|
|
1601
|
-
let
|
|
1602
|
-
|
|
1603
|
-
|
|
1964
|
+
let pending;
|
|
1965
|
+
const warmup = primed ?? (primeCache2 === "eager" ? primeCache({ ...coerceOptions, mode, provenance }) : void 0);
|
|
1966
|
+
if (warmup) {
|
|
1967
|
+
await Promise.resolve(warmup).catch(() => void 0);
|
|
1968
|
+
} else if (primeCache2 === true) {
|
|
1969
|
+
const first = await queue.next();
|
|
1970
|
+
if (first === void 0) return results;
|
|
1971
|
+
const second = await queue.next();
|
|
1972
|
+
if (second === void 0) {
|
|
1973
|
+
await runOne(first.index, first.input);
|
|
1974
|
+
return results;
|
|
1975
|
+
}
|
|
1976
|
+
await runOne(first.index, first.input);
|
|
1977
|
+
pending = second;
|
|
1604
1978
|
}
|
|
1605
|
-
const workers = Array.from({ length:
|
|
1606
|
-
|
|
1607
|
-
await
|
|
1979
|
+
const workers = Array.from({ length: concurrency }, async () => {
|
|
1980
|
+
for (; ; ) {
|
|
1981
|
+
const item = pending ?? await queue.next();
|
|
1982
|
+
pending = void 0;
|
|
1983
|
+
if (item === void 0) return;
|
|
1984
|
+
await runOne(item.index, item.input);
|
|
1608
1985
|
}
|
|
1609
1986
|
});
|
|
1610
1987
|
await Promise.all(workers);
|
|
@@ -1643,6 +2020,7 @@ function resolveConfig(callConfig) {
|
|
|
1643
2020
|
maxRepairAttempts: callConfig?.maxRepairAttempts ?? global.maxRepairAttempts,
|
|
1644
2021
|
onInvalidField: callConfig?.onInvalidField ?? global.onInvalidField,
|
|
1645
2022
|
instructions: callConfig?.instructions ?? global.instructions,
|
|
2023
|
+
retryOnEmpty: callConfig?.retryOnEmpty ?? global.retryOnEmpty,
|
|
1646
2024
|
maxInputChars: callConfig?.maxInputChars ?? global.maxInputChars,
|
|
1647
2025
|
truncate: callConfig?.truncate ?? global.truncate,
|
|
1648
2026
|
preprocess: callConfig?.preprocess ?? global.preprocess
|
|
@@ -1677,6 +2055,7 @@ var Coercible = class _Coercible {
|
|
|
1677
2055
|
maxRepairAttempts: this._config.maxRepairAttempts,
|
|
1678
2056
|
onInvalidField: this._config.onInvalidField,
|
|
1679
2057
|
instructions: this._config.instructions,
|
|
2058
|
+
retryOnEmpty: this._config.retryOnEmpty,
|
|
1680
2059
|
maxInputChars: this._config.maxInputChars,
|
|
1681
2060
|
truncate: this._config.truncate,
|
|
1682
2061
|
preprocess: this._config.preprocess
|
|
@@ -1743,6 +2122,7 @@ var ConsoleSink = class {
|
|
|
1743
2122
|
Constrain,
|
|
1744
2123
|
Describe,
|
|
1745
2124
|
EnumResolutionError,
|
|
2125
|
+
FIELD_FORMATS,
|
|
1746
2126
|
PROVENANCE_INSTRUCTIONS,
|
|
1747
2127
|
SOURCE_INSTRUCTIONS,
|
|
1748
2128
|
Schema,
|
|
@@ -1752,19 +2132,25 @@ var ConsoleSink = class {
|
|
|
1752
2132
|
ValuesFrom,
|
|
1753
2133
|
budgetSources,
|
|
1754
2134
|
buildPrompt,
|
|
2135
|
+
buildRepairCorrection,
|
|
1755
2136
|
buildRepairInput,
|
|
1756
2137
|
bundleOf,
|
|
1757
2138
|
coerce,
|
|
2139
|
+
coerceDetailed,
|
|
1758
2140
|
coerceMany,
|
|
1759
2141
|
coerceWithProvenance,
|
|
1760
2142
|
collectEnumSources,
|
|
1761
2143
|
defineSchema,
|
|
2144
|
+
describeFormat,
|
|
1762
2145
|
field,
|
|
2146
|
+
formatToJsonSchema,
|
|
1763
2147
|
isCoerceInput,
|
|
1764
2148
|
isSource,
|
|
1765
2149
|
normalizeInstructions,
|
|
1766
2150
|
partialCoerce,
|
|
2151
|
+
partialCoerceDetailed,
|
|
1767
2152
|
partialCoerceWithProvenance,
|
|
2153
|
+
primeCache,
|
|
1768
2154
|
provenanceInstructions,
|
|
1769
2155
|
renderSources,
|
|
1770
2156
|
resolveEnumSources,
|
|
@@ -1775,6 +2161,7 @@ var ConsoleSink = class {
|
|
|
1775
2161
|
toOpenAIJsonSchema,
|
|
1776
2162
|
toProvenanceSchema,
|
|
1777
2163
|
toSources,
|
|
2164
|
+
validateFormat,
|
|
1778
2165
|
validatePartial,
|
|
1779
2166
|
validateStrict
|
|
1780
2167
|
});
|